基于STM32F103的FreeRTOS系列(一)·单片机设计模式介绍·裸机程序的设计模式

目录

1.  轮询模式

2.  前后台

3.  定时器驱动

4.  基于状态机


        裸机程序的设计模式可以分为:轮询、前后台、定时器驱动、基于状态机。

  • 轮询:周期性地查询各个模块或函数的状态,适用于简单且响应时间要求不高的系统,但无法有效解决复杂函数之间的相互影响问题。

  • 前后台:通过将关键任务放在前台处理,而非关键任务放在后台,以确保关键任务的优先执行。这种方式对优先级管理有效,但复杂函数之间的相互影响仍可能存在。

  • 定时器驱动:通过定时器周期性地触发任务执行,适合需要定期执行任务的场景,但不能解决函数间相互影响的问题。

  • 基于状态机:将系统的各种状态和状态转移定义清晰,通过状态切换来控制系统行为。这种方法理论上可以避免复杂函数之间的直接影响,但在实践中确实需要良好的设计和调试

        前面三种方法都无法解决一个问题:假设有A、B两个都很耗时的函数,无法降低它们相互之间的影响。第4种方法可以解决这个问题,但是实践起来有难度。

1.  轮询模式

         可以理解为我们stm32的正常顺序编程,一步一步来,若是前面的条件有延时或者执行时间过长,就会导致后面的条件执行往后推迟。

       在main函数中是一个while循环,里面依次调用2个函数,这两个函数相互之间有影响:如果“喂一口饭”太花时间,就会导致迟迟无法“回一个信息”;如果“回一个信息”太花时间,就会导致迟迟无法“喂下一口饭”。

        使用轮询模式编写程序看起来很简单,但是要求while循环里调用到的函数要执行得非常快,在复杂场景里反而增加了编程难度。

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

2.  前后台

        所谓“前后台”就是使用中断程序。当主程序在执行过程中,若是另一程序触发中断,优先中断的程序进行执行。

        会出现问题:中断程序要是执行过长,将会导致迟迟离不开中断,并且要是创建多个中断还会出现抢占优先级的情况。

// 前后台程序
void main()
{
    while (1)
    {
        // 后台程序
        喂一口饭();
    }
}

// 前台程序
void 滴_中断()
{
    回一个信息();
}

        在这个场景里,给同事回复信息非常及时:即使正在喂饭也会暂停下来去回复信息。“喂一口饭”无法影响到“回一个信息”。但是,如果“回一个信息”太花时间,就会导致 “喂一口饭”迟迟无法执行。

        继续改进,假设小孩吞下饭菜后会发出“啊”的一声,妈妈听到后才会喂下一口饭。喂饭、回复信息都是使用中断函数来处理。示例程序如下:

// 前后台程序
void main()
{
    while (1)
    {
        // 后台程序
    }
}

// 前台程序
void 滴_中断()
{
    回一个信息();
}

// 前台程序
void 啊_中断()
{
    喂一口饭();
}

        main函数中的while循环是空的,程序的运行靠中断来驱使。如果电脑声音“滴”、小孩声音“啊”不会同时、相近发出,那么“回一个信息”、“喂一口饭”相互之间没有影响。在不能满足这个前提的情况下,比如“滴”、“啊”同时响起,先“回一个信息”时就会耽误“喂一口饭”,这种场景下程序遭遇到了轮询模式的缺点:函数相互之间有影响。(中断之间抢占优先级)

3.  定时器驱动

        定时器驱动模式,是前后台模式的一种,可以按照不用的频率执行各种函数。比如需要每2分钟给小孩喂一口饭,需要每5分钟给同事回复信息。那么就可以启动一个定时器,让它每1分钟产生一次中断,让中断函数在合适的时间调用对应函数。示例代码如下:

// 前后台程序: 定时器驱动
void main()
{
    while (1)
    {
        // 后台程序
    }
}

// 前台程序: 每1分钟触发一次中断
void 定时器_中断()
{
    static int cnt = 0;
    cnt++;
    if (cnt % 2 == 0)
    {
        喂一口饭();
    }
    else if (cnt % 5 == 0)
    {
        回一个信息();
    }
}
  • main函数中的while循环是空的,程序的运行靠定时器中断来驱使。
  • 定时器中断每1分钟发生一次,在中断函数里让cnt变量累加(代码第14行)。
  • 第15行:进行求模运算,如果对2取模为0,就“喂一口饭”。这相当于每发生2次中断就“喂一口饭”。
  • 第19行:进行求模运算,如果对5取模为0,就“回一个信息”。这相当于每发生5次中断就“回一个信息”。

        这种模式适合调用周期性的函数,并且每一个函数执行的时间不能超过一个定时器周期。如果“喂一口饭”很花时间,比如长达10分钟,那么就会耽误“回一个信息”;反过来也是一样的,如果“回一个信息”很花时间也会影响到“喂一口饭”;这种场景下程序遭遇到了轮询模式的缺点:函数相互之间有影响。

4.  基于状态机

        当“喂一口饭”、“回一个信息”都需要花很长的时间,无论使用前面的哪种设计模式,都会退化到轮询模式的缺点:函数相互之间有影响。可以使用状态机来解决这个缺点,示例代码如下:

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

        在main函数里,还是使用轮询模式依次调用2个函数。

        关键在于这2个函数的内部实现:使用状态机,每次只执行一个状态的代码,减少每次执行的时间,代码如下:

void 喂一口饭(void)
{
    static int state = 0;
    switch (state)
    {
        case 0:
        {
            /* 舀饭 */
            /* 进入下一个状态 */
            state++;
            break;
        }
        case 1:
        {
            /* 喂饭 */
            /* 进入下一个状态 */
            state++;
            break;
        }
        case 2:
        {
            /* 舀菜 */
            /* 进入下一个状态 */
            state++;
            break;
        }
        case 3:
        {
            /* 喂菜 */
            /* 恢复到初始状态 */
            state = 0;
            break;
        }
    }
}

void 回一个信息(void)
{
    static int state = 0;

    switch (state)
    {
        case 0:
        {
            /* 查看信息 */
            /* 进入下一个状态 */
            state++;
            break;
        }
        case 1:
        {
            /* 打字 */
            /* 进入下一个状态 */
            state++;
            break;
        }
        case 2:
        {
            /* 发送 */
            /* 恢复到初始状态 */
            state = 0;
            break;
        }
    }
}

        以“喂一口饭”为例,函数内部拆分为4个状态:舀饭、喂饭、舀菜、喂菜。每次执行“喂一口饭”函数时,都只会执行其中的某一状态对应的代码。以前执行一次“喂一口饭”函数可能需要4秒钟,现在可能只需要1秒钟,就降低了对后面“回一个信息”的影响。

        同样的,“回一个信息”函数内部也被拆分为3个状态:查看信息、打字、发送。每次执行这个函数时,都只是执行其中一小部分代码,降低了对“喂一口饭”的影响。

        使用状态机模式,可以解决裸机程序的难题:假设有A、B两个都很耗时的函数,怎样降低它们相互之间的影响。但是很多场景里,函数A、B并不容易拆分为多个状态,并且这些状态执行的时间并不好控制。所以这并不是最优的解决方法,需要使用多任务系统。

FreeRTOS_时光の尘的博客-CSDN博客

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

时光の尘

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值