在前面我們提到過 DMA,這一章我們就來學(xué)習(xí) STM32F1 的DMA 使
用。要實現(xiàn)的功能是:通過 K_UP 按鍵控制 DMA 串口 1 數(shù)據(jù)的傳送,在傳送過程中讓 D2 指示燈不斷閃爍,直到數(shù)據(jù)傳送完成。D1 指示燈閃爍提示系統(tǒng)正常運行。學(xué)習(xí)時可以參考《STM32F10x 中文參考手冊》-10 DMA 控制器(DMA)章節(jié)。
DMA 簡介
DMA,全稱是 Direct Memory Access,中文意思為直接存儲器訪問。DMA 可用于實現(xiàn)外設(shè)與存儲器之間或者存儲器與存儲器之間數(shù)據(jù)傳輸?shù)母咝?。之所以稱為高效, 是因為 DMA 傳輸數(shù)據(jù)移動過程無需 CPU 直接操作, 這樣節(jié)省的 CPU 資源就可供其它操作使用。從硬件層面來理解,DMA 就好像是 RAM 與 I/O 設(shè)備間數(shù)據(jù)傳輸?shù)耐罚?外設(shè)與存儲器之間或者存儲器與存儲器之間可以直接在這條通路上進行數(shù)據(jù)傳輸。這里說的外設(shè)一般指外設(shè)的數(shù)據(jù)寄存器, 比如 ADC、SPI、I2C、DCMI等外設(shè)的數(shù)據(jù)寄存器, 存儲器一般是指片內(nèi) SRAM、 外部存儲器、 片內(nèi) Flash等。
STM32F1 最多有 2 個 DMA 控制器 ( DMA2 僅存在大容量產(chǎn)品中) ,DMA1 有7 個通道。DMA2 有 5 個通道。每個通道專門用來管理來自于一個或多個外設(shè)對存儲器訪問的請求。還有一個仲裁器來協(xié)調(diào)各個 DMA 請求的優(yōu)先權(quán)。
STM32F1 的 DMA 有以下主要特性:
● 12 個獨立的可配置的通道(請求):DMA1 有 7 個通道, DMA2 有 5 個通
道
● 每個通道都直接連接專用的硬件 DMA 請求,每個通道都同樣支持軟件觸
發(fā)。這些功能通過軟件來配置。
● 在同一個 DMA 模塊上, 多個請求間的優(yōu)先權(quán)可以通過軟件編程設(shè)置(共有
四級:很高、高、中等和低),優(yōu)先權(quán)設(shè)置相等時由硬件決定(請求 0 優(yōu)先于請求1,依此類推) 。
● 獨立數(shù)據(jù)源和目標(biāo)數(shù)據(jù)區(qū)的傳輸寬度(字節(jié)、半字、全字),模擬打包和拆包的過程。源和目標(biāo)地址必須按數(shù)據(jù)傳輸寬度對齊。
● 支持循環(huán)的緩沖器管理
● 每個通道都有 3 個事件標(biāo)志(DMA 半傳輸、 DMA 傳輸完成和 DMA 傳輸出錯),這3 個事件標(biāo)志邏輯或成為一個單獨的中斷請求。
● 存儲器和存儲器間的傳輸
● 外設(shè)和存儲器、存儲器和外設(shè)之間的傳輸
● 閃存、 SRAM、外設(shè)的 SRAM、 APB1、 APB2 和 AHB 外設(shè)均可作為訪問的源和目標(biāo)。
● 可編程的數(shù)據(jù)傳輸數(shù)目:最大為 65535
DMA 結(jié)構(gòu)框圖
DMA 控制器獨立于內(nèi)核,屬于一個單獨的外設(shè),結(jié)構(gòu)比較簡單,從編程的角度來看,我們只需掌握結(jié)構(gòu)框圖中的三部分內(nèi)容即可。如圖所示(大家也可以查看《STM32F10x中文參考手冊》-10 DMA 控制器(DMA)章節(jié)內(nèi)容)。
我們把 DMA 結(jié)構(gòu)框圖分成3個子模塊,按照順序依次進行簡單介紹。
(1)標(biāo)號 1:DMA 請求
如果外設(shè)要想通過 DMA 來傳輸數(shù)據(jù), 必須先給 DMA 控制器發(fā)送 DMA 請求,DMA 收到請求信號之后, 控制器會給外設(shè)一個應(yīng)答信號, 當(dāng)外設(shè)應(yīng)答后且 DMA 控制器收到應(yīng)答信號之后,就會啟動 DMA 的傳輸,直到傳輸完畢。
根據(jù)前面介紹我們知道,DMA 含有 DMA1 和 DMA2 兩個控制器,其中 DMA1 含有 7個通道,DMA2 含有 5 個通道,不同的 DMA 控制器的通道對應(yīng)著不同的外設(shè)請求。
從DMA 請求映射圖中可以知道各通道所對應(yīng)的外設(shè)請求,如圖分別是DMA1和DMA2請求映射圖:
DMA2 請求通道中 ADC3、 SDIO 和 TIM8 的 DMA 請求只在大容量產(chǎn)品中存在,這個在具體項目時要注意。
(2)標(biāo)號 2:DAM 通道
DMA 具有 12 個獨立可編程的通道,其中 DMA1 有 7 個通道, DMA2 有 5個通道,每個通道對應(yīng)不同的外設(shè)的 DMA 請求。雖然每個通道可以接收多個外設(shè)的請求,但是同一時間只能接收一個,不能同時接收多個。
(3)標(biāo)號 3:仲裁器
當(dāng)發(fā)生多個 DMA 通道請求時,就意味著有先后響應(yīng)處理的順序問題,這個就由仲裁器也管理。仲裁器管理 DMA 通道請求分為兩個階段。第一階段屬于軟件階段,可以在 DMA_CCRx 寄存器中設(shè)置,有 4 個等級:非常高、高、中和低四個優(yōu)先級。第二階段屬于硬件階段,如果兩個或以上的 DMA 通道請求設(shè)置的優(yōu)先級一樣,則他們優(yōu)先級取決于通道編號,編號越低優(yōu)先權(quán)越高,比如通道 0高于通道 1。在大容量產(chǎn)品和互聯(lián)型產(chǎn)品中,DMA1 控制器擁有高于 DMA2 控制器的優(yōu)先級。
DMA 數(shù)據(jù)配置
使用 DMA, 最核心就是配置要傳輸?shù)臄?shù)據(jù), 包括數(shù)據(jù)從哪里來, 要到哪里去,傳輸?shù)臄?shù)據(jù)的單位是什么,要傳多少數(shù)據(jù),是一次傳輸還是循環(huán)傳輸?shù)取?
(1)從哪里來到哪里去
我們知道 DMA 傳輸數(shù)據(jù)的方向有三個:從外設(shè)到存儲器,從存儲器到外設(shè),從存儲器到存儲器。具體的方向 DMA_CCR 位 4 DIR 配置:0 表示從外設(shè)到存儲器, 1 表示從存儲器到外設(shè)。這里面涉及到的外設(shè)地址由 DMA_CPAR 配置,存儲器地址由 DMA_CMAR配置。
外設(shè)到存儲器
當(dāng)我們使用從外設(shè)到存儲器傳輸時,以 ADC 采集為例。DMA 外設(shè)寄存器的地址對應(yīng)的就是 ADC 數(shù)據(jù)寄存器的地址,DMA 存儲器的地址就是我們自定義的變量(用來接收存儲 AD 采集的數(shù)據(jù))的地址。方向我們設(shè)置外設(shè)為源地址。
存儲器到外設(shè)
當(dāng)我們使用從存儲器到外設(shè)傳輸時, 以串口向電腦端發(fā)送數(shù)據(jù)為例。DMA 外設(shè)寄存器的地址對應(yīng)的就是串口數(shù)據(jù)寄存器的地址, DMA 存儲器的地址就是我們自定義的變量(相當(dāng)于一個緩沖區(qū),用來存儲通過串口發(fā)送到電腦的數(shù)據(jù))的地址。方向我們設(shè)置外設(shè)為目標(biāo)地址。
存儲器到存儲器
當(dāng)我們使用從存儲器到存儲器傳輸時,以內(nèi)部 FLASH 向內(nèi)部 SRAM 復(fù)制數(shù)據(jù)為例。DMA 外設(shè)寄存器的地址對應(yīng)的就是內(nèi)部 FLASH(我們這里把內(nèi)部 FALSH當(dāng)作一個外設(shè)來看)的地址, DMA 存儲器的地址就是我們自定義的變量(相當(dāng)于一個緩沖區(qū),用來存儲來自內(nèi)部 FLASH 的數(shù)據(jù))的地址。方向我們設(shè)置外設(shè)(即內(nèi)部 FLASH)為源地址。跟上面兩個不一樣的是,這里需要把 DMA_CCR 位14:MEM2MEM:存儲器到存儲器模式配置為 1,啟動 M2M 模式。
(2)要傳多少,單位是什么
當(dāng)我們配置好數(shù)據(jù)要從哪里來到哪里去之后, 我們還需要知道我們要傳輸?shù)臄?shù)據(jù)是多少,數(shù)據(jù)的單位是什么。
以串口向電腦發(fā)送數(shù)據(jù)為例,我們可以一次性給電腦發(fā)送很多數(shù)據(jù),具體多少由 DMA_CNDTR 配置,這是一個 32 位的寄存器,一次最多只能傳輸 65535 個數(shù)據(jù)。
要想數(shù)據(jù)傳輸正確,源和目標(biāo)地址存儲的數(shù)據(jù)寬度還必須一致,串口數(shù)據(jù)寄存器是 8 位的,所以我們定義的要發(fā)送的數(shù)據(jù)也必須是 8 位。外設(shè)的數(shù)據(jù)寬度由 DMA_CCR 的 PSIZE[1:0]配置,可以是 8/16/32 位,存儲器的數(shù)據(jù)寬度由DMA_CCR 的 MSIZE[1:0]配置,可以是 8/16/32 位。
在 DMA 控制器的控制下,數(shù)據(jù)要想有條不紊的從一個地方搬到另外一個地方,還必須正確設(shè)置兩邊數(shù)據(jù)指針的增量模式。外設(shè)的地址指針由 DMA_CCRx 的PINC 配置,存儲器的地址指針由 MINC 配置。以串口向電腦發(fā)送數(shù)據(jù)為例,要發(fā)送的數(shù)據(jù)很多,每發(fā)送完一個,那么存儲器的地址指針就應(yīng)該加 1,而串口數(shù)據(jù)寄存器只有一個,那么外設(shè)的地址指針就固定不變。具體的數(shù)據(jù)指針的增量模式由實際情況決定。
(3)什么時候傳輸完成
數(shù)據(jù)什么時候傳輸完成,我們可以通過查詢標(biāo)志位或者通過中斷的方式來判斷。每個 DMA 通道在 DMA 傳輸過半、傳輸完成和傳輸錯誤時都會有相應(yīng)的標(biāo)志位,如果使能了該類型的中斷后,則會產(chǎn)生中斷。有關(guān)各個標(biāo)志位的詳細描述請參考 DMA 中斷狀態(tài)寄存器DMA_ISR 的詳細描述。
傳輸完成還分兩種模式,是一次傳輸還是循環(huán)傳輸,一次傳輸很好理解,即傳輸一次之后就停止,要想再傳輸?shù)脑挘仨氷P(guān)斷 DMA 使能后再重新配置后才能繼續(xù)傳輸。循環(huán)傳輸則是一次傳輸完成之后又恢復(fù)第一次傳輸時的配置循環(huán)傳輸,不斷的重復(fù)。具體的由 DMA_CCR 寄存器的 CIRC 循環(huán)模式位控制。
DMA 配置步驟
接下來我們介紹下如何使用庫函數(shù)對 DMA 進行配置。這個也是在編寫程序中必須要了解的。具體步驟如下:(DMA 相關(guān)庫函數(shù)在 stm32f10x_dma.c 和stm32f10x_dma.h 文件中)
(1)使能 DMA 控制器(DMA1 或 DMA2)時鐘
要使能 DMA 時鐘,需通過AHB1ENR 寄存器來控制,使能 DMA時鐘庫函數(shù)為:
void RCC_AHBPeriphClockCmd(uint32_t RCC_AHBPeriph, FunctionalState
NewState);
例如使能 DMA1 時鐘,函數(shù)如下:
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
(2)初始化 DMA 通道,包括配置通道、外設(shè)和內(nèi)存地址、傳輸數(shù)據(jù)量等要使用 DMA,必須對其相關(guān)參數(shù)進行設(shè)置,包括通道選擇、外設(shè)和內(nèi)存地址、
通道優(yōu)先級、傳輸數(shù)據(jù)量的配置等。該部分設(shè)置通過 DMA 初始化函數(shù) DMA_Init完成的:
void DMA_Init(DMA_Channel_TypeDef* DMAy_Channelx,DMA_InitTypeDef*
DMA_InitStruct);
函數(shù)中第一個參數(shù)是用來確定 DMA 通道,參數(shù)范圍為:
DMA1_Channel_0~DMA1_Channel_7(DMA2 是DMA2_Channel_0-DMA2_Channel_5)
第二個參數(shù)是一個結(jié)構(gòu)體指針變量,結(jié)構(gòu)體類型是 DMA_InitTypeDef,其內(nèi)包含了 DMA 相關(guān)參數(shù)的設(shè)置。下面我們簡單介紹下它的成員:
typedef struct
{
uint32_t DMA_PeripheralBaseAddr; // 外設(shè)地址
uint32_t DMA_MemoryBaseAddr; // 存儲器地址
uint32_t DMA_DIR; // 傳輸方向
uint32_t DMA_BufferSize; // 傳輸數(shù)目
uint32_t DMA_PeripheralInc; // 外設(shè)地址增量模式
uint32_t DMA_MemoryInc; // 存儲器地址增量模式
uint32_t DMA_PeripheralDataSize; // 外設(shè)數(shù)據(jù)寬度
uint32_t DMA_MemoryDataSize; // 存儲器數(shù)據(jù)寬度
uint32_t DMA_Mode; // 模式選擇
uint32_t DMA_Priority; // 通道優(yōu)先級
uint32_t DMA_M2M; // 存儲器到存儲器模式
} DMA_InitTypeDef;
DMA_PeripheralBaseAddr:外設(shè)地址,通過 DMA_CPAR 寄存器設(shè)置,一般設(shè)置為外設(shè)的數(shù)據(jù)寄存器地址,比如要進行串口 DMA 傳輸,那么外設(shè)基地址為串口接受發(fā)送數(shù)據(jù)存儲器 USART1- >DR 的地址,表示方法為&USART1- >DR。如果是存儲器到存儲器模式則設(shè)置為其中一個存儲區(qū)地址。
DMA_Memory0BaseAddr:存儲器地址,通過 DMA_CMAR 寄存器設(shè)置,一般設(shè)置為我們自定義存儲區(qū)的首地址,即我們存放 DMA 傳輸數(shù)據(jù)的內(nèi)存地址。比如我們定義一個 u32 類型數(shù)組,將數(shù)組首地址(直接使用數(shù)組名即可)賦值給DMA_Memory0BaseAddr,在DMA傳輸?shù)臅r候就可以把數(shù)組內(nèi)的數(shù)據(jù)發(fā)送或接收。
DMA_DIR:數(shù)據(jù)傳輸方向選擇,可選擇外設(shè)到存儲器、存儲器到外設(shè)以及存
儲器到存儲器。通過設(shè)定 DMA_CCR 寄存器的 DIR[1:0]位的值決定。比如本章實驗是從內(nèi)存讀取數(shù)據(jù)發(fā)送到串口,所以數(shù)據(jù)傳輸方向為存儲器到外設(shè),配置為DMA_DIR_MemoryToPeripheral。
DMA_BufferSize:用來設(shè)置一次傳輸數(shù)據(jù)的大小,通過 DMA_CNDTR 寄存器設(shè)置。
DMA_PeripheralInc:用來設(shè)置外設(shè)地址是遞增還是不變,通過 DMA_CCR寄存器的 PINC 位設(shè)置,如果設(shè)置為遞增,那么下一次傳輸?shù)臅r候地址加 1。通常外 設(shè) 只 有 一 個 數(shù) 據(jù) 寄 存 器 , 所 以 一 般 不 會 使 能 該 位 , 即 配 置 為DMA_PeripheralInc_Disable。
DMA_MemoryInc:用來設(shè)置內(nèi)存地址是否遞增,通過 DMA_CCR 寄存器的MINC位設(shè)置。我們自定義的存儲區(qū)一般都是存放多個數(shù)據(jù)的,所以需要使能存儲器地址自動遞增功能,即配置為 DMA_MemoryInc_Enable。
DMA_PeripheralDataSize:外設(shè)數(shù)據(jù)寬度選擇,可以為字節(jié)(8 位)、半字
(16位)、字(32 位),通過 DMA_CCR 寄存器的 PSIZE[1:0]位設(shè)置。例如本實驗數(shù)據(jù)是按照 8 位字節(jié)傳輸,所以配置為DMA_PeripheralDataSize_Byte。
DMA_MemoryDataSize:存儲器數(shù)據(jù)寬度選擇,可以為字節(jié)(8 位)、半字(16位)、字(32 位),通過 DMA_CCR 寄存器的 MSIZE[1:0]位設(shè)置。本章實驗同樣設(shè)置為 8 位字節(jié)傳輸,這個要和我們定義的數(shù)組對應(yīng),所以配置為DMA_MemoryDataSize_Byte。
DMA_Mode:DMA 傳輸模式選擇, 可選擇一次傳輸或者循環(huán)傳輸, 通過DMA_CCR寄存器的 CIRC 位來設(shè)定。比如我們要從內(nèi)存 (存儲器) 中傳輸 64 個字節(jié)到串口,如果設(shè)置為循環(huán)傳輸,那么它會在 64 個字節(jié)傳輸完成之后繼續(xù)從內(nèi)存的第一個地址傳輸,如此循環(huán)。這里我們設(shè)置為一次傳輸完成之后不循環(huán)。所以設(shè)置值為DMA_Mode_Normal。
DMA_Priority:用來設(shè)置 DMA 通道的優(yōu)先級,有低,中,高,超高四種級別,可通過 DMA_CCR 寄存器的PL[1:0]位來設(shè)定。DMA 優(yōu)先級只有在多個 DMA 通道同時使用時才有意義,本章實驗我們只使用了一個 DMA 通道,所以可以任意設(shè)置DMA 優(yōu)先級,這里我們就設(shè)置為中等優(yōu)先級,配置參數(shù)為DMA_Priority_Medium。
DMA_Priority:用來設(shè)置 DMA 通道的優(yōu)先級,有低,中,高,超高四種級別,可通過 DMA_CCR 寄存器的PL[1:0]位來設(shè)定。DMA 優(yōu)先級只有在多個 DMA 通道同時使用時才有意義,本章實驗我們只使用了一個 DMA 通道,所以可以任意設(shè)置DMA 優(yōu)先級,這里我們就設(shè)置為中等優(yōu)先級,配置參數(shù)為DMA_Priority_Medium。
DMA_M2M:用來設(shè)置存儲器到存儲器模式,使用存儲器到存儲器時用到,設(shè)定 DMA_CCR 的位 14 MEN2MEN 即可啟動存儲器到存儲器模式。
了解結(jié)構(gòu)體成員功能后,就可以進行配置,本實驗配置代碼如下:
DMA_InitTypeDef DMA_InitStructure;
DMA_InitStructure.DMA_PeripheralBaseAddr = par;//DMA外設(shè)地址
DMA_InitStructure.DMA_MemoryBaseAddr = mar;//DMA 存儲器0地址
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;//存儲器到外
設(shè)模式
DMA_InitStructure.DMA_BufferSize = ndtr;//數(shù)據(jù)傳輸量
DMA_InitStructure.DMA_PeripheralInc =
DMA_PeripheralInc_Disable;//外設(shè)非增量模式
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;//存儲器
增量模式
DMA_InitStructure.DMA_PeripheralDataSize =
DMA_PeripheralDataSize_Byte;//外設(shè)數(shù)據(jù)長度:8 位
DMA_InitStructure.DMA_MemoryDataSize =
DMA_MemoryDataSize_Byte;//存儲器數(shù)據(jù)長度:8 位
DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;// 使用普通模式
DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;//中等優(yōu)先
級
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; //DMA 通道 x 沒有
設(shè)置為內(nèi)存到內(nèi)存?zhèn)鬏?/p>
DMA_Init(DMAy_Channelx, &DMA_InitStructure);//初始化DMA
(3)使能外設(shè) DMA功能(DMA 請求映射圖對應(yīng)的外設(shè))
配置好 DMA 后,我們就需要使能外設(shè) DMA 功能,例如我們要使能串口的DMA發(fā)送功能,調(diào)用的庫函數(shù)為:
USART_DMACmd(USART1,USART_DMAReq_Tx,ENABLE); //使能串口 1 的 DMA發(fā)送
如果是要使能串口DMA接受, 那么第二個參數(shù)修改為USART_DMAReq_Rx即可。
如果是其他的外設(shè)需開啟DMA功能, 只需要在對應(yīng)的標(biāo)準(zhǔn)外設(shè)庫函數(shù)中查找到對應(yīng)的外設(shè) DMA 使能函數(shù)。
(4)開啟 DMA 的通道傳輸
初始化 DMA 后,要使用DMA還必須開啟它,開啟 DMA 通道傳輸?shù)膸旌瘮?shù)為:
void DMA_Cmd(DMA_Channel_TypeDef* DMAy_Channelx, FunctionalStateNewState);
第一個參數(shù)為外設(shè)所對應(yīng)的 DMA 通道,例如本章使用的是 USART1_TXDMA請求,因此它對應(yīng)的是 DMA1_Channel4,可通過前面DMA 請求映射圖選擇。第二個參數(shù)相信不說也知道,就是使能或失能。
本實驗使能 DMA1_Channel4函數(shù)為:
DMA_Cmd(DMA1_Channel4,ENABLE);
(5)查詢 DMA 傳輸狀態(tài)
通過以上 4 步設(shè)置,我們就可以啟動一次 DMA 傳輸了。但是在 DMA 傳輸過程中,我們還需要查詢 DMA傳輸通道的狀態(tài),使用的庫函數(shù)是:
FlagStatus DMA_GetFlagStatus(uint32_t DMAy_FLAG);
例如我們要查詢 DMA1通道4 傳輸是否完成,方法是:
DMA_GetFlagStatus(DMA1_FLAG_TC4);
標(biāo)準(zhǔn)庫中,還提供了獲取當(dāng)前剩余數(shù)據(jù)量大小的函數(shù):
uint16_t DMA_GetCurrDataCounter(DMA_Channel_TypeDef*DMAy_Channelx);
例如我們要獲取 DMA1通道4 還有多少個數(shù)據(jù)沒有傳輸,方法是:
DMA_GetCurrDataCounter(DMA1_Channel4);
同樣,標(biāo)準(zhǔn)庫中還提供了設(shè)置傳輸數(shù)據(jù)量大小的函數(shù):
void DMA_SetCurrDataCounter(DMA_Channel_TypeDef* DMAy_Channelx,uint16_t DataNumber);
將以上幾步全部配置好后,我們就可以使用 DMA 來傳輸對應(yīng)外設(shè)的數(shù)據(jù)了。
硬件設(shè)計
本實驗使用到硬件資源如下:
(1)D1 和 D2 指示燈
(2)K_UP 按鍵
(3)串口 1
(4)DMA
D1和 D2 指示燈、K_UP 按鍵、串口 1 電路在前面章節(jié)都介紹過,這里就不多說,至于 DMA 它屬于 STM32F1 芯片內(nèi)部的資源,只要通過軟件配置好 DMA即可使用。D1指示燈用來提示系統(tǒng)運行狀態(tài),K_UP 按鍵用來控制 DMA發(fā)送,每按一次K_UP鍵,DMA 就將內(nèi)存(自定義的一個數(shù)組)內(nèi)數(shù)據(jù)發(fā)送 USART1,并通過串口 1將發(fā)送的內(nèi)容打印出來,在 DMA 數(shù)據(jù)傳輸?shù)倪^程中讓 D2 指示燈不斷閃爍,直到數(shù)據(jù)傳輸完成。D2 指示燈閃爍表示 CPU在執(zhí)行其他的任務(wù),說明 DMA 傳輸是不需要占用 CPU 的。
所要實現(xiàn)的功能是:通過 K_UP 按鍵控制 DMA 串口 1 數(shù)據(jù)的傳送,在傳送過程中讓 D2 指示燈不斷閃爍,直到數(shù)據(jù)傳送完成。D1 指示燈閃爍提示系統(tǒng)正常運行。程序框架如下:
(1)初始化 USART1_TX對應(yīng)的 DMA 通道相關(guān)參數(shù)
(2)編寫主函數(shù)
前面介紹 DMA 配置步驟時, 就已經(jīng)講解如何初始化 DMA。下面我們打開 “DMA實驗” 工程, 在 APP 工程組中可以看到添加了dma.c文件(里面包含了 DMA 驅(qū)動程序),在 StdPeriph_Driver 工程組中添加了stm32f10x_dma.c 庫文件。DMA 操作的庫函數(shù)都放在 stm32f10x_dma.c 和stm32f10x_dma.h 文件中,所以使用到 DMA 就必須加入 stm32f10x_dma.c文件,同時還要包含對應(yīng)的頭文件路徑。
這里我們分析幾個重要函數(shù),其他部分程序大家可以打開工程查看。
DMA 初始化函數(shù)
要使用 DMA,我們必須先對它進行配置。初始化代碼如下:
/****************************************************************
* 函 數(shù) 名 : DMAx_Init
* 函數(shù)功能 : DMA 初始化函數(shù)
* 輸 入 :
DMAy_Channelx:DMA 通 道 選 擇 ,@ref DMA_channel
DMA_Channel_0~DMA_Channel_7
par:外設(shè)地址
mar:存儲器地址
ndtr:數(shù)據(jù)傳輸量
* 輸 出 : 無
*****************************************************************/
void DMAx_Init(DMA_Channel_TypeDef* DMAy_Channelx,u32 par,u32
mar,u16 ndtr)
{
DMA_InitTypeDef DMA_InitStructure;
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);//DMA1時鐘使能
//DMA_DeInit(DMAy_Channelx);
/* 配置 DMA */
DMA_InitStructure.DMA_PeripheralBaseAddr = par;//DMA外設(shè)地址
DMA_InitStructure.DMA_MemoryBaseAddr = mar;//DMA 存儲器0地址
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;//存儲器到外設(shè)模式
DMA_InitStructure.DMA_BufferSize = ndtr;//數(shù)據(jù)傳輸量
DMA_InitStructure.DMA_PeripheralInc =
DMA_PeripheralInc_Disable;//外設(shè)非增量模式
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;//存儲器增量模式
DMA_InitStructure.DMA_PeripheralDataSize =
DMA_PeripheralDataSize_Byte;//外設(shè)數(shù)據(jù)長度:8 位
DMA_InitStructure.DMA_MemoryDataSize =DMA_MemoryDataSize_Byte;//存儲器數(shù)據(jù)長度:8 位
DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;// 使用普通模式
DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;//中等優(yōu)先級
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; //DMA 通道 x 沒有設(shè)置為內(nèi)存到內(nèi)存?zhèn)鬏?DMA_Init(DMAy_Channelx, &DMA_InitStructure);//初始化DMA
}
在 DMAx_Init()函數(shù)中,首先使能 DMA1 時鐘,然后初始化 DMA 各參數(shù),即配置 DMA_InitStructure結(jié)構(gòu)體,初始化 DMA 通道,這一過程在前面步驟介紹中已經(jīng)提了。
通道的選擇有函數(shù)參數(shù)DMAy_Channelx傳遞進來, 函數(shù)中還有另外3個形參,par 傳遞的是外設(shè)地址,mar傳遞的是存儲器(內(nèi)存)地址,ndtr傳遞的是DMA數(shù)據(jù)傳輸量。這樣做的好處是方便大家修改。
開啟 DMA 傳輸函數(shù)
配置好 DMA 后,我們需要開啟它,代碼如下:
/****************************************************************
* 函 數(shù) 名 : DMAx_Enable
* 函數(shù)功能 : 開啟一次 DMA 傳輸
* 輸 入 : DMAy_Channelx:DMA 通道選擇,@ref DMA_channel
DMA_Channel_0~DMA_Channel_7
ndtr:數(shù)據(jù)傳輸量
* 輸 出 : 無
*****************************************************************/
void DMAx_Enable(DMA_Channel_TypeDef *DMAy_Channelx,u16 ndtr)
{
DMA_Cmd(DMAy_Channelx, DISABLE); //關(guān)閉
DMA 傳輸
DMA_SetCurrDataCounter(DMAy_Channelx,ndtr); //數(shù)據(jù)傳輸量
DMA_Cmd(DMAy_Channelx, ENABLE); //開啟DMA傳輸
}
此函數(shù)功能很簡單,首先失能 DMA 傳輸通道,然后設(shè)置傳輸?shù)臄?shù)據(jù)量大小,最后使能 DMA 傳輸通道。函數(shù)帶有形參 DMAy_Channelx 和 ndtr,用于方便選擇對應(yīng)外設(shè)的通道和數(shù)據(jù)量。
主函數(shù)
編寫好 DMA 的初始化和使能函數(shù)后, 接下來就可以編寫主函數(shù)了, 代碼如下:
/****************************************************************
* 函 數(shù) 名 : main
* 函數(shù)功能 : 主函數(shù)
* 輸 入 : 無
* 輸 出 : 無
*****************************************************************/
int main()
{
u8 i=0;
u8 key;
SysTick_Init(72);
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //中斷優(yōu)先級分組 分2 組
LED_Init();
USART1_Init(9600);
KEY_Init();
DMAx_Init(DMA1_Channel4,(u32)&USART1- >DR,(u32)send_buf,send_buf_len);
Send_Data(send_buf);
while(1)
{
key=KEY_Scan(0);
if(key==KEY_UP)
{
USART_DMACmd(USART1,USART_DMAReq_Tx,ENABLE); //使能串口 1的DMA 發(fā)送
DMAx_Enable(DMA1_Channel4,send_buf_len); //開始一次DMA 傳輸!
//等待 DMA傳輸完成,此時我們來做另外一些事
//實際應(yīng)用中,傳輸數(shù)據(jù)期間,可以執(zhí)行另外的任務(wù)
while(1)
{
if(DMA_GetFlagStatus(DMA1_FLAG_TC4)!=0)//判斷通道 4傳輸完成
{
DMA_ClearFlag(DMA1_FLAG_TC4);
break;
}
led2=!led2;
delay_ms(300);
}
}
i++;
if(i%20==0)
{
led1=!led1;
}
delay_ms(10);
}
}
主函數(shù)實現(xiàn)的功能很簡單,首先調(diào)用之前編寫好的硬件初始化函數(shù),包括SysTick系統(tǒng)時鐘, 中斷分組, LED初始化等。然后調(diào)用我們前面編寫的DMAx_Init函數(shù),由于 USART1_TX 是在 DMA1 的通道 4 中,所以通道選擇為 DMA1_Channel4。
外設(shè)地址傳遞的是&USART1- >DR,因為 USART1 用來接收數(shù)據(jù)的寄存器是USART1- >DR,加上一個&就轉(zhuǎn)換成地址了。存儲器地址為send_buf(數(shù)組名就是數(shù)組的地址) , 這個是我們自定義的一個u8類型數(shù)組, 數(shù)組大小為send_buf_len,這個也是我們定義的一個宏,值為 5000。然后調(diào)用Send_Data 函數(shù),此函數(shù)功能是將數(shù)組 send_buf 內(nèi)所有成員全部賦值為字符 5,代碼如下:
#define send_buf_len 5000
u8 send_buf[send_buf_len];
void Send_Data(u8 *p)
{
u16 i;
for(i=0;i< send_buf_len;i++)
{
*p='5';
p++;
}
}
最后進入 while 循環(huán),調(diào)用KEY_Scan 函數(shù),不斷檢測 K_UP按鍵是否按下,如果 K_UP 按鍵按下,啟動一次 USART_TX 的 DMA 傳輸,在傳輸?shù)倪^程中讓 CPU控制 D2指示燈閃爍,直到 DMA 數(shù)據(jù)傳輸完成。D1 指示燈間隔200ms 閃爍,提示系統(tǒng)正常運行。
將工程程序編譯后下載到開發(fā)板內(nèi),可以看到 D1 指示燈不斷閃爍,表示程序正常運行。當(dāng) K_UP 按鍵按下,DMA 開始將內(nèi)存數(shù)組內(nèi)的數(shù)據(jù)傳輸?shù)酱?span id="0lamwpm" class="hljs-number">1 上,同時傳輸過程中,D2 指示燈閃爍,直到傳輸完成。如果想在串口調(diào)試助手上看到傳輸信息,可以打開“串口調(diào)試助手”,首先勾選下標(biāo)號 1 DTR 框,然后再取消勾選。這是因為此串口助手啟動時會把系統(tǒng)復(fù)位住,通過 DTR 狀態(tài)切換下即可。然后設(shè)置好波特率等參數(shù)后,串口助手上即會收到串口發(fā)送過來的信息。(串口助手上先勾選下標(biāo)號1 DTR 框,然后再取
消勾選)如圖所示:
-
函數(shù)
+關(guān)注
關(guān)注
3文章
4327瀏覽量
62569 -
dma
+關(guān)注
關(guān)注
3文章
560瀏覽量
100544 -
stm32f1
+關(guān)注
關(guān)注
1文章
56瀏覽量
12199
發(fā)布評論請先 登錄
相關(guān)推薦
評論