Search code examples
ctimermicrocontrollerpicled

PIC timers to create PWM


I'm trying to use the timer0 module in a PIC18F13k22.

All I want to be able to do is turn the LED on and then off when the TMR0IF == 1, and to have it simulate in PROTEUS. It has been a struggle and I'm sure I am just missing something simple.

I've used the delay function, and managed to turn different LEDs on and off. But now trying with the timer, the LED turns on, but stays on and there is no other change to the simulation. I have tried using PORTBbits.RB4 and the same no change happens. I am using PROTEUS 8 and MPLAB X IDE.

The LED is connected to pin RB4/LB4 and GND with a 220R resistor.

void main(void)
{
    TRISB = 0;
    PORTB = 0;
    T0CON = 0x07;   
    T0CONbits.T08BIT = 1;
    T0CONbits.T0PS = 0b111;
    T0CONbits.PSA = 0;
    T0CONbits.T0SE = 1;
    INTCONbits.TMR0IE = 1;
    T0CONbits.T0CS = 0;
    TMR0H = 0xFF;
    TMR0L = 0x00;
    T0CONbits.TMR0ON = 1;
    
    while (1)
    {
        LATBbits.LB4 = 1;

        if (TMR0IF == 1)
        {
            LATBbits.LB4 = 0;
            TMR0IF = 0;
            T0CONbits.TMR0ON = 0;
        }
    }
}

Solution

  • Actually there is a change on the LED but not visible for us since it happens too quickly. Let's see what happens first by writing a step by step text algorithm of your while loop first.

    1. Init the system
    2. Set B4 pin high - The while loop starts from here
    3. Timer overflowed?
    4. No, then goto step 1 (keep setting B4 pin high)
    5. Yes, set B4 low, clear flag, turn off the timer and goto step 1 (keep setting B4 pin high)

    Have you seen something in the steps above? Especially in step 4?
    The B4 is actually set low, but after a few instructions the B4 is set back to high!
    If we assume that you use 4 MHz of oscillator frequency, the instruction cycle time would be:

    1/(4000000/4) = 1 microsecond
    

    Considering this instruction cycle time, there are only 2 instructions between where you set B4 low and set B4 back to high state. That is, there is only 2 visible instructions and in total 4 instruction if we count the assembly branching instruction so that the flow keeps going from the while loop. 4 instructions makes 4 microsecond delay between the output state change, make it to be quite invisible to us.

    Moreover the logic of the code is somewhat problematic to get to your goal. If you want to turn of the LED after the timer overflow and see the effect visually, you should not loop infinitely. Here is how:

    // Set B4 high once
    LATBbits.LB4 = 1;
    
    // Now wait for the Timer to overflow
    while(TMR0IF == 0)
     ;
    
    // If the flow reached here, then timer has overflowed
    TMR0IF = 0;
    T0CONbits.TMR0ON = 0;
    // Set B4 low
    LATBbits.LB4 = 0;
    
    // Halt here
    while(1)
     ;
    

    If your requierement implies you to loop though, then this code is not complete to do the job. If we consider your comment:

    I am trying to get it to turn off in response to the overflow flag that gets triggered when TMR0H overflows to the value of TMR0L. I then want the timer module to turn off too to save power to get the understanding of this functionality.

    You would put the processor to sleep to save power. Then your code with a loop would be complete as following:

    while (1)
    {
        LATBbits.LB4 = 1;
    
        if (TMR0IF == 1)
        {
            LATBbits.LB4 = 0;
            TMR0IF = 0;
            T0CONbits.TMR0ON = 0;
            
            // Put the processor to sleep (power saving)
            SLEEP();
        }
    }
    

    However this time, when the processor wakes up from the power saving state, the LED will turn on, but never turn off since the timer is disabled. Therefore the timer should keep running. You don't have to concern about the timer since the oscillator supply to the timer will cut off automatically and timer cannot keep running in sleep state when the system gets into the power saving state with the sleep instruction. Unless ofcourse you feed the timer clock externally via an IO pin.
    In the datasheet, section 9.4:

    Since Timer0 is shut down in Sleep mode, the TMR0 interrupt cannot awaken the processor from Sleep

    It is valid that the timer is shut down even you don't use interrupts. So we can omit disabling the timer to get the desired behaviour. The final code would be as following:

    while (1)
    {
        LATBbits.LB4 = 1;
    
        if (TMR0IF == 1)
        {
            LATBbits.LB4 = 0;
            TMR0IF = 0;
            
            // Put the processor to sleep (power saving)
            SLEEP();
        }
    }