0
  • 聊天消息
  • 系統(tǒng)消息
  • 評論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

STM32F1的DMA使用

科技綠洲 ? 來源:單片機(jī)與嵌入式 ? 作者:單片機(jī)與嵌入式 ? 2023-06-22 10:23 ? 次閱讀

在前面我們提到過 DMA,這一章我們就來學(xué)習(xí) STM32F1 的DMA 使

用。要實(shí)現(xiàn)的功能是:通過 K_UP 按鍵控制 DMA 串口 1 數(shù)據(jù)的傳送,在傳送過程中讓 D2 指示燈不斷閃爍,直到數(shù)據(jù)傳送完成。D1 指示燈閃爍提示系統(tǒng)正常運(yùn)行。學(xué)習(xí)時可以參考《STM32F10x 中文參考手冊》-10 DMA 控制器(DMA)章節(jié)。

DMA 簡介

DMA,全稱是 Direct Memory Access,中文意思為直接存儲器訪問。DMA 可用于實(shí)現(xiàn)外設(shè)與存儲器之間或者存儲器與存儲器之間數(shù)據(jù)傳輸?shù)母咝?。之所以稱為高效, 是因?yàn)?DMA 傳輸數(shù)據(jù)移動過程無需 CPU 直接操作, 這樣節(jié)省的 CPU 資源就可供其它操作使用。從硬件層面來理解,DMA 就好像是 RAM 與 I/O 設(shè)備間數(shù)據(jù)傳輸?shù)耐罚?外設(shè)與存儲器之間或者存儲器與存儲器之間可以直接在這條通路上進(jìn)行數(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)品中) ,DMA17 個通道。DMA25 個通道。每個通道專門用來管理來自于一個或多個外設(shè)對存儲器訪問的請求。還有一個仲裁器來協(xié)調(diào)各個 DMA 請求的優(yōu)先權(quán)。

STM32F1 的 DMA 有以下主要特性:

● 12 個獨(dú)立的可配置的通道(請求):DMA1 有 7 個通道, DMA2 有 5 個通

● 每個通道都直接連接專用的硬件 DMA 請求,每個通道都同樣支持軟件觸

發(fā)。這些功能通過軟件來配置。

● 在同一個 DMA 模塊上, 多個請求間的優(yōu)先權(quán)可以通過軟件編程設(shè)置(共有

四級:很高、高、中等和低),優(yōu)先權(quán)設(shè)置相等時由硬件決定(請求 0 優(yōu)先于請求1,依此類推) 。

● 獨(dú)立數(shù)據(jù)源和目標(biāo)數(shù)據(jù)區(qū)的傳輸寬度(字節(jié)、半字、全字),模擬打包和拆包的過程。源和目標(biāo)地址必須按數(shù)據(jù)傳輸寬度對齊。

● 支持循環(huán)的緩沖器管理

● 每個通道都有 3 個事件標(biāo)志(DMA 半傳輸、 DMA 傳輸完成和 DMA 傳輸出錯),這3 個事件標(biāo)志邏輯或成為一個單獨(dú)的中斷請求。

● 存儲器和存儲器間的傳輸

● 外設(shè)和存儲器、存儲器和外設(shè)之間的傳輸

● 閃存、 SRAM、外設(shè)的 SRAM、 APB1、 APB2 和 AHB 外設(shè)均可作為訪問的源和目標(biāo)。

● 可編程的數(shù)據(jù)傳輸數(shù)目:最大為 65535

DMA 結(jié)構(gòu)框圖

DMA 控制器獨(dú)立于內(nèi)核,屬于一個單獨(dú)的外設(shè),結(jié)構(gòu)比較簡單,從編程的角度來看,我們只需掌握結(jié)構(gòu)框圖中的三部分內(nèi)容即可。如圖所示(大家也可以查看《STM32F10x中文參考手冊》-10 DMA 控制器(DMA)章節(jié)內(nèi)容)。

圖片

我們把 DMA 結(jié)構(gòu)框圖分成3個子模塊,按照順序依次進(jìn)行簡單介紹。

(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 請求通道中 ADC3SDIO 和 TIM8 的 DMA 請求只在大容量產(chǎn)品中存在,這個在具體項(xiàng)目時要注意。

(2)標(biāo)號 2:DAM 通道

DMA 具有 12 個獨(dú)立可編程的通道,其中 DMA17 個通道, DMA25個通道,每個通道對應(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ù)指針的增量模式由實(shí)際情況決定。

(3)什么時候傳輸完成

數(shù)據(jù)什么時候傳輸完成,我們可以通過查詢標(biāo)志位或者通過中斷的方式來判斷每個 DMA 通道在 DMA 傳輸過半、傳輸完成和傳輸錯誤時都會有相應(yīng)的標(biāo)志位,如果使能了該類型的中斷后,則會產(chǎn)生中斷有關(guān)各個標(biāo)志位的詳細(xì)描述請參考 DMA 中斷狀態(tài)寄存器DMA_ISR 的詳細(xì)描述。

傳輸完成還分兩種模式,是一次傳輸還是循環(huán)傳輸,一次傳輸很好理解,即傳輸一次之后就停止,要想再傳輸?shù)脑?,必須關(guān)斷 DMA 使能后再重新配置后才能繼續(xù)傳輸循環(huán)傳輸則是一次傳輸完成之后又恢復(fù)第一次傳輸時的配置循環(huán)傳輸,不斷的重復(fù)。具體的由 DMA_CCR 寄存器的 CIRC 循環(huán)模式位控制。

DMA 配置步驟

接下來我們介紹下如何使用庫函數(shù)對 DMA 進(jìn)行配置。這個也是在編寫程序中必須要了解的。具體步驟如下:(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ù)進(jìn)行設(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ù)寄存器地址,比如要進(jìn)行串口 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]位的值決定。比如本章實(shí)驗(yàn)是從內(nèi)存讀取數(shù)據(jù)發(fā)送到串口,所以數(shù)據(jù)傳輸方向?yàn)榇鎯ζ鞯酵庠O(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í)驗(yàn)數(shù)據(jù)是按照 8 位字節(jié)傳輸,所以配置為DMA_PeripheralDataSize_Byte。

DMA_MemoryDataSize:存儲器數(shù)據(jù)寬度選擇,可以為字節(jié)(8 位)、半字(16位)、字(32 位),通過 DMA_CCR 寄存器的 MSIZE[1:0]位設(shè)置。本章實(shí)驗(yàn)同樣設(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 通道同時使用時才有意義,本章實(shí)驗(yàn)我們只使用了一個 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 通道同時使用時才有意義,本章實(shí)驗(yàn)我們只使用了一個 DMA 通道,所以可以任意設(shè)置DMA 優(yōu)先級,這里我們就設(shè)置為中等優(yōu)先級,配置參數(shù)為DMA_Priority_Medium。

DMA_M2M:用來設(shè)置存儲器到存儲器模式,使用存儲器到存儲器時用到,設(shè)定 DMA_CCR 的位 14 MEN2MEN 即可啟動存儲器到存儲器模式。

了解結(jié)構(gòu)體成員功能后,就可以進(jìn)行配置,本實(shí)驗(yàn)配置代碼如下:
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ù)相信不說也知道,就是使能或失能。

本實(shí)驗(yàn)使能 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è)計(jì)

本實(shí)驗(yàn)使用到硬件資源如下:

(1)D1 和 D2 指示燈

(2)K_UP 按鍵

(3)串口 1

(4)DMA

D1和 D2 指示燈、K_UP 按鍵、串口 1 電路在前面章節(jié)都介紹過,這里就不多說,至于 DMA 它屬于 STM32F1 芯片內(nèi)部的資源,只要通過軟件配置好 DMA即可使用。D1指示燈用來提示系統(tǒng)運(yùn)行狀態(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 的。

所要實(shí)現(xiàn)的功能是:通過 K_UP 按鍵控制 DMA 串口 1 數(shù)據(jù)的傳送,在傳送過程中讓 D2 指示燈不斷閃爍,直到數(shù)據(jù)傳送完成。D1 指示燈閃爍提示系統(tǒng)正常運(yùn)行。程序框架如下:

(1)初始化 USART1_TX對應(yīng)的 DMA 通道相關(guān)參數(shù)

(2)編寫主函數(shù)

前面介紹 DMA 配置步驟時, 就已經(jīng)講解如何初始化 DMA。下面我們打開 “DMA實(shí)驗(yàn)” 工程, 在 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,我們必須先對它進(jìn)行配置。初始化代碼如下:
/****************************************************************
* 函 數(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傳遞進(jìn)來, 函數(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傳輸完成,此時我們來做另外一些事
//實(shí)際應(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ù)實(shí)現(xiàn)的功能很簡單,首先調(diào)用之前編寫好的硬件初始化函數(shù),包括SysTick系統(tǒng)時鐘, 中斷分組, LED初始化等。然后調(diào)用我們前面編寫的DMAx_Init函數(shù),由于 USART1_TX 是在 DMA1 的通道 4 中,所以通道選擇為 DMA1_Channel4。

外設(shè)地址傳遞的是&USART1- >DR,因?yàn)?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++;
}
}
最后進(jìn)入 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)正常運(yùn)行。

將工程程序編譯后下載到開發(fā)板內(nèi),可以看到 D1 指示燈不斷閃爍,表示程序正常運(yùn)行。當(dāng) K_UP 按鍵按下,DMA 開始將內(nèi)存數(shù)組內(nèi)的數(shù)據(jù)傳輸?shù)酱?span id="8fxpeul"    class="hljs-number">1 上,同時傳輸過程中,D2 指示燈閃爍,直到傳輸完成。如果想在串口調(diào)試助手上看到傳輸信息,可以打開“串口調(diào)試助手”,首先勾選下標(biāo)號 1 DTR 框,然后再取消勾選。這是因?yàn)榇舜谥謫訒r會把系統(tǒng)復(fù)位住,通過 DTR 狀態(tài)切換下即可。然后設(shè)置好波特率等參數(shù)后,串口助手上即會收到串口發(fā)送過來的信息。(串口助手上先勾選下標(biāo)號1 DTR 框,然后再取

消勾選)如圖所示:

圖片

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請聯(lián)系本站處理。 舉報(bào)投訴
  • 函數(shù)
    +關(guān)注

    關(guān)注

    3

    文章

    4234

    瀏覽量

    61961
  • dma
    dma
    +關(guān)注

    關(guān)注

    3

    文章

    552

    瀏覽量

    99925
  • stm32f1
    +關(guān)注

    關(guān)注

    1

    文章

    56

    瀏覽量

    12133
收藏 人收藏

    評論

    相關(guān)推薦

    STM32實(shí)例教程-DMA實(shí)驗(yàn)

    在前面我們提到過 DMA,這一章我們就來學(xué)習(xí) STM32F1DMA 使用。要實(shí)現(xiàn)的功能是:通過 K_UP 按鍵控制 DMA 串口 1 數(shù)
    發(fā)表于 05-24 09:20 ?1022次閱讀
    <b class='flag-5'>STM32</b>實(shí)例教程-<b class='flag-5'>DMA</b>實(shí)驗(yàn)

    求各位大神看一下哪里出錯了 我想把發(fā)送的數(shù)據(jù)顯示在lcd上 可是下載以后lcd直接熄滅了

    ];//·¢?íêy?Y?o3???const u8 TEXT_TO_SEND[]={"ALIENTEK WarShip STM32F1 DMA ′??úêμ?é"}; int main(void
    發(fā)表于 10-09 21:50

    請問有STM32F1 DMA傳輸?shù)腎2C通信例程嗎?

    求一個STM32F1 DMA傳輸?shù)腎2C通信例程
    發(fā)表于 02-18 06:32

    STM32F4與STM32F1使用串口DMA有何區(qū)別呢

    STM32的串口DMA有哪些特性呢?STM32F4與STM32F1使用串口DMA有何區(qū)別呢?
    發(fā)表于 12-06 06:51

    如何利用STM32F1DMA來實(shí)現(xiàn)串口數(shù)據(jù)傳送

    本章介紹STM32F1DMA。在本章中,我們將利用STM32F1DMA來實(shí)現(xiàn)串口數(shù)據(jù)傳送,并在TFTLCD模塊上顯示當(dāng)前的傳送進(jìn)度。當(dāng)CPU初始化這個傳輸動作,傳輸動作本身是由
    發(fā)表于 02-08 07:36

    關(guān)于stm32f1中的ADC和DMA理解

    怎樣去校準(zhǔn)stm32f1中的ADC呢?怎樣去理解stm32f1中的ADC和DMA呢?
    發(fā)表于 02-21 06:05

    STM32F2直接存儲器訪問DMA

    /unpack? 突發(fā)傳輸模式? 通用DMA傳輸過程? 循環(huán)模式和雙緩沖模式(STM32F2新增)? 流控(STM32F2新增)? 事件、中斷和錯誤管理? DMA stream配置流程
    發(fā)表于 09-13 07:02

    STM32F1的ADC2如何使用DMA功能 USART過載錯誤Overrun error

    STM32F1的ADC2如何使用DMA功能,USART過載錯誤Overrun error
    的頭像 發(fā)表于 03-14 14:42 ?7770次閱讀
    <b class='flag-5'>STM32F1</b>的ADC2如何使用<b class='flag-5'>DMA</b>功能 USART過載錯誤Overrun error

    STM32F1 _DMA_USART

    STM32F1_DMA_USART
    的頭像 發(fā)表于 04-08 10:23 ?4475次閱讀
    <b class='flag-5'>STM32F1</b> _<b class='flag-5'>DMA</b>_USART

    STM32F1系列芯片中文參考手冊

    STM32F1系列芯片中文參考手冊(嵌入式開發(fā)培訓(xùn)教程)-STM32F1系列芯片的中文用戶手冊
    發(fā)表于 07-30 09:32 ?215次下載
    <b class='flag-5'>STM32F1</b>系列芯片中文參考手冊

    STM32F1F4的區(qū)別

    STM32F1F4的區(qū)別
    發(fā)表于 12-04 13:51 ?24次下載
    <b class='flag-5'>STM32F1</b>和<b class='flag-5'>F</b>4的區(qū)別

    STM32F1開發(fā)指南筆記32----DMA

    本章介紹STM32F1DMA。在本章中,我們將利用STM32F1DMA來實(shí)現(xiàn)串口數(shù)據(jù)傳送,并在TFTLCD模塊上顯示當(dāng)前的傳送進(jìn)度。當(dāng)CPU初始化這個傳輸動作,傳輸動作本身是由
    發(fā)表于 12-04 19:21 ?20次下載
    <b class='flag-5'>STM32F1</b>開發(fā)指南筆記32----<b class='flag-5'>DMA</b>

    STM32F1 ADC和DMA的簡單理解

    本文主要講解stm32f1中ADC和DMA的使用,在學(xué)習(xí)中參考了多篇文章,感謝前輩的付出。STM32F10X ADC多通道讀取小教程(包含DMA)_JameScottX的博客-CSDN
    發(fā)表于 12-27 19:09 ?35次下載
    <b class='flag-5'>STM32F1</b> ADC和<b class='flag-5'>DMA</b>的簡單理解

    STM32F1DMA提高串口速度

    STM32F1DMA,提高串口速度
    發(fā)表于 09-26 16:11 ?5次下載

    AN4904_從STM32F1STM32F4的軟件移植

    AN4904_從STM32F1STM32F4的軟件移植
    發(fā)表于 11-21 17:06 ?3次下載
    AN4904_從<b class='flag-5'>STM32F1</b>到<b class='flag-5'>STM32F</b>4的軟件移植