Bug related to UART FIFO under multi-tasks

2019-06-14 16:46发布

Program attached: Those codes are written by me based on the TI uart.c driver library.

        while(1){
        //check if message on Queue -> read or check UART input
        if(uxQueueMessagesWaiting( UART_TASKQ ) != 0){ //may have bugs

        // deQueue
        xQueueReceive( UART_TASKQ, &UARTTaskHandle, 0x0A );
        //do the task's mission using the data in the stucture(put by control 
        task)
        //Print out the input data.

        //**********debugging data
/*
        testPointer = UARTTaskHandle->dataBuffer;
        testAmount = UARTTaskHandle->dataSize;

        while(testAmount){
            if(*testPointer != 1){
                error = error + 1;
            }
            if(*(testPointer + 1) != 2){
                error = error + 1;
            }
            if(*(testPointer + 2) != 3){
                            error = error + 1;
                        }
            if(*(testPointer + 3) != 4){
                            error = error + 1;
                        }
            if(*(testPointer + 4) != 5){
                            error = error + 1;
                        }
            if(*(testPointer + 5) != 6){
                            error = error + 1;
                        }
            if(*(testPointer + 6) != 7){
                            error = error + 1;
                        }
            if(*(testPointer + 7) != 8){
                            error = error + 1;
                        }

            testPointer = testPointer + 8;
            testAmount = testAmount - 8;
        }
*/

        if(UART_write(UART_Handle, UARTTaskHandle->dataBuffer, UARTTaskHandle->dataSize, 0xff ) >= 0){
        UARTwriteCount = UARTwriteCount + 1;
        }

        //let control task take new command
        //free allocated memory
        free(UARTTaskHandle->dataBuffer);
        free(UARTTaskHandle); // free memory space

//(above is code using UART)


//here are UART driver code:


     unsigned long UARTStatus(unsigned long ulBase){
    ASSERT(UARTBaseValid(ulBase));
    return(HWREG(ulBase + UART_O_FR));
}
//*****************************************
//UART_ISR
//Interrupt service routine for
//the UART read and write process
//*****************************************
void UART_ISR(){


    //read FIFO full or read time out
    if(UARTIntStatus(UART_Handle->UART_PORT,false) & (UART_INT_RX | UART_INT_RT)){
        UARTIntClear(UART_Handle->UART_PORT, UART_INT_RX | UART_INT_RT);    //clear INT flag

        while (!(UARTStatus(UART_Handle->UART_PORT) & UART_FR_RXFE)){
            //data reading
            *UART_Handle->pCurrentRead = UARTCharGet(UART_Handle->UART_PORT); //read autoly clear INT
            UART_Handle->pCurrentRead++;
            UART_Handle->ReadLength--;

            //adjust code here:
            if(UART_Handle->ReadLength == 0){
                break;
            }
        }

        //check if read certain bytes finished
        if(UART_Handle->ReadLength == 0){
            memcpy(UART_Handle->dataput, UART_Handle->pReadBuf,UART_Handle->ReadLengthcpy); // copy data back
            xSemaphoreGiveFromISR( UART_Handle->UARTRead_Semaphore, &xHigherPriorityTaskWoken );// release semaphore
            portYIELD_FROM_ISR( xHigherPriorityTaskWoken );//forcing context exchange
        }

    }

    //send FIFO empty
    if(UARTIntStatus(UART_Handle->UART_PORT,false) & UART_INT_TX){

        UARTIntClear(UART_Handle->UART_PORT, UART_INT_TX);  //clear INT flag

        if(UART_Handle->WriteLength == BUFFEMPTY){

            UART_Handle->UART_SendComplete = true;

            xSemaphoreGiveFromISR( UART_Handle->UARTWrite_Semaphore, &xHigherPriorityTaskWoken );// release semaphore
            portYIELD_FROM_ISR( xHigherPriorityTaskWoken );//forcing context exchange
        }

            //putting data into send FIFO
                if(UART_Handle->WriteLength > FIFOMAX){
                    for( Cindex = 0 ; Cindex < FIFOMAX ;){
                        if(UARTCharPutNonBlocking(UART_Handle->UART_PORT, *(UART_Handle->pCurrentWrite))){//write autoly clear INT
                            (UART_Handle->pCurrentWrite) = (UART_Handle->pCurrentWrite) + 1;
                            (UART_Handle->WriteLength) = (UART_Handle->WriteLength) - 1;
                            Cindex = Cindex + 1;
                            UART_Handle->sentCount = UART_Handle->sentCount + 1;
                        }
                    }
                }else{
                    templength = UART_Handle->WriteLength;
                    for( Cindex = 0; Cindex < templength ;){
                        if(UARTCharPutNonBlocking(UART_Handle->UART_PORT, *(UART_Handle->pCurrentWrite))){//write autoly clear INT
                            (UART_Handle->pCurrentWrite) = (UART_Handle->pCurrentWrite) + 1;
                            (UART_Handle->WriteLength) = (UART_Handle->WriteLength) - 1;
                            Cindex = Cindex + 1;
                            UART_Handle->sentCount = UART_Handle->sentCount + 1;
                        }
                    }
                }

    }

}

//*****************************************
//UART_write
//write certain length of data to UART port
//*****************************************
int32_t UART_write( UART_STATE *UART_Handle, uint8_t *pData, uint32_t length, uint32_t time_out ){


    while(!UART_Handle->UART_SendComplete); //debugging purpose
    UART_Handle->UART_SendComplete = false;//debugging purpose

    UART_Handle->WriteLength = length;

    if(UART_Handle->WriteLength <= UART_Handle->WriteBufSize){

        UARTIntClear(UART_Handle->UART_PORT, UART_INT_TX);  //clear INT flag

        memcpy(UART_Handle->pWriteBuf,pData,UART_Handle->WriteLength); //copy data into writebuff
        UART_Handle->pCurrentWrite = UART_Handle->pWriteBuf;
        //putting data into send FIFO
        if(UART_Handle->WriteLength > FIFOMAX){
            // if
            for( Cindex = 0 ; Cindex < FIFOMAX ;){
                if(UARTCharPutNonBlocking(UART_Handle->UART_PORT, *(UART_Handle->pCurrentWrite))){//write autoly clear INT
                    (UART_Handle->pCurrentWrite) = (UART_Handle->pCurrentWrite) + 1;
                    (UART_Handle->WriteLength) = (UART_Handle->WriteLength) - 1;
                    Cindex = Cindex + 1;
                    UART_Handle->sentCount = UART_Handle->sentCount + 1;
                }
            }
        }else{
            for( Cindex = 0 ; Cindex < FIFOMAX ;){
                if(UARTCharPutNonBlocking(UART_Handle->UART_PORT, *(UART_Handle->pCurrentWrite))){//write autoly clear INT
                    (UART_Handle->pCurrentWrite) = (UART_Handle->pCurrentWrite) + 1;
                    (UART_Handle->WriteLength) = (UART_Handle->WriteLength) - 1;
                    Cindex = Cindex + 1;
                    UART_Handle->sentCount = UART_Handle->sentCount + 1;
                }
            }
        }

        //start sending
        UARTEnable(UART_Handle->UART_PORT);

        if(UART_Handle->UARTWrite_Semaphore != NULL ) {
            if(xSemaphoreTake(UART_Handle->UARTWrite_Semaphore, time_out/(portTICK_PERIOD_MS) ) == pdTRUE){
                reValue = WRITESUCCESS; //wait return till write complete

            }else{
                reValue = WRITETIMEOUT; // timeout (ms)

                 }
        }else{
            while(1); //no Semaphore
             }

        return reValue;

    }else{
        return FAILURE; //wrong length
    }

}

//*****************************************
//UART_read
//read certain length of data from UART port
//*****************************************
int32_t UART_read(UART_STATE *UART_Handle, uint8_t *pData, uint32_t length, uint32_t time_out){



    //later added part
    UARTDisable(UART_Handle->UART_PORT);    //clearUART
    UARTFIFOEnable(UART_Handle->UART_PORT);
    //

    UART_Handle->ReadLength = length;   // set readlength
    UART_Handle->ReadLengthcpy = length;

    if(UART_Handle->ReadLength <= UART_Handle->ReadBufSize){

        UARTIntClear(UART_Handle->UART_PORT, UART_INT_RX | UART_INT_RT);    //clear INT flag
        UART_Handle->dataput = pData; //store the destination buffer address
        UART_Handle->pCurrentRead = UART_Handle->pReadBuf; //set current read

        UARTEnable(UART_Handle->UART_PORT); //start receiving

        //suspend before read ISR finish whole process
        if(UART_Handle->UARTRead_Semaphore != NULL ) {
                    if(xSemaphoreTake(UART_Handle->UARTRead_Semaphore, time_out/(portTICK_PERIOD_MS) ) == pdTRUE){
                        reValue = READSUCCESS; //wait return till write complete
                    }else{
                        reValue = READTIMEOUT; // timeout (ms)
                         }
                }else{
                    while(1); //no Semaphore
                     }

                return reValue;

    }else{
        return FAILURE; //wrong length
    }
}

//*****************************************
//UART_open
//open UART for certain port and bandrate
//*****************************************
UART_HANDLE UART_open(uint32_t UART_port, uint32_t UART_portperiph, uint32_t UART_baudrate){

    //initialize structure
    UART_Handle = (UART_HANDLE)malloc(sizeof(UART_STATE));

    UART_Handle->ReadBufSize = UARTBUFFERSIZE;
    UART_Handle->WriteBufSize = UARTBUFFERSIZE;
    UART_Handle->UART_PORT = UART_port;
    UART_Handle->UART_PORTPERIPH = UART_portperiph;
    UART_Handle->UART_BRATE = UART_baudrate;
    UART_Handle->pWriteBuf = (uint8_t*)malloc(UART_Handle->WriteBufSize * sizeof(uint8_t));
    UART_Handle->pReadBuf = (uint8_t*)malloc(UART_Handle->ReadBufSize * sizeof(uint8_t));
    UART_Handle->pCurrentWrite = UART_Handle->pWriteBuf;
    UART_Handle->pCurrentRead = UART_Handle->pReadBuf;
    UART_Handle->UARTWrite_Semaphore = NULL;
    UART_Handle->UARTRead_Semaphore = NULL;
    UART_Handle->UARTprotect_Semaphore = NULL;
    UART_Handle->UART_SendComplete = true;

    UART_Handle->sentCount = 0;//debugging purpose

    vSemaphoreCreateBinary( UART_Handle->UARTWrite_Semaphore ); //semaphore create
    vSemaphoreCreateBinary( UART_Handle->UARTRead_Semaphore ); //semaphore create
//  vSemaphoreCreateBinary( UART_Handle->UARTprotect_Semaphore ); //debugging purpose
    xSemaphoreTake( UART_Handle->UARTRead_Semaphore, portMAX_DELAY );   //semaphore take
    xSemaphoreTake( UART_Handle->UARTWrite_Semaphore, portMAX_DELAY );  //semaphore take

    // Enable Peripheral Clocks
    MAP_PRCMPeripheralClkEnable(UART_Handle->UART_PORTPERIPH, PRCM_RUN_MODE_CLK);

    // Configure PIN_55 for UART0 UART0_TX
    MAP_PinTypeUART(PIN_55, PIN_MODE_3);

    // Configure PIN_57 for UART0 UART0_RX
    MAP_PinTypeUART(PIN_57, PIN_MODE_3);

    // configuration, 8 bits length data width, 1 stop bit, no parity check
    UARTConfigSetExpClk(UART_Handle->UART_PORT,PRCMPeripheralClockGet(  UART_Handle->UART_PORTPERIPH),
            UART_Handle->UART_BRATE, (UART_CONFIG_WLEN_8 | UART_CONFIG_STOP_ONE |
             UART_CONFIG_PAR_NONE));

    // disable UART since function above contained UARTenable
    UARTDisable(UART_Handle->UART_PORT);

    UARTIntEnable(UART_Handle->UART_PORT, UART_INT_TX | UART_INT_RX | UART_INT_RT); // enable interrupt for send and receive and receive timeout
    UARTIntRegister(UART_Handle->UART_PORT, UART_ISR);  //hook ISR
    UARTFIFOEnable(UART_Handle->UART_PORT); //enable FIFO for send and receive
    UARTFIFOLevelSet(UART_Handle->UART_PORT, UART_FIFO_TX1_8, UART_FIFO_RX4_8); //Interrupt occur when 7 bytes send from FIFO or read in FIFO


    return UART_Handle;
}

I am dealing with a multi-thread bug for long time. After many test, I aware that the bug most possibly related to the UART FIFO transmitting.

Here is my description of my program: The program is based on Free-RTOS. It has two threads running in parallel. one thread is reading from sensor by I2C driver. another is sending data from the reading task to the computer by UART driver. I have a multi-task frame to transfer data from reading task to sending task. I have a timer file to control real time measuring. My I2C driver and UART driver are all interrupt based.

Problem description: My data reading is good only for 2 mins. after that, my data shift. When do testing, I blocked the I2C driver, only put constant data "1 2 3 4 5 6 7 8" into the I2C read buffer, and transfer them to sending task to do UART send. And after 2 mins, my data read out will become "8 1 2 3 4 5 6 7" and after the first change, later changes happen quickly, "7 8 1 2 3 4 5 6" and continue till the end of timing. this is plot of one channel of my data, begin with 8 and changing

I set many test to my program already, and I am sure my multi-task frame (trans data from reading task to sending task) don't alter the data. Important observations: 1.when I put read and send into one thread, the data is very good, without this bug. 2.I set counting in my UART driver, found that the amount of sending is correct, here the sending is putting bytes into the TX FIFO. However, in the Excel, I read by a JAVA UART program, data missed. And JAVA UART program should be ok, since it works well when I use the single thread testing. 3. no memory leak error pump up by CCS while debugging.

So I am thinking, under the multi-thread environment, something stopped my UART TX FIFO transmit after I put bytes into that. But I can't find out where is wrong in my codes, they all seems correct.

And another reasonable reason can be something altered my pointer in the interrupt, in linked picture the "pCurrentWrite" pointer. But that pointer only accessed by UART driver, and before the interrupt finished, the next UART write can't enter.

I include partial code in the link, where I want to show that the data is put into the TX FIFO, and the send count is correct.

I can't include all codes, I don't think anyone want to check those long codes. and without running it, it's hard to find out where is wrong.

So, I just want to describe the situation i met, and see if someone had similar bug before. And maybe someone know the problem of UART FIFO under multi-thread environment. If someone who can help do want to see the code, we can do further contact.

thanks (My new account can't include picture yet, picture linked below)

This code is part of my UART driver, in the sending interrupt. I use the PutCharNonBlocking to put byte, since it return 1 if success, return 0 if FIFO full, so in this way I make sure the program will send all my data into the FIFO, which turn out to be true, all data was put into the TX FIFO

1条回答
孤傲高冷的网名
2楼-- · 2019-06-14 17:26

Although you have included the use of semaphores, I would suggest further reading on mutual exclusion and shared resources.

The solution is overly complex and it would be pertinent to better abstract your code and simplify.

Note: you should not cast the result of malloc.

UART_Handle = (UART_HANDLE)malloc(sizeof(UART_STATE));
查看更多
登录 后发表回答