詳細理解STM32F42x系列的DMA配置


以SPI2 為例,將存儲器中的數據,通過DMA方式搬運到外設,也就是往外發SPI_TX,DMA配置步驟:

1、選擇DMA1還是DMA2:通過圖1可查看到SPI2是在DMA1表里,所以選擇DMA1。

  RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA1,ENABLE);

2、選擇數據流:該配置應該放在所有信息配置完在使能。

  DMA_Cmd(DMAX_StreamY, ENABLE);  

  其中X = 1、2,Y=0、1…7,有兩個DMA,分別是DMA1和DMA2,每個DMA控制器有又有8個數據流。

  問題1:一個外設怎么知道選哪個數據流呢?

  答:先查看參考手冊,找到DMA1/2請求映射表,如下圖1、2

 

圖1

 

圖2

  比如現在用到外設SPI2_TX(存儲器的數據搬運到外設,所以得找發送) ,選擇數據流4。

  DMA_Cmd(DMA1_Stream4, ENABLE);

3、通道選擇,有8個通道,不是隨便選擇的,得查看圖1和圖2,SPI2外設所對應的通道0。

  DMA_InitStructure.DMA_Channel = DMA_Channel_0;

4、設置外設基地址。

  DMA_InitStructure.DMA_PeripheralBaseAddr = 0Xxxxx;

  問題2:怎么知道當前選用外設的基地址?看人家例子都是寫好了的,到時換其他外設,這基地址又不知道該寫多少了。

       答:先找到這個表,如圖3,可以看到SPI2在0x4000 3800~0x4000 3BFF范圍內。

 

圖3

  按照右邊的提示“第769頁的xxxxx映射”提示找到下表圖4,找到SPI_DR數據寄存器。

 

圖4

  可以查看SPI_DR具體信息圖5,該寄存器表示:已接收或者要發送的數據。說明找到這個偏移地址是沒有錯的,DMA就從該地址上搬走或送來數據。

 

 

 圖5

  通過以上查找最終地址是0x4000 3800 + 0x0C = 0x4000 380C

  DMA_InitStructure.DMA_PeripheralBaseAddr =0x4000 380C;

5、設置存儲數據的地址,DMA從存儲器取數據,該存儲器在內存中的的首地址。

  u32 buffer[] = {0,1,2,3,4,5};

       DMA_InitStructure.DMA_Memory0BaseAddr = (u32) buffer;

6、設置DMA傳輸方向(存儲器到外設、存儲器到存儲器或外設到存儲器)

  DMA_InitStructure.DMA_DIR = DMA_DIR_MemoryToPeripheral;  //存儲器到外設

7、設置DMA 緩沖區的容量大小,查看資料,圖6,DMA傳輸最大的數量是65535。

 

 

圖6

  DMA_InitStructure.DMA_BufferSize = (uint32_t)0xFFFF; //0xFFFF轉換十進制就是65535。

  設置DMA緩沖器的大小關系到另外兩個參數設,源(存儲器)和目標(外設)傳輸數據的寬度,是以字節、半字或字為單位進行搬運。

  DMA_InitStructure.DMA_PeripheralDataSize  = DMA_PeripheralDataSize_HalfWord;

  DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;

  注意:

  (1)、設置的緩沖區大小65535,不能單純理解65535個字節,還得看設置的數據寬度是多少,數據寬度設置的是半字(16bit),即16 *65535 bit。

  (2)、源(存儲器)和目標(外設)傳輸數據的寬度要設置一樣。

 8、設置外設和存儲器地址是否要遞增

  DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; //外設遞增不地址,是一直從SPI2地址上獲取數據的

  DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; //存儲器地址要遞增,否則數據會被覆蓋掉

