nrf5340 transfer data with UART and CDC_ACM, know when to add data to UART buffer

I want to transfer a bunch of data over the virtual com port to a PC using the USB port on the nrf5340.  I'm using the CDC ACM sample: https://developer.nordicsemi.com/nRF_Connect_SDK/doc/latest/zephyr/samples/subsys/usb/cdc_acm/README.html

That sample takes a character in and pushes the character back out.  Simple and it works. 

Since I want to dump a bunch of data, I started doing the same uart_fifo_fill() command.  It appears I can only add 1024 bytes to that at a time, which is fine.  That function returns the number of bytes it send (or added to the buffer). 

My question is, how do I know when I can add more data to that buffer?  When can I call that command again.  There were a couple other functions in UART.H and the one when TX was completed is not implemented and the other is "TX buffer can take at least 1 byte of data" but that doesn't seem ideal.  I want to know when I can send 1024 bytes of data again.  

What's the ideal method for pushing a bunch of data through the UART so that it runs as fast as possible and I know when I can push more data into the buffer?

Parents
  • Hi,

    You can register a callback using uart_callback_set() to register a callback, which will be called when a transfer is finished (an also in some other cases, so you need to check the event type in the callback). This includes the number of bytes that was sent (see uart_event_tx).

  • Thanks for getting back to me. 

    The example uses:

    uart_irq_callback_set(dev, interrupt_handler);

    Is that different than the uart_callback_set()?  And since I already setup the above callback, do I check for a different event in the same interrupt_handler() function?  Do I use the one you mentioned?  Do I need to setup 2?

  • It's not a problem, I have 8 gigabits that I want to transmit (at most, it's a flash chip and I want to dump the memory, yes I know it's gonna take a while).  Slight smile  So I know I have to split it up, and it's gonna be the only job for the micro when this happens so I don't care if it takes 100% of CPU cycles, I just want to minimize the time spent uart-ing. 

    The problem I saw with the uart_irq_tx_ready() is that it says that it returns true if it can take at least 1 byte.  Ok, but what if I want to send 1024 bytes?  Or more than 1, it doesn't tell me how many bytes I can send just that 1 is ok, any more????  Guess?  Does that mean I have to send 1 byte at a time? 

    I quickly checked out the logger example you have, and I'm not sure I understand it, or if it looks like it's any better.  It does look like they keep track of things a bit, but maybe I'm missing something. 

    Thanks again for taking the time to post stuff for me here.

  • And I'm trying to send a bunch of data, in order.  I believe I tested this already but it seems like such the wrong way to do things that I must have screwed it up.  But when you say, fill fifo with X bytes it returns the number of bytes that it DID send.  But when I tested this keeping track of the values in and out, it looked like it took the LAST bytes. 

    For instance if I said uart_fifo_fill with 10 bytes and I did it 3 times (really quickly and these numbers are small so it's easy to type and think about).  And I'm trying to send the values 1, 2, 3, ... up to 30.  And the first time uart_fifo_fill returned 10, second time uart_fifo_fill returned 5, the 3rd time it returned 10. 

    On the other end of the uart I got:

    1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30.

    If it would have sent:

    1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30.

    I could see that it took 5 bytes, and then be smart about it and try to send the last 5, but instead it took the last 5 and threw away the first 5.  That means the first time I send X bytes and it actually sent less my whole transmission is bad and I have to start over. 

    Maybe I have to try this again since, I can't imagine this is a good way to function. It's totally possible I screwed something up when I tested this before.

  • stevenminnick said:
    Ok, but what if I want to send 1024 bytes?  Or more than 1, it doesn't tell me how many bytes I can send just that 1 is ok, any more????  Guess?  Does that mean I have to send 1 byte at a time? 

    Try to send as much as you want, and check the return value of uart_fifo_fill(). This is the number of bytes that was sent.

    stevenminnick said:
    I quickly checked out the logger example you have, and I'm not sure I understand it, or if it looks like it's any better.  It does look like they keep track of things a bit, but maybe I'm missing something. 

    This point with linking to this is that it does try to send as much data as it has, and check how much was sent. And attempts to send the rest later. And so on. (See how the variable called curr_offset is increased by the return value of uart_fifo_fill() and this is done repeatedly until all data has been sent.

    stevenminnick said:
    Maybe I have to try this again since, I can't imagine this is a good way to function. It's totally possible I screwed something up when I tested this before.

    I don't see the code you used, but so I cannot say what could have been wrong. But I really suggest you just do exactly as the function from the logging subsystem that we have already mentioned. This is facing the exact same problem as you (potentially need to transfer more data than what you can do in a single call to uart_fifo_fill(), and you can do the exact same thing.

  • So I was testing the way Einor was showing me.  I believe this is the way they listed it in that logging example. 

                        buffLength += snprintf(buffer+buffLength, 1000-buffLength, "%d,",(int16_t)data);

                        if (buffLength > 1000)
                        {
                            send_len = 0;
                            buffPointer = 0;

                            while (send_len < buffLength && buffLength > 0)
                            {
                                send_len = uart_fifo_fill(dev, buffer+buffPointer, buffLength);
                                printk ("\nTried to print %d bytes only did %d", buffLength, send_len);
                                if (buffLength != send_len)
                                {
                                    printk (", failed %d bytes", buffLength - send_len);
                                }
                                buffLength = buffLength - send_len;
                                buffPointer = buffPointer + send_len;
                            }
                            memset(buffer, 0, sizeof(buffer));
                            buffLength = 0;
                            buffPointer = 0;
                            k_sleep(K_MSEC(4));
                        }

    So I've got a bunch of data, I'm taking that and packing my buffer.  What you see above is in my loop where I'm going through the data one int at a time.  Once my buffer length is > 1000, I try to send it to the fifo.  There is logic in here, where not only will it print, but if the fifo fill command returns a value that's not = to what I tried it will try to send the remainder. 

    The way this seems to work, if I send < 1024 bytes and the uart is empty, it works great.  So I put a sleep for 4ms in that loop.  What you see above works.  Because every time I come around, I'm sending < 1024 bytes and I wait an appropriate amount of time before sending again.  But let's look at this next item.

                        buffLength += snprintf(buffer+buffLength, 4096-buffLength, "%d,",(int16_t)data);

                        if (buffLength > 4000)
                        {
                            send_len = 0;
                            buffPointer = 0;

                            while (send_len < buffLength && buffLength > 0)
                            {
                                send_len = uart_fifo_fill(dev, buffer+buffPointer, buffLength);
                                printk ("\nTried to print %d bytes only did %d", buffLength, send_len);
                                if (buffLength != send_len)
                                {
                                    printk (", failed %d bytes", buffLength - send_len);
                                }
                                buffLength = buffLength - send_len;
                                buffPointer = buffPointer + send_len;
                            }
                            memset(buffer, 0, sizeof(buffer));
                            buffLength = 0;
                            buffPointer = 0;
                        }

    Here, the buffer is 4096, and once I pack 4000 bytes in my buffer, then I'm ready to send, I also don't "wait" in the loop.

    But the weird part is, my buffer packs numbers that increment.  And if I use the first code I posted, all the data comes out perfect.  Every time I send fifo fill with less than 1024 bytes, it returns the bytes that it did send which is the amount I tried to send.  Then I wait, hoping to wait long enough to not cause an error.

    When I use the second batch of code here, I have packed the same data, and I said ok fifio fill, send 4000 bytes and it returns with 1024, which I take it to mean that it was going to send only 1024, and to send the rest I'm going to have to try later. 

    BUT here's the weird part, since I tried to send more bytes than the fifo would take, this is in the MIDDLE of the first 1024 bytes that it sends.

    130,131,132,133,134,135,136,137,138,13994,395,396,3,143,144,145,146,

    If I try to send < 1024 bytes (cause that's all that will fit) this data comes out perfect, incrementing numbers.  When I try to send more than it can take, in the middle of what it DID take I get data corruption.

  • Hi,

    The code snippets here looks good, so I suspect the corruption is caused by something else. Could it be that the source buffer is modified by another thread while it is being transmitted, for instance because it is re-used too early?

Reply Children
Related