串口+DMA解決數據接收的難題
之前由于項目需要不停地利用串口接收數據,剛開始的時候采用單字節中斷的方式接收判斷。但是用來做通信的時候需要不停的產生串口接收中斷,會嚴重影響主程序的運行。后來采用DMA接收的方式,但是一般情況下配置的DMA都是接收指定長度的串口數據,對于未知長度的串口數據接收并不適用。后來在網上發現了一種方法可以利用串口的 空閑中斷+DMA接收的方法可解決此類問題,特別適用于不需要每個接收字節都判斷的串口數據接收,下面簡單介紹一下。
思路:采用APM32E103的串口1,并配置成空閑中斷模式且使能DMA接收,并同時設置接收緩沖區和初始化DMA。當初始化完成之后,外部給MCU發送數據的時候,假設這幀數據長度是100個字節,那么在MCU中接收到一個字節的時候并不會產生串口中斷,而是DMA在后臺把數據全部搬運到你指定的緩沖區里面,當整幀數據發送完畢之后串口才會產生一次中斷,此時可以利用CurrDataCounterBegin = DMA_ReadDataNumber(DMA1_Channel6);計算出本次的數據接受長度,從而進行數據處理。
關鍵代碼分析:
#include "public.h"
#ifndef __USART_H
#define __USART_H
#include "stdio.h"
volatile uint32_t CurrDataCounterBegin = 0;
#define DMA_Rec_Len 256 //定義一個256個字節的數據緩沖區。
void uartInit(void)
{
//GPIO端口設置
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
DMA_InitTypeDef DMA_InitStructure;
RCM_EnableAPB2PeriphClock((RCM_APB2_PERIPH_T)(RCM_APB2_PERIPH_GPIOA | RCM_APB2_PERIPH_USART1)); //使能USART1,GPIOA時鐘
RCM_EnableAHBPeriphClock(RCM_AHB_PERIPH_DMA1); //使能DMA傳輸
USART_DeInit(USART1);//復位串口1
//USART1_TX PA9
GPIO_InitStructure.pin = GPIO_PIN_9; //PA.9
GPIO_InitStructure.speed = GPIO_SPEED_50MHz;
GPIO_InitStructure.mode = GPIO_MODE_AF_PP; //復用推挽輸出
GPIO_Config(GPIOA, &GPIO_InitStructure); //初始化PA9
//USART1_RXA10
GPIO_InitStructure.pin = GPIO_PIN_10;
GPIO_InitStructure.mode = GPIO_MODE_IN_FLOATING;//浮空輸入
GPIO_Config(GPIOA, &GPIO_InitStructure);//初始化PA10
//USART 初始化設置
USART_InitStructure.USART_BaudRate = 115200;
USART_InitStructure.USART_WordLength = USART_WORD_LEN_8B;//字長為8位數據格式
USART_InitStructure.USART_StopBits = USART_STOP_BIT_1;//一個停止位
USART_InitStructure.USART_Parity = USART_PARITY_NONE;//無奇偶校驗位
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//無硬件數據流控制
USART_InitStructure.USART_Mode = USART_MODE_TX | USART_MODE_RX; //收發模式
USART_Config(USART1, &USART_InitStructure);
USART_EnableInterrupt(USART1, USART_INT_IDLE);//開啟空閑中斷
USART_EnableDMA(USART1,USART_DMA_RX); //使能串口1 DMA接收
USART_Enable(USART1); //使能串口
// NVIC 配置
NVIC_EnableIRQRequest(DMA1_Channel6_IRQn, 3, 2);
//相應的DMA配置
DMA_Reset(DMA1_Channel6);
DMA_InitStructure.DMA_PeripheralBaseAddr = (u32)&USART1->DR;//DMA外設ADC基地址
DMA_InitStructure.DMA_MemoryBaseAddr = (u32)DMA_Rece_Buf;//DMA內存基地址
DMA_InitStructure.DMA_DIR = DMA_DIR_PERIPHERAL_SRC;//數據傳輸方向,從外設讀取發送到內存
DMA_InitStructure.DMA_BufferSize = DMA_Rec_Len;//DMA通道的DMA緩存的大小
DMA_InitStructure.DMA_PeripheralInc = DMA_PERIPHERAL_INC_DISABLE;//外設地址寄存器不變
DMA_InitStructure.DMA_MemoryInc = DMA_MEMORY_INC_ENABLE;//內存地址寄存器遞增
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PERIPHERAL_DATA_SIZE_WOED;//數據寬度為8位
DMA_InitStructure.DMA_MemoryDataSize = DMA_MEMORY_DATA_SIZE_WOED; //數據寬度為8位
DMA_InitStructure.DMA_Mode = DMA_MODE_NORMAL;//工作在正常緩存模式
DMA_InitStructure.DMA_Priority = DMA_PRIORITY_HIGH; //DMA通道 x擁有中優先級
DMA_InitStructure.DMA_M2M = DMA_M2MEN_ENABLE;//DMA通道x沒有設置為內存到內存傳輸
DMA_Init(DMA1_Channel6, &DMA_InitStructure);//根據DMA_InitStruct中指定的參數初始化DMA的通道
DMA_EnableInterrupt(DMA1_Channel6, DMA_INT_TC);
CurrDataCounterBegin = DMA_ReadDataNumber(DMA1_Channel6);
DMA_Enable(DMA1_Channel6);
}
//串口中斷函數
void USART1_IRQHandler(void) //串口1中斷服務程序
{
if(USART_ReadStatusFlag(USART1, USART_FLAG_IDLE) != RESET)//接收中斷(接收到的數據必須是0x0d 0x0a結尾)
{
USART_RxData(USART1);//讀取數據 注意:這句必須要,否則不能夠清除中斷標志位。
Usart1_Rec_Cnt = DMA_Rec_Len-CurrDataCounterBegin(DMA1_Channel6); //算出接本幀數據長度
//幀數據處理函數
printf ("The lenght:%d ",Usart1_Rec_Cnt);
printf ("The data: ");
USART_TxData(DMA_Rece_Buf,Usart1_Rec_Cnt);
USART_ClearIntFlag(USART1, USART_FLAG_IDLE); //清除中斷標志
DMA_Enable(DMA1_Channel6); //恢復DMA指針,等待下一次的接收
}
原文標題:APM32芯得 EP.03 | APM32E103串口+DMA解決數據接收的困擾
文章出處:【微信公眾號:RTThread物聯網操作系統】歡迎添加關注!文章轉載請注明出處。
-
mcu
+關注
關注
146文章
17171瀏覽量
351512 -
dma
+關注
關注
3文章
562瀏覽量
100635 -
串口數據
+關注
關注
0文章
33瀏覽量
13899
原文標題:APM32芯得 EP.03 | APM32E103串口+DMA解決數據接收的困擾
文章出處:【微信號:RTThread,微信公眾號:RTThread物聯網操作系統】歡迎添加關注!文章轉載請注明出處。
發布評論請先 登錄
相關推薦
評論