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

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

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

STM32F1的DMA使用

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

在前面我們提到過 DMA,這一章我們就來學習 STM32F1 的DMA 使

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

DMA 簡介

DMA,全稱是 Direct Memory Access,中文意思為直接存儲器訪問。DMA 可用于實現(xiàn)外設與存儲器之間或者存儲器與存儲器之間數(shù)據(jù)傳輸?shù)母咝浴V苑Q為高效, 是因為 DMA 傳輸數(shù)據(jù)移動過程無需 CPU 直接操作, 這樣節(jié)省的 CPU 資源就可供其它操作使用。從硬件層面來理解,DMA 就好像是 RAM 與 I/O 設備間數(shù)據(jù)傳輸?shù)耐罚?外設與存儲器之間或者存儲器與存儲器之間可以直接在這條通路上進行數(shù)據(jù)傳輸。這里說的外設一般指外設的數(shù)據(jù)寄存器, 比如 ADC、SPI、I2C、DCMI等外設的數(shù)據(jù)寄存器, 存儲器一般是指片內(nèi) SRAM、 外部存儲器、 片內(nèi) Flash等。

STM32F1 最多有 2 個 DMA 控制器 ( DMA2 僅存在大容量產(chǎn)品中) ,DMA17 個通道。DMA25 個通道。每個通道專門用來管理來自于一個或多個外設對存儲器訪問的請求。還有一個仲裁器來協(xié)調(diào)各個 DMA 請求的優(yōu)先權。

STM32F1 的 DMA 有以下主要特性:

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

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

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

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

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

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

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

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

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

● 外設和存儲器、存儲器和外設之間的傳輸

● 閃存、 SRAM、外設的 SRAM、 APB1、 APB2 和 AHB 外設均可作為訪問的源和目標。

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

DMA 結構框圖

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

圖片

我們把 DMA 結構框圖分成3個子模塊,按照順序依次進行簡單介紹。

(1)標號 1:DMA 請求

如果外設要想通過 DMA 來傳輸數(shù)據(jù), 必須先給 DMA 控制器發(fā)送 DMA 請求,DMA 收到請求信號之后, 控制器會給外設一個應答信號, 當外設應答后且 DMA 控制器收到應答信號之后,就會啟動 DMA 的傳輸,直到傳輸完畢。

根據(jù)前面介紹我們知道,DMA 含有 DMA1 和 DMA2 兩個控制器,其中 DMA1 含有 7個通道,DMA2 含有 5 個通道,不同的 DMA 控制器的通道對應著不同的外設請求。

從DMA 請求映射圖中可以知道各通道所對應的外設請求,如圖分別是DMA1和DMA2請求映射圖:

圖片

圖片

DMA2 請求通道中 ADC3、 SDIO 和 TIM8 的 DMA 請求只在大容量產(chǎn)品中存在,這個在具體項目時要注意。

(2)標號 2:DAM 通道

DMA 具有 12 個獨立可編程的通道,其中 DMA17 個通道, DMA25個通道,每個通道對應不同的外設的 DMA 請求。雖然每個通道可以接收多個外設的請求,但是同一時間只能接收一個,不能同時接收多個。

(3)標號 3:仲裁器

當發(fā)生多個 DMA 通道請求時,就意味著有先后響應處理的順序問題,這個就由仲裁器也管理。仲裁器管理 DMA 通道請求分為兩個階段。第一階段屬于軟件階段,可以在 DMA_CCRx 寄存器中設置,有 4 個等級:非常高、高、中和低四個優(yōu)先級。第二階段屬于硬件階段,如果兩個或以上的 DMA 通道請求設置的優(yōu)先級一樣,則他們優(yōu)先級取決于通道編號,編號越低優(yōu)先權越高,比如通道 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ù)的方向有三個:從外設到存儲器,從存儲器到外設,從存儲器到存儲器。具體的方向 DMA_CCR 位 4 DIR 配置:0 表示從外設到存儲器, 1 表示從存儲器到外設。這里面涉及到的外設地址由 DMA_CPAR 配置,存儲器地址由 DMA_CMAR配置。

外設到存儲器

