1)實(shí)驗(yàn)平臺(tái):正點(diǎn)原子APM32E103最小系統(tǒng)板
2)平臺(tái)購(gòu)買地址:https://detail.tmall.com/item.htm?id=609294757420
3)全套實(shí)驗(yàn)源碼+手冊(cè)+視頻下載地址: http://www.openedv.com/docs/boards/xiaoxitongban
第三十二章 DMA實(shí)驗(yàn)
本章介紹APM32E103直接存儲(chǔ)訪問(DMA)的使用,DMA能夠在無CPU干預(yù)的情況下,實(shí)現(xiàn)外設(shè)與存儲(chǔ)器或存儲(chǔ)器與存儲(chǔ)器之間數(shù)據(jù)的高速傳輸,從而節(jié)省CPU資源來做其他操作。通過本章的學(xué)習(xí),讀者將學(xué)習(xí)到DMA的使用。
本章分為如下幾個(gè)小節(jié):
32.1 硬件設(shè)計(jì)
32.2 程序設(shè)計(jì)
32.3 下載驗(yàn)證
32.1 硬件設(shè)計(jì)
32.1.1 例程功能
- 按下KEY0按鍵,USART1以DMA方式發(fā)送數(shù)據(jù),同時(shí)在LCD上顯示DMA傳輸?shù)倪M(jìn)度
- LED0閃爍,指示程序正在運(yùn)行
32.1.2 硬件資源 - LED
LED0 - PB5 - 按鍵
KEY0 - PE4 - 正點(diǎn)原子 2.8/3.5/4.3/7/10寸TFTLCD模塊(僅限MCU屏,16位8080并口驅(qū)動(dòng))
- USART1(PA9、PA10連接至板載USB轉(zhuǎn)串口芯片上)
- DMA(DMA1通道4)
32.1.3 原理圖
本章實(shí)驗(yàn)使用的DMA為APM32E103的片上資源,因此沒有對(duì)應(yīng)的連接原理圖。
32.2 程序設(shè)計(jì)
32.2.1 Geehy標(biāo)準(zhǔn)庫(kù)的DMA驅(qū)動(dòng)
在使用DMA之前,需要先根據(jù)需求對(duì)DMA的通道進(jìn)行配置,例如本章實(shí)驗(yàn)要使用DMA進(jìn)行串口數(shù)據(jù)的發(fā)送,因此應(yīng)將DMA的通道配置為存儲(chǔ)器到外設(shè)的模式等等。使用DMA的具體步驟如下:
①:根據(jù)需求配置DMA通道
②:使能DMA通道進(jìn)行數(shù)據(jù)傳輸
③:讀DMA通道的狀態(tài)標(biāo)志判斷傳輸是否完成
④:傳輸完成后清除DMA通道的傳輸完成標(biāo)志
在Geehy標(biāo)準(zhǔn)庫(kù)中對(duì)應(yīng)的驅(qū)動(dòng)函數(shù)如下:
①:配置DMA通道
該函數(shù)用于配置DMA通道的各項(xiàng)參數(shù),其函數(shù)原型如下所示:
void DMA_Config(DMA_Channel_T* channel, DMA_Config_T* dmaConfig)
該函數(shù)的形參描述,如下表所示:
形參 描述
channel 指定的DMA通道
例如:DMA1_channelx,其中x可以從1到7或DMA2_channely,同樣的y可以從1至5(在apm32e10x.h文件中有定義)
dmaConfig 指向DMA通道配置結(jié)構(gòu)體的指針
需自行定義,并根據(jù)DMA通道的配置參數(shù)填充結(jié)構(gòu)體中的成員變量
表32.2.1.1 函數(shù)DMA_Config()形參描述
該函數(shù)的返回值描述,如下表所示:
返回值 描述
無 無
表32.2.1.2 函數(shù)DMA_Config()返回值描述
該函數(shù)使用DMA_Config_T類型的結(jié)構(gòu)體變量傳入DMA通道的配置參數(shù),該結(jié)構(gòu)體的定義如下所示:
typedef enum
{
DMA_DIR_PERIPHERAL_SRC,
DMA_DIR_PERIPHERAL_DST
} DMA_DIR_T;
typedef enum
{
DMA_PERIPHERAL_INC_DISABLE, /* 禁止外設(shè)增量模式 */
DMA_PERIPHERAL_INC_ENABLE /* 使能外設(shè)增量模式 */
} DMA_PERIPHERAL_INC_T;
typedef enum
{
DMA_MEMORY_INC_DISABLE, /* 禁用存儲(chǔ)器遞增模式 */
DMA_MEMORY_INC_ENABLE /* 使能存儲(chǔ)器遞增模式 */
} DMA_MEMORY_INC_T;
typedef enum
{
DMA_PERIPHERAL_DATA_SIZE_BYTE, /* 外設(shè)數(shù)據(jù)大小為字節(jié) */
DMA_PERIPHERAL_DATA_SIZE_HALFWORD, /* 外設(shè)數(shù)據(jù)大小為半字 */
DMA_PERIPHERAL_DATA_SIZE_WORD /* 外設(shè)數(shù)據(jù)大小為字 */
} DMA_PERIPHERAL_DATA_SIZE_T;
typedef enum
{
DMA_MODE_NORMAL, /* 禁用循環(huán)模式 */
DMA_MODE_CIRCULAR /* 使能循環(huán)模式 */
} DMA_LOOP_MODE_T;
typedef enum
{
DMA_PRIORITY_LOW, /* 優(yōu)先級(jí)低 */
DMA_PRIORITY_MEDIUM, /* 優(yōu)先級(jí)中 */
DMA_PRIORITY_HIGH, /* 優(yōu)先級(jí)高 */
DMA_PRIORITY_VERYHIGH /* 優(yōu)先級(jí)非常高 */
} DMA_PRIORITY_T;
typedef struct
{
uint32_t peripheralBaseAddr; /* 外設(shè)基地址 */
uint32_t memoryBaseAddr; /* 存儲(chǔ)器基地址 */
DMA_DIR_T dir; /* 數(shù)據(jù)傳輸方向 */
uint32_t bufferSize; /* 傳輸?shù)臄?shù)據(jù)項(xiàng)數(shù)目 */
DMA_PERIPHERAL_INC_T peripheralInc; /* 外設(shè)增量模式 */
DMA_MEMORY_INC_T memoryInc; /* 存儲(chǔ)器遞增模式 */
DMA_PERIPHERAL_DATA_SIZE_T peripheralDataSize; /* 外設(shè)數(shù)據(jù)大小 */
DMA_MEMORY_DATA_SIZE_T memoryDataSize; /* 存儲(chǔ)器數(shù)據(jù)大小 */
DMA_LOOP_MODE_T loopMode; /* 循環(huán)模式 */
DMA_PRIORITY_T priority; /* 優(yōu)先級(jí) */
DMA_M2MEN_T M2M;
} DMA_Config_T;
該函數(shù)的使用示例,如下所示:
#include "apm32e10x.h"
#include "apm32e10x_dma.h"
static uint8_t membuf[256];
void example_fun(void)
{
DMA_Config_T dma_init_struct;
/* 配置DMA1通道0通道0 */
dma_init_struct.channel = DMA_CHANNEL_0;
dma_init_struct.peripheralBaseAddr = (uint32_t)&USART1->DATA;
dma_init_struct.memoryBaseAddr = (uint32_t)&membuf[0];
dma_init_struct.dir = DMA_DIR_MEMORYTOPERIPHERAL;
dma_init_struct.bufferSize = sizeof(membuf) / sizeof(membuf[0]);
dma_init_struct.peripheralInc = DMA_PERIPHERAL_INC_DISABLE;
dma_init_struct.memoryInc = DMA_MEMORY_INC_ENABLE;
dma_init_struct.peripheralDataSize = DMA_PERIPHERAL_DATA_SIZE_BYTE;
dma_init_struct.memoryDataSize = DMA_MEMORY_DATA_SIZE_BYTE;
dma_init_struct.loopMode = DMA_MODE_NORMAL;
dma_init_struct.priority = DMA_PRIORITY_MEDIUM;
dma_init_struct.fifoMode = DMA_FIFOMODE_DISABLE;
dma_init_struct.fifoThreshold = DMA_FIFOTHRESHOLD_FULL;
dma_init_struct.memoryBurst = DMA_MEMORYBURST_SINGLE;
dma_init_struct.peripheralBurst = DMA_PERIPHERALBURST_SINGLE;
DMA_Config(DMA1_Stream0, &dma_init_struct);
}
②:使能DMA通道
該函數(shù)用于使能DMA通道進(jìn)行數(shù)據(jù)傳輸,其函數(shù)原型如下所示:
void DMA_Enable(DMA_Channel_T *channel);
該函數(shù)的形參描述,如下表所示:
形參 描述
channel 指定的DMA通道
表32.2.1.3 函數(shù)DMA_Enable()形參描述
該函數(shù)的返回值描述,如下表所示:
返回值 描述
無 無
表32.2.1.4 函數(shù)DMA_Enable()返回值描述
該函數(shù)的使用示例,如下所示:
#include "apm32e10x.h"
#include "apm32e10x_dma.h"
void example_fun(DMA_Channel_T *dma_chx)
{
/* 使能DMA通道 */
DMA_Enable(dma_chx);
}
③:讀取DMA通道的狀態(tài)標(biāo)志
該函數(shù)用于讀取DMA通道的指定狀態(tài)標(biāo)志,其函數(shù)原型如下所示:
uint8_t DMA_ReadStatusFlag(DMA_FLAG_T flag);
該函數(shù)的形參描述,如下表所示:
形參 描述
flag 指定DMA通道的狀態(tài)標(biāo)志
例如:DMA1_FLAG_GINT1、DMA1_FLAG_TC1等(在apm32e10x_dma.h文件中有定義)
表32.1.2.5 函數(shù)DMA_ReadStatusFlag()形參描述
該函數(shù)的返回值描述,如下表所示:
返回值 描述
SET 事件標(biāo)志發(fā)生
RESET 事件標(biāo)志未發(fā)生
表32.1.2.6 函數(shù)DMA_ReadStatusFlag()返回值描述
該函數(shù)的使用示例,如下所示:
#include "apm32e10x.h"
#include "apm32e10x_dma.h"
void example_fun(void)
{
uint8_t flag;
/* 讀取DMA1通道4的數(shù)據(jù)傳輸完成標(biāo)志 */
flag = DMA_ReadStatusFlag(DMA1_FLAG_TC4);
if (flag == SET)
{
/* Do something. */
}
else
{
/* Do something. */
}
}
④:清除DMA通道狀態(tài)標(biāo)志
該函數(shù)用于清除DMA通道的狀態(tài)標(biāo)志,其函數(shù)原型如下所示:
void DMA_ClearStatusFlag(uint32_t flag);
該函數(shù)的形參描述,如下表所示:
形參 描述
flag 清除指定DMA通道的標(biāo)志
例如:DMA1_FLAG_GINT1、DMA1_FLAG_TC1等(在apm32e10x_dma.h文件中有定義)
表32.2.1.7 函數(shù)DMA_ClearStatusFlag()形參描述
該函數(shù)的返回值描述,如下表所示:
返回值 描述
無 無
表32.2.1.8 函數(shù)DMA_ClearStatusFlag()返回值描述
該函數(shù)的使用示例,如下所示:
#include "apm32e10x.h"
#include "apm32e10x_dma.h"
void example_fun(void)
{
/* 清除DMA1通道1的傳輸完成標(biāo)志 */
DMA_ClearStatusFlag(DMA1_FLAG_TC1);
}
32.2.2 DMA驅(qū)動(dòng)
本章實(shí)驗(yàn)的DMA驅(qū)動(dòng)主要負(fù)責(zé)向應(yīng)用層提供DMA的初始化和開啟傳輸?shù)暮瘮?shù)。本章實(shí)驗(yàn)中,DMA的驅(qū)動(dòng)代碼包括dma.c和dma.h兩個(gè)文件。
DMA驅(qū)動(dòng)中,DMA的初始化函數(shù),如下所示:
/**
* @brief 初始化DMA
* @param dma_chx : DMA通道
* @param par : 外設(shè)地址
* @param mar : 存儲(chǔ)器0地址
* @param ndtr : 傳輸?shù)臄?shù)據(jù)項(xiàng)數(shù)目
* @retval 無
*/
void dma_init( DMA_Channel_T *dma_chx,
uint32_t par,
uint32_t mar,
uint16_t ndtr)
{
DMA_Config_T dma_init_struct;
RCM_EnableAHBPeriphClock(RCM_AHB_PERIPH_DMA1); /* 使能DMA時(shí)鐘 */
DMA_Reset(dma_chx); /* 復(fù)位DMA */
/* 配置DMA */
dma_init_struct.peripheralBaseAddr = par; /* 外設(shè)基地址 */
dma_init_struct.memoryBaseAddr = mar; /* DMA內(nèi)存基地址 */
dma_init_struct.dir = DMA_DIR_PERIPHERAL_DST; /* 數(shù)據(jù)傳輸方向 */
dma_init_struct.bufferSize = ndtr; /* 傳輸?shù)臄?shù)據(jù)項(xiàng)數(shù)目 */
/* 外設(shè)增量模式 */
dma_init_struct.peripheralInc = DMA_PERIPHERAL_INC_DISABLE;
dma_init_struct.memoryInc = DMA_MEMORY_INC_ENABLE; /* 存儲(chǔ)器遞增模式 */
/* 外設(shè)數(shù)據(jù)大小 */
dma_init_struct.peripheralDataSize = DMA_PERIPHERAL_DATA_SIZE_BYTE;
/* 存儲(chǔ)器數(shù)據(jù)大小 */
dma_init_struct.memoryDataSize = DMA_MEMORY_DATA_SIZE_BYTE;
dma_init_struct.loopMode = DMA_MODE_NORMAL; /* 正常工作模式 */
dma_init_struct.priority = DMA_PRIORITY_MEDIUM; /* 優(yōu)先級(jí) */
/* DMA通道x沒有設(shè)置為內(nèi)存到內(nèi)存?zhèn)鬏?*/
dma_init_struct.M2M = DMA_M2MEN_DISABLE;
DMA_Config(dma_chx, &dma_init_struct); /* 配置DMA通道 */
DMA_Enable(dma_chx); /* 使能DMA通道 */
}
從上面的代碼中可以看到,DMA的初始化函數(shù)提供了DMA通道、外設(shè)基地址、存儲(chǔ)器基地址、傳輸數(shù)據(jù)項(xiàng)數(shù)據(jù)的配置參數(shù),結(jié)合這些傳入的參數(shù),通過DMA通道配置函數(shù)DMA_Config()配置了指定的DMA通道。
DMA驅(qū)動(dòng)中,開啟DMA傳輸?shù)暮瘮?shù),如下所示:
/**
* @brief 開啟一次DMA傳輸
* @param dma_chx : DMA通道
* @param ndtr : 傳輸?shù)臄?shù)據(jù)項(xiàng)數(shù)目
* @retval 無
*/
void dma_enable(DMA_Channel_T *dma_chx, uint16_t ndtr)
{
DMA_Disable(dma_chx); /* 關(guān)閉USART1 TX DMA1 所指示的通道 */
DMA_ConfigDataNumber(dma_chx, ndtr); /* 配置傳輸?shù)臄?shù)據(jù)項(xiàng)數(shù)目 */
DMA_Enable(dma_chx); /* 使能USART1 TX DMA1 所指示的通道 */
}
從上面的代碼中可以看到,開啟DMA數(shù)據(jù)傳輸實(shí)際上就是調(diào)用函數(shù)DMA_Enable()使能DMA通道,但考慮到每次傳輸?shù)臄?shù)據(jù)量可能不同,因此在使能DMA通道前,還配置了DMA通道傳輸數(shù)據(jù)項(xiàng)目的數(shù)量。
32.2.3 實(shí)驗(yàn)應(yīng)用代碼
本實(shí)驗(yàn)的應(yīng)用代碼,如下所示:
/* 要循環(huán)發(fā)送的字符串 */文章來源:http://www.zghlxwxcb.cn/news/detail-837153.html
const uint8_t text_to_send[] = {"正點(diǎn)原子 APM32E103最小系統(tǒng)板 DMA實(shí)驗(yàn)\r\n"};
#define SEND_BUF_SIZE (sizeof(text_to_send) * 200) /* 發(fā)送數(shù)據(jù)長(zhǎng)度 */
uint8_t g_sendbuf[SEND_BUF_SIZE]; /* 發(fā)送數(shù)據(jù)緩沖區(qū) */
int main(void)
{
uint8_t key;
uint16_t i;
uint16_t len;
uint16_t pro;
NVIC_ConfigPriorityGroup(NVIC_PRIORITY_GROUP_4);/* 設(shè)置中斷優(yōu)先級(jí)分組為組4 */
sys_apm32_clock_init(15); /* 配置系統(tǒng)時(shí)鐘 */
delay_init(120); /* 初始化延時(shí)功能 */
usart_init(115200); /* 初始化串口 */
led_init(); /* 初始化LED */
lcd_init(); /* 初始化LCD */
dma_init( DMA1_Channel4,
(uint32_t) & USART1->DATA,
(uint32_t)g_sendbuf,
SEND_BUF_SIZE); /* 初始化DMA */
lcd_show_string(30, 50, 200, 16, 16, "APM32", RED);
lcd_show_string(30, 70, 200, 16, 16, "DMA TEST", RED);
lcd_show_string(30, 90, 200, 16, 16, "ATOM@ALIENTEK", RED);
lcd_show_string(30, 110, 200, 16, 16, "KEY0:Start", RED);
len = sizeof(text_to_send); /* 填充發(fā)送數(shù)據(jù)緩沖區(qū) */
for (i = 0; i < SEND_BUF_SIZE; i++)
{
g_sendbuf[i] = text_to_send[i % len];
}
i = 0;
while (1)
{
key = key_scan(0);
if (key == KEY0_PRES)
{
lcd_show_string(30, 130, 200, 16, 16, "Start Transimit....", BLUE);
lcd_show_string(30, 150, 200, 16, 16, " %", BLUE);
USART_EnableDMA(USART_UX, USART_DMA_TX); /* 使能USART的DMA發(fā)送 */
dma_enable(DMA1_Channel4, SEND_BUF_SIZE); /* 開啟一次DMA傳輸 */
/* 等待DMA傳輸完成
* 實(shí)際應(yīng)用中,在等待DMA傳輸完成期間,可以執(zhí)行另外的任務(wù)
*/
while (1)
{ /* 判斷通道4傳輸完成 */
if (DMA_ReadStatusFlag(DMA1_FLAG_TC4) != RESET)
{ /* 清除通道4傳輸完成標(biāo)志 */
DMA_ClearStatusFlag(DMA1_FLAG_TC4);
break;
}
pro = DMA_ReadDataNumber(DMA1_Channel4); /* 得到當(dāng)前還剩余多少個(gè)數(shù)據(jù) */
pro = ((SEND_BUF_SIZE - pro) * 100) / SEND_BUF_SIZE; /* 獲得百分比 */
lcd_show_num(30, 150, pro, 3, 16, BLUE); /* LCD顯示DMA傳輸進(jìn)度 */
}
pro = DMA_ReadDataNumber(DMA1_Channel4);
pro = ((SEND_BUF_SIZE - pro) * 100) / SEND_BUF_SIZE;
lcd_show_num(30, 150, pro, 3, 16, BLUE);
lcd_show_string(30, 130, 200, 16, 16, "Transimit Finished!", BLUE); /* LCD顯示傳送完成 */
}
i++;
if ((i % 20) == 0)
{
i = 0;
LED0_TOGGLE();
}
delay_ms(10);
}
}
從上面的代碼中可以看到,在DMA的初始化中,初始化了DMA1通道4,這是因?yàn)閁SART1的發(fā)送就對(duì)應(yīng)了DMA1通道4,完成初始化工作后,就不斷地掃描按鍵,若掃描到KEY0按鍵,并使用DMA進(jìn)行USART1發(fā)送數(shù)據(jù),在DMA進(jìn)行數(shù)據(jù)傳輸?shù)拇似陂g,LCD上不斷刷新顯示DMA傳輸?shù)倪M(jìn)度,直至DMA傳輸完畢。
32.3 下載驗(yàn)證
在完成編譯和燒錄操作后,可以看到LCD上顯示了本實(shí)驗(yàn)的實(shí)驗(yàn)信息,此時(shí)若按下KEY0按鍵,可以看到串口調(diào)試助手不斷打印“正點(diǎn)原子 APM32E103最小系統(tǒng)板 DMA實(shí)驗(yàn)\r\n”(200次),在此期間也能看到LCD上不斷地實(shí)時(shí)刷新串口打印的進(jìn)度。文章來源地址http://www.zghlxwxcb.cn/news/detail-837153.html
到了這里,關(guān)于【正點(diǎn)原子STM32連載】第三十二章 DMA實(shí)驗(yàn) 摘自【正點(diǎn)原子】APM32E103最小系統(tǒng)板使用指南的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!