通过TIM+DMA Burst 实现STM32输出变频且不同脉冲数量的PWM波形

Burst介绍:

DMA控制器可以生成单次传输或增量突发传输,传输的节拍数为4、8或16。

为了确保数据一致性,构成突发传输的每组传输都是不可分割的:AHB传输被锁定,AHB总线矩阵的仲裁器在突发传输序列期间不会撤销DMA主设备的授权。

作用:

可以通过Burst进行多个寄存器的同时修改,在M2P时同时配置多个定时器。

burst会占用总线直到数据发完为止,此期间CPU打断不了,但是不连续的节拍之间CPU依然可以打断

问题剖析:

需要STM32输出 变频 且不同 脉冲 数量的 PWM 波形,具体要求如下:

交替输出两组参数:

参数组1:频率较高(ARR=1000),输出3个脉冲(RCR=2)。

参数组2:频率较低(ARR=5000),输出2个脉冲(RCR=1)。

实现方式:通过DMA Burst功能,在一次定时器事件中批量修改多个寄存器(ARR、RCR、CCR),无需CPU干预。

STM32的TIM模块支持DMA Burst功能,允许通过单次定时器事件触发多次DMA传输,从而批量更新多个寄存器。其核心硬件模块如下:

(1) 关键寄存器

TIMx_DCR(DMA控制寄存器):

DBSS (DMA Burst Source Selection):选择触发DMA Burst的事件源(如定时器更新事件)。

DBL (DMA Burst Length):设置一次DMA Burst传输的数据个数(例如3次传输,对应修改ARR、RCR、CCR)。

DBA (DMA Burst Address):设置DMA传输的起始寄存器地址偏移(例如ARR寄存器的地址偏移为0x2C)。

TIMx_DMAR(DMA地址寄存器):

DMA通过访问此寄存器,将数据写入目标寄存器(如ARR、RCR、CCR)。

(2) 工作原理

触发事件:定时器产生指定事件(如更新事件TIM_UPDATE)。

DMA请求:事件触发DMA Burst传输,DMA控制器根据TIMx_DCR配置的传输次数(DBL)和起始地址(DBA),将内存中的数据连续写入多个寄存器。

自动更新参数:寄存器值被修改后,定时器立即使用新参数生成PWM波形。

理解关键参数:

Burst Size与传输次数的关系

在STM32的DMA Burst模式中,Burst Size 表示 单次突发传输(Burst)中连续传输的数据单元个数,而 传输总次数 由以下两个参数共同决定:

Burst Size(突发传输单元数):例如设置为4,表示一次突发传输4个数据单元。

Data Width(数据宽度):每个数据单元的大小(字节、半字或字)。

NDTR(Number of Data):DMA传输的总数据单元数(需在代码中动态设置)。

公式:

总传输次数 = NDTR / Burst Size

例如:若NDTR=12,Burst Size=4,则总传输次数为3次(每次突发传输4个单元)。

这里我们让DMA Burst输出一次,一次传四个数据单元的值(实际上只用三个,但是mx中只可以配4increment,第四个数据传0即可),输出的脉冲个数通过传入的四个单元值中RCR 的值决定;

PWM 参数定义

ARR (Auto-Reload Register):决定PWM频率。

频率公式:PWM频率 = 定时器时钟 / (ARR + 1)

示例:

pulse1[0] = 1000 → 频率 = 100MHz / 1001 ≈ 99.9 kHz

pulse2[0] = 5000 → 频率 = 100MHz / 5001 ≈ 20 kHz

RCR (Repetition Counter Register):控制脉冲个数。

脉冲个数公式:脉冲数 = RCR + 1

示例:

pulse1[1] = 2 → 输出3个脉冲

pulse2[1] = 1 → 输出2个脉冲

CCR (Capture/Compare Register):决定占空比。

占空比公式:占空比 = CCR / (ARR + 1)

示例:

pulse1[2] = 500 → 占空比 ≈ 50%

pulse2[2] = 2500 → 占空比 ≈ 50%

参数结构:

cs 复制代码
uint32_t pulse1[3] = {1000, 2, 500};  // ARR=1000, RCR=2, CCR=500
uint32_t pulse2[3] = {5000, 1, 2500}; // ARR=5000, RCR=1, CCR=2500

