This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

CC2540, loosing data on UART

Other Parts Discussed in Thread: CC2540

Hi,

I am trying to transfer data to a CC2540 device through the UART link and I observe data loss during the transfer.

My hardware setup is a CC2540EM plugged into a SmartRF05 rev 1.8.1 evaluation board. The evaluation board is connected to a PC through a serial cable.

I slightly modified the SimpleBLEPeripheral project (from the BLE-CC254x-1.2 software version) to include a UART test (see patch bellow). The test is very simple: it counts the number of bytes it received into the callback function gave to the HAL_UART component. The HAL_UART uses the underlying _hal_uart_dma component. Every 5 seconds it displays on the LCD display (line 1) the received amount of bytes. The test also checks for the HAL_UART_RX_FULL event. During the test, the cc2540 device remains in advertising mode.

Serial communication is set at 115200 bauds, 8 bits, no parity, no flow control. The serial speed and the flow control don’t matter. Whatever these values are, the issue remains.

On the PC side, I send through the serial connection the amount of 56 bytes every 10 ms at 115200 bauds. I am performing this operation 1000 times resulting in a transferred amount of 56000 bytes.

Once the transfer ends, the amount of received bytes displayed is different of 56000 and varies each time the test is run. Also no overflow in the receive buffer occurs, meaning that the problem resides in data acquisition from the hardware to the internal receive buffer.

I have tested DMA or IRQ methods and same results popped up. The problem is even worse when the POWER_SAVING option is defined.

 

Is here any way to ensure a correct transfer through the UART interface on the CC2540 device?

 

Marc

 

Patch file with UART modifications for the SimpleBLEPeripheral project.

 

*********************************************************************************

diff -rupN SimpleBLEPeripheral.org/CC2540DB/SimpleBLEPeripheral.ewp SimpleBLEPeripheral/CC2540DB/SimpleBLEPeripheral.ewp

--- SimpleBLEPeripheral.org/CC2540DB/SimpleBLEPeripheral.ewp         2012-01-11 11:40:18.000000000 +0100

+++ SimpleBLEPeripheral/CC2540DB/SimpleBLEPeripheral.ewp              2012-03-06 14:46:47.041232900 +0100

@@ -1326,10 +1326,11 @@

           <state>OSAL_CBTIMER_NUM_TASKS=1</state>

           <state>HAL_AES_DMA=TRUE</state>

           <state>HAL_DMA=TRUE</state>

-          <state>POWER_SAVING</state>

+          <state>xPOWER_SAVING</state>

           <state>xPLUS_BROADCASTER</state>

           <state>HAL_LCD=TRUE</state>

           <state>HAL_LED=FALSE</state>

+          <state>HAL_UART=TRUE</state>

         </option>

         <option>

           <name>CCPreprocFile</name>

diff -rupN SimpleBLEPeripheral.org/Source/simpleBLEPeripheral.c SimpleBLEPeripheral/Source/simpleBLEPeripheral.c

--- SimpleBLEPeripheral.org/Source/simpleBLEPeripheral.c       2012-02-01 10:05:42.000000000 +0100

+++ SimpleBLEPeripheral/Source/simpleBLEPeripheral.c            2012-03-06 15:51:00.409196600 +0100

@@ -50,6 +50,7 @@

 #include "hal_led.h"

 #include "hal_key.h"

 #include "hal_lcd.h"

+#include "hal_uart.h"

 

 #include "gatt.h"

 

@@ -252,6 +253,55 @@ static simpleProfileCBs_t simpleBLEPerip

   simpleProfileChangeCB    // Charactersitic value change callback

 };

 

+/* UART test */

+static void uart0CB(uint8 port, uint8 event);

+static halUARTCfg_t com0 =

+{

+  FALSE,

+  HAL_UART_BR_115200,

+  FALSE,

+  0,

+  0,

+  { 0, 0, 0, NULL, },

+  { 0, 0, 0, NULL, },

+  0,

+  0,

+  uart0CB,

+};

+

+static uint32 uart0RXBytes = 0;

+static uint32 uart0ErrPort = 0;

+static uint32 uart0ErrFull = 0;

+

+static void uart0CB(uint8 port, uint8 event)

