Search code examples
ctimerinterruptatmega

TCC timer stops apparently without reason on ATXMEGA16E5


I hope you can help me with this very strange issue. I have synthesized my code as much as possible here.

On a statistic of 10 identical devices, the exact same issue happens on about half of them, while the others work without problems.

I use uart to send text commands to the ATXMEGA16E5.

Each text command is processed by “myFunction” that updates a specific value of the array “Values” at index “myIndex”.

Only when “myIndex” is equal to 3 and the “InputValue” function is executed (see the arrows on the right of the code), the TCC4 timer stops. Apparently, there isn’t any connection between the function and the timer.

If I turn off and then on and use the same device with all the other values of “myIndex” the issue does not arise.

I checked both “CommaIndex” and “InputValue” functions on a separate dummy C program and they are ok.

Thank you in advance.

#define BUFLEN 50       // for UART
#define CHARGE_LVLs     10 
char Rxbuf[BUFLEN];
volatile uint16_t Values[LVLs] = {12400, 4600, 2250, 1150, 430, 210, 15980, 15630, 15000, 13700};

int main(void)
{
    ...
    RTCInit();
    IRQInit(1, 1, 1);
    TimerInit();
    ...
    sei();
    
    while(1) 
    {
        ... 
        if(Rxflag==1)
        {
            if (strstr(Rxbuf, "myString,"))
                myFunction();
            Rxflag=0;
        }

    }
}

void myFunction()
{
    if(strstr(Rxbuf, ",abc,")) {myIndex=0; isUpdated = 1;}
    if(strstr(Rxbuf, ",def,")) {myIndex=1; isUpdated = 1;}
    if(strstr(Rxbuf, ",ghi,")) {myIndex=2; isUpdated = 1;}
    if(strstr(Rxbuf, ",lmn,")) {myIndex=3; isUpdated = 1;} // <------
    if(strstr(Rxbuf, ",opq,")) {myIndex=4; isUpdated = 1;}
    if(strstr(Rxbuf, ",rst,")) {myIndex=5; isUpdated = 1;}
    if(strstr(Rxbuf, ",uvz,")) {myIndex=6; isUpdated = 1;}
    if(strstr(Rxbuf, ",123,")) {myIndex=7; isUpdated = 1;}
    if(strstr(Rxbuf, ",456,")) {myIndex=8; isUpdated = 1;}
    if(strstr(Rxbuf, ",789,")) {myIndex=9; isUpdated = 1;}
    
    if(isUpdated == 1)
    {
        if(strstr(Rxbuf, WRITE_CALIB))
        {
            comma_index = CommaIndex(s, BUFLEN);
            if (InputValue(s, comma_index) > MAXVALUE) // <------
                Values[myIndex] = MAXVALUE;
            else
                Values[myIndex] = InputValue(s, comma_index);
            
            answ=1;
            UsartSendString(OK);
        }
    }
    
    UsartSendString("\r\n");
}

char CommaIndex(char s[], char strlength)
{
    int i;
    
    for(i=5; i<strlength; i++)  
        if (s[i] == ',')
        break;
    return (char)i;
}

uint16_t InputValue(char s[], char comma_index)
{
    return atoi(s+comma_index+1);
}

void IRQInit(char hi, char med, char lo) {
    PMIC.CTRL = (hi<<PMIC_HILVLEN_bp)|(med<<PMIC_MEDLVLEX_bp)|(lo<<PMIC_LOLVLEN_bp);
}

void RTCInit(void) {
    RTC.PER = 100;                              // 100ms
    RTC.INTCTRL = RTC_OVFINTLVL_MED_gc;         
    RTC.CTRL = RTC_PRESCALER_DIV1_gc;           // No prescaler
}

void TimerInit(void)
{
    TCC4.CTRLA =  TC_CLKSEL_DIV64_gc;           // CLKSEL=DIV64 : 32MHz/64=500KHz
    TCC4.PER = 4;                               // 500KHz/4 = 125KHz        @ 32 MHz
    TCC4.INTCTRLA = TC_CCAINTLVL_HI_gc;         // Int pri HI 
    TCC4.CTRLGCLR = 0b00000000;                 // Up direction
}

