学习目标

  1. 能够理解bsp开发
  2. 能够基于需求进行bsp驱动封装

    学习内容

    需求介绍

    009.png
    开发版中有4个灯,现在需要用4个灯显示充电情况:

  3. 开始充电时,需要呈现出流水灯闪烁

  4. 4盏灯表示当前的电量
  5. 充电流水灯起始位置是当前电量,全部点亮后,再次从当前电量位置进入流水灯效果
  6. 结束充电时,关闭充电显示,当前电量进行闪烁3次,然后熄灭。

充电.gif

现实问题

  1. 产品最终电路板还没画好,目前只有产品所使用的芯片对应的开发板。
  2. ADC功能是别人开发,还没完成。
  3. 老板要求,如果开发板好了,要尽快完成工作。

    需求分析

    要啥没啥,还得尽快完成。盘点手头有的东西,开发板。构建测试案例逻辑,方便后续移植。
    测试案例设计:

  4. 准备工作,4个灯,3个按钮

  5. 按钮1按下时,模拟开始充电
  6. 按钮2按下时,模拟停止充电
  7. 按钮3按下时,模拟电量增加。

如果测试方案通过,基本上功能完成,那么后续其他人工作完成后,只需要对接以下逻辑:

  1. 灯对应的引脚和最终设计的电路板引脚进行校准
  2. 开始充电
  3. 电量变化时,更新电量
  4. 结束充电

编码实现分析:

  1. 需要把4个灯作为一个业务逻辑整体,完成一套关于电池电量显示的驱动
  2. 需要抽象出业务逻辑,转换为函数实现

    测试案例构建

    174.png175.png
  • PD0作为:开始按钮
  • PD1作为:停止按钮
  • PD5作为:电量更新按钮

按钮逻辑构建

  1. static void GPIO_config(){
  2. // LED1 测试按钮
  3. // rcu时钟
  4. // rcu_periph_clock_enable(RCU_GPIOE);
  5. // // GPIO
  6. // gpio_mode_set(GPIOE,GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, GPIO_PIN_3);
  7. // gpio_output_options_set(GPIOE, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_3);
  8. // PD0 开始充电
  9. // PD1 结束充电
  10. // PD5 电量变化
  11. // rcu时钟
  12. rcu_periph_clock_enable(RCU_GPIOD);
  13. // 配置GPIO模式
  14. gpio_mode_set(GPIOD,GPIO_MODE_INPUT, GPIO_PUPD_PULLUP, GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_5);
  15. }
  1. int main(void)
  2. {
  3. systick_config();
  4. GPIO_config();
  5. Battery_led_init();
  6. uint32_t cnt = 0;
  7. uint8_t power = 1;
  8. FlagStatus pre_key1 = SET;
  9. FlagStatus pre_key2 = SET;
  10. FlagStatus pre_key3 = SET;
  11. while(1) {
  12. FlagStatus cur_key1 = gpio_input_bit_get(GPIOD, GPIO_PIN_0);
  13. if (cur_key1 != pre_key1){
  14. if (cur_key1 == RESET){
  15. // 按下
  16. }else {
  17. // 抬起, 开始充电
  18. Battery_led_start(power);
  19. }
  20. pre_key1 = cur_key1;
  21. }
  22. FlagStatus cur_key2 = gpio_input_bit_get(GPIOD, GPIO_PIN_1);
  23. if (cur_key2 != pre_key2){
  24. if (cur_key2 == RESET){
  25. // 按下
  26. }else {
  27. // 抬起, 停止充电
  28. Battery_led_stop();
  29. }
  30. pre_key2 = cur_key2;
  31. }
  32. FlagStatus cur_key3 = gpio_input_bit_get(GPIOD, GPIO_PIN_5);
  33. if (cur_key3 != pre_key3){
  34. if (cur_key3 == RESET){
  35. // 按下
  36. }else {
  37. // 抬起, 增加电量
  38. power++;
  39. if(power > 4) power = 4;
  40. Battery_led_update(power);
  41. }
  42. pre_key3 = cur_key3;
  43. }
  44. if(++cnt % 25 == 0){ // 20ms * 25 = 500ms
  45. Battery_led_loop();
  46. }
  47. delay_1ms(20);
  48. }
  49. }

BSP驱动构建

接口定义

  1. 驱动初始化,属于标配
  2. 业务相关的操作行为抽象化
  3. 时序问题

    1. void Battery_led_int();

    具体的业务抽象行为

    1. void Battery_led_start(uint8_t power);
    1. void Battery_led_stop();
    1. void Battery_led_update(uint8_t power);

    在涉及到需要控制时间的问题时,我们通常有以下做法:

  4. 自己主动调用 delay来进行延时

  5. 使用统一的延时,到达自己的时间点就去执行

