本帖最后由 nich20xx 于 2020-5-31 06:04 编辑
基于MSP430FR6972平台移植TencentOS tiny
1 前言
TencentOS tiny是面向物联网的RTOS,具有小的系统开销;MSP430是面向混合信号处理的16位单片机,具有超低功耗,当两者相遇,会擦出什么样的火花,让我们拭目以待。
1.1 主要内容提要
在移植RTOS时,必须先根据处理器的结构与特点来确定任务栈的堆栈结构等。因此本文先重点介绍了MSP430 CPU架构等基础知识,然后说明如何移植TencentOS tiny到MSP430FR6972芯片平台,并实现第一个串口应用。主要包括如下内容:
- MSP430基础知识
- MSP430芯片简介
- MSP430总体架构
- MSP430 CPU架构
- MSP430存储空间
- MSP430中断响应
- TencentOS tiny移植到MSP430X MCU架构
- TencentOS tiny移植到MSP430X MCU架构
- 搭建TencentOS tiny与MSP430FR6972的软硬件开发环境
- 运行第一个串口应用程序
完整的移植工程文件,可查看 https://github.com/Forest-Rain/TencentOS-tiny
2 MSP430基础知识
2.1 MSP430芯片简介
MSP430 单片机是美国德州仪器 TI ( Texas Instruments )公司从1996年开始推向世界的一款16位超低功耗混合信号处理器 MSP ( Mixed Signal Processor )。
MSP430 集成了许多的数字、模拟电路,形成了非常丰富的外设功能,MSP430 具有强大的处理能力和运行速度快,功耗超低,应用方便等优点,广泛应用在便携式仪器仪表、监测、医疗器械以及汽车电子等领域。
MSP430最富盛名的标签之一莫过于其优异的超低功耗特性,全球目前拥有最低功耗的微控制器,拥有 5种主低功耗模式——即使处于极低功耗模式的情况下,外设仍可以快速唤醒CPU,这个过程最多只需要6us,同时低功耗应用编程非常的方便与灵活。
目前MSP430产品主要包括F1系列、F2系列、G2系列、I2系列、F4系列、F5系列、F6系列、FR系列、低电压系列与RF Soc CC430F系列等
MSP430FR系列是MSP新一代16位MCU,采用的存储介质是FRAM(铁电存储器),而不是FLASH。FRAM 是全新的非易失性存储器,其完美结合了 SRAM 的速度、灵活性和耐用性与闪存的稳定性和可靠性,具有1015次擦写次数,超短的读写时间,写一个字节只需125ns,写完64KB仅需4ms的时间,总功耗更低。
2.1.1 MSP430FR6972芯片简介
MSP430FR6972芯片的典型特点:
- 嵌入式微控制器
- 高达 16MHz 时钟频率的 16 位 RISC 架构
- 3.6V 至 1.8V 的宽电源电压范围(最低电源电压受限于 SVS 电平,请参阅 SVS 规格)
- 经优化的超低功耗模式
- 工作模式:大约 100µA/MHz
- 待机(具有低功率低频内部时钟源 (VLO) 的 LPM3):0.4µA(典型值)
- 实时时钟 (RTC) (LPM3.5):0.35µA(典型值) (1)
- 关断电流 (LPM4.5):0.04µA(典型值)
- 超低功耗铁电 RAM (FRAM)
- 高达 64KB 的非易失性存储器
- 超低功耗写入
- 125ns 每个字的快速写入(4ms 内写入 64KB)
- 统一标准存储器 = 单个空间内的程序、数据和存储
- 1015 写入周期持久性
- 抗辐射和非磁性
-
- 具有 10 个可选厂家调整频率的定频数控振荡器 (DCO)
- 低功率低频内部时钟源 (VLO)
- 32kHz 晶振 (LFXT)
- 高频晶振 (HFXT)
....
更多的信息,详见http://www.ti.com.cn/product/cn/MSP430FR6972
图2.1 MSP430FR6972硬件资源
2.1 MSP430单片机总体架构
MSP430是16位单片机,其总体架构如下图所示。
图2.2 MSP430F697x构架框图
(1)灵活的片上时钟系统
- 除了片内集成一个晶体振荡器DCO外,还可外接 1~2 个晶体振荡器。不同的内部功能模块可根据需要使用不同的晶体振荡器,在不需要时可以通过设置寄存器将其关闭,以降低功耗。
(2)丰富的外设功能模块
- 集成了大量的外设功能模块,包括电源管理PMM、多通道ADC、DAC、电压比较器、段式液晶驱动器、串行接口(UART、SPI)、硬件乘法器、看门狗定时器、多个16位定时器(可进行捕获、比较、PWM输出)、DMA、FLASH控制器(In System Programmable)、AES256、CRC16\32、MPU等等
(3)方便的片上调试接口
- 集成了JTAG 接口,因此在仿真调试程序时,通过一个廉价的 TAG接口转换器就可以完成以往用昂贵的仿真器才能完成的功能,如设置断点、单步执行程序、读写寄存器等
- 集成了SBW接口,可以使用BSL 方式向CPU内装载程序,方便后续固件升级。
2.1.1 CPU架构
MSP430采用16位CPU架构,提供了16个高度灵活、可完全寻址的单周期操作的CPU寄存器(R0~R15),减少了指令执行时间,寄存器到寄存器操作的执行时间是一个CPU周期。外围模块通过地址总线、数据总线和控制总线与CPU相连。
MSP430 CPU架构使用精简指令集,仅有27条核心指令、24条模拟指令与7种寻址方式。所有的操作,除了程序流程指令,都是通过源操作数的7种寻址模式与目的操作数的4种寻址模式的组合来对寄存器操作。
目前MSP430 CPU架构有两种类型,分别是MSP430 CPU(上一代)与MSP430X CPU(新一代)。
图2.3 MSP430 CPU架构分类
- 小于 64K 的空间可以用 16 位地址去访问,大于 64K 的范围要用 20 位地址去访问。
- 大于 64K 的型号使用扩展型 MSP430X CPU,20 位地址线,最大寻址空间 1M。
-
- 比如MSP430F5XX 系列及 MSP430F261X 系列、MSP430FR系列等等
MSP430X CPU向后兼容MSP430 CPU,两种CPU的数据总线的宽度都是16位,但是MSP430X CPU的地址总线扩展为20位。同时MSP430X CPU的数据逻辑单元(ALU)也扩展为支持20位计算。MSP430X CPU虽然地址总线扩展,但是其中断向量表里存储的中断服务函数的起始地址还是16位,即中断服务函数依旧还是在地址空间的低64KB的范围。
图2.4 MSP430 CPU与MSP430 CPUX架构框图
MSP430(X) CPU架构共有16个CPU寄存器,其中R0~R3为特殊功能寄存器,R4~R15为通用工作寄存器,如下表所示
表2.1 MSP430(X) CPU架构的16个CPU寄存器
CPU寄存器
|
功能描述
|
R0
|
程序计数器PC,指示要执行的指令的存放地址
|
R1
|
堆栈指针,用SP表示
|
R2
|
CPU的状态寄存器\常数发生器(SR\CG1 Status)
|
R3
|
常数发生器(CG2 Status)
|
R4~R15
|
通用工作寄存器。通用工作寄存器是MSP430内核活动的主要场所,可以执行算术逻辑运算,也可以作为临时的暂存单元,可以字操作,也可以字节操作
|
|
|
MSP430X CPU的寄存器除 SR寄存器(R2)外,其他都是 20-bit。
图2.5 MSP430X CPU寄存器
2.1.2 MSP430存储空间
MSP430存储空间采用RISC架构(冯-诺依曼架构),ROM与RAM在统一地址空间,使用一组数据与地址总线。(比如STM32单片机也是RISC架构)
图2.6 MSP430FR69x2\MSP430FR68x2存储空间
2.1.2.1 MSP430X Data model
MSP430存储空间的组织方式分为大模式与小模式:
-
- 在小模式时,总的寻址空间为64KBtyte;
- 在大模式时,总的寻址空间为1MByte.
IAR 编译器默认支持的指针变量最大为 0xFFFF,如果超过 0xFFFF,则需要在“Project--Option”内进行设置:
Project-->Option-->General-->Target-->Data model 选择 Large
2.1.3 MSP430中断响应
(1)中断响应过程
中断触发时,中断发生对应的标志置1。
- 如果CPU处于活动状态,则完成当前指令;若CPU处于低功耗状态,则退出低功耗状态;
- 将指向下一条指令的PC压栈。
- 将状态寄存器SR压栈。
- 若有多个中断请求,选择最高优先级的中断进行服务。
- 单源中断的中断标志位会被自动清零
-
- 注意P1,P2属于多源中断,多源中断标志位不会自动清零,需软件清零。
- 状态寄存器SR被清零,将会终止任何低功耗状态,并且全局中断使能被关闭(SR.GIE)。
-
- 注意MSP430响应了中断后会关闭全局中断使能,不会响应任何其他的中断,包括优先级高的中断,即默认状态下没有中断嵌套,若需要中断嵌套,可使用_EINT()打开全局中断.
- 中断向量被装载到PC,(MSP430X PC入栈,会自动将PC.19:16与SR寄存器组合),然后开始执行中断服务函数
图2.7 MSP430X中断响应,PC入栈
图2.7 MSP430(X)中断处理
(2)中断返回过程
中断服务函数会由RETI这条指令返回,SR被弹出,CPU恢复到中断前的状态,PC也被弹出,继续执行指令。
- 从堆栈中恢复状态寄存器SR值
- 从堆栈中恢复PC值
- 若响应中断前,CPU处于低功耗模式,则仍然恢复低功耗模式;
- 若响应中断前,CPU不处于低功耗模式,则从此地址继续执行程序。
2.1.4 MSP430X汇编指令
2.1.4.1 寻址方式
MSP430汇编指令包含7种寻址方式,如下图所示:
图2.8 7种寻址方式
这7种寻址方式均可以用于源操作数,而寄存器寻址、索引寻址、符号寻址与绝对寻址只能用于目的操作数。
2.1.4.2 汇编指令集
MSP430汇编指令由操作码与操作数组成。
- 操作码是一条汇编指令中不可缺省的部分
- 在汇编语言中用助记符表示,不区分大小写。
- .后缀,给出操作数的类型,.B为字节类型,.W为字类型,.a为MSP430X扩展指令(地址字指针)。缺省后缀默认是字类型。
表2.2 MSP430汇编指令的基本构成
|
汇编指令 |
说明
|
|
操作码
|
操作数<src,dst>
|
|
示例1
|
ADD.W
|
R5,R6
|
寄存器方式,R5+R6 --> R6
|
示例2
|
PUSH.W
|
R6
|
SP-2 --> SP,R6 --> @SP
|
示例3
|
RETI
|
|
中断返回
|
MSP430汇编指令集共有27条内核指令,如下图所示
图2.9 MSP430 27条核心指令集
MSP430X CPU指令集与上一代 16 位地址总线的 MSP430 CPU指令集在使用中存在一些区别:
- 当操作数或数据长度超过 16 位时,要使用 MSP430X指令,其它情况下完全与MSP430指令兼容。
- 扩展指令如 MOVX.(W/B/A),RRAX.(W/B/A),PUSHX,POPX,SWPBX 等
- 所有的MSP430X汇编程序不能再用BR、CALL 、RET等,要使用扩展指令 BRA,CALLA 、RETA等。
- MSP430X CPU指令集与MSP430 CPU指令集完全兼容,同时具有更高的代码密度
- MSP430X ISR的消耗由11个周期降至8个
注:MSP430汇编文件后缀为.s43
3 MSP430FR6972平台移植TencentOS tiny内核
TencentOS tiny移植到MSP430平台,主要涉及的内容有汇编文件port_s.s43、任务栈初始化等。
3.1 汇编文件port_s.s43
TencentOS tiny移到MSP430FR6972主要涉及的汇编函数,如下表所示:
表3-1 port_s.s43汇编文件
序号
|
汇编函数
|
功能描述
|
1
|
port_sched_start
|
启动滴答定时器与RTOS调度
|
2
|
port_context_switch
|
上下文切换
|
3
|
port_irq_context_switch
|
中断上下文切换
|
4
|
port_int_disable |
关总中断
|
5
|
port_int_enable
|
开总中断
|
6
|
port_cpsr_save
|
保存状态寄存器
|
7
|
port_cpsr_restore |
恢复状态寄存器
|
8
|
port_systick_isr
|
滴答定时器中断服务
|
3.1.1 启动滴答定时器与RTOS调度
;/*
;* Start off the scheduler by initialising the RTOS tick timer, then restoring
;* the context of the first task.
;*/
port_sched_start:
/* Interrupts are turned off here, to ensure a tick does not occur
before or during the call to port_sched_start(). The stacks of
the created tasks contain a status word with interrupts switched on
so interrupts will automatically get re-enabled when the first task
starts to run. */
dint
nop
;/* Setup the hardware to generate the tick. Interrupts are disabled
;when this function is called. */
calla #port_setup_systick
;mov_x &k_next_task,&k_curr_task
;/* Restore the context of the first task that is going to run. */
PORT_RESTORE_CONTEXT
3.1.2 上下文切换
port_context_switch:
;
push.w sr
;
dint
nop
PORT_SAVE_CONTEXT
;k_next_task -> k_curr_task
mov_x &k_next_task,&k_curr_task
PORT_RESTORE_CONTEXT
reti
3.1.3 开启与关闭总中断
port_int_disable:
dint
nop
reta
port_int_enable:
nop
eint
reta
3.1.4 保存与恢复状态寄存器
port_cpsr_save:
mov.w SR,R12
dint
nop
reta
port_cpsr_restore:
nop
mov.w R12,SR
nop
reta
3.1.5 系统滴答定时器中断服务
port_systick_isr:
;
push.w sr
PORT_SAVE_CONTEXT
calla #systick_handler
cmp.w #0x0,irq_context_switch_flag
mov.w #0x0, &irq_context_switch_flag
jeq skip_context_switch
;k_next_task -> k_curr_task
mov_x &k_next_task,&k_curr_task
skip_context_switch:
PORT_RESTORE_CONTEXT
reti
3.2 任务栈初始化
单核CPU要实现多任务的条件是每个任务互相独立,也就是说每个任务拥有"自己的CPU"、堆栈与数据存储区、程序代码等。
任务(私有)栈作用是保存局部变量、函数参数等,它是一个线性空间,因此通常是申请一个静态数组,然后把栈顶指针SP指向栈的数组的首元素(递增栈)或者最后一个元素(递减栈)。MSP430堆栈指针是向下生长。
当任务被创建后,还没有任务上下文内容,因此需要先初始化每个任务的任务栈,这样才能确保在系统启动的时候,将第一个任务的正确上下文弹入到硬件寄存器里。
__KERNEL__ k_stack_t *cpu_task_stk_init(void *entry,
void *arg,
void *exit,
k_stack_t *stk_base,
size_t stk_size)
{
cpu_data_t *sp;
uint16_t *pus_top_of_stack;
uint32_t *pul_top_of_stack;
#define PORT_BYTE_ALIGNMENT_MASK ( 0x0001 )
/* The stack type changes depending on the data model. */
/* The last byte of task stack */
sp = (cpu_data_t *)&( stk_base[ stk_size - ( uint32_t ) 1 ] );
sp = ( cpu_data_t * ) ( ( ( cpu_data_t ) sp ) & ( ~( ( cpu_data_t ) PORT_BYTE_ALIGNMENT_MASK ) ) );
/* cpu_data_t is either 16 bits or 32 bits depending on the data model.
Some stacked items do not change size depending on the data model so have
to be explicitly cast to the correct size so this function will work
whichever data model is being used. */
if( sizeof( cpu_data_t ) == sizeof( uint16_t ) )
{
/* Make room for a 20 bit value stored as a 32 bit value. */
pus_top_of_stack = ( uint16_t * ) sp;
pus_top_of_stack--;
pul_top_of_stack = ( uint32_t * ) pus_top_of_stack;
}
else
{
pul_top_of_stack = ( uint32_t * ) sp;
}
/* PC - Interrupt return pointer */
*pul_top_of_stack = ( uint32_t ) entry;
pus_top_of_stack = ( uint16_t * ) pul_top_of_stack;
pus_top_of_stack--;
/* R2 - SR.GIE - bit8,golbal interrupt enable */
*pus_top_of_stack = 0x08;
/* SR size is 16-bits */
pus_top_of_stack -= ( sizeof( cpu_data_t ) / 2 );
/* From here on the size of stacked items depends on the memory model. */
sp = ( cpu_data_t * ) pus_top_of_stack;
#if 0 // enable for debug
*sp = ( cpu_data_t ) 0xffff;
sp--;
*sp = ( cpu_data_t ) 0xeeee;
sp--;
*sp = ( cpu_data_t ) 0xdddd;
sp--;
*sp = ( cpu_data_t ) arg;
sp--;
*sp = ( cpu_data_t ) 0xbbbb;
sp--;
*sp = ( cpu_data_t ) 0xaaaa;
sp--;
*sp = ( cpu_data_t ) 0x9999;
sp--;
*sp = ( cpu_data_t ) 0x8888;
sp--;
*sp = ( cpu_data_t ) 0x5555;
sp--;
*sp = ( cpu_data_t ) 0x6666;
sp--;
*sp = ( cpu_data_t ) 0x5555;
sp--;
*sp = ( cpu_data_t ) 0x4444;
#else
sp -= 3;
*sp = ( cpu_data_t ) arg;
sp -= 8;// R11-R4
#endif
return (k_stack_t *)sp;
}
一个任务栈的初始化过程,如下图示例:
图3.1 MSP430任务栈初始化(enable for debug)
4 搭建MSP430FR与TencentOS tiny的软硬件开发环境
4.1 软硬件开发环境
- 软件平台:IAR版本6.50.1及其以上版本(注:低版本可能不支持MSP430FR系列)
- 硬件平台:MSP430FR6972 EVM
4.2 新建应用工程
- 在IAR中,新建一个C应用工程。
图4.1 新建C工程
PS:
.eww是工程空间文件后缀
.ewp是工程的后缀名
- 选择主芯片型号,本文使用的是MSP430FR6972。
Options -> General Options -> Target -> Device -> MSP430FRxx Family -> MSP430FR6 -> MSP430FR6972
图4.2 选择主芯片型号
4.2.1 新建board \ TI_MSP430FR6972_EVM
在board文件夹下,新建以下内容,并加入工程中
TI_MSP430FR6972_EVM\BSP\Src\main
TI_MSP430FR6972_EVM\IAR\hello_world\TencentOS-tiny.eww
TI_MSP430FR6972_EVM\IAR\hello_world\TencentOS-tiny.ewp
TI_MSP430FR6972_EVM\TOS_CONFIG\tos_config.h
4.2.2 新建platform \ MSP430FR5xx_6xx驱动库
在platform文件夹下,添加以下内容,并加入工程
\platform\vendor_bsp\TI\MSP430FR5xx_6xx
注:MSP430FR5xx_6xx来自TI的 MSP430Ware\MSP430 Driver Library
4.2.3 添加TOS内核文件
4.2.3.1 添加TOS kernel
添加 tos/kernel 文件内容到工程中,如下图所示
图4.3 TOS内核文件
4.2.3.2 新建arch \ msp430
在arch文件夹下, 新建以下内容,并加入工程中
arch/msp430/MSP430X/icc430/port_c.c
arch/msp430/MSP430X/icc430/port_s.s43
arch/msp430/MSP430X/icc430/data_model.h
arch/msp430/MSP430X/icc430/port.h
arch/msp430/MSP430X/icc430/port_config.h
arch/msp430/common/tos_cpu.c
arch/msp430/common/include/tos_cpu.h
arch/msp430/common/include/tos_cpu_def.h
arch/msp430/common/include/tos_cpu_types.h
arch/msp430/common/include/tos_fault.h
4.2.4 添加必要头文件路径
在IAR工程的Options -> C/C++ Complier -> Preprocessor,添加系统所需头文件的所在路径:
$PROJ_DIR$\..\..\..\..\platform\vendor_bsp\TI\MSP430FR5xx_6xx
$PROJ_DIR$\..\..\..\..\kernel\core\include
$PROJ_DIR$\..\..\..\..\kernel\pm\include
$PROJ_DIR$\..\..\..\..\arch\msp430\common\include
$PROJ_DIR$\..\..\..\..\arch\msp430\msp430x\icc430
$PROJ_DIR$\..\..\TOS_CONFIG
4.2.5 其他重要特性
4.2.5.1 MSP430FR __persistent
对于MSP430FR系列,基于FRAM的优秀特性,在系统SRAM不够的情况,可以直接扩展FRAM作为RAM使用。
在IAR中,使用__persistent 可以将变量\数组等强制放在FRAM的地址上去,这样可以达到扩展RAM的目的。注意使用__persistent修饰的变量将具有非易失性的特性。
另外也可以通过修改.xls来实现系统RAM的扩展,使用非常方便与灵活。
4.2.5.2 Data Model
根据实际使用芯片来选择,比如MSP430FR6972空间为64K,因此可以选择Small,也可以选择Large。
可在Options -> General Options ->Target -> Data Model选择。
图4.4 Data Model
4.2.5.3 查找最高优先级任务
由于MSP430是16位数据总线,因此对于32位变量,需要执行强制类型转换,否则由于高16位没有正确加载到CPU寄存器中,导致数据出错。
图4.5 32位变量强制类型转换
4.2.5.4 增加icc430编译器支持
在tos_compiler.h增加编译器__ICC430__支持
图4.6 新增ICC430编译
4.2.5.5 Warning[Pe301] Warning[Ta027]
IAR编译中,会出现如下警告,当前通过如下方式消除
- Warning[Pe301]: typedef name has already been declared (with same type)
- Warning[Ta027]: Converting a code pointer to a smaller pointer type resulted in truncation
图4.7 消除编译警告
4.3 第一个串口应用
完成上述过程后,基本实现了TencentOS tiny与MSP430FR6972软硬件环境的搭建,接下来创建一个串口应用示例。
该串口应用示例中,创建了2个任务,并周期性反转LED,输出串行信息。task1每2S运行一次,task2每1s运行一次。
/****************************************************************************************
TencentOS tiny Blink the LED Demo - Software Toggle P1.0 and P9.7
Description; Toggle P1.0 and P9.7 inside of a software loop.
ACLK = 32768Hz, MCLK = SMCLK = default DCO = 8MHz
MSP430FR6972
-----------------
/|\| XIN|-
| | | 32768
--|RST XOUT|-
| |
| P1.0|-->LED1
| P9.7|-->LED2
| |
| P3.4(TXD)|--> PC
| P3.5(RXD)|<--
Built with IAR6.50.1
*****************************************************************************************/
#include <msp430.h>
#include <driverlib.h>
#include <stdint.h>
#include "tos.h"
#define TASK1_STK_SIZE 320 // more than 300 for stb printf
#define TASK2_STK_SIZE 320 // more than 300 for stb printf
#define TASK1_PRIO 3
#define TASK2_PRIO 3
k_task_t task1;
k_task_t task2;
__persistent k_stack_t task1_stack[TASK1_STK_SIZE];
__persistent k_stack_t task2_stack[TASK2_STK_SIZE];
/**
* @brief task1_entry
* @details task1 process
* @param void *arg
* @return None
*/
void task1_entry(void *arg)
{
int count = 1;
while (1)
{
printf("###This is task1, %d\r\n", count++);
GPIO_toggleOutputOnPin(GPIO_PORT_P1,GPIO_PIN0);
tos_task_delay(tos_millisec2tick(2000));
}
}
/**
* @brief task2_entry
* @details task2 process
* @param void *arg
* @return None
*/
void task2_entry(void *arg)
{
int count = 1;
while (1)
{
printf("***This is task2, %d\r\n", count++);
GPIO_toggleOutputOnPin(GPIO_PORT_P9,GPIO_PIN7);
tos_task_delay(tos_millisec2tick(1000));
}
}
int main(void)
{
k_err_t err;
board_init();
printf("Welcome to TencentOS tiny\r\n");
tos_knl_init();
err = tos_task_create(&task1, "task1", task1_entry,
K_NULL, TASK1_PRIO,
task1_stack, sizeof(task1_stack),0);
err = tos_task_create(&task2, "task2", task2_entry,
K_NULL, TASK2_PRIO,
task2_stack, sizeof(task2_stack),0);
if( err == K_ERR_NONE )
{
err = tos_knl_start();
}
else
{
printf("TencentOS tiny fail to creat tasks \r\n");
while(1);
}
}
实际运行日志如下图所示:
图4.8 串口示例运行日志
5 参考
- MSP430的库文件 MSP430Ware for MSP Microcontrollers
- MSP430X:扩展存储器无止境.pdf
- MSP430汇编指令集
- FreeRTOSv10.2.1\FreeRTOS\Demo\MSP430X_MSP430FR5969_LaunchPad_IAR_CCS
- TencentOS tiny 内核移植参考指南(IAR版)