找回密碼
 立即注冊(cè)

QQ登錄

只需一步,快速開始

搜索
查看: 2225|回復(fù): 0
打印 上一主題 下一主題
收起左側(cè)

模仿正點(diǎn)原子到STM32寄存器編寫-DMA

[復(fù)制鏈接]
跳轉(zhuǎn)到指定樓層
樓主
ID:413056 發(fā)表于 2021-4-29 02:28 | 只看該作者 回帖獎(jiǎng)勵(lì) |倒序?yàn)g覽 |閱讀模式
本次實(shí)驗(yàn)我們是模仿正點(diǎn)原子的寄存器Demo來(lái)深入學(xué)習(xí)DMA的工作原理并來(lái)純手工敲寫DMA實(shí)驗(yàn)。DMA是可以存儲(chǔ)器與存儲(chǔ)器,外設(shè)與存儲(chǔ)器之間進(jìn)行數(shù)據(jù)傳輸?shù)摹_@里我們
以串口1作為外設(shè),BUFF[xx]字符數(shù)組作為存儲(chǔ)器的外設(shè)與存儲(chǔ)器之間的數(shù)據(jù)傳輸實(shí)驗(yàn)。

那怎么看中文參考手冊(cè)呢?
第一步:看手冊(cè)的核心要點(diǎn)。
1、
簡(jiǎn)介是表示用最簡(jiǎn)單的語(yǔ)言來(lái)描述那種模塊的大致內(nèi)容,所以這個(gè)我們必須看。

2、
模塊的特性我們也需要重點(diǎn)關(guān)注的,因?yàn)檫@里描述的東西在我們配置時(shí)極大可能需要了解的又可能疏忽的點(diǎn)。

3、
              模塊處理過(guò)程是很重要的,我們根據(jù)理解它的處理過(guò)程來(lái)配置程序的。

4、
看到這個(gè)配置過(guò)程字眼,我們要知道,這是重點(diǎn)中的重點(diǎn),因?yàn)槲覀兊氖歉鶕?jù)這個(gè)爹來(lái)配置程序的。

第二步:理解了上面的描述后,我們就要理解一下這個(gè)DMA的工作框圖。
紅色線:這表示我們?cè)谄匠4谕ㄐ诺臅r(shí)候我們是將usart_dr寄存器的值放入定義好的buff[xx]緩沖區(qū)里作為接收操作,而將字符串逐個(gè)的值賦予usart_dr寄存器作為輸出操作。
從字面理解來(lái)說(shuō),這樣我們就要將MCU騰出的時(shí)間出進(jìn)行串口數(shù)據(jù)接收和數(shù)據(jù)發(fā)送了,這樣就消耗了MCU的一部分資源了。
藍(lán)色線:第一條線表示MCU與DMA的連線,是用來(lái)配置DMA用的。第二條線表示外設(shè)或存儲(chǔ)器與DMA的數(shù)據(jù)交互的,可以理解成它就是一個(gè)小型的MCU,能直接處理存儲(chǔ)器與寄存器之間的數(shù)據(jù)交互,不需要STM32這個(gè)MCU的參與,這大大減小了MCU資源占用。

第三步:配置的大致我們都知道后,我們要知道一個(gè)要點(diǎn),但凡我們使用一個(gè)模塊的東西去操作另一個(gè)模塊的時(shí)候,這兩個(gè)模塊之間是沒(méi)有線路直接相連的,而且還有這么多個(gè)通道那種,那么我們就要查看映射表了。
藍(lán)色框就是我們要使用的外設(shè)了,它在DMA1模塊里的通道4。

第四步:開始寫寄存器操作的程序。(我們看著剛剛那個(gè)配置過(guò)程來(lái)寫程序)
                          


配置DMA的基地址,上門圖可知,DMA這個(gè)模塊里面每個(gè)通道都有自己對(duì)應(yīng)的寄存器,那么我們配置寄存器也要相應(yīng)的分開配置那個(gè)通道的,所以我們基地址需要分開來(lái)。
  • 基地址