當我們使用從外設到存儲器傳輸時,以 ADC 采集為例。DMA 外設寄存器的地址對應的就是 ADC 數(shù)據(jù)寄存器的地址,DMA 存儲器的地址就是我們自定義的變量(用來接收存儲 AD 采集的數(shù)據(jù))的地址。方向我們設置外設為源地址。

存儲器到外設

當我們使用從存儲器到外設傳輸時, 以串口向電腦端發(fā)送數(shù)據(jù)為例。DMA 外設寄存器的地址對應的就是串口數(shù)據(jù)寄存器的地址, DMA 存儲器的地址就是我們自定義的變量(相當于一個緩沖區(qū),用來存儲通過串口發(fā)送到電腦的數(shù)據(jù))的地址。方向我們設置外設為目標地址。

存儲器到存儲器

當我們使用從存儲器到存儲器傳輸時,以內(nèi)部 FLASH 向內(nèi)部 SRAM 復制數(shù)據(jù)為例。DMA 外設寄存器的地址對應的就是內(nèi)部 FLASH(我們這里把內(nèi)部 FALSH當作一個外設來看)的地址, DMA 存儲器的地址就是我們自定義的變量(相當于一個緩沖區(qū),用來存儲來自內(nèi)部 FLASH 的數(shù)據(jù))的地址。方向我們設置外設(即內(nèi)部 FLASH)為源地址。跟上面兩個不一樣的是,這里需要把 DMA_CCR 位14:MEM2MEM:存儲器到存儲器模式配置為 1,啟動 M2M 模式。

(2)要傳多少,單位是什么

當我們配置好數(shù)據(jù)要從哪里來到哪里去之后, 我們還需要知道我們要傳輸?shù)臄?shù)據(jù)是多少,數(shù)據(jù)的單位是什么。

以串口向電腦發(fā)送數(shù)據(jù)為例,我們可以一次性給電腦發(fā)送很多數(shù)據(jù),具體多少由 DMA_CNDTR 配置,這是一個 32 位的寄存器,一次最多只能傳輸 65535 個數(shù)據(jù)。

要想數(shù)據(jù)傳輸正確,源和目標地址存儲的數(shù)據(jù)寬度還必須一致,串口數(shù)據(jù)寄存器是 8 位的,所以我們定義的要發(fā)送的數(shù)據(jù)也必須是 8 位。外設的數(shù)據(jù)寬度由 DMA_CCR 的 PSIZE[1:0]配置,可以是 8/16/32 位,存儲器的數(shù)據(jù)寬度由DMA_CCR 的 MSIZE[1:0]配置,可以是 8/16/32 位。

在 DMA 控制器的控制下,數(shù)據(jù)要想有條不紊的從一個地方搬到另外一個地方,還必須正確設置兩邊數(shù)據(jù)指針的增量模式。外設的地址指針由 DMA_CCRx 的PINC 配置,存儲器的地址指針由 MINC 配置。以串口向電腦發(fā)送數(shù)據(jù)為例,要發(fā)送的數(shù)據(jù)很多,每發(fā)送完一個,那么存儲器的地址指針就應該加 1,而串口數(shù)據(jù)寄存器只有一個,那么外設的地址指針就固定不變。具體的數(shù)據(jù)指針的增量模式由實際情況決定。

(3)什么時候傳輸完成

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

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

DMA 配置步驟

