声明:以下内容均为本人学习心得
一、基础知识
HC32F460拥有2个DMA控制单元,共8个独立通道,可以独立操作不同的DMA传输功能
每次请求传输一个数据块,最小是1个数据,最大是1024个数据。
每个数据的宽度可配置为8bit、16bit或32bit。
可以配置最多65535次传输。
源地址和目标地址可以独立配置为固定,自增,自减,循环或制定偏移量的跳转
可产生3种中断:块传输完成中断、传输完成中断、传输错误中断。
每个DMA控制单元有4个独立通道,4个通道优先级顺序为:通道0>通道1>通道2>通道3;当一个DMA单元有多个通道请求传输时将按照优先级顺序执行。但已处于传输中的通道不会被打断,高优先通道需等当前通道传输完成后才会启动。
二、实验代码详解
本样例为DMA连续数据传输,通过软件触发DMA传输数据。
现象:传输按预期完成,可观察到蓝色灯亮,否则红灯亮。
①相关参数配置定义
#define DMA_UNIT (CM_DMA2) //DMA第二个单元基地址
#define DMA_CH (DMA_CH3) //通道3地址
#define DMA_TC (4UL) //DMA传输次数
#define DMA_BC (5UL) //传输数据块的大小
#define DMA_DW (DMA_DATAWIDTH_32BIT) //传输数据的宽度
#define DMA_INT_SRC (INT_SRC_DMA2_TC3) //DMA中断序号 39U
#define DMA_IRQn (INT000_IRQn) //中断回调函数
#define DMA_TRIGGER_CH (AOS_DMA2_3) //AOS触发源为DMA2_3
特别声明:对于小白来说,中断序号为啥是39U,很多小白都是迷迷糊糊的,这是因为通过用户手册查看中断INTC里面的中断事件请求序号可以查找到DMA2_TC3对应的中断事件请求序号为27H,换算成10进制就是39U。
②定义软件触发事件变量、源地址缓存区、目标地址缓存区以及预期目标地址缓存区
__IO static en_flag_status_t m_u8DmaTcEnd = RESET;
static const uint32_t u32SrcBuf[20] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
11, 12, 13, 14, 15, 16, 17, 18,
19, 20
};
static uint32_t u32DestBuf[20] = {0};
static uint32_t u32ExpectDestBufData[20] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12,
13, 14, 15, 16, 17, 18, 19, 20
};
m_u8DmaTcEnd= RESET; 此变量目的是为了产生一次软件触发事件,后面主函数会体现。再DMA产生中断里置为SET,就不会再次产生软件触发事件,需要重新置为 RESET
u32SrcBuf[20]、u32DestBuf[20] 模拟的是片外设备和片内设备之间的内存。
u32ExpectDestBufData[20] 目的是:传输的数据不能是盲目的,预期知道需要传输什么数据。
③DMA初始化
static void DmaInit(void)
{
stc_dma_init_t stcDmaInit; //DMA初始化结构体
AOS_SetTriggerEventSrc(DMA_TRIGGER_CH, EVT_SRC_AOS_STRG);
(void)DMA_StructInit(&stcDmaInit);//DMA结构体的初始化
stcDmaInit.u32IntEn = DMA_INT_ENABLE;
stcDmaInit.u32BlockSize = DMA_BC;
stcDmaInit.u32TransCount = DMA_TC;
stcDmaInit.u32DataWidth = DMA_DW;
stcDmaInit.u32DestAddr = (uint32_t)(&u32DestBuf[0]);
stcDmaInit.u32SrcAddr = (uint32_t)(&u32SrcBuf[0]);
stcDmaInit.u32SrcAddrInc = DMA_SRC_ADDR_INC;
stcDmaInit.u32DestAddrInc = DMA_DEST_ADDR_INC;
(void)DMA_Init(DMA_UNIT, DMA_CH, &stcDmaInit);
}
具体参数配置说明上面说了,我们看看DMA结构体成员有哪些:
typedef struct {
uint32_t u32IntEn; /*!< 使能DMA中断 */
uint32_t u32SrcAddr; /*!< 选择DMA触发源地址 */
uint32_t u32DestAddr; /*!< 声明DMA传输的目标地址 */
uint32_t u32DataWidth; /*!< 设置DMA数据传输宽度,由DMA_CHxCTL.HIZE决定 */
uint32_t u32BlockSize; /*!< 设置数据块大小 */
uint32_t u32TransCount; /*!< 设置DMA传输次数 */
uint32_t u32SrcAddrInc; /*!< 设置源地址更新方式为:固定、递增、递减、重载或者不连续跳转。 */
uint32_t u32DestAddrInc; /*!< 设置目标地址更新方式为:固定、递增、递减、重载或者不连续跳转 */
} stc_dma_init_t;
AOS_SetTriggerEventSrc(DMA_TRIGGER_CH, EVT_SRC_AOS_STRG); 作用是:选择DMA2为传输启动触发源。
④DMA中断配置 以及 中断回调函数
static void DmaIntInit(void)
{
stc_irq_signin_config_t stcIrqSignConfig;
stcIrqSignConfig.enIntSrc = DMA_INT_SRC;
stcIrqSignConfig.enIRQn = DMA_IRQn;
stcIrqSignConfig.pfnCallback = &DMA2_CH3_TransEnd_IrqCallback;
(void)INTC_IrqSignIn(&stcIrqSignConfig);
DMA_ClearTransCompleteStatus(DMA_UNIT, DMA_FLAG_TC_CH3);
/* NVIC setting */
NVIC_ClearPendingIRQ(DMA_IRQn);
NVIC_SetPriority(DMA_IRQn, DDL_IRQ_PRIO_DEFAULT);
NVIC_EnableIRQ(DMA_IRQn);
}
static void DMA2_CH3_TransEnd_IrqCallback(void)
{
m_u8DmaTcEnd = SET;
DMA_ClearTransCompleteStatus(DMA_UNIT, DMA_FLAG_TC_CH3);
}
中断配置在之前文章有详解过,这里就不多解释了,需要特别说明的是:在中断回调函数中,m_u8DmaTcEnd = SET; 要将软件触发变量置1,这样就不会产生第二次软件触发事件。
⑤主函数
int32_t main(void)
{
/* Register write enable for some required peripherals. */
LL_PERIPH_WE(LL_PERIPH_GPIO | LL_PERIPH_PWC_CLK_RMU | LL_PERIPH_FCG | LL_PERIPH_EFM | LL_PERIPH_SRAM);
/* System clock init */
BSP_CLK_Init();
/* LED init */
BSP_LED_Init();
/* DMA/AOS FCG enable */
FCG_Fcg0PeriphClockCmd((FCG0_PERIPH_DMA2 | FCG0_PERIPH_AOS), ENABLE);
/* Register write protected for some required peripherals. */
LL_PERIPH_WP(LL_PERIPH_GPIO | LL_PERIPH_FCG | LL_PERIPH_EFM | LL_PERIPH_SRAM);
/* Config DMA */
DmaInit();
/* DMA interrupt config */
DmaIntInit();
/* DMA module enable */
DMA_Cmd(DMA_UNIT, ENABLE);
/* DMA channel enable */
(void)DMA_ChCmd(DMA_UNIT, DMA_CH, ENABLE);
/* 1st trigger for DMA */
AOS_SW_Trigger();
while (RESET == m_u8DmaTcEnd) {
if (SET == DMA_GetTransCompleteStatus(DMA_UNIT, DMA_FLAG_BTC_CH3)) {
DMA_ClearTransCompleteStatus(DMA_UNIT, DMA_FLAG_BTC_CH3);
AOS_SW_Trigger();
}
}
if (0 != memcmp(u32DestBuf, u32ExpectDestBufData, sizeof(u32DestBuf))) {
/* LED red */
BSP_LED_On(LED_RED);
} else {
/* LED blue, as expected */
BSP_LED_On(LED_BLUE);
}
for (;;) {
;
}
}
AOS_SW_Trigger(); 作用是 通过设置A0S中外设触发事件寄存器(INTSFTTRG)中b0为1,置1:产生一次软件触发事件。
while(...): 这个语句的作用是 传输之前判断相应的寄存器清0没有,否则无法传输。通过if()判断上一次是否传输完成(中断状态寄存器0(DMA_INTSTAT1)b3~b0)。
if (0 != memcmp(u32DestBuf, u32ExpectDestBufData, sizeof(u32DestBuf))) 用来判断是否按预期完成传输。
到此DMA数据传输实验代码分析完毕,如有疑问可以私信我~