APP下载

基于ARMCortex?M+KL系列MCU的汇编构件设计

2018-01-20蔡伯峰王宜怀

现代电子技术 2018年1期
关键词:寄存器底层串口

蔡伯峰+王宜怀

摘 要: 汇编编程的复杂性及有关32位ARM Cortex?M0+等内核汇编开发资料和样例程序的短缺,使编程者学习、研究和开发微处理器汇编程序难度很大。针对这一现状,在对ARM Cortex?M0+内部寄存器、汇编指令系统等进行深入分析的基础上,以NXP半导体公司KL系列MCU为蓝本,提出一种规范、易用、实用的汇编构件设计编程方法。该方法根据软件工程构件设计思想,基于构件封装要点分析和汇编工程框架,设计并实现汇编构件,并给出了典型UART模块的汇编底层驱动构件的样例程序。通过对汇编构件设计编程方法的使用和样例程序的学习与理解,降低嵌入式汇编学习和编程难度,并进而轻松设计其他类似的规范化的汇编构件和程序。

关键词: ARM Cortex?M0+; 汇编构件设计; KL系列MCU; 底层驱动构件; 汇编工程框架; UART

中图分类号: TN919?34; TP311 文献标识码: A 文章编号: 1004?373X(2018)01?0038?05

Abstract: It is difficult for programmer to learn, study and develop the microprocessor assemble program due to the complexity of assembly programming and the shortage of assembly development data and sample programs of the 32?bit ARM Cortex?M0+, and other kernels. In view of this situation, on the basis of the deep analysis of the ARM Cortex?M0+ internal register and assembly instruction system, the KL Series MCU made by NXP is taken as the exsample to present a standardized, usable and practical design method for assembly component. According to the design thought of software engineering component, analysis of component packaging key points and assembly engineering framework, the assembly component was designed and implemented. The sample program of the assembly bottom?driven component of the typical UART module is given. With application of design and programming methods of the assembly component, and by learning and understanding of the sample programaim, the difficulty of the embedded assembly learning and programming is reduced, and the design of other similar standardized assembly components and programs becomes easy.

Keywords: ARM Cortex?M0+; assembly component design; KL series MCU; bottom?layer driving component; assembly project framework; UART

0 引 言

ARM Cortex?M0+微处理器是ARM公司的一款耗电量仅为9 μA/MHz的全球最低功耗的32位微处理器,性价比高、易用,并具有良好的软件兼容性,可方便地移植到更高性能的Cortex?M3/M4上。该微处理器基于ARMv6M架构,支持Thumb指令集和部分Thumb2指令集[1]。NXP半导体公司的KL系列MCU是业内首款采用ARM Cortex?M0+内核的MCU,具有应用设计方便、可扩展性好、超低功耗、品种齐全等特点[2]。目前,对微处理器的应用开发绝大多数编程者使用C语言,构件设计和研究也是针对C语言驱动构件进行的[3?5],这些研究成果并不适合汇编编程,尤其是汇编底层驱动构件设计,因此,汇编资料、样例程序和相关研究成果非常短缺。汇编编程是嵌入式开发的基本功,掌握好汇编编程可增加编程者的“内力”。学习研究一些组织结构完整、清晰的汇编程序如硬件底层汇编驱动等,对嵌入式开发有很大帮助,也有助于更深层次地理解微处理器软件的设计。实际上,一些微处理器深层次应用开发如MCU初始化、操作系统调度、快速响应[6]等特殊功能的实现必须使用汇编完成。

本文通过对NXP公司生产的采用ARM Cortex?M0+内核的KL系列MCU进行深入研究,在分析内核特点、内部寄存器及汇编指令系统的基础上,以提高嵌入式软件的可重用性和可移植性,降低嵌入式汇编语言学习、开发难度为目标,基于对汇编构件封装要点分析和笔者研制的汇编工程框架,规范地设计汇编构件,并针对MCU必不可少的典型模块UART进行具体实现。

