2
\$\begingroup\$

I am trying to blink an LED using one of the PIC16F15214's timers and I have noticed a strange behaviour.

I have started the project following the PIC1000 (TB3261) guidelines for writing C code and I am using XC8, MPLAB X and MPLAB SNAP. I've built the circuit recommended in the microcontroller's datasheet on a breadboard and I have connected RA5 to an LED through a 1 kΩ resistor.

Here are the configuration bits:

// CONFIG1
#pragma config FEXTOSC = OFF    // External Oscillator Mode Selection bits (Oscillator not enabled)
#pragma config RSTOSC = HFINTOSC_1MHZ// Power-up Default Value for COSC bits (HFINTOSC (1 MHz))
#pragma config CLKOUTEN = OFF   // Clock Out Enable bit (CLKOUT function is disabled; I/O function on RA4)
#pragma config VDDAR = HI       // VDD Range Analog Calibration Selection bit (Internal analog systems are calibrated for operation between VDD = 2.3V - 5.5V)

// CONFIG2
#pragma config MCLRE = EXTMCLR  // Master Clear Enable bit (If LVP = 0, MCLR pin is MCLR; If LVP = 1, RA3 pin function is MCLR)
#pragma config PWRTS = PWRT_OFF // Power-up Timer Selection bits (PWRT is disabled)
#pragma config WDTE = OFF       // WDT Operating Mode bits (WDT disabled; SEN is ignored)
#pragma config BOREN = ON       // Brown-out Reset Enable bits (Brown-out Reset Enabled, SBOREN bit is ignored)
#pragma config BORV = LO        // Brown-out Reset Voltage Selection bit (Brown-out Reset Voltage (VBOR) set to 1.9V)
#pragma config PPS1WAY = ON     // PPSLOCKED One-Way Set Enable bit (The PPSLOCKED bit can be set once after an unlocking sequence is executed; once PPSLOCKED is set, all future changes to PPS registers are prevented)
#pragma config STVREN = ON      // Stack Overflow/Underflow Reset Enable bit (Stack Overflow or Underflow will cause a reset)

// CONFIG3

// CONFIG4
#pragma config BBSIZE = BB512   // Boot Block Size Selection bits (512 words boot block size)
#pragma config BBEN = OFF       // Boot Block Enable bit (Boot Block is disabled)
#pragma config SAFEN = OFF      // SAF Enable bit (SAF is disabled)
#pragma config WRTAPP = OFF     // Application Block Write Protection bit (Application Block is not write-protected)
#pragma config WRTB = OFF       // Boot Block Write Protection bit (Boot Block is not write-protected)
#pragma config WRTC = OFF       // Configuration Registers Write Protection bit (Configuration Registers are not write-protected)
#pragma config WRTSAF = OFF     // Storage Area Flash (SAF) Write Protection bit (SAF is not write-protected)
#pragma config LVP = ON         // Low Voltage Programming Enable bit (Low Voltage programming enabled. MCLR/Vpp pin function is MCLR. MCLRE Configuration bit is ignored.)

// CONFIG5
#pragma config CP = OFF         // User Program Flash Memory Code Protection bit (User Program Flash Memory code protection is disabled)

Here is some working code which makes the LED blink approximately every second using TMR0 (in order to expand my knowledge I have decided not to use the __delay_ms() function):

#include <xc.h>

void main(void) {
    /* Oscillator configuration */
    OSCEN |= _OSCEN_HFOEN_MASK; // HFINTOSC explicitly enabled, MFINTOSC, LFINTOSC and ADCRC disabled
    OSCFRQ &= ~_OSCFRQ_FRQ_MASK; // 1 MHz oscillator frequency
    while (!(OSCSTAT & _OSCSTAT_HFOR_MASK)); // Wait until HFINTOSC is ready to be used

    /* TMR0 configuration */
    T0CON0 = (T0CON0 & ~_T0CON0_EN_MASK) // Disable TMR0
            | _T0CON0_MD16_MASK // Set 16 bit operation mode
            & ~_T0CON0_OUTPS3_MASK // Set 1:4 postscaler (0b0011)
            & ~_T0CON0_OUTPS2_MASK
            | _T0CON0_OUTPS1_MASK
            | _T0CON0_OUTPS0_MASK;
    T0CON1 = (T0CON1 & ~_T0CON1_CS2_MASK) // Set FOSC/4 clock source
            | _T0CON1_CS1_MASK
            & ~_T0CON1_CS0_MASK
            & ~_T0CON1_ASYNC_MASK // Set sync mode
            & ~_T0CON1_CKPS_MASK; // Set 1:1 prescaler
    T0CON0 |= _T0CON0_EN_MASK; // Enable TMR0 which toggles every (0xFFFF)*OUTPS*1/(FOSC/4)

    /* GPIO configuration */
    PORTA = 0x00; // Clear PORTA
    LATA = 0x00; // Clear LATA
    ANSELA = 0x00; // Clear ANSELA (set RAx as digital I/O)
    TRISA = 0x00; // Clear TRISA (set RAx as OUTPUT)

    while (1) {
        if (T0CON0bits.OUT) {
            LATAbits.LATA5 = ~LATAbits.LATA5;
        }
    }
}

