娇小w搡bbbb搡bbb,《第一次の人妻》,中国成熟妇女毛茸茸,边啃奶头边躁狠狠躁视频免费观看

歷史上的今天

今天是:2024年11月03日(星期日)

2021年11月03日 | STM32直接存儲器訪問DMA

發(fā)布者:huanran 來源: eefocus關鍵字:STM32  DMA 手機看文章 掃描二維碼
隨時隨地手機看文章

第一次接觸DMA是在學校學習ARM9裸板程序的時候,想起來都時隔快2年了?,F(xiàn)在來看看STM32平臺的DMA,一樣,在標準外設庫的支持下,STM32的DMA編程十分簡單,但是既是學習,那還是花點時間看看DMA的相關概念及原理的了解下。


1. DMA簡介

DMA是Direct Memory Access的簡稱,是直接存儲器訪問的意思。DMA是STM32單片機的外設之一,主要功能是用來搬移數(shù)據(jù)的。通過DMA搬移數(shù)據(jù)不需要CPU直接參與控制,也不需要中斷處理方式那樣保留現(xiàn)場和恢復現(xiàn)場。在傳輸數(shù)據(jù)的時候,CPU可以干其他事情。


無使用DMA的數(shù)據(jù)傳輸:

這里寫圖片描述

使用DMA后的數(shù)據(jù)傳輸:

這里寫圖片描述

DMA數(shù)據(jù)傳輸支持從外設到存儲器、存儲器到外設、存儲器到存儲器(這里所講的存儲器可以是SRAM,也可以是FLASH)。DMA控制器包含了DMA1控制器和DMA2控制器,分別由7和5個通道作為數(shù)據(jù)傳輸。每個通道專門用來管理來自一個或者多個外設對存儲器訪問的請求,還有一個仲裁器用于協(xié)調(diào)各個外設對DMA傳輸請求的優(yōu)先權。注意,DMA2只存在于大容量或互聯(lián)型的STM32單片機中。


2. DMA功能框圖

這里寫圖片描述

2.1 STM32外設對DMA的請求及通道

請求及通道對應圖中的標號1和標號2:STM32外設想要通過DMA來傳輸數(shù)據(jù),需先給DMA控制器發(fā)送DMA請求,控制器在收到外設的DMA請求之后會給外設一個應答信號,外設應答且DMA控制器收到外設的應答后,DMA啟動傳輸,直至傳輸完畢。

為什么需要發(fā)出請求,應答和接收應答這幾個繁瑣的步驟?由圖中藍色框框可以看出,DMA傳輸和CPU是共用系統(tǒng)總線的,要啟動DMA傳輸?shù)那疤崾窍到y(tǒng)總線是空閑的,換句話說是CPU沒有占用系統(tǒng)總線,所以啟動DMA傳輸前需要以上幾個應答機制,其最底層是DMA控制器和CPU正為系統(tǒng)總線作出協(xié)調(diào)。DMA1有7個通道,DMA2有5個通道,不同的外設請求要通過對應的DMA通道發(fā)給DMA控制器。將不同的外設請求傳輸至對應的通道,這個是我們在軟件編程上設置的。


DMA1開放的通道及對應請求:

這里寫圖片描述

DMA2開放的通道及對應請求:

這里寫圖片描述

雖然每個通道可以接收多個外設的請求,但是同一時間內(nèi)只能接收一個。


2.2 仲裁器

仲裁器對應圖中的標號3:當DMA控制器的多個通道發(fā)生DMA請求時,就需要仲裁器管理響應處理的順序。仲裁器通過軟件和硬件來管理DMA請求:軟件指的是我們寫的代碼,在DMA_CCRx(x指通道號)寄存器中設置,有4個等級,非常高(DMA_Priority_VeryHigh)、高(DMA_Priority_High)、中(DMA_Priority_Medium)和低(DMA_Priority_Low)。硬件則是指若有兩個或以上的DMA通道請求設置的優(yōu)先級一樣,則它們的響應順序取決于通道編號,編號低者優(yōu)先級高,在有DMA2的STM32中,DMA1控制器擁的響應優(yōu)先級高于DMA2。


2.3 配置DMA控制器

配置DMA控制器,無非就是下圖這幾個寄存器:

這里寫圖片描述

前面說到,DMA數(shù)據(jù)傳輸機制并不需要CPU的參與,但是DMA控制器要正常工作,數(shù)據(jù)要正確傳輸,需有三個必要條件:源地址、目的地址和數(shù)據(jù)大小,對于數(shù)據(jù)分批傳輸?shù)那闆r,數(shù)據(jù)大小這個條件還包含每次傳輸?shù)拇笮〖皢挝弧?p>

