制作变频器-第六部分

Posted on December 8, 2023

从多任务说起.

前序日子,都在进行核心代码的开发。终于最近进入了 UI 界面的开发了。

虽然叫 UI, 但是其实并没有窗口。也没有命令行。

而是几个 LED 灯 + 一个旋钮 + 一个红外遥控器。

这些也算是能和用户交互了,所以,算UI没什么问题吧?

一旦进入 UI 的开发领域,马上就遇到单片机的一个掣肘了: 没有操作系统。

为什么UI需要操作系统呢?

因为 UI 是由多个子任务构成的。

vvvf图解

libvfd 里, SVPWM 的生成是受时钟中断驱动的。

但是,V/F 控制,则是一个独立的循环。V/F 控制,收到外部的控制请求后, 会根据电机的加减速特性,来逐步的提高 svpwm 模块的电压和频率。

还有,遥控接收,编码器处理。这些代码,都需要自己的一个控制循环。

也就是意味着,这个变频器的内部,有多个独立的 “控制流”。

独立的控制流,就是线程。

但是单片机没有操作系统,如何使用线程呢?

用户自己实现的线程

其实不需要操作系统,用户也可以自己实现“控制流上下文切换”。也就是用户态线程。矣?等等,单片机没有操作系统,更没有用户态和内核态之分。不过也可以借用这个术语嘛。

那么在单片机里,所谓的用户态线程,指的其实是 c++ 协程。特别是 C++20 的协程。

其实,在单片机里,所谓协程,主要的上下文切换手段,就是 co_await delay(ms);

只要当前人物想 sleep 一段时间,就让出 cpu 时间。然后交给协程的列队管理器去调度下一个协程。

因为协程的运行是不会被抢占的。只能是主动的 delay 放弃。所以,这种多任务的方式,就叫 协作式多任务

实现 yield

目前单片机的编译器版本参差不齐,co_await 对编译器的版本要求太高。 加上 co_await 背后的机制也不容易理解。

所以,目前我使用的是 asio 作者发明的 stackless coro.

stackless coro 对系统的要求 = 0 。他自身是表现为一个 函数对象。

每次调用的时候,他都会从上次 return 的地方继续。

具体原理可以参考 asio 爸爸的文章 http://blog.think-async.com/2009/07/wife-says-i-cant-believe-it-works.html

那么,对单片机系统工程本身的需求是什么呢?

其实就是写了一个 Executor.

这个 Executor 不需要调用 OS 的 epoll_wait, 也不调用 GetIoCompletionPort, 更不会使用 select. 而是简单的忙等待,死循环。

正如操作系统的pid 0也是在一个死循环里调用CPU的 WAIT 指令。

这个简单的 Executor 就可以驱动 asio 的 stackless coroutine 了。

等日后单片机厂的编译器提上来了,就可以直接使用 asio 的 co_spawn 了。反正 asio 的 cp_spawn 其实也只依赖 Executor, 而不是依赖 asio 的 io_service.

Comments