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

小熊派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

猜你喜欢

转载自blog.csdn.net/qq_38113006/article/details/108962766