ARM32开发--GPIO--LED驱动开发

知不足而奋进 望远山而前行


目录

文章目录

前言

目标

内容

需求介绍

现实问题

需求分析

测试案例构建

BSP驱动构建

接口定义

业务实现

总结


前言

前言:

在这个快节奏的开发环境中,面对紧迫的项目截止日期和有限的资源,我们作为软件工程师需要具备快速解决问题的能力。本项目要求我们设计一个基于STM32的BSP驱动,以实现一个智能充电状态显示系统,该系统通过四个LED灯来展示充电状态和电量百分比。尽管在产品最终的电路板还未完成的情况下,我们只有开发板可以使用,但我们仍然面临着快速完成这个任务的压力。

在这个过程中,我们将采用模块化的方法来编写代码,以确保其可读性和可维护性。我们将定义一系列接口,以便其他开发者在将来可以轻松地与我们的驱动代码集成。我们还将在设计中考虑时间管理和延时问题,以确保LED灯的闪烁效果能够准确地反映充电状态。


目标

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

内容

需求介绍

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

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

现实问题

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

需求分析

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

测试案例设计:

  1. 准备工作,4个灯,3个按钮
  2. 按钮1按下时,模拟开始充电
  3. 按钮2按下时,模拟停止充电
  4. 按钮3按下时,模拟电量增加。

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

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

编码实现分析:

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

测试案例构建

  • PC0作为:开始按钮
  • PC1作为:停止按钮
  • PC2作为:电量更新按钮

按钮逻辑构建

static void GPIO_config(){	
	// PD0	开始充电
	// PD1 	结束充电
	// PD5	电量变化
	// rcu时钟
	rcu_periph_clock_enable(RCU_GPIOD);
	// 配置GPIO模式
	gpio_mode_set(GPIOD,GPIO_MODE_INPUT, GPIO_PUPD_PULLUP, GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_5);	
}


int main(void) {

	// 系统滴答定时器初始化
	systick_config();
	// GPIO初始化
	GPIO_config();

	// LED初始化
	Battery_led_init();

	FlagStatus pre_state0 = SET;// 默认高电平抬起
	FlagStatus pre_state1 = SET;// 默认高电平抬起
	FlagStatus pre_state2 = SET;// 默认高电平抬起
	uint32_t cnt = 0;
	uint8_t power = 1;
	while(1) {
		// PC0
		FlagStatus state0 = gpio_input_bit_get(GPIOC, GPIO_PIN_0);
		if (state0 != pre_state0){
			if(state0 == RESET){   // 当前低电平, 上一次为高电平,按下
				Battery_led_start(power);
			}
			pre_state0 = state0;	
		}
		// PC1
		FlagStatus state1 = gpio_input_bit_get(GPIOC, GPIO_PIN_1);
		if (state1 != pre_state1){
			if(state1 == RESET){   // 当前低电平, 上一次为高电平,按下
				Battery_led_stop();
			}
			pre_state1 = state1;	
		}
		// PC2
		FlagStatus state2 = gpio_input_bit_get(GPIOC, GPIO_PIN_2);
		if (state2 != pre_state2){
			if(state2 == RESET){   // 当前低电平, 上一次为高电平,按下
				//				Battery_led_turn_off(LED1);
				Battery_led_update(++power);
			}
			pre_state2 = state2;	
		}

		// 间隔50个10ms = 500ms执行一次led的状态更新
		if(++cnt % 50 == 0){
			Battery_led_loop();
		}

		delay_1ms(10);
	}

}
BSP驱动构建
接口定义
  1. 驱动初始化,属于标配

  2. 业务相关的操作行为抽象化

  3. 时序问题

    void Battery_led_int();

具体的业务抽象行为

void Battery_led_start(uint8_t power);

void Battery_led_stop();

void Battery_led_update(uint8_t power);

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

  1. 自己主动调用 delay来进行延时
  2. 使用统一的延时,到达自己的时间点就去执行

自己调用delay 不利于后续的移植。

采用统一时钟,方便移植,也方便时间片统一调度管理

