充电指示灯

状态机

在嵌入式开发中,状态机(State Machine)是一种编程模式,用来管理系统的不同状态及其转换逻辑。状态机通过分解复杂的控制逻辑,将系统的不同行为划分为独立的状态,每个状态定义在特定条件下如何转移到其他状态。这种模式广泛应用于嵌入式系统,因为它能够简化复杂的流程控制和事件处理,便于理解和维护。

因为无法使用中断和多任务模式,为了保证程序在顺序执行的时候,不会一直阻塞在 LED 延时闪烁的代码中,我们这里使用状态机的方式来进行功能模拟;

代码实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
/*
功能:模拟设备充电时候充电指示灯闪烁的情况,以四格电为例:
0%-25% 四个灯依次亮起
25%~50% 第一个亮起,其他三个依次闪烁
50%~75% 第一二个亮起,其他两个依次闪烁
75%~100% 第一二三亮起,最后一个闪烁
100% 四个灯亮起不闪烁

当前使用独立按键进行模拟,key1按下表示充电,key2按下表示电量递增,key3表示断开充电

使用状态机的思维进行开发:
因为按照正常的显示逻辑,在灯进行显示和闪烁的时候按钮的功能将会被阻塞
因此,我们通过将延时拆分为更细小的时间片从而保证可以轮询检查按键而不是阻塞在LED灯的显示里
*/
===================================================================================
// main.c
#include "gd32f4xx.h"
#include "gd32f4xx_gpio.h"
#include "gd32f4xx_rcu.h"
#include "systick.h"
#include "GPIO_LED_Driver.h"
#include "battery_led.h"

// 状态机的状态 0(默认)开始充电
// 1 表示电量增加
// 2 表示移除充电器
int8_t battery_state = 0;

// KEY 配置
void GPIO_KEY_Config(void) {
rcu_periph_clock_enable(RCU_GPIOC);

gpio_mode_set(GPIOC, GPIO_MODE_INPUT, GPIO_PUPD_PULLUP, GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_3);
}

/*!
\brief 按键状态处理
\param[in] key_flag: 用于按键消抖
key_input:哪个按键
battery_state:状态机控制变量
state_value: 状态机状态:0电量显示,1电量增加,2关闭显示
\param[out] none
\retval none
*/
void update_key_state(int8_t *key_flag, FlagStatus key_input, int8_t *battery_state, int8_t state_value) {
if (key_input == RESET && *key_flag) {
*key_flag = 0;
*battery_state = state_value;
} else if (key_input == SET && *key_flag == 0) {
*key_flag = 1;
}
}

int main(void) {

systick_config();
battery_led_Init();
GPIO_KEY_Config();

// 防抖
int8_t key1_flag = 1;
int8_t key2_flag = 1;
int8_t key3_flag = 1;

// 延时控制
int32_t time_Control = 0;
while(1) {

// Key1Flag 模拟插上开始充电
// Key2Flag 模拟电量增加
// Key3Flag 模拟移除充电器
FlagStatus Key1 = gpio_input_bit_get(GPIOC, GPIO_PIN_0);
FlagStatus Key2 = gpio_input_bit_get(GPIOC, GPIO_PIN_1);
FlagStatus Key3 = gpio_input_bit_get(GPIOC, GPIO_PIN_2);

update_key_state(&key1_flag, Key1, &battery_state, 0);
update_key_state(&key2_flag, Key2, &battery_state, 1);
update_key_state(&key3_flag, Key3, &battery_state, 2);

// 灯闪烁时间为 0.5S
if(time_Control < 20) {
delay_1ms(25);
time_Control++;
} else {
time_Control = 0;
// 状态机处理函数
battery_machine_handle(&battery_state);
}
}
}
============================================================================
// battery_led.c
#include "battery_led.h"

// 模式传过来的电量百分比默认 26%
int8_t battery = 26;
int8_t batteryNum = 0;
int8_t flowNum = 1;

void battery_led_Init(void){
leds_switch_Init();
leds_switch_open();
leds_driver_init();
}

// 常量与闪烁,0点亮1灭
void set_led_state(int flowNum) {
bsp_led_write(GPIOD, GPIO_PIN_8, flowNum > 0 ? RESET : SET);
bsp_led_write(GPIOD, GPIO_PIN_9, flowNum > 1 ? RESET : SET);
bsp_led_write(GPIOD, GPIO_PIN_10, flowNum > 2 ? RESET : SET);
bsp_led_write(GPIOD, GPIO_PIN_11, flowNum > 3 ? RESET : SET);
}

void battery_machine_handle(int8_t* battery_state) {

// batteryNum 表示长量几格 总共四格电
batteryNum = battery / 25;
switch (*battery_state) {
case 1:
battery += 10;
if(battery >= 100) {battery = 100;}
*battery_state = 0;
case 0:
if(flowNum > 4) {flowNum = batteryNum;}
set_led_state(flowNum);
flowNum++;
break;
case 2:
bsp_leds_close_all();
break;
default:
break;
}
}
================================================================
// GPIO_LED_Driver.h
#include "GPIO_LED_Driver.h"

//每一个LED初始化 核心配置:时钟 端口 引脚编号
LED_PARAM leds[] = {
{RCU_GPIOD,GPIOD,GPIO_PIN_8},
{RCU_GPIOD,GPIOD,GPIO_PIN_9},
{RCU_GPIOD,GPIOD,GPIO_PIN_10},
{RCU_GPIOD,GPIOD,GPIO_PIN_11},
// {RCU_GPIOD,GPIOD,GPIO_PIN_12},
// {RCU_GPIOD,GPIOD,GPIO_PIN_13},
// {RCU_GPIOD,GPIOD,GPIO_PIN_14},
// {RCU_GPIOD,GPIOD,GPIO_PIN_15}
};

//元素个数
uint8_t len = sizeof(leds)/sizeof(leds[0]);

// 初始化总开关
void leds_switch_Init(void) {
rcu_periph_clock_enable(RCU_GPIOC);
gpio_mode_set(GPIOC, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, GPIO_PIN_6);
gpio_output_options_set(GPIOC, GPIO_OTYPE_OD, GPIO_OSPEED_MAX, GPIO_PIN_6);
}

//单个引脚初始化
static void led_pin_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 leds_driver_init(void){
for(int i=0;i<len;i++){
LED_PARAM led = leds[i];
led_pin_config(led.rcu,led.port,led.pin);

// 初始化默认关闭LED
gpio_bit_set(led.port,led.pin);

}
}

// 打开总开关
void leds_switch_open() {

gpio_bit_reset(GPIOC,GPIO_PIN_6);
}

// 关闭总开关
void leds_switch_close() {

gpio_bit_set(GPIOC,GPIO_PIN_6);
}

//点亮某一个灯
void leds_open(LED led){
gpio_bit_reset(leds[led].port,leds[led].pin);
}

//关闭某一个灯
void leds_close(LED led){
gpio_bit_set(leds[led].port,leds[led].pin);
}

//点亮所有灯
void leds_open_all(){
for(int i=1;i<len;i++){
LED_PARAM led = leds[i];
gpio_bit_reset(led.port,led.pin);
}
}

//关闭所有灯
void bsp_leds_close_all(){
for(int i=0;i<len;i++){
LED_PARAM led = leds[i];
gpio_bit_set(led.port,led.pin);
}
}

// 写入一个 0 或者 1
void bsp_led_write(uint32_t gpio_periph, uint32_t pin, bit_status bit_value){
gpio_bit_write(gpio_periph, pin, bit_value);
}