STM32串口DMA方式接收數(shù)據(jù)
1:如果用接收中斷的話,每接收1byte就得中斷一次。這樣太消耗CPU資源!
2:如果用DMA方式接收數(shù)據(jù),那么如何確定接收數(shù)據(jù)的長度又不好確定了。(比如GPRS模塊AT命令的接收!)
3:DMA方式接收+定時器的超時中斷。這樣處理也比較復雜,需要開定時器,關定時器。。。。個人不喜歡?。ˋTMEL的ARM系列的串口倒是有硬件超時中斷可以直接使用。我現(xiàn)在用AT91SAM7系列處理GPRS的AT命令就采用這種方式,挺好用。但是STM32就沒有了,需要自己加定時器,還要硬件處理:RXD連接定時器的一個觸發(fā)引腳!)。
所以之前用STM32接收串口數(shù)據(jù)都是采用接收中斷,然后寫入一個FIFO隊列。然后在主函數(shù)里面去查詢隊列緩沖中是否有數(shù)據(jù)需要處理。但是這樣的話,串口中斷服務函數(shù)始終是很大的硬件開銷。比如我現(xiàn)在用串口下載STM32的升級固件的時候,數(shù)據(jù)量較大。
廢話完畢,今天突然腦子發(fā)熱想要把DMA和環(huán)形的FIFO隊列結(jié)合一下使用。把想法跟同事交流一下,覺得有可行性!馬上動手實驗。經(jīng)過半天調(diào)試,結(jié)果令人滿意。
說說我的思路(本人表達能力有限,描述不清楚的希望大家跟帖):關在在于讓DMA來實現(xiàn)“環(huán)形隊列中往緩沖區(qū)寫入1byte”的功能!剩下的讀取隊列就跟普通環(huán)形隊列沒多大區(qū)別了。這樣我們的程序中擁有了一個不占用CPU資源的“環(huán)形隊列”后,我們就不用擔心CPU頻繁中斷,我們只需要在適當?shù)臅r間讀取隊列中的數(shù)據(jù)然后慢慢分析處理數(shù)據(jù)!
A:串口初始化配置串口為DMA方式接收數(shù)據(jù)。具體配置請看:
DMA1_Channel5->CCR = DMA_CCR5_PL //通道優(yōu)先級最高
| 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);
關鍵,開啟DMA循環(huán)模式,這樣接收完之后會自動回到FIFO緩沖區(qū)開頭地方,這樣能省不少事情。
當然,考慮到可能把緩沖區(qū)撐爆的情況,所以開啟通道傳輸完成標志位,在傳輸完成中斷中查詢一下隊列中有多少數(shù)據(jù)沒有讀取出來,如果太多數(shù)據(jù)沒有讀取,那么在中斷里面處理讀取FIFO數(shù)據(jù)并做相應處理!
B:關于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]; //實際長度由初始化分配內(nèi)存!(memloc的時候確定)
}FIFOTYPE;
C:關于FIFO隊列的初始化,具體配置
//
//函數(shù):FIFO_Init
//參數(shù):FIFO類型的指針地址,隊列大小
//返回:>=0初始化成功
//描述:初始化FIFO隊列
//
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)在堆里面申請了地址
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:清空隊列緩沖區(qū)函數(shù)
//
//函數(shù):FIFO_Clear
//參數(shù):無
//返回:無
//描述:清空FIFO隊列
//
Int32S FIFO_Clear(FIFOTYPE *fifo)
{
volatile INT32U da;
if(fifo == NULL)
return FIFO_ERROR_PARAM;
fifo->front = fifo->staraddr; //將下一讀取地址設置為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ū),這個跟標準的環(huán)形隊列基本沒區(qū)別
//
//函數(shù):FIFO_Read
//參數(shù):隊列指針,1byte數(shù)據(jù)指針
//返回:>=0讀取成功
//描述:從FIFO隊列中讀出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ù):隊列指針
//返回:>0隊列中有未讀出數(shù)據(jù)
//描述:獲取FIFO隊列狀態(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;
}
說明:
1:STM32的DMA_CMAR傳輸?shù)刂芳拇嫫鞑粫S傳輸數(shù)據(jù)量的變化而真正的指向下一個存儲位置(AT91SAM就是總是指向下一個存儲地址的)。所以我需要根據(jù)傳輸數(shù)量寄存器DMA_CNDTR來推算下一傳輸位置寄存器!
2:需要考慮環(huán)形隊列寫入指針已經(jīng)重新回到緩沖區(qū)開頭了,而讀取指針還在緩沖區(qū)尾部的情況!
評論