1 汇编构件封装要点分析

在嵌入式软件领域,由于软、硬件紧密联系的特性,使得底层驱动程序开发成为嵌入式软件开发的重要内容,其好坏直接影响嵌入式系统的稳定性和可靠性。因此,驱动程序开发要按照构件化设计原则进行分析设计,并按照规范的过程实现,以提高其可重用性与可移植性。endprint

汇编底层驱动构件是具有独立性的功能或函数集合,对其进行封装要点分析就是要分析出应设计哪些函数及出入口参数。通常可根据MCU各个模块所具有的基本操作来确定应设计哪些函数。以MCU典型的串口模块——UART模块为例,由于它具有初始化、发送和接收三种基本操作,按照构件设计思想,可将其封装成各个独立的功能函数,分别为初始化、发送单个字节、接收单个字节。其中,初始化函数用来设定UART模块工作属性,发送和接收单个字节函数用来实现实际的通信,但从实际使用出发,还应封装发送[N]个字节、发送字符串、接收[N]个字节及串口接收中断使能与禁止等功能函数。

要实现编程的构件化,在分析出各个功能函数后,还要充分设计好函数出入口参数,并提供对外服务接口的使用注释,因为出入口参数设计的好坏会直接影响构件化编程的成败。以初始化函数uart_init为例,由于串口可能有多个,每个串口使用的MCGIRCLK,MCGPLLCLK,BUSCLK等时钟源并不相同[7],收发数据时的波特率也可有多种选择,所以串口号、时钟源、波特率都被设计为入口参数,而函数不必有返回值,这样当应用程序和高层构件在调用时将具有极大的灵活性。

按照uart_init函数设计方法设计的UART构件的所有功能函数见表1。

2 用于汇编构件开发的汇编工程框架

架构清晰、规范易用的汇编工程框架是汇编底层驱动开发的基础,能极大地降低嵌入式汇编语言学习难度、提高开发效率。笔者经过多年的研究,研制了在主流嵌入式集成开发环境KDS下使用的树状结构的汇编工程框架,其文件组织结构见表2。该工程框架为底层驱动构件的开发提供了统一的工程模板,使用它可方便快速地開发底层驱动构件和汇编程序,也方便构件的移植和重用。

通过对汇编框架组织结构的分析可知,使用汇编框架开发驱动构件的步骤为:

1) 在KDS中导入汇编工程框架,以创建汇编底层驱动构件工程项目。

2) 在05_Driver文件夹<05_Driver >中新建以构件名命名的构件文件夹,如

3) 按照提供的gpio构件的头文件(.inc)和汇编源程序文件(.S)内容布局模板,在新构件文件夹中设计构件头文件和源程序文件。

4) 在<08_Source>中,按照提供的各个文件的内容布局模板编制测试样例程序。

5) 工程编译链接后,将目标代码(.elf)下载到包含有MCU硬件最小系统的目标板上运行测试。

表2 树状结构的汇编工程框架

Tab. 2 Assembly engineering framework of tree structure

3 汇编构件头文件与源程序文件设计

由于对MCU模块的编程,实际上是对硬件底层寄存器的直接操作,即通过操作相关寄存器对硬件模块进行操作,因此,可将MCU各个模块的所有功能函数分别集中放置在以各个模块名命名的.S源文件中,并按照相对严格的构件设计原则封装,同时配以以各个模块名命名的.inc头文件,以定义相应模块的基本信息和对外接口。这两种文件分别放置在汇编工程的<05_Driver>文件夹下以模块名命名的构件文件夹中,如<05_Driver/uart>等。当其他工程需要使用相应构件时,一般只需简单拷贝构件文件夹中的这两个文件即可,只有在进行不同芯片间的移植时,才需检查并修改头文件中与硬件相关的宏定义(若内核不同,还需修改源文件中的部分汇编指令)。

3.1 UART模块的编程结构

