I have recently been playing with UART and DMA, although i’m still getting my head around it,.
DMA allows you to transfer large amounts of data through the system without getting the processor core involved. Rather than the CPU laboriously reading a byte, the writing a byte from one buffer to another, you’d give the DMA controller a pointer to the start of a source buffer, a pointer to the start of the destination buffer, and then specify how many bytes you need moved. The DMA hardware would then handle the data transfer between the buffers, while the CPU would be free to do other things. It DMA controller can also do this with little overhead, whereas an interrupt is going to, as its name states, interrupt the CPU in order to handle whatever the interrupt needs to be done.
DMA is very efficient for transmission when a block of data is to be sent. However, It can however become a bit complicated when flow control can cause the block to have to be suspended during mid-transmission.
The following source code is a simple library for USART(1-3-6) of the STM32F779NI-Eval Board.
/*! @file dev_serial.h @brief < brief description here > This program is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 2.1 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. Date: 14 Feb 2017 Version: 1 Design: Ioannis D. Implementation: Ioannis D. */ #ifdef __cplusplus extern "C" { #endif /****************************************************************************** * Header File Guard Symbol Start ******************************************************************************/ #ifndef DEVICES_DEV_SERIAL_H_ /****************************************************************************** * Definitions ******************************************************************************/ #define DEVICES_DEV_SERIAL_H_ /****************************************************************************** * Includes ******************************************************************************/ #include <Libraries/lbr_framework.h> // Can be safely removed #include "usart.h" #include "usart.h" #include "dma.h" /****************************************************************************** * Structures & Global Variables & Extern ******************************************************************************/ #if !defined(CNODE_UART_CBUFFER_SIZE) #define CNODE_UART_CBUFFER_SIZE 128 #endif typedef enum { SERIAL1 = 0x00, SERIAL3 = 0x01, SERIAL6 = 0x02 }I_Dev_Serial; typedef struct { volatile uint8_t data[CNODE_UART_CBUFFER_SIZE]; volatile uint16_t tail; volatile uint16_t head; } ring_buffer_t; extern volatile ring_buffer_t uart1_rx_buffer, uart6_rx_buffer, uart3_rx_buffer; extern I_FunctionPtr SERIAL1_RXCallback, SERIAL3_RXCallback, SERIAL6_RXCallback; /****************************************************************************** * Functions ******************************************************************************/ I_Status dev_serial_init(I_Dev_Serial); I_Status dev_serial_deinit(I_Dev_Serial); I_Status dev_serial_send_str(I_Dev_Serial, char *); I_Status dev_serial_send_str_crlf(I_Dev_Serial, uint8_t *); I_Status dev_serial_send_array(I_Dev_Serial, uint8_t *, uint32_t); I_Status dev_serial_send_array_crlf(I_Dev_Serial, uint8_t *, uint32_t); I_Status dev_serial_set_rx_callback(I_Dev_Serial, I_FunctionPtr); I_Status dev_serial_clear_rx_callback(I_Dev_Serial); int * dev_serial_get_status(I_Dev_Serial serial); volatile ring_buffer_t * dev_serial_get_rbuffer(I_Dev_Serial serial); UART_HandleTypeDef * dev_serial_get_handler(I_Dev_Serial serial); I_FunctionPtr * dev_serial_get_callback(I_Dev_Serial serial); int dev_serial_get_data(I_Dev_Serial serial,uint8_t * buffer,uint32_t size); /****************************************************************************** * Header File Guard Symbol End *******************************************************************************/ #endif #ifdef __cplusplus } #endif
/*! @file dev_serial.c @brief < brief description here > This program is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 2.1 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. Date: 15 Ìáñ 2017 Version: 1 Design: Ioannis D. Implementation: Ioannis D. */ /****************************************************************************** * Source File Start ******************************************************************************/ /****************************************************************************** * Includes ******************************************************************************/ typedef void (*fptr)(); #include <Devices/dev_serial.h> /****************************************************************************** * Extern Variables, Global Variables ******************************************************************************/ UART_HandleTypeDef huart1, huart3, huart6; volatile ring_buffer_t uart1_rx_buffer, uart6_rx_buffer, uart3_rx_buffer; int is_active_serial1 = 0, is_active_serial6 = 0, is_active_serial3 = 0; I_FunctionPtr SERIAL1_RXCallback = NULL, SERIAL6_RXCallback = NULL, SERIAL3_RXCallback = NULL; /****************************************************************************** * Private Functions ******************************************************************************/ inline int * dev_serial_get_status(I_Dev_Serial serial) { switch(serial) { case SERIAL1: return &is_active_serial1; case SERIAL3: return &is_active_serial3; case SERIAL6: return &is_active_serial6; default: break; } return NULL; } inline volatile ring_buffer_t * dev_serial_get_rbuffer(I_Dev_Serial serial) { switch(serial) { case SERIAL1: return &uart1_rx_buffer; case SERIAL3: return &uart3_rx_buffer; case SERIAL6: return &uart6_rx_buffer; default: break; } return NULL; } inline UART_HandleTypeDef * dev_serial_get_handler(I_Dev_Serial serial) { switch(serial) { case SERIAL1: return &huart1; case SERIAL3: return &huart3; case SERIAL6: return &huart6; default: break; } return NULL; } inline I_FunctionPtr * dev_serial_get_callback(I_Dev_Serial serial) { switch(serial) { case SERIAL1: return &SERIAL1_RXCallback; case SERIAL3: return &SERIAL3_RXCallback; case SERIAL6: return &SERIAL6_RXCallback; default: break; } return NULL; } /****************************************************************************** * Functions ******************************************************************************/ void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) { if (huart->Instance == USART1) { HAL_UART_DMAStop(&huart1); uart1_rx_buffer.tail = uart1_rx_buffer.tail==CNODE_UART_CBUFFER_SIZE-1 ? 0 : uart1_rx_buffer.tail+1; while(HAL_UART_Receive_DMA(&huart1,&uart1_rx_buffer.data[uart1_rx_buffer.tail],1)!=HAL_OK) { HAL_UART_DMAStop(&huart1); __HAL_UNLOCK(&huart1); } } else if (huart->Instance == USART3) { HAL_UART_DMAStop(&huart3); uart3_rx_buffer.tail = uart3_rx_buffer.tail==CNODE_UART_CBUFFER_SIZE-1 ? 0 : uart3_rx_buffer.tail+1; while(HAL_UART_Receive_DMA(&huart3,&uart3_rx_buffer.data[uart3_rx_buffer.tail],1)!=HAL_OK) { HAL_UART_DMAStop(&huart3); __HAL_UNLOCK(&huart3); } } else if (huart->Instance == USART6) { HAL_UART_DMAStop(&huart6); uart6_rx_buffer.tail = uart6_rx_buffer.tail==CNODE_UART_CBUFFER_SIZE-1 ? 0 : uart6_rx_buffer.tail+1; while(HAL_UART_Receive_DMA(&huart6,&uart6_rx_buffer.data[uart6_rx_buffer.tail],1)!=HAL_OK) { HAL_UART_DMAStop(&huart6); __HAL_UNLOCK(&huart6); } } } ring_buffer_t * rbufferr; UART_HandleTypeDef * huart; int dev_serial_get_data(I_Dev_Serial serial,uint8_t * buffer,uint32_t size) { rbufferr = dev_serial_get_rbuffer(serial); huart = dev_serial_get_handler(serial); int max_data; uint32_t temp6 = rbufferr->tail; if(rbufferr->head==temp6) return 0; if(rbufferr->head<temp6) { max_data = temp6-rbufferr->head <= size ? temp6-rbufferr->head : size; memmove(buffer,&rbufferr->data[rbufferr->head],max_data); rbufferr->head = rbufferr->head+max_data; return max_data; } else { max_data = ((CNODE_UART_CBUFFER_SIZE)-rbufferr->head) <= size ? ((CNODE_UART_CBUFFER_SIZE)-rbufferr->head) : size; memmove(buffer,rbufferr->data+rbufferr->head,max_data); if(max_data<size) { int tmp = max_data + temp6 <= size ? temp6 : size - max_data; memmove(buffer+((CNODE_UART_CBUFFER_SIZE)-rbufferr->head),rbufferr->data,tmp); rbufferr->head = tmp; return max_data+tmp; } rbufferr->head = rbufferr->head + max_data; return max_data; } return 0; } I_Status dev_serial_init(I_Dev_Serial serial) { int * is_active = dev_serial_get_status(serial); UART_HandleTypeDef * huart = dev_serial_get_handler(serial); ring_buffer_t * rx_buffer = dev_serial_get_rbuffer(serial); void (**serial_RX_callback)() = dev_serial_get_callback(serial); switch(serial) { case SERIAL1: MX_USART1_UART_Init(); huart->Instance = USART1; break; case SERIAL3: MX_USART3_UART_Init(); huart->Instance = USART3; break; case SERIAL6: MX_USART6_UART_Init(); huart->Instance = USART6; break; default: return I_ERROR; break; } if ((*is_active) == 1) return I_WARNING; (*is_active) = (*is_active) + 1; (*serial_RX_callback) = NULL; memset(rx_buffer->data,0,CNODE_UART_CBUFFER_SIZE); rx_buffer->tail = 0; rx_buffer->head = 0; HAL_UART_Receive_DMA(huart,rx_buffer->data,1); return I_OK; } I_Status dev_serial_deinit(I_Dev_Serial serial) { int * is_active = dev_serial_get_status(serial); UART_HandleTypeDef * huart = dev_serial_get_handler(serial); void (**serial_RX_callback)() = dev_serial_get_callback(serial); if ((*is_active) == 0) return I_WARNING; (*is_active) = 0; *serial_RX_callback = NULL; HAL_UART_DeInit(huart); HAL_UART_MspDeInit (huart); return I_OK; } I_Status dev_serial_send_str(I_Dev_Serial serial, char * data) { int * is_active = dev_serial_get_status(serial); UART_HandleTypeDef * huart = dev_serial_get_handler(serial); if((*is_active) == 0) return I_INVALID; while(HAL_UART_Transmit_IT(huart,(uint8_t*)data,strlen((char *)data))!=HAL_OK) { vTaskDelay(15); } return I_OK; } I_Status dev_serial_send_str_crlf(I_Dev_Serial serial, uint8_t * data) { int * is_active = dev_serial_get_status(serial); UART_HandleTypeDef * huart = dev_serial_get_handler(serial); if((*is_active) == 0) return I_INVALID; HAL_UART_Transmit(huart,data,strlen((char *)data),10); HAL_UART_Transmit(huart, (uint8_t *) "\r", 1, 15); HAL_UART_Transmit(huart, (uint8_t *) "\n", 1, 15); return I_OK; } I_Status dev_serial_send_array(I_Dev_Serial serial, uint8_t * data, uint32_t size) { int * is_active = dev_serial_get_status(serial); UART_HandleTypeDef * huart = dev_serial_get_handler(serial); if((*is_active) == 0) return I_INVALID; while(HAL_UART_Transmit(huart,(uint8_t *)data,size,30)!=HAL_OK) { BSP_LED_Init(0); BSP_LED_Toggle(1); osDelay(25); } return I_OK; } I_Status dev_serial_send_array_crlf(I_Dev_Serial serial, uint8_t * data, uint32_t size) { int * is_active = dev_serial_get_status(serial); UART_HandleTypeDef * huart = dev_serial_get_handler(serial); if((*is_active) == 0) return I_INVALID; HAL_UART_Transmit(huart,(uint8_t *)data,size,10); HAL_UART_Transmit(huart, (uint8_t *) "\r", 1, 15); HAL_UART_Transmit(huart, (uint8_t *) "\n", 1, 15); return I_OK; } I_Status dev_serial_set_rx_callback(I_Dev_Serial serial, void(*function)()) { int * is_active = dev_serial_get_status(serial); void (**serial_RX_callback)() = dev_serial_get_callback(serial); if((*is_active) == 0) return I_INVALID; *serial_RX_callback = function; return I_OK; } I_Status dev_serial_clear_rx_callback(I_Dev_Serial serial) { void (**serial_RX_callback)() = dev_serial_get_callback(serial); if((*serial_RX_callback) == NULL) return I_INVALID; *serial_RX_callback = NULL; return I_OK; } /****************************************************************************** * Source File End ******************************************************************************/