ARM Cortex-M3嵌入式开发实例详解
上QQ阅读APP看本书,新人免费读10天
设备和账号都新为新人

第3章 LPC1700程序设计基础

本章内容是LPC17XX软件设计的基础。本章主要介绍了MDK开发环境、ARM公司的CMSIS,以及NXP公司的固件库。在MDK部分,主要介绍了工程建立、编译、调试等操作。CMSIS作为ARM的软件设计标准,对ARM的软件设计做了各项规定。NXP公司对CMSIS做了一些具体实现,并在此基础上定义了各种针对LPC17XX外设的操作函数与定义。

3.1 RealView MDK开发环境

RealView MDK开发工具源自德国的Keil公司,被全球超过10万的嵌入式开发工程师验证和使用,是ARM公司目前最新推出的针对各种嵌入式处理器的软件开发工具。RealView MDK集成了业内最领先的技术,包括μVision集成开发环境与RealView编译器。它具有支持ARM7、ARM9和最新的Cortex-M3核处理器,自动配置启动代码,集成Flash烧写模块,强大的Simulation设备模拟、性能分析等功能。与ARM之前的工具包ADS等相比,RealView编译器的最新版本可将性能改善超过20%。

3.1.1 RealView MDK开发环境简介

RealView MDK开发环境提供了启动代码生成向导。由于启动代码和系统硬件结合紧密,必须用汇编语言编写,所以MDK开发环境成为许多工程师难以跨越的门槛。RealView MDK的μVision工具可以帮助用户自动生成完善的启动代码,并提供图形化的窗口,便于用户轻松修改。无论是初学者,还是有经验的开发工程师,它都能大大节省时间,提高开发效率。

μVision IDE是一款集编辑、编译和项目管理于一身的基于窗口的软件开发环境。μVision集成了C语言编译器、宏编译、链接/定位,以及HEX文件产生器。μVision具有如下特性:

(1)具备功能齐全的源代码编辑器;

(2)具备用于配置开发工具的设备库;

(3)具备用于创建工程和维护工程的项目管理器;

(4)所有的工具配置都采用对话框进行;

(5)集成了源码级的仿真调试器,包括高速CPU和外设模拟器;

(6)具备用于往Flash ROM下载应用程序的Flash编程工具;

(7)具备完备的开发工具帮助文档、设备数据表和用户使用向导。

RealView MDK具有良好的界面风格。如图3-1所示是一个典型的μVision调试界面。

图3-1 μVision调试界面

(1)工程区:用于访问文件组和文件,调试时可以查看CPU寄存器。

(2)调试输出窗口:显示编译结果,以便快速查找错误的地方,它同时还是调试命令窗口,可以用于显示查找结果。

(3)内存窗口:显示指定地址内存里的内容。

(4)代码窗口:用于查看和编辑源文件。

(5)外设对话框:检查微控制的片上外设的状态。

RealView MDK提供了性能分析器,可根据模块或函数实现对程序运行时间、被调用次数的统计,可用于代码优化、性能检测。该分析器需要在模拟器状态下进行,如图3-2所示。

图3-2 性能分析器

3.1.2 工程创建、编译

本节是结合LPC176X的兼容CMSIS标准外设的固件驱动库(简称固件库)(“LPC175X and LPC176X CMSIS-Compliant Standard Peripheral Firmware Driver Library”)进行讲解的,该固件库提供了CMSIS兼容的驱动程序,这里直接使用这部分驱动程序。本节不涉及LPC176X的固件库内容,该部分内容将在后续章节中介绍。

该固件库中有一个core目录。该目录包含启动文件配置信息及CMSIS文件内容。该目录文件的含义参见3.2节及3.3节。在本节中,需要将该目录复制到建立工程的目录中。

一个项目需要经过以下几大步:

(1)选择处理器与启动文件;

(2)配置必备文件;

(3)配置工程信息;

(4)编译链接;

(5)程序下载;

