STM32F411基于DMA的PWM脉冲序列输出技术问询
STM32F411定时器PWM+DMA问题解答
问题1:DMA传输与PWM不同步时的CCR值处理
这取决于你是否开启了定时器输出比较通道的预装载功能(对应TIMx_CCMRx寄存器的OCxPE位):
- 若开启预装载(
OCxPE=1):DMA写入的是预装载寄存器,实际生效的影子寄存器仅在定时器**更新事件(UEV,即每个3us的脉冲周期溢出)**时才会同步预装载的值。这种情况下,即使第一个脉冲周期内DMA连续写入两个CCR值,第二个值会被存在预装载寄存器中,不会被丢弃,会在第二个脉冲周期的更新事件后生效,第一个脉冲仍使用第一个写入的CCR值。 - 若未开启预装载(
OCxPE=0):DMA直接写入影子寄存器,新值会立即生效。此时第一个脉冲周期内写入的第二个CCR值会覆盖第一个,导致第一个脉冲的后半段使用第二个值,第一个值相当于被丢弃。
结合你的场景(每个脉冲对应一个CCR值),必须开启OCxPE位,才能保证每个DMA传输的CCR值对应到下一个脉冲周期,避免数据丢失或覆盖。
问题2:实现16个脉冲后拉低引脚并等待下一次传输
利用DMA传输完成中断+定时器输出通道控制即可实现,步骤如下:
核心配置思路
- DMA配置:设为单次传输模式,传输长度固定为16(对应16个CCR值),开启DMA传输完成中断。
- 定时器配置:
- 定时器周期设为3us,PWM模式选择
PWM1(计数器小于CCR值时输出高电平,溢出前保持低电平),开启输出比较预装载(OCxPE=1)。 - 初始状态关闭定时器输出通道(
TIMx_CCER的CCxE位清0),或把CCR设为0使输出保持低电平。
- 定时器周期设为3us,PWM模式选择
- 传输控制流程:
- 准备好16个CCR值的内存缓冲区。
- 开启定时器输出通道,启动DMA传输(若定时器未运行则同时启动计数器)。
- DMA完成16次传输后触发中断,在中断服务函数中关闭定时器输出通道(或等待第16个脉冲的更新事件后再关闭,保证脉冲完整输出),等待下一次触发。
libopencm3代码片段
DMA传输完成中断服务函数
void dma1_streamx_isr(void) { if (dma_get_interrupt_flag(DMA1, DMA_STREAMx, DMA_TCIF)) { dma_clear_interrupt_flags(DMA1, DMA_STREAMx, DMA_TCIF); // 可选:等待第16个脉冲的更新事件,确保脉冲完整输出后再拉低引脚 while (!tim_get_flag(TIMx, TIM_SR_UIF)); tim_clear_flag(TIMx, TIM_SR_UIF); // 关闭PWM输出通道,引脚立即拉低 tim_cc_disable(TIMx, TIM_OCx); } }
触发16脉冲传输的函数
void start_16pulse_stream(uint32_t *ccr_buffer) { // 重新配置DMA参数(单次传输模式下完成后需重新设置) dma_set_memory_address(DMA1, DMA_STREAMx, (uint32_t)ccr_buffer); dma_set_peripheral_address(DMA1, DMA_STREAMx, (uint32_t)&TIMx_CCRx); dma_set_number_of_data(DMA1, DMA_STREAMx, 16); // 开启PWM输出通道 tim_cc_enable(TIMx, TIM_OCx); // 启动DMA传输 dma_enable_stream(DMA1, DMA_STREAMx); // 若定时器处于停止状态,启动计数器 // tim_enable_counter(TIMx); }
注意事项
- 若定时器保持持续运行,关闭输出通道后引脚会立即拉低;如果需要精确等待第16个脉冲结束,可在中断中等待定时器更新事件标志(如代码中注释部分)。
- 通用定时器(如TIM2-TIM5)无重复计数器,因此用DMA传输完成中断是最可靠的方式。
内容的提问来源于stack exchange,提问作者m3m3n70 m0r1
相关产品推荐
相关产品推荐

