Search code examples
cpicmicrochippwm

PWM for LED dimming unusual behavior


I made a function, where PWM signal is generated at the output (PORTD) without usage of PWM control registers inside PIC microcontroller (PIC18F452). In order to slowly dim LED connected at the output, I was trying to increase the time needed for pulse to advance from 0% of one period to 100% of one period of square wave, while having square wave frequency constant. Everything should go as planned, except that second parameter being passed into pwm function, somehow resets, when going from 655 to 666 (that is, when duty cycle is at 65%). After this event, value being passed to pwm function proceeds from 0. Where as it should not reset at transition from 655 to 656 but at transition from 1000 to 1001.

void main(void) {
    TRISD = 0x00; //port D set as output
    LATD = 0x00; //port D output set LOW

    unsigned int width = 1000;      // length of T_on + T_off
    unsigned int j;
    unsigned int res;
    while(1){
        for (j = 1; j <= width; j++){
            res = (unsigned int)((j*100)/width);
            pwm(&LATD, res);
        }
    }
    return;
}

void pwm(volatile unsigned char *lat, unsigned int cycle){
    if(cycle > 100){        // reset the "cycle"
        cycle = 100;
    }
    unsigned int i = 1;
    while(i<=(cycle)){    // T_on
        *lat = 0x01;    
        i++;
    }
    unsigned int j = 100-cycle;
    while(j){    // T_off
        *lat = 0;
        j--;
    }
    return;
}

As for the program itself, it should work like so:

  • second parameter passed into pwm function is the duty cycle (in %) which changes from 0 to 100
  • with variable "width" the time needed for duty cycle to advance from 0% to 100% is controlled (width = 100 represents fastest time and everything above that is considered gradually slower time from 0% to 100%)
  • expression ((j*100)/width) serves as step variable inside "while" loop inside pwm function:
  1. if width = 100, step is increased every increment of "j"
  2. if width = 1000, step is increased every 10 increments of "j", etc.
  • PORTD is passed into function as its address, whereas in function pwm, this address is operated via pointer variable lat

As for the problem itself, I could only assume two possibilities: either data type of second parameter of function pwm is incorrect or there is some unknown limitation within PIC microprocessor.

Also, here are definitions of configuration bits (device specific registers) of PIC, located int header file included in this program: https://i.sstatic.net/4ajxD.jpg

This is, how the program should operate: https://vimeo.com/488207207

This is, how the program currently operates: https://vimeo.com/488207746


Solution

  • The problem is a 16 Bit overflow:

    res = (unsigned int)((j*100)/width);
    

    if j is greater then 655 the result of the calculation j*100 is greater 16 Bit. Switch this to 32 Bit. Or easier make your loop from 0...100 for res.

    e.g.

    for (res = 0; res <= 100; res++){
        pwm(&LATD, res);
    }