《STM32从零开始学习历程》——DMA直接存储区访问实验例程
《STM32從零開始學習歷程》@EnzoReventon
DMA—直接存儲區訪問實驗例程
本章節為DMA直接存儲區訪問的實驗例程講解,以“正點原子”的例程為基礎進行講解,如有不足之處還懇請各位大佬不吝賜教。
參考資料:
[野火EmbedFire]《STM32庫開發實戰指南——基于野火霸天虎開發板》
[正點原子]STM32F4開發指南-庫函數版本_V1.2
[ST]《STM32F4xx中文參考手冊》
1. DMA簡介
DMA的詳細介紹已經在上一講中進行過詳細的介紹:《STM32從零開始學習歷程》——DMA直接存儲區訪問理論知識
2. 本實驗歷程實現功能介紹
根據《STM32從零開始學習歷程》——DMA直接存儲區訪問理論知識的詳細介紹,我們可以知道DMA是一種可以不通過CPU的直接進行數據傳輸的控制器。本例程主要功能為使用DMA串口通訊將一定量的數據發送出去,使用串口助手接收發送到的數據。程序功能要點如下:
(1). 通過DMA將數據發送到USART1,使用串口助手接收數據。
(2). 使用一個按鍵控制DMA發送,按下按鈕就進行一次DMA數據發送操作。
(3). LCD屏幕顯示發送狀態與發送進度。(LCD的講解將在后續blog中講解,本文只要會用就行)
3. 實驗準備
軟件:Keil μVision5 v5.33(MDK5),串口助手XCOM V2.6
環境:Windows10 Enterprise x64
芯片:STM32F406ZGT6
設備:正點原子STM32F4探索者開發板,正點原子4.3寸 TFTLCD屏
仿真器:ST-Link
參考手冊:
[野火EmbedFire]《STM32庫開發實戰指南——基于野火霸天虎開發板》
[正點原子]STM32F4開發指南-庫函數版本_V1.2
[ST]《STM32F4xx中文參考手冊》
[ST]《STM32F407xx》
4. 硬件設計
本實驗中需要用到USART1,所以我們需要將USART1的TX與RX引腳與相應的GPIO引腳相連接,此處我們使用PB6/PB7引腳進行通訊,關于USART通訊串口的配置與選擇問題可以看:《STM32從零開始學習歷程》——USART串口通訊實驗篇1——中斷接收與發送,此處就不多贅述。
硬件連接呢我們還是通過使用杜邦線將USART TX/RX與PB6/PB7向連接,同時將USART1串口連接至電腦。如下圖所示:
5. 程序設計流程
1.DMA配置程序過程
①使能DMA時鐘
RCC_AHB1PeriphClockCmd();
② 初始化DMA通道參數
DMA_Init();
③使能串口DMA發送,串口DMA使能函數:
USART_DMACmd();
④查詢DMA的EN位,確保數據流就緒,可以配置
DMA_GetCmdStatus();
⑤設置通道當前剩余數據量
DMA_SetCurrDataCounter();
⑥使能DMA1通道,啟動傳輸。
DMA_Cmd();
⑤查詢DMA傳輸狀態
DMA_GetFlagStatus();
⑥獲取/設置通道當前剩余數據量:
DMA_GetCurrDataCounter();
2. 相關函數介紹
1)使能 DMA2 時鐘,并等待數據流可配置 。
DMA的時鐘使能是通過 AHB1ENR 寄存器來控制的,這里我們要先使能時鐘,才可以配置 DMA相關寄存器。所以先要使能 DMA2 的時鐘。另外,要對配置寄存器( DMA_SxCR )進行設置,必須先等待其最低位為 0 (也就是 DMA 傳輸禁止了),才可以進行配置。
庫函數使能DMA2 時鐘的方法為:
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2,ENABLE); //DMA2 時鐘使能等待
DMA 可配置,也就是等待 DMA_SxCR 寄存器最低位為 0 的方法為:
while (DMA_GetCmdStatus(DMA_Streamx) != DISABLE) { } // 等待 DMA 可配置
2)初始化 DMA2 數據流 7 ,包括配置通道,外設地址,存儲器地址,傳輸數據量等 。
DMA的某個數據流各種配置參數初始化是通過 DMA_Init 函數實現的:
void DMA_Init(DMA_Stream_TypeDef* DMAy_Streamx, DMA_InitTypeDef* DMA_InitStruct)
函數的第一個參數是指定初始化的 DMA 的數據流編號,這個很容易理解。入口參數范圍為:
DMA x _Stream0 DMA x _Stream 7(x=1,2) 。
下面我們主要看看第二個參數。跟其他外設一樣,同樣是通過初始化結構體成員變量值來達到初始化的目的,下面我們來看看 DMA_InitTypeDef結構體的定義:
1) DMA_Channel:
DMA 請求通道選擇,可選通道0 至通道7,每個外設對應固定的通道,具體設置值需要查表DMA1 各個通道的請求映像和表DMA2 各個通道的請求映像。
2) DMA_PeripheralBaseAddr:
外設地址,設定DMA_SxPAR 寄存器的值;一般設置為外設的數據寄存器地址,如果是存儲器到存儲器模式則設置為其中一個存儲區地址。
ADC3 的數據寄存器ADC_DR 地址為((uint32_t)ADC3+0x4C)。
3) DMA_Memory0BaseAddr:
存儲器0 地址,設定DMA_SxM0AR 寄存器值;一般設置為我們自義存儲區的首地址。我們程序先自定義一個16 位無符號整形數組ADC_ConvertedValue[4]用來存放每個通道的ADC 值, 所以把數組首地址(直接使用數組名即可) 賦值給DMA_Memory0BaseAddr。
4) DMA_DIR:
傳輸方向選擇,可選外設到存儲器、存儲器到外設以及存儲器到存儲器。它設定DMA_SxCR 寄存器的DIR[1:0] 位的值。ADC 采集顯然使用外設到存儲器模式。
5) DMA_BufferSize:
設定待傳輸數據數目,初始化設定DMA_SxNDTR 寄存器的值。這里ADC是采集4 個通道數據,所以待傳輸數目也就是4。
6) DMA_PeripheralInc:
如果配置為DMA_PeripheralInc_Enable,使能外設地址自動遞增功能,它設定DMA_SxCR 寄存器的PINC 位的值;一般外設都是只有一個數據寄存器,所以一般不會使能該位。ADC3 的數據寄存器地址是固定并且只有一個所以不使能外設地址遞增。
7) DMA_MemoryInc:
如果配置為DMA_MemoryInc_Enable,使能存儲器地址自動遞增功能,它設定DMA_SxCR 寄存器的MINC 位的值;我們自定義的存儲區一般都是存放多個數據的,所以使能存儲器地址自動遞增功能。我們之前已經定義了一個包含4 個元素的數字用來存放數據,使能存儲區地址遞增功能,自動把每個通道數據存放到對應數組元素內。
8) DMA_PeripheralDataSize:
外設數據寬度,可選字節(8 位)、半字(16 位) 和字(32 位),它設定DMA_SxCR 寄存器的PSIZE[1:0] 位的值。ADC 數據寄存器只有低16 位數據有效,使用半字數據寬度。
9) DMA_MemoryDataSize:
存儲器數據寬度,可選字節(8 位)、半字(16 位) 和字(32 位),它設定DMA_SxCR 寄存器的MSIZE[1:0] 位的值。保存ADC 轉換數據也要使用半字數據寬度,這跟我們定義的數組是相對應的。
10) DMA_Mode:
DMA 傳輸模式選擇,可選一次傳輸或者循環傳輸,它設定DMA_SxCR 寄存器的CIRC 位的值。我們希望ADC 采集是持續循環進行的,所以使用循環傳輸模式。
11) DMA_Priority:
軟件設置數據流的優先級,有4 個可選優先級分別為非常高、高、中和低,它設定DMA_SxCR 寄存器的PL[1:0] 位的值。DMA 優先級只有在多個DMA 數據流同時使用時才有意義,這里我們設置為非常高優先級就可以了。
12) DMA_FIFOMode:
FIFO 模式使能,如果設置為DMA_FIFOMode_Enable 表示使能FIFO 模式功能;它設定DMA_SxFCR 寄存器的DMDIS 位。ADC 采集傳輸使用直接傳輸模式即可,不需要使用FIFO 模式。
13) DMA_FIFOThreshold:
FIFO 閾值選擇,可選4 種狀態分別為FIFO 容量的1/4、1/2、3/4 和滿;它設定DMA_SxFCR 寄存器的FTH[1:0] 位;DMA_FIFOMode 設置為DMA_FIFOMode_Disable,那DMA_FIFOThreshold 值無效。ADC 采集傳輸不使用FIFO 模式,設置改值無效。
14) DMA_MemoryBurst:
存儲器突發模式選擇,可選單次模式、4 節拍的增量突發模式、8 節拍的增量突發模式或16 節拍的增量突發模式,它設定DMA_SxCR 寄存器的MBURST[1:0] 位的值。ADC 采集傳輸是直接模式,要求使用單次模式。
15) DMA_PeripheralBurst:
外設突發模式選擇,可選單次模式、4 節拍的增量突發模式、8 節拍的增量突發模式或16 節拍的增量突發模式,它設定DMA_SxCR 寄存器的PBURST[1:0] 位的值。
ADC 采集傳輸是直接模式,要求使用單次模式。
3)使能串口 1 的 DMA 發送
進行DMA 配置之后,我們就要開啟串口的 DMA 發送功能,使用的函數是:
USART_DMACmd(USART1,USART_DMAReq_Tx,ENABLE); //使能串口 1的 DMA發送
如果是要使能串口DMA 接受,那么第二個參數修改為 USART_DMAReq_Rx 即可。
4)使能 DMA 2 數據流 7 ,啟動傳輸。
使能 DMA 數據流的函數為:
void DMA_Cmd(DMA_Stream_TypeDef* DMAy_Streamx, FunctionalState NewState)
使能 DMA2_Stream7 ,啟動傳輸的方法為:
DMA_Cmd DMA2_Stream7 ENABLE
通過以上4 步設置,我們就可以啟動一次 USART1 的 DMA 傳輸了。
5)查詢 DMA 傳輸狀態
在DMA 傳輸過程中,我們要查詢 DMA 傳輸通道的狀態,使用的函數是:
FlagStatus DMA_GetFlagStatus(uint32_t DMAy_FLAG)
比如我們要查詢DMA數據流 7傳輸是否完成,方法是:
DMA_GetFlagStatus(DMA2_Stream7,DMA_FLAG_TCIF7);
這里還有一個比較重要的函數就是獲取當前剩余數據量大小的函數:
int16_t DMA_GetCurrDataCounter(DMA_Stream_TypeDef* DMAy_Streamx);
比如我們要獲取DMA數據流 7還有多少個數據沒有傳輸,方法是:
DMA_GetCurrDataCounter(DMA1_Channel4);
同樣,我們也可以設置對應的DMA數據流傳輸的數據量大小 ,函數為:
void DMA_SetCurrDataCounter(DMA_Stream_TypeDef* DMAy_Streamx, uint16_t Counter);
6. 程序詳解
USART.C
對于USART串口初始化的子程序我們仍然使用串口通訊中的程序代碼。
中斷服務函數。
void USART1_IRQHandler(void) //串口1中斷服務程序 {uint8_t temp;if(USART_GetFlagStatus(USART1,USART_FLAG_RXNE) != RESET){temp = USART_ReceiveData(USART1);USART_SendData(USART1,temp);}DMA.C
DMA初始化函數。此函數有5個形參。
DMA_Stream_TypeDef *DMA_Streamx: 為選擇數據流。
u32 chx: 為通道選擇。
u32 par: 為外設地址。
u32 mar: 存儲器地址。
u16 ndtr: 為數據傳輸量。
開啟一次DMA數據傳輸。
其中:DMA_Stream_TypeDef *DMA_Streamx 為DMA數據流;
u16 ndtr為為數據傳輸量。
MAIN.C
①進行宏定義
7. 效果展示
將程序下載進芯片后,屏幕將出現提示信息,按下按鍵0將執行一次DMA數據傳輸操作。
打開串口調試助手,設置好波特率,打開串口,將看到所有傳輸的數據。
注意事項:
在傳輸數據的過程中如果SEND_BUF_SIZE發送數據的長度沒有定義準確將導致發送數據的不完整,如下圖所示:
顯然最后一行沒有將“STM32F4 DMA”完整發送,如果需要完整發送需要對SEND_BUF_SIZE進行合理取值,最好等于sizeof(TEXT_TO_SEND)+2的整數倍。
8. 小結
本實驗到此結束,其實實驗不難,主要是一個實驗的過程,需要對這個過程進行了解,還有就是相關函數的使用標志位的判定等。此外,多查手冊仍然很重要。
總結
以上是生活随笔為你收集整理的《STM32从零开始学习历程》——DMA直接存储区访问实验例程的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 64位win7禁用驱动程序签名强制
- 下一篇: 3D立体幻镜(tm) Pro