(6)调试程序。

下面将详述每个步骤。

(1)建立工程。在主菜单中选择“new →new project”命令,建立名为“helloworld”的工程。建立工程之后,会弹出选择CPU型号的对话框,选择LPC1768,如图3-3所示。

图3-3 选择CPU型号

(2)单击图3-3中的“OK”按钮,在弹出的对话框中确定是否需要复制启动文件,如图3-4所示。

图3-4 是否复制启动文件

这里选择否(当然可以根据需要选择是)。如果该步骤选择是,将使用Keil自带的启动文件。该启动文件需要在main函数里增加SystemInit函数调用。如果使用NXP提供的固件库,则不需在main函数里调用SystemInit()函数。两者的文件区别如下:

        Reset_Handler   PROC
                      EXPORT  Reset_Handler           [WEAK]
                      IMPORT  SystemInit
                      IMPORT  __main
                      LDR    R0,=SystemInit;NXP提供的固件库中有此调用,但Keil提供的启
                                         ;动文件无此启动文件。
                      BLX    R0
                      LDR    R0,=__main
                      BX     R0
                      ENDP

(3)管理项目文件组织。

在工程区单击鼠标右键,会出现如图3-5所示的菜单,选择“Manage Components…”选项。

图3-5 项目文件组织

在弹出的如图3-6所示的对话框中添加相应文件。

图3-6 添加文件

在图3-6中,在Groups菜单里添加“Source”、“Startup”、“CMSIS-core”3个管理文件夹,然后在Files菜单里分别针对各组添加文件:

① startup管理文件夹里添加\Core\CM3\DeviceSupport\NXP\LPC17xx\startup\arm目录下的startup_LPC17xx.s文件;

② CMSIS-core管理文件夹里分别添加\Core\CM3\CoreSupport目录下的core_cm3.c;Core\CM3\DeviceSupport\NXP\LPC17xx目录下的system_LPC17xx.c。

(4)选择工具集:默认情况使用RealView编译器,如图3-7所示。对于计划使用GNU编译器的,请选择“Use GNU Compiler”。

图3-7 工具集设置

(5)工程配置。

在图3-5中,如果选择“Options for Target’helloworldProject’”将会弹出“Options for Target’helloworldProject’”对话框。

如图3-8所示是晶振设置,该时钟信息应与硬件环境下的时钟信息相同。

图3-8 晶振设置

单击图3-8中的“Output”选项卡,会出现如图3-9所示的配置界面,在该界面上根据需要设置输出文件的目录信息。各选项的含义如下所示。

图3-9 设置编译输出路径及输出文件信息

(1)Create Executable:生成axf及HEX文件。

(2)Debug Information:用于Debug版本,生成调试信息,否则无法进行单步调试。

(3)Create Batch File:生成用于实现整个编译过程的批处理文件,使用这个文件可以脱离IDE对程序进行编译。

(4)Create HEX File:这个选项在默认情况下未被选中,如果要烧录就必须选中该项。

(5)Browse Information:产生用于在源文件快速定位的信息。

在图3-10中,在“Include Paths”处添加链接目录“.\Core\CM3\CoreSupport;.\Core\CM3\DeviceSupport\NXP\LPC17xx”。也可以采用图3-10中右图所示的方式,使用弹出的“Folder Setup”对话框进行设置。

图3-10 定义宏及编译链接路径

在图3-10左图所示的“Options for Target’helloworldProject’”对话框中,Preprocessor Symbols是添加整个工程的宏定义。

单击图3-10中的“Debug”选项卡,会出现如图3-11所示的界面。该页面用于设置程序调试方式。可选择模拟器方式,或者选择仿真器方式。

图3-11 选择调试方式及设置仿真器类型

模拟器的使用注意事项:在模拟器中测试程序,如果在main函数里定义了局部变量,则使用调试窗口观察发现该变量的运行结果不正常。可以将该变量改成全局变量。对于有条件能在实际环境运行的,还是推荐使用实际环境运行程序。

