新聞中心

EEPW首頁(yè) > 嵌入式系統(tǒng) > 設(shè)計(jì)應(yīng)用 > STM32使用DMA加串口空閑中斷接收數(shù)據(jù)

STM32使用DMA加串口空閑中斷接收數(shù)據(jù)

作者: 時(shí)間:2016-11-19 來(lái)源:網(wǎng)絡(luò) 收藏
STM32中,需要用串口接收數(shù)據(jù),是使用串口中斷來(lái)接收數(shù)據(jù)。但是用這種方法的話,就要頻繁進(jìn)入串口中斷,然后處理,效率就比較低。于是就想到用DMA來(lái)接收串口數(shù)據(jù),這個(gè)STM32也是支持的。但是關(guān)鍵的一點(diǎn),怎么知道數(shù)據(jù)接收完畢了呢?如果接收的數(shù)據(jù)長(zhǎng)度固定,那就好辦,直接設(shè)置DMA的接收數(shù)據(jù)個(gè)數(shù)就行了。但是如果長(zhǎng)度不固定了,那應(yīng)該怎么辦了?

這個(gè)時(shí)候,就要用到STM32在串口中提供的另一個(gè)好用的東西了,就是串口空閑中斷。在STM32的串口控制器中,設(shè)置了有串口空閑中斷,即如果串口空閑,又開(kāi)啟了串口空閑中斷的話,就觸發(fā)串口空閑中斷,然后程序就會(huì)跳到串口中斷去執(zhí)行。有了這個(gè),是不是可以判斷什么時(shí)候串口數(shù)據(jù)接收完畢了呢?因?yàn)榇跀?shù)據(jù)接收完畢后,串口總線肯定是會(huì)空閑的嘛,那這個(gè)中斷肯定是會(huì)觸發(fā)的了。

本文引用地址:http://butianyuan.cn/article/201611/318282.htm

還有一個(gè)問(wèn)題,這串口空閑中斷是只要串口空閑就會(huì)產(chǎn)生嗎?其實(shí)不是的,串口空閑中斷要觸發(fā)的話,是要RXNE位被置位后,串口總線空閑才會(huì)觸發(fā)的。所以我們不用擔(dān)心,串口數(shù)據(jù)發(fā)送完畢后,會(huì)不會(huì)觸發(fā)串口空閑中斷了。

下面用代碼來(lái)說(shuō)明。

1、配置串口。包括設(shè)置串口的引腳配置,串口的配置,串口中斷的配置,串口的接收DMA的配置

