STM32中USART的DMA 實(shí)現
傳輸數據到某個(gè)位置,如果不用DMA,那要CPU參與操作,一個(gè)字節一個(gè)字節地搬,效率高
點(diǎn)的,就一個(gè)字一個(gè)字地搬.但當你用了DMA 后,那就是只需要設置:A.從哪里開(kāi)始搬; B,
搬到哪里去;C以字節方式搬還是半字還是字;D:一共搬多少個(gè).之后,啟動(dòng)DMA.CPU內部
就會(huì )開(kāi)始搬數據了,整個(gè)搬數據的過(guò)程都不需要指令的參與,唯一要做的,就是檢測什么時(shí)
候搬完.你可以?huà)呙杓拇嫫?也可以用中斷.這里,我使用了中斷.
具體設置功能看注釋就可以明白了.注意一點(diǎn)就是,有一個(gè)設置:
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
這個(gè)是外設的地址不遞增.也就是說(shuō),每次搬動(dòng),都是從源頭,也就是USART1的DR寄存器
搬,但內存地址卻是遞增的:
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
這個(gè)歷程實(shí)現了 接受 串口的數據 寫(xiě)到FLASH 之中工作,而DMA的作用在于將 串口收寄存器 USART1->DR 的 數據寫(xiě)到內存之中 比如某個(gè)數組之中 u8 USART1_DMA_Buf1[512]; 寫(xiě)滿(mǎn)512個(gè)字節之后將進(jìn)入DMA中斷(通道5)在這里修改DMA 的內存寫(xiě)入入口
u8 USART1_DMA_Buf2[512]; ,同時(shí)標記 下次的入口Free_Buf_No=BUF_NO1; 與 Buf_Ok=TRUE; 證明已有數據準備完成。這時(shí)CUP將USART1_DMA_Buf1中的數據寫(xiě)入FLASH .
又抄了一點(diǎn)
這次使用的是雙緩沖,也有人
叫乒乓緩沖.因為一般情況下,串口的數據DMA 傳輸進(jìn)BUF1 的過(guò)程中,是不建議對
BUF1 進(jìn)行操作的.但由于串口數據是不會(huì )等待的直傳,所以你總不能等BUF1 滿(mǎn)了,
才往FLASH 上寫(xiě),因為這時(shí)候串口數據依舊是源源不斷.于是,使用雙緩沖就變的理
所當然了.當BUF1 滿(mǎn)了的時(shí)候,就馬上設置DMA的目標為BUF2,并且BUF1的數據
往25F080上灌.當串口DMA寫(xiě)滿(mǎn)了BUF2的時(shí)候,再設置DMA的目標為BUF1,此時(shí)
再操作BUF2寫(xiě)進(jìn)25F080.如此一直循環(huán),就好像打乒乓球那樣吧,所以就叫乒乓緩沖.
用這個(gè)方法的速度極限就是,你必須確保兩點(diǎn)a.DMA 灌滿(mǎn)了BUF1 的時(shí)候,會(huì )發(fā)生中
斷,此時(shí)切換DMA 的目標緩沖為BUF2,而且切換的過(guò)程必須在新的串口數據溢出之
前完成.b.在DMA的BUF1滿(mǎn)之前,另外一個(gè)有數據的BUF2必須能全部寫(xiě)進(jìn)25F080,
其中包括了遇到新的扇區邊界而要刷除扇區的操作時(shí)間!!
可以看出,BUF的增大,并不能夠很大程度的提升速度極限.

假設 USART 與 FLASH 的 底層驅動(dòng)已經(jīng)寫(xiě)好了。 點(diǎn)擊查看。
/************DMA方式傳輸***************************/
#defineSRC_USART1_DR (&(USART1->DR)) //串口接收寄存器作為源頭
//DMA目標緩沖,這里使用雙緩沖
u8USART1_DMA_Buf1[512];
u8USART1_DMA_Buf2[512];
boolBuf_Ok;//BUF是否已經(jīng)可用
BUF_NOFree_Buf_No; //空閑的BUF號typedefenum{BUF_NO1=0,BUF_NO2=1}BUF_NO;
DMA_InitTypeDefDMA_InitStructure;
voidUSART_DMAToBuf1(void)
{
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);//開(kāi)DMA時(shí)鐘
DMA_DeInit(DMA1_Channel5);//將DMA的通道1寄存器重設為缺省值
DMA_InitStructure.DMA_PeripheralBaseAddr = (u32)SRC_USART1_DR; //源頭BUF既是 (&(USART1->DR))
DMA_InitStructure.DMA_MemoryBaseAddr = (u32)USART1_DMA_Buf1; //目標BUF 既是要寫(xiě)在哪個(gè)個(gè)數組之中
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC; //外設作源頭//外設是作為數據傳輸的目的地還是來(lái)源
DMA_InitStructure.DMA_BufferSize = 512; //DMA緩存的大小 單位在下邊設定
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_PeripheralDataSize_Byte; //內存字節為單位
DMA_InitStructure.DMA_Mode = DMA_Mode_Circular; //工作在循環(huán)緩存模式
DMA_InitStructure.DMA_Priority = DMA_Priority_High; //4優(yōu)先級之一的(高優(yōu)先)VeryHigh/High/Medium/Low
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; //非內存到內存
DMA_Init(DMA1_Channel5, &DMA_InitStructure);//根據DMA_InitStruct中指定的參數初始化DMA的通道1寄存器
DMA_ITConfig(DMA1_Channel5, DMA_IT_TC, ENABLE); //DMA5傳輸完成中斷

