小熊派gd32f303学习之旅(7)—使用PWM实现LED呼吸灯

    科技2025-08-14  7

    小熊派gd32f303学习之旅(7)—使用PWM实现LED呼吸灯

    一、前言

    通过查看gd32f30x的参考手册,可以知道gd32f303的通用定时器和高级定时器可以硬件生成PWM波,然后我们查看gd32f303xx的数据手册,可以看到,小熊派上连接LED的引脚PB0在定时器2的通道2上,这样我们就可以使用其PWM功能产生呼吸灯的效果。

    二、初始化定时器

    首先,编写定时器2的初始化函数,将其通道2设置为PWM模式0

    /* 通用定时器2初始化PWM函数 * 参数: psr:时钟预分频系数,预分频值=psr+1 arr:自动重装载值,计数次数=arr+1 * 返回值:无 */ void timer2_pwm_init(uint32_t psr, uint32_t arr, uint32_t duty) { /* 定义一个定时器初始化结构体 */ timer_parameter_struct timer_init_struct; /* 定义一个定时器输出比较参数结构体*/ timer_oc_parameter_struct timer_oc_init_struct; /* 开启定时器时钟 */ rcu_periph_clock_enable(RCU_TIMER2); /* 开启GPIOB时钟 */ rcu_periph_clock_enable(RCU_GPIOB); /* 开启复用功能时钟 */ rcu_periph_clock_enable(RCU_AF); /* 初始化PB0(TIMER2 CH2)为复用功能 */ gpio_init(GPIOB,GPIO_MODE_AF_PP,GPIO_OSPEED_50MHZ,GPIO_PIN_0); /* 初始化定时器 */ timer_deinit(TIMER2); timer_init_struct.prescaler = psr; /* 预分频系数 */ timer_init_struct.period = arr; /* 自动重装载值 */ timer_init_struct.alignedmode = TIMER_COUNTER_EDGE; /* 计数器对齐模式,边沿对齐 */ timer_init_struct.counterdirection = TIMER_COUNTER_UP; /* 计数器计数方向,向上 */ timer_init_struct.clockdivision = TIMER_CKDIV_DIV1; /* DTS时间分频值 */ timer_init_struct.repetitioncounter = 0; /* 重复计数器的值(定时器2无效)*/ timer_init(TIMER2, &timer_init_struct); /* PWM初始化 */ timer_oc_init_struct.outputstate = TIMER_CCX_ENABLE; /* 通道使能 */ timer_oc_init_struct.outputnstate = TIMER_CCXN_DISABLE; /* 通道互补输出使能(定时器2无效) */ timer_oc_init_struct.ocpolarity = TIMER_OC_POLARITY_HIGH; /* 通道极性 */ timer_oc_init_struct.ocnpolarity = TIMER_OCN_POLARITY_HIGH;/* 互补通道极性(定时器2无效)*/ timer_oc_init_struct.ocidlestate = TIMER_OC_IDLE_STATE_LOW;/* 通道空闲状态输出(定时器2无效)*/ timer_oc_init_struct.ocnidlestate = TIMER_OCN_IDLE_STATE_LOW;/*互补通道空闲状态输出(定时器2无效) */ timer_channel_output_config(TIMER2, TIMER_CH_2, &timer_oc_init_struct); /* 通道2占空比设置 */ timer_channel_output_pulse_value_config(TIMER2, TIMER_CH_2, duty); /* PWM模式0 */ timer_channel_output_mode_config(TIMER2,TIMER_CH_2,TIMER_OC_MODE_PWM0); /* 不使用输出比较影子寄存器 */ timer_channel_output_shadow_config(TIMER2,TIMER_CH_2,TIMER_OC_SHADOW_DISABLE); /* 自动重装载影子比较器使能 */ timer_auto_reload_shadow_enable(TIMER2); /* 使能Timer2 */ timer_enable(TIMER2); }

    三、编写mian函数

    接下来编写main函数,使其实现呼吸灯的功能。如下所示,将PWM波的周期设置为1ms,可改变的占空比步长为1us,每隔3ms改变一次LED亮度,使其呈现0-500-0的循环,另外,在LED熄灭时等待300ms,可以得到更好的呼吸灯效果。

    int main(void) { uint8_t dir = 0; uint32_t pwmval = 300; /* 配置系统时钟 */ systick_config(); /* 初始化LED */ // led_init(); /* 初始化USART0 */ uart0_init(115200); /* 初始化定时器5 */ timer2_pwm_init(120-1, 1000-1, 300); /* 通过串口打印 Hello world! */ u1_printf("Hello world! "); u1_printf("I am William. \r\n"); TIMER_CH2CV(TIMER2) = pwmval; while(1) { if(dir) pwmval++; // dir==1 pwmval递增 else pwmval--; // dir==0 pwmval递减 if( pwmval>500 ) dir=0; // pwmval到达500后,方向为递减 if( pwmval==0 ) dir=1; // pwmval递减到0后,方向改为递增 TIMER_CH2CV(TIMER2) = pwmval; // 修改比较值,修改占空比 if( pwmval==0 ) delay_1ms(300); delay_1ms(3); } }

    四、功能验证

    编译链接烧录到小熊派开发板,然后观察LED的情况,可以看到呼吸灯的效果

    五、代码优化

    现在,我们使用的是在while循环中完成的呼吸灯效果,但是这样,就很难让CPU去做其他的事了,所以,结合之前的定时器中断,将PWM波形的改变放到定时器中断中去。 首先,初始化定时器5为3ms产生中断

    /* 初始化定时器5,3ms */ timer5_init(1200-1, 300-1);

    然后修改定时器5 的中断服务函数如下

    /* TIMER5 中断服务函数 * 参数:无 * 返回值:无 */ void TIMER5_IRQHandler(void) { static uint8_t led_flag = 1, dir = 0;; static uint32_t pwmval = 300; if(timer_interrupt_flag_get(TIMER5, TIMER_INT_FLAG_UP)) { /* 清除TIMER5 中断标志位 */ timer_interrupt_flag_clear(TIMER5, TIMER_INT_FLAG_UP); if(led_flag == 1) { if(dir) pwmval++; /* dir==1 pwmval递增 */ else pwmval--; /* dir==0 pwmval递减 */ if( pwmval>500 ) dir=0; /* pwmval到达500后,方向为递减 */ if( pwmval==0 ) dir=1; /* pwmval递减到0后,方向改为递增 */ TIMER_CH2CV(TIMER2) = pwmval; /* 修改比较值,修改占空比 */ if( pwmval==0 ) { led_flag = 0; timer_counter_value_config(TIMER5, 30000-1); /* 设置为300ms */ } } else { led_flag = 1; timer_counter_value_config(TIMER5, 300-1); /* 设置为3ms */ } } }

    编译链接烧录到小熊派开发板,然后观察LED的情况,可以得到一样的效果

    六、附录

    完整代码我存放在码云,可以查看:https://gitee.com/william_william/BearPi-GD32F303RGT6.git 上一篇:小熊派gd32f303学习之旅(6)—使用基本定时器实现LED闪烁 下一篇:小熊派gd32f303学习之旅(8)— 使用软件模拟I2C读写EEPROM

    Processed: 0.013, SQL: 8