Flash编程的设置如图3-12和图3-13所示。图3-12中的“Use Target Driver for Flash Programming”默认选择Ulink仿真器。“Settings”按钮用于Flash编程的设置。下载参数的设置如图3-13所示。下载参数需选择“LPC17xx IAP 512kB Flash”。该处需要结合硬件实际情况进行选择,否则运行结果不正常。

图3-12 设置编程器类型

图3-13 设置Flash下载参数

(6)准备好工程文件后,可以按一下F7键进行编译。编译完毕工程文件后,需要将程序下载到LPC17XX中,可以通过主菜单中的“Flash→Download”命令进行下载,如图3-14所示。如果在图3-11中选择的是模拟器方式,则可以跳过该步。

图3-14 程序下载

(7)下载完毕程序后,进行仿真调试。通过主菜单中的“Debug→Start/Stop Debug Session”命令,开启调试,然后单击“Run”子菜单进行调试,如图3-15所示。

图3-15 调试

3.1.3 Flash编程器

ULINK2是ARM公司最新推出的配套RealView MDK使用的仿真器,如图3-16所示。开发工程师通过结合使用RealView MDK的调试器和ULINK2,可以方便地在目标硬件上进行片上调试(使用on-chip JTAG,SWD和OCDS)、Flash编程。ULINK2可实现USB通信接口高速下载用户代码;查看存储区域/寄存器;运行快速单步程序;可设置多个程序断点;片内Flash编程。

图3-16 ULINK2

ULINK2的特点:

(1)具备标准Windows USB驱动,支持ULINK2即插即用;

(2)支持基于ARM Cortex-M3的串行调试;

(3)支持程序运行期间的存储器读/写、终端仿真和串行调试输出;

(4)支持10-pin接线(也支持20-pin连接线)。

3.1.4 简易实例

经过3.1.2节与3.1.3节两小节的准备工作,现在开始实现显示“hello world”。在C语言中,最简单的“hello world”只有两行:

        void main ()
        {printf(“hello world”);
        }

在ARM环境中,可以通过UART输出字符串(第8章中将介绍UART的使用)。本节使用MDK的模拟器输出字符串,使之更符合我们学习任何一门语言所见到的第一个程序设计的习惯。

由于标准库函数的默认输出设备是显示器,所以要想实现在串口或LCD输出,必须重定义标准库函数里调用的与输出设备相关的函数。这里将printf函数重定向到串口,见serial.c文件。由于printf()之类的函数使用了半主机模式,使用标准库会导致程序无法运行,所以这里采用#pragma import(__use_no_semihosting) 关闭半主机模式,见retarget.c文件。

Serial.c与retarget.c文件,以及配套serial.h文件均在Keil\ARM\Boards\Keil\MCB1700\Blinky目录下存在(读者所安装的mdk目录)。请自行将其复制到3.1.3节中建立的工程目录下。

创建helloworld.c文件之后,将其添加到3.1.3节创建的工程中。Helloworld.c文件的内容如下:

        #include <stdio.h>
        #include"LPC17xx.H"                 /*LPC17xx definitions*/
        #include "Serial.h"
    @@@
        void  main()
        {
          SER_init(1);
        printf("hello world \n");
        }

选择模拟器方式运行编译,可通过“view→serial windows→Uart#2”看到输出的“hello world”,如图3-17所示。

图3-17 运行结果

3.2 CMSIS——Cortex-M3微控制器软件接口标准

