RTT学习

固件尺寸优化

使用 RT-Thread-Studio 进行工程构建时,为了实现业务需求,我们常常会增加驱动文件、组件或者软件包等等,并且在调试代码时也可能需要使能调试相关的功能(例如打开 ulog 功能 )或者自行打印一些调试的信息。

因此,我们会编译得到一个稍微冗余的固件。对于 MCU 的 Flash 比较紧张时,我们需要考虑代码体积的优化,使其尽量精简,这样的代码在之后的迭代开发中才可以实现小而美的目标。

下面是几个可以去考虑的优化的方向:

裁剪

裁剪是优先需要考虑的方向,这种方式操作简单,也最为见效。
裁剪Ulog组件。
裁剪文件系统以及Flash设备。由于系统不再使用 QSPI 设备,那么相对应的 QSPI设备驱动框架,也是可以取消掉的。这点在裁剪系统时候很重要,因为我们开发中经常 使能/除能 一些总线上的设备,却常常忘记关 总线/设备驱动框架 造成系统体积上的损耗。
最后将虚拟文件系统 DFS 关闭。

选择合适的优化等级

GCC编译器对代码的编译优化有一系列配置项,大体分为五个优化等级:-O0、-O1、-O2、-O3 和 -Os。

  • -O0:关闭所有优化选项,是GCC默认的等级,目的是让编译器减少编译时间并使调试产生与其的结果。
  • -O1:这是最基本的优化等级。编译器会在不花费太多编译时间的同时试图生成更快更小的代码。这些优化是非常基础的,但一般这些任务肯定能顺利完成。
  • O2:O1 的进阶。这是推荐的优化等级,除非你有特殊的需求。O2 会比 O1 启用更多的优化选项。当设置了 O2 等级后,编译器会试图增加编译的时间和提升生成代码的性能(我们一般选用此优化等级完成编译任务)。

开启newlib-nano选项

默认使用的libc,提供了printf、scanf等很多标准库函数,但是这些库函数相对较大,而且很可能一些复杂的可能,我们在项目中并没有使用到,这样会造成代码体积的增大。

因此 newlib 提供了一个精简功能的版本,将一些标准库函数进行简化,仅仅实现一些简单常用的功能,这样便可以使得编译的代码轻量化,更适合嵌入式平台使用。(但是如果我们使用了标准库的一些复杂的功能,而 newlib-nano 并没有完备的实现这些功能,那么可能会造成一些意外的运行结果,我们在使用时要注意这些。)

对 Map File 进行分析优化

在进行裁剪之后,我们还可以使用 Amap.exe 工具( map 文件分析工具)

使用该工具只是辅助性的分析函数调用所占字段大小,从而针对各个组件和函数进行优化裁剪等。

在这里插入图片描述

Cortex-M常用寄存器

PRIMASK寄存器为1位宽的中断屏蔽寄存器。在置位时,它会阻止不可屏蔽中断(NMI)和HarfFault之外所有的异常(包括中断)。实际上,它是将当前异常优先级提升为0,这也是可编程异常 / 中断的最高优先级。

FAULTMASK 与 PRIMASK 相类似,但同时它能屏蔽 HardFault 异常,它实际上是将异常优先级提升到了 -1。

程序状态寄存器(xPSR)
xPSR包含:
应用 PSR(APSR)

执行 PSR(EPSR)

中断 PSR(IPSR)

在这里插入图片描述
GE在Cortex-M4等ARMv7E-M处理器中存在,在Cortex-M3处理器中则不可用。

  • N:负标志
  • Z:零标志
  • C:进位或借位标志
  • V:溢出标志
  • Q:饱和标志
  • GE:大于或等于标志
  • ICI/IT:中断继续指令
  • T:Thumb状态,总是1,清除此位会引起错误异常
  • 异常编号:表示处理器正在处理的异常。

Cortex-M系列处理器的中断向量表位于0x00000000,但Cotrex-M3/4系列提供了中断向量表偏移寄存器(SCB_VTOR),所以,中断向量表的位置位于 0x00000000 + SCB_VTOR。

CPSIE I 使能中断 (清除 PRIMASK)
CPSID I 禁止中断 (设置 PRIMASK),NMI 和 HardFault 不受影响
CPSIE F 使能中断 (清除 FAULTMASK)
CPSID F 禁止中断 (设置 FAULTMASK),NMI 不受影响

移植过程

在嵌入式领域有多种不同CPU架构,例如Cortex-M、ARM920T、MIPS32、RISC-V等等。

为了使RTT能够在不同CPU架构的芯片上运行,RTT提供了一个libcpu抽象层来适配不同的CPU架构。
libcpu层向上对内核提供统一的接口,包括全局中断的开关,线程栈的初始化,上下文切换等。
RT-Thread 的 libcpu 抽象层向下提供了一套统一的 CPU 架构移植接口,这部分接口包含了全局中断开关函数、线程上下文切换函数、时钟节拍的配置和中断函数、Cache 等等内容。

要对CPU进行移植,只需要实现上述接口。
关闭全局中断

	.global rt_hw_interrupt_disable
	.type rt_hw_interrupt_disable, %function
rt_hw_interrupt_disable:
	MRS R0,PRIMASK
	CPSID I;
	BX LR

打开全局中断

	.global rt_hw_interrupt_enable
	.type rt_hw_interrupt_enable, %function
rt_hw_interrupt_enable:
	MSR PRIMASK,R0
	BX LR