接下來我們介紹下如何使用庫函數(shù)對 DMA 進行配置。這個也是在編寫程序中必須要了解的。具體步驟如下:(DMA 相關庫函數(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 通道,包括配置通道、外設和內(nèi)存地址、傳輸數(shù)據(jù)量等要使用 DMA,必須對其相關參數(shù)進行設置,包括通道選擇、外設和內(nèi)存地址、

通道優(yōu)先級、傳輸數(shù)據(jù)量的配置等。該部分設置通過 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ù)是一個結構體指針變量,結構體類型是 DMA_InitTypeDef,其內(nèi)包含了 DMA 相關參數(shù)的設置。下面我們簡單介紹下它的成員:
typedef struct
{
uint32_t DMA_PeripheralBaseAddr; // 外設地址
uint32_t DMA_MemoryBaseAddr; // 存儲器地址
uint32_t DMA_DIR; // 傳輸方向
uint32_t DMA_BufferSize; // 傳輸數(shù)目
uint32_t DMA_PeripheralInc; // 外設地址增量模式
uint32_t DMA_MemoryInc; // 存儲器地址增量模式
uint32_t DMA_PeripheralDataSize; // 外設數(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:外設地址,通過 DMA_CPAR 寄存器設置,一般設置為外設的數(shù)據(jù)寄存器地址,比如要進行串口 DMA 傳輸,那么外設基地址為串口接受發(fā)送數(shù)據(jù)存儲器 USART1- >DR 的地址,表示方法為&USART1- >DR。如果是存儲器到存儲器模式則設置為其中一個存儲區(qū)地址。

DMA_Memory0BaseAddr:存儲器地址,通過 DMA_CMAR 寄存器設置,一般設置為我們自定義存儲區(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ù)傳輸方向選擇,可選擇外設到存儲器、存儲器到外設以及存

儲器到存儲器。通過設定 DMA_CCR 寄存器的 DIR[1:0]位的值決定。比如本章實驗是從內(nèi)存讀取數(shù)據(jù)發(fā)送到串口,所以數(shù)據(jù)傳輸方向為存儲器到外設,配置為DMA_DIR_MemoryToPeripheral。

DMA_BufferSize:用來設置一次傳輸數(shù)據(jù)的大小,通過 DMA_CNDTR 寄存器設置。

DMA_PeripheralInc:用來設置外設地址是遞增還是不變,通過 DMA_CCR寄存器的 PINC 位設置,如果設置為遞增,那么下一次傳輸?shù)臅r候地址加 1。通常外 設 只 有 一 個 數(shù) 據(jù) 寄 存 器 , 所 以 一 般 不 會 使 能 該 位 , 即 配 置 為DMA_PeripheralInc_Disable。

DMA_MemoryInc:用來設置內(nèi)存地址是否遞增,通過 DMA_CCR 寄存器的MINC位設置。我們自定義的存儲區(qū)一般都是存放多個數(shù)據(jù)的,所以需要使能存儲器地址自動遞增功能,即配置為 DMA_MemoryInc_Enable。

DMA_PeripheralDataSize:外設數(shù)據(jù)寬度選擇,可以為字節(jié)(8 位)、半字

(16位)、字(32 位),通過 DMA_CCR 寄存器的 PSIZE[1:0]位設置。例如本實驗數(shù)據(jù)是按照 8 位字節(jié)傳輸,所以配置為DMA_PeripheralDataSize_Byte。

DMA_MemoryDataSize:存儲器數(shù)據(jù)寬度選擇,可以為字節(jié)(8 位)、半字(16位)、字(32 位),通過 DMA_CCR 寄存器的 MSIZE[1:0]位設置。本章實驗同樣設置為 8 位字節(jié)傳輸,這個要和我們定義的數(shù)組對應,所以配置為DMA_MemoryDataSize_Byte。

DMA_Mode:DMA 傳輸模式選擇, 可選擇一次傳輸或者循環(huán)傳輸, 通過DMA_CCR寄存器的 CIRC 位來設定。比如我們要從內(nèi)存 (存儲器) 中傳輸 64 個字節(jié)到串口,如果設置為循環(huán)傳輸,那么它會在 64 個字節(jié)傳輸完成之后繼續(xù)從內(nèi)存的第一個地址傳輸,如此循環(huán)。這里我們設置為一次傳輸完成之后不循環(huán)。所以設置值為DMA_Mode_Normal。

DMA_Priority:用來設置 DMA 通道的優(yōu)先級,有低,中,高,超高四種級別,可通過 DMA_CCR 寄存器的PL[1:0]位來設定。DMA 優(yōu)先級只有在多個 DMA 通道同時使用時才有意義,本章實驗我們只使用了一個 DMA 通道,所以可以任意設置DMA 優(yōu)先級,這里我們就設置為中等優(yōu)先級,配置參數(shù)為DMA_Priority_Medium。

DMA_Priority:用來設置 DMA 通道的優(yōu)先級,有低,中,高,超高四種級別,可通過 DMA_CCR 寄存器的PL[1:0]位來設定。DMA 優(yōu)先級只有在多個 DMA 通道同時使用時才有意義,本章實驗我們只使用了一個 DMA 通道,所以可以任意設置DMA 優(yōu)先級,這里我們就設置為中等優(yōu)先級,配置參數(shù)為DMA_Priority_Medium。

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

了解結構體成員功能后,就可以進行配置,本實驗配置代碼如下:
DMA_InitTypeDef DMA_InitStructure;
DMA_InitStructure.DMA_PeripheralBaseAddr = par;//DMA外設地址
DMA_InitStructure.DMA_MemoryBaseAddr = mar;//DMA 存儲器0地址
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;//存儲器到外
設模式
DMA_InitStructure.DMA_BufferSize = ndtr;//數(shù)據(jù)傳輸量
DMA_InitStructure.DMA_PeripheralInc =
DMA_PeripheralInc_Disable;//外設非增量模式
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;//存儲器
增量模式
DMA_InitStructure.DMA_PeripheralDataSize =
DMA_PeripheralDataSize_Byte;//外設數(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 沒有

設置為內(nèi)存到內(nèi)存?zhèn)鬏?/p>

