Welcome to Software Development on Codidact!
Will you help us build our independent community of developers helping developers? We're small and trying to grow. We welcome questions about all aspects of software development, from design to code to QA and more. Got questions? Got answers? Got code you'd like someone to review? Please join us.
Post History
[ rb_put(UDR0) sounds like an AVR. I'll be using STM32 in some examples, because I'm not familiar with AVRs. You were asking about about STM32 on EE.Codidact recently. ] But I have a gut feeli...
#1: Initial revision
*[ `rb_put(UDR0)` sounds like an AVR. I'll be using STM32 in some examples, because I'm not familiar with AVRs. You were asking about about STM32 on EE.Codidact recently. ]*
> But I have a gut feeling that this is wrong. It feels wrong that I handle an intrinsic UART construct `UDRE0` in `main.c` when it feels like it belongs in `usart.c` .
You can factor out the serial packet handler into a separate .c file. It would sit between `main.c` and `usart.c` It may be possible to reuse it [with upgrades over time] across multiple projects.
<h3>UART transmission (TX)</h3>
Transmission on UART is the easy part.¹ Olin already mentioned that you can iterate through the buffer and call the equivalent of `putc(...)` for each byte.
STM32 HAL has got a synchronous transmit function `HAL_UART_Transmit(...)` . There's the DMA transmit function `HAL_UART_Transmit_DMA(...)`. DMA lends itself well to transmission.
<h3>UART reception (RX)</h3>
Receiving on UART is always harder than transmitting.¹ The inconvenience with synchronous approach is that the `main()` always needs to poll the UART fast enough. Receiving the UART bytes in the interrupt makes sure that none of them will be missed.
The UART on STM32 can receive variable length packets, and DMA them into memory. It determines the end of a packet by the idle period after it. There's one DMA interrupt at the end of the packet, and the complete buffer would be given to the packet processor.
¹ That's true for a lot of comms. Usually it's easier to transmit well than to receive correctly.
<h3>Hard-coded callback</h3>
Your proposed way of creating a callback shows up once in a while. I’ll refer to it as *hard-coded callback* [as opposed to function pointer callback], but I don’t know what this method is called actually. For example, it shows up in the STM32 HAL.
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* phAdc)
``` C
// @file stm32g4xx_hal_adc.h
// @brief Header file of ADC HAL module.
// ADC IRQHandler and Callbacks used in non-blocking modes (Interruption and DMA)
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef *hadc);
// ...and there are more prototypes for callbacks there.
```
The HAL also provides a default implementation (a stub). It’s declared with `__weak` so it can be overridden in another compilation unit. The __weak keyword is not part of the C standard. The GCC for AVR supports weak linkage via `__attribute__((weak))` .
``` C
// @file stm32g4xx_hal_adc.c
__weak void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef *hadc)
{
UNUSED(hadc); /* Prevent unused argument(s) compilation warning */
/* NOTE : This function should not be modified. When the callback is needed,
function HAL_ADC_ConvCpltCallback must be implemented in the user file. */
}
```