上面程序里為什么還要分DMA_BASE呢?這需要我們?nèi)タ词謨?cè)一個(gè)地方,如下圖所示:
DMA_ISR和DMA_IFCR這兩個(gè)寄存器和下面的是有很明顯的區(qū)別的,下面的沒(méi)4個(gè)對(duì)應(yīng)一個(gè)通道,里面除了起始地址不一樣外,大小都一樣,所以配置時(shí)可以共用一個(gè)結(jié)構(gòu)體,而那個(gè)兩個(gè)就只能用一個(gè)不同的結(jié)構(gòu)體獨(dú)立出來(lái)了,也就是為什么用DMA_BASE的原因了。
寄存器的基地址在哪里找呢?如下圖可以查找到模塊的起始地址。
而每個(gè)通道的基地址就在DMA寄存器印象和復(fù)位這個(gè)表中查找,模塊起始地址加上表左邊的偏移量就是基地址了。
  • 結(jié)構(gòu)體
這個(gè)結(jié)構(gòu)體就按照DMA寄存器印象和復(fù)位表來(lái)寫的。這里就步重復(fù)解釋了。
  • 指針 (每個(gè)通道結(jié)構(gòu)體和那個(gè)特別一點(diǎn)的只有兩個(gè)寄存器的結(jié)構(gòu)體)
有以上的環(huán)境基礎(chǔ)下,我們就可以開始配置DMA了。
  • 配置
我們需要寄存器那些位呢?配置哪個(gè)寄存器就進(jìn)入哪個(gè)寄存器查詢相關(guān)位的功能,列入下圖2
   
                                                        圖1                            圖2
好了下面開始配置
這個(gè)配置程序基本是根據(jù)手冊(cè)中描述通道配置來(lái)編寫的
(1打開時(shí)鐘 2設(shè)置外設(shè) 3設(shè)置存儲(chǔ)器 4傳輸?shù)臄?shù)量 5方向、是否循環(huán)模式、外設(shè)是否增量、存儲(chǔ)器是否增量、外設(shè)數(shù)據(jù)寬、存儲(chǔ)器數(shù)據(jù)寬、優(yōu)先級(jí)、什么模式啟動(dòng))
這里我就說(shuō)一些重點(diǎn),簡(jiǎn)單的自己看手冊(cè),只有自己學(xué)會(huì)看手冊(cè)才學(xué)到東西。
sendlen=BUFFLEN; 這是用來(lái)保存你要傳輸?shù)臄?shù)據(jù)長(zhǎng)度,用來(lái)觸發(fā)你每次傳輸都是傳輸這個(gè)長(zhǎng)度的數(shù)據(jù)。
循環(huán)模式:中文參考手冊(cè)有詳細(xì)解釋。
為什么要把外設(shè)和存儲(chǔ)器的位寬都設(shè)置8位:首先我們要理解一個(gè)過(guò)程DMA傳輸是將我們存儲(chǔ)器的值全部發(fā)送出去為止,那么8位為一個(gè)字節(jié),在這個(gè)傳輸?shù)倪^(guò)程就是每次取存儲(chǔ)器1個(gè)字節(jié)數(shù)據(jù)傳輸?shù)酵庠O(shè),而外設(shè)也是一次取1個(gè)字節(jié)數(shù)據(jù),那么實(shí)現(xiàn)數(shù)據(jù)一一對(duì)應(yīng)了。
  • 編寫每次觸發(fā)DMA傳輸過(guò)程
這個(gè)沒(méi)什么好說(shuō)的,就是關(guān)閉上一次已經(jīng)傳輸完成的;重置它的傳輸量,因?yàn)槊看蝹鬏敂?shù)據(jù)它都會(huì)以傳輸一個(gè)數(shù)據(jù)遞減1的形式遞減至0,并且它不會(huì)自己重載回去(循環(huán)模式就可以遞減至0又重載);開啟新的DMA傳輸。
  • 編寫主程序