为了方便理解后文设计实现的汇编底层驱动构件,此处以KL系列MCU的UART模块为例分析一下其编程模型和使用到的寄存器情况。

UART模块的主要功能是收发数据,即接收时,将外部单线输入数据变成一字节并行数据送MCU内部;发送时,将待发送的一字节并行数据转换为单线输出[8],因此UART模块编程时采用的编程模型如图1所示。其中,UART波特率寄存器用于设置波特率;UART控制寄存器用于设置通信格式、是否校验和允许中断等;UART状态寄存器用于判断串口收发数据状态,如数据是否发送出去、是否有数据可收等。而UART的2个数据寄存器分别存放收、发的数据。程序员编程时,直接对数据寄存器操作,再由MCU自动完成对移位寄存器的操作。

KL25系列MCU每个UART模块都有相对应的8位寄存器,寄存器包括控制类寄存器C1~C5,状态寄存器S1~S2,波特率寄存器BDH和BDL,数据寄存器D,地址匹配寄存器MA1~MA2等几种,各个寄存器通过映像地址访问。

UARTi的寄存器映像地址=0x4006_A000+i*1 000+n*1。其中i=0~2;n表示寄存器号,UART0时,n=0~11分别代表寄存器BDH,BDL,C1,C2,S1,S2,C3,D,MA1,MA2,C4,C5,UART1,2时,n=0~8分别代表寄存器BDH,BDL,C1,C2,S1,S2,C3,D,C4[9]。

与UART模块编程相关的各个主要寄存器的功能及寄存器关键位[2]见表3。

3.2 头文件设计

头文件描述了构件接口,用户通过头文件获取构件服务。一个合格的头文件应是一份完备、简明的信息定义和操作使用说明,除包含基本的程序编码框架、对其他文件的包含语句外,还包含模块本身及相关寄存器信息的定义、各个功能函数全局声明与对外服务接口的详细说明,使用者无需查看源文件就能完全使用该构件。

以UART构件为例,uart.inc包含的部分内容如下:

#---------------------------------------------

#文件名称:uart.inc

#功能概要:KL25 UART底层驱动构件(汇编)头文件

#---------------------------------------------

#ifndef UART_INC @编码框架

#define UART_INC

.include "gpio.inc" @包含外部构件头文件

.section .rodata @数据定义:各串口的基地址

UART_BASE_PTR: .word 0x4006A000, 0x4006B000,

0x4006C000

.equ UART0,(0<<16) @宏定义各个串口号

.equ UART1,(1<<16)

.equ UART2,(2<<16)

.equ MCGIRCLK,4000 @宏定义各个时钟源频率

.equ MCGPLL,48000

.equ BUSCLK,24000

.equ UART_BDH,0x0 @宏定义各寄存器偏移地址

.equ UART_BDL,0x1

.equ UART_C1,0x2

.equ UART_C2,0x3

.equ UART_S1,0x4

.equ UART_D,0x7

.equ S1_TDRE_mask,0x80 @宏定义S1_TDRE掩码

……

#---------------------------------------------

# 函数名称:uart_init

# 函数返回:无

#参数说明:r0:((串口号)|(时钟源KHz)) 例(UART1|BUSCLK)表示UART1、总线时钟

# r1: 波特率:300、600、1 200…

#功能概要:初始化UART模块。

#---------------------------------------------

.global uart_init @全局函数声明

……

#endif

3.3 源程序文件设计

为保证构件工作的独立性,实现高内聚、低耦合的设计要求,构件的实现内容应封装在源文件内部。源文件内容包括自身头文件包含语句、各个功能函数和内部函数的实现代码。源文件中只允许一处使用“.include xxx”语句,包含自身头文件,需要包含的内容应在自身头文件中包含,以便有统一、清晰的程序结构。源文件要给出良好的封装、简洁的说明与注释、清晰的对外接口说明、规范的编程风格等,方便编程者进行学习、研究。限于篇幅,本文仅给出UART构件的uart_send1函数的实现代码,使用的指令可参考文献[1,10?11]中关于ARM Cortex?M0+汇编指令集的有关内容。

