1
\$\begingroup\$

Trying to understand the flow of the DMA transfer via uart:

- Configure uart
- Configure dma
- enable DMA stream to initiate the transfer
- IRQ handler gets fired 
- disable interrupts, and invoke `DMAXferCmplCallback()` in case of receiving data from the uart
-  write the received data to a FIFO in memory (circular buffer) while keeping track of the start/end index.
-  process the data / do any action based on what was received?

Is that a general idea? My questions:

  • do we really need to have a callback? is the main reason for having it to mainly process the incoming data? is it better than something like:
    while(true) {
        usart_serial_read();
        parse_serial_read();
        execute_serial_data();
        //clear out the rx buffer for the next input ...
     }
  • is circular buffer really required if I don't have to keep track of past inputs? One reason I know circular buffers are useful is how you can continue writing to it without having to worry about overflowing it like in case of a linear buffer since you can wrap things around in a FIFO. How I have it now is I receive the data, store it into a linear buffer, parse it before \r, and then clear it. This way I have the full buffer prior to each input reading.
  • should the callback be a part of the uart or dma file?
\$\endgroup\$
1
  • \$\begingroup\$ Comments are not for extended discussion; this conversation has been moved to chat. \$\endgroup\$ – Voltage Spike Jun 13 '20 at 4:12
1
\$\begingroup\$

In general, how to use DMA will depend on what data you transfer and how much.

At least I don't understand why would you disable interrupts, while on STM32 the DMA itself can implement a ring or ping-pong buffer. As soon as one half of the buffer is full, it triggers you with the interrupt (which calls the callback) and keeps receiving data into the second half of the buffer.

But if you are receiving say single keypresses from a terminal program, you most certainly don't want to wait until half-buffer worth of letters has arrived, you would poll the DMA controller if any bytes and how many bytes have been received so you can act on them.

\$\endgroup\$
7
  • \$\begingroup\$ I guess it wouldn't make sense to disable the interrupts if we are continuously reading in the data so the FIFO keeps getting populated with the data from uart. If you're not, when would you disable the interrupts i.e what determines the end of transmission? Also, if I'm receiving keypresses from a terminal, why would I have to poll and not use interrupts? Regarding the flow, what would it be like in case of reading: a) keypresses from a terminal b) from a sensor \$\endgroup\$ – xyf May 12 '20 at 19:55
  • \$\begingroup\$ It depends on the data stream, it may never end, and you don't ever have to disable the interrupts if keeping them enabled makes more sense than disabling them. Some protocols have data packets that have varying length, so you may never know which byte is last. And I did not say you should not use interrupts when receiving keypresses, all I meant that it makes no sense to require a half-DMA buffer worth of keypresses before getting the half-buffer interrupt, because the commands may be 3 letters and buffer 16 bytes. You either use DMA for receiving each byte, or UART interrupts, but not both. \$\endgroup\$ – Justme May 12 '20 at 20:29
  • \$\begingroup\$ I understand your point about not using half-transfer interrupts since user might enter lesser than half the FIFO size. Polling though doesn't seem to be a suggested option. Perhaps timer where you call the application callback every certain period? \$\endgroup\$ – xyf May 12 '20 at 21:53
  • \$\begingroup\$ Of course that is possible, but technically, using a timer interrupt to poll if something has happened and should be reacted to, is still polling. If the DMA (or the UART itself if it has a FIFO buffer) had a mechanism to generate an interrupt if data has been received, but not read within a timeout period, it would not be polling. \$\endgroup\$ – Justme May 12 '20 at 22:46
  • \$\begingroup\$ yes, but the timer interrupt will only be triggered after certain intervals so you're technically not polling, isn't it? also, there doesn't seem to be character matching interrupt on my board so using DMA doesn't seem to make much sense I guess since interrupts are only fired at either half or full transfers and I wouldn't know if the user input was completed by then (at least by half-transfer)...unless there's an alternative. \$\endgroup\$ – xyf May 13 '20 at 2:55
1
\$\begingroup\$

Since human entered ASCII characters usually have termination characters, I just used the UART character match interrupt on the STM32F3 to flag to the main loop to parse with the half-transfer of transfer complete interrupts to detect invalidly long messages. Half-transfer interrupts setting flags (possibly combined with timer interrupts) to trigger the main loop to parse would be more suited to streaming data or data that has no termination characters.

But LAME. The STM32F4 USART does not have character interrupts. It's address matching on an address mark interrupt is also much less developed than the STM32F303 (the character match and address match share registers).

Half-transfer interrupts would be best for continuous data streams that will regularly fill the buffer and trigger the parsing to occur. Not so good for user inputs. What would happen is the only commands that would get processed immediately are those that exactly matched the half-buffer size and only if they were entered when the buffer was empty. Any other time, the command either doesn't get processed because it is too short, or it only gets processed when more commands are entered so that the buffer is filled past half (and no command that crossed that half-buffer point would get processed at that time either).

Timer interrupts to trigger parsing would work better for user inputs so commands are promptly executed (from the user's perspective anyways) regardless of length since it does not rely on a constant data stream to trigger parsing like the DMA half-transfer interrupt would.

You don't need circular buffers for UART data but they make the most efficient use of your buffer memory and let you have the longest messages lengths compared to ping-ping buffers or simple linear buffers: electronics.stackexchange.com/questions/498526/…

\$\endgroup\$
3
  • \$\begingroup\$ what if you're reading in data after a higher speed than what you can write? say you continuously read from an i2c device that runs at 100KHz (byte/ 10us), store into a software buffer via DMA, then you write to serial at 9600 baud rate (byte / 1.04ms). by the time you get to writing to uart, you already have ~100 samples sitting in your buffer to send out. \$\endgroup\$ – xyf May 12 '20 at 22:13
  • \$\begingroup\$ 100 kHz I2C would not be 10us per byte, but 10us per bit, or 90us per byte. But you would not end up in a situation like that. If you do end up in a situation like that, where more data comes in than can be sent out, something has already gone wrong in the design and implementarion and it must be fixed. Like reading slower or using faster baud rate. Or keeping pauses reading until there is a free buffer. Or transmitting only the latest data buffer, skipping oldest data buffers. You can transmit every 12th byte for example. But this has nothing to do with DMA really. \$\endgroup\$ – Justme May 12 '20 at 22:59
  • \$\begingroup\$ you're right about 90us per byte. I would not end up in a situation like that cause it's done sequentially? read from i2c device every 90us, then write to serial at 9600 baud rate. Yeah, I guess it doesn't have anything to do with the DMA since DMA is merely used for transferring a whole chunk of data, but I was just concerned with the overflow considering different speeds. \$\endgroup\$ – xyf May 13 '20 at 3:00

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.