一、传统串口收发与引入DMA控制的区别
传统串口收发每一步都经过CPU处理和控制,当总线数据量大且频繁时CPU要反复地进入中断中处理,而引入DMA的差异就在于DMA会自动处理这个过程,并不需要占用CPU。
二、在不同芯片上所包含的DMA数量不同
对于stm32f103c8这颗芯片只有DMA1,但它有7个通道,每个通道对应不同外设 如下
三、对于DMA初始化及其配置
cpp
DMA_DeInit(DMA1_Channel4);
DMA_InitStructure.DMA_PeripheralBaseAddr = (u32)&USART1->DR;
DMA_InitStructure.DMA_MemoryBaseAddr = (u32)&UP_TxBuffer;
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;
DMA_InitStructure.DMA_BufferSize = 20;//TxMaxLen;
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_Channel4, &DMA_InitStructure);
DMA_DeInit(DMA1_Channel5);
DMA_InitStructure.DMA_PeripheralBaseAddr = (u32)&USART1->DR;
DMA_InitStructure.DMA_MemoryBaseAddr = (u32)&UP_RxBuffer;
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
// DMA_InitStructure.DMA_BufferSize = sizeof(RxBuffer);
DMA_InitStructure.DMA_BufferSize = 149;
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_VeryHigh;
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
DMA_ClearFlag(DMA1_FLAG_GL4|DMA1_FLAG_TC4|DMA1_FLAG_HT4|DMA1_FLAG_TE4);
DMA_Init(DMA1_Channel5,&DMA_InitStructure);
DMA_Cmd(DMA1_Channel5,ENABLE);
其中较为关键的三步:
DMA_InitStructure.DMA_PeripheralBaseAddr = (u32)&USART1->DR;
DMA_InitStructure.DMA_MemoryBaseAddr = (u32)&UP_TxBuffer;
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;
第一二步是两个容器的地址,即告诉DMA数据流的源头和目的地,第三步指明DMA的传输方向,从UP_TxBuffer首地址开始流向USART1->DR即实现了串口的发送。
接受亦是如此DMA_InitStructure.DMA_PeripheralBaseAddr = (u32)&USART1->DR;
DMA_InitStructure.DMA_MemoryBaseAddr = (u32)&UP_RxBuffer;
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
不同点在于第二步的缓冲区不同以及第三步的数据流向不同,这里为USART1->DR流向UP_Rxbuffer即实现了串口的接收。
四、关于数据帧的接收处理
cppvoid USART1_IRQHandler(void) { unsigned char i = 0,len = 0; //当总线空闲时,往外发送数据 // if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET){ // // } if(USART_GetITStatus(USART1, USART_IT_IDLE) != RESET){ //读串口SR和DR清除idle标志 DMA_Cmd(DMA1_Channel5,DISABLE); if(1){ len = 149 - DMA_GetCurrDataCounter(DMA1_Channel5); for(i = 0;i<len;i++){ UP_analysis_buf[i] = UP_RxBuffer[i]; } //uart1_send_string(UP_RxBuffer,strlen((char*)UP_RxBuffer)); //memset(UP_RxBuffer,0,149); } // if(sendFlag == 0){ // memset(UP_RxBuffer,0,149); // memset(UP_TxBuffer,0,20); DMA_SetCurrDataCounter(DMA1_Channel5,149); DMA_Cmd(DMA1_Channel5,ENABLE); USART_ClearITPendingBit(USART1,USART_IT_IDLE); i = USART1->SR; i = USART1->DR; sendFlag = 1; //} } // if(USART_GetITStatus(USART1, USART_IT_TC) != RESET) // { // USART_ClearITPendingBit(USART1,USART_IT_TC); // } }
这里利用串口的空闲中断,当总线产生一个空闲中断时代表一帧数据的结束,这时我们可对数据选择我们需要的长度进行备份从而方便解析。
五、数据的发送方法
如下对缓冲区进行填充并使能DMA相应通道就能自动处理这一过程。
cpp
memset(UP_TxBuffer,0,20);
for(i = 0;i<strlen(UP_RxBuffer);i++){
UP_TxBuffer[i] = UP_RxBuffer[i];
}
memset(UP_RxBuffer,0,149);
UP_TxBuffer[i] = '\0';
//snprintf(UP_TxBuffer,strlen(UP_RxBuffer)+1,UP_RxBuffer);
//strcpy(UP_TxBuffer,UP_RxBuffer);
DMA_SetCurrDataCounter(DMA1_Channel4,strlen(UP_TxBuffer));
DMA_Cmd(DMA1_Channel4 ,ENABLE);