新聞中心

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ò) 收藏
一直以來(lái)都為串口接收數(shù)據(jù)所困擾:
1:如果用接收中斷的話,每接收1byte就得中斷一次。這樣太消耗CPU資源!
2:如果用DMA方式接收數(shù)據(jù),那么如何確定接收數(shù)據(jù)的長(zhǎng)度又不好確定了。(比如GPRS模塊AT命令的接收?。?br />3:DMA方式接收+定時(shí)器的超時(shí)中斷。這樣處理也比較復(fù)雜,需要開定時(shí)器,關(guān)定時(shí)器。。。。個(gè)人不喜歡?。ˋTMEL的ARM系列的串口倒是有硬件超時(shí)中斷可以直接使用。我現(xiàn)在用AT91SAM7系列處理GPRS的AT命令就采用這種方式,挺好用。但是STM32就沒(méi)有了,需要自己加定時(shí)器,還要硬件處理:RXD連接定時(shí)器的一個(gè)觸發(fā)引腳!)。

所以之前用STM32接收串口數(shù)據(jù)都是采用接收中斷,然后寫入一個(gè)FIFO隊(duì)列。然后在主函數(shù)里面去查詢隊(duì)列緩沖中是否有數(shù)據(jù)需要處理。但是這樣的話,串口中斷服務(wù)函數(shù)始終是很大的硬件開銷。比如我現(xiàn)在用串口下載STM32的升級(jí)固件的時(shí)候,數(shù)據(jù)量較大。

廢話完畢,今天突然腦子發(fā)熱想要把DMA和環(huán)形的FIFO隊(duì)列結(jié)合一下使用。把想法跟同事交流一下,覺得有可行性!馬上動(dòng)手實(shí)驗(yàn)。經(jīng)過(guò)半天調(diào)試,結(jié)果令人滿意。
說(shuō)說(shuō)我的思路(本人表達(dá)能力有限,描述不清楚的希望大家跟帖):關(guān)在在于讓DMA來(lái)實(shí)現(xiàn)“環(huán)形隊(duì)列中往緩沖區(qū)寫入1byte”的功能!剩下的讀取隊(duì)列就跟普通環(huán)形隊(duì)列沒(méi)多大區(qū)別了。這樣我們的程序中擁有了一個(gè)不占用CPU資源的“環(huán)形隊(duì)列”后,我們就不用擔(dān)心CPU頻繁中斷,我們只需要在適當(dāng)?shù)臅r(shí)間讀取隊(duì)列中的數(shù)據(jù)然后慢慢分析處理數(shù)據(jù)!
A:串口初始化配置串口為DMA方式接收數(shù)據(jù)。具體配置請(qǐng)看:

DMA1_Channel5->CCR = DMA_CCR5_PL //通道優(yōu)先級(jí)最高
| DMA_CCR5_MINC //MEM地址增量使能
| DMA_CCR5_CIRC //接收緩沖區(qū)循環(huán)模式
| DMA_CCR5_TCIE //傳輸完成中斷
;
DMA1->IFCR |= 0x000F0000;
DMA1_Channel5->CPAR = USART1_BASE + 4;
// Enable the DMA1_CH5 Interrupt
NVIC_InitStructure.NVIC_IRQChannel = DMA1_Channel5_IRQChannel;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);

關(guān)鍵,開啟DMA循環(huán)模式,這樣接收完之后會(huì)自動(dòng)回到FIFO緩沖區(qū)開頭地方,這樣能省不少事情。
當(dāng)然,考慮到可能把緩沖區(qū)撐爆的情況,所以開啟通道傳輸完成標(biāo)志位,在傳輸完成中斷中查詢一下隊(duì)列中有多少數(shù)據(jù)沒(méi)有讀取出來(lái),如果太多數(shù)據(jù)沒(méi)有讀取,那么在中斷里面處理讀取FIFO數(shù)據(jù)并做相應(yīng)處理!

B:關(guān)于FIFO的一些聲明:
#define FIFO_OK 0
#define FIFO_ERROR_PARAM -1
#define FIFO_ERROR_MEM -2
#define FIFO_ERROR_FULL -3
#define FIFO_ERROR_EMPTY -4
#define FIFO_ERROR_BUSY -5

typedef struct _FIFO_TYPE_
{
INT32U size; //FIFO緩沖區(qū)大小
INT32U front; //FIFO下一讀取位置
INT32U staraddr; //FIFO緩沖區(qū)起始地址
INT32U endaddr; //FIFO緩沖區(qū)結(jié)束地址
INT8U buffer[1]; //實(shí)際長(zhǎng)度由初始化分配內(nèi)存!(memloc的時(shí)候確定)
}FIFOTYPE;


