历史上的今天
返回首页

历史上的今天

今天是:2025年03月04日(星期二)

正在发生

2021年03月04日 | ARM-CortexM0/M0+单片机的指针变量替换方法

2021-03-04 来源:eefocus

引言

CortexM0/M0+是RISC类型的低端ARM内核,其指令集与高端ARM兼容,在性能、功耗和价格方面远优于传统的以8051、68S08/12等为代表的8/16位CISC(复杂指令流)CPU。目前,各半导体厂商纷纷以之替代原有的8/16位MCU内核,32位ARM MCU全面替代8/16位MCU已是大势所趋。


CortexM0+将CortexM0的3级流水线简化为2级,并进一步降低功耗、提高性能,这些优点使得CortexM0+成为目前8/16位处理器较好的替代者。不过


替代8/16位MCU的低端ARM往往内存资源非常有限,目前典型的CortexM0/M0+ MCU往往仅有2 KB、4 KB或8 KB,最多16 KB片内RAM。Flash一般也不大于64KB。对这类MCU编程,使用短指针变量就够了。而目前ARM处理器的集成开发环境(IDE)中的C编译器,延续CortexM3/M4的使用传统,仍使用32位长指针变量。这无形中多占用了1倍的RAM资源。这里以飞思卡尔ARM CortexM0+处理器中的Kinetis 系列MCU为例,说明如何使用16位短指针替代32位长指针,以便在将原有的以8/16位MCU为核心的产品升级到采用32位ARM内核时,不增加系统开销。特别是若使用了实时操作系统,系统的内存会更加紧张。在专门面向CortexM0/M0+的集成开发环境(IDE)推出前,可使用本文提供的替换方法,以降低系统的RAM开销,提升系统的性能。


1 指针替换原理

32位ARM内核的内部寄存器都是32位的,其寻址空间可以达到4 GB,通常也应使用32位的地址指针。但在数据空间、程序空间和I/O空间都不大于64 KB的情况下,可以采用1个32位基地址加1个16位偏移量的方法,合成ARM需要的长指针。


以CortexM0+为内核的MCU,其SRAM、FLASH很少超过64 KB,一般使用16位的偏移量指针就能满足需要。


以Freescale公司的KL25Z128 MCU为例,有16 KB SRAM和128 KB FLASH存储空间。其SRAM的地址范围是0x1FFF_F000~0x2000_2FFF[1],使用16位的偏移量指针便可以满足寻址范围的要求。


图1说明了长指针替换方法的基本原理,通过使用一个32位的RAM基地址,完成原始32位绝对地址与相对基地址的16位相对偏移地址的相互转化。



其转化关系如下所示:


Address_32bits=Address_16bits+Address_base(1)


Address_16bits=Address_32bits-Address_base(2)


对于KL25Z128 ,Address_base基地址值可选择为0x1FFF_F000。通过以上方法的转化,32位的地址空间0x1FFF_F000~0x2000_2FFF(16 KB)可以转化为16位的地址空间0x0000~0x3FFF(16 KB)。


2 指针替换方案

2.1 常量形式实现方案

以下使用Freescale公司推荐的IDE CodeWarrior v10.5予以说明。


typedef unsigned short pointer_16;


#define address_base 0x1ffff000Lu


/*定义基地址*/


#define addr_16(pt_addr_32)((unsigned short)((unsignedint)pt_addr_32 - address_base))


/*32位地址指针转换为16位地址“指针”*/


#define addr_32(pt_addr_16)((unsigned int)((unsigned int)pt_addr_16 + address_base))


/*16位地址“指针”转换为32位地址“指针”*/


程序中利用宏定义了一个32位常数的基地址,显然也可以使用一个全局变量或寄存器变量来存储基地址。在将长指针变量pt_addr_32转化为16位地址“指针”时,需先将指针变量pt_addr_32做强制类型转化,变为32位无符号数后再进行基地址扣除的计算。该段代码还声明了一个16位无符号数的数据类型pointer_16,用来定义或存储16位地址偏移量,例如使用如下语句来定义一个16位的指针变量:


pointer_16 pt16_data = addr_16(&data);


pt16_data的值便是指向data的16位“指针”(转化而成的16位地址偏移量值),编译器编译出的汇编代码如下所示:


69pt16_data = addr_16(&data);


