TreeOS学习总结
TreeOS在自我介绍时说自己提出的一种面向场景编程的构件化方法,解决了标准化系统软件架构的问题。TreeOS是一种无核的、软件构件化的、实时嵌入式操作系统。 笔者觉得的这是一种前后台系统(文章后面会解释什么是前后台),它对于编程各个任务让我错乱的我提供了特别大的帮助,目前网上相关的代码很少,我通过官网找到了它的两百页的使用指南,大受裨益。如果你也是苦恼于单片机各个任务的管理,或者在准备电赛的程序,这里强烈推荐大家阅读它前80页内容。[TREEOS操作指南] 就像和《疯狂动物城》里的闪电对话一样 定时器中断中的显示—按键结合函数可以在公共while中调用,以节约中断资源
场景子场景的子场景编写规范。 为提高编程效果,当A为主场景时。与B同级的场景,如B1、B2等建议分成不同的.C和.H文件,但对于场景B而言,C和D可看作相同.C文件中的不同任务,通过不同函数来实现,再通过switch来选择调用。 这种多级树形结构可将任务持续细化,但也规定了必须有且只有一个主场景。但是如果持续细化任务嵌套导致过于占用堆栈。
常用的单片机裸机程序框架
如果不上操作系统的话(如freetos 、rt-thread ),裸机框架其实可以完成各种任务,并且系统非常考验处理器的性能,也会占用过多的资源。所以我打算还是主要学习逻辑框架。以下学习来自野火的《RT-Thread内核实现与应用开发实践指南》
1.轮询系统
最常用也是最简单的一种方法,没有外部事件时常用。
int main(void)
{
init_something();
while(1)
{
do_something1();
do_something2();
do_something3();
}
}
2.前后台系统
前后台系统是在轮询系统的基础上加入了中断。外部事件的响应在中断里面完成,事件的处理还是回到轮询系统中完成,中断在这里我们称为前台,main 函数里面的无限循环我们称为后台。 特点:事件的响应和处理分开,事件的处理还是在后台里面顺序执行的,但相比轮询系统,前后台系统 确保了事件不会丢失,再加上中断具有可嵌套的功能,这可以大大的提高程序的实时响应能力
int flag1 = 0;
int flag2 = 0;
int flag3 = 0;
?
int main(void)
{
HardWareInit();
?
for (;;) {
if (flag1) {
DoSomething1();
}
?
if (flag2) {
DoSomethingg2();
}
?
if (flag3) {
DoSomethingg3();
}
}
}
?
void ISR1(void)
{
flag1 = 1;
DoSomething1();
}
?
void ISR2(void)
{
flag2 = 2;
?
DoSomething2();
}
?
void ISR3(void)
{
flag3 = 1;
DoSomething3();
}
3.多线程系统
相比前后台系统,多线程系统的事件响应也是在中断中完成的,但是事件的处理是在线程中完成的。在多线程系统中,线程跟中断一样,也具有优先级,优先级高的线程会被优先执行。当一个紧急的事件在中断被标记之后,如果事件对应的线程的优先级足够高,就会立马得到响应。相比前后台系统,多线程系统的实时性又被提高了。 线程:在多线程系统中,根据程序的功能,我们把这个程序主体分割成一个个独立的,无限循环且不能返回的小程序,这个小程序我们称之为线程。每个线程都是独立的,互不干扰的,且具备自身的优先级,它由操作系统调度管理。
int flag1 = 0;
int flag2 = 0;
int flag3 = 0;
int main(void)
{
HardWareInit();
RTOSInit();
RTOSStart();
}
void ISR1(void)
{
flag1 = 1;
}
void ISR2(void)
{
flag2 = 2;
}
void ISR3(void)
{
flag3 = 1;
}
void DoSomething1(void)
{
for (;;)
{
if (flag1) {
}
}
}
void DoSomething2(void)
{
for (;;)
{
if (flag2) {
}
}
}
void DoSomething3(void)
{
for (;;)
{
if (flag3) {
}
}
}
系统重要概念
临界段
临界段用一句话概括就是一段在执行的时候不能被中断的代码段。在系统里面,这个临界段最常出现的就是对全局变量的操作,全局变量就好像是一个枪把子,谁都可以对他开枪,但是我开枪的时候,你就不能开枪,否则就不知道是谁命中了靶子。 那么什么情况下临界段会被打断?一个是系统调度,还有一个就是外部中断。在进入临界段之前,我们会先把中断关闭,退出临界段时再把中断打开。 在STM32中,简单粗暴的方法就是直接:
INTX_DISABLE();
.....................
INTX_ENABLE();
共享资源
这里通俗来讲就是不同场景之间的全局变量,多个任务共同使用的资源叫做共享资源(如数据、文件、输入输出设备等)。为了防止数据被破坏或设备异常,每个任务使用共享资 源时,必须独占该资源,不能被打断。最常用的做法是先关中断,运行临界区代码,然后重新开中断。
阻塞与非阻塞赋值
这段概念来源于学习FPGA,若大家想深入了解可以参考正点原子的FPGA教程。
阻塞: 非阻塞
拆分与拆分技术
学习来自treeos: 对于运行时间长、实时性要求不高的普通任务,可以拆分成两部分或两部分以上的任务,而每部分运行时间都较短。每次循环只运行其中的一部分,多次运行才能获得结果。这实际上是一种多道系统的处理方法,也称为分时调度。 例如,对键盘可以通过多次循环扫描的方法,监控按键释放动作出现后再进行处理。这样可以避免占用处理 器长时间延时等待的问题。拆分技术可以减少循环总时间。 拆分技术会经常用到。 而对于实时性要求较高的普通任务,可以在任务顺序队列中插入两次或两次以上的运行机会,使任务的实时性要求获得满足。插入技术使循环总时间增加。例如,在一个场景中,任务顺序队列完成一次循环最大时间需要 50ms,而某个任务则需要 30ms 内执行一次,那么 就可以采用这种插入技术,使该任务的实时性要求获得满足。
底半处理(优先任务=中断标志+while触发任务)
尽管嵌入式系统对中断程序处理时间一般不做限制,但是普遍的做法是尽量缩短中断处理时间,以免影响其它中断服务或紧急任务。 一种有效的做法是把中断处理任务过程分为两部分,上半部分(top half)处理紧急事务,例如设置标志或者接收输入数据单元,这部分运行时间很短,放在中断服务程序中进行。然后把一些需耗时处 理或对时间要求不太紧急的事务放在之后运行(例如放在后台程序 中),这部分程序称为底半部分(bottom half)。
下一次会总结一下状态机的学习经验
|