文章目录
为什么要写这篇
因为功能的需要,要实现六个呼吸灯可独立控制最大亮度和呼吸时间,可使用的硬件为六路PWM和一个定时器,"独立控制"因此需要使用硬件定时器分出六个软件定时器,每个软件定时器可设置自己的定时时间,在自己的定时回调函数中设置对应灯引脚的PWM占空比。涉及到很多变量,最初的版本(幼嫩)写了六个回调函数,六个软件定时器独立设置定时时间,这种重复性太强的代码不能提升我技术能力,明明有更简洁的版本可以实现,减少重复代码,变量的处理整合在一起,成为一个模块。出了问题,一套逻辑只需要改一次就可以,而不是六次,从大量变量中解放出来。这好像牵扯到什么工厂模式。
有哪些收获
有三点收获
- 将一个有static 静态变量的函数变成了一个可重入的函数
- 函数指针常量 和 函数指针变量分清楚了
- 通过实例了解到了什么时候需要定时回调函数传入参数
新的认识
通过这一次,感觉自己对C语言和数据结构的运用更进了一步,对程序的稳定性更有了一些信心,现在比较欠缺的是关于函数声明的
- 函数对接,每位工程师的命名方式并不相同,在对接的时候会产生一些摩擦。
- 每个源文件的外部接口函数很多,也比较杂,如何分类。
最初版本的代码实现方式
定时回调函数
/**
* white_led_1_pwm_callback
* @brief White led 1 timer callback: set PWM duty cycle to control white led 1 brightness
* @note 1. If brightness is re controlled, the duty cycle will be cleared to 0
* @note 2. If set the brightness to 0, the duty cycle will be 0
* @note 3. state_change_flag 0: from dark to bright, maximum value : set_brightness.white_led_1_brightness
* @note 4. state_change_flag 1: from bright to dark, minimum value : 0
* @note 5. set PWM duty cycle to control white led 1 brightness
*/
void
white_led_1_pwm_callback(void)
{
static uint16_t duty_value = 0;
static uint8_t state_change_flag = 0;
if(flag_breath_white_1)
{
flag_breath_white_1 = FALSE;
duty_value = 0;
state_change_flag = 0;
}else
{
/* no code */
}
if(set_brightness.white_led_1_brightness == 0)
{
duty_value = 0;
}else
{
if(state_change_flag == 0)
{
duty_value++;
if(duty_value >= set_brightness.white_led_1_brightness)
{
state_change_flag = 1;
}
}else if(state_change_flag == 1)
{
duty_value--;
if(duty_value < 1)
{
state_change_flag = 0;
}
}
}
pwm_set_led_n_brightness(PWM_CHANNEL_WHITE_LED_ID(1), WHITE_LED_ID(1), duty_value);
}
void
white_led_2_pwm_callback(void)
{
static uint16_t duty_value = 0;
static uint8_t state_change_flag = 0;
if(flag_breath_white_2)
{
flag_breath_white_2 = FALSE;
duty_value = 0;
state_change_flag = 0;
}else
{
/* no code */
}
if(set_brightness.white_led_2_brightness == 0)
{
duty_value = 0;
}else
{
if(state_change_flag == 0)
{
duty_value++;
if(duty_value >= set_brightness.white_led_2_brightness)
{
state_change_flag = 1;
}
}else if(state_change_flag == 1)
{
duty_value--;
if(duty_value < 1)
{
state_change_flag = 0;
}
}
}
pwm_set_led_n_brightness(PWM_CHANNEL_WHITE_LED_ID(2), WHITE_LED_ID(2), duty_value);
}
void
white_led_3_pwm_callback(void)
{
static uint16_t duty_value = 0;
static uint8_t state_change_flag = 0;
if(flag_breath_white_3)
{
flag_breath_white_3 = FALSE;
duty_value = 0;
state_change_flag = 0;
}else
{
/* no code */
}
if(set_brightness.white_led_3_brightness == 0)
{
duty_value = 0;
}else
{
if(state_change_flag == 0)
{
duty_value++;
if(duty_value >= set_brightness.white_led_3_brightness)
{
state_change_flag = 1;
}
}else if(state_change_flag == 1)
{
duty_value--;
if(duty_value < 1)
{
state_change_flag = 0;
}
}
}
pwm_set_led_n_brightness(PWM_CHANNEL_WHITE_LED_ID(3), WHITE_LED_ID(3), duty_value);
}
设置定时函数
/**
* rgb_pwm_breath_set
* @brief set up 6 white led breathe info
* @param led_breath_time : timer interval, Unit : 0.51 second
* @note 1. If set timer interval to 0, return
* @note 2. Clear the original status information of the 6 white led
* @note 3. PWM init
* @note 4. According to led_breath_time and set_brightness.white_led_1_brightness set timer interval
*/
void
rgb_pwm_breath_set(uint8_t led_breath_time)
{
if(!led_breath_time)
{
return;
}
reset_to_gpio();
pwm_set_clk(CLOCK_SYS_CLOCK_HZ, CLOCK_SYS_CLOCK_HZ);
if(set_brightness.white_led_1_brightness)
{
uint16_t used_white_1_set_time = (led_breath_time * 510)/(set_brightness.white_led_1_brightness * 2);
flag_breath_white_1 = TRUE;
software_timer_set(&timer_white_led_1, white_led_1_pwm_callback, used_white_1_set_time, TRUE);
}
if(set_brightness.white_led_2_brightness)
{
uint16_t used_white_2_set_time = (led_breath_time * 510)/(set_brightness.white_led_2_brightness * 2);
flag_breath_white_2 = TRUE;
software_timer_set(&timer_white_led_2, white_led_2_pwm_callback, used_white_2_set_time, TRUE);
}
if(set_brightness.white_led_3_brightness)
{
uint16_t used_white_3_set_time = (led_breath_time * 510)/(set_brightness.white_led_3_brightness * 2);
flag_breath_white_3 = TRUE;
software_timer_set(&timer_white_led_3, white_led_3_pwm_callback, used_white_3_set_time, TRUE);
}
}
它们之间有太强的相似性,但是要将它们整合在一起并不是一个简单的事情。 涉及到局部变量,静态变量,结构体变量,位域,宏定义 ,结构体指针和函数指针。但这是一件很有必要的事情,相当于将所有的数据进行了一次整合,对程序代码进行了一次优化,程序逻辑更清晰了,对变量的操作少了,更容易维护了。
怎么优化
我想到了软件定时器的那种链表查询方式,想到了表驱动,因为这个灯的数目是一定的,相对于链表实现,数组实现更好一些,实现起来简单。
通过这一次,新增了两个结构体和一个结构体数组,以及去掉了一个结构体,一个位域。
去掉的结构体和位域
/* Maximum set brightness of white led */
typedef struct
{
uint8_t white_led_1_brightness;
uint8_t white_led_2_brightness;
uint8_t white_led_3_brightness;
uint8_t white_led_4_brightness;
uint8_t white_led_5_brightness;
uint8_t white_led_6_brightness;
}White_Led_Brightness;
/* LED mode start flag bit */
union rgb_flag
{
struct mode
{
unsigned bit0 : 1;
unsigned bit1 : 1;
unsigned bit2 : 1;
unsigned bit3 : 1;
unsigned bit4 : 1;
unsigned bit5 : 1;
unsigned bit6 : 1;
unsigned bit7 : 1;
}bit_type;
unsigned char byte;
}FLAG_RGB;
#define flag_mode FLAG_RGB.byte
#define flag_blink_white FLAG_RGB.bit_type.bit0
#define flag_breath_white_1 FLAG_RGB.bit_type.bit1
#define flag_breath_white_2 FLAG_RGB.bit_type.bit2
#define flag_breath_white_3 FLAG_RGB.bit_type.bit3
#define flag_breath_white_4 FLAG_RGB.bit_type.bit4
#define flag_breath_white_5 FLAG_RGB.bit_type.bit5
#define flag_breath_white_6 FLAG_RGB.bit_type.bit6
优化后的代码实现方式
一些相关设置
/* Configure white led pin */
#define WHITE_LED_ID_1 GPIO_PB5
#define WHITE_LED_ID_2 GPIO_PB4
#define WHITE_LED_ID_3 GPIO_PC2
#define WHITE_LED_ID(n) WHITE_LED_ID_##n
/* Configure PWM channel for controlling white led */
#define PWM_CHANNEL_WHITE_LED_ID_1 PWM5_ID
#define PWM_CHANNEL_WHITE_LED_ID_2 PWM4_ID
#define PWM_CHANNEL_WHITE_LED_ID_3 PWM0_ID
#define PWM_CHANNEL_WHITE_LED_ID(n) PWM_CHANNEL_WHITE_LED_ID_##n
typedef (*Breath_Callback)(FOR_CALLBACK* para_val);
void white_led_breath_callback(FOR_CALLBACK* para_val);
相关数据结构定义
typedef struct
{
uint8_t flag_led_breath_start; ///< Flag of breath start, used to clear variables : flag_state_change and u8_duty_value
uint8_t u8_led_brightness; ///< Store the maximum brightness setting
Pwm_Id pwm_id; ///< pwm channel ID
GPIO_PinTypeDef pwm_pin; ///< Pin configured in PWM mode
uint8_t flag_state_change; ///< Private, protected, flag of state change, state : darkest -> brightest or brightest -> darkest
uint16_t u8_duty_value; ///< Private, protected, PWM duty cycle
}FOR_CALLBACK;
typedef struct
{
Soft_Timer * timer; ///< point of soft timer
Breath_Callback timer_callback; ///< callback of soft timer
FOR_CALLBACK series_variables; ///< A series of variables used for callback
}Breath_Info;
Breath_Info array_breath_info[3];
相关初始化
void breath_info_array_regist(void)
{
array_breath_info[0].timer = &timer_white_led_1;
array_breath_info[0].timer_callback = white_led_breath_callback;
array_breath_info[0].series_variables.flag_led_breath_start = 0;
array_breath_info[0].series_variables.u8_led_brightness = 0;
array_breath_info[0].series_variables.pwm_id = PWM_CHANNEL_WHITE_LED_ID(1);
array_breath_info[0].series_variables.pwm_pin = WHITE_LED_ID(1);
array_breath_info[1].timer = &timer_white_led_2;
array_breath_info[1].timer_callback = white_led_breath_callback;
array_breath_info[1].series_variables.flag_led_breath_start = 0;
array_breath_info[1].series_variables.u8_led_brightness = 0;
array_breath_info[1].series_variables.pwm_id = PWM_CHANNEL_WHITE_LED_ID(2);
array_breath_info[1].series_variables.pwm_pin = WHITE_LED_ID(2);
array_breath_info[2].timer = &timer_white_led_3;
array_breath_info[2].timer_callback = white_led_breath_callback;
array_breath_info[2].series_variables.flag_led_breath_start = 0;
array_breath_info[2].series_variables.u8_led_brightness = 0;
array_breath_info[2].series_variables.pwm_id = PWM_CHANNEL_WHITE_LED_ID(3);
array_breath_info[2].series_variables.pwm_pin = WHITE_LED_ID(3);
}
设置呼吸时间
根据某种功能要求,先设置亮度,再设置呼吸时间。
设置亮度 : 将亮度数值传入 array_breath_info[i].series_variables.u8_led_brightness 变量
设置呼吸时间 :
/**
* test_rgb_pwm_breath_set
* @brief set up 6 white led breathe info
* @param led_breath_time : timer interval, Unit : 0.51 second
* @note 1. If set timer interval to 0, return
* @note 2. Clear the original status information of the 6 white led
* @note 3. PWM init
* @note 4. According to led_breath_time and set_brightness.white_led_1_brightness set timer interval
*/
void test_rgb_pwm_breath_set(uint8_t led_breath_time)
{
if(!led_breath_time)
{
return;
}
for(uint8_t i = 0; i < sizeof(array_breath_info) / sizeof(array_breath_info[0]); i++)
{
if(array_breath_info[i].series_variables.u8_led_brightness)
{
uint16_t used_white_set_time = (led_breath_time * 510)/(array_breath_info[i].series_variables.u8_led_brightness * 2);
array_breath_info[i].series_variables.flag_led_breath_start = 1;
software_timer_set(array_breath_info[i].timer, array_breath_info[i].timer_callback, used_white_set_time, TRUE, &array_breath_info[i].series_variables);
}
}
}
呼吸的定时回调
/**
* white_led_breath_callback
* @brief Timer callback function for white LED breathing
* @param para_val, para description: Variable pointer
*/
void
white_led_breath_callback(FOR_CALLBACK* para_val)
{
if(para_val->flag_led_breath_start)
{
para_val->flag_led_breath_start = 0;
para_val->u8_duty_value = 0;
para_val->flag_state_change = 0;
}else
{
/* no code */
}
if(para_val->u8_led_brightness == 0)
{
para_val->u8_duty_value = 0;
}else
{
if(para_val->flag_state_change == 0)
{
para_val->u8_duty_value++;
if(para_val->u8_duty_value >= para_val->u8_led_brightness)
{
para_val->flag_state_change = 1;
}
}else if(para_val->flag_state_change == 1)
{
para_val->u8_duty_value--;
if(para_val->u8_duty_value < 1)
{
para_val->flag_state_change = 0;
}
}
}
pwm_set_led_n_brightness(para_val->pwm_id, para_val->pwm_pin, para_val->u8_duty_value);
}
代码用于实际的可能性
因为该代码是基于软件定时器Multi_timer的,要想将代码用于实际,需要软件定时器支持回调函数传入参数,而要想支持的话,需要修改模块底层实现,同时将设置定时器的接口多一个传入参数指针的形参。
从技术上完全可以实现,然后将代码用于实际,如果是自己写的代码可以,但如果是公司成熟(大量使用)的代码更多时候是不让改。
lue–;
if(para_val->u8_duty_value < 1)
{
para_val->flag_state_change = 0;
}
}
}
pwm_set_led_n_brightness(para_val->pwm_id, para_val->pwm_pin, para_val->u8_duty_value);
}
## 代码用于实际的可能性
  因为该代码是基于软件定时器Multi_timer的,要想将代码用于实际,需要软件定时器支持回调函数传入参数,而要想支持的话,需要修改模块底层实现,同时将设置定时器的接口多一个传入参数指针的形参。
  从技术上完全可以实现,然后将代码用于实际,如果是自己写的代码可以,但如果是公司成熟(大量使用)的代码更多时候是不让改。