首页 > 其他分享 >嵌入式裸机设计思想——时间片轮裸机开发架构+状态机+定时器调度机制

嵌入式裸机设计思想——时间片轮裸机开发架构+状态机+定时器调度机制

时间:2023-10-06 11:07:50浏览次数:40  
标签:状态机 task gray void 任务 裸机 state 片轮 display


前言

(1)
(2)在MCU开发的时候,很多入门者会固执的认为,做项目一定要上实时操作系统。但是真的是这样的吗?
(3)我曾经阅读过一位10年嵌入式开发经验的大佬分享的公众号,这位大佬感叹到,其实对于绝大多数时候,MCU开发不需要上操作系统。只要任务分配的合理,百分之九十的项目不上操作系统都是能够跑的。
(4)今天我就分享一下我之前备赛期间所搭建基于TM4C123的工程模板。不过这个模板问题还是有很多,比如模块之间耦合很严重,当时没有这个意识,现在才发现写的有多垃圾。虽说如此,但本人认为还是有优点的地方。
(5)注意:本文需要一点函数指针,结构体,枚举的知识,不了解的同学请先去补充好基础再来看

正文

状态机

(1)状态机其实很好理解,说白了就是一个switch()语句。根据情况将一个任务拆分成多种。例如,在我的代码中,有一个OLED显示程序。因为OLED显示是非常浪费时间的,为了不因为一个这样的程序,而影响到其他任务的执行,我们可以将OLED显示任务分成多个,依次显示。

static void Display(void)
{
	static uint8_t gray_display_state=2;
	switch(gray_display_state)
	{
		case 2:gray_display_state++;break;
		case 3:gray_display_state++;break;
		case 4:gray_display_state++;break;
		case 5:gray_display_state++;break;
		case 6:gray_display_state++;break;
		default:gray_display_state = 2;
	}
}

(2)上面那种其实是最简单的形式,整个状态是呈现圆圈型状的状态机。但是,我们有时候会遇到一些情况,他的状态有很多种任务流程,例如洗衣机,有待机状态,运行状态,运行结束之后的自动停止进入待机状态,手动关停状态,因一些异常情况引起的停止状态。因此,状态机也是一个很好的思路。

嵌入式裸机设计思想——时间片轮裸机开发架构+状态机+定时器调度机制_裸机开发

enum Washer_State_List
{
	off,            //待机状态
	run,            //运行机状态
	automatic_stop, //自动停止
	hand_stop,      //手动关停状态
	malfunction     //故障
};
static void washer_control(void)
{
	static uint8_t washer_state=2;
	switch(washer_state)
	{
		case off:
			LCD_show("stop");
			if(operation_key == true) washer_state = run;
			break;
		case run:
			LCD_show("runing");
			//如果手动关停
			if(hand_stop_key == true) washer_state = hand_stop;
			//如果计时结束
			if(time-- == over) washer_state = automatic_stop;
			//识别到了异常
			if(err == true) goto err;
			break;
		case automatic_stop:
			//显示洗衣机已经关闭
			LCD_show("Finished washing");
			washer_state = off;
			break;
		case hand_stop:
			//关闭洗衣机
			washer_stop();
			//显示洗衣机已经手动关闭
			LCD_show("Manually closed");
			washer_state = off;
			break;
		case malfunction:
			err : 
				//关闭洗衣机
				washer_stop();
				//报警
				alarm_system(on);
				//显示机器故障
				LCD_show("Machine failure");
				washer_state = off;
			break;
		default:gray_display_state = 2;
	}
}

时间片轮裸机开发架构

