前言
单片机中使用定时器触发DMA是比较好用的一种操作,这里将对此做个示例说明。
基础说明
ST官方的例程中有一个 TIM_DMA
的例程,其功能是启用一个定时器,设置其中一路通道输出PWM,使用 TIM_CH
事件来触发DMA,然后用DMA搬运数据到 CCR
寄存器,从而来改变PWM的占空比。该示例是借鉴官方示例而来。
该示例演示通过 TIM_UP
请求来触发DMA搬运数据。本例中通过DMA从内存中搬运数据修改 GPIOx->BSRR
寄存器的值,从而实现修改GPIO口输出电平。使用该方式下可以实现同步控制一组GPIO口的输出,可以当作方波或是PWM输出等功能。除默认生成的代码,只在 main.c
文件添加几行手动编写的代码。本例中输出引脚为 PB0、PB1、PB2、PB3 。
关键配置与代码
这里只贴出关键的配置与代码,完整示例可以通过文后链接查看。
本示例中关键配置就是下图的通过 TIM_UP
请求来触发DMA了:
除了配置生成的代码,需要手动添加的代码就几行:
HAL库
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_DMA_Init();
MX_TIM3_Init();
// BSRR寄存器数据,高16位用于清除端口,低16位用于设置端口
uint32_t buff[5] ={0x000F0000, 0x00000008, 0x00000004, 0x00000002, 0x00000001};
// 以下步骤参考函数 HAL_TIM_Base_Start_DMA ,但该函数中外设地址是定时器的ARR寄存器,不符合我这里需求,所以根据该函数来编写下面代码
HAL_DMA_Start(&hdma_tim3_up, (uint32_t)buff, (uint32_t)&GPIOB->BSRR, 5); // 设置并启动DMA
__HAL_TIM_ENABLE_DMA(&htim3, TIM_DMA_UPDATE); // 使能定时器触发DMA
HAL_TIM_Base_Start(&htim3); // 启动定时器
while (1)
{
}
}
LL库
int main(void)
{
LL_APB4_GRP1_EnableClock(LL_APB4_GRP1_PERIPH_SYSCFG);
NVIC_SetPriorityGrouping(NVIC_PRIORITYGROUP_4);
NVIC_SetPriority(SysTick_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(),15, 0));
SystemClock_Config();
MX_GPIO_Init();
MX_DMA_Init();
MX_TIM3_Init();
// BSRR寄存器数据,高16位用于清除端口,低16位用于设置端口
uint32_t buff[5] ={0x000F0000, 0x00000008, 0x00000004, 0x00000002, 0x00000001};
LL_DMA_SetPeriphAddress(DMA1, LL_DMA_STREAM_0, (uint32_t)&GPIOB->BSRR); // 设置外设地址
LL_DMA_SetMemoryAddress(DMA1, LL_DMA_STREAM_0, (uint32_t)&buff); // 设备内存地址
LL_DMA_SetDataLength(DMA1, LL_DMA_STREAM_0, 5); // 设置传输长度
LL_DMA_EnableStream(DMA1, LL_DMA_STREAM_0); // 开启DMA传输
LL_TIM_EnableDMAReq_UPDATE(TIM3); // 使能TIM_UP请求DMA
LL_TIM_EnableCounter(TIM3); // 启动定时器
while (1)
{
}
}
本示例演示结果可以通过逻辑分析仪方便的观察:
示例链接
仓库地址: https://github.com/NaisuXu/STM32_MCU_Examples
本示例为仓库中 TIM_UP_DMA_HAL_H750
或 TIM_UP_DMA_LL_H750
。