1.引言
????????最近因为中断冲突问题,导致串口接收数据时随机丢失一两个字节。无奈串口的中断优先级不能是最高的,所以中断冲突问题明显存在,因此需要串口使用DMA方式来接收数据,从而规避串口接收数据丢失问题。
2.DMA配置经验
? ? ? ? 首次使用dma,需要了解一下dma的知识。
? ? ? ? 1.首先最重要要了解的一点就是,dma接收数据一定要接收到指定长度的数据,或指定长度的一半数据才能产生接收中断,如果数据没有达到一半或指定长度,就无法产生dma接收数据通知。
? ? ? ? 一般解决这个问题是串口空闲中断IDE方式解决。也就是串口传完数据产生空闲中断后去读取DMA收到的数据,就能接收不定长度的数据了。
? ? ? ? 然而现实很悲催,我所使用的cortex-m0芯片,居然没有串口空闲中断,为此需要另寻它法。所以就选择了在while(1)循环中接收DMA数据了。
? ? ? ? 2.既然配置了串口的DMA接收,因此就不需要启动串口接收中断了,但串口的收发模式配置还是需要的。如下:
UART_InitStructure.UART_Mode = UART_Mode_Rx | UART_Mode_Tx; //收发模式
? ? ? ? 3.DMA模式选择
????????DMA_Mode_Normal和DMA_Mode_Circular模式,指的是单次和多次的问题。如果是DMA_Mode_Normal模式,进行完一次DMA数据传输后,要启动第二次DMA传输,需先关闭该DMA通道,重新设置DMA传输数据个数,再开启DMA通道。而DMA_Mode_Circular模式则不需要,其会自动配置。
? ? ? ? ?下述代码中有这个体现,DMA_Mode_Normal模式下如果不重新配置,则收发就不正常。
? ? ? ? 4.查看串口对应的DMA channel
? ? ? ? 对于STM32F030单片机UART1引脚对应的DMA channel通道,可以看STM32F030参考手册,而不是芯片手册!!!!别找错文档了。然后查所使用的的引脚对应的通道选择就行。
? ? ? ? ?我所使用的是USART1,并且没有重映射RX到其它引脚,因此是channel3,这个实际的时候要注意,别搞错了,当然搞错了也没关系,试一下就好了,花不了什么时间。
?
3.实验代码
#define UART1_RX_LEN (64)
uint8_t tUart1_Rx[UART1_RX_LEN] = {0};
void UART1_Init(void)
{
//GPIO端口设置
GPIO_InitTypeDef GPIO_InitStructure = {0};
NVIC_InitTypeDef NVIC_InitStructure = {0};
UART_InitTypeDef UART_InitStructure = {0};
RCC_APB2PeriphClockCmd(RCC_APB2Periph_UART1, ENABLE); //使能UART1,GPIOA时钟
/
//1.引脚初始化
GPIO_PinAFConfig(GPIOB,GPIO_PinSource6,GPIO_AF_0);
GPIO_PinAFConfig(GPIOB,GPIO_PinSource7,GPIO_AF_0);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6; //PA.9
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出
GPIO_Init(GPIOB, &GPIO_InitStructure);//初始化GPIOA.9
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7;//PA10
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;//浮空输入
GPIO_Init(GPIOB, &GPIO_InitStructure);//初始化GPIOA.10
/
//2.串口初始化
UART_InitStructure.UART_BaudRate = 115200;//串口波特率
UART_InitStructure.UART_WordLength = UART_WordLength_8b;//字长为8位数据格式
UART_InitStructure.UART_StopBits = UART_StopBits_1;//一个停止位
UART_InitStructure.UART_Parity = UART_Parity_No;//无奇偶校验位
UART_InitStructure.UART_HardwareFlowControl = UART_HardwareFlowControl_None;//无硬件数据流控制
UART_InitStructure.UART_Mode = UART_Mode_Rx | UART_Mode_Tx; //收发模式
UART_Init(UART1, &UART_InitStructure); //初始化串口1
UART_DMACmd(UART1, UART_DMAReq_EN, ENABLE);
UART_Cmd(UART1, ENABLE);
//dma配置
UART1_DMAInit();
}
void UART1_DMAInit(void)
{
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
/
//dma初始化 p -> m
DMA_DeInit(DMA1_Channel3);
DMA_Cmd(DMA1_Channel3, DISABLE);
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&UART1->RDR;//外设数据地址
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)tUart1_Rx; //自己的接收buf
DMA_InitStructure.DMA_BufferSize = UART1_RX_LEN; // 缓存大小
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; // 内存到内存关闭
DMA_InitStructure.DMA_Mode = DMA_Mode_Normal; // 普通模式
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC; // 外设到内存
DMA_InitStructure.DMA_Priority = DMA_Priority_High; // DMA通道优先级
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;// 内存地址递增
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;//外设基地址不需要递增
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
DMA_Init(DMA1_Channel3,&DMA_InitStructure);
DMA_Cmd(DMA1_Channel3, ENABLE);
/
}
//因为该单片机没有串口空闲中断
//所以只能使用poll接收dma数据
//该函数放在while(1)中即可
void UART1_DMAChannel3_rcvTask(void)
{
uint16_t rxSize = 0;
//获取当前接收到的数据大小
rxSize = UART1_RX_LEN - DMA_GetCurrDataCounter(DMA1_Channel3);
//如果数据大于0,说明开始接收数据了
if(rxSize>0)
{
//休眠10ms,是因为让串口dma继续接收数据
//根据115200波特率计算,10ms足够传输1024字节大小的数据
//所以delay 10ms基本满足单次数据的收
delay_ms(50);
DMA_Cmd (DMA1_Channel3,DISABLE);//重新配置DMA时,需要先disableDMA
//接收完成,再获取一次实际的数据长度
rxSize = UART1_RX_LEN - DMA_GetCurrDataCounter(DMA1_Channel3);
//接收数据,写入buf
BleClient_RcvDataBufIn(tUart1_Rx, rxSize);
memset(tUart1_Rx,0,UART1_RX_LEN); //清空数组
DMA_Init(DMA1_Channel3, &DMA_InitStructure); //重新配置一次DMA
DMA_Cmd (DMA1_Channel3,ENABLE);
}
}
over!
|