1 簡介
stm32串口的配置很簡單,這里就不贅述了,使用USART_SendData() 阻塞模式發(fā)送數(shù)據(jù),或是接收中斷配置 “接收緩沖區(qū)非空” USART_IT_RXNE,這種做法效率很低,而且來一個數(shù)據(jù)中斷一次數(shù)據(jù)處理起來也麻煩。
這里基于STM32F407提供一種串口空閑中斷+DMA接收的方式,通過庫函數(shù)編程實現(xiàn)。
1.1 什么是串口空閑中斷
初學(xué)者一開始學(xué)習(xí)配置串口中斷時經(jīng)常將中斷條件配置為USART_IT_RXNE,也就是接收緩沖區(qū)非空,每接收一個U8數(shù)據(jù)便產(chǎn)生一次中斷,非常消耗系統(tǒng)資源。
而空閑中斷USART_IT_IDLE,俗稱幀中斷,即第一幀數(shù)據(jù)接收完畢到第二幀數(shù)據(jù)開始接收期間存在一個空閑狀態(tài)(就是沒數(shù)據(jù)接收的狀態(tài)),檢測到此空閑狀態(tài)后即執(zhí)行中斷程序。進入中斷程序即意味著已經(jīng)接收到一組完整幀數(shù)據(jù),僅需及時對數(shù)據(jù)處理或?qū)?shù)據(jù)轉(zhuǎn)移出緩沖區(qū)即可。
1.2 DMA簡介
DMA–DirectMemoryAccess(存儲器直接訪問)是指一種高速的數(shù)據(jù)傳輸操作,允許在外部設(shè)備和存儲器之間直接讀寫數(shù)據(jù),既不通過CPU,也不需要CPU干預(yù)。整個數(shù)據(jù)傳輸操作在一個稱為“DMA控制器”的控制下進行。CPU除了在數(shù)據(jù)傳輸開始和結(jié)束時做一點處理外,在傳輸過程中還可以進行其他的工作。這樣,在大部分時間里,CPU和輸入輸出都處于并行操作,因此使整個計算機系統(tǒng)的效率大大提高。
1.3 DMA模式
STM32中DMA有3種傳輸模式,如下表所示
其中DMA2支持上表3種傳輸模式,而DMA1只支持外設(shè)到存儲器和存儲器到外設(shè)兩種模式。
1.4 DMA請求映射
每一個外設(shè)請求都會占用一個數(shù)據(jù)流通道,同一個外設(shè)請求可以占用不同的數(shù)據(jù)流通道,如下表所示
例如本次使用的是STM32F407的USART1的,查上表發(fā)現(xiàn)串口1的發(fā)送USART1_TX是通道4數(shù)據(jù)流7,USART1_RX是通道4數(shù)據(jù)流5或通道4數(shù)據(jù)流2
1.4 DMA配置簡述
1.配置DMA結(jié)構(gòu)體 DMA_InitTypeDef ,使用DMA_Init()初始化DMA配置
2.啟用外設(shè)(串口)DMA接口 USART_DMACmd()
3.使能DMA DMA_Cmd()
DMA結(jié)構(gòu)體 DMA_InitTypeDef 具體成員含義自行參考庫函數(shù)源代碼說明。
2 DMA收發(fā)代碼實現(xiàn)
2.1 定義收發(fā)結(jié)構(gòu)體
為了方便管理,定義了收發(fā)緩沖區(qū)的結(jié)構(gòu)體。
#define RECEIVE_BUF_MAX_SIZE 100 //DMA單次最大搬運數(shù)據(jù)量
#define TRANSMIT_BUF_MAX_SIZE 50 //DMA單次最大搬運數(shù)據(jù)量
///定義數(shù)據(jù)接收結(jié)構(gòu)體
typedef struct _ReceiveBuffer{
uint8_t Buffer[RECEIVE_BUF_MAX_SIZE];//用于接收DMA搬運的接收數(shù)據(jù)
uint16_t Lenth;//接收的數(shù)據(jù)長度
}ReceiveBuffer_t;
///定義數(shù)據(jù)發(fā)送結(jié)構(gòu)體
typedef struct _TransmitBuffer{
uint8_t Buffer[TRANSMIT_BUF_MAX_SIZE];//用于接收DMA搬運的發(fā)送數(shù)據(jù)
uint16_t Lenth;//發(fā)送的數(shù)據(jù)長度
}TransmitBuffer_t;
static ReceiveBuffer_t ReceiveBuffer;//數(shù)據(jù)接收結(jié)構(gòu)體
static TransmitBuffer_t TransmitBuffer;//數(shù)據(jù)發(fā)送結(jié)構(gòu)體
/*DMA接收配置結(jié)構(gòu)體*/
static DMA_InitTypeDef DMA_TransmitInitStruct;
2.2 DMA配置
1.DMA接收配置
配置根據(jù)上表查詢的數(shù)據(jù)流通道,設(shè)置方向為外設(shè)到存儲器,配置外設(shè)地址及存儲器地址,設(shè)置最大搬運數(shù)量。
static void USART_ReceiveDMA_init(void){
DMA_InitTypeDef DMA_ReceiveInitStruct;
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2, ENABLE);
DMA_ReceiveInitStruct.DMA_Channel = DMA_Channel_4; //設(shè)置DMA通道
DMA_ReceiveInitStruct.DMA_PeripheralBaseAddr = (uint32_t) &USART1->DR;//設(shè)置外設(shè)地址
DMA_ReceiveInitStruct.DMA_Memory0BaseAddr = (uint32_t) ReceiveBuffer.Buffer;//設(shè)置內(nèi)存地址
DMA_ReceiveInitStruct.DMA_DIR = DMA_DIR_PeripheralToMemory;//設(shè)置搬運方向:外設(shè)到內(nèi)存
DMA_ReceiveInitStruct.DMA_BufferSize = RECEIVE_BUF_MAX_SIZE;//搬運數(shù)據(jù)長度
DMA_ReceiveInitStruct.DMA_PeripheralInc = DMA_PeripheralInc_Disable;//外設(shè)地址在搬運過程中是否自增(設(shè)置不自增)
DMA_ReceiveInitStruct.DMA_MemoryInc = DMA_MemoryInc_Enable;//內(nèi)存地址在搬運過程中是否自增(設(shè)置自增)
DMA_ReceiveInitStruct.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;//數(shù)據(jù)大小為一個字節(jié)
DMA_ReceiveInitStruct.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;//數(shù)據(jù)大小為一個字節(jié)
DMA_ReceiveInitStruct.DMA_Mode = DMA_Mode_Normal;//無需循環(huán)
DMA_ReceiveInitStruct.DMA_Priority = DMA_Priority_Medium;
DMA_ReceiveInitStruct.DMA_FIFOMode = DMA_FIFOMode_Disable;
DMA_ReceiveInitStruct.DMA_FIFOThreshold = DMA_FIFOThreshold_Full;
DMA_ReceiveInitStruct.DMA_MemoryBurst = DMA_MemoryBurst_Single;
DMA_ReceiveInitStruct.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;
DMA_Init(DMA2_Stream5, &DMA_ReceiveInitStruct);
USART_DMACmd(USART1,USART_DMAReq_Rx,ENABLE);
DMA_Cmd(DMA2_Stream5, ENABLE);//默認(rèn)開啟DMA接收
}
2.DMA發(fā)送配置
與DMA接收類似,只是現(xiàn)在是從存儲器到外設(shè)。
static void USART_TransmitDMA_init(void){
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2, ENABLE);
DMA_TransmitInitStruct.DMA_Channel = DMA_Channel_4;
DMA_TransmitInitStruct.DMA_PeripheralBaseAddr = (uint32_t) &USART1->DR;
DMA_TransmitInitStruct.DMA_Memory0BaseAddr = (uint32_t) TransmitBuffer.Buffer;
DMA_TransmitInitStruct.DMA_DIR = DMA_DIR_MemoryToPeripheral;
DMA_TransmitInitStruct.DMA_BufferSize =TRANSMIT_BUF_MAX_SIZE;
DMA_TransmitInitStruct.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
DMA_TransmitInitStruct.DMA_MemoryInc = DMA_MemoryInc_Enable;
DMA_TransmitInitStruct.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
DMA_TransmitInitStruct.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
DMA_TransmitInitStruct.DMA_Mode = DMA_Mode_Normal;
DMA_TransmitInitStruct.DMA_Priority = DMA_Priority_Medium;
DMA_TransmitInitStruct.DMA_FIFOMode = DMA_FIFOMode_Disable;
DMA_TransmitInitStruct.DMA_FIFOThreshold = DMA_FIFOThreshold_Full;
DMA_TransmitInitStruct.DMA_MemoryBurst = DMA_MemoryBurst_Single;
DMA_TransmitInitStruct.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;
DMA_Init(DMA2_Stream7, &DMA_TransmitInitStruct);
USART_DMACmd(USART1,USART_DMAReq_Tx,ENABLE);
DMA_Cmd(DMA2_Stream7, DISABLE);//默認(rèn)不開啟DMA發(fā)送,需要后續(xù)觸發(fā)
}
2.3 串口配置
串口配置很簡單,這里不贅述。文章來源:http://www.zghlxwxcb.cn/news/detail-604186.html
static void USART_init(void){
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);
//GPIO端口設(shè)置
//串口1對應(yīng)引腳復(fù)用映射
GPIO_PinAFConfig(GPIOA,GPIO_PinSource9,GPIO_AF_USART1); //GPIO復(fù)用
GPIO_PinAFConfig(GPIOA,GPIO_PinSource10,GPIO_AF_USART1); //GPIO復(fù)用
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9 | GPIO_Pin_10;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;//復(fù)用功能
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz; //速度50MHz
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; //開漏
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; //上拉
GPIO_Init(GPIOA,&GPIO_InitStructure); //初始化管腳
//USART1 初始化設(shè)置
USART_InitStructure.USART_BaudRate = 115200;//波特率設(shè)置
USART_InitStructure.USART_WordLength = USART_WordLength_8b;//字長為8位數(shù)據(jù)格式
USART_InitStructure.USART_StopBits = USART_StopBits_1;//一個停止位
USART_InitStructure.USART_Parity = USART_Parity_No;//無奇偶校驗位
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//無硬件數(shù)據(jù)流控制
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; //收發(fā)模式
USART_Init(USART1, &USART_InitStructure); //初始化串口
USART_Cmd(USART1, ENABLE); //使能串口
//收發(fā)DMA配置
BLE_USART_ReceiveDMA_init();//接收DMA配置
BLE_USART_TransmitDMA_init();//發(fā)送DMA配置
//配置中斷
BLE_USART_NVIC_Config();
}
2.4 中斷配置
1.中斷配置文章來源地址http://www.zghlxwxcb.cn/news/detail-604186.html
static void USART_NVIC_Config(void){
//配置中斷
NVIC_InitTypeDef NVIC_InitStructure;
//USART 空閑NVIC
NVIC_InitStructure.NVIC_IRQChannel =USART1_IRQn;//串口1中斷通道
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3;//搶占優(yōu)先級3
NVIC_InitStructure.NVIC_IRQChannelSubPriority =3; //子優(yōu)先級3
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能
NVIC_Init(&NVIC_InitStructure); //根據(jù)指定的參數(shù)初始化VIC寄存器
USART_ITConfig(USART1, USART_IT_IDLE, ENABLE);//開啟串口空閑中斷
//DMA NVIC
NVIC_InitStructure.NVIC_IRQChannel =DMA2_Stream5_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3;
NVIC_InitStructure.NVIC_IRQChannelSubPriority =3;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
DMA_ITConfig(DMA2_Stream5,DMA_IT_TC,ENABLE);
}
- 串口中斷函數(shù)
串口中斷配置為空閑中斷,當(dāng)一幀數(shù)據(jù)接收完畢后與下一幀數(shù)據(jù)到達之前有一段空閑,系統(tǒng)會在這段空閑時進入中斷,我們通過DMA搬運計數(shù)可以計算出DMA搬運了多少數(shù)據(jù)(即一幀數(shù)據(jù)的大小),并在該中斷中處理這個數(shù)據(jù)幀。
void USART1_IRQHandler(void)
{
//空閑中斷
if(USART_GetITStatus(USART1,USART_IT_IDLE) != RESET){
uint8_t clear;
DMA_Cmd(DMA2_Stream5, DISABLE); //關(guān)閉接收DMA,防止處理其間有數(shù)據(jù)
clear = USART1->SR;
clear = USART1->DR; //清除IDLE標(biāo)志
USART_ClearITPendingBit(USART1,USART_IT_IDLE);//清除標(biāo)志位
ReceiveBuffer.Lenth=RECEIVE_BUF_MAX_SIZE - DMA_GetCurrDataCounter(DMA2_Stream5);
/*
此處添加處理數(shù)據(jù)的代碼
*/
DMA_ClearFlag(DMA2_Stream5,DMA_FLAG_TCIF5 | DMA_FLAG_FEIF5 | DMA_FLAG_DMEIF5 | DMA_FLAG_TEIF5 | DMA_FLAG_HTIF5);//清除DMA2_Steam5傳輸完成標(biāo)志
DMA_SetCurrDataCounter(DMA2_Stream5, RECEIVE_BUF_MAX_SIZE);
DMA_Cmd(DMA2_Stream5, ENABLE);
}
}
- DMA中斷函數(shù)
void DMA2_Stream5_IRQHandler(void)
{
//清除標(biāo)志
if(DMA_GetFlagStatus(DMA2_Stream5,DMA_FLAG_TCIF5)!=RESET)//等待DMA2_Steam7傳輸完成
{
DMA_Cmd(DMA2_Stream5, DISABLE); //關(guān)閉DMA,防止處理期間有數(shù)據(jù)
ReceiveBuffer.Lenth =RECEIVE_BUF_MAX_SIZE - DMA_GetCurrDataCounter(DMA2_Stream5);
if(ReceiveBuffer.Lenth !=0)
{
/*RC_Process();//處理緩沖區(qū)ReceiveBuffer的數(shù)據(jù)*/
}
DMA_ClearFlag(DMA2_Stream5,DMA_FLAG_TCIF5 | DMA_FLAG_FEIF5 | DMA_FLAG_DMEIF5 | DMA_FLAG_TEIF5 | DMA_FLAG_HTIF5);//清除DMA2_Steam7傳輸完成標(biāo)志
DMA_SetCurrDataCounter(DMA2_Stream5, RECEIVE_BUF_MAX_SIZE);
DMA_Cmd(DMA2_Stream5, ENABLE); //打開DMA
}
}
2.5 DMA發(fā)送
void DMA_Send(char* data){
uint16_t datalenth=strlen(data);
memcpy(TransmitBuffer.Buffer,data,datalenth);
DMA_TransmitInitStruct.DMA_BufferSize=datalenth;
DMA_DeInit(DMA2_Stream7);//先關(guān)閉DMA
while (DMA_GetCmdStatus(DMA2_Stream7) != DISABLE){}//等待DMA可配置
DMA_Init(DMA2_Stream7, &DMA_TransmitInitStruct);
DMA_Cmd(DMA2_Stream7, ENABLE); //開啟DMA傳輸
}
到了這里,關(guān)于Stm32407串口1空閑中斷+DMA收發(fā)(基于標(biāo)準(zhǔn)庫實現(xiàn))的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!