日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > windows >内容正文

windows

STM32系统学习——DMA(直接储存器访问)

發(fā)布時間:2025/3/21 windows 48 豆豆
生活随笔 收集整理的這篇文章主要介紹了 STM32系统学习——DMA(直接储存器访问) 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

DMA主要功能是傳輸數(shù)據(jù),但是不需要占用CPU,即在傳輸數(shù)據(jù)時,CPU可以做別的事,像多線程。數(shù)據(jù)傳輸從外設到存儲器或者從存儲器到存儲器。DMA控制器包含了DMA1和DMA2,其中DMA1有7個通道,DMA2有5個通道,可以理解為傳輸數(shù)據(jù)的一種管道。要注意的是,DMA2只存在于大容量單片機中。?
一、DMA框圖解析?
DMA控制器獨立于內(nèi)核,屬于一個單獨外設,結(jié)構(gòu)結(jié)合下圖來看?
?
1.DMA請求?
如果外設想通過DMA傳輸數(shù)據(jù),必須先向DMA控制器發(fā)送DMA請求,DMA收到請求信號后,控制器會給外設一個應答信號,當外設應答且DMA控制器收到應答信號后,就會啟動DMA傳輸,直到傳輸完畢。?
DMA有DMA1和DMA2兩個控制器,DMA1有兩個控制器,DMA1有7個通道,DMA2有5個通道,不同DMA控制器的通道有不同的外設請求。?
2、通道?
DMA有12個獨立可編程的通道,DMA1有7個通道,DMA2有5個通道,每個通道對應不同外設的DMA請求。雖然每個通道可以接收多個外設請求,但是同一時間只能接收一個,不能同時接收多個。?
3、仲裁器?
當同時有多個DMA請求時,就意味著有先后響應的問題,這個就由仲裁器管理。仲裁器管理DMA請求分為2個階段:第一階段屬于軟件階段,可以在MDA_CCRx寄存器中設置,有 4 個等級:非常高、高、中和低四個優(yōu)先級。第二階段屬于硬件階段,如果兩個或以上的 DMA 通道請求設置的優(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ù)。?
1、從哪兒來,到哪兒去?
DMA傳輸數(shù)據(jù) 的方向有3個:外設到存儲器,存儲器到外設,存儲器到存儲器。具體方向由DMA_CCR中第四位DIR配置:0表示外設到存儲器,1表示存儲器到外設。涉及的地址由DMA_CPAR配置,存儲器地址由DMA_CMAR配置。?
1)從外設到存儲器?
以ADC采集為例,DMA外部寄存器地址對應ADC數(shù)據(jù)寄存器地址,DMA存儲器地址是我們自定義的變量的地址。方向設置為源地址。?
2)存儲器到外設?
存儲器到外設傳輸以串口向電腦端發(fā)送為例,DMA 外設寄存器的地址對應的就是串口數(shù)據(jù)寄存器的地址,DMA 存儲器的地址就是我們自定義的變量(相當于一個緩沖區(qū),用來存儲通過串口發(fā)送到電腦的數(shù)據(jù))的地址。方向我們設置外設為目標地址。?
3)存儲器到存儲器?
當我們使用從存儲器到存儲器傳輸時,以內(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、要傳什么,單位是多少?
以串口向電腦發(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ù)脑?#xff0c;必須關斷 DMA 使能后再重新配置后才能繼續(xù)傳輸。循環(huán)傳輸則是一次傳輸完成之后又恢復第一次傳輸時的配置循環(huán)傳輸,不斷的重復。具體的由 DMA_CCR寄存器的 CIRC 循環(huán)模式位控制。

三、DMA初始化結(jié)構(gòu)體?
結(jié)構(gòu)體 xxx_InitTypeDef 定義在stm32f10x_xxx.h(后面xxx為外設名稱)文件中,庫函數(shù)xxx_Init定義在stm32f10x_xxx.c文件中。

DMA_ InitTypeDef 初始化結(jié)構(gòu)體 1 typedef struct 2 { 3 uint32_t DMA_PeripheralBaseAddr; // 外設地址 4 uint32_t DMA_MemoryBaseAddr; // 存儲器地址 5 uint32_t DMA_DIR; // 傳輸方向 6 uint32_t DMA_BufferSize; // 傳輸數(shù)目 7 uint32_t DMA_PeripheralInc; // 外設地址增量模式 8 uint32_t DMA_MemoryInc; // 存儲器地址增量模式 9 uint32_t DMA_PeripheralDataSize; // 外設數(shù)據(jù)寬度 10 uint32_t DMA_MemoryDataSize; // 存儲器數(shù)據(jù)寬度 11 uint32_t DMA_Mode; // 模式選擇 12 uint32_t DMA_Priority; // 通道優(yōu)先級 13 uint32_t DMA_M2M; // 存儲器到存儲器模式 14 } DMA_InitTypeDef;

1) DMA_PeripheralBaseAddr:外設地址,設定 DMA_CPAR 寄存器的值;一般設置為外設的數(shù)據(jù)寄存器地址,如果是存儲器到存儲器模式則設置為其中一個存儲器地址。?
2) DMA_Memory0BaseAddr:存儲器地址,設定 DMA_CMAR 寄存器值;一般設置為我們自定義存儲區(qū)的首地址。?
3) DMA_DIR:傳輸方向選擇,可選外設到存儲器、存儲器到外設。它設定DMA_CCR 寄存器的 DIR[1:0]位的值。這里并沒有存儲器到存儲器的方向選擇,當使用存儲器到存儲器時,只需要把其中一個存儲器當作外設使用即可。?
4) DMA_BufferSize:設定待傳輸數(shù)據(jù)數(shù)目,初始化設定 DMA_CNDTR 寄存器的值。?
5) DMA_PeripheralInc:如果配置為 DMA_PeripheralInc_Enable,使能外設地址自動遞增功能,它設定 DMA_CCR 寄存器的 PINC 位的值;一般外設都是只有一個數(shù)據(jù)寄存器,所以一般不會使能該位。?
6) DMA_MemoryInc:如果配置為DMA_MemoryInc_Enable,使能存儲器地址自動遞增功能,它設定 DMA_CCR 寄存器的 MINC 位的值;我們自定義的存儲區(qū)一般都是存放多個數(shù)據(jù)的,所以要使能存儲器地址自動遞增功能。?
7) DMA_PeripheralDataSize:外設數(shù)據(jù)寬度,可選字節(jié)(8位)、半字(16位)和字(32位),它設定 DMA_CCR寄存器的 PSIZE[1:0]位的值。?
8) DMA_MemoryDataSize:存儲器數(shù)據(jù)寬度,可選字節(jié)(8 位)、半字(16 位)和字(32位),它設定 DMA_CCR 寄存器的 MSIZE[1:0]位的值。當外設和存儲器之間傳數(shù)據(jù)時,兩邊的數(shù)據(jù)寬度應該設置為一致大小。?
9) DMA_Mode:DMA 傳輸模式選擇,可選一次傳輸或者循環(huán)傳輸,它設定DMA_CCR 寄存器的 CIRC 位的值。例程我們的 ADC 采集是持續(xù)循環(huán)進行的,所以使用循環(huán)傳輸模式。?
10) DMA_Priority:軟件設置通道的優(yōu)先級,有 4 個可選優(yōu)先級分別為非常高、高、中和低,它設定 DMA_CCR 寄存器的 PL[1:0]位的值。DMA 通道優(yōu)先級只有在多個 DMA 通道同時使用時才有意義,如果是單個通道,優(yōu)先級可以隨便設置。?
11) DMA_M2M:存儲器到存儲器模式,使用存儲器到存儲器時用到,設定DMA_CCR 的位 14MEN2MEN 即可啟動存儲器到存儲器模式。


