完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
1. 在uC/OS-II的帮助手册内,作者特地强调绝对不能在OSInit()或者OSStart()内调用timer初始化程序,那会破坏系统的可移植性同时带来性能上的损失。所以,一个折中的办法就是: 在优先级最高的程序内调用,这样可以保证当OSStart()调用系统内部函数OSStartHighRdy()开始多任务后,首先执行的就是Timer初始化程序。或者专门开一个优先级最高的任务,只做一件事情,那就是执行 Timer初始化,之后通过调用OSTaskSuspend()将自己挂起来,永远不再执行。不过这样会浪费一个TCB空间。对于那些RAM吃紧的系统来说,还是不用为好。
2. 一些重要的uC/OS-II API介绍 任何一个操作系统都会提供大量的API供程序员使用,uC/OS-II也不例外。由于uC/OS-II面向的是嵌入式开发,并不要求大而全,所以内核提供的API也就大多和多任务息息相关。 主要的有以下几类: 1)任务类 2)消息类 3)同步类 4)时间类 5)临界区与事件类 我个人认为对于初级程序员而言,任务类和时间类是必须要首先掌握的两种类型的API. 下面我就来介绍比较重要的: 1)OSTaskCreate函数 这个函数应该至少再main函数内调用一次,在OSInit函数调用之后调用。作用就是创建一个任务。目前有四个参数,分别是任务的入口地址,任务的参数, 任务堆栈的首地址和任务的优先级。调用本函数后,系统会首先从TCB空闲列表内申请一个空的TCB指针,然后将会根据用户给出参数初始化任务堆栈,并在内部的任务就绪表内标记该任务为就绪状态。最后返回,这样一个任务就创建成功了。 2)OSTaskSuspend函数 这个函数很简单,一看名字就该明白它的作用,它可以将指定的任务挂起。如果挂起的是当前任务的话,那么还会引发系统执行任务切换先导函数OSShed来进行一次任务切换。这个函数只有一个参数,那就是指定任务的优先级。那为什么是优先级呢?事实上在系统内部,优先级除了表示一个任务执行的先后次序外,还起着分别每一个任务的作用,换句话说,优先级也就是任务的ID.所以uC/OS-II不允许出现相同优先级的任务。 3)OSTaskResume函数 这个函数和上面的函数正好相反,它用于将指定的已经挂起的函数恢复成就绪状态。如果恢复任务的优先级高于当前任务,那么还为引发一次任务切换。其参数类似 OSTaskSuspend函数,为指定任务的优先级。需要特别说明是,本函数并不要求和OSTaskSuspend函数成对使用。 4)OS_ENTER_CRITICAL宏 很多人都以为它是个函数,其实不然,仔细分析一下OS_CPU.H文件,它和下面马上要谈到的OS_EXIT_CRITICAL都是宏。他们都是涉及特定 CPU的实现。一般都被替换为一条或者几条嵌入式汇编代码。由于系统希望向上层程序员隐藏内部实现,故而一般都宣称执行此条指令后系统进入临界区。其实, 它就是关个中断而已。这样,只要任务不主动放弃CPU使用权, 别的任务就没有占用CPU的机会了,相对这个任务而言,它就是独占了。所以说进入临界区了。这个宏能少用还是少用,因为它会破坏系统的一些服务,尤其是时间服务。并使系统对外界响应性能降低。 5)OS_EXIT_CRITICAL宏 这个是和上面介绍的宏配套使用另一个宏,它在系统手册里的说明是退出临界区。其实它就是重新开中断。需要注意的是,它必须和上面的宏成对出现,否则会带来意想不到的后果。最坏的情况下,系统会崩溃。我们推荐程序员们尽量少使用这两个宏调用,因为他们的确会破坏系统的多任务性能。 6)OSTimeDly函数 这应该程序员们调用最多的一个函数了,这个函数完成功能很简单,就是先挂起当起当前任务,然后进行任务切换,在指定的时间到来之后,将当前任务恢复为就绪状态,但是并不一定运行,如果恢复后是优先级最高就绪任务的话,那么运行之。简单点说,就是可以任务延时一定时间后再次执行它,或者说,暂时放弃CPU的使用权。一个任务可以不显式的调用这些可以导致放弃CPU使用权的API,但那样多任务性能会大大降低,因为此时仅仅依靠时钟机制在进行任务切换。一个好的任务应该在完成一些操作主动放弃使用权,好东西要大家分享嘛! 3. 我们推荐程序员们尽量少使用OS_ENTER_CRITICAL宏和 OS_EXIT_CRITICAL宏两个宏调用,因为他们的确会破坏系统的多任务性能。why?? 4. 在以uC/OS为操作系统的项目中,系统可能要处理各种不同的中断请求,如果某个中断处理程序需要调用uC/OS的各种Post函数向任务发出消息,那么uC/OS建议中断服务程序的写法是: 1、保存全部CPU寄存器 2、调用OSIntEnter或OSIntNesting直接加1 3、执行用户代码做中断服务 4、调用OSIntExit 5、恢复所有CPU寄存器 6、执行中断返回指令 暂且称为“标准中断”方式,这种方式实际上是将这个中断处理加入了任务调度系统,也就是说这个中断可以引起任务的切换。 如果在中断处理中没有调用各种Post函数的话,则可以用一般的、象原来没有操作系统时的写法: 1、保存中断处理程序需要用到的CPU寄存器 2、执行中断处理 3、恢复保存了的CPU寄存器 4、执行中断返回指令 暂且称为“快中断”方式,按照这种方法定义的中断永远不会引起任务切换。 在uC/OS系统中,每个任务都要定义独立的栈空间,一个栈空间的使用包括5个部分: 1、任务包括的各个函数的调用返回地址 2、任务包括的各个函数中可能在栈上分配的局部变量 3、发生了“标准中断”方式定义的中断或任务被挂起时,所要保存的任务上下文 4、发生了“快中断”方式定义的中断时,中断处理程序所需要的栈空间 5、中断嵌套时,所要保存的中断嵌套上下文 在这些使用的部分中,1,2,3,4的内存占用量是比较容易估算的,最精确和保险的确定方法是:查看由C生成的asm文件,并计算各个函数的栈使用量。但是第5部分的栈空间使用量是随中断嵌套的深度而不断增加的,是不确定的,一般的方法只能定义一个充分大的栈空间,使之不会溢出。 为每个任务都定义一个充分大的栈空间,这在某些内存稀缺的小项目中是非常痛苦的,有时不得不增扩内存,这就会使成本增加。我深入研究了uC/OS后,认为,可以将所有任务栈空间使用的第5部分合并,这样将会大大的降低整个系统对内存的需求。 uC/OS的任务调度是靠OS_Sched和 OSIntExit来完成的,这两个函数中都要先判断一个叫OSIntNesting的系统变量,如果OSIntNesting不为0,则不进行任务切换。也就是说:在OSIntNesting为1(当前只有一个中断在处理中,并且没有嵌套的中断)时起,如果发生了嵌套的中断(不管嵌套的层数有深),那么在所有嵌套的中断一层一层地都返回直到 OSIntNesting再次为1时止,任务栈是不会切换的(栈指针都在一个任务的栈空间中变化)。 据此,我们可以这样改动:设置一个缓冲区OSInterruptStk,作为嵌套中断的栈空间 由所有任务共享,中断服务程序改为: 1、保存全部CPU寄存器 2、调用OSIntEnter或OSIntNesting直接加1 增加:2.1、判断OSIntNesting是否等于1,如果不是则转到3 增加:2.2、将栈指针SP保存到OSTCBCur-》OSTCBStkPtr 增加:2.3、将SP指向OSInterruptStk的栈顶(注意栈增长的方向)。 3、执行用户代码做中断服务 4、调用OSIntExit 增加:4.1、判断OSIntNesting是否等于0,如果不是则转到5 增加:4.2、从OSTCBCur-》OSTCBStkPtr中恢复栈指针SP 5、恢复所有CPU寄存器 6、执行中断返回指令 并且要修改OSIntCtxSw函数,原始的OSIntCtxSw函数的写法是: 1、调整栈指针来去掉在调用:OSIntExit,OSIntCtxSw过程中入栈的多余内容 2、将当前任务栈指针保存到OSTCBCur中(OSTCBCur-》OSTCBStkPtr = __SP__) 3、如果需要则调用OSTaskSwHook 4、OSTCBCur = OSTCBHighRdy 5、OSPrio = OSPrioHighRdy 6、从OSTCBCur中恢复栈指针(__SP__ = OSTCBCur-》OSTCBStkPtr) 7、恢复保存了的CPU寄存器 8、执行中断返回指令 新的写法只需将原写法中的1,2去掉即可,因为1,2步只是保存旧任务的栈指针,而新的写法中,这些步被移到了“中断服务程序”中的2.2. 5. 注意ARM Image for uCOSII for lpc213x 模板中的TargetInit() 对于很多使用ZLG arm Image for uCOSII for lpc213x 模板的初学者,常常会置疑使用该模板后自动生成的target.c文件,和在程序中调用的TargetInit()函数,我和 Zgpswh都是如此,这个问题当初困扰了很久:当用户程序中不调用TargetInit()时,发现内核能运行,但是等待机制失灵,调用 TargetInit(),很多硬件中断打不开,后来,在很多热心人的指点下解决了,现重新总结如下: 请仔细察看ZLG模板里的target.c文件,这里的TargetInit()如下: void TargetInit(void) { OS_ENTER_CRITICAL(); srand((uint32)TargetInit); VICInit(); Timer0Init(); OS_EXIT_CRITICAL(); } 其中的Timer0Init();用于硬件定时器0的初始化,事实上,ZLG的移植代码的μC/OS-Ⅱ的时钟节拍是通过定时器0提供的,不在主程序里调用这个函数,μC/OS-Ⅱ的时钟源就无法打开;但是,没有开启时钟源的μC/OS-Ⅱ是同样能运行的,只是内核提供的延时和等待时限机制都不起作用,系统虽能将就运行,但因没调用TargetInit()而使内核功能不健全。 请注意,TargetInit()中的另一个函数VICInit ()是用来中断的初始化,它其中含有对UART0中断的分配,在用户程序里需要根据使用的硬件中断修改这部分代码,否则,这些硬件中断无法开启; 再者,在不调用TargetInit()的时候,硬件的中断初始化是在硬件初始化函数中完成,这也就是Zgpswh提到的现象:不调用TargetInit()内核运行异常,调用了却开不了UART0的中断。解决的方法如下: 这在《arm嵌入式系统基础教程》的430页7.4.3节中论述的很清楚: ……关键在于把程序与芯片相关中断源挂接,使芯片在产生相应的中断后会调用相应的处理程序。这需要做两方面事情: 1. 增加汇编接口的支持。…… 2. 初始化向量中断控制器。…… 按照一下方法完成中断源的的挂接: 1、增加汇编接口的支持。方法是修改IRQ.s文件,在末尾添加本句代码: UART0_Handler HANDLER UART0_Exception 追加定义了通用串口0 中断句柄。 2、初始化向量中断控制器。将target.c文件中的VICInit()修改如下: void VICInit(void) { extern void IRQ_Handler(void); extern void Timer0_Handler(void); extern void UART0_Handler(void); VICIntEnClr = 0xffffffff; VICDefVectAddr =(uint32)IRQ_Handler; VICVectAddr0 =(uint32)Timer0_Handler; VICVectCntl0 =(0x20 | 0x04); VICIntEnable = 1《《4; VICVectAddr14 =(uint32)UART0_Handler; VICVectCntl14 =(0x20 | 0x06); VICIntEnable = 1《《6; } 此为初始化向量中断控制器。包括定时器0和串口0,特别要注意的是,一定不可以省略对定时器0的初始化。中断里,不可以调用延时 请注意,中断里面是不支持等待机制的。请用自己编的一个软件延时,问题就可以解决了。 中断源挂接正确是没问题的。 6.问 void TargetInit(void) { OS_ENTER_CRITICAL(); srand((uint32)TargetInit); VICInit(); Timer0Init(); OS_EXIT_CRITICAL(); } 在此单独用srand()函数有什么作用(用了有什么好处,不用又会怎样),一般srand()用于给rand()设定种子(即srand给定rand运算式子的第一个值)。查了FAQ(P22)仅仅说明了 seed的译文。 答2 你说的没有错,他就是用来设置随机数的种子。 每次编译一次,void TargetInit(void)函数在Ram或者Flash中的地址都不一样(即种子也不一样)。如果你在程序中不用随机函数rand(),那么srand()在这里对你来说是没有意义的,如果你要用rand(),那么每次编译程序后你的rand()产生的随机数就不一样。当然,你也可以自己种种子咯。 答3 srand((uint32) TargetInit);是初始化uc/os-ii随机数函数rand();也就是说给随机数一个基值,以后调用相关随机数函数时,随机数就在此基值的基础上改变。 只要不调用rand()函数,就可以去掉这句。 7.在arm上移植操作系统有一点需要注意:建立任务的任务,最好不要做复杂的工作。频繁的访问其他硬件或者做时序要求比较严的工作容易造成系统死机,希望大家多多注意。我的做法是:将建立任务的任务,闲置起来,但是不能进入死循环。方法是:利用一个空邮箱,让任务无限期的等待,这样可以实现与其他任务的切换。 8.//定义与编译器无关的数据类型 typedef unsigned char BOOLEAN;//布尔变量 typedef unsigned char INT8U;//无符号8位整型变量 typedef signed char INT8S;//有符号8位整型变量 typedef unsigned short INT16U;//无符号16位整型变量 typedef signed short INT16S;//有符号16位整型变量 typedef unsigned int INT32U;//无符号32位整型变量 typedef signed int INT32S;//有符号32位整型变量 typedef float FP32;//单精度浮点数(32位长度) typedef double FP64;//双精度浮点数(64位长度) typedef INT32U OS_STK;//堆栈是32位宽度 注:这里为什么用typedef,因为如果用#define,那么代码中的每一个相应的类型都会被替代,很有可能会出现问题,毕竟他只是一个替代的关系,且编译时间会增加,而用typedef则不会,它就相当于我们C++里面的引用,一样的思维,在这里面就是说多了一个名称。还有要注意的是我们怎么知道unsigned char 就是无符号8位整型变量,可以arm公司里面下载ADS_CompilerGuide_D.PDF文件,或者在你所装的ADS1.2目录里面有一个文件夹叫PDF,打开它就可以了。 |
|
相关推荐
1 个讨论
|
|
只有小组成员才能发言,加入小组>>
1912个成员聚集在这个小组
加入小组我的项目我做主,使用GN+Ninja来完成构建系统(VSCode开发RT106X)
36369 浏览 0 评论
NXP IMX8应用处理器快速入门必备:技巧、使用、设计指南
4405 浏览 0 评论
6063 浏览 1 评论
6772 浏览 0 评论
NXP i.MX6UL开发板(linux系统烧录+规格+硬件+模块移植)使用手册
4221 浏览 0 评论
625浏览 2评论
求助,S32G上Core M启动后如何让Core A在Flash指定位置加载uboot?
621浏览 2评论
ESP32-WROVER-IE + LAN8720以太网,GPIO0电压只有1.6v,无法正常进入spi flash boot模式如何解决?
616浏览 2评论
求分享适用于PN7160 Android的NFC工厂测试应用程序
698浏览 2评论
802浏览 2评论
小黑屋| 手机版| Archiver| 电子发烧友 ( 湘ICP备2023018690号 )
GMT+8, 2024-11-30 03:42 , Processed in 1.100418 second(s), Total 50, Slave 42 queries .
Powered by 电子发烧友网
© 2015 bbs.elecfans.com
关注我们的微信
下载发烧友APP
电子发烧友观察
版权所有 © 湖南华秋数字科技有限公司
电子发烧友 (电路图) 湘公网安备 43011202000918 号 电信与信息服务业务经营许可证:合字B2-20210191 工商网监 湘ICP备2023018690号