条条大路通罗马。ARM7TDMI的年代给出了100条甚至1万条大路通罗马(供选择的很多,而且每个都有着美好前景。但这就跟找对象/工作一样,容易挑花眼)。但对于一个需要经常往来于罗马及罗马之外的地区的人而言,每次往来都是不同路线,可能是一件痛苦的事情,毕竟去罗马不需要那么多次旅游。对于需要经常更换ARM7TDMI芯片型号及编译工具的人而言,每次都走一条不同的大路去往罗马不是一件享受的事情。Cortex-M3的年代变了,它只有一条路(也可以说有三条路,根据编译器的不同会有稍微差别),你往返的次数越多,越熟练,也会发现时间节省得越多。Cortex-M3的年代就是ARM公司提供CMSIS这一条大路。第1章中不断提到过CMSIS,本节将详细讲解CMSIS的构架。

3.2.1 CMSIS概述

Cortex微控制器软件接口标准(Cortex Microcontroller Software Interface Standard,CMSIS)是Cortex-M处理器系列具备的与供应商无关的硬件抽象层。使用CMSIS,可以为处理器和外设实现一致且简单的软件接口,从而简化软件的重用,缩短微控制器新开发人员的学习过程,并缩短新设备的上市时间。软件的创建被嵌入式行业公认为主要成本。通过在所有Cortex-M芯片供应商产品中标准化软件接口,这一成本会明显降低,尤其是在创建新项目或将现有软件迁移到新设备时。CMSIS提供了一致软件接口,改善了软件移植性和可重复性。通用的软件库可以存取不同的厂家芯片的设备库函数,减少了学习时间、开发成本、产品化时间。开发者使用标准化的软件接口可以快速完成软件的编写工作。CMSIS提供了编译器独立层,可用不同编译器进行编译。CMSIS支持主流编译器(ARMCC,IAR,GNU)。目前CMSIS已经发展到版本3。

CMSIS可以分为以下3个基本功能层。

(1)核内外设访问层(Core Peripheral Access Layer):包含了命名定义、地址定义、存取内核寄存器和外围设备的协助函数,同时定义了一个与设备无关的RTOS内核接口函数;一些对特殊用途寄存器的访问都被定义成内联函数或内嵌汇编的形式。

(2)中间件访问层(Middleware Access Layer):该层用于存储外设,由ARM公司开发,其他芯片厂家扩展。

(3)设备访问层(Device Peripheral Access):用来定义一些硬件寄存器的地址及对外设的访问函数。另外,芯片厂商会对异常向量进行扩展。设备访问层由芯片厂商实现。

通过使用CMSIS兼容软件组件,用户可以重复使用模板代码。

如图3-18所示是CMSIS的组织结构图。

图3-18 CMSIS的组织结构图

3.2.2 CMSIS编码规范

CMSIS制定了基本规范及推荐规范,供芯片厂家及普通用户使用。

1.基本规范

CMSIS的C代码遵照MISRA 2004规则,使用标准ANSI C头文件<stdint.h>中定义的标准数据类型。

(1)由#define定义的包含表达式的常数必须用括号括起来。

(2)变量和参数必须有完整的数据类型。

(3)CPAL层的函数必须是可重入的。

(4)CPAL层的函数不能有阻塞代码,也就是说,等待、查询等循环必须在其他的软件层中。

(5)定义每个异常/中断的

① 每个异常处理函数的后缀是_Handler,每个中断处理器函数的后缀是_IRQHandler;

② 默认的异常中断处理器函数(弱定义)包含一个无限循环;

③ 用#define将中断号定义为后缀为_IRQn的名称。

2.推荐规范

(1)定义核寄存器、外设寄存器和CPU指令名称时使用大写。

(2)定义外设访问函数、中断函数名称时首字母大写。

(3)对于与某个外设对应的函数,一般用该外设名称作为其前缀。

