Relevant HAL functions are HAL_UART_Transmit_DMA and HAL_UART_Receive_DMA.
You can check the code in Drivers/STM32F0xx_HAL_Driver/Src/stm32f1xx_hal_uart.c.
In UART_Start_Receive_DMA
you can see that a call to HAL_DMA_Start_IT which enables relevant DMA Interrupts, and also several other bits related to UART errors and starting the DMA.
Personnaly, I have implemented a double buffer for transmission which seems to work for development purposes.
There are two buffers in uartTxBuffers.
currentIdx
indicates the buffer that is available for filling.
This is where the data is appended to.
The code then tries to start the DMA. If that succeeds then it switches buffers, if not it sticks with the current buffer.
/**
* Send data to serial link
*
* @param file For future use, to redirect according to ID
* @param ptr Pointer to data
* @param len Length of data to send
* @return
*/
int serialWrite(int file, const char *ptr, uint8_t len) {
// Append to current buffer
if (len != 0) {
if (currentIdx + len <= UART_TX_BUFFER_SIZE) {
memcpy((void *)uartTxBuffers[currentTxBuffer] + currentIdx, ptr, len); // NOLINT
currentIdx += len;
} else {
return 0;
}
}
if (currentIdx != 0) {
// DMA is not busy, send current buffer.
if (HAL_OK == HAL_UART_Transmit_DMA(&huart2, uartTxBuffers[currentTxBuffer], currentIdx)) {
currentTxBuffer = !currentTxBuffer;
currentIdx = 0;
}
}
return len;
}
These two callbacks regarding transmission make sure that the next buffer is sent when the transfer is complete, the first one may only be needed in case of a circular buffer - not checked.
// Probably unneeded
void HAL_UART_TxCpltCallback(UART_HandleTypeDef *UartHandle) {
/* Set transmission flag: transfer complete*/
// UartReady = SET;
serialWrite(0, NULL, 0); // Try sending next buffer if any.
}
void UART_DMATransmitCplt(UART_HandleTypeDef *UartHandle) {
serialWrite(0, NULL, 0); // Try sending next buffer if any.
}
I have not implemented Rx yet, but I have the following placeholders.
void serialRxInit() {
HAL_UART_Receive_DMA(&huart2, usart_rx_dma_buffer, sizeof (usart_rx_dma_buffer));
}
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *UartHandle) {
/* Set reception flag: transfer complete*/
// UartReady = SET;
__NOP();
}
void HAL_UART_ErrorCallback(UART_HandleTypeDef *UartHandle) {
Error_Handler();
}
There is some kind of "end of transmission" detection in reception by setting the ReceptionType to HAL_UART_RECEPTION_TOIDLE
The HAL code is helpful to know which interrupts are important.
When using the HAL code, note that USE_HAL_UART_REGISTER_CALLBACKS determines how you set the callbacks - either through weak functions or members of the UART_HandleTypeDef structure. My code above uses the weak functions.