DMA_Init(DMAy_Channelx, &DMA_InitStructure);//初始化DMA

(3)使能外設 DMA功能(DMA 請求映射圖對應的外設)

配置好 DMA 后,我們就需要使能外設 DMA 功能,例如我們要使能串口的DMA發(fā)送功能,調(diào)用的庫函數(shù)為:
USART_DMACmd(USART1,USART_DMAReq_Tx,ENABLE); //使能串口 1 的 DMA發(fā)送
如果是要使能串口DMA接受, 那么第二個參數(shù)修改為USART_DMAReq_Rx即可。

如果是其他的外設需開啟DMA功能, 只需要在對應的標準外設庫函數(shù)中查找到對應的外設 DMA 使能函數(shù)。

(4)開啟 DMA 的通道傳輸

初始化 DMA 后,要使用DMA還必須開啟它,開啟 DMA 通道傳輸?shù)膸旌瘮?shù)為:
void DMA_Cmd(DMA_Channel_TypeDef* DMAy_Channelx, FunctionalStateNewState);
第一個參數(shù)為外設所對應的 DMA 通道,例如本章使用的是 USART1_TXDMA請求,因此它對應的是 DMA1_Channel4,可通過前面DMA 請求映射圖選擇。第二個參數(shù)相信不說也知道,就是使能或失能。

本實驗使能 DMA1_Channel4函數(shù)為:
DMA_Cmd(DMA1_Channel4,ENABLE);

(5)查詢 DMA 傳輸狀態(tài)

通過以上 4 步設置,我們就可以啟動一次 DMA 傳輸了。但是在 DMA 傳輸過程中,我們還需要查詢 DMA傳輸通道的狀態(tài),使用的庫函數(shù)是:
FlagStatus DMA_GetFlagStatus(uint32_t DMAy_FLAG);
例如我們要查詢 DMA1通道4 傳輸是否完成,方法是:
DMA_GetFlagStatus(DMA1_FLAG_TC4);
標準庫中,還提供了獲取當前剩余數(shù)據(jù)量大小的函數(shù):
uint16_t DMA_GetCurrDataCounter(DMA_Channel_TypeDef*DMAy_Channelx);
例如我們要獲取 DMA1通道4 還有多少個數(shù)據(jù)沒有傳輸,方法是:
DMA_GetCurrDataCounter(DMA1_Channel4);
同樣,標準庫中還提供了設置傳輸數(shù)據(jù)量大小的函數(shù):
void DMA_SetCurrDataCounter(DMA_Channel_TypeDef* DMAy_Channelx,uint16_t DataNumber);
將以上幾步全部配置好后,我們就可以使用 DMA 來傳輸對應外設的數(shù)據(jù)了。

硬件設計

本實驗使用到硬件資源如下:

(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í)行其他的任務,說明 DMA 傳輸是不需要占用 CPU 的。

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

