0
\$\begingroup\$

I'm trying to implement a UART receiver that works by finding a '\0' in the datastream, OR when it receives 256 characters at which point the data should be processed by an interrupt. The point is to use DMA for reception but if either of these events occur it should stop filling up the current buffer and process the data. The reason is that I'm using COBS encoding to frame my data.

The STM32F7 chip has something called Character match as part of the Modbus specification, which I got working by setting the bits in the configuration register since it's not supported by the HAL(stm32cubemx, which would remove this code upon regenerating) libraries:

MODIFY_REG(huart->Instance->CR1, UART_CR1_FIELDS, tmpreg | USART_CR1_CMIE);

And at the end of the UART interrupt handler I do

UART8->ICR |= USART_ISR_CMF;

To clear the character match interrupt flag so it won't run again.

As I understand it the interrupt handlers in the STM32f7xx_it.c are in fact the first functions

void DMA1_Stream6_IRQHandler(void) and void UART8_IRQHandler(void)

in my case that are run as interrupts occur in a peripheral, and from these functions a handler(HAL_xxx_IRQHandler()) is run that check what interrupt occurred and run any user defined functions depending on the interrupt flags, like for example:

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *UartHandle) {
    //process data, this runs when DMA finishes receiving bytes
}

The problem is that it's not clear which one of the interrupt function is ran when for example I'm using DMA and I have received my specified amount of data over UART. Does it run the UART interrupt or the DMA stream interrupt? How can I reliably end up in the same callback function on '\0'(uart?) or 256 bytes(dma?) to process my data without botching the interrupts?

\$\endgroup\$
  • \$\begingroup\$ The amount of data to transfer is a DMA function; if you reach that value, the DMA handler will be invoked. I would use a simple global such as 'CMATCH' and set it if the CMF flag is set (the USART interrupt is triggered by this flag) and use that to get a reliable function sequence. \$\endgroup\$ – Peter Smith Feb 22 at 15:33
  • \$\begingroup\$ Do you actually need DMA? That's a fairly fast processor likely to be untroubled by running a receive interrupt at typical baud rates, so while you may be able to figure out how to get this working with DMA, doing it all in software could be conceptually cleaner and faster to develop and might let you move on to the next real blocking problem of your project more quickly... while also leaving open portability to potentially someday do a cost reduce port to a simpler target without the character match capability. If you need the hardware assistance, go for it, but if not, consider strategy. \$\endgroup\$ – Chris Stratton Feb 22 at 16:31
  • \$\begingroup\$ @ChrisStratton This microcontroller is going to be running 2 serial ports at 6Mbaud, while doing other stuff on the CPU. So yes performance is definitely an issue. \$\endgroup\$ – Tryphon Feb 22 at 17:13
1
\$\begingroup\$

The DMA CNDTR register counts down to zero after each transfer and shows how many elements remain to be trasferred. You can use it to check whether the DMA is running or not.

Your UART interrupts, if enabled, will always run alongside the DMA transfers and interrupts so the UART interrupts will have to chech the DMA's CNDTR register to know whether or not to actually execute.

You can also disable interrupts from within interrupts. So your DMA transfer complete interrupt can disable your character receive interrupt (or vice versa) and you can re-enable it elsewhere. Remember you also have interrupt priorities to handle when the last DMA transfer is "\0" character and triggers both the DMA transfer complete and character match interrupts at the same time.

In my code, I use character match to detect the enter key to know that a user command has been entered and to parse the message in memory being stored by the DMA. I also have a UART receive interrupt to echo characters that echoes the character directly from the UART receive register. These all run at the same time along with the DMA transfers and interrupts.

\$\endgroup\$
  • \$\begingroup\$ If you are running a per-character UART interrupt, there's not a lot of point to using DMA on the receive side at all. Using only the character match interrupt alongside DMA might make sense as an advisory condition, but then it might also make sense to just poll the flag at times when it would be convenient to deal with the result. \$\endgroup\$ – Chris Stratton Feb 22 at 16:09
  • \$\begingroup\$ If the echo is enabled, it does but this is used in a symmetrical PCB stack where end boards communicate with the user but intermediate boards communicate with each other. They all run the same software and the echo is not enabled for intermediate boards which saves the CPU from intervening until the message is complete. There is some circular buffer business going on as well which would eat CPU cycles if done in interrupt code rather than DMA hardware. I need to save my interrupts for motor commutation. \$\endgroup\$ – DKNguyen Feb 22 at 16:19
  • \$\begingroup\$ My character match interrupt just adds a +1 to a counter so the CPU knows how many messages are in the buffer to parse since it cannot be guaranteed the a message can be dealt with before another comes in (due to the presence of machine-to-machine-messages) before another comes in so I cannot just poll. It's more efficient than searching all the way to the end of the buffer each time for more messages to parse. \$\endgroup\$ – DKNguyen Feb 22 at 16:31
  • \$\begingroup\$ @Toor What do you mean it adds a +1 to a counter? What register is this exactly, because this would help immensely. \$\endgroup\$ – Tryphon Feb 22 at 17:12
  • \$\begingroup\$ It's not a register. It's just a counter variable I make. The character match interrupt goes +1 to the variable, and the parse algorithm which runs in the main program loop (basically a form of polling) goes -1 each time it parses until it reaches zero. That way, I can buffer multiple messages before parsing them and when I do parse I don't have to search the remainder of the buffer to check if there's a message remaining. It keeps the character match interrupt short. \$\endgroup\$ – DKNguyen Feb 22 at 17:17
0
\$\begingroup\$

I haven't worked with STM32F7 but you might check this repository. I shared a step-by guide to use DMA with character match and receiver timeout for STM32L4 MCU's. As I can see registers for both MCU's looks similar.

\$\endgroup\$

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.