9、 設置DMA 工作模式,有兩種模式DMA_Mode_Normal 和 DMA_Mode_Circular。

  (1)、正常模式,即DMA只傳輸一次。當傳輸完一次后,還想再傳一次,需重啟DMA_Cmd(DMA1_Stream4, ENABLE);

  (2)、循環模式可用於處理循環緩沖區和連續數據流(例如ADC掃描模式)。

  DMA_InitStructure.DMA_Mode  = DMA_Mode_Normal; //如果是單次讀取,可用正常模式

  DMA_InitStructure.DMA_Mode  = DMA_Mode_Circular ;//掃描讀取可用循環模式,存儲器到存儲器不能選擇循環模式

10、設置DMA優先級(有四種低、中、高、最高),多個外設需要用到DMA時,就需要設置優先級。

  DMA_InitStructure.DMA_Priority = DMA_Priority_High;

11、設置FIFO模式,用於在源數據傳輸到目標 之前 臨時存放 這些數據,也就是說數據先存放到FIFO中,待FIFO數據量達到一定閾值,再將數據傳輸到目標。

  DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable;//不使用FIFO模式

  DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_HalfFull;//可選擇1/4、1/2、3/4和full四種,不使用FIFO模式,這參數沒必要寫

12、設置外設和存儲器突發傳輸,DMA控制可以產生單次傳輸或4個、8個和16個節拍的增量突發傳輸

  (1)、單次傳輸時,每個DMA請求產生一次(一個節拍,這樣好理解突發傳輸)數據傳輸(傳輸數據寬度可以是字節、半字和字為單位)。

  (2)、突發傳輸時,每個DMA請求相應地生成4個、8個或16個節拍傳輸數據(傳輸數據寬度可以是字節、半字和字為單位),期間不被中斷。

  比如:此例子中傳輸數據寬度設置是半字,即16bit 

  DMA緩沖區大小設置的是最大值65535

       單次傳輸,DMA請求產生一次(一個節拍)數據傳輸,總共傳輸數據 16 / 8* 65535 = 131070Byte, 大概傳輸了128KByte

  突發傳輸4個節拍,4*128 = 513KByte

  突發傳輸8個節拍,8*128 = 1MByte

  突發傳輸16個節拍,16*128 = 2MByte

      一次DMA請求傳輸的數據量越大,占用DMA時間越長,期間不會被中斷,如果系統還有其他外設需要用DMA時,就得考慮一次傳輸的數據量了。

  DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;             //選擇單次傳輸

  DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;     // 選擇單次傳輸

完整配置:

DMA_InitTypeDef     DMA_InitStructure;
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA1,ENABLE);  //使能DMA1 時鍾
DMA_DeInit(DMA1_Stream4);    //初始化為默認復位值
DMA_InitStructure.DMA_Channel = DMA_Channel_0;         //SPI2 對應的是通道0
DMA_InitStructure.DMA_PeripheralBaseAddr =0x4000 380C;  //外設地址
DMA_InitStructure.DMA_Memory0BaseAddr = (u32) buffer;    //存儲器的首地址
DMA_InitStructure.DMA_DIR = DMA_DIR_MemoryToPeripheral;    //存儲器到外設
DMA_InitStructure.DMA_BufferSize = (uint32_t)0xFFFF;    //設置DMA緩沖大小,最大值65535
DMA_InitStructure.DMA_PeripheralDataSize  = DMA_PeripheralDataSize_HalfWord;    //寬度選擇半字進行傳輸,也就是16bit
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;        //寬度選擇半字進行傳輸,也就是16bit
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;     //外設地址不遞增
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;         //存儲器地址遞增
DMA_InitStructure.DMA_Mode  = DMA_Mode_Normal;        //正常模式
DMA_InitStructure.DMA_Priority = DMA_Priority_High;        //設置優先級 高
DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable;    //不使用FIFO模式
DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_HalfFull;    //設置FIFO閾值,不使用FIFO模式,此參數可不用管
DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;    //設置存儲器單次傳輸,DMA發一次請求,傳一個半字
DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;    //設置外設單次傳輸,DMA發一次請求,傳一個半字
DMA_Init(DMA1_Stream4, &DMA_InitStructure);  //進行初始化
DMA_Cmd(DMA1_Stream4, ENABLE);        //使能DMA1的第四個數據流

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM