裸机和rtos两种开发方式的区别

一、裸机开发

1.轮询方式

轮询,顾名思义就是事情一件件按顺序来,同一时间只能做一件事情。举个例子,一个妈妈要做两件事情,喂饭和回信息,只能先喂好饭,然后再回信息,两件事情不能同时做有先后顺序。

void main()
{
    while (1)
    {
        喂一口饭();
        回一个信息();
    }
}

2.事件驱动

事件是一个宽泛的概念,什么叫事件?可以是:按下了按键、串口接收到了数据、模块产生了中断、某个全局变量被设置了。

什么叫事件驱动?当某个事件发生时,才调用对应函数,这就叫事件驱动。放到MCU来讲,我们可以理解为来了一个按键中断,这时就要调用中断处理函数处理中断信息。

再以上个例子为例,妈妈一直给小孩喂饭,就做这件事情,某个时间点同事发来一条信息,这时就需要妈妈去处理这个信息,处理完成后继续喂饭。

void main()
{
	while (1)
    {
        喂饭();
    }
}

void message_isr() /* 同事发来信息触发中断 */
{
    回一个信息(); 
}

对于中断的处理,原则是"尽快"。否则会影响到其他中断,导致其他中断的处理被延迟、甚至丢失,所以中断处理的事情要尽量的精简。

3.定时器方式

上述例子中只有两个任务,如果有更多的任务,很多有经验的工程师会使用定时器来驱动:

  • 设置一个定时器,比如每1ms产生一次中断,以此为时间基准

  • 对于函数A,可以设置它的执行周期,比如每1ms执行一次

  • 对于函数B,可以设置它的执行周期,比如每2ms执行一次

  • 对于函数C,可以设置它的执行周期,比如每3ms执行一次

  • 注意:1ms、2ms、3ms只是假设,你可根据实际情况调整。

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;
        }
    }
}

但这时又出现个问题,如果某个函数处理的时间过长,这又会影响其他函数,或者时间基准。当然也可以把函数分得更精简,一件事情拆成多个步骤完成,这样可以解决这个问题。

也可以可以做以下改进,将任务放回主函数中,这样做就退回轮询方式。

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--;    
}

4.状态机方式

假设要调用两个函数AB,AB执行的时间都很长,又要AB任务都可以及时得到响应,裸机就很难处理这种场景,这时就需要使用状态机方式,将一个任务拆多个任务处理。

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

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

		case 1: /* 盛菜 */
		{
			/* 盛菜 */
			state++;
			return;
		}

		case 2: 
		{
			/* 拿勺子 */
			state++;
			return;
		}
		
	}
}

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

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

		case 1: 
		{
			/* 观看信息 */
			state++;
			return;
		}

		case 2: 
		{
			/* 打字 */
			state++;
			return;
		}
		
	}
}

void main()
{
	while (1)
    {
        feed_kid();
        send_msg();
    }
}

二、rtos开发

由以上裸机开发方式可见,在处理多任务方面,裸机开发或多或少都存在缺陷,这时就急需引入一种多任务并行运行的方式,实时处理信息。rtos的开发方式就由此而来。

有人可能会说,我使用状态机也可以实现同样的效果呀,为啥那么麻烦学习rtos。使用rtos的关键就是在于可以多任务并行,遇到执行时间长的任务,也不需要像状态机那样将任务分拆,而且每个任务之间的资源和栈都是独立的,程序的耦合性大大降低,可移植性和可读性大大增强。

举例如下:

// RTOS程序    
喂饭()
{
    while (1)
    {
        喂一口饭();
    }
}

回信息()
{
    while (1)
    {
        回一个信息();
    }
}

void main()
{
    create_task(喂饭);
    create_task(回信息);
    start_scheduler();
    while (1)
    {
        sleep();
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值