STM32H7(Arduino Giga)部分定时器无法生成PWM求助
问题分析与解决方案
针对你在Arduino Giga(STM32H7)上使用TIM15_CH1/CH2无PWM输出的问题,核心原因是TIM15的主输出使能未开启,同时缺少必要的预装载配置,以下是具体修复方案:
关键问题点
TIM15主输出使能(MOE)未激活
TIM15属于带死区控制的通用定时器,其输出受BDTR寄存器的MOE位控制,默认是关闭状态。即使定时器计数正常、输出比较配置正确,只要MOE未置位,引脚就不会输出信号。而TIM14是基础定时器,无此寄存器,所以无需额外配置即可输出。缺少预装载使能配置
未开启自动重装载寄存器(ARR)和捕获比较寄存器(CCR)的预装载功能,可能导致PWM波形不稳定或配置不生效。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
相关产品推荐
相关产品推荐

