Cortex-M3与M4权威指南——嵌入式软件开发概论

编译和链接
在大多数情况下,一个项目包含多个单独编译的文件。经过编译过程后,每个源文件都会有一个相应的目标文件。
为了生成最终的可执行映像,需要一个单独的链接过程。
在链接阶段之后,IDE还可以以其他文件格式生成程序映像,以便将映像编程到设备。

Flash编程
几乎所有的Cortex-M微控制器使用内存来存储程序。
创建程序映像后,我们需要将程序下载到微控制器的闪存中。
要做到这一点,需要一个调试适配器。
实际的Flash编程过程可能相当复杂,但这些通常由IDE完全处理,只需要单击鼠标即可执行整个编程过程。如果愿意,也可以将应用程序下载到SRAM并从那里执行它们。

执行程序和调试
编译后的程序下载到微控制器后,您可以运行程序,看看它是否工作。
可以使用IDE中的调试环境来停止处理器(通常称为halt)并检查系统状态以确保其正常工作。
如果不能正常工作,可以使用各种调试特性(如单步调试)详细检查程序操作。所有这些都需要一个调试适配器来连接IDE和被测试的微控制器。
如果发现了软件缺陷,那么可以编程程序代码,重新编译项目,将代码下载到微控制器,然后再次测试。

如果使用的是开源工具链,则可能没有IDE,并且可能需要脚本或使用Makefile来处理编译和链接过程。
根据使用的微控制器产品,可以使用第三方工具将编译后的程序映像下载到微控制器中的闪存中。

在编译程序的执行过程中,您可以通过各种I/O机制(如UART接口或LCD模块)输出信息来检查程序的执行状态和结果。本书中的一些示例将展示如何实现其中一些方法。

编译应用程序

编译嵌入式程序的过程取决于使用的开发工具。
在这里插入图片描述

嵌入式软件开发简介

首先,我们假设您正在使用C编程语言开发项目。这是微控制器软件开发中最常用的编程语言。我们的项目可能还包含一些汇编语言文件;例如,微控制器供应商提供的启动代码。

  • C编译器:将C程序文件编译成目标文件
  • 汇编器:将汇编代码文件汇编成目标文件
  • 链接器:将多个目标文件连接在一起并定义内存配置的工具
  • Flash编程器:将编译后的程序映像编程到微控制器的闪存中的工具
  • 调试器:一种工具,用于控制微控制器的操作和访问内部操作信息,以便检查系统状态和检查程序操作
  • 模拟器:允许在没有实际硬件的情况下模拟程序的执行

不同的开发工具有不同的方式来指定微控制器系统中程序和数据存储器的布局。
在ARM工具链中,可以使用一种称为分散加载文件,或者在Keil MDK-ARM,分散加载文件可以由mVision开发环境自动生成。
对于其他一些ARM工具链,您也可以使用命令行选项来指定ROM和RAM的位置。

在基于GNU工具链中,内存规范由链接脚本处理。这些脚本通常包含在商业GCC工具链的安装中。
但是,一些GCC用户必须自己创建这些文件。
在使用GNU gcc工具链时,通常一次编译整个应用程序,而不是将编译和链接阶段分开。

如果需要,gcc编译会自动调用链接器和汇编器。

这种安排确保将所需参数和库的详细信息正确地传递给链接器。使用链接器作为一个单独的步骤可能容易出错,因此大多数gcc工具供应商不建议使用。

输入,输出和外设访问

外设是内存映射的,这意味着寄存器可以从系统内存映射中访问。为了在C程序中访问这些外设寄存器,我们可以使用指针。

通常,外设在使用之前需要一个初始化过程。这可能包括以下步骤:

  • 编程时钟控制电路,使时钟信号连接到外设,时钟信号连接到相应的I/O引脚,如果需要。
    许多现代微控制器允许微调时钟信号分布,例如启用/禁用时钟连接到每个外围设备,以更好地节省能源。
    通常情况下,外设的时钟在默认情况下是关闭的,需要对外设进行编程之前启用时钟。在某些情况下,还需要启用外设总线系统的时钟。
  • 需要配置I/O引脚的操作模式。大多数微控制器都有可用于多种用途的多路I/O引脚。
    为了使用外设,可能需要配置器I/O引脚以匹配使用情况(例如,输入/输出方向,功能等)。此外,还需要编写额外的配置寄存器来定义预期的电气特性,例如输出类型(电压、上拉/下拉、漏极打开等)。
  • 外围配置。大多数外设包含许多可编程寄存器,在使用外设之前需要对其进行配置。
    在某些情况下,您会发现编程顺序比8位微控制器的编程顺序要复杂一些,因为32位微控制器上的外设通常比8位/16位系统上的外设复杂得多。另一方面,通常微控制器供应商将提供设备驱动程序库代码,您可以使用这些驱动程序函数来减少所需的编程工作。
  • 中断配置。如果外设要与中断操作仪器使用,则需要对Cortex-M3/4处理器(NVIC)的中断控制器进行编程,以启用中断并设置中断优先级。

所有这些初始化步骤都是通过在各个外设块中编程外设寄存器来完成的。如前所述,外设寄存器是内存映射的,因此可以使用指针访问。例如,你可以定义一个通用输入输出(GPIO)寄存器,它是由一些指针组成的。

__IO在CMSIS标准头文件中定义。它意味着一个易失性数据项(例如,外围寄存器),它可以被软件读或写。
除了__IO之外,外设寄存器也可以定义为__I(只读)和__O(只写)。

