GD32F303实战 ----- 定时器PWM驱动LED实现渐变调光
1. 从零开始理解PWM调光想象一下老式台灯的旋钮开关旋转角度越大灯光越亮——这种通过调节通电时间比例来控制亮度的原理就是PWM脉冲宽度调制技术的雏形。在GD32F303开发板上我们通过定时器产生精确的方波信号用数字方式完美复现了这个过程。PWM本质上是通过快速开关电路来控制平均功率。比如设置周期为10ms的PWM信号当高电平持续3ms时LED实际获得的能量就是30%亮度。这里涉及三个关键参数周期Period完整一次开关的时间长度占空比Duty Cycle高电平持续时间占周期的百分比频率Frequency1秒内完成的周期数频率1/周期在呼吸灯场景中我们通过动态调整占空比实现亮度渐变。GD32F303的定时器外设就像精准的电子节拍器TIMER0的CH0通道对应PA8引脚正好驱动板载LED。接下来我会手把手带你完成从硬件配置到渐变算法的完整实现。2. 硬件配置与定时器初始化2.1 引脚映射与时钟配置首先确认硬件连接根据开发板原理图LED0连接在PA8引脚。查阅GD32F303数据手册可知这个引脚复用为TIMER0的通道0输出。初始化时需要开启三个时钟源GPIOA总线时钟控制PA8引脚复用功能时钟启用引脚复用模式TIMER0外设时钟驱动定时器void gpio_config(void) { // 开启GPIOA和复用功能时钟 rcu_periph_clock_enable(RCU_GPIOA); rcu_periph_clock_enable(RCU_AF); // 配置PA8为复用推挽输出50MHz速度 gpio_init(GPIOA, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_8); }2.2 定时器参数计算假设我们需要1kHz的PWM频率周期1ms系统时钟为120MHz。通过预分频器prescaler和自动重装载值period来划分时间预分频器设为119将时钟分频为1MHz120MHz/(1191)自动重装载值设为999得到1kHz频率1MHz/(9991)void timer_config(void) { timer_oc_parameter_struct timer_ocintpara; timer_parameter_struct timer_initpara; // 定时器基础配置 timer_initpara.prescaler 119; // 预分频值 timer_initpara.period 999; // 自动重装载值 timer_initpara.alignedmode TIMER_COUNTER_EDGE; timer_initpara.counterdirection TIMER_COUNTER_UP; timer_initpara.clockdivision TIMER_CKDIV_DIV1; timer_init(TIMER0, timer_initpara); // PWM通道配置 timer_ocintpara.outputstate TIMER_CCX_ENABLE; timer_ocintpara.ocpolarity TIMER_OC_POLARITY_HIGH; timer_channel_output_config(TIMER0, TIMER_CH_0, timer_ocintpara); // 初始占空比设为0% timer_channel_output_pulse_value_config(TIMER0, TIMER_CH_0, 0); timer_channel_output_mode_config(TIMER0, TIMER_CH_0, TIMER_OC_MODE_PWM0); // 启动定时器 timer_auto_reload_shadow_enable(TIMER0); timer_enable(TIMER0); }3. 实现亮度渐变算法3.1 基础线性渐变最简单的渐变方式是线性增减占空比。设置一个方向标志位当亮度达到上限时反转变化方向void linear_breathing(void) { uint16_t duty 0; uint8_t dir 1; // 1递增0递减 while(1) { delay_1ms(10); // 10ms更新一次 if(dir) { duty 10; if(duty 1000) dir 0; } else { duty - 10; if(duty 0) dir 1; } timer_channel_output_pulse_value_config(TIMER0, TIMER_CH_0, duty); } }这种直线变化虽然简单但人眼对亮度的感知是非线性的遵循史蒂文斯幂定律实际看起来会显得变化不均匀。3.2 指数曲线优化更自然的渐变应该采用指数曲线。我们可以预先计算好亮度表或者实时计算// 指数曲线亮度表256级 const uint16_t gamma_table[256] {0, 1, 2, ..., 1000}; void gamma_breathing(void) { uint8_t index 0; uint8_t dir 1; while(1) { delay_1ms(15); if(dir) { if(index 255) dir 0; } else { if(--index 0) dir 1; } timer_channel_output_pulse_value_config(TIMER0, TIMER_CH_0, gamma_table[index]); } }实测发现当PWM频率高于100Hz时周期10ms人眼就看不到闪烁现象。但为了获得更平滑的渐变效果建议将更新间隔控制在15-30ms之间。4. 高级技巧与性能优化4.1 使用DMA自动更新占空比当需要同时控制多个LED时CPU频繁介入会影响系统性能。这时可以配置DMA自动搬运占空比数据创建占空比数组存储渐变序列配置TIMER0的DMA请求源设置DMA循环模式自动更新CCR寄存器// DMA配置示例伪代码 void dma_config(void) { dma_init_struct.direction DMA_MEMORY_TO_PERIPH; dma_init_struct.memory_addr (uint32_t)duty_buffer; dma_init_struct.periph_addr (uint32_t)TIMER0-CH0CV; dma_init_struct.number 256; dma_init_struct.memory_inc DMA_MEMORY_INCREASE_ENABLE; dma_init_struct.circular_mode DMA_CIRCULAR_MODE_ENABLE; dma_init(DMA_CHx, dma_init_struct); timer_dma_enable(TIMER0, TIMER_DMA_CH0D); dma_channel_enable(DMA_CHx); }4.2 多通道同步控制GD32F303的定时器支持多通道同步输出非常适合RGB呼吸灯场景。只需在timer_config()中额外配置其他通道并保持相同的period值// 添加CH1配置假设接LED1 timer_channel_output_config(TIMER0, TIMER_CH_1, timer_ocintpara); timer_channel_output_pulse_value_config(TIMER0, TIMER_CH_1, 500); // 初始50%在更新占空比时可以分别控制各通道值实现彩色渐变效果。通过调整三个通道的相位差还能创造出更丰富的动态光效。
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2523422.html
如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!