(1)这个机制名字很多,有些人叫做软定时器,有些叫做Easy51RTOS,具体叫做什么,我们就不深究了。
(2)新手入门MCU裸机开发,肯定都是一股脑的采用while(1)死循环,然后一直跑。如果是开发稍微复杂一点点的任务时候,你就会深刻的感受到,这样写究竟有多垃圾。
(3)一股脑的while(1)死循环,然后里面堆一大堆的任务,这样做有一下几种问题:
<1>在后续的维护中非常复杂。想找到目标任务找起来很费力。
<2>并不是所有的任务都需要频繁执行,例如OLED显示,就是一个非常费时间,但是又没有必要一直保持刷新的任务。他只要能够做到50ms刷新一次就能够做动画了。因此这种任务放在while(1)无脑执行无疑是对CPU的浪费,导致真正需要CPU执行的任务没有有效的照顾。
(4)为了优化上面这些问题,大佬们于是提出了基于时间片的裸机开发架构,我们可以利用一个定时器提供心跳,不断的进行计数。然后当定时时间一到,那么就可以开始执行相应的任务了。
(5)时间片轮转的裸机架构看起来是不是很完美?NO,不是的,他确实比无脑while(1)优秀很多,但是我们需要知道,每个任务的执行时间不能超出一次时间片。(例如我们上面的滴答定时器是2ms定时,所以时间片是2ms)
(6)因此,我们需要大概估计每个任务的执行时间,这里有很两种做法:
<1>利用滴答定时器,当任务开始记录当前时间,任务结束记录当前时间然后两者相减。
<2>进入函数,让某个引脚为高电平,函数结束让他为低电平,然后用示波器捕捉这个引脚的上升沿和下降沿。(这个方法感觉有点麻烦,我是使用的第一个)
(7)时间片轮裸机开发框架机制确实非常好,但是毕竟还是软件实现的任务调度,实时性只能说相对于只有一个while(1)好,对于其他的机制还是不太行。所以我个人还是建议,只有不重要的任务,对整个项目影响不大的任务可以放在这个框架里面,例如OLED显示,按键扫描。

/*------------------------------------------------*/
/*------------------- systick.c ------------------*/
/*------------------------------------------------*/
/* 滴答定时器中断,每2毫秒进入一次,count表示过了多少个2毫秒,最多计时2366多个小时*/
static void SycTickHandler(void) {
	counter++;
	task_remarks();    //用于任务调度,此任务执行时间在5us之内
}
/*------------------------------------------------*/
/*-------------------- Task.h --------------------*/
/*------------------------------------------------*/
#ifndef     _Task__H
#define     _Task__H

// 任务结构
typedef struct 
{
    uint8_t  run;                          // 程序运行标记:0:不运行,1:运行
    uint16_t timer;                        // 定时器,用于自减 单位:ms
    uint16_t itv_time;                     // 任务运行间隔时间 单位:ms
    void (*hook)(void);                    // 要运行的任务函数
} task_params_t;  

/*****    函数声明    *******/
void task_proc(void);     //任务执行处理
void task_remarks(void);  //任务标志位处理

#endif
/*------------------------------------------------*/
/*-------------------- Task.c --------------------*/
/*------------------------------------------------*/
static void SW_Scan(void);
// 任务清单
enum TASK_LIST {
	TASK1_SW_Scan,                    // 任务2,按键扫描
	TASK2_Display,                    // 任务3,OLED显示
	
	TASKS_NUM                         // 任务总数
};


//任务列表
static task_params_t s_task_params[TASKS_NUM] = {
	{0, Task_interval_ms(20),  Task_interval_ms(20),  SW_Scan },                  //任务2
	{0, Task_interval_ms(10),  Task_interval_ms(10),  Display },                  //任务3
};

/*
*********************************************************************************************************
*	函 数 名: task_proc
*	功能说明: 任务处理
*	形    参:无
*	返 回 值: 无
*********************************************************************************************************
*/
void task_proc(void)
{
    uint8_t i = 0;

	for(i = 0; i < TASKS_NUM; i++)
	{
		if(s_task_params[i].run)
		{
			s_task_params[i].run = 0;
			s_task_params[i].hook();
			s_task_params[i].timer = s_task_params[i].itv_time;
			break;
		}
	}
}