实现线程栈初始化
在动态创建线程和初始化线程的时候,会使用到内部的线程初始化函数_rt_thread_init(),_rt_thread_init()函数会调用栈初始化函数rt_hw_stack_init(),在栈初始化函数里会手动构造一个上下文内容,这个上下文内容会被作为每个线程第一次执行的初始值。

在这里插入图片描述

rt_uint8_t *rt_hw_stack_init(void *tentry, void *parameter, rt_uint8_t *stack_addr, void *texit){
	struct stack_frame *stack_frame;
	rt_uint8_t *stk;
	unsigned long i;

	stk  = stack_addr + sizeof(rt_uint32_t);
    stk  = (rt_uint8_t *)RT_ALIGN_DOWN((rt_uint32_t)stk, 8);
    stk -= sizeof(struct stack_frame);
    stack_frame = (struct stack_frame *)stk;

	for (i = 0; i < sizeof(struct stack_frame) / sizeof(rt_uint32_t); i ++)
    {
        ((rt_uint32_t *)stack_frame)[i] = 0xdeadbeef;
    }

	stack_frame->exception_stack_frame.r0 = (unsigned long)parameter;
	stack_frame->exception_stack_frame.r1  = 0;                        /* r1 */
    stack_frame->exception_stack_frame.r2  = 0;                        /* r2 */
    stack_frame->exception_stack_frame.r3  = 0;                        /* r3 */
    /* 将 IP(Intra-Procedure-call scratch register.) 设置为 0 */
    stack_frame->exception_stack_frame.r12 = 0;   
    /* 将线程退出函数的地址保存在lr寄存器 */
    stack_frame->exception_stack_frame.lr = (unsigned long)texit;
    /* 将线程入口函数的地址保存在pc寄存器 */
    stack_frame->exception_stack_frame.pc = (unsigned long)tentry;
    /* 设置psr的值,表示默认切换过去是Thumb模式 */
    stack_frame->exception_stack_frame.psr = 0x01000000L;              /* PSR */

    /* return task's current stack address */
    return stk;
}

实现上下文切换

在Cortex-M里面上下文切换都是统一使用PendSV异常完成。
为了适应不同的CPU架构,RTT的libcpu抽象层需要实现三个线程相关的函数:
1) rt_hw_context_switch_to():没有来源线程,切换到目标线程,在调度器启动第一个线程的时候被调用。

2) rt_hw_context_switch():在线程环境下,从当前线程切换到目标线程。

3) rt_hw_context_switch_interrupt ():在中断环境下,从当前线程切换到目标线程。

PendSV_Handler
产生PendSV异常时,Cortex-M系列处理器硬件会自动将from线程的PSR、PC、LR、R12、R3-R0压栈,因此在 PendSV_Handler 中,我们需要把 from 线程的 R11-R4 压栈,并把 to 线程的 R11-R4 弹出。修改 PSP 为 to 线程的栈地址,在退出 PendSV 中断时,硬件会自动弹出 to 线程的 R3-R0、R12、LR、PC、PSR 寄存器。

实现时钟节拍

要实现时间轮转调度、软定时器、rt_thread_delay()等功能,就需要保证rt_tick_increase() 被周期性调用。在Cortex-M系列MCU中,可以使用系统滴答定时器来对其周期性调用。

void SysTick_Handler(void)
{
    /* enter interrupt */
    rt_interrupt_enter();

    rt_tick_increase();

    /* leave interrupt */
    rt_interrupt_leave();
}

网络工具集NetUtils

RT-Thread NetUtils 作为网络工具合集,既有用于测试调试的Ping命令,同步时间的NTP工具,性能和带宽测试的Iperf、NetIO,还有在嵌入式系统中广泛使用的轻量级文件传输工具TFTP,方便地通过网络完成两个设备间的文件互传。
另外, RT-Thread 还针对开发中的实际问题,提供了一些高级的辅助工具,如可以远程登录到 RT-Thread Finsh/MSH Shell 的 Telnet 工具,以及基于 lwIP 的网络抓包工具 tcpdump。

在这里插入图片描述
Ping
Ping是一种网络诊断工具,用来测试数据包能否通过IP协议到达特定主机。估计与主机间的丢包率和网络时延。

NTP
NTP是网络时间协议,它是用来同步网络中各个计算机时间的协议。在RTT上实现了NTP客户端,连接上网络后,可以获取当然UTC时间,并更新到RTC中。

如果开启RTC设备,还可以使用ntp_sync命令同步NTP的本地时间到RTC。

MQTT

MQTT是专为受限设备和低带宽、高延迟或不可靠的网络而设计的,是一种基于发布/订阅模式的轻量级通讯协议,该协议构建于TCP/IP协议之上。

在这里插入图片描述

  • Publisher:发布者
  • Broker:代理(服务端)
  • Subscriber - 订阅者
  • Topic - 发布/订阅的主题

上图中,各类传感器的角色是发布者,譬如温度传感器和湿度传感器分别向加入的MQTT Broker周期性发布两个主题名为temp何moisture的主题;当然伴随着两个主题共同发布的,还是温度值和湿度值,被称为消息。几个客户端的角色是订阅者Subscriber,如手机APP从Broker订阅了"Temp"主题,便能在手机上获取到温度传感器Publish在Broker中的温度值。

  1. 发布者和订阅者的角色 并非是固定的,而是相对的。发布者也可以同时从Broker订阅主题,同理,订阅者也可以向Broker发布主题;即发布者可以是订阅者,订阅者也可以是发布者。
  2. Broker可以是在线的云服务器,也可以是本地搭建的局域网客户端;按照需求,实际上Broker自身也会包含一些订阅/发布主题的功能。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

饼干饼干圆又圆

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值