void USART_init(void){   GPIO_InitTypeDef   GPIO_InitStructure;    USART_InitTypeDef  USART_InitStructure;    NVIC_InitTypeDef   NVIC_InitStructure;     //開(kāi)啟時(shí)鐘    RCC_APB2PeriphClockCmd(USART_RCC,ENABLE);    //配置TX端口    GPIO_InitStructure.GPIO_Pin = GPIO_USART_TX;    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;    GPIO_Init(GPIO_USART_TYPE,&GPIO_InitStructure);    //配置RX端口    GPIO_InitStructure.GPIO_Pin = GPIO_USART_RX;    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;    GPIO_Init(GPIO_USART_TYPE,&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_Rx | USART_Mode_Tx;    USART_Init(USART1,&USART_InitStructure);       //中斷配置    NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;    NVIC_InitStructure.NVIC_IRQChannelSubPriority=0;    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;    NVIC_Init(&NVIC_InitStructure);     /* 若總線空閑,產(chǎn)生中斷 */    USART_ITConfig(USART1, USART_IT_IDLE, ENABLE);     /*開(kāi)啟串口DMA接收*/    USART_DMACmd(USART1,USART_DMAReq_Rx,ENABLE);       USART_Cmd(USART1,ENABLE);}

代碼比較簡(jiǎn)單,一看就明白了,這就是使用庫(kù)函數(shù)開(kāi)發(fā)的好處,代碼易懂。這里,關(guān)鍵的是要開(kāi)啟總線空閑中斷,并且開(kāi)啟串口DMA接收。注意,不要開(kāi)啟串口接收中斷,不然接收數(shù)據(jù)就會(huì)一直產(chǎn)生中斷了。

2、DMA配置

DMA配置,要先查看串口接收是使用的哪個(gè)DMA的哪個(gè)通道,對(duì)于USART1_RX使用的是DMA1的5通道。

然后就是代碼配置DMA了。

void DMA_init(void){   DMA_InitTypeDef    DMA_Initstructure;//   NVIC_InitTypeDef   NVIC_Initstructure;      /*開(kāi)啟DMA時(shí)鐘*/   RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1,ENABLE);  //   /* Enable the DMA1 Interrupt *///   NVIC_Initstructure.NVIC_IRQChannel = DMA1_Channel4_IRQn;       //通道設(shè)置為串口1中斷//   NVIC_Initstructure.NVIC_IRQChannelSubPriority = 1;     //中斷響應(yīng)優(yōu)先級(jí)0//   NVIC_Initstructure.NVIC_IRQChannelPreemptionPriority=1;//   NVIC_Initstructure.NVIC_IRQChannelCmd = ENABLE;        //打開(kāi)中斷//   NVIC_Init(&NVIC_Initstructure);    /*DMA配置*/   DMA_Initstructure.DMA_PeripheralBaseAddr =  (u32)(&USART1->DR);;   DMA_Initstructure.DMA_MemoryBaseAddr     = (u32)receive_data;   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_Normal;   DMA_Initstructure.DMA_Priority = DMA_Priority_High;   DMA_Initstructure.DMA_M2M = DMA_M2M_Disable;   DMA_Init(DMA1_Channel5,&DMA_Initstructure);     //啟動(dòng)DMA   DMA_Cmd(DMA1_Channel5,ENABLE);    //開(kāi)啟DMA發(fā)送發(fā)成中斷   //DMA_ITConfig(DMA1_Channel4,DMA_IT_TC,ENABLE); }

因?yàn)檫@里,不需要用到DMA中斷,所以DMA中斷就不要使能了。因此DMA中斷配置也就不需要了。這里,關(guān)鍵的是要設(shè)置DMA_DIR為DMA_DIR_PeripheralSRC,表示數(shù)據(jù)是從外設(shè)到內(nèi)存。這里設(shè)定的DMA_Mode是普通模式,即數(shù)據(jù)傳輸就只能一次。

3、串口中斷程序編寫(xiě)

這個(gè)就是關(guān)鍵的地方了。在這里,需要做什么了。需要對(duì)DMA設(shè)置下。當(dāng)進(jìn)入這個(gè)中斷的時(shí)候,串口接收的數(shù)據(jù),已經(jīng)在內(nèi)存的數(shù)組中了。通過(guò)讀取DMA的計(jì)數(shù)值,就可以知道接收到了多少個(gè)數(shù)據(jù)。然后再把DMA給diable掉,重新設(shè)置接收數(shù)據(jù)長(zhǎng)度,在開(kāi)啟DMA,接收下一次串口數(shù)據(jù)。為什么要這么做了,因?yàn)樵赟TM32手冊(cè)中有如下說(shuō)明:

另外還有一點(diǎn),串口空閑中斷觸發(fā)后,硬件會(huì)自動(dòng)將串口空閑中斷標(biāo)志位給置1,我們是需要將給標(biāo)志位給置0的,不然又要進(jìn)中斷了,這個(gè)在手冊(cè)中也有說(shuō)明。

代碼就如下了:

void USART1_IRQHandler(void){    unsigned char num=0;    if(USART_GetITStatus(USART1,USART_IT_IDLE) == SET)    {       num = USART1->SR;       num = USART1->DR; //清USART_IT_IDLE標(biāo)志       DMA_Cmd(DMA1_Channel5,DISABLE);    //關(guān)閉DMA       num = 128 -  DMA_GetCurrDataCounter(DMA1_Channel5);      //得到真正接收數(shù)據(jù)個(gè)數(shù)         receive_data[num] =