
1、配置app.overlaytimers3{statusokay;st,prescaler0;/* 配置定时器中断通常使用更新中断 Update Interrupt */interrupts290;// interrupt-names update;interrupt-namescc;qdec:qdec{compatiblest,stm32-qdec;statusokay;/* 引用上面定义的引脚标签 */pinctrl-0tim3_ch1_pa6tim3_ch2_pa7;pinctrl-namesdefault;/* 可选输入极性反转 */st,input-polarity-inverted;/* 配置输入滤波器级别用于消除编码器机械抖动 */st,input-filter-level5;/* 编码器每转产生的脉冲数 */st,counts-per-revolution700;/* 编码器模式例如 1 表示特定的计数模式 */st,encoder-mode1;};};/* 定义 PB10 对应的 GPIO 节点 */my_gpio_input{compatiblegpio-keys;/* 如果是按键推荐使用此兼容字符串 */debounce-interval-ms10;my_signal:signal_0{gpiosgpiob10(GPIO_PULL_UP|GPIO_ACTIVE_LOW);/* PB10上拉低电平有效 */labelMy PB10 Signal;zephyr,codeINPUT_KEY_0;};};配置prj.confCONFIG_COUNTERy CONFIG_SENSORy CONFIG_INPUTy CONFIG_QDEC_STM32y代码段#includezephyr/drivers/counter.h#includezephyr/drivers/sensor.h#includezephyr/drivers/gpio.h#includezephyr/sys_clock.h#includezephyr/devicetree.h#includezephyr/drivers/sensor.h#includestm32_ll_tim.h// 1. 定义触发阈值例如每 100 个脉冲触发一次#definePULSES_PER_INTERRUPT100#defineQDEC_NODEDT_ALIAS(qdec0)staticconststructdevice*constqdec_devDEVICE_DT_GET(QDEC_NODE);// 2. 中断服务例程 (ISR)voidqdec_update_isr(constvoid*param){//必须清除 TIM3 的更新中断标志位防止陷入死循环LL_TIM_ClearFlag_UPDATE(TIM3);// 业务逻辑读取当前绝对位置并打印structsensor_valueposition;if(sensor_sample_fetch(qdec_dev)0){if(sensor_channel_get(qdec_dev,SENSOR_CHAN_ROTATION,position)0){LOG_INF(TIM3 IRQ Triggered! Current Pos: %d\n,position.val1);}}}K_SEM_DEFINE(qdec_sem,0,1);staticatomic_tisr_trigger_count0;// 1. CC 中断服务例程 (ISR)voidqdec_cc_isr(constvoid*param){// 清除 CC1 中断标志位假设使用通道1LL_TIM_ClearFlag_CC1(TIM3);// 更新下一次比较值实现周期性触发uint32_tcurrent_ccrLL_TIM_OC_GetCompareCH1(TIM3);LL_TIM_OC_SetCompareCH1(TIM3,current_ccrPULSES_PER_INTERRUPT);atomic_inc(isr_trigger_count);// 释放信号量k_sem_give(qdec_sem);}staticconststructgpio_dt_specmy_signalGPIO_DT_SPEC_GET(DT_NODELABEL(my_signal),gpios);/* 2. 定义 GPIO 中断回调结构体 */staticstructgpio_callbackgpio_cb_data;/* 3. GPIO 中断服务例程 (ISR) */staticvoidgpio_signal_handler(conststructdevice*dev,structgpio_callback*cb,uint32_tpins){LOG_INF(PB10 Interrupt Triggered! Pin mask: 0x%x\n,pins);}voidled_thread_entry4(void*p1,void*p2,void*p3){structsensor_valueposition;// 必须保留这一行声明int32_tlast_raw_pos0;int32_tabsolute_pos0;// 用于记录真实的绝对位置不受16位溢出影响int32_tdelta;/* 检查设备是否就绪 */if(!device_is_ready(my_signal.port)){LOG_INF(GPIO port is not ready!\n);}/* 配置引脚为输入模式 */intretgpio_pin_configure_dt(my_signal,GPIO_INPUT);if(ret0){LOG_INF(Failed to configure PB10 as input: %d\n,ret);}/* * 配置中断触发条件 * GPIO_INT_EDGE_TO_ACTIVE 会根据设备树中的极性ACTIVE_LOW/HIGH * 自动选择下降沿或上升沿触发非常推荐这种跨平台写法。 */retgpio_pin_interrupt_configure_dt(my_signal,GPIO_INT_EDGE_TO_ACTIVE);if(ret0){LOG_INF(Failed to configure PB10 interrupt: %d\n,ret);}/* 初始化并添加回调函数 */gpio_init_callback(gpio_cb_data,gpio_signal_handler,BIT(my_signal.pin));retgpio_add_callback(my_signal.port,gpio_cb_data);if(ret0){LOG_INF(Failed to add GPIO callback: %d\n,ret);}LOG_INF(PB10 GPIO Input Interrupt Initialized.\n);// 检查设备是否绑定成功if(!device_is_ready(qdec_dev)){LOG_ERR(QDEC device is not ready!);}// 设置自动重装载值ARR计满 100 个脉冲触发一次// LL_TIM_SetAutoReload(TIM3, PULSES_PER_INTERRUPT - 1);// 设置初始比较值LL_TIM_OC_SetCompareCH1(TIM3,PULSES_PER_INTERRUPT);// 使能 CC1 中断LL_TIM_EnableIT_CC1(TIM3);// 使能 TIM3 更新中断// LL_TIM_EnableIT_UPDATE(TIM3);// 使用 Zephyr 的 IRQ_CONNECT 绑定中断// 确保 timers3 节点在你的设备树中定义了正确的中断号IRQ_CONNECT(DT_IRQN(DT_NODELABEL(timers3)),3,// 优先级0为最高建议设为 3 或 4qdec_cc_isr,// qdec_update_isr,NULL,0);// 使能该中断线irq_enable(DT_IRQN(DT_NODELABEL(timers3)));LOG_INF(TIM3 QDEC Update Interrupt Initialized (Threshold: %d)\n,PULSES_PER_INTERRUPT);LOG_INF(Hardware QDEC initialized successfully.);while(1){k_sem_take(qdec_sem,K_FOREVER);// 读取并打印中断触发次数uint32_tcountatomic_get(isr_trigger_count);if(sensor_sample_fetch(qdec_dev)0){if(sensor_channel_get(qdec_dev,SENSOR_CHAN_ROTATION,position)0){int32_tcurrent_rawposition.val1;// 1. 使用 int16_t 计算差值完美解决 16位定时器溢出回绕问题delta(int16_t)(current_raw-last_raw_pos);// 2. 【软件消抖】如果单次采样差值过大例如 5说明是机械抖动引起的跳变直接忽略// 正常的旋钮旋转在 100ms 内不可能瞬间转过几十个脉冲if(delta5||delta-5){// LOG_WRN(Ignored bounce: %d, delta); // 可选打印警告continue;}// 3. 累加到绝对位置if(delta!0){absolute_posdelta;last_raw_poscurrent_raw;LOG_INF(ISR Count: %d | Abs Pos: %d, Delta: %d,count,absolute_pos,delta);}}}}}结果定时器输出pwm官方也有简单配置一下app.overlaytimers4{statusokay;pwm4c1:pwm{compatiblest,stm32-pwm;statusokay;pinctrl-0tim4_ch1_pd12;pinctrl-namesdefault;};};aliases{pwmgpiod12pwm4c1;};prj.conf CONFIG_PWM_CAPTUREy CONFIG_PWM_STM32y CONFIG_PWMy 主程序#includezephyr/drivers/pwm.h#includezephyr/drivers/counter.h#definePWM_NODEDT_ALIAS(pwmgpiod12)staticconststructdevice*constpwm_devDEVICE_DT_GET(PWM_NODE);#definePWM_CHANNEL1// 使用通道0即上面设备树中配置的第一个pwm输出引脚#definePWM_PERIOD_NS(PWM_USEC(100))// 100 µs - 100,000 ns#definePWM_PULSE_NS(PWM_USEC(80))// 80 µs - 80,000 ns占空比 80000/100000 80%#definePWM_FLAGSPWM_POLARITY_NORMALdevice_is_ready(pwm_dev);pwm_set(pwm_dev,PWM_CHANNEL,PWM_PERIOD_NS,PWM_PULSE_NS,PWM_FLAGS);效果如下