I have then edited the main loop in order to use bitmasks there too, getting this (which still works as expected):

while (1) {
    if (T0CON0 & _T0CON0_OUT_MASK) {
        LATA |= _LATA_LATA5_MASK;
    } else {
        LATA &= ~_LATA_LATA5_MASK;
    }
}

Since I want to simply toggle an LED, I have thought that using a bitwise XOR operator would have been smarter, so I've changed the main loop again:

while (1) {
    if (T0CON0 & _T0CON0_OUT_MASK) {
         LATA ^= _LATA_LATA5_MASK;
    }
}

Here comes the strange behaviour: when using the last loop code, sometimes the LED varies its brightness instead of going LOW (respecting the temporal pattern), keeps doing this for 3-5 cycles, then turns off and restarts working as expected for something like 3-5 cycles; this behaviour repeats itself over time. I have tried to change the pin and the optimization level, without success.

I can't understand why this happens and, unfortunately, I don't have neither an oscilloscope nor a logic analyzer to see what is going on, so I hope somebody will be able to point out my errors (I am a newbie, so apologies for the rookie mistakes you will spot).


EDIT What I am really trying to understand is why the two following pieces of code, which as far as I know should do the same thing, namely switching on and off the LED very quickly when the condition is met, result in two different behaviours:

while (1) {
    if (T0CON0bits.OUT) {
        LATAbits.LATA5 = ~LATAbits.LATA5;
    }
}
while (1) {
    if (T0CON0 & _T0CON0_OUT_MASK) {
         LATA ^= _LATA_LATA5_MASK;
    }
}

Here are the assembly equivalents of the two different versions:

!        if (T0CON0bits.OUT) {
0x7E9: MOVLB 0xB
0x7EA: BTFSS T0CON0, 0x5
0x7EB: GOTO 0x7ED
0x7EC: GOTO 0x7EE
0x7ED: GOTO 0x7E9
!            LATAbits.LATA5 = ~LATAbits.LATA5;
0x7EE: BCF STATUS, 0x0
0x7EF: MOVLB 0x0
0x7F0: BTFSS LATA, 0x5
0x7F1: BSF STATUS, 0x0
0x7F2: BTFSC STATUS, 0x0
0x7F3: GOTO 0x7F5
0x7F4: GOTO 0x7F8
0x7F5: MOVLB 0x0
0x7F6: BSF LATA, 0x5
0x7F7: GOTO 0x7FA
0x7F8: MOVLB 0x0
0x7F9: BCF LATA, 0x5
0x7FA: GOTO 0x7E9
0x7FB: MOVLP 0x0
!        if (T0CON0 & _T0CON0_OUT_MASK) {
0x7F0: MOVLB 0xB
0x7F1: BTFSS T0CON0, 0x5
0x7F2: GOTO 0x7F4
0x7F3: GOTO 0x7F5
0x7F4: GOTO 0x7F0
!            LATA ^= _LATA_LATA5_MASK;
0x7F5: MOVLW 0x20
0x7F6: MOVWF 0x5F0
0x7F7: MOVF 0x5F0, W
0x7F8: MOVLB 0x0
0x7F9: XORWF LATA, F
0x7FA: GOTO 0x7F0
0x7FB: MOVLP 0x0
\$\endgroup\$
6
  • \$\begingroup\$ In your code you have a comment that T0CON0bits.OUT "Toggles every (0xFFFF)*OUTPS*1/(FOSC/4)" - which implies that 50% of the time it's high and 50% of the time it's low. In your XOR version, you're toggling your LATA5 whenever T0CON0bits.OUT is high - so the effect will be that for 50% of the time it'll be toggling as fast as the loop runs, and the other 50% of the time it'll stay in whatever state it was in the last time the loop ran. \$\endgroup\$
    – brhans
    Commented Aug 16, 2022 at 20:59
  • \$\begingroup\$ @brhans Thank you, I have moved that comment since in that line it was quite misleading. The behaviour you have described for the XOR version is correct, however the same behaviour, if I am not mistaken, should be exhibited by the bit union version, even if I can't see it with my eyes due to the high switching frequency of the LED. What I am not understanding is why two pieces of code which should do the same thing result in two different behaviours. \$\endgroup\$
    – gwn
    Commented Aug 16, 2022 at 21:25
  • \$\begingroup\$ If built on a breadboard, throw in a (much slower) crystal, and/or otherwise decrease the clock rate slow enough that you can visually see what the LED is doing. PICs will clock all the way down to DC frequency i.e. halted. Can also step-through in simulation or a debugger for most PICs and see what the values are doing. \$\endgroup\$
    – rdtsc
    Commented Aug 16, 2022 at 21:44
  • \$\begingroup\$ @rdtsc Yes, this is what I am planning to do as soon as possible, thank you \$\endgroup\$
    – gwn
    Commented Aug 16, 2022 at 21:53
  • \$\begingroup\$ It would probably help if you compared the compiled assembler code for your different while loops so you can see how many instructions each of them take to execute for each case (.OUT either high or low). Then compare the while loop execution times with the timer period and see home many iterations of the different loops occur in one timer period - you'll almost certainly end up with some non-integer result. So it's not easily determinable what state the LED output will be left in when the timer's .OUT goes low. \$\endgroup\$
    – brhans
    Commented Aug 16, 2022 at 21:54

1 Answer 1

4
\$\begingroup\$

This code:

while (1) {
    if (T0CON0 & _T0CON0_OUT_MASK) {
        LATA |= _LATA_LATA5_MASK;
    } else {
        LATA &= ~_LATA_LATA5_MASK;
    }
}

makes LATA5 the same as T0CON0_OUT. I assume that LATA5 is the LED. I assume that T0CON0 & _T0CON0_OUT_MASK flashes on and off according to the timer. I didn't investigate the way the timer is set up, just the loop itself.

T0CON0 goes on, LED goes on, LED goes on, LED goes on, LED goes on, ...., T0CON0 goes off, LED goes off, LED goes off, LED goes off, LED goes off, .....

Whereas this code:

while (1) {
    if (T0CON0 & _T0CON0_OUT_MASK) {
         LATA ^= _LATA_LATA5_MASK;
    }
}

causes the LED to flash very quickly when T0CON0 is on, otherwise it doesn't change.

T0CON0 goes on, LED goes on, LED goes off, LED goes on, LED goes off, ...., T0CON0 goes off, LED stays off (or on), LED stays off (or on), LED stays off (or on), ....

When the LED is supposed to be on, the loop actually makes it flash very quickly; when the LED is supposed to be off, the loop does nothing (the LED stays on if it was already on, or stays off when it was already off).

Very quickly means something like a million times a second. You cannot see that with your eyes, but the LED looks dimmer because it is only lit up half the time. And when T0CON0 is off, the LED is in a random state, because it's unpredictable whether the LED is on or off when T0CON0 changes to off.

Solution: change the code back to the code that works. You wrote code that did what you wanted it to; then you wrote code that doesn't do what you want it to do; so change it back to the code that does.


You also asked why the ~ code gives a different flashing pattern than the ^= code. Hard to say without detailed analysis but I bet it's because of the speed of the loop versus the speed of the timer. If the loop runs let's say 9999999.9 times per flash, then usually it'll invert an even number of times and the LED still stay the same at the end as it did at the beginning, but 1 out of 10 times it will miss a loop and the LED will invert. The different loop generates a different number of instructions which take a different amount of time per loop, so you get a different number of loops per timer cycle.

\$\endgroup\$
7
  • \$\begingroup\$ Thank you for your answer, I have understood that the code version with AND and OR and the one with XOR actually do different things, so, as I have pointed out in my edited question, I am trying to understand why the code version with XOR behaves differently from the version with bit unions. \$\endgroup\$
    – gwn
    Commented Aug 16, 2022 at 22:03
  • \$\begingroup\$ @gwn it behaves differently because it's different code with different timing. \$\endgroup\$
    – brhans
    Commented Aug 16, 2022 at 23:04
  • \$\begingroup\$ @brhans I have compared the assembler code of both the XOR and the bit unions code and, despite not knowing the language, it looks like to me that the XOR code translates to lesser instructions than the bit unions one; am I correct asserting that this discrepancy is what explains the different timings you mentioned? \$\endgroup\$
    – gwn
    Commented Aug 16, 2022 at 23:33
  • \$\begingroup\$ @gwn You could enhance your question, if you edit your question and add the resulting assembly code of the different alternatives. -- Any yes, the type and number of instructions (and generally, their number of system cycles) determines the necessary runtime. \$\endgroup\$ Commented Aug 17, 2022 at 6:11
  • 1
    \$\begingroup\$ @Unimportant Yes, I have tried and the LED behaves the same with ~ or !. \$\endgroup\$
    – gwn
    Commented Aug 17, 2022 at 13:01

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.