C:關(guān)于FIFO隊(duì)列的初始化,具體配置
//
//函數(shù):FIFO_Init
//參數(shù):FIFO類型的指針地址,隊(duì)列大小
//返回:>=0初始化成功
//描述:初始化FIFO隊(duì)列
//
Int32S FIFO_Init(FIFOTYPE * *fifo,INT32U fifosize)
{
volatile INT32U da;
if(fifo==NULL || fifosize == 0)
{
return FIFO_ERROR_PARAM;
}
(*fifo) = malloc(16+fifosize);
if((*fifo) == NULL)
{
//已經(jīng)在堆里面申請(qǐng)了地址
return FIFO_ERROR_MEM;
}
(*fifo)->size = fifosize;
(*fifo)->staraddr = (INT32U)(&(*fifo)->buffer[0]); //記錄FIFO緩沖區(qū)起始地址
(*fifo)->endaddr = (INT32U)(&(*fifo)->buffer[fifosize-1]); //記錄FIFO緩沖區(qū)結(jié)束地址
(*fifo)->front = (*fifo)->staraddr; //FIFO下一讀取數(shù)據(jù)地址
memset((*fifo)->buffer,0,(*fifo)->size); //清除緩沖區(qū)里面的數(shù)據(jù),可省略

DMA1_Channel5->CCR &= ~DMA_CCR5_EN;
DMA1_Channel5->CMAR = (INT32U)(*fifo)->staraddr; //配置DMA傳輸?shù)刂?br /> DMA1_Channel5->CNDTR = (*fifo)->size; //配置DMA傳輸數(shù)據(jù)量
da = USART1->DR;
da = da;
DMA1->IFCR |= 0x000F0000;
DMA1_Channel5->CCR |= DMA_CCR5_EN;

return FIFO_OK;
}

D:清空隊(duì)列緩沖區(qū)函數(shù)
//
//函數(shù):FIFO_Clear
//參數(shù):無(wú)
//返回:無(wú)
//描述:清空FIFO隊(duì)列
//
Int32S FIFO_Clear(FIFOTYPE *fifo)
{
volatile INT32U da;
if(fifo == NULL)
return FIFO_ERROR_PARAM;
fifo->front = fifo->staraddr; //將下一讀取地址設(shè)置為FIFO緩沖開始
DMA1_Channel5->CCR &= ~DMA_CCR5_EN;
DMA1_Channel5->CMAR = fifo->staraddr; //重新配置DMA地址
DMA1_Channel5->CNDTR = fifo->size; //重新配置DMA傳輸數(shù)據(jù)量
memset(fifo->buffer,0,fifo->size);
da = USART1->DR;
da = da;
DMA1->IFCR |= 0x000F0000;
DMA1_Channel5->CCR |= DMA_CCR5_EN;

return FIFO_OK;
}

E:讀取FIFO緩沖區(qū),這個(gè)跟標(biāo)準(zhǔn)的環(huán)形隊(duì)列基本沒(méi)區(qū)別
//
//函數(shù):FIFO_Read
//參數(shù):隊(duì)列指針,1byte數(shù)據(jù)指針
//返回:>=0讀取成功
//描述:從FIFO隊(duì)列中讀出1byte數(shù)據(jù)
//
Int32S FIFO_Read(FIFOTYPE *fifo,INT8U *data)
{
if(fifo == NULL )
return FIFO_ERROR_PARAM;
if(FIFO_Status(fifo)==0)
{
return FIFO_ERROR_EMPTY;
}
*data = (INT8U)(*((INT8U *)(fifo->front)));
if(fifo->front == fifo->endaddr)
{
fifo->front = fifo->staraddr;
}
else
{
fifo->front++;
}
return FIFO_OK;
}

F:獲取緩沖區(qū)的數(shù)據(jù)量
//
//函數(shù):FIFO_Status
//參數(shù):隊(duì)列指針
//返回:>0隊(duì)列中有未讀出數(shù)據(jù)
//描述:獲取FIFO隊(duì)列狀態(tài)
//
INT32S FIFO_Status(FIFOTYPE *fifo)
{
INT32S res;
INT32S nextsave = (INT32S)fifo->endaddr + 1 - (INT32S)DMA1_Channel5->CNDTR;
res = nextsave- (INT32S)(fifo->front);
if(res < 0)
{
res = ( (INT32S)(fifo->endaddr)+1 - (INT32S)(fifo->front) ) + (nextsave - (INT32S)fifo->staraddr);
}
return res;
}
說(shuō)明:
1:STM32的DMA_CMAR傳輸?shù)刂芳拇嫫鞑粫?huì)隨傳輸數(shù)據(jù)量的變化而真正的指向下一個(gè)存儲(chǔ)位置(AT91SAM就是總是指向下一個(gè)存儲(chǔ)地址的)。所以我需要根據(jù)傳輸數(shù)量寄存器DMA_CNDTR來(lái)推算下一傳輸位置寄存器!
2:需要考慮環(huán)形隊(duì)列寫入指針已經(jīng)重新回到緩沖區(qū)開頭了,而讀取指針還在緩沖區(qū)尾部的情況!


評(píng)論


技術(shù)專區(qū)

關(guān)閉