一、前言
F28335棣属于TI C2000 DSP系列,它在电控相关的行业有较多的应用。但是因为种种原因,某些行业因为无“芯”可用,也会将它作为通用芯片使用。少得可怜的资料加上年代久远的CPU架构,要在这上面使用RTOS变得异常艰难。
二、混乱的SYS/BIOS
1.到底是SYS/BIOS、DSP/BIOS还是TI-RTOS?
28335官方主推使用的RTOS是SYS/BIOS,只是网络上几乎找不到实践项目,很多资料因为年代久远甚至描述得相互冲突。光是搞明白它们之间的关系,就花了我相当长的时间。
- TI-RTOS:TI嵌入式生态系统的最新名称,它不仅包括了内核(SYS/BIOS)、还包含网络栈、文件系统等其他组件,适用于最新的TI CPU。
- SYS/BIOS :TI-RTOS 的内核,28335只适用于它
- DSP/BIOS :在一些老的资料中出现,其实它是SYS/BIOS的前身,在6.30版本改名SYS/BIOS
- XDCtools :包含了SYS/BIOS包,也是它的构建工具
2.不推荐使用SYS/BIOS的理由
虽然SYS/BIOS是官方钦点的RTOS系统,我却不推荐刚准备入坑的人在F28335上使用或学习它,原因有:
- 从官方用户手册来看,SYS/BIOS的使用和构建与主流OS差异较大,学习的收益极低;
- DSP主要应用场景并不使用RTOS,因为成本和生态它的生存空间还不断被ARM挤压,感觉连TI自己都快放弃了。所以这极可能是一个大坑,出现了问题根本找不到相关资料。
三、移植UCOS的问题和改进
除了SYS/BIOS、网络资料中流传最广的是一份OS应用是由micrium的移植的ucosii代码,然而测试时却发现,这份代码存在不能调度抢断的问题。要理解和解决这个问题首先要从C2000系列CPU的栈空间架构和中断流程讲起。
1.28335的栈机制和中断流程
- 28335由于设计得比较早,并不具备常见的双堆栈机制,也就是说应用和中断会共用同一个栈。
- 根据手册和自己的测试,C2000的CPU级中断的过程如下(忽略外设级和PIE级):
1、中断锁存至IFRx中(其中x为中断线号),如果IERx有效且INTM为0,CPU响应中断;
2、cpu保存上下文环境到栈空间(包括了IER寄存器状态);
3、IFRx 置0,IERx置 0,INTM 置 1;
4、从向量表获取中断处理函数地址并执行;
5、恢复INTM,从栈空间恢复IER。
6、中断返回
2.问题分析
接下来以两个任务的系统调度为例看看为什么会发生异常。假设系统运行了两个任务,分别为线程A和B,当前运行线程A,接下来发生以下事件:
1.tick调度中断触发,CPU进入中断开始执行调度;
2.cpu因为中断保存上下文环境,此时IER被保存到线程A的栈空间;
3.调度器算法结果为系统切换到线程B;
4.切换SP到线程B的栈空间,为线程B恢复上下文环境;
5.继续执行中断第5步,即从线程B栈上下文环境恢复IER。
很显然,调度完成后,IERx并没有正确地在中断后恢复。如果线程B不主动执行调度切回到线程A,被置0的IERx将一直无法恢复,tick中断一直处于屏蔽状态,系统任务也无法完成切换。
3.IERx的恢复
IERx不能恢复这一坑,一度让我觉得无解,为此甚至使用了双定时器tick这样的骚操作。直到最近才找到较好的解决办法,timer tick函数的主要部分实现如下:
_KTS_TIMER2_ISR:
...
; 保存任务上下文到TCB中
...
; 获取IER值
PUSH ST1
POP AL
TBIT AL, #4
SB SPA_BIT_SET, TC ;TC = 1 跳到SPA_BIT_SET去获取IER
MOV AR7, *-SP[46]
SB SAVE_IER, UNC ; 已获取到IER, 跳到 SAVE_IER
SPA_BIT_SET:
MOV AR7, *-SP[48]
SAVE_IER:
MOVL *SP++, XAR7 ; 保存IER到当前任务的堆栈
...
; 判断是任务主动执行的调度还是timer_tick函数触发
; timer_tick触发时执行tick处理函数
...
LCR _KTS_SV_Handle ; 执行调度函数
; 需要理解,IER的恢复是在下一个调度的任务中完成。
MOVL XAR7, *--SP ; 从"上一个"任务的堆栈,获得IER
MOVL XAR0, #_kts_p_current
MOVL XAR0, *XAR0
MOVL XAR0, *XAR0
MOV @SP, AR0 ; 切换到"新"任务
...
; 从TCB中恢复"新"任务上下文环境
...
四、其他问题
- DSP奇葩16位数据总线,不支持很多GNU特性,对初次上手的人非常不友好;
- 所有任务应用栈必须额外预留中断栈的空间,包括空闲任务。
- TI的printf函数会在栈上申请520字的内存,打印浮点,还会额外再申请520字。所以,不是有特别有需求,可以放弃浮点打印并使用精简的mini-printf函数。
五、总结
考虑收益问题,资料匮乏的情况下,学习一项过时的技术是愚蠢的。因此,总结就是,有条件还是尽量换平台,如果实在要用,需要充分考虑填坑的难度。
参考资料
[TI-RTOS Kernel (SYS/BIOS)User’s Guide] https://www.ti.com.cn/cn/lit/ug/spru430f/spru430f.pdf?ts=1628091876454
[TI SYS/BIOS的 创建工程实例] https://blog.csdn.net/xiaoluoshan/article/details/53785059
[micrium ucOSii_in_DSP28335] https://github.com/RanFang66/ucOSii_in_DSP28335
[TMS320x2833x, 2823x System Control and Interrupts Reference Guide]