+{

+  uint8 dummy[256];

+  uint16 bLen;

+  // some error checking

+  if (port != HAL_UART_PORT_0) {

+    uart0ErrPort++;

+    return;

+  }

+

+  // filter and counters

+  switch (event) {

+    case HAL_UART_RX_FULL:

+      uart0ErrFull++;

+      break;

+    case HAL_UART_RX_ABOUT_FULL:

+    case HAL_UART_RX_TIMEOUT:

+      break;

+    default:

+      return;

+  }

+  // just read uart in order to empty the buffer

+  bLen = Hal_UART_RxBufLen(HAL_UART_PORT_0);

+  if (bLen > 256) bLen = 256;

+  uart0RXBytes += HalUARTRead(HAL_UART_PORT_0, dummy, bLen);

+}

+

 /*********************************************************************

  * PUBLIC FUNCTIONS

  */

@@ -407,7 +457,9 @@ void SimpleBLEPeripheral_Init( uint8 tas

   HCI_EXT_MapPmIoPortCmd( HCI_EXT_PM_IO_PORT_P0, HCI_EXT_PM_IO_PORT_PIN7 );

 

 #endif // defined ( DC_DC_P0_7 )

+

+  HalUARTOpen(HAL_UART_PORT_0, &com0);

+

   // Setup a delayed profile startup

   osal_set_event( simpleBLEPeripheral_TaskID, SBP_START_DEVICE_EVT );

  

@@ -712,6 +764,16 @@ static void performPeriodicTask( void )

      */

     SimpleProfile_SetParameter( SIMPLEPROFILE_CHAR4, sizeof(uint8), &valueToCopy);

   }

+

+#if (defined HAL_LCD) && (HAL_LCD == TRUE)

+  if (uart0ErrFull) {

+    HalLcdWriteStringValue("OF:", (uint16)(uart0RXBytes), 10,  HAL_LCD_LINE_1);

+  } else if (uart0ErrPort) {

+    HalLcdWriteStringValue("PE:", (uint16)(uart0RXBytes), 10,  HAL_LCD_LINE_1);

+  } else {

+    HalLcdWriteValue(uart0RXBytes, 10,  HAL_LCD_LINE_1);

+  }

