[GD32L233C-START Review] Part 3 PWM driving breathing light
[Copy link]
The official lighting program is just a flowing light. Let's try something a little more complicated, a breathing light.
The simple principle of the breathing light is to use PWM wave to drive the LED, gradually increase the duty cycle, and then gradually reduce the duty cycle after reaching the maximum duty cycle. Due to the high frequency of PWM and the visual persistence effect of the human eye, the user cannot feel the flickering of the LED, but can only feel the changes in the brightness of the LED, thus achieving the breathing effect.
GigaDevice's GD32L233C series MCU does not directly provide PWM peripherals, but it can be implemented through timers. GigaDevice's GD32L233C series MCU provides four types of timers: general timer L0 (TIMER1, TIMER2), general timer L1 (TIMER8, TIMER11), basic timer (TIMER5, TIMER6) and low power timer LPTIMER. Among them, general timer L0 (TIMER1, TIMER2), general timer L1 (TIMER8, TIMER11) and low power timer LPTIMER can all realize programmable PWM function. Note: Basic timer (TIMER5, TIMER6) cannot realize programmable PWM function. In addition, the timer function of GigaDevice's GD32L233C series MCU is still very powerful and can be used for many purposes. The firmware library and example programs provided by GigaDevice are also very powerful.
This time we use the general timer L0 (TIMER2) to implement programmable PWM, and simply use the edge-aligned type. TIMER2 output channel 1 is connected to PA7 (PA7 on the evaluation board just drives LED1). By the way, the basic timers (TIMER5, TIMER6) cannot implement PWM functions because they have no output channels.
First, set the PA7 function connected to the PWM peripheral ----TIMER2 output channel 1:
void gpio_config(void)
{
rcu_periph_clock_enable(RCU_GPIOA);
/* TIMER1 GPIO */
gpio_mode_set(GPIOA, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_7 );
gpio_output_options_set(GPIOA, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_7);
gpio_af_set(GPIOA, GPIO_AF_1, GPIO_PIN_7);
}
Next, set the PWM function of TIMER2: It seems a bit complicated. But read more example programs provided by GigaDevice, there are many things you can refer to.
void timer_config(void)
{
timer_oc_parameter_struct timer_ocinitpara;
timer_parameter_struct timer_initpara;
/* enable the peripherals clock */
rcu_periph_clock_enable(RCU_TIMER2);
/* deinit a TIMER */
timer_deinit(TIMER2);
/* initialize TIMER init parameter struct */
timer_struct_para_init(&timer_initpara);
/* TIMER2 configuration */
timer_initpara.prescaler = 63;
timer_initpara.alignedmode = TIMER_COUNTER_EDGE;
timer_initpara.counterdirection = TIMER_COUNTER_UP;
timer_initpara.period = 9999;
timer_initpara.clockdivision = TIMER_CKDIV_DIV1;
timer_init(TIMER2, &timer_initpara);
/* initialize TIMER channel output parameter struct */
timer_channel_output_struct_para_init(&timer_ocinitpara);
/* configure TIMER channel output function */
timer_ocinitpara.outputstate = TIMER_CCX_ENABLE;
timer_ocinitpara.ocpolarity = TIMER_OC_POLARITY_HIGH;
timer_channel_output_config(TIMER2, TIMER_CH_1, &timer_ocinitpara);
timer_channel_output_pulse_value_config(TIMER2, TIMER_CH_1, pwm_pulse_value);
/* CH0 configuration in OC timing mode */
timer_channel_output_mode_config(TIMER2, TIMER_CH_1, TIMER_OC_MODE_PWM0);
timer_enable(TIMER2);
}
The next step is to implement the main program: adjust the PWM duty cycle parameters in an endless loop (first gradually increase, then gradually decrease), and then delay for 5ms.
#include "gd32l23x.h"
#include "gd32l233c_start.h"
#include "systick.h"
uint16_t T_ms = 0,pwm_pulse_value = 20;
void gpio_config(void);
void nvic_config(void);
void timer_config(void);
int main(void)
{
systick_config();
gpio_config();
timer_config();
while(1)
{
T_ms ++;
if(T_ms < 400)
{
pwm_pulse_value = pwm_pulse_value + 10;
}
else
{
pwm_pulse_value = pwm_pulse_value - 10;
}
if(T_ms == 800)
{
T_ms = 0;
pwm_pulse_value = 20;
}
timer_channel_output_pulse_value_config(TIMER2, TIMER_CH_1, pwm_pulse_value);
delay_1ms(5);
}
}
Compile and download, power on and it runs smoothly.
But isn't it a bit low-class to use delay plus infinite loop to update the duty cycle parameters? We can simply put the duty cycle update into the timer interrupt function and use the basic timer (TIMER5) to implement a 5ms timer (add the following code to the timer configuration program void gpio_config(void) above): (Note that two global variables are defined: uint16_t T_ms = 0, pwm_pulse_value = 20; )
/* enable the peripherals clock */
rcu_periph_clock_enable(RCU_TIMER5);
/* deinit a TIMER */
timer_deinit(TIMER5);
/* initialize TIMER init parameter struct */
timer_struct_para_init(&timer_initpara);
/* TIMER5 configuration */
timer_initpara.prescaler = 63;
timer_initpara.alignedmode = TIMER_COUNTER_EDGE;
timer_initpara.counterdirection = TIMER_COUNTER_UP;
timer_initpara.period = 5000; //5ms
timer_initpara.clockdivision = TIMER_CKDIV_DIV1;
timer_init(TIMER5, &timer_initpara);
/* auto-reload preload enable */
timer_auto_reload_shadow_enable(TIMER5);
/* clear channel 0 interrupt bit */
timer_interrupt_flag_clear(TIMER5, TIMER_INT_FLAG_UP);
/* channel 0 interrupt enable */
timer_interrupt_enable(TIMER5, TIMER_INT_UP);
/* enable a TIMER */
timer_enable(TIMER5);
Another thing is to add the TIMER5 interrupt program void TIMER5_IRQHandler(void) in the gd32l23x_it.c file:
void TIMER5_IRQHandler(void)
{
if(SET == timer_interrupt_flag_get(TIMER5, TIMER_INT_FLAG_UP))
{
/* clear channel 1 interrupt bit */
timer_interrupt_flag_clear(TIMER5, TIMER_INT_FLAG_UP);
T_ms ++;
if(T_ms < 600)
{
pwm_pulse_value = pwm_pulse_value + 2;
}
else
{
pwm_pulse_value = pwm_pulse_value - 2;
}
if(T_ms == 1200)
{
T_ms = 0;
pwm_pulse_value = 4;
}
timer_channel_output_pulse_value_config(TIMER2, TIMER_CH_1, pwm_pulse_value);
}
}
Of course, the main program also needs to be modified:
int main(void)
{
systick_config();
gpio_config();
nvic_config();
timer_config();
while(1)
{
}
}
void nvic_config(void)
{
nvic_irq_enable(TIMER5_IRQn, 0);
}
Compile and download, power on and run smoothly. Perfect! Take a look at the effect:
|