四、存儲器到存儲器的實驗?
先定義一個靜態(tài)的源數(shù)據(jù),存放在內(nèi)部Flash存儲器中,使用DMA傳輸,把源數(shù)據(jù)拷貝到目標地址上(內(nèi)部SRAM),最后對比源數(shù)據(jù)和目標地址的數(shù)據(jù),看看是否準確傳輸。?
1、思路要點?
1)使能DMA時鐘?
2)配置DMA數(shù)據(jù)參數(shù)?
3)使能DMA,進行傳輸?
4)等待傳輸完成,并對源數(shù)據(jù)和目標地址數(shù)據(jù)進行比較。?
2、DMA宏定義以及變量定義

1 // 當使用存儲器到存儲器模式時候,通道可以隨便選,沒有硬性的規(guī)定 2 #define DMA_CHANNEL DMA1_Channel6 3 #define DMA_CLOCK RCC_AHBPeriph_DMA1 4 5 // 傳輸完成標志 6 #define DMA_FLAG_TC DMA1_FLAG_TC6 7 8 // 要發(fā)送的數(shù)據(jù)大小 9 #define BUFFER_SIZE 32 10 11 /* 定義 aSRC_Const_Buffer 數(shù)組作為 DMA 傳輸數(shù)據(jù)源 12 * const 關鍵字將 aSRC_Const_Buffer 數(shù)組變量定義為常量類型 13 * 表示數(shù)據(jù)存儲在內(nèi)部的 FLASH 中 14 */ 15 const uint32_t aSRC_Const_Buffer[BUFFER_SIZE]= 16 { 17 0x01020304,0x05060708,0x090A0B0C,0x0D0E0F10, 18 0x11121314,0x15161718,0x191A1B1C,0x1D1E1F20, 19 0x21222324,0x25262728,0x292A2B2C,0x2D2E2F30, 20 0x31323334,0x35363738,0x393A3B3C,0x3D3E3F40, 21 0x41424344,0x45464748,0x494A4B4C,0x4D4E4F50, 22 0x51525354,0x55565758,0x595A5B5C,0x5D5E5F60, 23 0x61626364,0x65666768,0x696A6B6C,0x6D6E6F70, 24 0x71727374,0x75767778,0x797A7B7C,0x7D7E7F80 25 }; 26 /* 定義 DMA 傳輸目標存儲器 27 * 存儲在內(nèi)部的 SRAM 中 28 */ 29 uint32_t aDST_Buffer[BUFFER_SIZE];