00000a06:ldr r3,[pc,#72]//R3指向data


00000a08:uxth r2,r3//16位 R3送32位 R2


00000a0a:adds r3,r7,#4//R3指向pt16_data


00000a0c:movs r1,#128//R1 = 0x80


00000a0e:lsls r1,r1,#5//生成基地址补码


00000a10:adds r2,r2,r1//R2=32位指针-基地址


00000a12:strh r2,[r3,#0]//存储R2低16位


00000a50:1FFF_F020//存储data地址


需要将16位地址转化为长指针时,以下面的整型数据赋值操作为例:


int temp = *(int*) (addr_32(pt16_data));


数据data的值赋值给了变量temp,其中int数据类型可以替换成任意其他的数据类型(例如unsigned int、unsigned short、short、unsigned char、char等)。


2.2 高组寄存器优化方案

CortexM系列内核是专门为ARM MCU设计的,仅支持无条件执行的Thumb指令。CortexM0/M0+使用ARMv6指令集,而CortexM3/M4使用ARMv7指令集。ARMv6 对ARMv7做了高度简化,仅保留了其中56条指令。指令中除个别32位指令外,都是16位指令。CortexM0/M0+的内部寄存器结构与高端ARM兼容,但低端MCU应用往往不需要那么多寄存器,CortexM0/M0+仅提供了R0~R12共13个通用寄存器。这些通用寄存器分为两部分:低组寄存器(Low registers,R0~R7),高组寄存器(High registers,R8~R12)[2]。CortexM0/M0+牺牲了大量面向高组寄存器的指令,尽量减少32位指令的使用。实际上CortexM0/M0+的指令集中仅有以下3条指令支持高组寄存器R8~R12:


MOV ,;寄存器间数据传送


ADD, ;基地址+偏移量


CMP, ;地址的比较


这里Rd和Rm之一可以是高组寄存器。可以看出,对于高组寄存器,ARMv6仅保留了高低组寄存器间数据传递、不影响标志位的加法运算和单独的地址比较这3种操作,其用处显然是为了支持将高组寄存器用于地址运算。


目前基于gcc的主流ARMC编译器对CortexM0/M0+的高组寄存器采取尽量不予使用的策略,在定义指针变量时,仅使用长指针。而分析ARMv6指令集的设计初衷,显然应该用高组寄存器和相关指令。这对于旨在替代8/16位MCU的低成本ARM器件非常必要。


实际上,应用程序中可通过MOV指令将R8~R11初始化成“寄存器常数”,而以后不再改变它们的值。例如可以令:


R8,= 0用于低寄存器的快速清零


R9,=RAM基地址用于拼接长指针


R10,= I/O模块基地址


R11,=库函数基地址


当FLASH存储器空间不大于64 KB时,函数指针无需设定基地址,可以直接使用低16位作为16位指针。对于超过64 KB的FLASH,可以使用库函数基地址,采用类似分页的方法实现16位指针替换。


最后一个高组寄存器R12可在响应中断时和R0~R3,PC、SP一同自动入栈,是用户可以使用的寄存器变量。


2.1节中提出的宏定义方案形式上简单清楚,但展开后需要多条指令才能完成。将Address_base作为寄存器变量,存放在R8~R12中的某个高组寄存器中,而不是使用宏定义常量或全局变量。由于C语言不能直接对通用寄存器进行操作,需通过将汇编嵌入到C语言中实现长指针的替换。在程序初始化时,将R8~R12中的一个寄存器初始化为Address_base的值,例如下面给出的语句:


asm("LDR r1, =0x1ffff000"); //R1=基地址


asm("MOV R9, R1"); //R9=R1,即基地址


R9寄存器初始化后无需再修改,是一个“寄存器常数”。对于已经存储在R0中的长指针,则使用如下汇编代码,很容易将其转化为16位地址:


asm("MOV R1, R9");//R1=基地址


asm("SUB R0, R0, R1"); //R0=R0-R1,R0低16位即16位


//短指针值


代码首先将R9寄存器存储的基地址转移到R1寄存器,随后利用单条指令完成从R0寄存器所存长指针值减去R1中存储的基地址,并将所得结果保存在R0中。执行完成后,R0低16位便是转化后的16位地址。16位地址转化为长指针是类似的转化形式(SUB指令换为ADD指令),在此不再赘述。这种方法充分利用了内核提供的高组寄存器,并且简化了指针转化的算法,减少了所需指令的数目,提高了运行效率,缩短了转换时间,降低MCU因指针替换而产生的时间损失。转换所需指令数目也压缩到两条,减少转换过程所带来的额外指令代码的存储空间开销。


3 指针替换结果

μC/OS(含μC/OSII、μC/OSIII)是适用于低成本MCU的多任务实时内核。以μC/OS为例,当最大任务数为10时,整个内核需使用12个全局指针型变量,而非指针型变量仅需占用8字节RAM空间。若使用默认的长指针模式,共需12×4+8=56字节;若改用短指针,则需使用12×2+8=32字节。任务数目、任务间通信机制增多时,指针变量的使用将更频繁,本文介绍的方法所节约的RAM空间也更加显著。在CortexM0/M0+处理器替代8/16位MCU的应用中,非常有必要使用短指针。


最新版本的μC/OSIII针对带有计算前导零硬件指令(CLZ)的CortexM3/M4处理器进行了重大改进,提高了其优先级任务搜索的效率。但CortexM0/M0+的ARMv6指令集简化掉了CLZ指令,故不适宜使用μC/OSIII。这里以运行μC/OSII v2.92(最多256个任务)为例,说明指针替换效果。实际上对于内存紧张的MCU,μC/OSII v2.82及以下的版本(最多64个任务)就足够用了。


μC/OSII每个任务都需要使用任务控制块TCB(Task Control Block)的数据结构,来维护任务相关的信息[3]。在μC/OSII v2.92中,每个任务的TCB数据结构包含9个指针变量,采用本文描述的16位指针替换方法后,每个任务控制块均可以节省18字节的RAM空间。在μC/OSII中还存在很多数据结构,均包含着大量的指针变量。这些数据结构采用本文描述的方法所节约的RAM空间如表1所列。


表1 μC/OSII数据结构内存占用情况对比



注:其中X为OS_LOWEST_PRIO,由用户进行配置,典型值为63。表中内存占用大小是笔者根据ucos_ii.h文件进行统计的,实际占用内存可能会由于用户配置不同而略有差异。


可以看出,以16位短指针替代ARM编译器默认的32位长指针,能使CortexM0/M0+ MCU对RAM资源的占用接近8/16位MCU。这一点对“全面替代”是十分重要的。 70


结语

以ARM CortexM0/M0+为内核的32位MCU以其性能、功耗和价格的优势,“全面替代”以8051/52、68S08/12等为代表的8/16位MCU已是大势所趋。而目前主流ARM IDE中的C编译器仅支持长指针变量。若将原有的8/16位MCU应用程序移植到内存资源相当的ARM MCU上,大量长指针变量的使用可能会导致RAM资源不足,而改用更大内存的MCU无疑会增加产品成本。通过使用CortexM0/M0+内核的高组寄存器操作指令,可以实现长短指针的转换,极大地节约RAM占用量,为既有应用的顺利移植提供帮助。


当然,长短指针的转换操作会带来额外的运行时间的开销,转换指令也带来代码存储量的增加。在一定程度上,这种方法是通过增加程序存储量和运行周期的代价来换取数据存储量的减少。由于ARM精简指令集的结构,其指令编码长度和执行速度上都有提升,可以部分抵销程序存储量和运行周期的开销,而数据存储量的矛盾则更加突出和棘手。本文介绍的方法对此作出了有益尝试。


本文介绍的方法需要对已有代码进行一定的改造,笔者希望ARM编译器能尽快提供面向CortexM0/M0+内核的短指针优化编译选项,为完成ARM对8/16位MCU内核的“全面替代”提供良好的支持。


推荐阅读

史海拾趣

HSMC公司的发展小趣事

HSMC的发展离不开巨额的资金投入。公司项目总投资额达到约200亿美元(另一说法为1280亿元人民币),这一庞大的投资规模使得HSMC能够迅速构建起先进的生产设施。项目规划包括建设14纳米及7纳米以下节点的逻辑工艺生产线,以及晶圆级先进封装生产线。这些生产线预计月产能可达数万片,为市场提供高质量的芯片产品。

ADI(亚德诺半导体)公司的发展小趣事

HSMC深知技术创新是企业发展的核心驱动力。公司不仅拥有丰富的14纳米及7纳米以下节点FinFET先进逻辑工艺与晶圆级先进封装技术经验,还持续投入研发,瞄准世界先进的制程工艺。通过与全球各大科研院所的合作,HSMC不断储备行业专利,增强自身的技术壁垒。这种对技术创新的执着追求,为公司的长远发展奠定了坚实基础。

ESTEK公司的发展小趣事

ESTEK公司自创立之初,就专注于电子产品的技术创新。在早期的市场竞争中,公司研发团队通过不懈努力,成功研发出一款具有革命性意义的电子元件。这款元件不仅性能卓越,而且成本远低于同类产品,使得ESTEK公司迅速在市场上脱颖而出。随着这款元件的广泛应用,ESTEK公司的知名度逐渐提升,为公司的后续发展奠定了坚实基础。

CONTEC公司的发展小趣事

2008年,对于CONTEC公司来说是具有里程碑意义的一年。这一年,公司建成了康泰产业园(一期),并进一步加强了研发和生产能力。同时,公司技术中心被省发改委等部门确定为省认定企业技术中心,这标志着公司在技术研发和创新方面得到了官方认可和支持。随着产业园的投入使用,公司的生产规模和效率得到了显著提升。

全鹏(CHAMPION)公司的发展小趣事

随着电子商务的快速发展,全鹏公司也积极拥抱这一趋势。自2010年起,全鹏公司成立了电子商务零售部门,并与国内知名电商平台建立了长期战略合作关系。通过线上销售过季产品和折扣商品,全鹏公司的电商业务迅速增长,成为其新的增长点。同时,全鹏公司还不断优化电商平台的运营和管理,提升客户服务质量,为客户提供更加便捷、高效的购物体验。

以上五个故事基于全鹏公司在电子行业的发展历程和成就进行虚构,旨在展示全鹏公司在市场拓展、品牌建设、质量管理、研发创新和电商业务等方面的努力和成果。

高通(GENITOP)公司的发展小趣事

进入21世纪,超霸电池继续加大技术研发力度,推出了Recyko绿再系列高端绿色充电套装。这一系列产品不仅外观时尚、性能卓越,还实现了快速充电、低自放电等先进技术。其中,“十分充”产品更是将充电速度提升到了前所未有的高度,仅需10分钟即可将电池电量从0充至90%以上。Recyko绿再系列的成功推出,不仅为超霸电池赢得了更多用户的青睐,也推动了整个电池行业的技术进步。

问答坊 | AI 解惑

应用程序或DLL为无效的Windows映像。请在检测一遍您的安装盘。程序无法下载到CE环境里

问题如标题,程序没有办法下载到CE环境里,和这个无效的DLL镜像有关,怎么解决呢?…

查看全部问答>

win7系统中无法安装usb-blaster

  最近我在win7的操作系统中一直无法安装usb-blaster驱动程序,导致我无法在quartus II软件中下载程序,如图F:\\picture\\no hardware.jpg 但是我在安装usb-blaster驱动程序的时候,系统弹出对话框F:\\picture\\drivers.jpg,我怀疑是因 ...…

查看全部问答>

想从上层开发(asp.net..c#) 转到 底层的嵌入开发(C)....请前辈指点....回答必得分

现在以有一年的上层开发经验....对asp.net..c# 什么的开发都 比较行了... 但现在有一个很好的机会 ...不过是做底层的嵌入开发(C).... 请高手给点好的意见...... 如果转入嵌入开发(C),有没有好的东东可以介绍... 先谢谢各位!…

查看全部问答>

C++能做驱动开发吗?

请知道的告诉我以下,谢谢啦!(*^__^*) …

查看全部问答>

IAR报错!!!!!!!!!!在线等

                                 嗖嗖嗖嗖…

查看全部问答>

F2812的AD采样可以采样正弦波吗?

F2812的AD采样可以采样有正负的波形吗?我现在采用一个正弦波,但是采样后只有正半周,负半周为零,不知道是哪里出了问题??请高手指点一下,谢谢!…

查看全部问答>

LED驱动IC

在LED灯恒流的问题上,不知各位有哪些好的LED恒流IC?有用过NU501或是HV9910的吗?这两款在LED灯中运用得应比较广泛的吧。…

查看全部问答>

求Mini2440或JZ2440一套

如题出价280左右,吃灰的仍一个过来。…

查看全部问答>

比较G2系列和FR57xx的XT1模块,有重大变化

上图是G2的XT1,下图是FR57xx的XT1,看出区别没有?红色框框 FR57xx里面把内部的可调补偿电容给取消了,也就是需要外部电容了。…

查看全部问答>

LM258差分输入时必须正负电源供电吗?

今天将LM258用作差分输入,单电源供电,发现运放LM258正负输入端的电压不相等(虚短不成立),所以怀疑是不是LM258差分输入时不能单电源供电?…

查看全部问答>