#---------------------------------------------

#函数名称:uart_send1

#参数说明:r0:串口号,用0~2表示UART0~2

# r1:待发送的字节

#函数返回:r0:0=正常,1=异常

#功能概要:串行发送1个字节

#---------------------------------------------

uart_send1:

push {r4?r7,lr} @保存现场,将pc(lr)入栈

#初始化循环变量r5和存放各串口基地址的单元地址偏移量

mov r5 ,#0 @r5=0

lsl r0,#2 @r0=r0*4,基地址单元地址偏移量

#在规定时间内(循环一定的次数)轮询发送缓冲区,一旦为空则将待发字节送数据寄存器D发送,否则退出循环

send1_loop:

ldr r4,=0xFBBB @r4=发送缓冲区是否为空轮询次数

cmp r5,r4 @判断循环变量值

bcs send1_exit @达到轮询次数阈值转send1_exit

add r5,#1 @当前循环变量加1

ldr r7,=UART_BASE_PTR @r7=所有UART的基地址

ldr r7,[r7,r0] @r7=特定UART的基地址

ldr r6,=UART_S1 @r6=S1寄存器偏移地址

ldrb r4,[r7,r6] @r4=S1寄存器内容

uxth r4,r4 @扩展成32位无符号数

ldr r6,=s1_TDRE_mask @r6=S1寄存器的TDRE位掩码

and r4,r6 @取出S1中TDRE位的值

cmp r4,#0 @判断发送缓冲区是否为空

beq send1_loop @非空则继续轮询,为空则继续

ldr r6,=UART_D @为空,r6=D寄存器偏移地址

strb r1,[r7,r6] @将待发送字节送D寄存器发送

send1_exit:

#根据是否超时来判断是否发送成功

ldr r4,=0xFBBB @r4=0xFBBB, 轮询次数阈值

cmp r5,r4 @比较r5与0xFBBB的大小endprint

bcc send1_suc @小于则发送成功,转send1_suc

mov r0,#1 @大于等于则发送失败,r0=1

b send1_end @转send1_end处理

send1_suc:

mov r0,#0 @发送成功,r0=0

send1_end:

pop {r4?r7,pc} @恢复现场,将lr出栈到pc

在高层构件或应用程序中要使用构件的某个功能函数,需先通过“.include xxx”语句包含构件头文件,再根据功能函数的出入口参数要求提供参数值,就可通过“bl功能函数名”语句来调用功能函数了。

3.4 汇编构件的测试

测试工程实现功能的是:主程序对UART模块初始化后,向PC端分别以字符串、单字节、多字节等方式发送数据;开启UART模块接收中断用中断方式接收PC端发来的数据,接收后回发给PC端。中断服务例程中,先通过uart_re1函数接收来自PC端的数据,再通过uart_send1函数回发给PC端。

按照前文介绍的“使用汇编框架开发驱动构件的步骤”中所述方法在KDS环境下编制测试工程的相关程序,程序代码不再赘述。再对测试工程编译链接后,将目标代码文件(.elf)下载到KL25开发板上,并将开发板的UART接口通过USB?TTL串口线与PC机相连,在PC端运行通用的串口调试器软件。开发板重新上电后开始测试。

经测试,PC端的串口调试器界面上收到MCU方主程序依次用uart_send_str函数发送的信息“现在测试uart_send_str 函数的功能.”、两次用uart_send1函数发送的换行符、用uart_sendN函数发送的信息“现在测试uart_sendN 函数的功能.”,当从串口调试器界面上发送信息“这是从本界面发送的信息.”后,立即在界面上收到MCU方回发的信息,如图2所示。对KL25的3个UART模块分别进行反复测试。测试结果表明,UART模块能正确收发数据且运行稳定,说明本文所设计的UART汇编构件正确有效且易于使用。

4 结 语

