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