《STM32从零开始学习历程》——DMA直接存储区访问理论知识
《STM32從零開始學習歷程》@EnzoReventon
DMA—直接存儲區訪問理論知識
本文主要介紹STM32F4 DMA直接存儲區的理論知識部分,本文主要參考手冊為:
[野火EmbedFire]《STM32庫開發實戰指南——基于野火霸天虎開發板》
[正點原子]STM32F4開發指南-庫函數版本_V1.2
[ST]《STM32F4xx中文參考手冊》
在學習野火教程第22章的基礎上進行理解、解讀與拓展,爭取以一種比較好理解的簡述方式向大家介紹這一內容。
1. DMA簡介
DMA(Direct Memory Access, 直接存儲區訪問)是一種獨立于CPU的控制器。它的主要功能是實現數據在“外設寄存器與存儲器之間”、“存儲器與存儲器之間”獨立于CPU的高速傳輸。
在這里,外設一般指外設的數據寄存器(如ADC,SPI,I2C,DCMI等外設的數據寄存器);存儲器一般是指片內SRAM、外部存儲器、片內FLASH等。
① 外設寄存器到存儲器傳輸:就是把外設數據寄存器內容轉移到指定的內存空間中。
② 存儲器到外設寄存器傳輸:就是把特定存儲區域的內容轉移到外設寄存器中。
③ 存儲器到存儲器傳輸:就是把一個指定存儲區內容拷貝到另一個指定的存儲區中。
注意: DMA1 僅支持① ② ,不支持③。,DMA2支持① ② ③。
=============================================================================================
解釋一下為什么DMA1不支持存儲器到存儲器的傳輸:
如上圖所示,DMA2控制器ANB接口都與中間的總線矩陣相連接了,存儲器RAM與總線矩陣相連接,因此DMA2可以實現存儲器與存儲器之間的數據傳輸。
而DMA1,其中一個AHB接口沒有與總線矩陣相連接,固然也就無法實現存儲器與存儲器之間的數據傳輸了。
作為一個固定的知識點記住就好。
2. DMA的主要特點(針對STM32F4)
3. 功能框圖分析
STM32F4其中一個的DMA控制器框圖如上圖所示,下面我們來詳細講解一下這個框圖。(STM32一共有兩個DMA控制器)
=============================================================================================
在上圖中,左邊的叫做通道,右邊的叫做流。
好了,那么問題來了,什么是“通道”?什么是“流”?
首先,“流”是數據傳輸的一條鏈路,“通道”:emmm不知道怎么解釋,只可意會不可言傳,不同的通道對印著不同的DMA請求。
緊接著,從上圖看到,對于一個DMA控制器共有8路“流”,每一“流”,又有8個“通道”!也就是說,對于一個DMA控制共有8 x 8 = 64 路通道可以用來獨立的傳輸數據。
好了又有問題來了,那么這么多通道怎么用?是不是可以隨便用?
答案是否定的!
每一個通道有著不同的傳輸請求映射!如下表所示:
DMA1:
| 通道 0 | SPI3_RX | SPI3_RX | SPI2_RX | SPI2_TX | SPI3_TX | SPI3_TX | ||
| 通道 1 | I2C1_RX | TIM7_UP | TIM7_UP | I2C1_RX | I2C1_TX | I2C1_TX | ||
| 通道 2 | TIM4_CH1 | I2S3_EXT_RX | TIM4_CH2 | CH2 I2S2_EXT_TX | I2S3_EXT_TX | TIM4_UP | TIM4_CH3 | |
| 通道 3 | I2S3_EXT_RX | TIM2_UP TIM2_CH3 | I2C3_RX | I2S2_EXT_RX | I2C3_TX | TIM2_CH1 | TIM2_CH2 TIM2_CH4 | TIM2_UP TIM2_CH4 |
| 通道 4 | UART5_RX | USART3_RX | UART4_RX | USART3_TX | UART4_TX | USART2_RX | USART2_TX | UART5_TX |
| 通道 5 | UART8_TX | UART7_TX | TIM3_CH4 TIM3_UP | UART7_RX | TIM3_CH1 TIM3_TRIG | TIM3_CH2 | UART8_RX | TIM3_CH3 |
| 通道 6 | TIM5_CH3 TIM5_UP | TIM5_CH4 TIM5_TRIG | TIM5_CH1 | TIM5_CH4 TIM5_TRIG | TIM5_CH2 | TIM5_UP | ||
| 通道 7 | TIM6_UP | I2C2_RX | I2C2_RX | USART3_TX | DAC1 | DAC2 | I2C2_TX |
DMA2:
| 通道 0 | ADC1 | TIM8_CH1 TIM8_CH2 TIM8_CH3 | ADC1 | TIM1_CH1 TIM1_CH2 TIM1_CH3 | ||||
| 通道 1 | DCMI | ADC2 | ADC2 | SPI6_TX | SPI6_RX | DCMI | ||
| 通道 2 | ADC3 | ADC3 | SPI5_RX | SPI5_TX | CRYP_OUT | CRYP_IN | HASH_IN | |
| 通道 3 | SPI1_RX | SPI1_RX | SPI1_TX | SPI1_TX | ||||
| 通道 4 | SPI4_RX | SPI4_TX | USART1_RX | SDIO | SART1_RX | SDIO | USART1_TX | |
| 通道 5 | USART6_RX | USART6_RX | SPI4_RX | SPI4_TX | USART6_TX | USART6_TX | ||
| 通道 6 | TIM1_TRIG | TIM1_CH1 | TIM1_CH2 | TIM1_CH1 | TIM1_CH4 TIM1_TRIG TIM1_COM | TIM1_UP | TIM1_CH3 | |
| 通道 7 | TIM8_UP | TIM8_CH1 | TIM8_CH2 | TIM8_CH3 | SPI5_RX | SPI5_TX | TIM8_CH4 TIM8_TRIG TIM8_COM |
每個外設請求都會占用一個數據流通道,相同外設請求可以占用不同的數據流通道。
例如:DMA1的數據流0,我選擇使用通道2 I2C1_RX,那么其他7路通道就不可以使用了。
再例如:DMA1的數據流2,通道1,TIM7_UP與DMA1的數據流4,通道1,TIM7_UP是可以同時使用的。也就是說只要不是同一個數據流就可以了。
好了,通道 和 流 的基本信息就已經介紹完了,總結一下:
① STM32F4共有2個DMA控制器
② DMA1不支持存儲器到存儲器傳輸
③ 每一個DMA有8路數據流
④ 每一路流有8個通道
⑤ 每一個通道有特定的功能映射,需要查閱上文的表格進行選擇使用
⑥ 每一路流的只能有一個通道使用,一路流有一個以上通道使用是不允許的
⑦ 相同功能不同流的通道可以同時使用
=============================================================================================
顧名思義,仲裁器是用來仲裁、評判的,一個DMA控制器有8個數據流,如果在某一時刻,我們使用同一個DMA控制器中的多個數據流進行傳輸數據,那么必然會導致有多個數據流,如果沒有仲裁器,就像是十字路口沒有紅綠燈,會造成擁堵甚至事故,對于數據也是如此,因此需要一個仲裁器來設定數據流的優先傳輸的權力。
通過仲裁器來設定流的優先級時分為兩個階段,第一階段為軟件階段,第二階段為硬件階段。
軟件階段: 用戶可以通過配置優先級寄存器,將數據流優先級設定為:非常高,高,中和低四個級別。
硬件階段: 如果兩個或以上數據流的優先級一樣,則仲裁器優先級設定轉為硬件層面,他們的優先級取決于數據流的編號,編號越低則優先級越高,例如,在軟件層面優先級相同的情況下,數據流1的優先級高于數據流5的優先級。
=============================================================================================
我們要重點的來講一下什么是FIFO,First In First Out。
首先,FIFO叫做先進先出存儲緩沖區,它介于源與目標之間,是一個數據的中轉站。
每一個數據流都有4字大小的FIFO。1字 = 4字節 = 32位,4字 = 16字節。
對于這個緩沖區,存在有兩種數據傳輸模式:第一種為直接模式,第二種為FIFO模式。
模式的選擇由 DMA_SxFCR寄存器的DMDIS位控制,0為使能直接模式,1為禁止直接模式、開啟FIFO模式。
直接模式: 數據經過FIFO,不在FIFO中停留,直接將數據傳送到目標地址中。
FIFO模式: 數據經過FIFO,在FIFO中停留,根據設置的閾值,等到數據量達到了設定的閾值然后再發送到目標地址中。閾值可以設定為1/4,1/2,3/4,1。例如,閾值設定為1/4時,FIFO中的數據量達到16*(1/4)= 4 字節時將數據打包發送到目標地址中。
對于在FIFO中的數據,是以什么方式進行傳輸的呢?例如,閾值設定為1/4時,FIFO中也存滿了4字節的數據,這4字節的數據怎么打包發送呢?它是4字節全部打包發送還是4字節分四次打包發送呢?這就需要另外一個寄存器進行選擇配置了:DMA_SxCR中的MBURST(存儲器突發傳輸配置)以及PBURST(外設突發傳輸配置),突發模式!
下一章節將詳細的講解何為突發模式!
4. DMA數據配置
1. 突發&節拍的配置
位 24:23 MBURST:存儲器突發傳輸配置 (Memory burst transfer configuration)
這些位將由軟件置 1 和清零。
00:單次傳輸
01:INCR4(4 個節拍的增量突發傳輸)
10:INCR8(8 個節拍的增量突發傳輸)
11:INCR16(16 個節拍的增量突發傳輸)
這些位受到保護,只有 EN 為“0”時才可以寫入 在直接模式中,當位EN=“1”時,這些位由硬件強制置為 0x0。
位 22:21 PBURST[1:0]:外設突發傳輸配置 (Peripheral burst transfer configuration)
這些位將由軟件置 1 和清零。
00:單次傳輸
01:INCR4(4 個節拍的增量突發傳輸)
10:INCR8(8 個節拍的增量突發傳輸)
11:INCR16(16 個節拍的增量突發傳輸)
這些位受到保護,只有 EN為“0”時才可以寫入 在直接模式下,這些位由硬件強制置為 0x0。
這里又來了一個“節拍”的概念,我們來詳細的講一下這是什么。
| 字節 | 1/4 | 4個節拍1次突發 | – | – |
| 字節 | 1/2 | 4個節拍2次突發 | 8個節拍1次突發 | – |
| 字節 | 3/4 | 4個節拍3次突發 | – | – |
| 字節 | 1 | 4個節拍4次突發 | 8個節拍2次突發 | 16個節拍1次突發 |
| 半字 | 1/4 | – | – | – |
| 半字 | 1/2 | 4個節拍1次突發 | – | – |
| 半字 | 3/4 | – | – | – |
| 半字 | 1 | 4個節拍2次突發 | 8個節拍1次突發 | – |
| 字 | 1/4 | – | – | – |
| 字 | 1/2 | – | – | – |
| 字 | 3/4 | – | – | – |
| 字 | 1 | 4個節拍1次突發 | – | – |
首先,FIFO大小為4個字,FIFO閾值 = FIFO級別 x FIFO大小。
例如:
FIFO級別為1/4時,FIFO閾值大小為 4 x(1/4) = 1字 = 4字節。
FIFO級別為3/4時,FIFO閾值大小為 4 x(3/4) = 3字 = 12字節。
=============================================================================================
當目標地址存儲單元為字節時:
FIFO級別為1/4時:
當FIFO中存滿4 x(1/4) = 1字 = 4字節時,完成1次突發,4字節數據一次性打包發送給目標地址,每次發送4個字節。
FIFO級別為1/2時:
當FIFO中存滿4 x(1/2) = 2字 = 8字節時,完成2次突發,8字節數據分兩次發送給目標地址,每次發送4個字節;
或者8字節數據一次性發送給目標地址,每次發送8個字節。
FIFO級別為3/4時:
當FIFO中存滿4 x(3/4) = 3字 = 12字節時,完成3次突發,12字節數據分三次發送給目標地址,每次發送4個字節。
FIFO級別為1時:
當FIFO中存滿4 x 1 = 4字 = 16字節時,可以以4次突發,16字節數據分四次發送給目標地址,每次發送4個字節;
可以以2次突發,16字節數據分兩次發送給目標地址,每次發送8個字節;
也可以以1次突發,16字節數據一次性傳輸給目標地址,每次發送16個字節。
=============================================================================================
為什么FIFO級別為1/4時不能以8個節拍進行發送呢?
因為,FIFO的級別為1/4,其閾值為1個字,4個字節,此時FIFO存滿也就4個字節的數據,固然無法實現8個節拍進行發送。
為什么MSIZE為半字時,無法實現FIFO的級別為1/4以4個字節發送?
因為,FIFO的級別為1/4,其閾值為1個字,4個字節;如果是4個節拍發送的話,就是4個半字=8個字節發送,此時FIFO最大也就只有4個字節的數據,也便無法實現發送。
為什么MSIZE為字時,無法實現FIFO的級別為3/4以3個字節發送?
因為,FIFO的級別為3/4,其閾值為3個字,12個字節;如果實現4個節拍發送的畫,就是4個字=16個字節發送,此時FIFO最大也就12個字節的數據,無法實現發送。
下面再來解釋下為什么為什么MSIZE為半字時,可以實現FIFO的級別為1/2以8個字節發送?
因為,FIFO的級別為1/2,其閾值為2個字,8個字節;以4個節拍也就是4個半字=8個字節發送,正好等于FIFO閾值的數據大小(8個字節),所以可以成功發送。
以此類推!
=============================================================================================
PS: 在這里為了更好的理解FIFO閾值配置表格,可以將節拍理解為MSIZE,例如:MSIZE為字節時,FIFO級別為1/4(1個字,4個字節),4個節拍1次突發可以理解為4個字節一次突發;MSIZE為半字時,8個節拍一次突發可以理解為8個半字(4個字,16個字節),FIFO級別為1時(4個字,16個字節),可以實現一次突發。
也就是說:
節拍數(MSIZE數)x 突發數 = FIFO級別 x FIFO大小 (FIFO大小固定為4字)
例如:MSIZE = 半字;FIFO級別為 = 1/2;MBURST = 4個節拍1次突發
節拍數(MISIZE數)= 半字
突發數 = 4節拍 x 1次突發 = 4 半字 = 2 字 = 8字節
那么:節拍數(MSIZE數)x 突發數 = 8 字節
FIFO閾值 = 1/2 x 4 字 = 2 字 = 8字節
由此可見 FIFO閾值 = 節拍數(MSIZE數)x 突發數。
2. 循環模式
循環模式相對應于一次模式。一次模式就是傳輸一次就停止傳輸,下一次傳輸需要手動控制,而循環模式在傳輸一次后會自動按照相同配置重新傳輸,周而復始直至被控制停止或傳輸發生錯誤。
可以通過DMA_SxCR 寄存器的CIRC 位可以使能循環模式。
3. 雙緩沖模式
設置DMA_SxCR 寄存器的DBM 位為1 可啟動雙緩沖傳輸模式,并自動激活循環模式。
雙緩沖不應用與存儲器到存儲器的傳輸。雙緩沖模式下,兩個存儲器地址指針都有效,即DMA_SxM1AR寄存器將被激活使用。開始傳輸使用DMA_SxM0AR 寄存器的地址指針所對應的存儲區,當這個存儲區數據傳輸完DMA 控制器會自動切換至DMA_SxM1AR 寄存器的地址指針所對應的另一塊存儲區,如果這一塊也傳輸完成就再切換至DMA_SxM0AR 寄存器的地址指針所對應的存儲區,這樣循環調用。
4. DMA中斷
每個DMA 數據流可以在發送以下事件時產生中斷:
5. 初始化結構體庫函數
typedef struct {uint32_t DMA_Channel; //通道選擇uint32_t DMA_PeripheralBaseAddr; //外設地址uint32_t DMA_Memory0BaseAddr; //存儲器0 地址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_FIFOMode; //FIFO 模式uint32_t DMA_FIFOThreshold; //FIFO 閾值uint32_t DMA_MemoryBurst; //存儲器突發傳輸uint32_t DMA_PeripheralBurst; //外設突發傳輸}1) DMA_Channel:
DMA 請求通道選擇,可選通道0 至通道7,每個外設對應固定的通道,具體設置值需要查表DMA1 各個通道的請求映像和表DMA2 各個通道的請求映像。
2) DMA_PeripheralBaseAddr:
外設地址,設定DMA_SxPAR 寄存器的值;一般設置為外設的數據寄存器地址,如果是存儲器到存儲器模式則設置為其中一個存儲區地址。
ADC3 的數據寄存器ADC_DR 地址為((uint32_t)ADC3+0x4C)。
3) DMA_Memory0BaseAddr:
存儲器0 地址,設定DMA_SxM0AR 寄存器值;一般設置為我們自義存儲區的首地址。我們程序先自定義一個16 位無符號整形數組ADC_ConvertedValue[4]用來存放每個通道的ADC 值, 所以把數組首地址(直接使用數組名即可) 賦值給DMA_Memory0BaseAddr。
4) DMA_DIR:
傳輸方向選擇,可選外設到存儲器、存儲器到外設以及存儲器到存儲器。它設定DMA_SxCR 寄存器的DIR[1:0] 位的值。ADC 采集顯然使用外設到存儲器模式。
5) DMA_BufferSize:
設定待傳輸數據數目,初始化設定DMA_SxNDTR 寄存器的值。這里ADC是采集4 個通道數據,所以待傳輸數目也就是4。
6) DMA_PeripheralInc:
如果配置為DMA_PeripheralInc_Enable,使能外設地址自動遞增功能,它設定DMA_SxCR 寄存器的PINC 位的值;一般外設都是只有一個數據寄存器,所以一般不會使能該位。ADC3 的數據寄存器地址是固定并且只有一個所以不使能外設地址遞增。
7) DMA_MemoryInc:
如果配置為DMA_MemoryInc_Enable,使能存儲器地址自動遞增功能,它設定DMA_SxCR 寄存器的MINC 位的值;我們自定義的存儲區一般都是存放多個數據的,所以使能存儲器地址自動遞增功能。我們之前已經定義了一個包含4 個元素的數字用來存放數據,使能存儲區地址遞增功能,自動把每個通道數據存放到對應數組元素內。
8) DMA_PeripheralDataSize:
外設數據寬度,可選字節(8 位)、半字(16 位) 和字(32 位),它設定DMA_SxCR 寄存器的PSIZE[1:0] 位的值。ADC 數據寄存器只有低16 位數據有效,使用半字數據寬度。
9) DMA_MemoryDataSize:
存儲器數據寬度,可選字節(8 位)、半字(16 位) 和字(32 位),它設定DMA_SxCR 寄存器的MSIZE[1:0] 位的值。保存ADC 轉換數據也要使用半字數據寬度,這跟我們定義的數組是相對應的。
10) DMA_Mode:
DMA 傳輸模式選擇,可選一次傳輸或者循環傳輸,它設定DMA_SxCR 寄存器的CIRC 位的值。我們希望ADC 采集是持續循環進行的,所以使用循環傳輸模式。
11) DMA_Priority:
軟件設置數據流的優先級,有4 個可選優先級分別為非常高、高、中和低,它設定DMA_SxCR 寄存器的PL[1:0] 位的值。DMA 優先級只有在多個DMA 數據流同時使用時才有意義,這里我們設置為非常高優先級就可以了。
12) DMA_FIFOMode:
FIFO 模式使能,如果設置為DMA_FIFOMode_Enable 表示使能FIFO 模式功能;它設定DMA_SxFCR 寄存器的DMDIS 位。ADC 采集傳輸使用直接傳輸模式即可,不需要使用FIFO 模式。
13) DMA_FIFOThreshold:
FIFO 閾值選擇,可選4 種狀態分別為FIFO 容量的1/4、1/2、3/4 和滿;它設定DMA_SxFCR 寄存器的FTH[1:0] 位;DMA_FIFOMode 設置為DMA_FIFOMode_Disable,那DMA_FIFOThreshold 值無效。ADC 采集傳輸不使用FIFO 模式,設置改值無效。
14) DMA_MemoryBurst:
存儲器突發模式選擇,可選單次模式、4 節拍的增量突發模式、8 節拍的增量突發模式或16 節拍的增量突發模式,它設定DMA_SxCR 寄存器的MBURST[1:0] 位的值。ADC 采集傳輸是直接模式,要求使用單次模式。
15) DMA_PeripheralBurst:
外設突發模式選擇,可選單次模式、4 節拍的增量突發模式、8 節拍的增量突發模式或16 節拍的增量突發模式,它設定DMA_SxCR 寄存器的PBURST[1:0] 位的值。
ADC 采集傳輸是直接模式,要求使用單次模式。
總結
以上是生活随笔為你收集整理的《STM32从零开始学习历程》——DMA直接存储区访问理论知识的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Mindjet
- 下一篇: 如何使用计算机制作数学公式,利用电脑编程