(1)源地址和目的地址

DMA的傳輸數(shù)據(jù)的方向有三個:從外設到存儲器、從存儲器到外設、從存儲器到存儲器。DMA_CCR的BIT[4]DIR就是用于配置數(shù)據(jù)傳輸方向的:

這里寫圖片描述

取值為0表從外設到存儲器,取值為1表從存儲器到外設。外設地址在DMA_CPAR寄存器配置,存儲器地址在DMA_CMAR寄存器配置。

這里寫圖片描述

(2)傳輸數(shù)據(jù)的大小及單位

以串口向電腦發(fā)送數(shù)據(jù)為例(存儲器->外設方向),開發(fā)板軟件可以一次性給電腦發(fā)送大量數(shù)據(jù),具體多少在DMA_CNDTR配置:

這里寫圖片描述

DMA_CNDTR低16位有效,一次最多只能傳輸65535個數(shù)據(jù)。

數(shù)據(jù)要正確傳輸,源、目標存儲的數(shù)據(jù)寬度必須一致。串口數(shù)據(jù)寄存器是8位的,也就是外設數(shù)據(jù)寬度設置寄存器DMA_CCRx的BIT[9:8]PSIZE取值為0:

這里寫圖片描述

存儲器的數(shù)據(jù)寬度設置寄存器DMA_CCRx的BIT[11:10]MSIZE取值也為0:

這里寫圖片描述

DMA傳輸數(shù)據(jù),還需要設置源地址上的數(shù)據(jù)發(fā)送指針和目的地址數(shù)據(jù)存放指針的增量模式。開發(fā)板串口向電腦發(fā)送數(shù)據(jù),假設要發(fā)送的數(shù)據(jù)很多,那么存儲器(源地址)上數(shù)據(jù)發(fā)送指針每次發(fā)送完畢需要加1,而串口數(shù)據(jù)寄存器則不需要,因為該寄存器只有一個,數(shù)據(jù)寄存器上的數(shù)據(jù)傳送到電腦后被清空了(就算不清空,數(shù)據(jù)直接覆蓋也沒關系)。外設的地址指針增量模式由DMA_CCRx的PINC配置,存儲器的地址指針則由MINC配置。

這里寫圖片描述

(3)傳輸結束

DMA中斷狀態(tài)寄存器DMA_ISR可以設置每個DMA通道傳輸過半、傳輸完成和傳輸錯誤示產(chǎn)生對應標志,

這里寫圖片描述
這里寫圖片描述

在DMA_CCRx位1、2、3可以設置發(fā)生傳輸過半、傳輸完成和傳輸錯誤時產(chǎn)生中斷:

這里寫圖片描述

另外補充一點,位0用于使能DMA傳輸

這里寫圖片描述

傳輸完成分兩種模式:一次傳輸和循環(huán)傳輸,一次傳輸指傳輸一次后就停止,要再傳輸需要關閉DMA使能后重新配置后才能繼續(xù)傳輸。循環(huán)傳輸則是一次傳輸完成后又恢復第一次傳輸時的配置循環(huán)傳輸,如此循環(huán)。設置位在DMA_CCRx寄存器的CIRC。


3. DMA功能模塊描述結構體

標準庫的一貫風格,在stm32f10x_dma.h文件中定于可DMA_InitTypeDef初始化結構體,DMA_Init()函數(shù)定義在stm32f10x_dma.c中。


typedef struct

{

  uint32_t DMA_PeripheralBaseAddr;  //外設地址

  uint32_t DMA_MemoryBaseAddr;      //存儲器地址

  uint32_t DMA_DIR;                 //傳輸方向

  uint32_t DMA_BufferSize;          //傳輸?shù)臄?shù)據(jù)的數(shù)目大小

  uint32_t DMA_PeripheralInc;       //外設地址的增量模式

  uint32_t DMA_MemoryInc;           //存儲器地址的增量模式

  uint32_t DMA_PeripheralDataSize;  //外設數(shù)據(jù)寬度

  uint32_t DMA_MemoryDataSize;      //存儲器數(shù)據(jù)寬度

  uint32_t DMA_Mode;                //模式選擇

  uint32_t DMA_Priority;            //通道優(yōu)先級

  uint32_t DMA_M2M;                 //存儲器到存儲器模式

}DMA_InitTypeDef;


(1)DMA_PeripheralBaseAddr:外設地址,若是存儲器到存儲器模式,此成員設置為其中一個存儲器的地址,否則設置為外設的地址。

