应用程序处理流程可以用三种方式来实现,下面是一些基本概念:
而对于更加复杂的应用程序,可以使用RTOS。RTOS将处理器时间划分为多个时间片,在有多个应用进程运行时,只有一个进程会获得时间片。使用RTOS,需要由定时器产生周期性的中断请求。当一个时间片的时间到时,RTOS的任务调度器会由定时器中断触发,并判断是否需要执行上下文切换。如果需要,任务调度会暂停正在执行的任务,并切换到下一个准备就绪的任务。如下:
RTOS可以提高系统的反应能力,并且确保在一定时间内能够执行到所有任务。
在许多嵌入式系统中,可用的输入和输出有简单的电子接口,如数字和模拟输入/输出接口(I/O)、UART、I2C和SPI等,此外,还提供了USB、以太网、CAN图像LCD以及SD卡等接口,这些接口是由微控制器的外设控制的。
Cortex-M0的寄存器映射到了系统空间,并且它们还控制着外设。外设的典型初始化过程一般包括以下步骤:
ARM微控制器可以使用的开发工具链有很多种,它们大多数都支持C和汇编语言。开发嵌入式工程时,可以使用C语言,也可以用汇编,或者两者混合编程。多数情况下,程序代码生成流程可以总结为如下所示的形式:
根据所使用开发工具的不同,链接器可能会使用命令行选项来指定内存分布。
生成可执行映像后,可以将其下载到微控制器的Flash存储器或内存中进行测试。开发工具与在线调试器配合使用时,可以分步进行以下工作:创建工程,编译应用程序,然后下载嵌入式应用程序到微控制器中,如下:
要想对Flash进行编程,可以使用开发组件中的调试器软件,或者从微控制器供应商网站下载的Flash编程工具。在微控制器上运行程序可以测试其正确性,而将调试器连接到微控制器上,则可以控制程序的运行并且观察其各种操作。这些功能都可以通过Cortex-M0处理器的调试接口实现,如下:
C语言和汇编语言的使用比较如下:
Cortex-M0的程序映像一般包括以下几个部分:
向量表:
向量表可以用C语言或汇编语言实现。由于向量表的入口需要编译器和链接器生成的内容,所以向量表代码的实现细节是同开发工具链相关的。
为了将向量表置于系统存储器映射的开头(地址为0x00000000),链接文件或命令行选项需要知道段的名字,以便链接器能够正确识别向量并将其地址映射。
复位向量一般指向C启动代码的开头,不过,也可以自己定义复位处理,在跳转到C启动代码前执行附加的初始化操作。
C启动例程:
C启动代码用于设置像全局变量之类的数据,也会清零加载时未被初始化的内存区域。初始化完成后,启动代码跳转到main()程序执行。
C启动代码由编译器/链接器自动嵌入到程序中,并且是和开发工具链相关的,而只使用汇编代码编程则可能不存在C启动代码。对于ARM编译器,C启动代码被标识为"_mian",而是用GUN C编译器生成的代码则通常被标记为"_start"。
程序代码(应用程序代码和数据):
用户指定的任务是由应用程序生成的指令完成的,除了指令以外,还有以下各类数据:
1.变量的初始值,函数或子程序中的局部变量需要初始化,这些初始值会在程序执行期间被赋给相应的变量。
2.程序代码中的常量,应用程序中的常量数据有多种用法,如数据值、外设等寄存器的地址和常量字符串等,这些数据在程序映像中一般作为数据块放在一起,并被称作文字库。
3.其他的常量,如查找表和图像数据。
C库代码(C库函数的程序代码,链接时插入):
当使用特定的C/C++库函数时,它们的库代码就会由链接器嵌入到程序映像中。另外,由于有些数据处理任务需要浮点数或除法运算,在进行这些运算时,C库代码也会被包含进来。Cortex-M0中没有除法指令,需要借助C函数库中的除法函数来实现除法运算。
除了向量表需要放在存储器映射的开头以外,程序映像中剩余部分的位置就没有限制了。
RAM中的数据:
像程序ROM一样,微控制器的RAM也有多种用法。典型地,RAM的使用一般可以分为数据、栈和堆区域,如下所示:
数据、栈和堆区域存储内容如下:
数据:数据存储在内存的底部,包含全局变量和静态变量,为了节省内存,可以将局部变量分配在栈上,而且函数内未用的局部变量不占用存储器空间。
栈:栈空间用于临时数据存储(常见的PUSH和POP操作)、局部变量的存储空间、函数调用参数传递和异常处理的寄存器备份等。
堆:堆存储用于C函数自动分配存储器区域,以及其他使用这些函数的函数调用。为了确保这些函数能够正确地分配存储器空间,C启动代码需要初始化堆存储及其控制变量。
一般来说,栈位于存储器空间的顶部,而堆区域则位于底部,这样做使得内存使用具有最大的灵活性。在操作系统环境中,可能会有多个内存区域用作数据、栈和堆。
有些操作系统允许用户自定义任务的栈,这样也就需要更大的栈空间。有些操作系统则将内存分为若干个段,每个任务分配一个段,用于各自的数据、栈和堆区域,如下:
Cortex-M0处理器支持的数据类型如下:
将应用程序从其他架构的处理器移植到ARM上时,如果两者的数据类型的宽度不同,那么可能就需要修改C代码,以确保程序运行正常。此外,在Cortex-M0上编程时,变量的存放地址为其数据宽度的倍数。
随着嵌入式软件复杂性的增加,软件代码的兼容性和可重用性变得更加重要。为了使软件产品具有高度的兼容性和可移植性,ARM同一些微控制器供应商一起开发了一个通用的软件框架CMSIS,该框架适用于大多数的Cortex-M处理器以及Cortex-M微控制器产品,如下:
CMSIS一般是作为微控制器厂商提供的设备驱动库的一部分来使用的。为了使用诸如NVIC和系统控制功能等处理器特性,CMSIS提供了一种标准化的软件接口。
CMSIS为嵌入式软件提供了以下标准化的内容:
CMSIS可以分为以下 几层:
这些层的作用在下图中做了总结:
CMSIS被集成在微控制器供应商提供的设备驱动包中,如果使用设备驱动库进行软件开发,那么就已经在使用CMSIS了。
对于C程序代码,通常只需要包含微控制器供应商提供的设备驱动库里的头文件。这个头文件又包含了其他所有需要的文件,包括CMSIS特性和外设驱动等。
下图是包含了一个使用CMSIS包建立的简单工程,有些文件的名字由实际的微控制器设备决定(在图中标识为< device >)。
下图是一个使用CMSIS的简单例子:
不同的Cortex-M微控制器之间的程序移植变得容易。
提高了兼容性,这是因为由于CMSIS文件的使用,中间件和嵌入式操作系统就会基于相同的内核外设寄存器定义以及内核访问函数。
这同时也减小了代码冲突的概率,因为不同软件组件都分别使用自己的内核访问函数和寄存器定义时,就可能会发生冲突。没有CMSIS,可能会发现不同的第三方软件组件都包含了自己特有的驱动函数,这就可能会带来诸多问题,包括寄存器命名冲突、多个函数拥有类似命名引起的混乱以及重复函数带来的空间浪费,如下:
CMSIS内核访问函数占用的存储器空间很小,并且经过测试,这有助于减少软件测试的时间。