// *** Timer Overflow: 125KHz (8us) ***
ISR(TCC4_OVF_vect)
{
    if(emission)
    {
        PULSE_ON;
        
        emission = 0;
    }
    else
    {
        PULSE_OFF;
    }
    
    //************************************
    // Only for debug *********
    interruptCounter++;
    
    if (interruptCounter > 125000)
    {
        UsartSendString("Running");
        interruptCounter = 0;
    }
    //************************************

    TCC4.INTFLAGS = 1;
}

********************* UPDATE ***************************************

Sorry for my long silence but I needed some time to perform some tests suggested by Craig Estey that I sincerely thank for his kind attention.

I try to answer his questions and provide some comment and code for clarity.

“strstr is string func so it need a 0x00 terminator at the end” -> I forced the 0x00 terminator at the end of the string in RxBuf. The issue stays.

The format of the message sent to the atmega is: cmnd,lmn,1000 where cmnd is always a 4 chars word, lmn is one of the combinations provided in myFunction (see above), 1000 is a number between 0 and 16383.

I know when the buffer has a full message checking the ‘\n’ char that is always present.

I am sure that myFunction is called once and it is undisturbed during the process for a given message. I doubled checked that.

Rx ISR does not drop incoming data, even when the issue occurs, because the Values array is correctly updated.

What I have seen is:

  • If in myFunction the two UsartSendString lines are commented the issue does not occur.
  • If in UsartTxChar I add the 1ms delay (see below), the issue does not occur even leaving the two UsartSendString lines in myFunction UNcommented. This could be a solution actually, but I am not sure that it is the real solution and not simply a workaround.
void
UsartInit(void)
{

    USARTC0.CTRLA = USART_DRIE_bm | USART_RXCINTLVL_MED_gc; // RX Int + Med pri
    USARTC0.CTRLB = USART_RXEN_bm | USART_TXEN_bm;  // RX & TX enable, Clk 1x
    USARTC0.CTRLC = USART_CMODE_ASYNCHRONOUS_gc | USART_CHSIZE_8BIT_gc; // Asynch, 8 bit, 1 stop, par none
    USARTC0.CTRLD = 0;                  // No encoding
    USARTC0.BAUDCTRLA = 51;             // BSEL
    USARTC0.BAUDCTRLB = 0b00100000;     // BSCALE 2 @ 32MHz
}

void
UsartTxChar(char b)
{
    USARTC0.DATA = b;

    _delay_ms(1);  // <---- THIS SOLVES THE ISSUE *****************

    while (!(USARTC0.STATUS & USART_TXCIF_bm)) {
    };

}

void
UsartSendString(char *b)
{
    int i;

    i = 0;
    while (b[i]) {
        UsartTxChar(b[i]);
        i++;
    }
}

ISR(USARTC0_RXC_vect)
{

    Rxchar = USARTC0.DATA;

    if (!Rxflag) {
        Rxbuf[Rxpnt] = Rxchar;
        Rxpnt++;
        if (Rxpnt == BUFLEN)
            Rxpnt--;
    }

    if (Rxchar == '\n') {
        Rxflag = 1;
        Rxpnt = 0;
    }
}

Solution

  • So, if you agree, you can write your answer, providing a little bit of explanation, and I will be glad to mark it as the answer of this discussion. – user1941332

    Okay. Most of this was covered in the top comments.

    If adding a delay to code that intermittently fails "fixes" the issue, then this means the program isn't fixed. The real issue is just masked.

    Here is your transmit code:

    void
    UsartTxChar(char b)
    {
        USARTC0.DATA = b;
    
        _delay_ms(1);                       // <---- THIS SOLVES THE ISSUE *****************
    
        while (!(USARTC0.STATUS & USART_TXCIF_bm)) {
        };
    
    }
    

    Correct USART Transmit Function points out the issue. We must spin on the status register, waiting for the transmit buffer to clear before we output a new character to the transmitter.

    And, the status bit you were testing wasn't the correct one. Here is the refactored code:

    void
    UsartTxChar(char b)
    {
    
        while (! (USARTC0.STATUS & USART_DREIF_bm));
    
        USARTC0.DATA = b;
    }