本文以KL系列MCU为蓝本,按照构件化设计原则对汇编底层驱动构件的封装要点进行详细的分析,设计了构件的各个功能函数及出入口参数;根据汇编工程框架给出了开发驱动构件的规范步骤和设计实现构件头文件和源程序文件的方法。在对UART模块的编程模型、寄存器功能、Cortex?M0+内核指令系统进行深入分析的基础上,具体实现了UART汇编构件,并对其进行测试。旨在引导读者通过规范的汇编构件设计编程及样例程序的学习研究,较快地掌握嵌入式汇编语言的编程方法及在构件设计中的应用,并能自行设计其他汇编构件、编写汇编程序。

参考文献

[1] ARM. Cortex?M0+ processor technical reference manual [DB/OL]. (2012?12?16) [2016?06?20]. http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0432c/index.html.

[2] NXP. KL25 sub?family reference manual [EB/OL]. (2012?09?01) [2016?06?20]. http://www.nxp.com/.

[3] 蔡剑卿,王宜怀,冯德旺,等.可移植的UART底层驱动构件设计[J].福建农林大学学报(自然科学版),2014,43(3):332?335.

CAI Jianqing, WANG Yihuai, FENG Dewang, et al. Design of UART′s bottom?layer driving component with good portability [J]. Journal of Fujian Agricultural and Forestry University, 2014, 43(3): 332?335.

[4] 胡宗棠,王宜怀.构件化ColdFire系列MCUs通用GPIO驱动设计[J].微计算机信息,2012,28(4):69?71.

HU Zongtang, WANG Yihuai. Component?oriented general GPIO driver design of ColdFire series MCUs [J]. Microcomputer information, 2012, 28(4): 69?71.

[5] 杨炯,曹金华,王宜怀.基于KL25的UART通信UHM构件研究与实现[J].实验室研究与探索,2014,33(9):122?126.

YANG Jiong, CAO Jinhua, WANG Yihuai. Research and rea?lization of UHM unit for UART based on KL25 [J]. Laboratory research and exploration, 2014, 33(9): 122?126.

[6] 凌艺春,黄飞.汇编程序移植性的研究与实践[J].制造业自动化,2011,33(3):174?175.

LING Yichun, HUANG Fei. Research and practice of assembler portability [J]. Manufacturing automation, 2011, 33(3): 174?175.

[7] NXP. Kinetis KL25 sub?family data sheet [EB/OL]. (2014?08?01) [2016?06?20]. http://www.docin.com/p?2035480854.html.

[8] GIOVANI G, SEBASTIAN F. Tracing and recording interrupts in embedded software [J]. Journal of systems architecture, 2012, 58(9): 372?385.

[9] 王宜怀,朱仕浪,郭芸.嵌入式技术基础与实践:ARM Cortex?M0+ KinetisL系列微控制器[M].3版.北京:清华大学出版社,2013:48?51.

WANG Yihuai, ZHU Shilang, GUO Yun. Embedded technology foundation and practice: ARM Cortex?M0+ KinetisL series microcontrollers [M]. 3rd ed. Beijing: Tsinghua University Press, 2013: 48?51.

[10] ARM. Cortex?M0+ devices generic user guide [EB/OL]. (2012?12?18) [2016?06?20]. https://wenku.baidu.com/view/f26247f2?f61fb7360b4c65a9.html.

[11] NXP. Kinetis assembler reference manual [EB/OL]. (2014?02?01) [2016?06?20]. http://www.nxp.com/.endprint

猜你喜欢

寄存器底层串口
航天企业提升采购能力的底层逻辑
浅谈AB PLC串口跟RFID传感器的通讯应用
Lite寄存器模型的设计与实现
分簇结构向量寄存器分配策略研究*
USB接口的多串口数据并行接收方法探索
基于蓝牙串口适配器的GPS接收机与AutoCAD的实时无线通信
基于并行控制的FPGA多串口拓展实现
回到现实底层与悲悯情怀
中国底层电影研究探略
略论“底层”