在上一篇中介绍了 ringbuffer 方式去实现串口收发数据的无阻塞。这里介绍另外一种实现方式 - DMA 模
式,个人感觉使用 DMA 方式在接收数据时不仅更为方便,解析帧协议更为准确(尤其是不定长帧协议),而且占
用 CPU 资源也更小。在 ringbuffer 方式下,收发数据都是基于中断实现的,因此在大数据量进行传输时,频繁
的进中断会导致系统的性能急剧下降。使用 DMA 模式在整个收发过程中只有当所有数据完成发送时才产生中断,因
此效率大大提高。在解析帧协议方面,这里充分利用了帧与帧之间的间隙,因为在实际传输中,一帧数据是连续的,
但是帧与帧之间是有间隙的,也就是存在着总线的空闲。这里利用了 STM32 的串口总线空闲中断去判断一帧的结
束,从而也实现了不定长帧的解析。
1、USART DMA 初始化
#include "includes.h"
/* 驱动层接收配置双缓冲 */
static uint8_t debug_rxBuffer[128];
static void debug_usart_nvic_init(void)
{
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
NVIC_InitStructure.NVIC_IRQChannel = DMA1_Channel4_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
NVIC_InitStructure.NVIC_IRQChannel = DMA1_Channel5_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
}
static void debug_usart_dma_config(void)
{
DMA_InitTypeDef DMA_InitStructure;
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
/* ------------------------ 发送通道 DMA ------------------------ */
DMA_DeInit(DMA1_Channel4);
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&(USART1->DR);
/* 内存到外设的传输 */
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST; DMA_InitStructure.DMA_BufferSize = 0;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_MemoryDataSize_Byte;DMA_InitStructure.DMA_Mode = DMA_Mode_Circular; DMA_InitStructure.DMA_Priority = DMA_Priority_High;DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;DMA_Init(DMA1_Channel4, &DMA_InitStructure);DMA_ITConfig(DMA1_Channel4, DMA_IT_TC | DMA_IT_TE, ENABLE);USART_DMACmd(USART1, USART_DMAReq_Tx, ENABLE); /* 关闭发送 DMA, 等待需要发送的时候再开启 */ DMA_Cmd(DMA1_Channel4, DISABLE); /* ------------------------ 接收通道 DMA ------------------------ */DMA_DeInit(DMA1_Channel5);DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&(USART1->DR);DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)debug_rxBuffer;DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC; DMA_InitStructure.DMA_BufferSize = 128;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_MemoryDataSize_Byte;DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;DMA_InitStructure.DMA_Priority = DMA_Priority_High;DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;DMA_Init(DMA1_Channel5, &DMA_InitStructure);DMA_ITConfig(DMA1_Channel5, DMA_IT_TC | DMA_IT_TE, ENABLE);USART_DMACmd(USART1, USART_DMAReq_Rx, ENABLE);DMA_Cmd(DMA1_Channel5, ENABLE);}void debug_usart_init(void){GPIO_InitTypeDef GPIO_InitStructure;USART_InitTypeDef USART_InitStructure;RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_USART1 | RCC_APB2Periph_AFIO, ENABLE);/* -------------------------- 端口配置 -------------------------- */GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_Init(GPIOA, &GPIO_InitStructure);GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; GPIO_Init(GPIOA, &GPIO_InitStructure);USART_InitStructure.USART_BaudRate = 115200;USART_InitStructure.USART_WordLength = USART_WordLength_8b;USART_InitStructure.USART_StopBits = USART_StopBits_1;USART_InitStructure.USART_Parity = USART_Parity_No;USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;USART_InitStructure.USART_Mode = USART_Mode_Tx | USART_Mode_Rx;USART_Init(USART1, &USART_InitStructure);/* 使能空闲中断 */ USART_ITConfig(USART1, USART_IT_IDLE, ENABLE); USART_Cmd(USART1, ENABLE);USART_ClearFlag(USART1, USART_FLAG_TC);debug_usart_dma_config();debug_usart_nvic_init();}void debug_tx_data(uint8_t str[], uint16_t leng){debug_tx_finished = 0;DMA_Cmd(DMA1_Channel4, DISABLE);/* 设置发送缓冲区 */DMA1_Channel4->CMAR = (uint32_t)str;/* 设置传输长度 */DMA_SetCurrDataCounter(DMA1_Channel4, leng);/* 启动 DMA 传输 */DMA_Cmd(DMA1_Channel4, ENABLE);}void debug_print(const char *fmt, ...){ uint8_t buf[128] = {0}; va_list vlist; va_start(vlist, fmt); vsnprintf(buf, sizeof(buf) - 1, fmt, vlist); debug_tx_data(buf, strlen(buf) + 1); va_end(vlist);}void DMA1_Channel4_IRQHandler(void){DMA_ClearITPendingBit(DMA1_IT_TC4 | DMA1_IT_TE4);DMA_Cmd(DMA1_Channel4, DISABLE);/* 标志发送完成 */ debug_tx_finished = 1; }void DMA1_Channel5_IRQHandler(void){DMA_ClearITPendingBit(DMA1_IT_TC5 | DMA1_IT_TE5);DMA_Cmd(DMA1_Channel5, DISABLE);DMA1_Channel5->CNDTR = 128;DMA_Cmd(DMA1_Channel5, ENABLE);}void USART1_IRQHandler(void){uint8_t i;/* 空闲中断 */ if ( USART_GetITStatus(USART1, USART_IT_IDLE ) != RESET ) {USART1->SR;USART1->DR;DMA_Cmd(DMA1_Channel5, DISABLE);DMA_ClearFlag(DMA1_FLAG_GL5 | DMA1_FLAG_TC5 | DMA1_FLAG_TE5 | DMA1_FLAG_HT5);/* 获取接收到的数据长度 */ debug_recv_leng = 128 - DMA_GetCurrDataCounter(DMA1_Channel5);/* * 将数据拷贝至用户空间 */memcpy((void *)debug_user_buffer, (void *)debug_rxBuffer, debug_recv_leng);/* 标志接收完成 */debug_rx_finished = 1; /* 重新设置最大接收长度 */ DMA1_Channel5->CNDTR = 128;/* 开启接收 */ DMA_Cmd(DMA1_Channel5, ENABLE);}/* 传输错误中断 */if ( USART_GetITStatus(USART1, USART_IT_PE | USART_IT_FE | USART_IT_NE) != RESET ){USART_ClearITPendingBit(USART1, USART_IT_PE | USART_IT_FE | USART_IT_NE);}USART_ClearITPendingBit(USART1, USART_IT_TC | USART_IT_IDLE);}
使用 :
接收帧数据 :
if ( debug_rx_finished ) /* 接收到一帧数据 */
{
fmu_rx_finished = 0;
..... /* 对接收的数据进行处理 */
}
发送数据 :
debug_tx_data(txbuffer, leng);
或者
debug_print(...);
具体的工程文件,见飞控移植记录
参考文章 :
http://home.eeworld.com.cn/my/space-uid-53362-blogid-242185.html
http://wenku.baidu.com/link?url=1TW4enqcu6iZyIq5F0JlhxQmap5PLRL3sd3k1r-QxxrYJcnaeg_eOfOabxYA18nt-IvTD6cj9InRMKb21W34T5mtxOyO3A22WnDXardkMRe