配置完后就將我們準(zhǔn)備好的存儲(chǔ)器寫滿內(nèi)容,然后進(jìn)行DMA傳輸,在串口中觀看。
1、自己定義一個(gè)存儲(chǔ)器,記得要大點(diǎn),假設(shè)我們配置的是波特率為115200,那么就說(shuō)明數(shù)據(jù)傳輸速率115200bit/s = 14400字節(jié)/s,而我們用34000個(gè)字節(jié)的數(shù)據(jù)進(jìn)行傳輸?shù)脑,一個(gè)DMA傳輸過(guò)程大概花費(fèi)的時(shí)間是34000/14400=2.36s左右。
2、我們使用串口作為外設(shè)
3、串口1在DMA通道1,外設(shè)為串口USART_DR,存儲(chǔ)器SendBuff,存儲(chǔ)器的數(shù)據(jù)長(zhǎng)度。
4、弄個(gè)什么循環(huán)的把存儲(chǔ)器SendBuff的數(shù)據(jù)灌滿。例如下圖
5、
開啟數(shù)據(jù)傳輸,上面紅框框里的是我們將本來(lái)使用復(fù)用的PA9口作為發(fā)送端換成了DMA->USART1_TX上(這個(gè)東西就類似與GPIO口復(fù)用成串口等這樣)
上面虛線這行代碼是用不了的,若想顯示它傳輸?shù)陌俜直,就需要用OLED屏或者使用串口2等方式顯示(原因是因?yàn)镈MA直接控制USART1_DR寄存器的,不需要MCU參與,導(dǎo)致USART1_DR這個(gè)一直被占用,就打印出傳輸過(guò)程的百分比量了)

完整程序:
  1. #include "stdio.h"
  2. #include "gpio.h"
  3. #include "USART1.h"
  4. #include "delay_ms.h"
  5. #include "clock.h"
  6. #include "USART1.h"
  7. #include "key.h"
  8. #include "DMA.h"

  9. const u8 Text_Buff[]="鄧家文使用STM32F103 DMA串口實(shí)驗(yàn)";
  10. u16 str_len=sizeof(Text_Buff);                                                                                     //str_len將要發(fā)送字符的長(zhǎng)度
  11. u8 SendBuff[34000];                                            //發(fā)送數(shù)據(jù)緩沖區(qū)  DMA( 寄存器->存儲(chǔ)器SendBuff )
  12. u16 KeyKind=0;                                                                                                                                            //key_kind按鍵類

  13. float pro=0;                                                                                    //進(jìn)度條
  14. u16 i,mask,tt;                               //循環(huán)體使用的臨時(shí)變量


  15. int main(void)
  16. {
  17.               RCC_Config(9);                                                        //72MHz
  18.               delay_init1(72);                                          //打開延時(shí)
  19.               GPIO_Init();                                                                      //初始化LED口
  20.               USART_INIT(72,115200);  //初始化串口
  21.               key_init();                                                                                    //初始化按鍵
  22.               DMA_INIT(DMA_Channel4,(u32)&USART1->USART_DR,(u32)SendBuff,34000);                                                        //寄存器,存儲(chǔ)器,存儲(chǔ)器大小
  23.             
  24.               for(i=0;i<34000;i++)//填充數(shù)據(jù)到SendBuff
  25.   {
  26.                             if(tt>=str_len)//加入換行符
  27.                             {
  28.                                           if(mask)
  29.                                           {
  30.                                                         SendBuff[i]=0x0a;
  31.                                                         tt=0;
  32.                                           }else
  33.                                           {
  34.                                                         SendBuff[i]=0x0d;
  35.                                                         mask++;
  36.                                           }            
  37.                             }else//復(fù)制TEXT_TO_SEND語(yǔ)句
  38.                             {
  39.                                           mask=0;
  40.                                           SendBuff[i]=Text_Buff[tt];
  41.                                           tt++;
  42.                             }                    
  43.   }                           
  44.               while(1)
  45.               {
  46.                             KeyKind=key_scan(0);
  47.                             if(KeyKind==2)
  48.                             {
  49.                                           GPIOB->GPIO_ODR^=1<<5;
  50.                                           USART1->USART_CR3=1<<7;                                                                                                                //使能串口1的DMA發(fā)送
  51.                                           DMA_OneSend(DMA_Channel4);                                                                                    //開啟一次發(fā)送
  52.                            
  53.                                           while(1)
  54.                                           {
  55.                                                         if(DMA->DMA_ISR&(1<<13))                            //等待通道1傳輸完成
  56.                                                         {
  57.                                                                       DMA->DMA_IFCR|=1<<13;                                          //清除通道1傳輸完成標(biāo)志
  58.                                                                       break;
  59.                                                         }                                                      
  60.                                                         pro=DMA_Channel4->DMA_CNDTR;//獲取當(dāng)前剩余數(shù)據(jù)量
  61.                                                         pro=1-pro/34000;                                                                                       //獲取百分比
  62.                                                         pro*=100;                                                                                                                                            //得到整數(shù)的百分比
  63.                                                         printf("residue:%f\r\n",pro);
  64.                                           }            
  65.                             }
  66.               }

  67. }