(1)初始化 USART1_TX對應的 DMA 通道相關參數(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文件,同時還要包含對應的頭文件路徑。

這里我們分析幾個重要函數(shù),其他部分程序大家可以打開工程查看。

DMA 初始化函數(shù)

要使用 DMA,我們必須先對它進行配置。初始化代碼如下:
/****************************************************************
* 函 數(shù) 名 : DMAx_Init
* 函數(shù)功能 : DMA 初始化函數(shù)
* 輸 入 :
DMAy_Channelx:DMA 通 道 選 擇 ,@ref DMA_channel
DMA_Channel_0~DMA_Channel_7
par:外設地址
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外設地址
DMA_InitStructure.DMA_MemoryBaseAddr = mar;//DMA 存儲器0地址
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;//存儲器到外設模式
DMA_InitStructure.DMA_BufferSize = ndtr;//數(shù)據(jù)傳輸量
DMA_InitStructure.DMA_PeripheralInc =
DMA_PeripheralInc_Disable;//外設非增量模式
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;//存儲器增量模式
DMA_InitStructure.DMA_PeripheralDataSize =
DMA_PeripheralDataSize_Byte;//外設數(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 沒有設置為內(nèi)存到內(nèi)存?zhèn)鬏?DMA_Init(DMAy_Channelx, &DMA_InitStructure);//初始化DMA
}
DMAx_Init()函數(shù)中,首先使能 DMA1 時鐘,然后初始化 DMA 各參數(shù),即配置 DMA_InitStructure結構體,初始化 DMA 通道,這一過程在前面步驟介紹中已經(jīng)提了。

通道的選擇有函數(shù)參數(shù)DMAy_Channelx傳遞進來, 函數(shù)中還有另外3個形參,par 傳遞的是外設地址,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); //關閉
DMA 傳輸
DMA_SetCurrDataCounter(DMAy_Channelx,ndtr); //數(shù)據(jù)傳輸量
DMA_Cmd(DMAy_Channelx, ENABLE); //開啟DMA傳輸
}
此函數(shù)功能很簡單,首先失能 DMA 傳輸通道,然后設置傳輸?shù)臄?shù)據(jù)量大小,最后使能 DMA 傳輸通道。函數(shù)帶有形參 DMAy_Channelx 和 ndtr,用于方便選擇對應外設的通道和數(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ù)據(jù)期間,可以執(zhí)行另外的任務
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。

外設地址傳遞的是&USART1- >DR,因為 USART1 用來接收數(shù)據(jù)的寄存器是USART1- >DR,加上一個&就轉換成地址了。存儲器地址為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 指示燈不斷閃爍,表示程序正常運行。當 K_UP 按鍵按下,DMA 開始將內(nèi)存數(shù)組內(nèi)的數(shù)據(jù)傳輸?shù)酱?span id="oxchv2a"    class="hljs-number">1 上,同時傳輸過程中,D2 指示燈閃爍,直到傳輸完成。如果想在串口調(diào)試助手上看到傳輸信息,可以打開“串口調(diào)試助手”,首先勾選下標號 1 DTR 框,然后再取消勾選。這是因為此串口助手啟動時會把系統(tǒng)復位住,通過 DTR 狀態(tài)切換下即可。然后設置好波特率等參數(shù)后,串口助手上即會收到串口發(fā)送過來的信息。(串口助手上先勾選下標號1 DTR 框,然后再取

消勾選)如圖所示:

圖片

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

    關注

    3

    文章

    4331

    瀏覽量

    62594
  • dma
    dma
    +關注

    關注

    3

    文章

    561

    瀏覽量

    100572
  • stm32f1
    +關注

    關注

    1

    文章

    56

    瀏覽量

    12206
收藏 人收藏

    評論

    相關推薦

    STM32實例教程-DMA實驗

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

    求各位大神看一下哪里出錯了 我想把發(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來實現(xiàn)串口數(shù)據(jù)傳送

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

    關于stm32f1中的ADC和DMA理解

    怎樣去校準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 ?8100次閱讀
    <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 ?4640次閱讀
    <b class='flag-5'>STM32F1</b> _<b class='flag-5'>DMA</b>_USART

    STM32F1系列芯片中文參考手冊

    STM32F1系列芯片中文參考手冊(嵌入式開發(fā)培訓教程)-STM32F1系列芯片的中文用戶手冊
    發(fā)表于 07-30 09:32 ?218次下載
    <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來實現(xiàn)串口數(shù)據(jù)傳送,并在TFTLCD模塊上顯示當前的傳送進度。當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的使用,在學習中參考了多篇文章,感謝前輩的付出。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的軟件移植