《(4.1)--《嵌入式系统原理及设计》第4章.pdf》由会员分享,可在线阅读,更多相关《(4.1)--《嵌入式系统原理及设计》第4章.pdf(21页珍藏版)》请在taowenge.com淘文阁网|工程机械CAD图纸|机械工程制图|CAD装配图下载|SolidWorks_CaTia_CAD_UG_PROE_设计图分享下载上搜索。
1、62 嵌入式系统原理及设计 第 4 章 STM32 库函数及程序开发 讲解STM32库函数以及开发环境,主要包括:STM32库函数、STM32芯片开发环境介绍以及程序开发下载方面的知识。4.1 STM32 固件库 在以前传统的单片机例如 51 微控制器,程序开发通过直接配置微控制器,通过查询寄存器表,看要用到哪些配置位,这些都是很琐碎和机械的工作,相对于简单微控制器例如 51等寄存器数量少又很简单,可以通过直接配置寄存器的方式来开发。但 STM32 单片机由于功能强到灵活,例如有关 GPIO 的配置寄存器就有十几个,如果去查询寄存器手册开发就很不方便。因此 ST 公司针对 STM32 提供函数
2、接口,即 API(Application Program Interface),开发者可通过调用这些函数接口来配置 STM32 的寄存器,使得开发人员可以脱离最底层的寄存器操作,有开发快速、易于阅读和维护成本低等优点。图 4.1 库函数和寄存器方式对比 4.1.1 STM32 库结构 ST 公司 2007 年 10 月发布了 V1.0 版本的固件库,MDK ARM3.22 之前的版本均支持该库。2008 年 6 月发布了 V2.0 版的固件库,从 2008 年 9 月推出的 MDK ARM3.23 版本至今均使用 V2.0 版本的固件库。V3.0 以后的版本相对之前的版本改动较大,本书使用目前
3、较新的V3.5 版本,STM32 的 3.5 版本库函数可以直接从官方网站下载得到。解压缩库文件进入其目录:STM32F10 x_StdPeriph_Lib_V3.5.0 63 第 4 章 STM32 库函数及程序开发 各文件夹内容:图 4.2 STM32 固件库目录(1)Libraries 文件夹下是驱动库的源代码和启动文件,内部又分 2 个文件夹,如下图:图 4.3 Libraries 文件夹 由于 STM32 采用的 Cortex-M3 内核,Cortex-M3 内核是 ARM 公司提出的,区别主要是核外的片上外设的差异,这些差异会导致软件在同内核、不同外设的芯片上移植困难。为了解决不同
4、芯片厂商生产的 Cortex 微控制器软件的兼容性问题,ARM 与芯片厂商建立了CMSIS 标准。CMSIS 标准实际就是新建了一个软件抽象层,如图 4.4。CMSIS 层位于硬件层与操作系统或用户层之间,提供了与芯片生产商无关的硬件抽象层,可以为接口外设、实时操作系统提供简单的处理器软件接口,屏蔽了硬件差异,这对软件移植有极大的好处,STM32 固件库就是按照 CMSIS 标准建立的。CMSIS 标准中最主要的是 CMSIS 核心层,包括:内核函数层:其中包含用于访问内核寄存器的名称、地址定义,主要由 ARM 公司提供。设备外设访问层:提供了片上的核外外设的地址和中断定义,主要由芯片生产商提
5、供。64 嵌入式系统原理及设计 用户代码用户代码CortexCortex内核内核SysTickSysTick实实时系统内时系统内核时钟核时钟NVICNVIC中断中断控制器控制器调试跟踪调试跟踪模块模块其它外设其它外设CMSISCMSIS-DSPDSPDSPDSP固件库固件库CMSISCMSIS实时实时实时系统内核实时系统内核设备外设函数设备外设函数(如如STMSTM3232驱动函数库驱动函数库)SIMDSIMDCortexCortex-M M4 4外设寄存器定义外设寄存器定义&中断向量定义中断向量定义核内外设函数核内外设函数用用户户层层CMSISCMSIS层层MCUMCU 图 4.4 CMSI
6、S 架构 2)STM32F10 x_StdPeriph_Driver 文件夹 LibrariesSTM32F10 x_StdPeriph_Driver 文件夹下有 inc(include 的缩写)和 src(source 的缩写)这两个文件夹,这都属于 CMSIS 的设备外设函数部分,由芯片制造商在 Cortex-M3 核外加进去的。在 src 和 inc 文件夹里的就是 ST 公司针对每个 STM32 外设编写的库函数文件,每个外设对应一个.c和.h后缀的文件,本书把这类文件称为:stm32fl0 x_xxx.c和stm32f10 x_xxx.h文件,xxx 表示外设名称。65 第 4 章
7、STM32 库函数及程序开发 图 4.5 驱动的源文件及头文件 这两个文件夹中有一个特别的 misc.c 文件,这个文件提供外设对内核中断向量控制器(NVIC)的访问函数,在配置中断时,需要把这个文件添加到项目中。3)stm32f10 x_it.c 和 stm32f10 x_conf.h 文件 在库目录的 ProjectSTM32F10 x_StdPeriph_Template 目录下,存放了官方的工程模板,在用 库 建 立完 整 项 目时,需 要添 加 这 个目 录 下的 stm32f10 x_it.c、stm32f10 x_it.h 和stm32f10 x_conf.h 这三个文件。stm
8、32f10 x_it.c 是专门编写中断服务程序的,这个文件默认已经定义了一些系统异常的服务程序,其他普通中断服务程序由用户自己添加,这些普通中断服务程序已经默认了函数名,不可以自定义,具体名称可以在 startup_stm32f10 x_md.s 汇编程序中找到,如在汇编的以下这行,说明中断 0 的服务程序名称是 EXTI0_IRQHandler。DCD EXTI0_IRQHandler ;EXTI Line 0 stm32f10 x_conf.h 文件被包含进 stm32f10 x.h 文件,是用来配置使用了什么外设的头文件,这个头文件可以方便地增加或删除上面 Driver 目录下的驱动函
9、数。例如代码配置表示只用到了 gpio、rcc 的函数库,其他不用的可注释掉,如图 4.6。66 嵌入式系统原理及设计 图 4.6 stm32f10 x_conf.h 内容配置 stm32f10 x_conf.h 文件可配置是否使用“断言”编译选项,从而可以在开发时使用“断言”可由编译器检查库函数传入的参数是否正确,软件编写成功后,去掉“断言”编译选项可使程序全速运行。可通过定义 USE_FULL_ASSERT 或取消定义来配置是否使用“断言”。4.1.2 STM32 固件库文件间关系 固件库文件可以直接包含进入工程,不用进行修改,而有的文件就需要在使用过程中进行修改,因此需要整体上把握一下各
10、个文件在库工程中的层次和关系,这些文件都对应在CMSIS 标准架构上。下图描述了 STM32 固件库各文件之间的调用关系,对于用户层的文件来说,就是用户改动开发的程序。在学习库函数时,官方资料是所有关于STM32知识的源头,目前官方主要提供对开发有帮助的资料有如下内容:stm32f10 x_stdperiph_lib_um.chm 库函数帮助文件。在使用库函数时,可以通过查阅此文件来了解库函数原型或库函数调用的方法。67 第 4 章 STM32 库函数及程序开发 图 4.7 库各文件之间的关系 68 嵌入式系统原理及设计 4.2 搭建开发环境及程序开发 4.2.1 搭建开发环境 STM32 芯
11、片软件开发需要用于编译程序代码的开发环境,目前针对 ARM Cortex-M3 的开发环境平台有 ARM 公司的 MDK 开发环境,Embedded Workbench 公司的 IAR 开发环境,以及自己采用 ARM gcc 编译器,并选择合适的编辑器搭建开发一个开发平台。从初学者入手,本书采用 ARM 公司的 MDK 开发环境,目前最新的版本是 MDK5.2,但目前学习资料很多还是以 MDK4 系列版本来开发了,本书采用 MDK4.7 做为学习用的开发环境,其中包括了相应的编译器、汇编器、链接器和基本库函数等,开发环境具体信息如图4.8。图 4.8 MDK4.7 开发环境 4.2.2 程序开
12、发 安装完 MDK 之后,可以建立一个新的项目文件,具体过程如下:(1)新建一个项目文件,创建名称为 STM32Helloworld 图 4.9 创建项目(2)选择所用的目标芯片,本书中以 STM32F103RB 系列芯片为主,所以选择如图 4.10所示:69 第 4 章 STM32 库函数及程序开发 图 4.10 选择合适芯片 在选择后,会弹出对话框,问是否加入 startup_stm32f10 x_md.s 文件,由于采用库文件模式,我们可以后面导入库文件在 startup 目录下的 startup_stm32f10 x_md.s 文件,因此可以选择否。此时建立出的项目没有文件内容如图 4
13、.11,所以需要根据项目需求把相应库文件拷贝到项目中,最后需要编写出合适的 main.c 程序和其他应用程序,并添加到项目中。图 4.11 项目创建成功 本项目作为第一个程序,根据如下电路图 4.12,实现一个控制 LED 灯亮的程序。图 4.12 LED 灯电路 根据电路图,此 LED 连接到 STM32 微控制器 PA1 引脚,所以只要设置 PA1 引脚为低电平,即可以实现灯亮。在此程序中要用到 GPIO 控制,所以要导入 GPIO 库函数;同时由于芯片启动时,需要调用时钟管理程序,所以也需要导入 RCC 库函数。同时根据库文件结构,程序运行正常,还需要导入 startup_stm32f1
14、0 x_md.s,system_stm32f10 x.c 以及相应的头文件和 stm32f10 x.h 文件。注意,由于不直接对内核进行操作,所以 core_cm3.c 暂不用导入。首先从库文件拷贝所需的文件到项目文件目录下,本项目根据需求分别把这些文件拷贝到 core,lib 和 user 文件夹下,如图 4.13。70 嵌入式系统原理及设计 图 4.13 项目文件夹 core 目录下有 startup_stm32f10 x_md.s 文件,lib 目录下主要是有关 GPIO 和 RCC 模块的stm32f10 x_gpio.c,stm32f10 x_gpio.h,stm32f10 x_rc
15、c.c 和 stm32f10 x_rcc.h 文件。user 目录下stm32f10 x.h,stm32f10 x_conf.h,system_stm32f10 x.c 和 main.c 程序,其中 main.c 程序本项目实现的主程序。同时把这些文件导入到项目中,可以通过实现。图 4.14 相应的文件导入到项目中 在项目配置中引入头文件,通过进入 option for Target 界面,并在 C/C+栏导入在本项目要用到的头文件路径,包括:.libinc 和.user;同时需要对 Preprocessor Symbols 进行设置,由于采用中等密度芯片 STM32F103RB,并采用库函数
16、调用的方式,所以设置为:STM32F10X_MD,USE_STDPERIPH_DRIVER,如图 4.15:图 4.15 导入头文件 本项目根据需求,main.c 程序代码如下:#include stm32f10 x.h#include stm32f10 x_rcc.h#include stm32f10 x_gpio.h/*71 第 4 章 STM32 库函数及程序开发 brief 初始化 LED 灯,实现让 LED 亮 paramsNone retval0 程序正常结束*/int main(void)GPIO_InitTypeDef gpio_init;/使能 PA1 时钟 RCC_APB2
17、PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);/将 PA1 设置为推挽输出 gpio_init.GPIO_Mode=GPIO_Mode_Out_PP;gpio_init.GPIO_Pin=GPIO_Pin_1;gpio_init.GPIO_Speed=GPIO_Speed_50MHz;GPIO_Init(GPIOA,&gpio_init);while(1)/设置 PA1 为低电平,根据电路图 LED 灯亮 GPIO_ResetBits(GPIOA,GPIO_Pin_1);return 0;编译成功后,便可以下载程序到开发板。4.2.3 时钟设定程序说明
18、 时钟是否能启动正常影响着 STM32 微控制器是否能够正常工作,同时由于 STM32 微处理器的时钟源有 HSI(高速内部时钟)、HSE(高速外部时钟)、LSI(低速内部时钟)、LSE(低速外部时钟)、PLL(锁相环倍频输出)这 5 种类型。所以在程序刚开始运行时,需要调用时钟管理函数。(1)STM32 微控制器时钟管理 STM32 具有功能丰富的时钟系统,如图 4.16。72 嵌入式系统原理及设计 图 4.16 STM32F10 xx 时钟系统框图 用户可通过多个预分频器配置 AHB 总线、高速 APB2 总线和低速 APB1 总线的频率。AHB 和 APB2 域的最大频率是 72MHZ。
19、APB1 域的最大允许频率是 36MHZ。SDIO 接口的时钟频率固定为 HCLK/2。40kHz 的 LSI 供独立看门狗 IWDG 使用,另外它还可以被选择为实时时钟 RTC 的时钟源。另外,实时时钟 RTC 的时钟源还可以选择 LSE,或者是 HSE 的 128 分频。RTC 的时钟源通过寄存器 RTCSEL1:0来选择。STM32 中有一个全速功能的 USB 模块,其串行接口引擎需要一个频率为 48MHz 的时钟源。该时钟源只能从 PLL 输出端获取,可以选择为 1.5 分频或者 1 分频,也就是,当需要使用 USB 模块时,PLL 必须使能,并且时钟频率配置为 48MHz 或 72M
20、Hz。另外,STM32 还可以选择一个 PLL 输出的 2 分频、HSI、HSE、或者系统时钟 SYSCLK输出到 MCO 脚(PA8)上作为时钟输出。系统时钟 SYSCLK,是供 STM32 中绝大部分部件工作的时钟源,它可选择为 PLL 输出、HSI 或者 HSE,(一般程序中采用 PLL 倍频到 72Mhz)在选择时钟源前注意要判断目标时钟源是否已经稳定振荡。最大频率 72MHz,它分为 2 路,1 路送给 I2S2、I2S3 使用的 I2S2CLK,I2S3CLK;另外 1 路通过 AHB 分频器分频(1/2/4/8/16/64/128/256/512)分频后送给以下 8 大模块使用:
21、送给 SDIO 使用的 SDIOCLK 时钟。送给 FSMC 使用的 FSMCCLK 时钟。送给 AHB 总线、内核、内存和 DMA 使用的 HCLK 时钟。通过 8 分频后送给 Cortex 的系统嘀嗒定时器时钟(SysTick)。直接送给 Cortex 的空闲运行时钟 FCLK。送给 APB1 分频器。APB1 分频器可选择 1、2、4、8、16 分频,其输出一路供 APB1 外设使用(PCLK1,最大频率 36MHz),另一路送给定时器(Timer2-7)2、3、4 倍频器使用。该倍73 第 4 章 STM32 库函数及程序开发 频器可选择 1 或者 2 倍频,时钟输出供定时器 2、3、
22、4、5、6、7 使用。送给 APB2 分频器。APB2 分频器可选择 1、2、4、8、16 分频,其输出一路供 APB2 外设使用(PCLK2,最大频率 72MHz),另一路送给定时器(Timer1、Timer8)1、2 倍频器使用。该倍频器可选择 1 或者 2 倍频,时钟输出供定时器 1 和定时器 8 使用。另外,APB2 分频器还有一路输出供 ADC 分频器使用,分频后得到 ADCCLK 时钟送给 ADC 模块使用。ADC 分频器可选择为 2、4、6、8 分频。2 分频后送给 SDIO AHB 接口使用(HCLK/2)。在以上的时钟输出中有很多是带使能控制的,如 AHB 总线时钟、内核时钟
23、、各种 APB1外设、APB2 外设等。当需要使用某模块时,必需先使能对应的时钟。需要注意的是定时器的倍频器,当 APB 的分频为 1 时,它的倍频值为 1,否则它的倍频值就为 2。连接在 APB1(低速外设)上的设备有:电源接口、备份接口、CAN、USB、I2C1、I2C2、UART2、UART3、SPI2、窗口看门狗、Timer2、Timer3、Timer4。注意 USB 模块虽然需要一个单独的 48MHz时钟信号,但它应该不是供 USB 模块工作的时钟,而只是提供给串行接口引擎(SIE)使用的时钟。USB 模块工作的时钟应该是由 APB1 提供的。连接在 APB2(高速外设)上的设备有:
24、GPIO_A-E、USART1、ADC1、ADC2、ADC3、TIM1、TIM8、SPI1、AFIO。(2)时钟函数说明 在库函数 3.5 版本中,时钟的初始化是通过 system_stm32f10 x.c 文件 SystemInit()函数进行实现的,配置微控芯片系统。初始化内嵌闪存接口、PLL 及更新系统时钟频率。由于本书芯片采用 STM32F103RB,不是互联型芯片(STM32F105(7)系列),因此在程序中 STM32F10X_CL 模块可忽略,程序主要内容如下:选择 HSI 作为 SYSCLK 来源。RCC-CR|=(uint32_t)0 x00000001;复位 CFGR 的
25、bit26:24,15:0(MCO,时钟输出;ADCPRE,ADC 预分频;PPRE2,高速 APB 预分频(APB2);PPRE1,低速 APB 预分频(APB1);HPRE,AHB 预分频;SWS,系统时钟切换状态;SW 系统时钟切换)位,对应的含义分别为“微控器无时钟输出”、“PCLK2 2 分频后作为 ADC 时钟”、“HCLK 不分频(PCLK2 的预分频系数)”、“HCLK 不分频(PCLK1的预分频系数)”、“SYSCLK 不分频(AHB 预分频系数为 1)”、“目前是 HSI 作为系统时钟的状态”、“用 HSI 作为系统时钟”。RCC-CFGR&=(uint32_t)0 xF8
26、FF0000;复位 CFGR 的 bit24,19,17,16(PLLON,PLL 使能;CSSON,时钟系统安全使能;HSERDY,外部高速时钟就绪标志;HSEON,外部高速时钟使能),即 PLL 关闭,时钟监测器关闭,HSE 振荡器无旁路,HSE 振荡器关闭。RCC-CR&=(uint32_t)0 xFEF6FFFF;RCC-CR&=(uint32_t)0 xFFFBFFFF;复位 CFGR 的 bit22:16位,即 PLL 时钟 1.5 倍分频作为 USB 时钟,PLL2 倍频输出,HSE 不分频,HSI 振荡器时钟经 2 分频后作为 PLL 输入时钟。RCC-CFGR&=(uint3
27、2_t)0 xFF80FFFF;配置 CIR 的 bit23,20,19:16位为 1,清除 CSSF 安全系统、PLL 就绪、HSE 就绪、HSI就绪、LSE 就绪、LSI 就绪中断标志位。74 嵌入式系统原理及设计 RCC-CIR=0 x009F0000;接下来调用 SetSysClock()函数,这个函数根据用户定义的宏来配置不同的时钟系统及闪存的预取指令模块。static void SetSysClock(void)#ifdef SYSCLK_FREQ_HSE SetSysClockToHSE();#elif defined SYSCLK_FREQ_24MHz SetSysClockT
28、o24();#elif defined SYSCLK_FREQ_36MHz SetSysClockTo36();#elif defined SYSCLK_FREQ_48MHz SetSysClockTo48();#elif defined SYSCLK_FREQ_56MHz SetSysClockTo56();#elif defined SYSCLK_FREQ_72MHz SetSysClockTo72();#endif /*If none of the define above is enabled,the HSI is used as System clock source(default
29、 after reset)*/如果用户定义了 SYSCLK_FREQ_HSE 宏则就调用”SetSysClockToHSE();”函数来确定SYSCLK的 时 钟 来 源 为HSE,如 果 用 户 定 义 了SYSCLK_FREQ_24MHz则 调用”SetSysClockTo24();”函数来确定 SYSCLK 的时钟频率为 24MHz如果用户定义了SYSCLK_FREQ_72MHz 宏则调用”SetSysClockTo72();”函数来确定 SYSCLK 的时钟频率为72MHz,如果这些宏都未定义,则在系统复位后如果这些宏都未定义,则在系统复位后 HSI 默认为默认为 SYSCLK 的时钟
30、源的时钟源。若开启 SetSysClockToHSE(void)RCC_CR_HSEON 的值为(uint32_t)0 x00010000),配置 CR 寄存器 bit16(HSEON,外部高速时钟使能)为 1,开启外部高速时钟。RCC-CR|=(uint32_t)RCC_CR_HSEON);在开 启任何一 个时钟都 需要等待 此时钟的 稳定在开 启任何一 个时钟都 需要等待 此时钟的 稳定。RCC_CR_HSERDY 的值 为(uint32_t)0 x00020000),CR 的 bit17(HSERDY,外部高速时钟就绪标志),此位由硬件置位,当此位为 1 时表明 HSE 时钟已经稳定就绪
31、。此段代码一直用循环等待直到 CR 的 bit17为 1或者 StartUpCounter 的值为 HSEStartUp_TimeOut(uint16_t)0 x0500,如果 StartUpCounter 计数到这个值表示 HSE 时钟就绪超时)。do HSEStatus=RCC-CR&RCC_CR_HSERDY;StartUpCounter+;75 第 4 章 STM32 库函数及程序开发 while(HSEStatus=0)&(StartUpCounter!=HSE_STARTUP_TIMEOUT);RESET 的值为 0。如果 CR 的 bit17为 1,则 HSEStatus 为 1
32、 表示 HSE 时钟就绪成功,否则 HSEStatus 为 0,表示 HSE 时钟就绪失败。if(RCC-CR&RCC_CR_HSERDY)!=RESET)HSEStatus=(uint32_t)0 x01;else HSEStatus=(uint32_t)0 x00;4.2.4 程序调试和下载 有关程序下载可以通过 Jlink、Ulink 以及 ST 公司提供的 Bootloader 可以通过串口方式实现程序的下载。(1)利用)利用 Jlink 下载仿真工具实现对目标代码的下载下载仿真工具实现对目标代码的下载 程序编写后,进行编译和链接,可通过 Jlink 调试工具,下载到目标板中,在调试下
33、载过程中也需要对环境进行配置。具体配置,可通过如下两个方法进入到项目配置界面:选中项目框 Target1,并单击右键,点击“Options for TargetTarge1”选项或直接点击快捷按钮,进入到:图 4.17 项目配置界面 在 Debug 中需要配置 Jlink,如图 4.18:76 嵌入式系统原理及设计 图 4.18 配置 Jlink 其中单击 Setting,并选择 Flash Download 配置如下:图 4.19 配置 Flash Download 按照图 4.19 选择合适勾选项,并通过 Add 按钮添加 STM32F10 x Med-density Flash 文件,结
34、果如图 4.19。最后,在 Utilities 中也选择 Jlink,如图 4.20,系统配置完成,可以通过 Jlink 在线调试程序以及下载目标代码。77 第 4 章 STM32 库函数及程序开发 图 4.20 选择 Jlink 下载 (2)Serial Wire Mode Interface(SWD)模式模式下载方式下载方式 程序编写后,进行编译和链接,可通过 SWD 调试工具,下载到目标板中,在调试下载过程中也需要对环境进行配置。具体配置,可通过如下两个方法进入到项目配置界面:选中项目框 Target1,并单击右键,点击“Options for TargetTarge1”选项或直接点击快
35、捷按钮,进入到:图 4.21 配置界面 在 Debug 中需要配置 ULINK2,如下图 4.22:78 嵌入式系统原理及设计 图 4.22 选择 ULINK 其中单击 Setting,并选择 Flash Download 配置如下:图 4.23 配置下载 Flash 模式 按照图 4.23 选择合适勾选项,并通过 Add 按钮添加 STM32F10 x Med-density Flash 文件,结果如图 4.23。最后,在 Utilities 中也选择 ULINK2,如图 4.24,系统配置完成,可以通过 SWD 在线调试程序以及下载目标代码。79 第 4 章 STM32 库函数及程序开发
36、图 4.24 配置 ULINK 下载 (3)ST 公司提供公司提供 bootloader 实现串口下载方式实现串口下载方式 STM32 内部自带 bootloader,可以串口下载程序,利用串口下载方式简洁,廉价,通过芯片上的串口就可以完成下载过程。在能正常工作的 STM32 电路板上将串口电路调通,保证可用,就是无障碍的和 PC 通讯,然后将写好编译好的 HEX 文件载入下载即可。在 ST 官方网站下载 Flash_Loader_Demonstrator_V2.1.0_Setup 串口下载的上位机软件,并安装。图 4.25 Flash 下载软件确认你的串口设置 OK,注意串口号对应即可。点击 Next,注意当你单击 Next 下一步的时候,可能会弹出一个对话框:图 4.26 下载可能出现情况 检查两个 Boot 引脚,应该是 Boot0=1;Boot1=0(接地),设置完成,对 MCU重新上电就可以。继续 Next.80 嵌入式系统原理及设计 图 4.27 连接成功 连接 OK,Next。图 4.28 选择下载选型 这一步将自动选择器件,然后 NEXT。81 第 4 章 STM32 库函数及程序开发 图 4.29 选择下载文件 在 Download from file 中选择你的 HEX 文件,Next。图 4.30 下载成功 下载程序完成。82 嵌入式系统原理及设计