回顾下之前的章节:
本文我们介绍如何通过串口的DMA来实现串口数据的收发。
由于之前我们已经使用了串口1(STM中是USART1,GD中是USART0),本文中以串口2作为示例(STM中是USART2,GD中是USART1)。
前文中我们已经讲过了DMA的概念和原理:
DMA即直接存储器访问控制器,DMA提供了一种硬件的方式在外设和存储器之间或者存储器和存储器之间传输数据,而无需CPU的介入,避免了CPU多次进入中断进行大规模的数据拷贝,最终提高整体的系统性能。 简单而言,DMA相当于是外请(DMA硬件)的搬运工(数据拷贝),节约宝贵的CPU资源。 DMA一般需要配置的内容包括:
DMA的配置需要注意通道的匹配:
这里我们就不赘叙,直接给出串口DMA的设计思路:
简单一句话理解:把DMA缓存当做二级缓存!
串口发送和接收的DMA流程(手册中的):
注意的几个点手册中有描述,我在图中也给出来了,这几个坑描述如下:
代码中包括:
时钟配置,GPIO配置,串口参数配置,收发使能,DMA使能,使能空闲中断
GD32代码:
void uart2_init(uint32_t baudrate)
{
uint32_t com = USART1;
usart_deinit(com);
rcu_periph_clock_enable(RCU_USART1);
rcu_periph_clock_enable(RCU_GPIOA);
///< USART1_TX PA.2
gpio_af_set(GPIOA, GPIO_AF_7, GPIO_PIN_2);
gpio_mode_set(GPIOA, GPIO_MODE_AF, GPIO_PUPD_PULLUP, GPIO_PIN_2);
gpio_output_options_set(GPIOA, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_2);
///< USART1_RX PA.3
gpio_af_set(GPIOA, GPIO_AF_7, GPIO_PIN_3);
gpio_mode_set(GPIOA, GPIO_MODE_AF, GPIO_PUPD_PULLUP, GPIO_PIN_3);
gpio_output_options_set(GPIOA, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_3);
usart_baudrate_set(com, baudrate);
usart_word_length_set(com, USART_WL_8BIT);
usart_stop_bit_set(com, USART_STB_1BIT);
usart_parity_config(com, USART_PM_NONE);
usart_hardware_flow_rts_config(com, USART_RTS_DISABLE);
usart_hardware_flow_cts_config(com, USART_CTS_DISABLE);
usart_enable(com);
usart_receive_config(com, USART_RECEIVE_ENABLE);
usart_transmit_config(com, USART_TRANSMIT_ENABLE);
usart_dma_receive_config(com, USART_DENR_ENABLE);
usart_dma_transmit_config(com, USART_DENT_ENABLE);
usart_flag_clear(com, USART_FLAG_TC);
usart_interrupt_enable(com, USART_INT_IDLE);
nvic_irq_enable(USART1_IRQn, 3, 3);
}
STM32代码:
void uart2_init(uint32_t baudrate)
{
USART_InitTypeDef USART_InitStructure;
USART_TypeDef* USART = USART2;
USART_DeInit(USART);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
///< USART1_TX PA.2
gpio_init(GPIOA, GPIO_Mode_AF_PP, GPIO_Speed_50MHz, GPIO_Pin_2); ///< USART2_TX PA.2
gpio_init(GPIOA, GPIO_Mode_IN_FLOATING, GPIO_Speed_50MHz, GPIO_Pin_3); ///< USART2_RX PA.3
USART_StructInit(&USART_InitStructure);
USART_InitStructure.USART_BaudRate = baudrate; ///< 波特率
USART_Init(USART, &USART_InitStructure);
USART_Cmd(USART, ENABLE); ///< 使能串口
USART_DMACmd(USART, USART_DMAReq_Tx, ENABLE);
USART_DMACmd(USART, USART_DMAReq_Rx, ENABLE);
USART_ClearFlag(USART, USART_FLAG_TC);
USART_ITConfig(USART, USART_IT_IDLE, ENABLE);
nvic_irq_enable(USART2_IRQn, 3, 3);
}
uart2_dma_tx_init:串口发送DMA初始化
uart2_dma_send_ascii:DMA发送数据,严格按照手册中流程
【注】GD32 DMA是DMA0的通道6,STM DMA是DMA1的通道7。
GD32代码:
void uart2_dma_tx_init(uint32_t addr, uint32_t number)
{
dma_single_data_parameter_struct dma_parameter;
/* enable DMA1 */
rcu_periph_clock_enable(RCU_DMA0);
/* 发送 dm0 channel6(USART1 tx) */
dma_deinit(DMA0, DMA_CH6);
dma_parameter.direction = DMA_MEMORY_TO_PERIPH;
dma_parameter.periph_addr = (uint32_t)(&USART_DATA(USART1));
dma_parameter.periph_inc = DMA_PERIPH_INCREASE_DISABLE;
dma_parameter.periph_memory_width = DMA_PERIPH_WIDTH_8BIT;
dma_parameter.memory0_addr = addr;
dma_parameter.memory_inc = DMA_MEMORY_INCREASE_ENABLE;
dma_parameter.number = number;
dma_parameter.priority = DMA_PRIORITY_ULTRA_HIGH;
dma_parameter.circular_mode = DMA_CIRCULAR_MODE_DISABLE;
dma_single_data_mode_init(DMA0, DMA_CH6, &dma_parameter);
/* configure DMA mode */
dma_channel_subperipheral_select(DMA0, DMA_CH6, DMA_SUBPERI4);
}
void uart2_dma_send_ascii(uint8_t *buff, uint32_t count)
{
usart_flag_clear(USART1, USART_FLAG_TC);
dma_channel_disable(DMA0, DMA_CH6);
//dma_flag_clear(DMA0, DMA_CH6, DMA_CHINTF_RESET_VALUE);
dma_flag_clear(DMA0, DMA_CH6, DMA_FLAG_FTF);
dma_memory_address_config(DMA0, DMA_CH6, DMA_MEMORY_0, (uint32_t)buff);
dma_transfer_number_config(DMA0, DMA_CH6, count);
dma_channel_enable(DMA0, DMA_CH6);
while (usart_flag_get(USART1, USART_FLAG_TC)!=RESET);
}
void uart2_dma_send_string(char *str)
{
uart2_dma_send_ascii((uint8_t *)str, strlen(str));
}
STM32代码:
void uart2_dma_tx_init(uint32_t addr, uint32_t number)
{
DMA_InitTypeDef DMA_InitStructure;
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
DMA_DeInit(DMA1_Channel7);
/* 发送 dma1 channel7(USART2 tx) */
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)(&USART2->DR);
DMA_InitStructure.DMA_MemoryBaseAddr = addr;
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;
DMA_InitStructure.DMA_BufferSize = number;
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
DMA_InitStructure.DMA_MemoryDataSize = DMA_PeripheralDataSize_Byte;
DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
DMA_InitStructure.DMA_Priority = DMA_Priority_High;
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
DMA_Init(DMA1_Channel7, &DMA_InitStructure);
DMA_Cmd(DMA1_Channel7, ENABLE);
}
void uart2_dma_send_ascii(uint8_t *buff, uint32_t count)
{
USART_ClearFlag(USART2, USART_FLAG_TC);
DMA_Cmd(DMA1_Channel7, DISABLE);
DMA_ClearFlag(DMA1_FLAG_GL7);
DMA1_Channel7->CMAR = (uint32_t)buff;
DMA1_Channel7->CNDTR = count;
DMA_Cmd(DMA1_Channel7, ENABLE);
while (USART_GetFlagStatus(USART2, USART_FLAG_TC)!=RESET);
}
void uart2_dma_send_string(char *str)
{
uart2_dma_send_ascii((uint8_t *)str, strlen(str));
}
uart2_dma_rx_init:串口接收DMA初始化
uart2_dma_rx_get_count:获取DMA接收数据长度,并置位DMA,在串口空闲中断中调用
【注】GD32 DMA是DMA0的通道5,STM DMA是DMA1的通道6。
GD32代码:
void uart2_dma_rx_init(uint32_t addr, uint32_t number)
{
dma_single_data_parameter_struct dma_parameter;
/* enable DMA1 */
rcu_periph_clock_enable(RCU_DMA0);
/* 接收 dm0 channel5(USART1 rx) */
dma_deinit(DMA0, DMA_CH5);
dma_parameter.direction = DMA_PERIPH_TO_MEMORY;
dma_parameter.periph_addr = (uint32_t)(&USART_DATA(USART1));
dma_parameter.periph_inc = DMA_PERIPH_INCREASE_DISABLE;
dma_parameter.periph_memory_width = DMA_PERIPH_WIDTH_8BIT;
dma_parameter.memory0_addr = addr;
dma_parameter.memory_inc = DMA_MEMORY_INCREASE_ENABLE;
dma_parameter.number = number;
dma_parameter.priority = DMA_PRIORITY_ULTRA_HIGH;
dma_parameter.circular_mode = DMA_CIRCULAR_MODE_DISABLE;
dma_single_data_mode_init(DMA0, DMA_CH5, &dma_parameter);
/* configure DMA mode */
dma_channel_subperipheral_select(DMA0, DMA_CH5, DMA_SUBPERI4);
dma_channel_enable(DMA0, DMA_CH5);
}
uint32_t uart2_dma_rx_get_count(uint32_t dma_rx_count_max)
{
uint32_t receive_len = dma_rx_count_max - dma_transfer_number_get(DMA0, DMA_CH5);
if (receive_len != 0)
{
dma_channel_disable(DMA0, DMA_CH5);
dma_transfer_number_config(DMA0, DMA_CH5, dma_rx_count_max);
dma_flag_clear(DMA0, DMA_CH5, DMA_FLAG_FTF);
dma_channel_enable(DMA0, DMA_CH5);
}
return receive_len;
}
void USART1_IRQHandler(void)
{
uint8_t temp;
if (usart_interrupt_flag_get(USART1, USART_INT_FLAG_IDLE) != RESET)
{
// 软件先读USART_STAT0,再读USART_DATA可清除该位。
temp = USART_STAT0(USART1);
temp = USART_DATA(USART1);
temp = temp;
process_uart2_dma_rx_data();
}
}
STM32代码:
void uart2_dma_rx_init(uint32_t addr, uint32_t number)
{
DMA_InitTypeDef DMA_InitStructure;
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
DMA_DeInit(DMA1_Channel6);
/* 发送 dma1 channel6(USART2 rx) */
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)(&USART2->DR);
DMA_InitStructure.DMA_MemoryBaseAddr = addr;
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
DMA_InitStructure.DMA_BufferSize = number;
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
DMA_InitStructure.DMA_MemoryDataSize = DMA_PeripheralDataSize_Byte;
DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
DMA_InitStructure.DMA_Priority = DMA_Priority_High;
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
DMA_Init(DMA1_Channel6, &DMA_InitStructure);
DMA_Cmd(DMA1_Channel6, ENABLE);
}
uint32_t uart2_dma_rx_get_count(uint32_t dma_rx_count_max)
{
uint32_t receive_len = dma_rx_count_max - DMA_GetCurrDataCounter(DMA1_Channel6);
if (receive_len != 0)
{
DMA_Cmd(DMA1_Channel6, DISABLE);
DMA_ClearFlag(DMA1_FLAG_GL6);
DMA_SetCurrDataCounter(DMA1_Channel6, dma_rx_count_max);
DMA_Cmd(DMA1_Channel6, ENABLE);
}
return receive_len;
}
void USART2_IRQHandler(void)
{
uint8_t temp;
if (USART_GetITStatus(USART2, USART_IT_IDLE) != RESET)
{
// 软件先读USART_SR,再读USART_DR可清除该位。
temp = USART2->SR;
temp = USART2->DR;
temp = temp;
process_uart2_dma_rx_data();
}
}
process_uart2_dma_rx_data:处理数据主函数
void process_uart2_dma_rx_data(void)
{
uint32_t receive_len = uart2_dma_rx_get_count(UART_DATA_DMA_RX_COUNT);
uint32_t index;
if (receive_len != 0)
{
printf("receivelen = %d\r\n", receive_len);
for (index = 0; index < receive_len; index++)
{
printf("%02X ", uart_dma_rx[index]);
}
printf("\r\n");
}
}
COM3接串口2
MCU发送数据为:Hello, My Name is Cortex M !\r\n
COM9接串口1,COM3接串口2
COM3循环发数据给MCU,MCU通过串口1打印给电脑
【注:STM32的串口DMA没找到串口头子,没测试】
--EOF--