(2)DMA_MemoryBaseAddr:存儲器地址,一般設置為程序中存放數(shù)據(jù)的容器(數(shù)組)的首地址。

(3)DMA_DIR:傳輸方向,可設置為外設到存儲器,存儲器到外設。注意這里沒有存儲器到存儲器的選項,當使用存儲器到存儲器時,只需要把其中一個存儲器當做外設使用。

(4)DMA_BufferSize:設定待傳輸數(shù)據(jù)數(shù)目。(5)DMA_PeripheralInc:外設地址增量模式,若取值為DMA_PeripheralInc_Enable表使能外設地址自動遞增功能。一般外設都是只有一個數(shù)據(jù)寄存器,所以不會使能該位。

(6)DMA_MemoryInc:若配置為DMA_MemoryInc_Enable表使能存儲器地址自動遞增功能。一般存儲器都是我們自定義的,區(qū)域內(nèi)存放多個數(shù)據(jù),所以一般使能該位。

(7)DMA_MemoryDataSize:外設數(shù)據(jù)寬度,可選8位(字節(jié))、16位(半字)、32位(字)

(8)DMA_MemoryDataSize:存儲器數(shù)據(jù)寬度,可選8位(字節(jié))、16位(半字)、32位(字)

(9)DMA_Mode:傳輸模式選擇,一次傳輸或循環(huán)傳輸

(10)DMA_Priority:通道優(yōu)先級設置,非常高、高、中、低可選

(11)DMA_M2M:存儲器到存儲器模式


4. 編程常用函數(shù)

4.1 DMA時鐘使能

函數(shù)原型:RCC_AHBPeriphClockCmd(uint32_t RCC_AHBPeriph, FunctionalState NewState)

使用示例:RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);


4.2 初始化DMA功能模塊描述結構體

函數(shù)原型:void DMA_Init(DMA_Channel_TypeDef* DMAy_Channelx, DMA_InitTypeDef* DMA_InitStruct)

DMAy_Channelx指定哪一個DMA通道,DMA_InitStruct就是前面解析的描述結構體

使用示例:DMA_Init(DMAy_Channel1, &DMA_InitStruct);


4.3 使能外設DMA發(fā)送

以啟動DMA發(fā)送功能為例:


函數(shù)原型:void USART_DMACmd(USART_TypeDef* USARTx, uint16_t USART_DMAReq, FunctionalState 

NewState)

使用示例:

USART_DMACmd(USART1, USART_DMAReq_Tx, ENABLE);


外設的DMA傳輸需要相應的設置,而存儲器是不需要的。存儲器到存儲器,在DMA_InitTypeDef結構體中有DMA_M2M成員需要開啟。


4.4 使能DMA通道,開啟DMA傳輸

函數(shù)原型:void DMA_Cmd(DMA_Channel_TypeDef* DMAy_Channelx, FunctionalState NewState);

使用示例:DMA_Cmd(DMAy_Channel1, ENABLE);

使能之后,DMA控制器開始工作,在合適的時機(CPU無占據(jù)總線)開始DMA控制下的數(shù)據(jù)傳輸。


4.5 查詢DMA傳輸狀態(tài)

在DMA傳輸過程中,我們可以通過函數(shù)來查詢傳輸通道的狀態(tài):


函數(shù)原型:FlagStatus DMA_GetFlagStatus(uint32_t DMAy_FLAG)

假設要查詢DMA通道4傳輸是否完成:

DMA_GetFlagStatus(DMA1_FLAG_TC4);

返回值為RESET表示傳輸尚未完成,SET表傳輸完成。


獲取當前剩余數(shù)據(jù)量大小的函數(shù):

函數(shù)原型:uint16_t DMA_GetCurrDataCounter(DMA_Channel_TypeDef* DMAy_Channelx);

使用示例,獲取DMA通道4還有多少數(shù)據(jù)沒有傳輸:

DMA_GetCurrDataCounter(DMAy_Channel4);


5. 編程實踐

5.1 DMA傳輸–存儲器到存儲器模式

硬件平臺是正點原子MiniSTM32,板載有兩個LED分別為紅色和綠色。

程序功能實現(xiàn)把STM32內(nèi)置的FLASH數(shù)據(jù)拷貝到內(nèi)置的SRAM中:定義一個const靜態(tài)變量為源數(shù)據(jù),使用DMA傳輸將源數(shù)據(jù)拷貝到目標地址中,比對源數(shù)據(jù)和目標數(shù)據(jù)是否相同,若相同亮綠色LED燈,反之亮紅色LED燈。

