单片机裸机编程-时机管理
对于 RTOS 实时操作系统,我们是通过 TASK(任务)进行底层操作的,这与裸机编程中的函数(fun)类似。不同的任务或函数实现不同的功能,在RTOS中,单片机有信号量、队列等不同任务之间的通信机制,但对于裸机编程来说就引出了一个问题:不同任务或函数是如何配合工作的呢?换句话说,单片机是如何知道什么时候该做什么事情,或者为什么做完一件事之后再做另一件事呢?
1、裸机的逻辑轮询
在裸机编程中,任务的执行顺序通常是固定的,由程序的流程控制语句(如 if
、while
、switch
等)决定。程序从入口点开始,按顺序执行代码,直到遇到分支或循环。当然,也可以通过中断来实现某些功能。
例如,以下代码展示了通过全局变量的状态来控制不同函数的执行逻辑:
void main() {
while (1) {
if (sensor_data_ready) { // 检查传感器数据是否准备好
process_sensor_data();
}
if (button_pressed) { // 检查按钮是否被按下
handle_button_press();
}
update_display(); // 更新显示
}
}
在这个例子中,程序通过全局变量(sensor_data_ready
和 button_pressed
)来判断是否执行某个函数。
也就是说,在不同的执行函数之间的通信使用的是全局变量,或者说是标志位。我们通过if,switch这样的逻辑语句让单片机知道在什么情况下该做什么事,所以只需要一直轮询下去即可。
这种方式简单直接,但存在以下问题:
-
耦合性高:全局变量的使用使得代码之间的耦合性增加,难以维护和扩展。
-
灵活性差:任务的执行顺序固定,难以动态调整。
2、使用状态机进行任务管理
为了改善上述问题,我们可以引入 状态机 的概念,对不同的任务进行局部管理。状态机通过定义不同的状态和状态之间的转换条件,使得代码更加模块化和灵活。例如:
typedef enum {
STATE_IDLE,
STATE_PROCESS_SENSOR,
STATE_HANDLE_BUTTON,
STATE_UPDATE_DISPLAY
} StateTypeDef;
StateTypeDef currentState = STATE_IDLE;
void main() {
while (1) {
switch (currentState) {
case STATE_IDLE:
if (sensor_data_ready) {
currentState = STATE_PROCESS_SENSOR;
} else if (button_pressed) {
currentState = STATE_HANDLE_BUTTON;
}
break;
case STATE_PROCESS_SENSOR:
process_sensor_data();
currentState = STATE_IDLE;
break;
case STATE_HANDLE_BUTTON:
handle_button_press();
currentState = STATE_IDLE;
break;
case STATE_UPDATE_DISPLAY:
update_display();
currentState = STATE_IDLE;
break;
}
}
}
通过状态机,我们可以清晰地定义每个任务的执行条件和状态转换逻辑,从而提高代码的可读性和可维护性。
3、在裸机中实现时间片轮询
进一步思考,我们可以在裸机编程中借鉴 RTOS 的时间片轮询机制。虽然裸机没有 RTOS 内核的支持,但可以通过定时器中断来实现类似的效果。例如:
-
设置定时器中断:配置定时器以固定频率(如 10ms)触发中断。
-
维护任务状态:在定时器中断中维护一个任务状态数组,记录每个任务的执行状态和剩余时间片。
-
轮询任务执行:在主循环中,根据任务状态数组依次执行每个任务的一部分。
示例代码如下:
#define TASK_COUNT 3
#define TIME_QUANTUM 10 // 时间片大小,单位为毫秒
typedef struct {
void (*taskFunc)(void); // 任务函数指针
int remainingTime; // 剩余时间片
} TaskTypeDef;
TaskTypeDef tasks[TASK_COUNT] = {
{process_sensor_data, TIME_QUANTUM},
{handle_button_press, TIME_QUANTUM},
{update_display, TIME_QUANTUM}
};
void Timer_ISR(void) {
static int tick = 0;
tick++;
}
void main() {
int currentTask = 0;
while (1) {
if (tasks[currentTask].remainingTime > 0) {
tasks[currentTask].taskFunc(); // 执行当前任务
tasks[currentTask].remainingTime--;
}
currentTask = (currentTask + 1) % TASK_COUNT; // 轮询下一个任务
}
}
通过这种方式,我们可以在裸机中实现类似 RTOS 的时间片轮询机制,使得任务的执行更加公平和灵活。
个人喜欢使用状态机进行裸机编程,这样直接是多个代码块直接的裸机判断。也就相当于是有多个while(1)循环,方便代码的管理和调试。在不同的state模式下执行不同的小模块代码。