aSRC_Const_Buffer[BUFFER_SIZE]定義用來存放源數(shù)據(jù),并且使用了const關鍵字修飾,即常量類型,使得變量存儲在內(nèi)部Flash空間上。

3、DMA數(shù)據(jù)配置

void DMA_Config(void) 2 { 3 DMA_InitTypeDef DMA_InitStructure; 4 5 // 開啟 DMA 時鐘 6 RCC_AHBPeriphClockCmd(DMA_CLOCK, ENABLE); 7 // 源數(shù)據(jù)地址 8 DMA_InitStructure.DMA_PeripheralBaseAddr = 9 (uint32_t)aSRC_Const_Buffer; 10 // 目標地址 11 DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)aDST_Buffer; 12 // 方向:外設到存儲器(這里的外設是內(nèi)部的 FLASH) 13 DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC; 14 // 傳輸大小 15 DMA_InitStructure.DMA_BufferSize = BUFFER_SIZE; 16 // 外設(內(nèi)部的 FLASH)地址遞增 17 DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Enable; 18 // 內(nèi)存地址遞增 19 DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; 20 // 外設數(shù)據(jù)單位 21 DMA_InitStructure.DMA_PeripheralDataSize = 22 DMA_PeripheralDataSize_Word; 23 // 內(nèi)存數(shù)據(jù)單位 24 DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Word; 25 // DMA 模式,一次或者循環(huán)模式 26 DMA_InitStructure.DMA_Mode = DMA_Mode_Normal ; 27 //DMA_InitStructure.DMA_Mode = DMA_Mode_Circular; 28 // 優(yōu)先級:高 29 DMA_InitStructure.DMA_Priority = DMA_Priority_High; 30 // 使能內(nèi)存到內(nèi)存的傳輸 31 DMA_InitStructure.DMA_M2M = DMA_M2M_Enable; 32 // 配置 DMA 通道 33 DMA_Init(DMA_CHANNEL, &DMA_InitStructure); 34 // 使能 DMA 35 DMA_Cmd(DMA_CHANNEL,ENABLE); 36 }

使用 DMA_InitTypeDef 結(jié)構(gòu)體定義一個 DMA 初始化變量,這個結(jié)構(gòu)體內(nèi)容我們之前已經(jīng)有詳細講解。?
調(diào)用 RCC_AHBPeriphClockCmd 函數(shù)開啟 DMA時鐘,使用 DMA控制器之前必須開啟對應的時鐘。?
源地址和目標地址使用之前定義的數(shù)組首地址,傳輸?shù)臄?shù)據(jù)量為宏 BUFFER_SIZE 決定,源和目標地址指針地址遞增,使用一次傳輸模式不能循環(huán)傳輸,因為只有一個 DMA通道,優(yōu)先級隨便設置,最后調(diào)用 DMA_Init 函數(shù)完成 DMA 的初始化配置。?
DMA_ClearFlag函數(shù)用于清除DMA標志位,代碼用到傳輸完成標志位,使用之前先清除傳輸完成標志位以免產(chǎn)生不必要干擾。DMA_ClearFlag 函數(shù)需要 1 個形參,即事件標志位,可選有傳輸完成標志位、半傳輸標志位、FIFO 錯誤標志位、傳輸錯誤標志位等等,非常多,我們這里選擇傳輸完成標志位,由宏 DMA_FLAG_TC 定義。?
DMA_Cmd 函數(shù)用于啟動或者停止 DMA 數(shù)據(jù)傳輸,它接收兩個參數(shù),第一個是 DMA通道,另外一個是開啟 ENABLE 或者停止 DISABLE。