(4)按照Doxygen规范撰写函数的注释,注释使用C90风格(/*注释*/)或C++风格(//注释)。函数的注释应包含以下内容:

① 一行函数简介;

② 参数的详细解释;

③ 返回值的详细解释;

④ 函数功能的详细描述。

3.2.3 CMSIS文件结构

本节结合LPC17XX分析一下CMSIS的文件结构,并说明各个文件的主要功能。文件结构如图3-19所示。

图3-19 CMSIS的文件结构

CMSIS各个文件的对应关系及功能说明如表3-1所示。

表3-1 CMSIS各个文件的对应关系及功能说明

3.2.4 核内外设访问层

硬件层的数据类型定义使用了标准ANSI C头文件stdint.h定义的数据类型。CMSIS使用了3种标记符指定对寄存器的访问权限:_I(volatile const)、_O(volatile)、_IO(volatile)。其中_I表示只读权限,_O表示只写权限,_IO表示读写权限。

1.Cortex-M内核寄存器的存取

在core_cm0.h / core_cm3.h中定义了指令的内在函数,用于操作Cortex-M内核寄存器,如表3-2所示。

表3-2 内核寄存器的函数

2.Cortex-M指令的调用

在Coretex的时代,开发人员几乎可以不使用汇编语言进行开发了。但汇编语言仍然存在,而且在某些场合需要使用。ARM为此提供了一些C语言的封装函数,用于解决指令系统的调用问题。在core_m0.h/core_m3.h中对指令系统进行了定义。在core_cm0.c/core_cm3.c中实现了这些函数。如表3-3所示是CMSIS指令的操作函数。

表3-3 CMSIS指令的操作函数

3.嵌套中断向量访问函数

第1章中介绍了中断。中断的设置涉及组的配置,以及组优先级、子优先级的设置,属于比较复杂的配置。CMSIS提供了相应的函数,可以简化配置过程。

表3-4中给出了如何设置中断优先级,以及与中断使能相关的操作。

表3-4 嵌套中断函数

说明:IRQn_Type是一个枚举定义的数据类型。Cortex-M3中的处理器异常的起始值为负值。外部设备的中断起始值从0开始。IRQn_Type在device.h文件中。嵌套向量中断中的__NVIC_PRIO_BITS影响NVIC_EncodePriority和NVIC_DecodePriority的使用。

4.NXP实现的device.h

device.h由芯片厂家提供,NXP公司提供的device.h文件名是LPC17XX.h。该文件实现了中断号的枚举定义,用于提供所有异常和中断的配置;提供了core_cm3.h文件中针对实际芯片的配置信息;提供了对LPC17XX数据结构和地址映射的访问。

device.h可存取设备中断,并通过使用枚举方式定义了所有设备的中断号。LPC17XX.h中的定义方式如下:

        typedef enum IRQn
        {
        /******  Cortex-M3 异常号 ***************************************************/
          NonMaskableInt_IRQn      =-14,    /*!<2 NMI                    */
          MemoryManagement_IRQn   =-12,    /*!<4 Cortex-M3 存储管理中断     */
          BusFault_IRQn           =-11,    /*!<5 Cortex-M3 总线故障中断     */
          UsageFault_IRQn          =-10,    /*!<6 Cortex-M3 用法故障中断     */
          SVCall_IRQn            =-5,    /*!<11 Cortex-M3 SV调用中断     */
          DebugMonitor_IRQn       =-4,    /*!<12 Cortex-M3 调试监视中断    */
          PendSV_IRQn            =-2,    /*!<14 Cortex-M3 Pend SV中断     */
          SysTick_IRQn            =-1,    /*!<15 Cortex-M3 系统定时器中断   */
    @@@
        /******  LPC17XX指定中断号 *******************************************************/
          WDT_IRQn              =0,     /*!< 看门狗定时器中断           */
          TIMER0_IRQn           =1,     /*!<定时器0 中断               */
          TIMER1_IRQn           =2,     /*!< 定时器1 中断              */
          TIMER2_IRQn           =3,     /*!< 定时器2 中断              */
          TIMER3_IRQn           =4,     /*!< 定时器3中断               */
          UART0_IRQn            =5,     /*!<UART0 中断                */
          UART1_IRQn            =6,     /*!<UART1 中断                */
          UART2_IRQn            =7,     /*!<UART2 中断               */
          UART3_IRQn            =8,     /*!<UART3 中断               */
        PWM1_IRQn         =9,     /*!<PWM1 中断               */
        I2C0_IRQn          =10,    /*!<I2C0 中断                 */
        I2C1_IRQn          =11,    /*!<I2C1 中断                 */
        I2C2_IRQn          =12,    /*!<I2C2 中断                 */
        SPI_IRQn           =13,    /*!<SPI中断                  */
        SSP0_IRQn          =14,    /*!<SSP0 中断                 */
        SSP1_IRQn          =15,    SSP1 中断                    */
        PLL0_IRQn          =16,    /*!<PLL0 锁存 (Main PLL) 中断   */
        RTC_IRQn          =17,    /*!< 实时时钟中断              */
        EINT0_IRQn         =18,    /*!< 外部中断0                */
        EINT1_IRQn         =19,    /*!< 外部中断1                */
        EINT2_IRQn         =20,    /*!< 外部中断2                */
        EINT3_IRQn         =21,    /*!< 外部中断3                */
        ADC_IRQn          =22,    /*!<A/D转换中断              */
        BOD_IRQn          =23,     /*!<Brown-Out Detect Interrupt      */
        USB_IRQn          =24,    /*!<USB中断                 */
        CAN_IRQn          =25,    /*!<CAN中断                 */
        DMA_IRQn         =26,    /*!<GPDMA中断              */
        I2S_IRQn           =27,    /*!<I2S中断                  */
        ENET_IRQn         =28,    /*!< 网络 中断                */
        RIT_IRQn           =29,    /*!< 可重复定时器中断          */
        MCPWM_IRQn       =30,    /*!< 电机控制PWM中断         */
        QEI_IRQn           =31,    /*!<QEI中断                  */
        PLL1_IRQn          =32,    /*!<PLL1 锁存中断             */
        USBActivity_IRQn     =33,    /*!<USB活跃中断              */
        CANActivity_IRQn    =34     /*!<CAN活跃中断             */
      } IRQn_Type;

由于Cortex-M3对所有外设采用了内存映射方式,所以LPC17XX.h提供了芯片外设的内存映射访问方式。固件库采用了结构体定义寄存器,然后通过地址强制类型转换成结构体。例如,LPC17XX系列处理器的I2C寄存器组的数据结构定义如下:

        /*-------------Inter-IntegratedCircuit(I2C)---------------*/
        typedefstruct
        {
        __IOuint32_tI2CONSET;
        __Iuint32_tI2STAT;
        __IOuint32_tI2DAT;
        __IOuint32_tI2ADR0;
        __IOuint32_tI2SCLH;
        __IOuint32_tI2SCLL;
        __Ouint32_tI2CONCLR;
        __IOuint32_tMMCTRL;
        __IOuint32_tI2ADR1;
        __IOuint32_tI2ADR2;
        __IOuint32_tI2ADR3;
        __Iuint32_tI2DATA_BUFFER;
        __IOuint32_tI2MASK0;
        __IOuint32_tI2MASK1;
        __IOuint32_tI2MASK2;
        __IOuint32_tI2MASK3;
        }LPC_I2C_TypeDef;

LPC17XX处理器I2C接口的基地址定义如下:

        #defineLPC_I2C0_BASE(LPC_APB0_BASE+0x1C000)

访问LPC17XX处理器I2C接口的方法如下:

        #defineLPC_I2C0((LPC_I2C_TypeDef*)LPC_I2C0_BASE)

5.NXP提供的启动文件startup_LPC17xx.s

在Cortex-M3中断的使用方面,也是从ARM7转到Cortex-M编程的程序员感到困惑的地方,即在Cortex-M3中,虽然原来的interrupt修饰符消失了,但需要使用与startup_LPC17XX.s中定义一模一样的函数名字去定义中断。

在startup_LPC17xx.s文件中包含了所有外设的中断向量。每个中断句柄都定义成了weak属性,即成为哑函数,这样中断句柄可以直接使用应用程序里的函数,而不用修改startup_device(即startup_LPC17xx.s)的内容。

使用weak属性后,将通知链接器,如果可以使用其他源中的不同符号实例,则不同实例将优先于此实例。weak属性可与任何符号的可见性属性一起使用。

对于Cortex-M3系列,下列代码的编写都是相同的,定义中断向量地址的同时定义了中断服务程序的名字,并都定义在中断向量表的起始位置。

        __Vectors      DCD    __initial_sp              ; 堆栈起始地址
                      DCD    Reset_Handler            ; 复位中断
                      DCD    NMI_Handler            ;NMI句柄
                      DCD    HardFault_Handler         ; 硬故障
                      DCD    MemManage_Handler       ;MPU故障
                      DCD    BusFault_Handler          ; 总线故障
                      DCD    UsageFault_Handler        ; 用法故障
                      DCD    0                     ; 保留
                      DCD    0                     ; 保留
                      DCD    0                     ; 保留
                      DCD    0                     ; 保留
                      DCD    SVC_Handler             ;SVCall
                      DCD    DebugMon_Handler        ; 调试监视
                      DCD    0                      ;Reserved
                      DCD    PendSV_Handler          ;PendSV
                      DCD    SysTick_Handler          ;SysTick
                      ; 外部中断,厂商指定中断
                      DCD    WDT_IRQHandler         ;16: 定时器中断
                      DCD    TIMER0_IRQHandler       ;17: 定时器0
       ︙
        Default_Handler PROC
                      EXPORT  WDT_IRQHandler          [WEAK];;WEAK声明其他的同名
    标号优先于该标号被引用,也就是说,如果外面声明了,则会调用外面的
                      EXPORT  TIMER0_IRQHandler        [WEAK]
                      EXPORT  TIMER1_IRQHandler        [WEAK]
        ︙
                WDT_IRQHandler
                TIMER0_IRQHandler
                TIMER1_IRQHandler
                        ︙
                      B.
                      ENDP

用户程序可以简单定义一个中断服务程序的函数名字,即替代了原有ARM7TDMI必须采用__irq表示中断函数的步骤。简单定义函数的实例如下所示:

        void WDT_IRQHandler (void)
        {
        ︙
        }

6.NXP实现的其他文件

ARM公司提供了system_device.c文件模板,NXP公司将其调整成system_LPC17xx.c。该文件实现了:

        void SystemInit (void);//设置微控制器系统;主要配置时钟;更新SystemCoreClock;SystemInit
    //由启动文件调用
        void SystemCoreClockUpdate (void);//更新SystemCoreClock变量值。只要内核时钟改变,该函
    //数必须被调用
        uint32_t SystemCoreClock;//包含系统内核时钟,该变量可以用来设置SysTick时钟定时器,然
    //后配置其他参数;也可用于调试器查询调试时间或配置跟踪时钟速度。在应用程序不使用该变量的情况
    //下,编译器必须避免删除该变量。该变量对调试器系统是非常重要的

Cortex-M3提供仪器跟踪宏单元。该单元主要提供了ITM_SendChar的实现。但在LPC17XX中没有提供该功能。NXP公司有属于自己的debug_frmwrk.c文件,用于通过UART进行调试。

3.3 LPC1700 CMSIS标准固件库

如果将ARM7TDMI年代对各种外设驱动的编写理解成徒步,则针对LPC1700的芯片,LPC1700的固件库可以看成在唯一(CMSIS基础)的一条通往罗马(Cortex-M3)的大路上的坐骑。虽然现在还有不少人喜欢徒步通往罗马,但不少厂家出于商业性、兼容自己老产品的风格习惯,以及学习新产品的需要时间等原因仍然继续使用原有的自己配置、管理各种寄存器的方式。通过本节的介绍,你会发现坐骑年代绝对比徒步时代更方便、更快捷、成本还少。

注意:CMSIS指的是ARM公司提供的,即不管你使用哪个厂家的Cortex-M3芯片,CMSIS与芯片厂商没有任何关系,仅仅与ARM公司有关。

本书谈到的固件库是指NXP公司提供的,而且是针对LPC175X/6X的固件库。固件库因芯片的不同而不同。

3.3.1 固件库的组织结构

LPC1700 CMSIS标准固件库是LPC17XX的标准外围器件驱动函数库,包括宏定义、数据类型定义、结构体定义和功能函数。用户不需要深刻理解LPC1700的外围器件就可以编写出外围功能应用程序。

固件库遵循CMSIS标准。其使用的芯片主要是LPC176X和LPC175X。NXP公司针对LPC177X与LPC178X提供了另外一套固件库,但与本书使用的固件库基本一致。

该固件库支持的编译器是:

(1)CodeSourcery G++ Lite toolchain version 4.3.3;

(2)KEIL的RVMDK 4.0以上;

(3)IAR 5.4。

该固件库主要分为内核(Core)、驱动(Drivers)、例程、MAKE部分,Lib文件构建,其中Core与Drivers是开发的基础。内核部分即为CMSIS的内容,其中关于Cortex-M3的内容已经在3.2节做过介绍,NXP公司实现了需要厂家移植部分的内容。这部分内容被称为CMSIS的设备支持部分。

CMSIS的设备支持部分提供了LPC17XX设备支持文件,允许用户调用设备外围。设备支持部分包含支持ARM(KEIL MDK)/GCC/IAR编译器的启动文件、头文件、系统文件。

(1)启动文件根据编译器类型分别放在如下目录下。

① ARM编译器(KEIL MDK):“\Core\CM3\DeviceSupport\NXP\LPC17xx\startup\arm\startup_LPC17xx.s”。

② GCC编译器:“\Core\CM3\DeviceSupport\NXP\LPC17xx\startup\gcc\startup_LPC17xx.s”。

③ IAR编译器:“\Core\CM3\DeviceSupport\NXP\LPC17xx\startup\iar\startup_LPC17xx.s”。

(2)头文件(LPC17xx.h):LPC17XX外设设备的存取层定义了所有LPC17XX的外设,如如何访问ADC寄存器。

(3)系统文件包含了system_LPC17xx.h:与system_LPC17xx.c,定义了设备的特定配置信息,用于配置LPC17XX的时钟。

3.3.2 固件库的驱动部分

给LPC17XX外围设备提供驱动的部分位于“..\Driver”目录下。它被分成两部分:

(1)头文件部分,提供了宏定义、结构体、枚举类型的定义;

(2)源代码部分,提供了设备驱动的函数部分。

如表3-5所示是固件库的组织结构。

表3-5 固件库的组织结构

LPC1700驱动库包含利用UART端口连接的调试工具,它包含在两个文件中:debug_frmwrk.h与debug_frmwrk.c。

固件库中的默认端口是UART0,采用115200波特率、8位数据、无奇偶校验、一个停止位。如表3-6所示是固件库中的调试函数及其功能描述。

表3-6 固件库中的调试函数及其功能描述

3.3.3 驱动标识定义

LPC17XX固件库中的“lpc17xx_libcfg.h”文件定义了每个驱动源文件的使用问题。该文件通过宏定义的方式决定具体的某一个功能模块是否可被使用。如表3-7所示为lpc17xx_libcfg.h中的宏定义解释。

表3-7 lpc17xx_libcfg.h中的宏定义解释

3.4 小结

本章是进行LPC17XX软件开发的基础工作。本章介绍了开发环境,以及如何使用开发环境建立第一个软件程序。本章还对CMSIS与LPC17XX固件库进行了详细解释,这两部分是进行LPC17XX开发的基础。后续章节的软件开发将建立在本章介绍的内容基础上。尤其是CMSIS部分,它是理解软件设计的基础。