• Support
  • Polling function sometimes does not catch all characters

I am trying to read a serial line using uart_poll_in() function. I only read a part of the characters that should be in the buffer, and then the read stops. Any suggestions? Should I be using interrupt-driven or DMI approach?
Thank you!

  do {
           ret = uart_poll_in(dev, &c);                         // read the UART2 for the feedback from serial
           printk("     ****Receiving from at UART2 RX  0x%x\r\n", c);
      }while (ret == 0 );   //returns 0 if a character has arrived

I found this :
https://github.com/zephyrproject-rtos/zephyr/issues/53468

I use the async implementation. Plenty of samples across Zephyr and NCS.

jaredwolff Thank you for the link. I have implemented an interrupt-driven callback. My callback function is very simple (I just need to know what events I will get). I get a UART_RX_BUF_REQUEST event (good!). I know that when I run this code, I always get 4 characters from the device. My receive buffer is 1 character long, and it should be filled in quickly. Shouldn’t I get another events indicating that my buffer is full? Another thing is that I could not get uart_irq_rx_enable(tty2) to work (I wonder if I need to have a board-specific setting in prj.conf ?)
Thank you!

static void uart_callback(const struct device *dev, struct uart_event *evt, void * buf)
{

   // struct uart_data_event *event;

   // int err;

    switch (evt->type) {
    case UART_RX_RDY:
        LOG_ERR(" UART_RX_RDY: %d", evt->type);
        break;
    case  UART_RX_BUF_RELEASED :
        LOG_ERR(" UART_RX_BUF_RELEASED: %d", evt->type);
         break;
    case UART_RX_BUF_REQUEST:
        LOG_ERR(" UART_RX_BUF_REQUEST: %d", evt->type);
         break;
    case UART_RX_DISABLED:
        LOG_ERR(" UART_RX_DISABLED: %d", evt->type);
        break;
    case UART_TX_DONE:
        LOG_ERR(" UART_TX_DONE: %d", evt->type);
        break;
    case UART_TX_ABORTED:
        LOG_ERR("  UART_TX_ABORTED: %d", evt->type);
        break;
    case UART_RX_STOPPED:
        LOG_WRN("  UART_RX_STOPPED: %d", evt->type);
        break;
    default:
        LOG_ERR("Unexpected event: %d", evt->type);
        __ASSERT_NO_MSG(false);
        break;
     }
}  //uart_callback

    k_sleep(K_SECONDS(1));
        int err = uart_callback_set(tty2, (uart_callback_t *)uart_callback, (void*) &buf);
        if (err) {
                LOG_ERR("uart_callback_set: %d", err);
                    //return;
           }

   uint8_t buf[1];
     k_sleep(K_SECONDS(1));

     err = uart_rx_enable(tty2, &buff, 1, 10000);

    olgamill55 when you call the rx_enable function I believe you can set an RX timeout value. If you’re running in Async mode, you receive 1 byte or more, and then you go for a period of not receiving anything it will fire the Async UART callback with a pointer to the buffer you’re using.

    It’s a little funky and it took me a while to understand it. I hope that helps!

      10 days later

      jaredwolff Jared thank you so much for your help. I was able to get the UART_RX_RDY to fire once by setting my receive buffer to a small value, but now I can not get this interrupt to fire no matter what.

      The data that I am reading is a stale junk (not updated by the UART).

      1. The UART_RX_BUF_REQUEST event that fires does not update the//read_ptr , because every time I re-load, and this event is generated, this pointer keeps giving me the same memory content (junk)., even if I invoke an offset on it and read it with an offset. printk(“BUF Req LENGTH =%d\n”, read_len); is 0 at all the times. So it did not read anything on that event.

      2. case UART_RX_RDY : does not fire at all, even I know that my UART receives the data. If this event UART_RX_RDY does not fire, then the data in the buffer is not updated by the UART, so read_ptr gives me the same junk with no change (even if I apply the offset). g_rx_buf is currently 255. I was able to get this event to fire by setting up this buffer to only 2 characters , but even now this trick does not work (not sure why). Should be able to do that like you suggested - just set it to a small value and get the buffer to be full, and trigger this event, but no luck. Not sure what I am doing different, but something is different.

      ____________________ Read Characters _____________________

      I set up a semaphore k_sem_give(&rx_rdy); and the program sits and waits for the semaphore to be taken (indefinitely), causing a blocking wait:

      //----------------------------
      void read_a_char_print_on_screen (const struct device *dev)
      {
          
            int ret;
        
            int err = uart_rx_enable(dev, g_rx_buf, G_BUF_SIZE, 10); // SYS_FOREVER_US
            if (err) {
                LOG_INF("Can not do UART_RX_Enable from Print on Screen, ERR = %d", err);
             }
            k_sleep(K_MSEC(500));
      
      
            while (k_sem_take(&rx_rdy, K_MSEC(100)) !=0)
            {
                printk("wait for semaphore..");  <---- I am seeing this message no matter what
            }
      
            print_buffer(chained_read_buf1, G_BUF_SIZE);  <-------- this buffer is the secondary, and it also has no data
           // k_sleep(K_MSEC(1000));
            print_buffer(read_ptr, G_BUF_SIZE); <------------ to read this pointer, I have to disable the semaphore above
            uart_rx_disable (dev); 

      ____________________ Handler _____________________________________

      static void uart_callback(const struct device *dev, struct uart_event *evt, void * buf)
      {
          ARG_UNUSED(dev);
          ARG_UNUSED(buf);
      
          switch (evt->type) {
          case  UART_RX_BUF_RELEASED :
              LOG_ERR(" UART_RX_BUF_RELEASED: %d", evt->type);
                      k_sem_give(&rx_buf_released);
      
               break;
          case  UART_RX_RDY :
              LOG_ERR(" UART_RX_RDY: %d", evt->type);
                     read_ptr = evt->data.rx.buf+ evt->data.rx.offset;
                     read_len = evt->data.rx.len;
      
                      printk("BUF READY LENGTH =%d\n", read_len);
                     memcpy(g_rx_buf, read_ptr,read_len);
                     UART_RX_RDY_SET = true;
                     print_buffer(read_ptr, read_len);
                     k_sem_give(&rx_rdy);
               break;
          case UART_RX_BUF_REQUEST:
              LOG_ERR(" UART_RX_BUF_REQUEST: %d", evt->type);
               printk("BUF Req LENGTH =%d\n", read_len);
               uart_rx_buf_rsp(dev, chained_read_buf1, G_BUF_SIZE);
               __ASSERT(err == 0, "Failed to provide new buffer");
               k_sem_give(&rx_disabled);
               break;
          case UART_RX_DISABLED:
              LOG_ERR(" UART_RX_DISABLED: %d", evt->type);
      
              k_sem_give(&rx_disabled);
              break;
          case UART_TX_DONE:
              LOG_ERR(" UART_TX_DONE: %d", evt->type);
              k_sem_give(&tx_done);
              break;
          case UART_TX_ABORTED:
              LOG_ERR("  UART_TX_ABORTED: %d", evt->type);
              break;
          case UART_RX_STOPPED:
              LOG_WRN("  UART_RX_STOPPED: %d", evt->type);
              break;
          default:
              LOG_ERR("Unexpected event: %d", evt->type);
              __ASSERT_NO_MSG(false);
              break;
           }
      }  //uart_callback

      Thank you!

        olgamill55
        Sorry I forgot to include my prj.conf

        CONFIG_CAN=y
        CONFIG_NET_L2_ETHERNET=n
        CONFIG_CAN_MAX_FILTER=5
        CONFIG_NET_IF_MAX_IPV4_COUNT=3
        
        CONFIG_NETWORKING=y
        CONFIG_NET_DRIVERS=y
        CONFIG_NET_CANBUS=y
        CONFIG_NET_SOCKETS=y
        CONFIG_NET_SOCKETS_CAN=y
        CONFIG_NET_SOCKETS_POSIX_NAMES=y
        CONFIG_NET_LOOPBACK=y
        CONFIG_NET_TEST=y
        
        CONFIG_NET_IPV6=n
        CONFIG_NET_IPV4=y
        CONFIG_NET_MGMT=y
        CONFIG_NET_TCP=y
        CONFIG_NET_UDP=y
        CONFIG_RING_BUFFER=y
        
        CONFIG_NET_CONFIG_SETTINGS=n
        CONFIG_NET_CONFIG_NEED_IPV4=n 
        CONFIG_NET_CONFIG_NEED_IPV6=n
        
        CONFIG_NET_L2_ETHERNET=y
        
        CONFIG_NET_DEFAULT_IF_CANBUS_RAW=y
        
        CONFIG_NET_PKT_RX_COUNT=30
        CONFIG_NET_PKT_TX_COUNT=30
        CONFIG_NET_BUF_RX_COUNT=30
        CONFIG_NET_BUF_TX_COUNT=30
        
        #domains
        CONFIG_USERSPACE=y
        CONFIG_TEST_RANDOM_GENERATOR=y
        CONFIG_LOG=y
        CONFIG_NET_LOG=y
        CONFIG_LOG_MODE_IMMEDIATE=y
        CONFIG_LOG_BUFFER_SIZE=2048
        CONFIG_LOG_DEFAULT_LEVEL=4
        
        
        CONFIG_NET_SOCKETS_LOG_LEVEL_DBG=y
        CONFIG_CAN_LOG_LEVEL_DBG=y
        
        CONFIG_NET_BUF_DATA_SIZE=64
        
        CONFIG_MAIN_STACK_SIZE=2048
        CONFIG_HEAP_MEM_POOL_SIZE=4096
        
        CONFIG_FILE_SYSTEM=y
        CONFIG_PRINTK=y
        CONFIG_UART_DRV_CMD=y
        
        CONFIG_UART_INTERRUPT_DRIVEN=y
        CONFIG_SERIAL=y
        CONFIG_UART_LINE_CTRL=y
        CONFIG_UART_ASYNC_API=y

          olgamill55 pro tip, you can use the ``` to wrap your code in markdown. It makes it easier to read 🙂

          The way you’re blocking is a bit tricky. I have found many-an-error by sticking lots of these types of loops in my code. I would create another thread that blocks with K_FOREVER on data when it becomes available.

          I would also remove any print statements inside that handler if you can. The slower it is the more likely you’re going to have problems missing bytes!

          Also you’ve set the timeout to 10us. Are you giving the peripheral sufficient amount of time to receive bytes?

          Sorry I don’t have any better samples of the ASY?NC API in use.

            jaredwolff
            Thank you for all suggestions. I will try them for sure. I agree that a thread needs to read, not the main function (I am trying to put everything in main). I also discovered that print statements in a handler is an issue (and massive print statements in general).
            Again, your help is very appreciated!

            Thank you for your feedback !
            I made some changes and now I am getting the desired event UART_RX_RDY .
            My buffer is only 4 chars long, so it triggers, but I think it gets released too soon so I can not read it (the content in the primary and secondary buffers are 0×0).
            Sequence of events:
            [00:00:38.025,000] <err> net_socket_can_sample: RX_RDY
            X LENGTH =4
            [00:00:38.033,000] <err> net_socket_can_sample: RX_BUF_RELEASED
            [00:00:38.040,000] <err> net_socket_can_sample: RX_BUF_REQ

            
            #define G_BUF_SIZE 4
            uint8_t chained_read_buf1[G_BUF_SIZE];
            uint8_t g_rx_buf[G_BUF_SIZE];  
            //---------------------------------------------------- uart_thread 
            static void uart_thread (const struct device *local_tty2)
            {
            
             static int put_counter;
              
                 int ret;
                  while (true) {
            
                       if (k_sem_take(&rx_rdy, K_FOREVER) == 0)
                       {
                            print_buffer(chained_read_buf1, G_BUF_SIZE);  <-------- data is not in either buffer 
            
                            k_sleep(K_MSEC(70));
             
                            print_buffer(g_rx_buf, G_BUF_SIZE);
            
                        }
                     }
            
                    k_sleep(K_MSEC(100));
             } 
            
            
            static void uart_callback(const struct device *dev, struct uart_event *evt, void * buf)
            {
                ARG_UNUSED(dev);
                ARG_UNUSED(buf);
               // struct uart_data_event *event;
            
               int err;
            
                switch (evt->type) {
                case  UART_RX_BUF_RELEASED :
                    LOG_ERR("RX_BUF_RELEASED");
                    		k_sem_give(&rx_buf_released);
            
                     break;
                case  UART_RX_RDY :
                    LOG_ERR("RX_RDY");
                 
                           read_ptr = evt->data.rx.buf+ evt->data.rx.offset;
                           read_len = evt->data.rx.len;
                            printk("X LENGTH =%d\n", read_len);           <-------------------- I get this message that it read 4 characters
                           memcpy(g_rx_buf, read_ptr,read_len);
                           k_sem_give(&rx_rdy);
                     break;
                case UART_RX_BUF_REQUEST:
                    LOG_ERR("RX_BUF_REQ");
                     
                     memset (chained_read_buf1, 0, G_BUF_SIZE);
                     uart_rx_buf_rsp(dev, chained_read_buf1, sizeof (chained_read_buf1)); <---- secondary buffer 
                     __ASSERT(err == 0, "Failed to provide new buffer");
                     k_sem_give(&rx_buf_requested);
                     break;
                case UART_RX_DISABLED:
                    LOG_ERR("RX_DIS");
            
                    k_sem_give(&rx_disabled);
                    break;
                case UART_TX_DONE:
                    LOG_ERR("TX_DONE");
                    k_sem_give(&tx_done);
                    break;
                case UART_TX_ABORTED:
                    LOG_ERR("TX_ABORTED");
                    break;
                case UART_RX_STOPPED:
                    LOG_WRN("RX_STOPPED");
                    break;
                default:
                    LOG_ERR("Unexpected event: %d", evt->type);
                    __ASSERT_NO_MSG(false);
                    break;
                 }
            }  //uart_callback

            I guess I am confused why if I make my primary buffer g_rx_buf[G_BUF_SIZE]; 35 chars long, (not 4 chars long), I never get UART_RX_RDY (but my data that is coming is 76 characters long). I am also thinking it is a timing issue of some sort. May be the buffer gets filled so slow with so many gaps that I am triggering on one of the gaps, and Zephyr is thinking that all data is received and releases the buffer. But the data keeps coming and Zephyr is requesting an additional buffer (which also is full of 0×0 and I do not see any data there because it is choppy) . Thank you for your feedback as usual.

            Also I should be able to poll for the UART buffer (if I do not do uart_rx_enable().), leaving a transmit to ret = uart_tx(dev, arr, size, SYS_FOREVER_US); , which works.
            I have a C and Python applications that can receive the data from the same device using UART2 (with no issues). I am having trouble UART2 RX receive in Zephyr. I wonder if there are UART hardware settings that need to be checked to see if Zephyr default settings are different from other systems?

            The code that enables the UART RX is
            k_sleep(K_MSEC(50));
            memset(g_rx_buf, 0, G_BUF_SIZE);
            int err = uart_rx_enable(dev, g_rx_buf, G_BUF_SIZE, 70 * USEC_PER_MSEC); // SYS_FOREVER_US
            if (err) {
            LOG_INF(“Can not do UART_RX_Enable from Print on Screen, ERR = %d”, err);
            }
            k_sleep(K_MSEC(10));

            k_sleep(K_MSEC(50));
            memset(g_rx_buf, 0, G_BUF_SIZE);
            int err = uart_rx_enable(dev, g_rx_buf, G_BUF_SIZE, 70 * USEC_PER_MSEC); // SYS_FOREVER_US
            if (err) {
            LOG_INF(“Can not do UART_RX_Enable from Print on Screen, ERR = %d”, err);
            }
            k_sleep(K_MSEC(10));

            12 days later

            Found the issue - no reply needed. Thank you !

            Terms and Conditions | Privacy Policy