4、存儲器數(shù)據(jù)對比

1 uint8_t Buffercmp(const uint32_t* pBuffer, 2 uint32_t* pBuffer1, uint16_t BufferLength) 3 { 4 /* 數(shù)據(jù)長度遞減 */ 5 while (BufferLength--) { 6 /* 判斷兩個數(shù)據(jù)源是否對應相等 */ 7 if (*pBuffer != *pBuffer1) { 8 /* 對應數(shù)據(jù)源不相等馬上退出函數(shù),并返回 0 */ 9 return 0; 10 } 11 /* 遞增兩個數(shù)據(jù)源的地址指針 */ 12 pBuffer++; 13 pBuffer1++; 14 } 15 /* 完成判斷并且對應數(shù)據(jù)相對 */ 16 return 1; 17 }

判斷指定長度的兩個數(shù)據(jù)源是否完全相等,如果完全相等返回 1;只要其中一對數(shù)據(jù)不相等返回 0。它需要三個形參,前兩個是兩個數(shù)據(jù)源的地址,第三個是要比較數(shù)據(jù)長度。?
5、main函數(shù)

1 int main(void) 2 { 3 /* 定義存放比較結(jié)果變量 */ 4 uint8_t TransferStatus; 5 6 /* LED 端口初始化 */ 7 LED_GPIO_Config(); 8 9 /* 設置 RGB 彩色燈為紫色 */ 10 LED_PURPLE; 11 12 /* 簡單延時函數(shù) */ 13 Delay(0xFFFFFF); 14 15 /* DMA 傳輸配置 */ 16 DMA_Config(); 17 18 /* 等待 DMA 傳輸完成 */ 19 while (DMA_GetFlagStatus(DMA_FLAG_TC)==RESET) 20 { 21 22 } 23 24 /* 比較源數(shù)據(jù)與傳輸后數(shù)據(jù) */ 25 TransferStatus=Buffercmp(aSRC_Const_Buffer, aDST_Buffer, BUFFER_SIZE); 26 27 /* 判斷源數(shù)據(jù)與傳輸后數(shù)據(jù)比較結(jié)果*/ 28 if (TransferStatus==0) 29 { 30 /* 源數(shù)據(jù)與傳輸后數(shù)據(jù)不相等時 RGB 彩色燈顯示紅色 */ 31 LED_RED; 32 } 33 else 34 { 35 /* 源數(shù)據(jù)與傳輸后數(shù)據(jù)相等時 RGB 彩色燈顯示藍色 */ 36 LED_BLUE; 37 } 38 39 while (1) 40 { 41 } 42 }

首先定義一個變量用來保存存儲器數(shù)據(jù)比較結(jié)果。?
RGB 彩色燈用來指示程序進程,使用之前需要初始化它,LED_GPIO_Config 定義在bsp_led.c 文件中。開始設置 RGB 彩色燈為紫色,LED_PURPLE 是定義在 bsp_led.h 文件的一個宏定義。?
Delay函數(shù)只是一個簡單的延時函數(shù)。?
調(diào)用 DMA_Config 函數(shù)完成 DMA 數(shù)據(jù)流配置并啟動 DMA 數(shù)據(jù)傳輸。?
DMA_GetFlagStatus 函數(shù)獲取 DMA 事件標志位的當前狀態(tài),這里獲取 DMA 數(shù)據(jù)傳輸完成這個標志位,使用循環(huán)持續(xù)等待直到該標志位被置位,即 DMA 傳輸完成這個事件發(fā)生,然后退出循環(huán),運行之后程序。?
確定 DMA 傳輸完成之后就可以調(diào)用 Buffercmp 函數(shù)比較源數(shù)據(jù)與 DMA 傳輸后目標地址的數(shù)據(jù)是否一一對應。TransferStatus 保存比較結(jié)果,如果為 1 表示兩個數(shù)據(jù)源一一對應相等說明 DMA 傳輸成功;相反,如果為 0 表示兩個數(shù)據(jù)源數(shù)據(jù)存在不等情況,說明 DMA傳輸出錯。?
如果 DMA傳輸成功設置 RGB彩色燈為藍色,如果 DMA傳輸出錯設置 RGB彩色燈為紅色。

《新程序員》:云原生和全面數(shù)字化實踐50位技術專家共同創(chuàng)作,文字、視頻、音頻交互閱讀

總結(jié)

以上是生活随笔為你收集整理的STM32系统学习——DMA(直接储存器访问)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。