Java教程

嵌入式常用裸机编程框架

本文主要是介绍嵌入式常用裸机编程框架,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

RTOS嵌入式系统框架进阶

第一章 嵌入式常用裸机编程框架
第二章 面向对象编程


文章目录

    • **RTOS嵌入式系统框架进阶**
  • 前言
      • 1 轮询方式
      • 2 事件驱动方式
        • 2.1 普通事件驱动方式
        • 2.2 改进事件驱动方式
      • 3 常用事件驱动方式:定时器
        • 3.1 “时间片”框架
        • 3.2 改进“时间片”框架
        • 3.2 “时间片”+“状态机”框架
      • 4 RTOS实时操作系统
  • 总结


前言

学习韦东山老师的七天物联网实战,以其课程笔记为骨,记录一下学习的过程,可能会加入一些自己的感想。
最后欢迎点赞,评论交流!


1 轮询方式

常用的嵌入式程序框架设计方案如下所述:

// 常用模式
void main()
{
	while (1)
    {
        Task(A);
        Task(B);
    }
}

函数之间互相有影响,若函数A耗时特别长,尤其包含一些无意义的delay函数进行耗时,那么B任务还得必须等待A任务执行完,这样的话B任务的执行情况就很不好。

2 事件驱动方式

2.1 普通事件驱动方式

我们在看芯片的datasheet的时候,尤其是新手,总会混淆事件和中断的概念,尤其是在配置寄存器的时候,有时某些操作会产生事件或中断,中断我们理解是cpu放下当前任务去执行其他任务,那事件是什么却有时候不是很明白?
其实事件是一个宽泛的概念,什么叫事件?可以是:按下了按键、串口接收到了数据、模块产生了中断、某个全局变量被设置了。事件就是发生了某事,至于怎么去处理它得看你的软硬件如何设置。
那什么叫事件驱动?就是当某个事件发生时,才调用对应函数,这就叫事件驱动。

void main()
{
	while (1)
    {
        if (Flag_a)
            process_key();
    }
}

void a_isr() /* 事件a产生中断 */
{
    key = xxx;
    Flag_a = 1;
}

void b_isr() /* 事件b产生中断 */
{
    Task(b);
}

其实这种方式本质上还是采用轮询的方式,只不过使用了一些中断的技巧,但是b_isr()中进行了任务处理的一些函数,一般我们不这么使用中断,因为如果Task(b)耗时比较严重的话,cpu一直处于b任务的话,可能a中断的处理就会收到影响,比如a多次产生中断,但cpu一直在task(b)中,导致最后cpu应该就只处理一次a函数,所以一般我们常用以下的方式来改进事件驱动型框架。

2.2 改进事件驱动方式

void main()
{
	while (1)
    {
        if (Flag_a == 1)
            Task(a);
        if (Flag_b == 1)
            Task(b);
    }
}

void a_isr() /* 中断a */
{
    Flag_a = 1;
}

void b_isr() /* 中断b */
{
    Flag_b = 1;
}

改进型函数只在中断中修改标识符,处理速度就会快很多,不会导致别的中断被延迟、丢失。
但本质上还是轮询的框架。

3 常用事件驱动方式:定时器

3.1 “时间片”框架

“时间片”是嵌入式常听的一个概念,即原来的整个while(1)大循环切割成很多小的时间片,每过一个时间片就切换一次执行的任务,这样的话每个任务都在同时执行,不用等待。
例如ABC三个任务,A周期1ms,B周期2ms,C周期3ms,时间片为1ms,即第一个1ms,A执行,B剩余1ms,C剩余2ms;第二个1ms,A执行,B执行,C剩余1ms;第三个1ms,A执行,B剩余1ms,C执行…
代码如下:

typedef struct soft_timer {
    int remain;
    int period;
    void  (*function)(void);
}soft_timer, *p_soft_timer;

static soft_timer timers[] = {
    {1, 1, A},
    {2, 2, B},
    {3, 3, C},
};

void main()
{
	while (1)
    {
    }
}