復(fù)制代碼

二、DMA.h
  1. #include "stdio.h"
  2. #include "gpio.h"
  3. #include "USART1.h"
  4. #include "delay_ms.h"
  5. #include "clock.h"
  6. #include "USART1.h"
  7. #include "key.h"
  8. #include "DMA.h"

  9. const u8 Text_Buff[]="鄧家文使用STM32F103 DMA串口實(shí)驗(yàn)";
  10. u16 str_len=sizeof(Text_Buff);                                                                                     //str_len將要發(fā)送字符的長(zhǎng)度
  11. u8 SendBuff[34000];                                            //發(fā)送數(shù)據(jù)緩沖區(qū)  DMA( 寄存器->存儲(chǔ)器SendBuff )
  12. u16 KeyKind=0;                                                                                                                                            //key_kind按鍵類

  13. float pro=0;                                                                                    //進(jìn)度條
  14. u16 i,mask,tt;                               //循環(huán)體使用的臨時(shí)變量


  15. int main(void)
  16. {
  17.               RCC_Config(9);                                                        //72MHz
  18.               delay_init1(72);                                          //打開延時(shí)
  19.               GPIO_Init();                                                                      //初始化LED口
  20.               USART_INIT(72,115200);  //初始化串口
  21.               key_init();                                                                                    //初始化按鍵
  22.               DMA_INIT(DMA_Channel4,(u32)&USART1->USART_DR,(u32)SendBuff,34000);                                                        //寄存器,存儲(chǔ)器,存儲(chǔ)器大小
  23.             
  24.               for(i=0;i<34000;i++)//填充數(shù)據(jù)到SendBuff
  25.   {
  26.                             if(tt>=str_len)//加入換行符
  27.                             {
  28.                                           if(mask)
  29.                                           {
  30.                                                         SendBuff[i]=0x0a;
  31.                                                         tt=0;
  32.                                           }else
  33.                                           {
  34.                                                         SendBuff[i]=0x0d;
  35.                                                         mask++;
  36.                                           }            
  37.                             }else//復(fù)制TEXT_TO_SEND語(yǔ)句
  38.                             {
  39.                                           mask=0;
  40.                                           SendBuff[i]=Text_Buff[tt];
  41.                                           tt++;
  42.                             }                    
  43.   }                           
  44.               while(1)
  45.               {
  46.                             KeyKind=key_scan(0);
  47.                             if(KeyKind==2)
  48.                             {
  49.                                           GPIOB->GPIO_ODR^=1<<5;
  50.                                           USART1->USART_CR3=1<<7;                                                                                                                //使能串口1的DMA發(fā)送
  51.                                           DMA_OneSend(DMA_Channel4);                                                                                    //開啟一次發(fā)送
  52.                            
  53.                                           while(1)
  54.                                           {
  55.                                                         if(DMA->DMA_ISR&(1<<13))                            //等待通道1傳輸完成
  56.                                                         {
  57.                                                                       DMA->DMA_IFCR|=1<<13;                                          //清除通道1傳輸完成標(biāo)志
  58.                                                                       break;
  59.                                                         }                                                      
  60.                                                         pro=DMA_Channel4->DMA_CNDTR;//獲取當(dāng)前剩余數(shù)據(jù)量
  61.                                                         pro=1-pro/34000;                                                                                       //獲取百分比
  62.                                                         pro*=100;                                                                                                                                            //得到整數(shù)的百分比
  63.                                                         printf("residue:%f\r\n",pro);
  64.                                           }            
  65.                             }
  66.               }

  67. }
復(fù)制代碼

