stm32 DMA
stm32 DMA
- 介紹的定義
 - 存儲器到存儲器
 - main.c
 
- 存儲器到外設(shè)
 - main.c
 - usart_dma.c
 - usart_dma.h
 
- 外設(shè)到存儲器
 - main.c
 - usart_dma.c
 - usart_dma.h
 - 中斷函數(shù)
 
- 對比理解
 
介紹的定義
存儲器:用來存儲程序代碼和數(shù)據(jù)。
易失性存儲器:存儲器斷電后,它存儲的數(shù)據(jù)內(nèi)容丟失。易失性存儲器存取速度快,如內(nèi)存。
非易失性存儲器:存儲器斷電后,它存儲的數(shù)據(jù)內(nèi)容不丟失。非易失性存儲器可長期保存數(shù)據(jù),如硬盤。
RAM(Random Access Memory):隨機(jī)存儲器。當(dāng)存儲器中的消息被讀取或?qū)懭霑r,所需要的時間與這段信息所在的位置無關(guān)。也就是說,RAM 讀取其內(nèi)部任意地址的數(shù)據(jù),時間都是相同的。根據(jù)RAM 的存儲機(jī)制,分為動態(tài)隨機(jī)存儲器 DRAM;靜態(tài)隨機(jī)存儲器 SRAM。
DRAM(Dynamic RAM):動態(tài)隨機(jī)存儲器。DRAM 的存儲單元以電容的電荷來表示數(shù)據(jù),有電荷代表 1,無電荷代表 0。由于代表 1 的電容會放電,代表 0 的電容會吸收電荷,因此需要定期刷新操作,刷新操作會對電容進(jìn)行檢查,若電量大于滿電量的 1/2,則認(rèn)為其代表 1,并把電容充滿電;若電量小于 1/2, 則認(rèn)為其代表 0,并把電容放電。 DRAM 的結(jié)構(gòu)簡單,所以生產(chǎn)相同容量的存儲器,DRAM 的成本更低,集成度更高。外部擴(kuò)展的內(nèi)存一般使用 DRAM。
SRAM(Static RAM):靜態(tài)隨機(jī)存儲器。 SRAM 的存儲單元以鎖存器來存儲數(shù)據(jù),這種電路結(jié)構(gòu)不需要定時刷新充電,就能保持狀態(tài)。SRAM 一般用于 CPU 內(nèi)部的高速緩存(Cache)。
SDRAM(Synchronous DRAM):同步通訊方式的DRAM,使用時鐘同步的通訊速度更快。SDRAM 只在上升沿表示有效數(shù)據(jù),在 1 個時鐘周期內(nèi),只能表示 1 個有數(shù)據(jù)。
DDR SDRAM(Double Data Rate SDRAM):在時鐘的上升沿及下降沿各表示一個數(shù)據(jù),也就是說在 1 個時鐘周期內(nèi)可以表示 2 位數(shù)據(jù),在時鐘頻率同樣的情況下, 提高了一倍的速度。
ROM(Read Only Memory):英文是只能讀的存儲器,后來人們設(shè)計(jì)出可以寫入數(shù)據(jù)的 ROM。用于指代非易失性半導(dǎo)體存儲器。
MASK ROM:存儲在它內(nèi)部的數(shù)據(jù)是在出廠時使用特殊工藝固化的,生產(chǎn)后就不可修改,用在生產(chǎn)量大,數(shù)據(jù)不需要修改的場合。
EEPROM(Electrically Erasable Programmable ROM):電可擦除存儲器。可重復(fù)擦寫,擦除和寫入都是直接使用電路控制,不需要使用外部設(shè)備。而且可以以字節(jié)為單位修改數(shù)據(jù),無需整個芯片擦除。現(xiàn)在主要使用的 ROM 芯片都是 EEPROM。
FLASH:閃存,有人稱為flash ROM,也是可重復(fù)擦寫的儲器,容量一般比 EEPROM 大得多;在擦除時,一般以多個字節(jié)為單位。有的 FLASH 存儲器以 4096 個字節(jié)為扇區(qū),最小的擦除單位為一個扇區(qū)。FLASH 存儲器又分為 NOR FLASH 和 NAND FLASH。NOR 與 NAND 的共性是在數(shù)據(jù)寫入前都需要有擦除操作。FLASH 的擦除次數(shù)都是有限的(10 萬次左右),當(dāng)使用接近壽命的時候,可能會出現(xiàn)寫操作失敗。
NOR FLASH: NOR 的地址線和數(shù)據(jù)線分開,可以按“字節(jié)”讀寫數(shù)據(jù);假如 NOR 上存儲了代碼指令,CPU 給 NOR 一個地址,NOR 就能向 CPU 返回一個數(shù)據(jù)讓 CPU 執(zhí)行,中間不需要額外的處理操作。功能上可以認(rèn)為 NOR 是一種斷電后數(shù)據(jù)不丟失的 RAM,但他的讀寫速度比 RAM 要慢得多。NOR FLASH 一般應(yīng)用在代碼存儲的場合,如嵌入式控制器內(nèi)部的程序存儲空間。
NAND FLASH:NAND 的數(shù)據(jù)和地址線共用,只能按“塊”來讀寫數(shù)據(jù);若代碼存儲在 NAND 上,可以把它先加載到 RAM 存儲器上,再由 CPU 執(zhí)行。NAND FLASH 一般應(yīng)用在大數(shù)據(jù)量存儲的場合,包括 SD 卡、U 盤以及固態(tài)硬盤等。
DMA:直接存儲器存取,是單片機(jī)的一個外設(shè),主要功能是搬數(shù)據(jù),但是不需要占用 CPU(傳輸數(shù)據(jù)的時候,CPU 可以干其他的事情)。數(shù)據(jù)傳輸支持從外設(shè)到存儲器、存儲器到外設(shè)、存儲器到存儲器,這里的存儲器可以是 SRAM 或者是 FLASH。DMA 控制器獨(dú)立于內(nèi)核,屬于一個單獨(dú)的外設(shè)。
存儲器到存儲器
main.c
先定義一個靜態(tài)的源數(shù)據(jù),存放在內(nèi)部 FLASH,然后使用 DMA 傳輸把源數(shù)據(jù)拷貝到目標(biāo)地址上(內(nèi)部 SRAM),最后對比源數(shù)據(jù)和目標(biāo)地址的數(shù)據(jù),看看是否傳輸準(zhǔn)確。RGB 彩色燈用于指示程序狀態(tài),如果 DMA 傳輸成功設(shè)置 RGB 彩色燈為藍(lán)色,如果 DMA 傳輸出錯設(shè)置 RGB 彩色燈為紅色。
void DMA_Config(void)函數(shù)是根據(jù)DMA_InitTypeDef這個結(jié)構(gòu)體來編寫的,整體上,關(guān)于DMA的函數(shù)和結(jié)構(gòu)體定義,都在stm32f10x_dma.c和stm32f10x_dma.h文件里面,利用庫函數(shù)編程其實(shí)說白了就是結(jié)合外設(shè)對應(yīng)的那兩個文件來編程。
main整體流程:使能 DMA 時鐘;配置 DMA 數(shù)據(jù)參數(shù);使能 DMA,進(jìn)行傳輸;等待傳輸完成,并對源數(shù)據(jù)和目標(biāo)地址數(shù)據(jù)進(jìn)行比較。
main里面DMA_GetFlagStatus函數(shù)是獲取DMA事件標(biāo)志位的當(dāng)前狀態(tài),參數(shù)是DMA_FLAG_TC意味著獲取DMA 數(shù)據(jù)傳輸完成這個標(biāo)志位,DMA傳輸完成后,退出循環(huán),運(yùn)行之后程序。
main里面Buffercmp函數(shù),aSRC_Const_Buffer是FLASH中存儲的數(shù)據(jù),aDST_Buffer是內(nèi)部的SRAM中存儲的數(shù)據(jù),BUFFER_SIZE是數(shù)據(jù)大小。
TransferStatus=Buffercmp(aSRC_Const_Buffer, aDST_Buffer, BUFFER_SIZE); #include "stm32f10x.h" #include "./led/bsp_led.h"// 當(dāng)使用存儲器到存儲器模式時候,通道可以隨便選,沒有硬性的規(guī)定 #define DMA_CHANNEL DMA1_Channel6 #define DMA_CLOCK RCC_AHBPeriph_DMA1// 傳輸完成標(biāo)志 #define DMA_FLAG_TC DMA1_FLAG_TC6// 要發(fā)送的數(shù)據(jù)大小 #define BUFFER_SIZE 32/* 定義aSRC_Const_Buffer數(shù)組作為DMA傳輸數(shù)據(jù)源* const關(guān)鍵字將aSRC_Const_Buffer數(shù)組變量定義為常量類型* 表示數(shù)據(jù)存儲在內(nèi)部的FLASH中*/ const uint32_t aSRC_Const_Buffer[BUFFER_SIZE]= {0x01020304,0x05060708,0x090A0B0C,0x0D0E0F10,0x11121314,0x15161718,0x191A1B1C,0x1D1E1F20,0x21222324,0x25262728,0x292A2B2C,0x2D2E2F30,0x31323334,0x35363738,0x393A3B3C,0x3D3E3F40,0x41424344,0x45464748,0x494A4B4C,0x4D4E4F50,0x51525354,0x55565758,0x595A5B5C,0x5D5E5F60,0x61626364,0x65666768,0x696A6B6C,0x6D6E6F70,0x71727374,0x75767778,0x797A7B7C,0x7D7E7F80}; /* 定義DMA傳輸目標(biāo)存儲器* 存儲在內(nèi)部的SRAM中 */ uint32_t aDST_Buffer[BUFFER_SIZE];#define SOFT_DELAY Delay(0x0FFFFF); void Delay(__IO u32 nCount); uint8_t Buffercmp(const uint32_t* pBuffer, uint32_t* pBuffer1, uint16_t BufferLength); void DMA_Config(void); /*** @brief 主函數(shù)* @param 無 * @retval 無*/ int main(void) {/* 定義存放比較結(jié)果變量 */uint8_t TransferStatus;/* LED 端口初始化 */LED_GPIO_Config();/* 設(shè)置RGB彩色燈為紫色 */LED_PURPLE; /* 簡單延時函數(shù) */Delay(0xFFFFFF); /* DMA傳輸配置 */DMA_Config(); /* 等待DMA傳輸完成 */while(DMA_GetFlagStatus(DMA_FLAG_TC)==RESET){} /* 比較源數(shù)據(jù)與傳輸后數(shù)據(jù) */TransferStatus=Buffercmp(aSRC_Const_Buffer, aDST_Buffer, BUFFER_SIZE);/* 判斷源數(shù)據(jù)與傳輸后數(shù)據(jù)比較結(jié)果*/if(TransferStatus==0) {/* 源數(shù)據(jù)與傳輸后數(shù)據(jù)不相等時RGB彩色燈顯示紅色 */LED_RED;}else{ /* 源數(shù)據(jù)與傳輸后數(shù)據(jù)相等時RGB彩色燈顯示藍(lán)色 */LED_BLUE;}while (1){ } }void Delay(__IO uint32_t nCount) //簡單的延時函數(shù) {for(; nCount != 0; nCount--); }void DMA_Config(void) {DMA_InitTypeDef DMA_InitStructure;// 開啟DMA時鐘RCC_AHBPeriphClockCmd(DMA_CLOCK, ENABLE);// 外設(shè)DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)aSRC_Const_Buffer;// 存儲器DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)aDST_Buffer;// 方向:外設(shè)到存儲器(這里的外設(shè)是內(nèi)部的FLASH) DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;// 傳輸大小 DMA_InitStructure.DMA_BufferSize = BUFFER_SIZE;// 外設(shè)(內(nèi)部的FLASH)地址遞增 DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Enable;// 內(nèi)存地址遞增DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;// 外設(shè)數(shù)據(jù)單位 DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Word;// 內(nèi)存數(shù)據(jù)單位DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Word; // DMA模式,一次或者循環(huán)模式DMA_InitStructure.DMA_Mode = DMA_Mode_Normal ;//DMA_InitStructure.DMA_Mode = DMA_Mode_Circular; // 優(yōu)先級:高 DMA_InitStructure.DMA_Priority = DMA_Priority_High;// 使能內(nèi)存到內(nèi)存的傳輸DMA_InitStructure.DMA_M2M = DMA_M2M_Enable;// 配置DMA通道 DMA_Init(DMA_CHANNEL, &DMA_InitStructure);//清除DMA數(shù)據(jù)流傳輸完成標(biāo)志位DMA_ClearFlag(DMA_FLAG_TC);// 使能DMADMA_Cmd(DMA_CHANNEL,ENABLE); }/*** 判斷指定長度的兩個數(shù)據(jù)源是否完全相等,* 如果完全相等返回1,只要其中一對數(shù)據(jù)不相等返回0*/ uint8_t Buffercmp(const uint32_t* pBuffer, uint32_t* pBuffer1, uint16_t BufferLength) {/* 數(shù)據(jù)長度遞減 */while(BufferLength--){/* 判斷兩個數(shù)據(jù)源是否對應(yīng)相等 */if(*pBuffer != *pBuffer1){/* 對應(yīng)數(shù)據(jù)源不相等馬上退出函數(shù),并返回0 */return 0;}/* 遞增兩個數(shù)據(jù)源的地址指針 */pBuffer++;pBuffer1++;}/* 完成判斷并且對應(yīng)數(shù)據(jù)相對 */return 1; }//typedef struct //{ // uint32_t DMA_PeripheralBaseAddr; // 外設(shè)地址 // uint32_t DMA_MemoryBaseAddr; // 存儲器地址 // uint32_t DMA_DIR; // 傳輸方向 // uint32_t DMA_BufferSize; // 傳輸數(shù)目 // uint32_t DMA_PeripheralInc; // 外設(shè)地址增量模式 // uint32_t DMA_MemoryInc; // 存儲器地址增量模式 // uint32_t DMA_PeripheralDataSize; // 外設(shè)數(shù)據(jù)寬度 // uint32_t DMA_MemoryDataSize; // 存儲器數(shù)據(jù)寬度 // uint32_t DMA_Mode; // 模式選擇 // uint32_t DMA_Priority; // 通道優(yōu)先級 // uint32_t DMA_M2M; // 存儲器到存儲器模式 //}DMA_InitTypeDef;/*********************************************END OF FILE**********************/存儲器到外設(shè)
main.c
先定義一個數(shù)據(jù)變量,存于 SRAM 中,然后通過 DMA 的方式傳輸?shù)酱诘臄?shù)據(jù)寄存器,然后通過串口把這些數(shù)據(jù)發(fā)送到電腦的上位機(jī)顯示出來。結(jié)果就是串口一直收到P,然后小燈一直閃,這也就說明,DMA 傳輸過程不占用 CPU 資源,可以一邊傳輸一邊運(yùn)行其他任務(wù)。
// DMA 存儲器到外設(shè)(串口)數(shù)據(jù)傳輸實(shí)驗(yàn)#include "stm32f10x.h" #include "bsp_usart_dma.h" #include "bsp_led.h"extern uint8_t SendBuff[SENDBUFF_SIZE]; static void Delay(__IO u32 nCount); /*** @brief 主函數(shù)* @param 無* @retval 無*/ int main(void) {uint16_t i;/* 初始化USART */USART_Config(); /* 配置使用DMA模式 */USARTx_DMA_Config();/* 配置RGB彩色燈 */LED_GPIO_Config();//printf("\r\n USART1 DMA TX 測試 \r\n");/*填充將要發(fā)送的數(shù)據(jù)*/for(i=0;i<SENDBUFF_SIZE;i++){SendBuff[i] = 'P';}/*為演示DMA持續(xù)運(yùn)行而CPU還能處理其它事情,持續(xù)使用DMA發(fā)送數(shù)據(jù),量非常大,*長時間運(yùn)行可能會導(dǎo)致電腦端串口調(diào)試助手會卡死,鼠標(biāo)亂飛的情況,*或把DMA配置中的循環(huán)模式改為單次模式*/ /* USART1 向 DMA發(fā)出TX請求 */USART_DMACmd(DEBUG_USARTx, USART_DMAReq_Tx, ENABLE);/* 此時CPU是空閑的,可以干其他的事情 */ //例如同時控制LEDwhile(1){LED1_TOGGLEDelay(0xFFFFF);} }static void Delay(__IO uint32_t nCount) //簡單的延時函數(shù) {for(; nCount != 0; nCount--); } /*********************************************END OF FILE**********************/usart_dma.c
對于USARTx_DMA_Config函數(shù),數(shù)據(jù)是從存儲器到串口,所以設(shè)置存儲器為源地址,串口的數(shù)據(jù)寄存器為目標(biāo)地 址,要發(fā)送的數(shù)據(jù)有很多且都先存儲在存儲器中,則存儲器地址指針遞增,串口數(shù)據(jù)寄存器只有一個,則外設(shè)地址不變。
#include "bsp_usart_dma.h"uint8_t SendBuff[SENDBUFF_SIZE];/*** @brief USART GPIO 配置,工作參數(shù)配置* @param 無* @retval 無*/ void USART_Config(void) {GPIO_InitTypeDef GPIO_InitStructure;USART_InitTypeDef USART_InitStructure;// 打開串口GPIO的時鐘DEBUG_USART_GPIO_APBxClkCmd(DEBUG_USART_GPIO_CLK, ENABLE);// 打開串口外設(shè)的時鐘DEBUG_USART_APBxClkCmd(DEBUG_USART_CLK, ENABLE);// 將USART Tx的GPIO配置為推挽復(fù)用模式GPIO_InitStructure.GPIO_Pin = DEBUG_USART_TX_GPIO_PIN;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(DEBUG_USART_TX_GPIO_PORT, &GPIO_InitStructure);// 將USART Rx的GPIO配置為浮空輸入模式GPIO_InitStructure.GPIO_Pin = DEBUG_USART_RX_GPIO_PIN;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;GPIO_Init(DEBUG_USART_RX_GPIO_PORT, &GPIO_InitStructure);// 配置串口的工作參數(shù)// 配置波特率USART_InitStructure.USART_BaudRate = DEBUG_USART_BAUDRATE;// 配置 針數(shù)據(jù)字長USART_InitStructure.USART_WordLength = USART_WordLength_8b;// 配置停止位USART_InitStructure.USART_StopBits = USART_StopBits_1;// 配置校驗(yàn)位USART_InitStructure.USART_Parity = USART_Parity_No ;// 配置硬件流控制USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;// 配置工作模式,收發(fā)一起USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;// 完成串口的初始化配置USART_Init(DEBUG_USARTx, &USART_InitStructure); // 使能串口USART_Cmd(DEBUG_USARTx, ENABLE); }/***************** 發(fā)送一個字節(jié) **********************/ void Usart_SendByte( USART_TypeDef * pUSARTx, uint8_t ch) {/* 發(fā)送一個字節(jié)數(shù)據(jù)到USART */USART_SendData(pUSARTx,ch);/* 等待發(fā)送數(shù)據(jù)寄存器為空 */while (USART_GetFlagStatus(pUSARTx, USART_FLAG_TXE) == RESET); }/****************** 發(fā)送8位的數(shù)組 ************************/ void Usart_SendArray( USART_TypeDef * pUSARTx, uint8_t *array, uint16_t num) {uint8_t i;for(i=0; i<num; i++){/* 發(fā)送一個字節(jié)數(shù)據(jù)到USART */Usart_SendByte(pUSARTx,array[i]); }/* 等待發(fā)送完成 */while(USART_GetFlagStatus(pUSARTx,USART_FLAG_TC)==RESET); }/***************** 發(fā)送字符串 **********************/ void Usart_SendString( USART_TypeDef * pUSARTx, char *str) {unsigned int k=0;do {Usart_SendByte( pUSARTx, *(str + k) );k++;} while(*(str + k)!='\0');/* 等待發(fā)送完成 */while(USART_GetFlagStatus(pUSARTx,USART_FLAG_TC)==RESET){} }/***************** 發(fā)送一個16位數(shù) **********************/ void Usart_SendHalfWord( USART_TypeDef * pUSARTx, uint16_t ch) {uint8_t temp_h, temp_l;/* 取出高八位 */temp_h = (ch&0XFF00)>>8;/* 取出低八位 */temp_l = ch&0XFF;/* 發(fā)送高八位 */USART_SendData(pUSARTx,temp_h); while (USART_GetFlagStatus(pUSARTx, USART_FLAG_TXE) == RESET);/* 發(fā)送低八位 */USART_SendData(pUSARTx,temp_l); while (USART_GetFlagStatus(pUSARTx, USART_FLAG_TXE) == RESET); }///重定向c庫函數(shù)printf到串口,重定向后可使用printf函數(shù) int fputc(int ch, FILE *f) {/* 發(fā)送一個字節(jié)數(shù)據(jù)到串口 */USART_SendData(DEBUG_USARTx, (uint8_t) ch);/* 等待發(fā)送完畢 */while (USART_GetFlagStatus(DEBUG_USARTx, USART_FLAG_TXE) == RESET); return (ch); }///重定向c庫函數(shù)scanf到串口,重寫向后可使用scanf、getchar等函數(shù) int fgetc(FILE *f) {/* 等待串口輸入數(shù)據(jù) */while (USART_GetFlagStatus(DEBUG_USARTx, USART_FLAG_RXNE) == RESET);return (int)USART_ReceiveData(DEBUG_USARTx); }/*** @brief USARTx TX DMA 配置,內(nèi)存到外設(shè)(USART1->DR)* @param 無* @retval 無*/ void USARTx_DMA_Config(void) {DMA_InitTypeDef DMA_InitStructure;// 開啟DMA時鐘RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);// 外設(shè):串口數(shù)據(jù)寄存器地址*/DMA_InitStructure.DMA_PeripheralBaseAddr = USART_DR_ADDRESS;// 內(nèi)存地址(要傳輸?shù)淖兞康闹羔?DMA_InitStructure.DMA_MemoryBaseAddr = (u32)SendBuff;// 方向:從內(nèi)存到外設(shè) DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;// 傳輸大小 DMA_InitStructure.DMA_BufferSize = SENDBUFF_SIZE;// 外設(shè)地址不增 DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;// 內(nèi)存地址自增DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;// 外設(shè)數(shù)據(jù)單位 DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;// 內(nèi)存數(shù)據(jù)單位DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte; // DMA模式,一次或者循環(huán)模式DMA_InitStructure.DMA_Mode = DMA_Mode_Normal ;//DMA_InitStructure.DMA_Mode = DMA_Mode_Circular; // 優(yōu)先級:中 DMA_InitStructure.DMA_Priority = DMA_Priority_Medium; // 禁止內(nèi)存到內(nèi)存的傳輸DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;// 配置DMA通道 DMA_Init(USART_TX_DMA_CHANNEL, &DMA_InitStructure); // 使能DMADMA_Cmd (USART_TX_DMA_CHANNEL,ENABLE); }usart_dma.h
這里面是USART 和 DMA 的宏定義。
#ifndef __USARTDMA_H #define __USARTDMA_H#include "stm32f10x.h" #include <stdio.h>// 串口工作參數(shù)宏定義 #define DEBUG_USARTx USART1 #define DEBUG_USART_CLK RCC_APB2Periph_USART1 #define DEBUG_USART_APBxClkCmd RCC_APB2PeriphClockCmd #define DEBUG_USART_BAUDRATE 115200// USART GPIO 引腳宏定義 #define DEBUG_USART_GPIO_CLK (RCC_APB2Periph_GPIOA) #define DEBUG_USART_GPIO_APBxClkCmd RCC_APB2PeriphClockCmd#define DEBUG_USART_TX_GPIO_PORT GPIOA #define DEBUG_USART_TX_GPIO_PIN GPIO_Pin_9 #define DEBUG_USART_RX_GPIO_PORT GPIOA #define DEBUG_USART_RX_GPIO_PIN GPIO_Pin_10// 串口對應(yīng)的DMA請求通道 #define USART_TX_DMA_CHANNEL DMA1_Channel4 // 外設(shè)寄存器地址 #define USART_DR_ADDRESS (USART1_BASE+0x04) // 一次發(fā)送的數(shù)據(jù)量 #define SENDBUFF_SIZE 5000void USART_Config(void); void USARTx_DMA_Config(void);#endif /* __USARTDMA_H */外設(shè)到存儲器
main.c
實(shí)驗(yàn)結(jié)果就是,用電腦向開發(fā)板串口發(fā)送數(shù)據(jù),數(shù)據(jù)會返回到電腦。
// DMA 外設(shè)(串口)到存儲器數(shù)據(jù)傳輸實(shí)驗(yàn)#include "stm32f10x.h" #include "bsp_usart_dma.h" #include "bsp_led.h" static void Delay(__IO u32 nCount); /*** @brief 主函數(shù)* @param 無* @retval 無*/ int main(void) {/* 初始化USART */USART_Config(); /* 配置使用DMA模式 */USARTx_DMA_Config();/* 配置RGB彩色燈 */LED_GPIO_Config();printf("\r\nDMA外設(shè)到存儲器模式,用電腦向開發(fā)板串口發(fā)送數(shù)據(jù),數(shù)據(jù)會返回到電腦。\r\n");/* USART1 向 DMA發(fā)出RX請求 */USART_DMACmd(DEBUG_USARTx, USART_DMAReq_Rx, ENABLE);//用電腦向開發(fā)板串口發(fā)送數(shù)據(jù),數(shù)據(jù)會返回到電腦。while(1){LED1_TOGGLEDelay(0xFFFFF);} } static void Delay(__IO uint32_t nCount) //簡單的延時函數(shù) {for(; nCount != 0; nCount--); } /*********************************************END OF FILE**********************/usart_dma.c
#include "bsp_usart_dma.h"uint8_t ReceiveBuff[RECEIVEBUFF_SIZE];/*** @brief USART GPIO 配置,工作參數(shù)配置* @param 無* @retval 無*/ void USART_Config(void) {GPIO_InitTypeDef GPIO_InitStructure;USART_InitTypeDef USART_InitStructure;NVIC_InitTypeDef NVIC_InitStruct;// 打開串口GPIO的時鐘DEBUG_USART_GPIO_APBxClkCmd(DEBUG_USART_GPIO_CLK, ENABLE);// 打開串口外設(shè)的時鐘DEBUG_USART_APBxClkCmd(DEBUG_USART_CLK, ENABLE);NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);NVIC_InitStruct.NVIC_IRQChannel = DEBUG_USART_IRQ;NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 1;NVIC_InitStruct.NVIC_IRQChannelSubPriority = 3;NVIC_Init(&NVIC_InitStruct);// 將USART Tx的GPIO配置為推挽復(fù)用模式GPIO_InitStructure.GPIO_Pin = DEBUG_USART_TX_GPIO_PIN;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(DEBUG_USART_TX_GPIO_PORT, &GPIO_InitStructure);// 將USART Rx的GPIO配置為浮空輸入模式GPIO_InitStructure.GPIO_Pin = DEBUG_USART_RX_GPIO_PIN;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;GPIO_Init(DEBUG_USART_RX_GPIO_PORT, &GPIO_InitStructure);// 配置串口的工作參數(shù)// 配置波特率USART_InitStructure.USART_BaudRate = DEBUG_USART_BAUDRATE;// 配置 針數(shù)據(jù)字長USART_InitStructure.USART_WordLength = USART_WordLength_8b;// 配置停止位USART_InitStructure.USART_StopBits = USART_StopBits_1;// 配置校驗(yàn)位USART_InitStructure.USART_Parity = USART_Parity_No ;// 配置硬件流控制USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;// 配置工作模式,收發(fā)一起USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;// 完成串口的初始化配置USART_Init(DEBUG_USARTx, &USART_InitStructure); //使能空閑中斷USART_ITConfig(DEBUG_USARTx,USART_IT_IDLE,ENABLE);// 使能串口USART_Cmd(DEBUG_USARTx, ENABLE); }/***************** 發(fā)送一個字節(jié) **********************/ void Usart_SendByte( USART_TypeDef * pUSARTx, uint8_t ch) {/* 發(fā)送一個字節(jié)數(shù)據(jù)到USART */USART_SendData(pUSARTx,ch);/* 等待發(fā)送數(shù)據(jù)寄存器為空 */while (USART_GetFlagStatus(pUSARTx, USART_FLAG_TXE) == RESET); }/****************** 發(fā)送8位的數(shù)組 ************************/ void Usart_SendArray( USART_TypeDef * pUSARTx, uint8_t *array, uint16_t num) {uint8_t i;for(i=0; i<num; i++){/* 發(fā)送一個字節(jié)數(shù)據(jù)到USART */Usart_SendByte(pUSARTx,array[i]); }/* 等待發(fā)送完成 */while(USART_GetFlagStatus(pUSARTx,USART_FLAG_TC)==RESET); }/***************** 發(fā)送字符串 **********************/ void Usart_SendString( USART_TypeDef * pUSARTx, char *str) {unsigned int k=0;do {Usart_SendByte( pUSARTx, *(str + k) );k++;} while(*(str + k)!='\0');/* 等待發(fā)送完成 */while(USART_GetFlagStatus(pUSARTx,USART_FLAG_TC)==RESET){} }/***************** 發(fā)送一個16位數(shù) **********************/ void Usart_SendHalfWord( USART_TypeDef * pUSARTx, uint16_t ch) {uint8_t temp_h, temp_l;/* 取出高八位 */temp_h = (ch&0XFF00)>>8;/* 取出低八位 */temp_l = ch&0XFF;/* 發(fā)送高八位 */USART_SendData(pUSARTx,temp_h); while (USART_GetFlagStatus(pUSARTx, USART_FLAG_TXE) == RESET);/* 發(fā)送低八位 */USART_SendData(pUSARTx,temp_l); while (USART_GetFlagStatus(pUSARTx, USART_FLAG_TXE) == RESET); }///重定向c庫函數(shù)printf到串口,重定向后可使用printf函數(shù) int fputc(int ch, FILE *f) {/* 發(fā)送一個字節(jié)數(shù)據(jù)到串口 */USART_SendData(DEBUG_USARTx, (uint8_t) ch);/* 等待發(fā)送完畢 */while (USART_GetFlagStatus(DEBUG_USARTx, USART_FLAG_TXE) == RESET); return (ch); }///重定向c庫函數(shù)scanf到串口,重寫向后可使用scanf、getchar等函數(shù) int fgetc(FILE *f) {/* 等待串口輸入數(shù)據(jù) */while (USART_GetFlagStatus(DEBUG_USARTx, USART_FLAG_RXNE) == RESET);return (int)USART_ReceiveData(DEBUG_USARTx); }/*** @brief USARTx TX DMA 配置,內(nèi)存到外設(shè)(USART1->DR)* @param 無* @retval 無*/ void USARTx_DMA_Config(void) {DMA_InitTypeDef DMA_InitStructure;// 開啟DMA時鐘RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);// 外設(shè):串口數(shù)據(jù)寄存器地址*/DMA_InitStructure.DMA_PeripheralBaseAddr = USART_DR_ADDRESS;// 內(nèi)存地址(要傳輸?shù)淖兞康闹羔?DMA_InitStructure.DMA_MemoryBaseAddr = (u32)ReceiveBuff;// 方向:從外設(shè)到內(nèi)存 DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;// 傳輸大小 DMA_InitStructure.DMA_BufferSize = RECEIVEBUFF_SIZE;// 外設(shè)地址不增 DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;// 內(nèi)存地址自增DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;// 外設(shè)數(shù)據(jù)單位 DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;// 內(nèi)存數(shù)據(jù)單位DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte; // DMA模式,一次或者循環(huán)模式 // DMA_InitStructure.DMA_Mode = DMA_Mode_Normal ;DMA_InitStructure.DMA_Mode = DMA_Mode_Circular; // 優(yōu)先級:中 DMA_InitStructure.DMA_Priority = DMA_Priority_Medium; // 禁止內(nèi)存到內(nèi)存的傳輸DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;// 配置DMA通道 DMA_Init(USART_TX_DMA_CHANNEL, &DMA_InitStructure); // 使能DMADMA_Cmd (USART_TX_DMA_CHANNEL,ENABLE); }usart_dma.h
#ifndef __USARTDMA_H #define __USARTDMA_H#include "stm32f10x.h" #include <stdio.h>// 串口工作參數(shù)宏定義 #define DEBUG_USARTx USART1 #define DEBUG_USART_CLK RCC_APB2Periph_USART1 #define DEBUG_USART_APBxClkCmd RCC_APB2PeriphClockCmd #define DEBUG_USART_BAUDRATE 115200// USART GPIO 引腳宏定義 #define DEBUG_USART_GPIO_CLK (RCC_APB2Periph_GPIOA) #define DEBUG_USART_GPIO_APBxClkCmd RCC_APB2PeriphClockCmd#define DEBUG_USART_TX_GPIO_PORT GPIOA #define DEBUG_USART_TX_GPIO_PIN GPIO_Pin_9 #define DEBUG_USART_RX_GPIO_PORT GPIOA #define DEBUG_USART_RX_GPIO_PIN GPIO_Pin_10#define DEBUG_USART_IRQ USART1_IRQn #define DEBUG_USART_IRQHandler USART1_IRQHandler// 串口對應(yīng)的DMA請求通道 #define USART_TX_DMA_CHANNEL DMA1_Channel5 // 外設(shè)寄存器地址 #define USART_DR_ADDRESS (USART1_BASE+0x04) // 一次發(fā)送的數(shù)據(jù)量 #define RECEIVEBUFF_SIZE 5000void USART_Config(void); void USARTx_DMA_Config(void); void Usart_SendArray( USART_TypeDef * pUSARTx, uint8_t *array, uint16_t num); #endif /* __USARTDMA_H */中斷函數(shù)
由于在usart_dma.c的USART_Config函數(shù)中使能了 USART 接收中斷,當(dāng) USART 接收到數(shù)據(jù)就會執(zhí)行 DEBUG_USART_IRQHandler函數(shù)。里面有USART_GetITStatus函數(shù):
USART_GetITStatus(DEBUG_USARTx,USART_IT_IDLE) == SET這使jym想到,存儲器到存儲器也有個類似的DMA_GetFlagStatus函數(shù):
DMA_GetFlagStatus(DMA_FLAG_TC)==RESETUSART_GetITStatus()和USART_GetFlagStatus()的區(qū)別:
USART_GetITStatus常在串口中斷函數(shù)中使用,USART_GetFlagStatus常在做串口輪詢時使用。
USART_GetITStatus在判斷相應(yīng)位是否置1(讀SR寄存器)前會先判斷相應(yīng)位的中斷是否使能(讀CR寄存器);
在void USART_Config(void)函數(shù)里可以看到使能了空閑中斷:
//使能空閑中斷USART_ITConfig(DEBUG_USARTx,USART_IT_IDLE,ENABLE);USART_GetFlagStatus直接判斷相應(yīng)位是否置1(讀SR寄存器),而不會判斷相應(yīng)中斷是否開啟,通常可用于沒開啟相應(yīng)中斷時進(jìn)行判斷
串口數(shù)據(jù)是通過DMA傳到ReceiveBuff(在SRAM里),然后由于開了USART 的IDLE接收中斷(當(dāng)接收到1個字節(jié),就會產(chǎn)生RXNE中斷,當(dāng)接收到一幀數(shù)據(jù),就會產(chǎn)生IDLE中斷),中斷里面通過Usart_SendArray函數(shù)向電腦傳ReceiveBuff里面的數(shù)據(jù),這樣就達(dá)到了串口接收數(shù)據(jù),串口數(shù)據(jù)通過DMA傳到ReceiveBuff(在SRAM里),ReceiveBuff里面的數(shù)據(jù)返回到電腦這樣一個過程。
void DEBUG_USART_IRQHandler(void) {uint16_t t;if(USART_GetITStatus(DEBUG_USARTx,USART_IT_IDLE) == SET) //檢查中斷是否發(fā)生{ DMA_Cmd(USART_TX_DMA_CHANNEL,DISABLE); //關(guān)閉DMA傳輸t = DMA_GetCurrDataCounter(USART_TX_DMA_CHANNEL); //獲取剩余的數(shù)據(jù)數(shù)量Usart_SendArray(DEBUG_USARTx,ReceiveBuff,RECEIVEBUFF_SIZE-t); //向電腦返回?cái)?shù)據(jù)(接收數(shù)據(jù)數(shù)量 = SENDBUFF_SIZE - 剩余未傳輸?shù)臄?shù)據(jù)數(shù)量)DMA_SetCurrDataCounter(USART_TX_DMA_CHANNEL,RECEIVEBUFF_SIZE); //重新設(shè)置傳輸?shù)臄?shù)據(jù)數(shù)量DMA_Cmd(USART_TX_DMA_CHANNEL,ENABLE); //開啟DMA傳輸USART_ReceiveData(DEBUG_USARTx); //讀取一次數(shù)據(jù),不然會一直進(jìn)中斷USART_ClearFlag(DEBUG_USARTx,USART_FLAG_IDLE); //清除串口空閑中斷標(biāo)志位}}對比理解
jym對USART_DMACmd函數(shù)的理解:
USART_DMACmd函數(shù)用于控制 USART 的 DMA 請求的啟動和關(guān)閉。它接收三個參數(shù),第一個參數(shù)用于設(shè)置串口外設(shè);第二個參數(shù)設(shè)置串口的具體 DMA 請求,包括串口發(fā)送請求USART_DMAReq_Tx和接收請求 USART_DMAReq_Rx;第三個參數(shù)用于設(shè)置啟動請求 ENABLE 或者關(guān)閉請求 DISABLE;
運(yùn)行該函數(shù)后 USART 的 DMA 發(fā)送傳輸就開始了,如果是串口發(fā)送請求(請求發(fā)送到串口),即USART_DMAReq_Tx,存儲器的數(shù)據(jù)會發(fā)送到串口,如下面的存儲器到外設(shè)中的代碼;如果是串口接收請求(請求存儲器接收串口數(shù)據(jù)),即USART_DMAReq_Rx,串口數(shù)據(jù)會傳給存儲器。
存儲器到外設(shè):
/* USART1 向 DMA發(fā)出TX請求 */USART_DMACmd(DEBUG_USARTx, USART_DMAReq_Tx, ENABLE);外設(shè)到存儲器:
/* USART1 向 DMA發(fā)出RX請求 */USART_DMACmd(DEBUG_USARTx, USART_DMAReq_Rx, ENABLE);其實(shí)對于DMA設(shè)置,首要理解的就是這個DMA_InitTypeDef結(jié)構(gòu)體。jym采用三種方式定義了這個結(jié)構(gòu)體,下面把每個參數(shù)單獨(dú)摘出來,通過類比來理解。
typedef struct {uint32_t DMA_PeripheralBaseAddr; // 外設(shè)地址uint32_t DMA_MemoryBaseAddr; // 存儲器地址uint32_t DMA_DIR; // 傳輸方向uint32_t DMA_BufferSize; // 傳輸數(shù)目uint32_t DMA_PeripheralInc; // 外設(shè)地址增量模式uint32_t DMA_MemoryInc; // 存儲器地址增量模式uint32_t DMA_PeripheralDataSize; // 外設(shè)數(shù)據(jù)寬度uint32_t DMA_MemoryDataSize; // 存儲器數(shù)據(jù)寬度uint32_t DMA_Mode; // 模式選擇uint32_t DMA_Priority; // 通道優(yōu)先級uint32_t DMA_M2M; // 存儲器到存儲器模式 }DMA_InitTypeDef;DMA_PeripheralBaseAddr:一般設(shè)置為外設(shè)的數(shù)據(jù)寄存器地址,如果是存儲器到存儲器模式則設(shè)置為其中一個存儲器地址。
DMA_InitStructure.DMA_PeripheralBaseAddr = USART_DR_ADDRESS;//外設(shè)到存儲器 DMA_InitStructure.DMA_PeripheralBaseAddr = USART_DR_ADDRESS;//存儲器到外設(shè) DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)aSRC_Const_Buffer;//存儲器到存儲器DMA_MemoryBaseAddr:一般設(shè)置為自定義存儲區(qū)的首地址。
DMA_InitStructure.DMA_MemoryBaseAddr = (u32)ReceiveBuff;//外設(shè)到存儲器 DMA_InitStructure.DMA_MemoryBaseAddr = (u32)SendBuff;//存儲器到外設(shè) DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)aDST_Buffer;//存儲器到存儲器DMA_DIR:傳輸方向選擇,可選外設(shè)到存儲器、存儲器到外設(shè);并沒有存儲器到存儲器的方向選擇, 當(dāng)使用存儲器到存儲器時,只需要把其中一個存儲器當(dāng)作外設(shè)使用即可;
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;//外設(shè)到存儲器 DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;//存儲器到外設(shè) DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;//存儲器到存儲器 #define DMA_DIR_PeripheralDST ((uint32_t)0x00000010) #define DMA_DIR_PeripheralSRC ((uint32_t)0x00000000)剩下的jym不類比了,因?yàn)樘嗔恕F鋵?shí)要想知道DMA把數(shù)據(jù)從哪傳到哪,關(guān)鍵就是上面這三個參數(shù)的設(shè)置。
總結(jié)
                            
                        - 上一篇: 网站需要数据库服务器吗,网站需要独立的服
 - 下一篇: matlab 文件之间相互调用实例