DMA的編程核心在于

(1)使能DMA時鐘

(2)配置DMA初始化結構體參數(shù)

(3)使能DMA,開始進行數(shù)據(jù)傳輸

(4)等待數(shù)據(jù)傳輸完成


工程結構為:

這里寫圖片描述

BSP_LED.c實現(xiàn)配置LED引腳:


#include


void LED_Configuration(void)

{

    GPIO_InitTypeDef GPIO_InitTypeStu;


    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOD, ENABLE);


    //3?ê??ˉPA8í?íìê?3?

    GPIO_InitTypeStu.GPIO_Mode = GPIO_Mode_Out_PP;

    GPIO_InitTypeStu.GPIO_Pin = GPIO_Pin_8;

    GPIO_InitTypeStu.GPIO_Speed = GPIO_Speed_50MHz;

    GPIO_Init(GPIOA, &GPIO_InitTypeStu);        

    GREEN_LED_OFF;


    GPIO_InitTypeStu.GPIO_Pin = GPIO_Pin_2;

    GPIO_Init(GPIOD, &GPIO_InitTypeStu);

    RED_LED_OFF;

}


BSP_USART.c實現(xiàn)配置USART1功能:



#include "BSP_USART.h"


#pragma import(__use_no_semihosting)                             

struct __FILE 

    int handle; 


}; 


FILE __stdout;          

_sys_exit(int x) 

    x = x; 


int fputc(int ch, FILE *f)

{      

    while((USART1->SR&0X40) == RESET);

    USART1->DR = (u8) ch;      

    return ch;

}


void NVIC_Configuration(void)

{

    NVIC_InitTypeDef NVIC_InitStu;

    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);


    NVIC_InitStu.NVIC_IRQChannel = USART1_IRQn;

    NVIC_InitStu.NVIC_IRQChannelPreemptionPriority = 1;

    NVIC_InitStu.NVIC_IRQChannelSubPriority = 1;

    NVIC_InitStu.NVIC_IRQChannelCmd = ENABLE;


    NVIC_Init(&NVIC_InitStu);

}


void USART_Configuration(void)

{

    GPIO_InitTypeDef GPIO_InitStu;

    USART_InitTypeDef USART_InitStu;


    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);

    RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);


    GPIO_InitStu.GPIO_Mode = GPIO_Mode_AF_PP;

    GPIO_InitStu.GPIO_Pin = GPIO_Pin_9;

    GPIO_InitStu.GPIO_Speed = GPIO_Speed_50MHz;

    GPIO_Init(GPIOA, &GPIO_InitStu);


    GPIO_InitStu.GPIO_Mode = GPIO_Mode_IN_FLOATING;

    GPIO_InitStu.GPIO_Pin = GPIO_Pin_10;

    GPIO_Init(GPIOA, &GPIO_InitStu);


    USART_InitStu.USART_BaudRate = 115200;

    USART_InitStu.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;

    USART_InitStu.USART_Parity = USART_Parity_No;

    USART_InitStu.USART_StopBits = USART_StopBits_1;

    USART_InitStu.USART_WordLength = USART_WordLength_8b;

    USART_InitStu.USART_HardwareFlowControl = USART_HardwareFlowControl_None;

    USART_Init(USART1, &USART_InitStu);


    NVIC_Configuration();


    USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);



    USART_Cmd(USART1, ENABLE);

}


void USART_SendChar(USART_TypeDef* pUSARTx, uint8_t c)

{

    USART_SendData(pUSARTx, c);


    while (USART_GetFlagStatus(pUSARTx, USART_FLAG_TXE) == RESET);

}


void USART_SendString(USART_TypeDef* pUSARTx, char* str)

{

    uint32_t n = 0;


    while (*(str + n) != '

主站蜘蛛池模板: 明溪县| 扬中市| 许昌县| 满城县| 历史| 常山县| 苏尼特左旗| 鄂伦春自治旗| 莎车县| 海淀区| 甘南县| 东莞市| 江孜县| 怀化市| 岳西县| 余庆县| 淳化县| 临西县| 香格里拉县| 托克逊县| 彰化县| 濮阳市| 大名县| 中山市| 安新县| 嘉兴市| 永州市| 华阴市| 大邑县| 乌兰浩特市| 东乌珠穆沁旗| 墨玉县| 梁河县| 乌海市| 寿阳县| 石棉县| 平潭县| 晋江市| 乌拉特后旗| 黄骅市| 江油市|