void timer_isr() 
{
    int i;
	/* timers数组里每个成员的expire都减一 */
    for (i = 0; i < 3; i++)
        timers[i].remain--;
    
    /* 如果timers数组里某个成员的expire等于0:
     *   1. 调用它的函数
     *   2. 恢复expire为period
     */
    for (i = 0; i < 3; i++)
    {
        if (timers[i].remain == 0)
        {
            timer[i].function();
            timers[i].remain = timers[i].period;
        }
    }
}

上述例子中有三个函数:A、B、C。根据它们运行时消耗的时间调整运行周期,也可以达到比较好的效果。

但是,一旦某个函数执行的时间超长加入超过1ms(一个时间片的长度),这样这个时间片内即使所有时间都用来处理这个任务时间也不够,因此等到下次定时器中断时就不能正常处理原来的ABC任务,就会有如下后果:

  • 影响其他函数
  • 延误整个时间基准

3.2 改进“时间片”框架

此处的改进并没有进行特别的改进,只不过把任务执行的函数放到主循环中,本质仍是轮询算法,若任务函数执行时间过长 ,仍然会有以下的弊端:

  • 影响其他函数
  • 延误整个时间基准
typedef struct soft_timer {
    int remain;
    int period;
    void  (*function)(void);
}soft_timer, *p_soft_timer;

static soft_timer timers[] = {
    {1, 1, A},
    {2, 2, B},
    {3, 3, C},
};

void main()
{
    int i;
	while (1)
    {
        /* 如果timers数组里某个成员的expire等于0:
         *   1. 调用它的函数
         *   2. 恢复expire为period
         */
        for (i = 0; i < 3; i++)
        {
            if (timers[i].remain == 0)
            {
                timer[i].function();
                timers[i].remain = timers[i].period;
            }
        }
    }
}

void timer_isr() 
{
    int i;
	/* timers数组里每个成员的expire都减一 */
    for (i = 0; i < 3; i++)
        if (timers[i].remain)
	        timers[i].remain--;    
}

3.2 “时间片”+“状态机”框架

若非要用“裸机”的思想处理时间片的所说的难点,需要引入“状态机”的概念,其内核思想是:将耗时的任务进行拆分,确保拆分后的任务可以在每个时间片中完成所需功能。

  • 状态机函数示例如下
void task_A(void)
{
	static int state = 0;

	switch (state)
	{
		case 0: /* 开始 */
		{
			/* TA1 */
			state++;
			return;
		}

		case 1: /* 拆分后任务片 */
		{
			/* TA2 */
			state++;
			return;
		}

		case 2: 
		{
			/* TA3 */
			state++;
			return;
		}
		
	}
}

void task_B(void)
{
	static int state = 0;

	switch (state)
	{
		case 0: /* 开始 */
		{
			/* TB1 */
			state++;
			return;
		}

		case 1: 
		{
			/* TB2 */
			state++;
			return;
		}

		case 2: 
		{
			/* TB3 */
			state++;
			return;
		}
		
	}
}

void main()
{
	while (1)
    {
        task_A();
        task_B();
    }
}

但状态机拆分程序有以下难点:

  • 比较麻烦
  • 有些复杂的程序无法拆分为状态机

基于裸机的程序框架无法完美地解决这类问题:复杂的、很耗时的多个函数。

4 RTOS实时操作系统

假设要调用两个任务A、B,AB执行的时间都很长,可以使用以下两种办法来解决:1、使用裸机程序时可以把AB函数改造为"状态机",2、可以使用RTOS。

这两种方法的核心都是"分时复用":

  • 分时:函数A运行一小段时间,函数B再运行一小段时间

  • 复用:复用CPU

二者的核心思想类似,只不过因为RTOS会创建属于任务本身的堆栈,可以保存切换任务时的程序执行现场,因此不用我们再对任务进行人为的拆分。

// RTOS程序    
Task_A()
{
    while (1)
    {
        FuncA();
    }
}

Task_B()
{
    while (1)
    {
        FuncB();
    }
}

void main()
{
    create_task(Task_A);
    create_task(Task_B);
    start_scheduler();
    while (1)
    {
        sleep();
    }
}

总结

例如:以上就是今天总结的内容,本文仅仅简单介绍了嵌入式常用的系统框架,方便我们的后续使用。
这篇关于嵌入式常用裸机编程框架的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!