自己调用delay 不利于后续的移植。
采用统一时钟,方便移植,也方便时间片统一调度管理

业务实现

  1. 采用bsp独立驱动进行开发
  2. 状态管理,通过status记录当前状态。
  3. 电量记录,记录当前电量。
  4. 充电闪烁计数,记录当前的闪烁的值。 ```c

    ifndef BSP_BATTERY_LED_H

    define BSP_BATTERY_LED_H

include “gd32f4xx.h”

include “systick.h”

define LED1_RCU RCU_GPIOE

define LED1_GPIO GPIOE, GPIO_PIN_3

define LED2_RCU RCU_GPIOD

define LED2_GPIO GPIOD, GPIO_PIN_7

define LED3_RCU RCU_GPIOG

define LED3_GPIO GPIOG, GPIO_PIN_3

define LED4_RCU RCU_GPIOA

define LED4_GPIO GPIOA, GPIO_PIN_5

define LED1_ON() gpio_bit_set(LED1_GPIO)

define LED1_OFF() gpio_bit_reset(LED1_GPIO)

define LED2_ON() gpio_bit_set(LED2_GPIO)

define LED2_OFF() gpio_bit_reset(LED2_GPIO)

define LED3_ON() gpio_bit_set(LED3_GPIO)

define LED3_OFF() gpio_bit_reset(LED3_GPIO)

define LED4_ON() gpio_bit_set(LED4_GPIO)

define LED4_OFF() gpio_bit_reset(LED4_GPIO)

void Battery_led_init();

void Battery_led_start(uint8_t power);

void Battery_led_loop();

void Battery_led_update(uint8_t power);

void Battery_led_stop();

endif

  1. ```c
  2. #include "bsp_battery_led.h"
  3. static void LED_GPIO_init(rcu_periph_enum periph, uint32_t gpio_periph, uint32_t pin) {
  4. // 时钟
  5. rcu_periph_clock_enable(periph);
  6. // GPIO
  7. gpio_mode_set(gpio_periph,GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, pin);
  8. gpio_output_options_set(gpio_periph, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, pin);
  9. }
  10. void Battery_led_init() {
  11. // 初始化LED为推挽输出模式
  12. // PE3 PD7 PG3 PA5
  13. LED_GPIO_init(LED1_RCU, LED1_GPIO);
  14. LED_GPIO_init(LED2_RCU, LED2_GPIO);
  15. LED_GPIO_init(LED3_RCU, LED3_GPIO);
  16. LED_GPIO_init(LED4_RCU, LED4_GPIO);
  17. }
  18. // 0: stop , 1: start, 2: stoping
  19. int status = 0;
  20. // 当前电量
  21. static int current_power = 0;
  22. // 充电显示灯位置
  23. static int show_power = 0;
  24. void Battery_led_start(uint8_t power) {
  25. // power [0, 4]
  26. current_power = power;
  27. show_power = current_power;
  28. status = 1;
  29. }
  30. void Battery_led_loop() {
  31. if (status == 0) {
  32. LED1_OFF();
  33. LED2_OFF();
  34. LED3_OFF();
  35. LED4_OFF();
  36. } else if (status == 1) {
  37. show_power++;
  38. if (show_power > 4) {
  39. show_power = current_power;
  40. }
  41. // 亮前show_power个灯
  42. show_power >= 1 ? LED1_ON() : LED1_OFF();
  43. show_power >= 2 ? LED2_ON() : LED2_OFF();
  44. show_power >= 3 ? LED3_ON() : LED3_OFF();
  45. show_power >= 4 ? LED4_ON() : LED4_OFF();
  46. } else if (status == 2) {
  47. uint8_t i;
  48. // 当前电量闪三次
  49. for( i = 0; i < 3; i++) {
  50. LED1_OFF();
  51. LED2_OFF();
  52. LED3_OFF();
  53. LED4_OFF();
  54. delay_1ms(250);
  55. current_power >= 1 ? LED1_ON() : LED1_OFF();
  56. current_power >= 2 ? LED2_ON() : LED2_OFF();
  57. current_power >= 3 ? LED3_ON() : LED3_OFF();
  58. current_power >= 4 ? LED4_ON() : LED4_OFF();
  59. delay_1ms(250);
  60. }
  61. status = 0;
  62. }
  63. }
  64. void Battery_led_update(uint8_t power) {
  65. current_power = power;
  66. }
  67. void Battery_led_stop() {
  68. status = 2;
  69. }

练习

  1. 实现充电led驱动实现