第4章第3节 实时事件触发的实时抢…
来源:互联网 发布:中国程序员联合开发网 编辑:程序博客网 时间:2024/06/16 09:55
目前更新到5.3节,请在http://dl.dbank.com/c02ackpwp6下载5.3节的全部文档
本节源代码请在http://dl.dbank.com/c0z9e2wbch下载
本节用户代码在打印输出方面做了较大的修改,从本节开始,串口打印功能不再由任务实时向串口打印,而是由任务先将字符串打印到内存,然后使用一个低优先级的任务从内存中取出字符串打印到串口。串口是一个低速率的外设,9600的波特率差不多1ms打印1个字符,打印几十个字符的字符串时就需要几十ms,如果采用原有的打印方式,在打印字符串的过程中可能会发生任务切换,如果切换后的任务也在向串口打印字符串,那么这2个任务的字符就会混在一起,串口输出的字符发生混乱,得不到我们预期的输出结果。如果采用新的打印方式,每个任务需要从内存申请一个消息缓冲用来存放打印的字符,申请消息缓冲的过程非常快,可以将这一过程用中断锁住,防止其它任务干扰。消息缓冲对于任务来说,它是私有的,任务将字符串打印到消息缓冲的过程不会受任务切换的影响。当任务向消息缓冲打印完成后,先锁中断,然后将消息缓冲挂入消息队列,再解锁中断。消息打印任务会不断的查询消息队列,从队列中获取消息缓冲,将消息缓冲里面的数据打印到串口。向串口打印消息采用的是中断方式,消息打印任务将每条消息的第一个字符输出到串口,消息中剩下的其余字符会由上个字符触发的中断打印到串口,直至打印完最后一个字符。由于向串口打印数据是由同一个任务完成的,因此不会存在打印字符混乱的情况。
图
我们可以定义一个消息缓冲池,所有的消息缓冲都在这里:
BUFPOOL
展开里面的结构体:
typedef
{
}BUFPOOL;
typedef
{
}MSGBUF;
其中strFreeList是空闲消息链表,初始化消息缓冲池的时候将所有消息缓冲挂接到这个链表上,申请消息缓冲时从该链表就可以获取到空闲的消息缓冲。当消息缓冲使用完毕后需要再挂接到这个链表上,可以继续重复使用。strChain是每个消息缓冲挂接到空闲消息链表的节点,ucLength中存储的是消息的长度,ucCounter中存储的是消息收发时的计数,aucBuf是存放消息的数组。
任务向消息缓冲填充完字符串之后,需要将消息挂入队列,目前的队列结构就是一个链表,后续章节我们会再补充一些东西进来。
typedef
{
}M_QUE;
关于消息打印部分的细节就不做过多介绍了,请读者自行参考代码。
在学习C语言时,我们都使用过printf函数,这个函数可以根据个人需要,很灵活的向显示器终端打印数据,比如,我们可以按照下面的方式输出:
printf("Wanlix
printf("%d,
不知道你注意过没有,printf函数的参数个数是可变的,上面的第一个例子只有1个参数,第二个例子有3个参数,这点与我们一般所使用的函数是不同的。在Mindows中,我们仿造printf函数,将向内存打印的函数DEV_PutStrToMem也编写成一个参数可变的函数,下面我们一起来看看这是怎么实现的。
参数可变的函数原型定义为:
void
其中比较特别的是“...”,这三个点表示省略的参数。可变参数函数的原理非常简单,可变参数函数的参数不像我们前面讲过的那样,使用R0~R3寄存器传递参数,而是直接使用堆栈传递参数,而且这些参数都是连在一起存放的,而函数原型中第一个参数是固定的,我们可以获取到第一个参数的地址&pvStringPt,然后将这个地址加4就可以得到第二个参数的地址,再加4就是第三个参数的地址,依次类推,这样就可以获取到任意多的参数地址了,有了参数地址那么获取参数的内容也就不成问题了。还有一个问题要解决,DEV_PutStrToMem函数是怎么知道它究竟有多少个参数的呢?注意到参数里的“%”号了么,每个%号对应一个参数,我们只需要查找%号的个数就可以确定可变参数的个数,再将每个可变参数转换为对应的%号后面的格式就可以实现DEV_PutStrToMem函数的功能了。
图
可变参数函数的原型可以有多种形式,但必须要保证:
1.必须有第一个参数,通过这个参数才能获取到参数存放在栈中的首地址,后面的参数才是可变的。
2.在设计可变参数时需要能体现出可变参数的个数,如上面查找第一个参数中%号的方法,或者将第一个参数定义为可变参数的个数,或者其它方式。
在C语言标准头文件stdarg.h里面已经为可变函数定义了几个宏,使用这些宏也可以实现可变参数函数,原理都一样,
本节内容已经介绍完毕,设计一下测试函数来验证本节新增加的功能。本节共有4个测试函数TEST_TestTask1~TEST_TestTask4,每个函数的结构非常相似,循环执行打印、运行、延迟这3个过程。
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00038
00039
00040
00041
00042
00043
00044
00045
00046
00047
00048
00049
00056
00057
00058
00059
00060
00061
00062
00063
00064
00065
00066
00067
00068
00069
00070
00071
00072
00079
00080
00081
00082
00083
00084
00085
00086
00087
00088
00089
00090
不同的是TEST_TestTask1任务优先级为4,创建时不使用任务选项参数,默认为ready态。TEST_TestTask2任务优先级为6,创建时使用任务选项参数,为ready态。TEST_TestTask3任务优先级为2,创建时使用任务选项参数,为delay态,延迟2000个ticks,delay态结束后会使用MDS_TaskWake函数唤醒TEST_TestTask4任务。TEST_TestTask4任务优先级为1,创建时使用任务选项参数,为delay态,永久延迟。
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036
00037
00038
00039
00040
00041
00042
00043
00044
00045
00046
如果单从任务优先级来看,应该是TEST_TestTask4任务最先运行,但TEST_TestTask4任务处于永久delay状态,不能运行。剩下的优先级最高的任务是TEST_TestTask3,但TEST_TestTask3任务也处于delay状态,需要延迟2000个ticks后才能重新参与调度。TEST_TestTask1和TEST_TestTask2任务尽管使用了不同的任务参数,但都处于ready态,TEST_TestTask1的优先级高,先运行TEST_TestTask1,在TEST_TestTask1任务处于delay态时就会运行TEST_TestTask2任务。在前2000个ticks,TEST_TestTask1和TEST_TestTask2任务会交替运行,在2000个ticks时,TEST_TestTask3任务delay时间耗尽,参与调度,应该可以看到TEST_TestTask3任务会打断其它任务开始运行。在TEST_TestTask3运行之后它立刻唤醒了TEST_TestTask4任务,应该还可以看到在TEST_TestTask3任务运行后立刻被TEST_TestTask4任务打断了,此后这4个任务按照优先级的调度方式交替运行。
图
读者可以从网站下载视频观看串口输出的过程。在视频中我们看到要么是在一段时间内没有字符打印出来,要么是几个任务的字符一起打印出来。这是因为打印字符的任务处于最低优先级,只有当其它任务都处于delay状态时它才可以运行,将内存中的字符打印到串口上来。
我们在这4个函数里使用了DEV_DelayMs函数模拟任务的业务功能,该函数会连续运行几秒时间,这段时间内的CPU占有率是100%,因此字符打印到串口的时刻相比任务将字符打印到内存的时刻有较大的延迟,如果CPU占有率较低的话,我们可以看到几乎是实时的打印数据。
- 第4章第3节 实时事件触发的实时抢…
- 第4章第3节 实时事件触发的实时抢…
- 第4章第3节 实时事件触发的实时抢…
- 第4章第2节 定时器触发的实时抢占…
- 第4章第2节 定时器触发的实时抢占…
- Wireshark使用教程:第4章 实时捕捉数据包
- 第2章 实时系统概念
- 第2章 实时系统概念
- DataGridView DataGridViewCheckBoxColumn编辑时实时触发事件
- DataGridView DataGridViewCheckBoxColumn编辑时实时触发事件
- DataGridView DataGridViewCheckBoxColumn编辑时实时触发事件
- Dive into python 第4章 自省的威…
- 第2章第3节 ARM7芯片的函数调用标…
- 第3章第1节 两个固定任务之间的切…
- Linux 实时技术与典型实现分析, 第 2 部分: Ingo Molnar 的实时补丁
- 使用实时 Java 进行开发,第 1 部分: 探索实时 Java 的独特功能
- Linux 实时技术与典型实现分析, 第 2 部分: Ingo Molnar 的实时补丁
- 《嵌入式实时操作系统uC/OS-II》_第2章_实时系统概念
- 第4章第2节 定时器触发的实时抢占…
- 第4章第2节 定时器触发的实时抢占…
- 第4章第3节 实时事件触发的实时抢…
- 第4章第3节 实时事件触发的实时抢…
- MXNet的数据读取:data.py源码详解
- 第4章第3节 实时事件触发的实时抢…
- Vmware 将kalilinux装进 U盘
- cookie 和session 的区别详解
- JavaScript(jquery)写的像素游戏贪吃蛇
- zabbix使用sendEmail实现邮件报警
- 第4章第4节 任务切换钩子函数
- 第4章第5节 任务创建和任务删除钩…
- 第4章第6节 任务自结束
- 第4章第7节 二进制信号量(一)