“uint32_t”(无符号32位整数)是C99中支持的数据类型。这确保了数据大小是32位的,独立于处理器架构,这可以帮助软件更具可移植性。要使用此数据类型,项目需要包含标准数据类型头(注意:如果您使用的是符合cmsis的设备头文件,则已在设备头文件中为您完成了此操作)。

CMSIS

ARM有庞大的生态系统,软件基础设施工作方式的某种形式的标准化就变得必要,以确保软件与各种开发工具以及不同软件解决方案之间的兼容性。

为了减少开发时间以及降低产品中存在缺陷的风险,软件重用变得越来越普遍。此外,嵌入式系统的复杂性也增加了第三方软件解决方案的使用。例如,嵌入式软件项目可能涉及来自许多不同来源的软件组件。

在这种情况下,各种软件组件的互操作性变得至关重要。由于所有这些原因,ARM与各种微控制器供应商、工具供应商和软件解决方案提供商合作开发CMSIS,这是一个涵盖大多数Cortex-M处理器和Cortex-M微控制器产品的软件框架。

CMSIS的目标包括:

  • 增强的软件可重用性使得在不同的Cortex-M项目中重用软件代码变得更加容易,从而减少了上市时间和验证工作。
  • 通过拥有一致的软件基础设施(例如,处理器核心访问功能的API,系统初始化方法,定义外围设备的通用样式),增强软件兼容性,来自不同源的软件可以一起工作,减少集成中的风险。
  • 易于学习的CMSIS允许从C语言轻松访问处理器核心功能。此外,一旦学会使用一种Cortex-M微控制器产品,由于软件设置的一致性,开始使用另一种Cortex-M产品要容易得多。
  • 与工具链无关的兼容CMSIS的设备驱动程序可以与各种编译工具一起使用,从而提供更大的自由度。
  • 开放CMSIS核心文件的源代码,任何人都可以下载和访问,任何人都可以使用CMSIS开发软件产品。

CMSIS是一个不断发展的项目。它最初是作为一种Cortex-M微控制器建立设备驱动程序库一致性的方法。

CMSIS-Core的标准化领域

从软件开发的角度来看,CMSIS-Core标准化了许多方面:

处理器外设的标准化定义——这些包括在嵌套向量中断控制器中的寄存器,处理器中的系统滴答定时器,可选的内存保护单元,系统控制块中的各种可编程寄存器,以及与调试功能相关的一些软件可编程寄存器。

访问处理器特性的标准化访问函数。这些函数包括使用NVIC进行中断控制的各种函数,以及访问处理器中特殊寄存器的函数。如果需要,仍然可以直接访问寄存器,但是对于一般编程来说,使用访问函数可以帮助软件可移植性。

大多数现代功能丰富的微控制器产品在应用程序开始之前需要一些时钟电路和电源管理寄存器的配置。
在符合CMSIS的设备驱动程序库中,这些配置步骤放在一个名为“SystemInit()”的函数中。
显然,此功能的实际实现是特定于设备的,可能需要针对各种项目需求进行调整。
然而,有一个标准化的功能名称,使它更容易设计人员挑选和开始使用一个新的Cortex-M微控制器设备。

CMSIS-Core组织

CMSIS文件被集成到来自微控制器供应商的设备驱动程序库包中。
设备驱动程序库中的一些文件是由ARM编写的?并且在各种微控制器供应商中是通用的。其它文件是特定于供应商/设备的。一般来说,我们可以将CMSIS定义为多个层次:

  • 核心外设访问层——名称定义,地址定义和访问核心寄存器和核心外设的辅助函数。这是特定于处理器的,由ARM提供。
  • 设备外设访问层——名称定义,外围寄存器的地址定义,以及系统实现,包括中断分配,异常向量定义等。这是特定于设备的(注意:来自同一供应商的多个设备可能使用相同的文件集)。
  • 外设访问功能——外围设备访问的驱动程序代码。这是特定于供应商的,是可选的。您可以选择使用微控制器供应商提供的外设驱动程序代码来开发应用程序,或者如果您愿意,也可以直接对外设进行编程。

另外还有一个用于外设访问的附加层:中间件访问层e。该层在当前版本的CMSIS中不存在。其思想是开发一组api,用于连接通用外设,如UART、SPI和以太网。如果存在这一层,中间件开发人员就可以基于这一层开发他们的应用程序,从而允许软件在设备之间轻松移植。

在这里插入图片描述

如何使用CMSIS-Core?

CMSIS文件包含在微控制器供应商提供的设备驱动程序包中。
因此,当您使用由微控制器供应商提供的符合CMSIS的设备驱动程序库时,已经在使用CMSIS。

通常,需要执行以下操作,向项目添加源文件,这包括:

  • 特定于设备的、特定于工具链的启动代码,以汇编或C语言的形式。
  • 特定于设备的设备初始化代码(例如system_.c)
  • 用于外设访问功能的其他特定于供应商的源文件。这是可选的。

在项目的搜索路径中添加头文件。这包括:

  • 用于外设寄存器定义和中断分配定义的特定于设备的头文件。(例如<设备>.h)
  • 一个设备专用的头文件,用于设备初始化代码中的函数(例如system_.h)
  • 一些特定于处理器的头文件(例如core_cm3.h,core_cm4.h),它们对所有微控制器供应商都是通用的。
  • 可选的外设访问函数附加特定于供应商的头文件
  • 在某些情况下,开发套件可能还预装了一些通用的CMSIS支持文件

在这里插入图片描述
在某些情况下,当您创建新项目时,集成开发环境(IDE)会自动为您设置启动代码。否则,您只需要手动将设备驱动程序库中的启动代码添加到项目中。启动代码是处理器启动序列所必需的,它还包括中断处理所需的异常向量表定义。