/*****************************************************************************************************************************************************/
//初始化BUF標志
Free_Buf_No=BUF_NO2;//因為 DMA_InitStructure.DMA_MemoryBaseAddr = (u32)USART1_DMA_Buf1;
Buf_Ok=FALSE;//此時(shí)沒(méi)有數據準備完成 當然FALSE
DMA_Cmd(DMA1_Channel5, ENABLE); //正式允許DMA
}
再來(lái)看看DMA中斷:
//u16DataCounter;
externDMA_InitTypeDefDMA_InitStructure;
voidDMA1_Channel5_IRQHandler(void)
{
if(DMA_GetITStatus(DMA1_IT_TC5))//通道5傳輸完成中斷TC還有傳輸 過(guò)半中斷HT 錯誤中斷TE 全局中斷GL
{
//DataCounter = DMA_GetCurrDataCounter(DMA1_Channel5);//獲取剩余長(cháng)度,一般都為0,調試用
DMA_ClearITPendingBit(DMA1_IT_GL5);//清除全部中斷標志
//轉換可操作BUF
if(Free_Buf_No==BUF_NO1)
{
DMA_InitStructure.DMA_MemoryBaseAddr = (u32)USART1_DMA_Buf1;
DMA_Init(DMA1_Channel5, &DMA_InitStructure);
Free_Buf_No=BUF_NO2;
}
else
{
DMA_InitStructure.DMA_MemoryBaseAddr = (u32)USART1_DMA_Buf2;
DMA_Init(DMA1_Channel5, &DMA_InitStructure);
Free_Buf_No=BUF_NO1;
}
Buf_Ok=TRUE;//有準備好的數據了
}
}
寫(xiě)FLASH的操作
while(1)
{
if(Buf_Ok==TRUE)
{
LED1_ON;//一個(gè)標記
Buf_Ok=FALSE;//操作了準備好的數據
if((addr%4096)==0) //跨越一個(gè)扇區,則需要先刷除
{
SST25SectorErase(addr);
sector_count++;
}
if(Free_Buf_No==BUF_NO1)
SST25Write(addr,USART1_DMA_Buf1,512);
else
SST25Write(addr,USART1_DMA_Buf2,512);
addr+=512;
Timer1=5000;//時(shí)間重置
LED1_OFF;
}
//檢測超時(shí)開(kāi)了定時(shí)器
if(Timer1==0)//五秒內沒(méi)準備好的數據
{
//獲取長(cháng)度
len=512-DMA_GetCurrDataCounter(DMA1_Channel5);
//寫(xiě)入最后數據
if(Free_Buf_No==BUF_NO1)
SST25Write(addr,USART1_DMA_Buf2,len);
else
SST25Write(addr,USART1_DMA_Buf1,len);
addr+=len;
break;
}
}

還是很簡(jiǎn)單的。
有一點(diǎn)比較困擾 就是 FlagStatus標志位 與 ITStatus中斷標志位 的區別。 其實(shí)就 DMA 來(lái)說(shuō) DMA_IT值 與 DMA_FLAG值 是一樣的
甚至2者值的獲取 都是讀 DMA ISR register 的值 清除也是設置 DMA_IFCR 寄存器來(lái)清除的所以貌似沒(méi)有區別.........同理這個(gè)問(wèn)題在別的中斷也存在但我還不可保證 IT 與FLAG 的值總是相同的這個(gè)存在也許是為了兼容但一定有其意義務(wù)必不可混用即使有時(shí)用錯也正確.....
評論