You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

STM32H7(Arduino Giga)部分定时器无法生成PWM求助

问题分析与解决方案

针对你在Arduino Giga(STM32H7)上使用TIM15_CH1/CH2无PWM输出的问题,核心原因是TIM15的主输出使能未开启,同时缺少必要的预装载配置,以下是具体修复方案:

关键问题点

  1. TIM15主输出使能(MOE)未激活
    TIM15属于带死区控制的通用定时器,其输出受BDTR寄存器的MOE位控制,默认是关闭状态。即使定时器计数正常、输出比较配置正确,只要MOE未置位,引脚就不会输出信号。而TIM14是基础定时器,无此寄存器,所以无需额外配置即可输出。

  2. 缺少预装载使能配置
    未开启自动重装载寄存器(ARR)和捕获比较寄存器(CCR)的预装载功能,可能导致PWM波形不稳定或配置不生效。

  3. GPIO模式赋值存在风险
    直接赋值GPIOA->MODER会覆盖所有GPIOA引脚的模式,可能影响其他引脚的默认功能,建议改用位操作单独修改目标引脚。

修改后的代码

int VolRepTime = 11000;     // 单周期采集时间(微秒)
int Laser1UpTime = 8000;    // Laser1触发高电平时间(微秒)
int Laser2UpTime = 5000;    // Laser2触发高电平时间(微秒)
int Laser3UpTime = 4000;    // Laser3触发高电平时间(微秒)

void setup()
{
  Serial.begin(115200);
  while (!Serial);
  
  initTimers();
  initPins();

  Trigger1(Laser1UpTime, VolRepTime);
  Trigger2(Laser2UpTime, VolRepTime);  
  Trigger3(Laser3UpTime, VolRepTime);

}


void initTimers(){

  RCC->APB1LENR |= RCC_APB1LENR_TIM14EN;    // 使能TIM14时钟
  RCC->APB2ENR  |= RCC_APB2ENR_TIM15EN;     // 使能TIM15时钟

  // TIM14配置
  TIM14->CCMR1 &= ~TIM_CCMR1_OC1M_Msk;      // 清零OC1M位
  TIM14->CCMR1 |= (0x6UL << TIM_CCMR1_OC1M_Pos); // PWM模式1:CNT < CCR1时输出有效
  TIM14->CCMR1 |= TIM_CCMR1_OC1PE;          // 开启CCR1预装载
  TIM14->CR1 |= TIM_CR1_ARPE;               // 开启ARR预装载
  TIM14->CCER |= TIM_CCER_CC1E;             // 使能CH1输出
  TIM14->DIER |= (TIM_DIER_CC1IE | TIM_DIER_UIE); // 开启中断(若不需要可移除)
  TIM14->SR &= ~(TIM_SR_CC1IF | TIM_SR_UIF);      // 清除中断标志

  // TIM15配置
  TIM15->CCMR1 &= ~(TIM_CCMR1_OC1M_Msk | TIM_CCMR1_OC2M_Msk); // 清零OC1M、OC2M位
  TIM15->CCMR1 |= (0x6UL << TIM_CCMR1_OC1M_Pos); // CH1 PWM模式1
  TIM15->CCMR1 |= (0x6UL << TIM_CCMR1_OC2M_Pos); // CH2 PWM模式1
  TIM15->CCMR1 |= TIM_CCMR1_OC1PE | TIM_CCMR1_OC2PE; // 开启CCR1/CCR2预装载
  TIM15->CR1 |= TIM_CR1_ARPE;               // 开启ARR预装载
  TIM15->CCER |= TIM_CCER_CC1E | TIM_CCER_CC2E; // 使能CH1/CH2输出
  TIM15->BDTR |= TIM_BDTR_MOE;              // 关键:开启TIM15主输出使能
  TIM15->DIER |= (TIM_DIER_CC1IE | TIM_DIER_CC2IE | TIM_DIER_UIE); // 开启中断(若不需要可移除)
  TIM15->SR &= ~(TIM_SR_CC1IF | TIM_SR_CC2IF | TIM_SR_UIF);        // 清除中断标志

}

void initPins(){
  RCC->AHB4ENR |= RCC_AHB4ENR_GPIOAEN;      // 使能GPIOA时钟

  // 单独设置目标引脚为复用功能模式(避免覆盖其他引脚)
  // PA2 (D3) -> TIM15_CH1
  GPIOA->MODER &= ~GPIO_MODER_MODE2_Msk;
  GPIOA->MODER |= GPIO_MODER_MODE2_1;
  // PA3 (D2) -> TIM15_CH2
  GPIOA->MODER &= ~GPIO_MODER_MODE3_Msk;
  GPIOA->MODER |= GPIO_MODER_MODE3_1;
  // PA7 (D5) -> TIM14_CH1
  GPIOA->MODER &= ~GPIO_MODER_MODE7_Msk;
  GPIOA->MODER |= GPIO_MODER_MODE7_1;

  // 设置复用功能映射
  GPIOA->AFR[0] &= ~(GPIO_AFRL_AFSEL2_Msk | GPIO_AFRL_AFSEL3_Msk | GPIO_AFRL_AFSEL7_Msk);
  GPIOA->AFR[0] |= (0x4UL << GPIO_AFRL_AFSEL2_Pos);  // PA2 映射AF4
  GPIOA->AFR[0] |= (0x4UL << GPIO_AFRL_AFSEL3_Pos);  // PA3 映射AF4
  GPIOA->AFR[0] |= (0x9UL << GPIO_AFRL_AFSEL7_Pos);  // PA7 映射AF9
}

void Trigger1(int UpTime, int VolRepTime){
  TIM14->PSC = 240-1;             // 预分频:1 tick = 1微秒(240MHz / 240 = 1MHz)
  TIM14->ARR = VolRepTime-1;      // 自动重装载值(周期)
  TIM14->CCR1 = UpTime;           // 比较值(高电平时间)
}

void Trigger2(int UpTime, int VolRepTime){
  TIM15->PSC = 240-1;             
  TIM15->ARR = VolRepTime-1;      
  TIM15->CCR1 = UpTime;        
}

void Trigger3(int UpTime, int VolRepTime){
  TIM15->PSC = 240-1;             
  TIM15->ARR = VolRepTime-1;      
  TIM15->CCR2 = UpTime;        
}

void onoff(){
  TIM14->CR1 ^= TIM_CR1_CEN; 
  TIM15->CR1 ^= TIM_CR1_CEN;
}

void loop()
{
  delay(1000);
  
  if(Serial.read()!=-1){
    onoff();
  }
  Serial.println(TIM15->CNT);
}

额外说明

  • 若不需要定时器中断,可以移除DIER寄存器的配置和SR的清零操作,减少不必要的资源占用。
  • 强制有效电平测试(OCxM=0x5)无效的原因也是因为MOE位未开启,只要补上TIM15->BDTR |= TIM_BDTR_MOE;,该测试也能正常输出电平。

内容的提问来源于stack exchange,提问作者Timeverdrive

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.03 01:40:27