+#endif // (defined HAL_LCD) && (HAL_LCD == TRUE) 

 }

 

 /*********************************************************************

  • Hi,
    I am also having similar problem to receive UART data in BLE v1.2.
    I am now working on CC2540 on the Keyfob hardware, and trying to forward the data from UART bus to the USB donble via wireless.
    I have modified the HAL layer to use P1_5 and P1_4 as the UART port for UART 0 module, and UART data interrupt is being used.

    However, when I when try to receive 20 bytes 0xAA UART data, random data would usually be received in the callback function, like:
    AA AA AA EA 75 75 75 75 75 75 75 75 75 75 75 F5 00 00 00 00
    AA AA AA 9C 9D 9D 9D 9D 9D 9D 9D 9D 9D FD 75 F5 00 00 00 00

    Although sometimes correct data can be received, but not often.

    I am pretty sure that the baudrate is correct, as another processor can receive the correct test data from the keyfob through UART and then make the reply ( 20bytes 0xAA ).
    Is there any anyone has the UART port working properly on CC2540 in BLE v1.2, and may perhaps give me some advices?

    Thanks a lot!


    Some more changes have been made in _hal_uart_isr.c and _hal_uart_dma.c to support having P1 as the port of UART 0 module.
    I have attached a document to highlight those change.

    And here is the code:

    // this is the initalisation function
    void SerialComms_Init  (void)
    {
        uint8 port = HAL_UART_PORT_0;
        halUARTCfg_t conf;
       
        conf.baudRate = HAL_UART_BR_115200;    
        conf.flowControl = 0;
        conf.callBackFunc = SerialComms_UartRxCallBack;
        HalUARTInit();
        HalUARTOpen(port, &conf);
    }

    // this is the call back function to receive UART data.
    static void SerialComms_UartRxCallBack(uint8 port, uint8 event)
    {
        static uint8 s_RxBuf [128];   
        uint16 bufLen;   
        uint8 dataLength;
        uint8 uartData;
       
        uint8* pBuf;
       
        if( port!= HAL_UART_PORT_0 )//it should be my port
            return;

        switch (event)
        {
            //Intentionally drop through         
            case HAL_UART_RX_FULL:        
            case HAL_UART_RX_ABOUT_FULL:  
            case HAL_UART_RX_TIMEOUT:

                bufLen = Hal_UART_RxBufLen(HAL_UART_PORT_0);
                HalUARTRead(HAL_UART_PORT_0, s_RxBuf , bufLen);

                break;
        }
    }

    // this is the defined symbols in the Preprocessor of the project setting
    INT_HEAP_LEN=3072
    HALNODEBUG
    OSAL_CBTIMER_NUM_TASKS=1
    HAL_AES_DMA=TRUE
    POWER_SAVING
    xPLUS_BROADCASTER
    HAL_LCD=FALSE
    HAL_LED=TRUE
    CC2540_MINIDK
    HAL_UART
    HAL_DMA=TRUE
    HAL_UART_ISR=TRUE
    UART0_P1
    HAL_UART_DMA=0
    HAL_UART_ISR_RX_MAX=2504314.Changes in HAL_UART layer for UART0 at P1.doc

  • Hi,

    First of all, do not use POWER_SAVING ( In BLEv1.2.1 or older). 

    Secondly, when using a Baudrate of 115200 you should really consider using the DMA driver, which is done by using following defines:

    • HAL_UART
    • HAL_UART_DMA=TRUE
    • HAL_UART_ISR=0
    • HAL_UART_DMA=1
    Thirdly, make sure that HCI_EXT_ClkDivOnHaltCmd( HCI_EXT_ENABLE_CLK_DIVIDE_ON_HALT ); is commented out, you wouldn't wat to divide the clock when DMAs are in use. 
    Following these considerations you should have a reliable UART setup. Please indicate if you still loose bytes or experience any strange behaviour.
    If you do want to use flow control, please make sure that RTS/CTS are connected or else you will loose bytes since there's only one buffer.
    Br
  • Hi Nick,

    Thanks for the suggestions. Removing the HCI_EXT_ClkDivOnHaltCmd() does help to fix the corrupted data problem. Thank you very much.

    However, I am still having the problem of missing UART data in CC2540 when we send the UART data to it. I am sure that my CC2540 is always in high power mode throughout the test. And we have also monitored and confirmed that all the data is sent out properly to CC2540 at 115200bps.

    I have set the UART receive buffer to 512 bytes with the pre-processor macro:

    HAL_UART_ISR_RX_MAX=512  

    And each time only 128 bytes, from 0 -127, are sent to the CC2540. With the sufficiently big buffer of 512 bytes, we believe that there shouldn’t be any buffer overflow issue. However, I have never seen a successful receive of the complete 128 bytes. And most of the time 4- 20 bytes would be lost.

    I am using the software stack version of BLE1.2. And the UART data is received by interrupt subroutine without DMA. As you have suggested using DMA, I would like to know if the BLE1.2 also supports this feature. Or do I need to migrate to BLE v1.21 for this? As the software guideline of v1.2 states that HAL_UART_DMA is not supported.

    · HAL_UART_DMA – This symbol sets the CC2540/41 UART interface to use DMA mode, and should be either left undefined or set to 0. The CC2540/41 does not currently support this option.

    And also, as the v1.2 stack doesn’t support the use of P1 port as the communication port of the UART0 module, and I have already made the change for this. We can’t imagine how much more work will needed to setup the UART DMA properly if the software kit doesn’t support it. So we are keen to have any help so that the UART receive work properly with using DMA.

    At the bottom of this post is the code for the data test. The SerialComms_UartRxCallBack() is the call back function for the UART ISR. It doesn’t do anything heavy but just to check the packet of 128bytes and save the data for debugging purpose. If there is anything obviously stupid that you can spot, please let me know and I will be obliged!

    Br

     

    #define UART_RX_BUF_SIZE  250

     

    uint16 bufLen;   

    uint16 valueCnt = 0;

    static uint8 prevValue ;

    static uint8 s_RxBuf[UART_RX_BUF_SIZE];

    static uint8 s_UartRxDebugBuf[UART_RX_BUF_SIZE];

    static uint8 s_DebugIndex = 0;

     

    static void saveDebugData ( uint8 data )

    {

        s_UartRxDebugBuf [s_DebugIndex] = data;

        if ( ++s_DebugIndex >=  UART_RX_BUF_SIZE )

        {

            s_DebugIndex = 0;

        }   

    }

     

    static void SerialComms_UartRxCallBack(uint8 port, uint8 event)

    {  

        uint8* pBuf;

        uint8 missBytes;

       

        if( port!= HAL_UART_PORT_0 )//it should be my port

            return;

     

        switch (event)

        {

            // Intentionally drop through

            case HAL_UART_RX_TIMEOUT:    

            case HAL_UART_RX_FULL:        

            case HAL_UART_RX_ABOUT_FULL:  

               

                // get all the data and free the HAL uart buffer

                // if there is any data

                bufLen = Hal_UART_RxBufLen(HAL_UART_PORT_0);

                HalUARTRead(HAL_UART_PORT_0, s_RxBuf , bufLen);

                           

                pBuf = s_RxBuf;

     

                while (0 != bufLen)

                {

                    bufLen--;

     

                    if( *pBuf == 0x00 )     // the start of the test packet, reset those checking counters

                    {

                        valueCnt = 0;

                        prevValue = 0;

                    }

                    else if (*pBuf != 127 ) // in between the test packet

                    {

     

                        if(( *pBuf ) ==  ( prevValue + 1 ) )

                        {

                            // check if there is any discontinuity

                            valueCnt++;

                        }          

                        else

                        {

                            // check how many bytes a missed

                            missBytes = *pBuf - prevValue - 1;

                            while ( missBytes )

                            {

                                missBytes--;

                                // there is no 0xFF in the test data

                                // so stuff 0xFF to the missing byte to inidicate

                                saveDebugData ( 0xFF ) ; 

                            }              

                        }

                    }

                    else

                    {

                        // the end of the test packet, check if successive data is all received

                        if( valueCnt != 127)

                        {

                            Indigo_ToggleDebugPin();

                        }

     

                    }

     

                    // save the data in a ring buffer for debug

                    saveDebugData (  *pBuf ) ;  

                   

                    // save the previous value to check the continuity

                    prevValue = *pBuf++;

                   

                }

                break;

        }

    }

  • Hi Nick,

    Thanks for the suggestions. Removing the HCI_EXT_ClkDivOnHaltCmd() does help to fix the corrupted data problem. Thank you very much.

    However, I am still having the problem of missing UART data in CC2540 when we send the UART data to it. I am sure that my CC2540 is always in high power mode throughout the test. And we have also monitored and confirmed that all the data is sent out properly to CC2540 at 115200bps.

    I have set the UART receive buffer to 512 bytes with the pre-processor macro:

    HAL_UART_ISR_RX_MAX=512  

    And each time only 128 bytes, from 0 -127, are sent to the CC2540. With the sufficiently big buffer of 512 bytes, we believe that there shouldn’t be any buffer overflow issue. However, I have never seen a successful receive of the complete 128 bytes. And most of the time 4- 20 bytes would be lost.

    I am using the software stack version of BLE1.2. And the UART data is received by interrupt subroutine without DMA. As you have suggested using DMA, I would like to know if the BLE1.2 also supports this feature. Or do I need to migrate to BLE v1.21 for this? As the software guideline of v1.2 states that HAL_UART_DMA is not supported.

    · HAL_UART_DMA – This symbol sets the CC2540/41 UART interface to use DMA mode, and should be either left undefined or set to 0. The CC2540/41 does not currently support this option.

    And also, as the v1.2 stack doesn’t support the use of P1 port as the communication port of the UART0 module, and I have already made the change for this. We can’t imagine how much more work will needed to setup the UART DMA properly if the software kit doesn’t support it. So we are keen to have any help so that the UART receive work properly with using DMA.

    At the bottom of this post is the code for the data test. The SerialComms_UartRxCallBack() is the call back function for the UART ISR. It doesn’t do anything heavy but just to check the packet of 128bytes and save the data for debugging purpose. If there is anything obviously stupid that you can spot, please let me know and I will be obliged!

    Br

     

    #define UART_RX_BUF_SIZE  250

     

    uint16 bufLen;   

    uint16 valueCnt = 0;

    static uint8 prevValue ;

    static uint8 s_RxBuf[UART_RX_BUF_SIZE];

    static uint8 s_UartRxDebugBuf[UART_RX_BUF_SIZE];

    static uint8 s_DebugIndex = 0;

     

    static void saveDebugData ( uint8 data )

    {

        s_UartRxDebugBuf [s_DebugIndex] = data;

        if ( ++s_DebugIndex >=  UART_RX_BUF_SIZE )

        {

            s_DebugIndex = 0;

        }   

    }

     

    static void SerialComms_UartRxCallBack(uint8 port, uint8 event)

    {  

        uint8* pBuf;

        uint8 missBytes;

       

        if( port!= HAL_UART_PORT_0 )//it should be my port

            return;

     

        switch (event)

        {

            // Intentionally drop through

            case HAL_UART_RX_TIMEOUT:    

            case HAL_UART_RX_FULL:        

            case HAL_UART_RX_ABOUT_FULL:  

               

                // get all the data and free the HAL uart buffer

                // if there is any data

                bufLen = Hal_UART_RxBufLen(HAL_UART_PORT_0);

                HalUARTRead(HAL_UART_PORT_0, s_RxBuf , bufLen);

                           

                pBuf = s_RxBuf;

     

                while (0 != bufLen)

                {

                    bufLen--;

     

                    if( *pBuf == 0x00 )     // the start of the test packet, reset those checking counters

                    {

                        valueCnt = 0;

                        prevValue = 0;

                    }

                    else if (*pBuf != 127 ) // in between the test packet

                    {

     

                        if(( *pBuf ) ==  ( prevValue + 1 ) )

                        {

                            // check if there is any discontinuity

                            valueCnt++;

                        }          

                        else

                        {

                            // check how many bytes a missed

                            missBytes = *pBuf - prevValue - 1;

                            while ( missBytes )

                            {

                                missBytes--;

                                // there is no 0xFF in the test data

                                // so stuff 0xFF to the missing byte to inidicate

                                saveDebugData ( 0xFF ) ; 

                            }              

                        }

                    }

                    else

                    {

                        // the end of the test packet, check if successive data is all received

                        if( valueCnt != 127)

                        {

                            Indigo_ToggleDebugPin();

                        }

     

                    }

     

                    // save the data in a ring buffer for debug

                    saveDebugData (  *pBuf ) ;  

                   

                    // save the previous value to check the continuity

                    prevValue = *pBuf++;

                   

                }

                break;

        }

    }


  • Commenting the line:

        HCI_EXT_ClkDivOnHaltCmd( HCI_EXT_ENABLE_CLK_DIVIDE_ON_HALT );

    helped in my case. No more data loss.
    Thanks you for your help.

    Marc

    Salehiya

  • Hi Nick,

    Can you provide further information on your statement above?

    "First of all, do not use POWER_SAVING ( In BLEv1.2.1 or older)."

    Why is this?  What issues will be seen if POWER_SAVING is used in the older versions?


    Thanks,

    -Tyler

  • Our findings are that either using POWER SAVING, or HCI_EXT_ENABLE_CLK_DIVIDE_ON_HALT,  results in corrupted bit behaviour (the UART loses/switches its clock source half way through sending / receiving a byte - it's easy to see this behaviour if you monitor the TXD waveform).

    Having sorted out the bit timing, we also found that if you use the UART in non-DMA mode (HAL_UART_ISR = 1) it will lose blocks of data on receive  - typically if you send 128 bytes to the CC2540, then it will miss a block of 4 consecutive bytes at 115k2, or typically 2 consecutive bytes at 57k6.  This suggests some process is regularly hogging the CPU for 300-400us.  I would expect this to lead to occasional lost bytes even if we took the bit rate down further.

    Setting HAL_UART_DMA=1 (HAL_UART_ISR = 0) seems to fix the 'lost bytes' (so far), ***but*** in light of the first post on this topic, we're going to run some automated tests with large amounts of data, to see if this is really the end of the story.

  • Hi Graham,

    This is my experience also.  I should have a throughput  example up on wiki within next day or two - sleep, uart dma, in connection, no handshaking.

    Br,

    -Greg

  • hi Joakim,

    I'd like to know, is their anyway to use UART with power saving mode?

    I use BLE stack v1.4, no hci mode.

    I've asked a question but no one replies till now. 

    http://e2e.ti.com/support/low_power_rf/f/538/t/310686.aspx

     

     

     

  • After struggling with this same issue for a couple days, I tried the x-ing out the POWERSAVING option in the defined symbols and it worked for me.  I'm using USART0 on P1 with DMA for Rx.  I was getting corrupted data similar to what's described in the posts above.  I gisted a full patch of what I did to get it to work here: 

    https://gist.github.com/grundyoso/8130626

    I'm using the CC2540-MINIDK with BLE stack v1.4 to talk to another peripheral via the TEST header (P1_4 - RX, P1_5 - TX).  I hope this helps save some time for people.  I invested at least 50hrs to get to this point.  

    Cheers.