在英飞凌Tricore内核单片机上移植多核uCOS-III
文章平均质量分 62
从零开始讲解如何在Tricore内核单片机上移植RTOS(uC/OS-III),从原理到具体实现方法,并提供最终移植完成的源代码,以及参考资料和开发环境。
老孟的孟不是很老的孟
汽车行业基础软件工程师
展开
-
在Tricore上移植μC/OS-III——0.1总述
本文是《在Tricore上移植μC/OS-III》系列文章的第一篇,先简单介绍下整个项目。Tricore是英飞凌旗下的一款单片机内核,目前汽车电子领域常用的TC2XX和TC3XX等系列的单片机都是基于Tricore内核,有双核有三核,还有1个主核+一个校验核这种“1.5核”配置。我这次用的是TC265,双核,但在移植μC/OS的时候还是只用到了一个主核,日后有机会的话会继续研究一下RTOS调用多核。关于开发环境,试用和学习阶段英飞凌提供了Aurix Development Studio这个软件,集成了Ta原创 2021-04-25 15:52:39 · 4283 阅读 · 1 评论 -
在Tricore上移植μC/OS-III——1.1 AURIX Development Studio的使用
本文主要介绍AURIX Development Studio软件的安装和使用方法。英飞凌针对Tricore系列单片机推出了一款免费的软件——AURIX™ Development Studio,集成了iLLD库,内置Tasking compiler和Tasking Debugger,但这两个模块只能用于非商业用途,可供学习和试用。iLLD库是英飞凌为TC2XX和TC3XX Tricore系列单片机提供的底层驱动库,有完善的使用手册,和较好的移植性。可从下面的链接获取软件安装包,软件安装好后,新建工程时会自原创 2021-04-25 16:24:30 · 1889 阅读 · 1 评论 -
在Tricore上移植μC/OS-III——1.2 点亮一个LED灯
点亮LED灯就是嵌入式领域的“Hello world”,我们同样从点灯开始,熟悉一下开发环境,让单片机跑起来。首先在AURIX Development Studio中新建一个工程,包含以下两个头文件:#include "SysSe/Bsp/Bsp.h" //包含延时函数#include "Port/Io/IfxPort_Io.h" //Ports控制API其中Bsp.h中定定义了延时函数,IfxPort_Io.h是iLLD库中Ports的驱动接口,我们点亮LED就是用的Ports。打开工程的原创 2021-04-25 17:08:36 · 738 阅读 · 0 评论 -
在Tricore上移植μC/OS-III——1.3 发送CAN报文
在调试程序的时候,我们可以通过CAN报文把一些关键变量的值输出,来观察程序的运行状态,下面我们就来看一下如何输出CAN报文,并在发送成功后,触发一个CAN中断。1.首先要包含CAN驱动头文件:#include "SysSe/Bsp/Bsp.h"#include "Port/Io/IfxPort_Io.h"#include "Multican/Can/IfxMultican_Can.h" //包含CAN控制API2.定义CAN模块、节点、MessageObject// CAN handleIf原创 2021-04-25 17:25:12 · 752 阅读 · 0 评论 -
在Tricore上移植μC/OS-III——2.1 RTOS的任务切换原理
本章节主要讲述Tricore所特有的一种上下文环境管理机制——CSA。本文先来介绍一下RTOS的任务切换原理,以便于后文理解CSA机制。RTOS简单说就是一个实时抢占式的操作系统内核,在单片机中只有一个CPU的情况下,当低优先级任务正在运行的时候,如果有高优先级的任务想要开始运行,那么内核要先保存低优先级任务的运行状态,然后切换到高优先级的任务开始运行,运行完毕后,再恢复之前保存的低优先级任务状态,切换回低优先级任务。通常我们将任务切换前后单片机的状态称为上下文环境(Context)。单片机中,通常有一原创 2021-04-25 17:33:47 · 1516 阅读 · 0 评论 -
在Tricore上移植μC/OS-III——2.2 Tricore的CSA机制介绍
CSA实际上是一个链表,包含了N个64字节长度的节点,每个节点可以存储16个4字节长度的寄存器值,其中第一个寄存器是LinkWord,指向前一个节点,也叫PCXI(Previous Context Information);此外还可以存储15个系统寄存器的值,Tricore把系统寄存器分成了两组:Upper Context(后文统称UCX):包含地址寄存器A[10]A[15],和数据寄存器D[8]D[15],以及PSW(Program Status Word)和上文的PCXI。Lower Contex原创 2021-04-25 17:37:09 · 2234 阅读 · 1 评论 -
在Tricore上移植μC/OS-III——2.3 CSA的初始化状态
在单片机上电初始化的时候,会进行CSA的初始化,如果用的是iLLD库,我们可以在 IfxCpu.h 中找到 IfxCpu_initCSA(uint32 *csaBegin, uint32 *csaEnd) 这个函数:IFX_INLINE void IfxCpu_initCSA(uint32 *csaBegin, uint32 *csaEnd){ uint32 k; uint32 nxt_cxi_val = 0; uint32 *prvCsa = 0U; ui原创 2021-04-25 17:39:07 · 1103 阅读 · 0 评论 -
在Tricore上移植μC/OS-III——2.4 CSA的存储和调用
CSA的存储:当程序发生**函数调用(CALL)、中断(Interrupt)和陷阱(Trap)**时,系统自动将当前程序的UCX保存至FCX所指向的空CSA节点,同时寄存器PCXI指向这个节点,FCX向后移动一个节点。LCX不会自动保存,需要用户根据需要进行存储。但在实际使用的时候发现,对于中断和Trap, Tasking编译器会自动生成代码,保存LCX。保存LCX的过程和UCX相同,同样会占用一个CSA节点,已保存的LCX和UCX连接在一条链表中,用PCXI字段中的UL(见上图)位来标识该节点是UCX原创 2021-04-25 17:41:12 · 1233 阅读 · 1 评论 -
在Tricore上移植μC/OS-III——2.5 CSA机制运行示例
我们来分析一下程序从startup ->main ->Func -> main 的过程中,单片机自动保存UCX的过程。图片中带_Old后缀的表示该寄存器在上一个步骤中的值。①程序跳转进main函数前,CSA的状态一直保持上文的初始化状态。②程序跳转进main函数后,因为发生了函数调用,所以存储了一个UCX到CSA[0],PCXI指向该节点,FCX后移一个节点:③程序从main函数跳转进Func函数后,同样存储一个UCX,PCXI和FCX都向后移动一个节点,此时CSA[1]的PC原创 2021-04-25 17:47:41 · 1018 阅读 · 0 评论 -
在Tricore上移植μC/OS-III——2.6 有关CSA的其它注意事项
CSA的使用需要十分谨慎,在存储CSA的时候,因为有LCX和UCX两种类型,恢复的类型也要相对应。比如,调用函数时自动存储的是UCX,即PCXI所指向的是UCX,函数运行完毕后返回时恢复的也是UCX,如果在被调用的函数中用汇编指令“RET”(见上文)直接返回,系统也会自动恢复UCX,如果我们在函数中调用了恢复LCX的指令“RSLCX”,则会进Trap。同样的,如果我们进入函数后,手动存储了LCX,我们就需要先调用“RSLCX”恢复LCX,再让程序自动返回,或调用“RET”指令返回,否则会报错进入Trap。中原创 2021-04-25 17:56:05 · 965 阅读 · 0 评论 -
在Tricore上移植μC/OS-III——3.1 如何实现任务状态的存储
本章节将从原理到实践来详细讲解如何实现上下文的切换。本章节所涉及到的代码都在源码的 os_cpu_c.c 和 os_cpu.h 中,这两个文件μC/OS-III中与单片机内核强相关的文件,移植到不同内核的单片机上的时候通常都要进行修改。本文先简要描述一下如何实现任务状态的存储。μC/OS中,为每个任务分配了一个任务控制块(TCB-Task Control Block),存在RAM中,里面包含了这个任务所有的信息,包括入口函数、栈顶位置、优先级等等,这些信息是由操作系统来控制的。当需要切换任务时,对于常规原创 2021-04-26 09:21:30 · 731 阅读 · 0 评论 -
在Tricore上移植μC/OS-III——3.2 任务初始化
在创建一个任务的时候,按我们前文说的,需要给它分配一个CSA链表,并对其中寄存器的内容进行必要的初始化。我们看一下这一步都做了那些工作。代码见 os_cpu_c.c 中的 OSTaskStkInit()函数。① 我们首先根据Tricore的UCX和LCX的结构,定义两个结构体:typedef struct _OS_UCX /* TC upper context structure */{ CPU_INT32U _PCXI; /* upper co原创 2021-04-26 09:25:37 · 679 阅读 · 0 评论 -
在Tricore上移植μC/OS-III——3.3 任务切换
任务切换函数是 OSCtxSw() ,它在切换任务时被调用,具体如何调用的我们下一章节再讲,这里先知道:我们在想要从原任务切换到新任务时,先在原任务中触发一个Trap,再在Trap中调用这个函数。触发Trap的时候会依次自动保存原任务的UCX和LCX(UCX为系统自动保存,LCX的保存是Tasking编译器完成的),从Trap中调用OSCtxSw()的时候,又会保存一个Trap函数的UCX,所以当前PCXI所指向的CSA链表状态是下面这样的:基于以上信息,我们来看一下OSCtxSw()函数的内容:①原创 2021-04-26 09:29:13 · 883 阅读 · 3 评论 -
在Tricore上移植μC/OS-III——3.4 在中断中执行任务切换
在中断中执行任务切换时,调用的是OSIntCtxSw()函数。任务切换的原理和上面是一样的。系统进入中断的时候也会自动保存原任务的UCX和LCX,区别就是进入中断后到调用OSIntCtxSw()之间多了两层函数嵌套,也就是多存储了两个UCX,所以在调用OSCtxSw()之前,要先将这两个UCX释放掉。如图中所示,前三行使系统跳转到第四行的函数“j1”,消耗掉一个UCX,接下来三行使系统跳转到第七行的函数“j2”,再消耗掉一个UCX,然后在j2中直接跳转至函数OSCtxSw(),注意这里不是调用,而是直接跳原创 2021-04-26 09:36:06 · 710 阅读 · 3 评论 -
在Tricore上移植μC/OS-III——4.1 Tricore的Trap机制
前两章分别介绍了Tricore内核的CSA机制和上下文切换的具体实现方法。这章主要讲用Trap机制来调用上下文切换函数(OSCtxSw()),以及系统时钟的实现等内容。本文先来讲Tricore的Trap机制。/***************************************************/Trap简单来说,就是在发生一些异常情况或错误操作的时候,系统自动进入一个类似于中断的函数中,以防止错误的操作对系统产生损害,并允许用户对系统当时的运行状态进行跟踪查看,和ARM的HardF原创 2021-04-26 09:44:02 · 1680 阅读 · 1 评论 -
在Tricore上移植μC/OS-III——4.2 System Call的具体使用方法
iLLD库中已经为我们写好了Trap函数,我们需要做的就是使能并编写一个Hook函数。① 首先要在Ifx_Cfg.h中,使能 IFX_CFG_EXTEND_TRAP_HOOKS 这个宏定义:/*********************************************************************************************************************//*---------------------------------Conf原创 2021-04-26 09:54:32 · 851 阅读 · 0 评论 -
在Tricore上移植μC/OS-III——4.3 系统时钟的实现
在RTOS中,通常都需要一个系统时钟中断,以固定的频率触发,以为系统提供计时和任务调度功能。本次的系统中断采用Tricore的STM0定时器,每1ms触发一次,即系统时钟频率是1KHz。相关函数在Bsp.c中。操作系统中与系统时钟相关的两个函数分别是:初始化函数:OS_CPU_SysTickInit (CPU_INT32U cnts),传入的参数cnt就是时钟振荡多少次后触发中断,关于这个数值我们后面再说。时钟滴答函数:OS_CPU_SysTickHandler (void),每次触发系统中断的时原创 2021-04-26 09:56:58 · 592 阅读 · 0 评论 -
在Tricore上移植μC/OS-III——4.4 中断操作和CntLeadZeros函数
1. 开启和关闭中断:μC/OS在程序中比较关键的位置,会设置临界区,也就是关闭所有中断,保证程序在这段运行时间内不被打断。关闭和开启中断的接口在cpu.h中,如下图所示,直接调用的iLLD中的接口:/************************************************************************************/#define CPU_INT_DIS() do { cpu_sr = disableInterrupts(); }原创 2021-04-26 10:04:38 · 547 阅读 · 0 评论 -
在Tricore上移植μC/OS-III——5.1 μC/OS实现多核任务调度的基础工作
前三章已经完整介绍了在Tricore系列单片机上运行单核RTOS的方法,本章中我们来研究一下扩展调用多核。关于这个问题能找到的资料不多,文中算法、策略等大部分东西是我自己定的,欢迎大家交流探讨。仍以TC265为例,这款单片机共有两个CPU。本文先来做一些基础准备工作。μC/OS-III上,与任务调度算法强相关的几个参数有:OSPrioCur:当前任务优先级OSPrioHighRdy:最高就绪任务优先级OSTCBCurPtr:当前任务TCB指针OSTCBHighRdyPtr:最高优先级任务TCB指原创 2021-04-26 10:11:23 · 1050 阅读 · 0 评论 -
在Tricore上移植μC/OS-III——5.2 一种没能实现的理论最优任务调度算法
!!!注意:这种方法在Tricore上没有实现,原因见下文,但我仍然把它写出来了,仅供参考。这种方法就是指:任务和CPU之间没有绑定的关系,每次发生任务调度的时候,都选取2个优先级最高的任务,分配给两个核。理论上来说,这种方法能保证整个系统中优先级最高的两个任务总是能够得到执行,是最合理的,且对CPU利用率最高,但任务调度算法更为复杂,我们在为每个核分配任务的时候,需要考虑:**已经在某个CPU上运行的任务,如果仍被调度,我们要保证它仍被分配给这个CPU,而不被分配给其它CPU,否则会产生不必要的任务切换原创 2021-04-27 20:42:42 · 495 阅读 · 0 评论 -
在Tricore上移植μC/OS-III——5.3 本次采用的多核任务调度算法
我们上一篇文章介绍了一种理论上最优,但实际受到硬件限制无法实现的任务调度算法。本文就来介绍一下本次所采用的能够实现的调度算法。这种方法是把任务与CPU绑定,某个任务只能在某一个CPU上运行。这种方法涉及到另一个参数:OSPrioTbl:就绪任务优先级表这个参数中存储着当前CPU的所有就绪任务的优先级,每个CPU必须单独占用一个表,所以我们需要把它也扩展成数组。当发生任务调度的时候,从每个CPU各自的 OSPrioTbl 中选取优先级最高的任务,进行切换。每个CPU都至少要有一个优先级最低的空闲任务原创 2021-04-27 20:56:49 · 732 阅读 · 0 评论 -
在Tricore上移植μC/OS-III——5.4 系统启动流程
本文来介绍一下多核系统的启动流程,如下面的流程图所示:/**************************************************************/① 系统中区分出一个主CPU,其它都是副CPU,上电后主CPU先启动,外设和全局变量的初始化全由主CPU独自完成;② 之后主CPU启动各个副CPU,并独自完成初始化OS;③ 副CPU先进行自身初始化(堆栈等),然后等待主CPU初始化OS完成;④ OS初始化完成后,主CPU启动所有CPU的任务创建;⑤ 所有CPU各原创 2021-04-27 21:00:00 · 689 阅读 · 0 评论 -
在Tricore上移植μC/OS-III——5.5 任务调度的具体实现
多核RTOS任务调度发生的时机和单核类似,在调用系统延时、调用信号量、退出中断和系统时钟中断等多个时间点都会发生任务调度。多核RTOS中同样有一个系统时钟,只存在于主CPU中,实现方法和逻辑同单核RTOS。与单核不同的地方在于,当发生任务调度的时候,要检查所有CPU的就绪任务优先级,为每个CPU计算出优先级最高的任务,如果该任务和该CPU当前正在运行的任务不同,就要通过核间中断(见下一篇文章)的方式,使该CPU产生中断并执行任务切换。如下图所示,PEi就指所有的CPU。当然,CPU也可以触发自身中断来实原创 2021-04-27 21:04:11 · 583 阅读 · 0 评论 -
在Tricore上移植μC/OS-III——5.6 核间中断
单核RTOS中,任务切换是通过Trap来实现的,但Trap只能由自身CPU进行触发,而在上文中我们得知,多核RTOS中有时需要一个CPU触发其它CPU的任务切换,这时Trap就不能实现了,需要使用核间中断。核间中断简单来说就是一个可以指定执行CPU的软件触发中断,也就是说,这个中断可以由软件来触发,并且可以指定执行的CPU,不管由那个CPU执行程序来触发这个软件中断,执行的CPU都是预定义好的那个CPU。这样就完美解决了任务切换的问题。但有一点需要注意,核间中断是能够被禁止的,即系统进入临界区关中断的时候原创 2021-04-27 21:06:34 · 1008 阅读 · 0 评论 -
在Tricore上移植μC/OS-III——5.7 互斥量 & 源码获取方法
在单核RTOS中,访问一些关键代码和全局变量时,要想不被打断和干扰,关闭全局中断即可,但多核RTOS就不行了,因为关闭中断后代码或变量仍有可能被其它CPU访问,这时就需要用到互斥量来保证临界区关键代码段同一时间只能被一个CPU访问。在原来关闭和开启中断的语句处,添加互斥量操作。iLLD库中封装了Tricore互斥量的使用函数,代码如下图,当CPU想要访问临界区代码的时候,先获取互斥量,如果获取失败,说明其它CPU正在使用临界区代码,就等待1微妙再次获取,直到成功为止。当退出临界区时,再释放互斥量。/**原创 2021-04-27 21:20:51 · 893 阅读 · 1 评论