三、DMA.c
  1. #include "DMA.h"
  2. #include "delay_ms.h"

  3. u16 sendlen;                                                                      //保存DMA每次數(shù)據(jù)傳送的長(zhǎng)度,也就是最開始的傳輸量,100%

  4. //CPAR外設(shè)地址
  5. //CMAR存儲(chǔ)器地址
  6. //BUFFLEN數(shù)據(jù)傳輸?shù)拈L(zhǎng)度或量
  7. void DMA_INIT(DMA_ChannelType*DMA_Chx,u32 CPAR,u32 CMAR,u16 BUFFLEN)
  8. {
  9.               RCC->RCC_AHBENR=1<<0;                                                                                      //開啟DMA1的時(shí)鐘
  10.               delay_ms1(5);                                                                                                                      //等待DMA時(shí)鐘穩(wěn)定
  11.               DMA_Chx->DMA_CPAR=CPAR;                                                                       //指向外設(shè)寄存器地址
  12.               DMA_Chx->DMA_CMAR=CMAR;                                                                                    //指向存儲(chǔ)器地址
  13.               sendlen=BUFFLEN;                                                                                                                              //保存DMA傳輸數(shù)據(jù)量
  14.               DMA_Chx->DMA_CNDTR=BUFFLEN;                                                        //傳輸數(shù)據(jù)量                             每次傳輸都會(huì)自動(dòng)遞減,直到0后會(huì)重載配置的長(zhǎng)度數(shù)值BUFFLEN
  15.               DMA_Chx->DMA_CCR=0x00000000;                                          //將配置寄存器的所有位清除(復(fù)位)
  16.               DMA_Chx->DMA_CCR|=1<<4;                                                                        //從存儲(chǔ)器讀出數(shù)據(jù)
  17.               DMA_Chx->DMA_CCR|=0<<5;                                                                                    //普通模式,不循環(huán)
  18.               DMA_Chx->DMA_CCR|=0<<6;                                                                                    //外設(shè)地址非增量模式, 這個(gè)是讀數(shù)據(jù),選擇讀出的位置大小是固定的,也就是不會(huì)隨地址改變,所以非增量
  19.               DMA_Chx->DMA_CCR|=1<<7;                                                                                    //存儲(chǔ)器地址增量模式   這個(gè)是寫數(shù)據(jù),需要將讀出來(lái)的數(shù)據(jù)依次的寫入對(duì)應(yīng)的地方,所以增量
  20.               DMA_Chx->DMA_CCR|=0<<8;                       //外設(shè)數(shù)據(jù)寬度為8位  也就是一次讀出1個(gè)字節(jié)
  21.               DMA_Chx->DMA_CCR|=0<<10;                                                                      //存儲(chǔ)器數(shù)據(jù)寬度8位  也就是一次寫入1個(gè)字節(jié)
  22.               DMA_Chx->DMA_CCR|=1<<12;                                                                      //中等優(yōu)先級(jí)
  23.               DMA_Chx->DMA_CCR|=0<<14;                                                                      //啟動(dòng)非存儲(chǔ)器到存儲(chǔ)器模式                             
  24. }

  25. //啟動(dòng)一次DMA傳輸,上面定義的是一次傳輸1個(gè)字節(jié)
  26. void DMA_OneSend(DMA_ChannelType*DMA_Chx)
  27. {
  28.               DMA_Chx->DMA_CCR&=~(1<<0);                                          //先關(guān)閉上一次的DMA傳輸,因續(xù)上一次傳輸1個(gè)字節(jié)后并未關(guān)閉,若步關(guān)閉回影響下一次的傳輸,而且不能實(shí)現(xiàn)起停傳輸效果
  29.               DMA_Chx->DMA_CNDTR=sendlen;                                          //傳輸數(shù)據(jù)量
  30.               DMA_Chx->DMA_CCR|=1<<0;                                                                      //開啟下一次的DMA傳輸
  31. }
復(fù)制代碼

以上的的Word格式文檔51黑下載地址:
帶你模仿正點(diǎn)原子到寄存器編寫--DMA.docx (1.52 MB, 下載次數(shù): 14)

評(píng)分

參與人數(shù) 1黑幣 +50 收起 理由
admin + 50 共享資料的黑幣獎(jiǎng)勵(lì)!

查看全部評(píng)分

分享到:  QQ好友和群QQ好友和群 QQ空間QQ空間 騰訊微博騰訊微博 騰訊朋友騰訊朋友
收藏收藏3 分享淘帖 頂 踩
回復(fù)

使用道具 舉報(bào)

本版積分規(guī)則

手機(jī)版|小黑屋|51黑電子論壇 |51黑電子論壇6群 QQ 管理員QQ:125739409;技術(shù)交流QQ群281945664

Powered by 單片機(jī)教程網(wǎng)

快速回復(fù) 返回頂部 返回列表