CubeMX设置:

在CubeMX中配置Burst Size

打开DMA Settings标签页,选择对应的DMA通道。

设置 Burst Size 为 4 Increment(根据需求选择1/4/8/16)。

设置 Data Width 为 Word(32位,与TIM寄存器位宽一致)。

勾选 Increment Address(内存地址递增)。

选择 Mode 为 Normal 或 Circular。(若需持续传输,选择Circular模式并且设置足够大的NDTR)

每次DMA Burst需传输3个寄存器值(ARR、RCR、CCR)。

每个寄存器为32位(4字节),共需传输12字节。

Burst Size = 4 Increment(每次传输4个数据单元,但实际仅用3个,最后一个填充0)。

Data Width = Word(32位)。

NDTR = 3(传输3个数据单元)。

HAL_DMA_Start_IT()函数原型:

cpp 复制代码
HAL_StatusTypeDef HAL_DMA_Start_IT(DMA_HandleTypeDef *hdma, uint32_t SrcAddress, uint32_t DstAddress, uint32_t DataLength)

代码实现:

cpp 复制代码
uint32_t pulse_data[4] = {1000, 2, 500, 0}; // 第4个数据填充0
HAL_DMA_Start_IT(&hdma_tim1, (uint32_t)pulse_data, (uint32_t)&TIM1->DMAR, 3);// NDTR=3

交替输出的实现

(1) 中断切换模式

第一次传输:DMA传输pulse1到TIM寄存器。

传输完成中断:在中断回调函数中重新配置DMA,传输pulse2。

循环触发:重复上述过程,实现交替输出。

(2) 双缓冲模式

配置双缓冲:使能DMA双缓冲,设置两组内存地址(pulse1和pulse2)。

自动切换:DMA传输完当前缓冲区后,自动切换到下一组参数,无需CPU干预。

关键代码片段(基于 HAL 库)

cpp 复制代码
// 1. DMA传输完成中断回调函数
void HAL_TIM_DMADelayPulseCplt(DMA_HandleTypeDef *hdma) {
  // 切换参数组
  static uint8_t is_pulse1 = 0;
  if (is_pulse1) {
    HAL_DMA_Start_IT(&hdma_tim1, (uint32_t)pulse1, (uint32_t)&TIM1->DMAR, 3);
  } else {
    HAL_DMA_Start_IT(&hdma_tim1, (uint32_t)pulse2, (uint32_t)&TIM1->DMAR, 3);
  }
  is_pulse1 = !is_pulse1;
}

// 2. 主函数初始化
int main(void) {
  // 初始化定时器和DMA
  HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_1);
  HAL_DMA_Start_IT(&hdma_tim1, (uint32_t)pulse1, (uint32_t)&TIM1->DMAR, 3);
  while (1) {
    // 其他任务
  }
}
相关推荐
相醉为友1 小时前
001 使用单片机实现的逻辑分析仪——吸收篇
笔记·单片机·嵌入式硬件·fpga开发·嵌入式
【云轩】2 小时前
《哪吒的混天绫FPGA》
笔记·嵌入式硬件·fpga开发
【云轩】4 小时前
《孟婆汤的SHA-256加密》
笔记·嵌入式硬件
傍晚冰川5 小时前
【STM32】WDG看门狗(学习笔记)
c语言·笔记·科技·stm32·单片机·嵌入式硬件·学习
每月一号准时摆烂6 小时前
数字电子技术基础(三十六)——利用Multisim软件实现3线-8线译码器
单片机·嵌入式硬件
今天阳光明媚吗6 小时前
STM32-DMA
stm32
二年级程序员7 小时前
51单片机的五类指令(三)——逻辑操作类指令
单片机·嵌入式硬件·51单片机
sakabu7 小时前
基于Linux C语言多线程服务器+Qt客户端+STM32客户端实现的无人超市项目
linux·服务器·c语言·stm32·单片机·qt·stm32项目
lzb7597 小时前
蓝桥杯单片机刷题——串口发送实时时钟
单片机·蓝桥杯