业务实现
  1. 采用bsp独立驱动进行开发

  2. 状态管理,通过status记录当前状态。

  3. 电量记录,记录当前电量。

  4. 充电闪烁计数,记录当前的闪烁的值。

    #ifndef BSP_BATTERY_LED_H
    #define BSP_BATTERY_LED_H

    #include "gd32f4xx.h"

    #define LED1 1
    #define LED2 2
    #define LED3 3
    #define LED4 4

    void Battery_led_init();

    void Battery_led_turn_on(uint8_t led_index);

    void Battery_led_turn_off(uint8_t led_index);

    void Battery_led_turn(uint8_t led_index, uint8_t value);

    void Battery_led_start(uint8_t power);

    void Battery_led_loop();

    void Battery_led_update(uint8_t power);

    void Battery_led_stop();

    #endif

    #include "bsp_battery_led.h"
    #include "systick.h"

    // 声明gpio初始化所需参数的结构体
    typedef struct {
    rcu_periph_enum rcu;
    uint32_t port;
    uint32_t pin;
    } Led_GPIO_t;

    // 声明所有gpio对应参数的数组
    Led_GPIO_t g_gpio_list[] = {
    {RCU_GPIOC, GPIOC, GPIO_PIN_6}, // LED_SW
    {RCU_GPIOD, GPIOD, GPIO_PIN_8}, // LED1
    {RCU_GPIOD, GPIOD, GPIO_PIN_9}, // LED2
    {RCU_GPIOD, GPIOD, GPIO_PIN_10}, // LED3
    {RCU_GPIOD, GPIOD, GPIO_PIN_11}, // LED4
    };

    // 用于计算数组长度的宏
    #define MAX_LED_COUNT (sizeof(g_gpio_list) / sizeof(Led_GPIO_t))

    /**********************************************************

    • @brief LED GPIO初始化
      **********************************************************/
      static void GPIO_config(rcu_periph_enum rcu, uint32_t port, uint32_t pin) {
      // 初始化为推挽输出模式
      rcu_periph_clock_enable(rcu);
      gpio_mode_set(port, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, pin);
      gpio_output_options_set(port, GPIO_OTYPE_PP, GPIO_OSPEED_MAX, pin);
      }

    void Battery_led_init() {

    uint8_t count = MAX_LED_COUNT;
    for(uint8_t i = 0; i < count; i++) {
    Led_GPIO_t gpio = g_gpio_list[i];
    // 初始化
    GPIO_config(gpio.rcu,gpio.port, gpio.pin);
    // 默认全部拉高(关闭)
    gpio_bit_write(gpio.port, gpio.pin, SET);
    }

    // 总开关拉低(打开)
    gpio_bit_write(g_gpio_list[0].port, g_gpio_list[0].pin, RESET);
    }

    // 开灯
    void Battery_led_turn_on(uint8_t led_index) {
    Led_GPIO_t gpio = g_gpio_list[led_index];
    gpio_bit_write(gpio.port, gpio.pin, RESET);
    }

    // 关灯
    void Battery_led_turn_off(uint8_t led_index) {
    Led_GPIO_t gpio = g_gpio_list[led_index];
    gpio_bit_write(gpio.port, gpio.pin, SET);
    }

    /**********************************************************

    • @brief 设置灯亮灭
    • @param led_index LED索引
    • @param value 0亮,其他灭
    • @return
      **********************************************************/
      void Battery_led_turn(uint8_t led_index, uint8_t value) {
      Led_GPIO_t gpio = g_gpio_list[led_index];
      gpio_bit_write(gpio.port, gpio.pin, value ? RESET : SET);
      }

    int state = 0; // 0:停止, 1:充电中
    uint8_t current_power = 0;
    uint8_t show_power = 0;
    /**********************************************************

    • @brief 开始充电流水灯
    • @param 当前电量[0,1,2,3,4]
      **********************************************************/
      void Battery_led_start(uint8_t power) {

    current_power = power;
    show_power = current_power;

    state = 1;
    }

    void Battery_led_loop() {

    if(state == 0) {
    Battery_led_turn_off(LED1);
    Battery_led_turn_off(LED2);
    Battery_led_turn_off(LED3);
    Battery_led_turn_off(LED4);
    } else if(state == 1) {
    Battery_led_turn(LED1, show_power >= 1);
    Battery_led_turn(LED2, show_power >= 2);
    Battery_led_turn(LED3, show_power >= 3);
    Battery_led_turn(LED4, show_power >= 4);
    if(++show_power > 4) show_power = current_power;
    }
    }

    void Battery_led_update(uint8_t power) {
    current_power = power;
    }

    void Battery_led_stop() {
    // 当前电量闪三次
    for( uint8_t i = 0; i < 3; i++) {
    // 关闭所有灯
    Battery_led_turn_off(LED1);
    Battery_led_turn_off(LED2);
    Battery_led_turn_off(LED3);
    Battery_led_turn_off(LED4);
    delay_1ms(200);
    // 根据当前电量闪灯
    Battery_led_turn(LED1, current_power >= 1);
    Battery_led_turn(LED2, current_power >= 2);
    Battery_led_turn(LED3, current_power >= 3);
    Battery_led_turn(LED4, current_power >= 4);
    delay_1ms(200);
    }

    state = 0;
    }


总结

在本文中,我们详细介绍了如何在资源有限的情况下设计一个BSP驱动来控制四个LED灯,以展示充电状态和电量百分比。我们首先定义了接口和驱动初始化函数,然后实现了业务逻辑,包括开始充电、更新电量、停止充电和LED灯闪烁。我们还设计了测试案例来验证我们的驱动功能是否按照预期工作。

通过采用模块化设计,我们能够确保代码的可复用性和可移植性。尽管在产品最终电路板还未完成的情况下,我们只有开发板可以使用,但我们通过创新的方法和细致的规划,成功地解决了这一挑战。我们相信,通过遵循这些最佳实践,我们能够在未来的项目中更加高效地工作,并为我们的团队和公司创造更大的价值。

相关推荐
stm 学习ing16 分钟前
FPGA 第十讲 避免latch的产生
c语言·开发语言·单片机·嵌入式硬件·fpga开发·fpga
wenchm4 小时前
细说STM32单片机DMA中断收发RTC实时时间并改善其鲁棒性的另一种方法
stm32·单片机·嵌入式硬件
编码追梦人5 小时前
如何实现单片机的安全启动和安全固件更新
单片机
电子工程师UP学堂5 小时前
电子应用设计方案-16:智能闹钟系统方案设计
单片机·嵌入式硬件
飞凌嵌入式6 小时前
飞凌嵌入式T113-i开发板RISC-V核的实时应用方案
人工智能·嵌入式硬件·嵌入式·risc-v·飞凌嵌入式
blessing。。7 小时前
I2C学习
linux·单片机·嵌入式硬件·嵌入式
嵌新程8 小时前
day03(单片机高级)RTOS
stm32·单片机·嵌入式硬件·freertos·rtos·u575
Lin2012308 小时前
STM32 Keil5 attribute 关键字的用法
stm32·单片机·嵌入式硬件
电工小王(全国可飞)9 小时前
STM32 RAM在Memory Map中被分为3个区域
stm32·单片机·嵌入式硬件
maxiumII9 小时前
Diving into the STM32 HAL-----DAC笔记
笔记·stm32·嵌入式硬件