/* 作用 : 任务标志处理,单位是2ms,因为滴答定时器中断设置的是2ms
 * 传入参数 : 无
 * 返回参数 : 无
 * 任务时间 : TASKS_NUM少于10的时候,运行速度在5ns之内
*/
void task_remarks(void)
{
	uint8_t i = 0;

	for(i = 0; i < TASKS_NUM; i++) 
	{
		if(s_task_params[i].timer)
		{
			s_task_params[i].timer--;

			if(s_task_params[i].timer == 0)
			{
				s_task_params[i].run = 1;
			}
		}
	}
}

/*****************************************************************************************/
/*************************************  按键扫描任务  *************************************/
/*****************************************************************************************/
static void SW_Scan(void)
{
	//...
}

/*****************************************************************************************/
/*********************************  灰度传感器时间显示任务  *******************************/
/*****************************************************************************************/
static void Display(void)
{
	static uint8_t gray_display_state=2;
	switch(gray_display_state)
	{
		case 2:
			gray_display_state++;
			//...
			break;
		case 3:
			gray_display_state++;
			//...
			break;
		case 4:
			gray_display_state++;
			//...		
		break;
		case 5:
			gray_display_state++;
			//...
			break;
		case 6:
			gray_display_state++;
			//...
			break;
		default:
			gray_display_state = 2;
	}
}

定时器调度

(1)如果经验稍微丰富的同学会发现,上面的方法其实还是有问题的。例如有些任务我需要说执行就执行,如果是按照上面的方法来肯定是有一点点迟钝的。
(2)因此,我们可以采用定时器调度机制,例如我们的编码器数值读取和PID运算是优先级最高的任务,他的影响因素非常大,是我们的核心任务,所以他单独分配一个定时器。
(3)然后其他的一些任务,很相对来说,比较重要,但是却又不是核心任务。因此,我们可以将它存放在一个优先级低一级的定时器中。
(4)如果还有剩余的定时器,你可以自己根据需求进行任务分级,然后实现定时器调度功能。

//用于获取编码器的值
systime Time0_Delta;
void TIMER0A_Handler(void)
{
  get_systime(&Time0_Delta);
	cnt = 1;
	left_motor_speed_cmps =get_left_motor_speed();   //获取左边轮子实际速度值
	right_motor_speed_cmps=get_right_motor_speed();  //获取右边轮子实际速度值
	Motor_Foreward_Right(400);
	Motor_Foreward_Left(400);
  TimerIntClear(TIMER0_BASE,TIMER_TIMA_TIMEOUT);   //清除中断标志位
}
//普通任务
systime Time1_Delta;
void TIMER1A_Handler(void)
{
  get_systime(&Time1_Delta);
	cnt = 2;
	//任务列表
	//超声波数据解析
  US_100_Statemachine(UART5_BASE);  
	//MPU6050数据采集
	MPU6050_Read_Data(&WP_Sensor.gyro_raw,&WP_Sensor.accel_raw,&WP_Sensor.temperature);
  TimerIntClear(TIMER1_BASE,TIMER_TIMA_TIMEOUT);  //清除中断标志位
}

总结

(1)如果我们了解了如上机制,对于后续的操作系统学习,以及任务调度的理解会有一定的帮助。
(2)使用如上机制之后,也会很好的管理项目的各种任务,多裸机开发也很有利。


标签:状态机,task,gray,void,任务,裸机,state,片轮,display
From: https://blog.51cto.com/zyxfighting/7722154

