DMA 簡介
DMA(Direct Memory Access)—直接存儲器存取,是單片機的一個外設,它的主要功能是用來搬數據,但是不需要占用 CPU,即在傳輸數據的時候,CPU 可以干其他的事情,好像是多線程一樣。數據傳輸支持從外設到存儲器或者存儲器到存儲器,這里的存儲器可以是 SRAM 或者是 FLASH。DMA 控制器包含了 DMA1 和 DMA2,其中 DMA1 有 7 個通道, DMA2 有 5 個通道,這里的通道可以理解為傳輸數據的一種管道。要注意的是 DMA2 只存在於大容量的單片機中。
DMA 功能框圖
DMA 控制器獨立於內核,屬於一個單獨的外設,結構比較簡單,從編程的角度來看,我們只需掌握功能框圖中的三部分內容即可,具體見圖 22-1:DMA 控制器的框圖。
1. ①DMA 請求
如果外設要想通過 DMA 來傳輸數據,必須先給 DMA 控制器發送 DMA 請求,DMA 收到請求信號之后,控制器會給外設一個應答信號,當外設應答后且 DMA 控制器收到應答信號之后,就會啟動 DMA 的傳輸,直到傳輸完畢。 DMA 有 DMA1 和 DMA2 兩個控制器,DMA1 有 7 個通道,DMA2 有 5 個通道,不同的 DMA 控制器的通道對應着不同的外設請求,這決定了我們在軟件編程上該怎么設置,具體見 DMA 請求映像表。
2. ①通道
DMA 具有 12 個獨立可編程的通道,其中 DMA1 有 7 個通道,DMA2 有 5 個通道,每個通道對應不同的外設的 DMA 請求。雖然每個通道可以接收多個外設的請求,但是同一時間只能接收一個,不能同時接收多個。
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_CCRx 的 PSIZE[1:0]配置,可以是 8/16/32 位,存儲器的數據寬度由 DMA_CCRx 的 MSIZE[1:0]配置,可以是 8/16/32 位。
在 DMA 控制器的控制下,數據要想有條不紊的從一個地方搬到另外一個地方,還必須正確設置兩邊數據指針的增量模式。外設的地址指針由 DMA_CCRx 的 PINC 配置,存儲器的地址指針由 MINC 配置。以串口向電腦發送數據為例,要發送的數據很多,每發送完一個,那么存儲器的地址指針就應該加 1,而串口數據寄存器只有一個,那么外設的地址指針就固定不變。具體的數據指針的增量模式由實際情況決定。
3. 什么時候傳輸完成
數據什么時候傳輸完成,我們可以通過查詢標志位或者通過中斷的方式來鑒別。每個 DMA 通道在 DMA 傳輸過半、傳輸完成和傳輸錯誤時都會有相應的標志位,如果使能了該類型的中斷后,則會產生中斷。有關各個標志位的詳細描述請參考 DMA 中斷狀態寄存器 DMA_ISR 的詳細描述。
傳輸完成還分兩種模式,是一次傳輸還是循環傳輸,一次傳輸很好理解,即是傳輸一次之后就停止,要想再傳輸的話,必須關斷 DMA 使能后再重新配置后才能繼續傳輸。循環傳輸則是一次傳輸完成之后又恢復第一次傳輸時的配置循環傳輸,不斷的重復。具體的由 DMA_CCRx 寄存器的 CIRC 循環模式位控制。
DMA 初始化結構體詳解
標准庫函數對每個外設都建立了一個初始化結構體 xxx_InitTypeDef(xxx 為外設名稱),結構體成員用於設置外設工作參數,並由標准庫函數 xxx_Init()調用這些設定參數進入設置外設相應的寄存器,達到配置外設工作環境的目的。結構體 xxx_InitTypeDef 和庫函數 xxx_Init 配合使用是標准庫精髓所在,理解了結構體 xxx_InitTypeDef 每個成員意義基本上就可以對該外設運用自如。結構體 xxx_InitTypeDef 定義 在 stm32f10x_xxx.h( 后 面 xxx 為外設名稱 ) 文 件 中 , 庫 函 數 xxx_Init 定義在 stm32f10x_xxx.c 文件中,編程時我們可以結合這兩個文件內注釋使用。
DMA_ InitTypeDef 初始化結構體
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;
1) DMA_PeripheralBaseAddr:外設地址,設定 DMA_CPAR 寄存器的值;一般設置為外設的數據寄存器地址,如果是存儲器到存儲器模式則設置為其中一個存儲器地址。 2) DMA_Memory0BaseAddr:存儲器地址,設定 DMA_CMAR 寄存器值;一般設置為我們自定義存儲區的首地址
3) DMA_DIR:傳輸方向選擇,可選外設到存儲器、存儲器到外設。它設定 DMA_CCR 寄存器的 DIR[1:0]位的值。這里並沒有存儲器到存儲器的方向選擇,當使用存儲器到存儲器時,只需要把其中一個存儲器當作外設使用即可。
4) DMA_BufferSize:設定待傳輸數據數目,初始化設定 DMA_CNDTR 寄存器的值。
5) DMA_PeripheralInc:如果配置為 DMA_PeripheralInc_Enable,使能外設地址自動遞增功能,它設定 DMA_CCR 寄存器的 PINC 位的值;一般外設都是只有一個數據寄存器,所以一般不會使能該位。
6) DMA_MemoryInc:如果配置為 DMA_MemoryInc_Enable,使能存儲器地址自動遞增功能,它設定 DMA_CCR 寄存器的 MINC 位的值;我們自定義的存儲區一般都是存放多個數據的,所以要使能存儲器地址自動遞增功能。
7) DMA_PeripheralDataSize:外設數據寬度,可選字節(8 位)、半字(16 位)和字(32 位),它設定 DMA_CCR 寄存器的 PSIZE[1:0]位的值。
8) DMA_MemoryDataSize:存儲器數據寬度,可選字節(8 位)、半字(16 位)和字(32 位),它設定 DMA_CCR 寄存器的 MSIZE[1:0]位的值。當外設和存儲器之間傳數據時,兩邊的數據寬度應該設置為一致大小。
9) DMA_Mode:DMA 傳輸模式選擇,可選一次傳輸或者循環傳輸,它設定 DMA_CCR 寄存器的 CIRC 位的值。例程我們的 ADC 采集是持續循環進行的,所以使用循環傳輸模式。
10) DMA_Priority:軟件設置通道的優先級,有 4 個可選優先級分別為非常高、高、中和低,它設定 DMA_CCR 寄存器的 PL[1:0]位的值。DMA 通道優先級只有在多個 DMA 通道同時使用時才有意義,如果是單個通道,優先級可以隨便設置。
11) DMA_M2M :存 儲器 到存 儲器 模式 , 使 用存儲 器到 存儲 器時 用到, 設定 DMA_CCR 的位 14 MEN2MEN 即可啟動存儲器到存儲器模式。