相关文章

  • VMware ESXi 7.0 Update 3o 下载 - 领先的裸机 Hypervisor (重大更新)
    VMwareESXi7.0Update3o下载-领先的裸机Hypervisor(重大更新)VMwareESXi7.0Update3oStandard&AllCustomImageforESXi7.0U3InstallCD新增了22个服务器机型(Dell、HPE和Lenovo)和多个驱动对vSphereQuickBoot的支持,以及71个功能问题修复,属于”重大......
  • 状态机DP,力扣188. 买卖股票的最佳时机 IV
    状态机DP,力扣188.买卖股票的最佳时机IV整数数组prices和一个整数k,其中prices[i]是某支给定的股票在第i天的价格。一次只能参与一笔交易,最多可以进行k笔交易,求最大利润。确定状态f[n+1][k+2][2],f[i][j][0]、f[i][j][1]分别表示前i天最多进行j次交易,且在第i天时......
  • 第8期ThreadX视频教程:应用实战,将裸机工程移植到RTOS的任务划分,驱动和应用层交互,中断DM
    视频教程汇总帖:https://www.armbbs.cn/forum.php?mod=viewthread&tid=110519 这个是我们初学RTOS面临的最直接问题,很多时候,简单的RTOS机制明白了,API也会调用了,就是添加到RTOS后,总感觉那里不对劲,怎么使用才是正确姿势。针对这些问题,本期视频教程,我们ThreadX内核教程穿插一期实......
  • Go每日一库之134:fsm(基有限状态机库)
    开发中,我们经常会遇到这种情况,服务模块有多种状态,它们有一定的顺序,先后执行,逐步切换。这时,fsm这个库可以帮助我们更好的管理多个状态。fsm库,它主要基于两个FSM实现,增加了golang版本的实现:JavascriptFiniteStateMachine,https://github.com/jakesgordon/javascript-state-ma......
  • Linux任务的状态机制
    task的生命周期Linux内核调度就是管理CPU硬件资源,同时决定多任务系统的每一个task应该什么时候上CPU,上CPU运行多久的一个机制。因此调度的机制涉及到两个方面一个是task侧,一个是CPU侧,任务是被管理的对象之一,会随着调度和运行改变状态。因此从task的角度来理解调度机制我认为是一......
  • “国产双系统”出炉,RK3568J非对称AMP:Linux+RTOS/裸机
    “非对称AMP”双系统是什么AMP(AsymmetricMulti-Processing),即非对称多处理架构。“非对称AMP”双系统是指多个核心相对独立运行不同的操作系统或裸机应用程序,如Linux+RTOS/裸机,但需一个主核心来控制整个系统以及其它从核心。每个处理器核心相互隔离,拥有属于自己的内存,既可各......
  • VMware ESXi 8.0U2 发布 - 领先的裸机 Hypervisor
    VMwareESXi8.0U2发布-领先的裸机Hypervisor2023-09-21,北京时间22日凌晨vSphere8.0Update2正式发布。同步发布Dell和HPE等OEM定制版镜像请访问原文链接:https://sysin.org/blog/vmware-esxi-8-u2/,查看最新版。原创作品,转载请保留出处。作者主页:sysin.orgvS......
  • 【设计模式】状态模式State:通过有限状态机监控功能的 "状态变化"
    (目录)状态模式的应用场景非常广泛,比如:线上购物订单、手机支付、音乐播放器、游戏、工作流引擎等场景。状态模式设计的初衷是应对同一个对象里不同状态变化时的不同行为的变化。模式原理原始定义是:允许一个对象在其内部状态改变时改变它的行为,对象看起来似乎修改了自己的......
  • Unity 游戏开发、02 基础篇 | 知识补充、简单使用动画、动画状态机
    前置笔记(由浅入深)Unity游戏开发、01基础篇2场景操作3D场景Q手型工具(鼠标中键):上下左右移动场景ALT+鼠标左键:以视图为中心旋转鼠标右键:以观察者为中心旋转SHIFT+Gizmo方块:Y轴归位物体节点+F:观察者定位至物体窗口布局3D项目一般窗口布局如下3全局光照全......
  • 状态机
    这是一种根据状态情况进行的DP。母题考虑持有股票的数量,然后考虑买/不买。1考虑与之前做同一班车/不同的情况。2考虑状态显然。然后可以发现需要从往前\(k\)个转移,单调队列优化即可。3状态还是前xxx前xxx。单调队列优化。......