ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

微机原理与接口技术 · 第5章《ARM 微控制器开发》知识点全梳理

微机原理与接口技术 · 第5章《ARM 微控制器开发》知识点全梳理 微机原理与接口技术 · 第5章《ARM 微控制器开发》知识点全梳理本文整理自福州大学《微机原理与接口技术》吴衔誉教授第五章课件系统讲解开发流程与工具链、处理器启动过程、输入输出接口、程序映像、C 语言开发以及固件库六大板块。目录一、开发流程与工具链二、处理器的启动过程三、输入和输出接口四、程序映像五、C 语言开发 ARM 应用六、固件库本章小结与思考题一、开发流程与工具链1.1 什么是工具链toolchain工具链toolchain工具链条不是某一个软件而是一串工具首尾相接的组合一个工具的输出喂给下一个工具当输入像流水线一样把源代码变成能烧进芯片的机器码。ARM 微控制器开发常用工具包括C 编译器把.c源文件翻译成汇编或目标代码汇编器把汇编源文件翻译成机器码链接器把编译器产生的各个模块拼起来再从 C 语言程序库里抽取需要的代码组成一个可执行文件指令模拟器在电脑上模拟跑代码不用真板子也能调试逻辑Flash 编程器把最终二进制写进芯片的 Flash调试器单步、设断点、看变量。开发中可以用 C 语言、汇编语言或者两者混合编程。1.2 集成开发环境IDE与调试器IDEIntegrated Development Environment集成开发环境把代码编辑器、编译器、调试器、图形界面打包在一起的应用程序。本课程采用Keil MDK-RTM作为集成开发环境。ICEIn-Circuit Emulator在线仿真器 / 在线调试器调试嵌入式软件的硬件设备。本课程采用ST-LINK V2调试下载器。大多数开发工具都自带友好的 IDE把它和在线调试器配合使用就能分步完成「创建工程 → 编译应用程序 → 下载到微控制器」这一整套流程。一句话概括开发过程在 Keil 里建工程、写代码、编译再用 ST-LINK V2 把程序下载进 STM32 的 Flash。二、处理器的启动过程2.1 程序存在哪里现代微控制器内部几乎都带有片上 Flash 存储器用来保存编译好的程序代码。这些代码以二进制机器码的形式存放所以无论是汇编程序还是 C 程序都得先经过汇编或编译才能烧写进 Flash。有些微控制器还额外配了一块独立的启动 ROMBootROM里面固化了一个小的BootLoader引导加载程序。芯片启动后、执行 Flash 里的用户程序之前BootLoader 会先运行。多数情况下 BootLoader 是固定的只有 Flash 里的用户程序才是可变的。2.2 复位以后发生了什么程序烧写进 Flash 后处理器复位就会运行「复位流程」处理器取出MSP主堆栈指针的初始值取出复位向量复位处理程序的入口地址开始执行复位处理。这些信息都放在一个叫启动代码的程序文件里。对于 C 语言程序在进入main()之前启动代码就已经悄悄把变量、内存等都初始化好了——作为程序员完全不用操心因为 C 语言开发工具会自动把启动代码插进程序映像里。而汇编程序则麻烦得多设堆栈、设入口点都得自己来参考第 4 章汇编程序框架。2.3 启动代码的核心 5 步系统上电后第一个执行的一般是由汇编语言写的启动文件startup_stm32f10x_hd.s它的核心工作有 5 项步骤动作说明1初始化堆栈指针SP _initial_sp即 MSP给程序一个能用的栈顶2初始化 PC 指针 Reset_Handler告诉芯片复位后跳去哪3初始化中断向量表把各种异常 / 中断的入口地址排好4配置系统时钟配置时钟控制电路、锁相环 PLL 等5调用 C 库函数_main初始化用户堆栈最终调用main函数进入 C 的世界这 5 步一般由 ST 官方在固件库自带的汇编启动文件startup_stm32f10x_hd.s中实现开发者一般无需手写。2.4 应用程序通常包含 3 部分启动代码跑完后才轮到你的应用程序它通常由 3 块组成硬件初始化时钟、PLL、外设等应用程序的处理部分你真正想让芯片做的事中断服务程序响应外部事件。如果用了 C 库函数编译器 / 链接器会把所需的库代码自动纳进程序映像。三、输入和输出接口嵌入式系统里可用的 I/O 五花八门数字和模拟输入 / 输出、UART、I2C、SPI很多微控制器还提供 USB、以太网、CAN、LCD、SD 卡等接口——这些接口都由**外设peripheral**控制。外设的典型初始化过程一般分 4 步配置时钟控制回路使能外设的时钟信号必要时初始化相应引脚配置 I/O引脚功能配置、电气特性配置配置外设控制寄存器配置配置中断配置 NVICNested Vectored Interrupt Controller嵌套向量中断控制器。四、程序映像ARM 的映像文件就是可执行文件常见bin或hex两种格式可以直接烧到 ROM 里执行。4.1 程序映像的组成Cortex-M3 的程序映像一般包含 4 部分组成内容向量表存放堆栈顶、复位向量、各异常 / 中断入口地址C 启动代码初始化全局变量、清零未初始化内存、跳main程序代码应用程序指令 变量初值 常量查找表、图像数据等C 库代码用到特定 C / C 库函数时链接时嵌入4.2 向量表与启动文件片段向量表用 C 或汇编实现其入口需要编译器和链接器生成的内容因此实现细节和工具链相关。Keil MDK 建 STM32 工程时向量表是汇编启动代码的一部分用DCD定义常量数据指令创建。来看startup_stm32f10x_hd.s里最关键的一段Stack_Size EQU 0x00000400 ; 栈大小 1 KB AREA STACK, NOINIT, READWRITE, ALIGN3 Stack_Mem SPACE Stack_Size ; 分配栈空间 __initial_sp ; 栈顶标号 Heap_Size EQU 0x00000200 ; 堆大小 512 字节 AREA HEAP, NOINIT, READWRITE, ALIGN3 __heap_base Heap_Mem SPACE Heap_Size ; 分配堆空间 __heap_limit PRESERVE8 ; 指示编译器 8 字节对齐 THUMB ; 后面的指令为 Thumb 指令 AREA RESET, DATA, READONLY ; 声明 RESET 只读数据段 EXPORT __Vectors ; 导出向量表标号 __Vectors DCD __initial_sp ; 栈顶 DCD Reset_Handler ; 复位处理程序入口 DCD NMI_Handler ; NMI 处理程序入口 DCD HardFault_Handler ; 硬件错误处理程序入口几个要点Stack_Size EQU 0x00000400栈 1 KBHeap_Size EQU 0x00000200堆 512 字节。AREA ... NOINIT只预留内存单元不写入初始值。PRESERVE8要求 8 字节对齐THUMB后续为 Thumb 指令。DCD从地址 0x00000000 开始依次放栈顶4 字节、复位向量0x00000004、NMI0x00000008……这就是向量表在 Flash 最前面的排布。4.3 C 启动代码与程序代码C 启动代码设置全局变量、清零加载时未初始化的内存区域、若用到malloc还要初始化堆控制变量最后跳main()。它由编译器 / 链接器自动嵌入纯汇编程序可能没有它。程序代码用户任务的指令外加变量初值、常量、查找表、图像数据等。C 库代码用到特定库函数时由链接器嵌入。五、C 语言开发 ARM 应用虽然汇编效率更高但 C / C 仍是 ARM 嵌入式开发的主流语言执行效率较高、语法自由、模块化好。本节重点讲 C 在 ARM 嵌入式里和写 PC 软件不太一样的地方。5.1 数据类型C 语言数据类型按构造特点分为 4 大类基本类型整型、字符型、实型单精度、双精度构造类型数组、结构体、共用体、枚举指针类型存变量在内存中的地址空类型无返回值的函数定义为空类型。整型INTEGERARM 嵌入式 C 里整型按长度分类型说明符长度基本型int2 字节短整型short int/short2 字节长整型long int/long4 字节无符号基本型unsigned int/unsigned—无符号短整型unsigned short—无符号长整型unsigned long int—整型常量可用十进制、八进制0 开头、十六进制0x 开头表示。实型浮点型实型常量两种写法十进制数形式必须有小数点如25.0和指数形式aEn如5.789ARM 嵌入式代码里实型常数统一按double双精度处理实型变量分单精度float一般占 4 字节、双精度double、长双精度long double。字符型与字符串字符常量用单引号a、只占 1 字节字符串常量用双引号CHINA、C program字符串常量占的字节数 字符数 1多出的 1 字节存\0ASCII 0作为字符串结束标志ARM 嵌入式程序里没有字符串变量要用字符数组来存字符串。类型转换自动转换不同数据类型混合运算时由编译系统自动完成强制转换(类型说明符)(表达式)把结果强制转成目标类型。经典易错示例#includestdio.hintmain(){floatx11.20;inty(int)x;// y 11intz(int)(100*(x-y));// 100×(11.1999998…−11)19.99998取整得 19printf(z%d \n,z);// 输出 19return0;}11.20在float下存为11.1999998…减去11得0.1999998…乘100得19.99998取整后被截断为19而不是 20。这是浮点舍入造成的常被拿来考。输入输出格式字符格式字符含义d十进制带符号整数o八进制无符号整数x/X十六进制无符号整数u十进制无符号整数f小数形式单 / 双精度实数e/E指数形式单 / 双精度实数g/G较短宽度输出c单个字符s字符串scanf的变量前记得加取地址符。5.2 数据运算ARM 嵌入式 C 支持 10 类运算符算术 - * / % --关系 !逻辑 || !位操作 | ~ ^ 赋值 - ...条件? :逗号,指针*取内容、取地址求字节sizeof特殊括号()、下标[]、成员-.算术运算符共 7 种加、减、乘、除、求余模、自加、自减。注意 ARM 的乘法和除法有专用乘法器 / 除法器减轻处理器排队压力但会增加芯片成本。坑点示例整数除与浮点除混用printf(用%%d显示20/7 %d\n,20/7);// 2整数除截断printf(用%%f显示20.0/7 %f\n,20.0/7);// 2.857143printf(对100求3的余数%d\n,100%3);// 1优先级与结合性运算符优先级共15 级1 级最高。优先级相同时按结合性处理左结合自左至右如x - y z先算x-y再加z右结合自右至左最典型是赋值号x y z等价于x (y z)。自增与自减i先加再用、i先用再加--同理。课件里的经典追踪inti8;printf(%d\n,i);// 9printf(%d\n,--i);// 8printf(%d\n,i);// 8打印后变9printf(%d\n,i--);// 9打印后变8printf(%d\n,-i);// -8printf(%d\n,-i--);// -9提示除循环累加等明确场景外含全局变量的代码里不建议滥用自增 / 自减。复合赋值与逗号表达式复合赋值a b等价于a a b还有- * / % ^ |逗号表达式(表达式1, 表达式2)依次求值整个逗号表达式的值是表达式 2 的值。注意函数参数表里的逗号只是分隔符不是逗号表达式。5.3 流程控制流程控制分两类判断语句if、if-else、if-else if、switch循环语句goto、while、for。switch注意case 后不加break会「穿透」——匹配到某个 case 后会一路执行后面所有 case直到遇break或结束。case/default顺序可任意调换不影响结果default可省略。循环小结不提倡用goto容易让程序混乱while循环体里要有让循环趋于结束的语句且循环变量初始化要在while之前for可以在「表达式 1」里完成循环变量初始化最灵活。5.4 用 C 语言操作外设Cortex-M3 的外设寄存器被映射到系统存储器空间可通过指针访问。以 USART1 为例#definePERIPH_BASE((uint32_t)0x40000000)// 外设基地址#defineAPB2PERIPH_BASE(PERIPH_BASE0x10000)#defineUSART1_BASE(APB2PERIPH_BASE0x3800)#defineUSART1((USART_TypeDef*)USART1_BASE)USART_TypeDef结构体把外设寄存器按顺序排好typedefstruct{__IOuint16_tSR;uint16_tRESERVED0;__IOuint16_tDR;uint16_tRESERVED1;__IOuint16_tBRR;/* ... 其余寄存器与保留位 ... */}USART_TypeDef;发一个字节只需USART1-DR mydata;关键volatile与__IO结构体里的__IO实质上是volatile易变的宏定义在core_m3.h里。外设指针必须用volatile修饰原因是volatile告诉编译器这个变量随时可能变化比如被硬件改写了每次使用都必须重新从它的内存地址读取不要图省事把上次读到的值缓存起来做优化。没有volatile时编译器发现两次读变量之间没动过它就可能复用旧值导致读不到外设的最新状态——这在 Release 优化版本里尤其容易出 bug。进一步可以把外设操作封装成函数STM32 固件库就是这样设计的voidUSART_SendData(USART_TypeDef*USARTx,uint16_tData){USARTx-DR(Data(uint16_t)0x01FF);}5.5 Cortex 微控制器软件接口标准CMSISCMSISCortex Microcontroller Software Interface StandardCortex 微控制器软件接口标准是 Cortex-M 处理器系列与供应商无关的硬件抽象层。它提供的标准化内容包括访问 NVIC、SCBSystem Control Block系统控制块、SysTick 的中断控制与初始化函数NVIC、SCB、SysTick 寄存器的标准化定义Cortex-M 特殊指令的标准化函数系统异常的标准化命名系统初始化函数统一命名为void SystemInit(void)时钟频率信息变量统一命名为SystemCoreClock。CMSIS 分为 4 层核心外设访问层核心寄存器、NVIC、SCB、SysTick 的命名与访问中间件访问层面向 USART、Ethernet、SPI 等通信接口的通用访问方法提升跨芯片可移植性设备外设访问层MCU 相关具体 MCU 的寄存器定义与设备驱动外设访问函数MCU 相关可选的外设辅助函数。CMSIS 已集成在芯片厂商的设备驱动包里——你只要用了设备驱动库就已经在用 CMSIS 了。六、固件库6.1 寄存器直接操作与固件库的取舍直接操作寄存器如GPIOx-BRR 0x11;简单直接但 STM32 寄存器极多记起来麻烦固件库Firmware Library函数的集合向下负责直接操作寄存器向上提供用户调用的接口APIApplication Programming Interface应用程序编程接口。固件库把底层寄存器操作都封装起来大多数场合你不需要知道操作的是哪个寄存器只管调函数。所有驱动源码符合 ANSI-C 标准不受开发环境影响能大幅减少写代码时间、降低开发成本。弊端库函数是通用的会包含全部外设操作影响代码大小和执行速度对体积 / 速度有严格要求时可改用直接寄存器操作。此外固件库带实时错误检测校验输入值调试时好用最终产品代码里可移除以优化。6.2 命名规则 PPP_Action固件库最精妙的设计之一是统一命名PPP_Action其中PPP是外设缩写如GPIO、TIM、USARTAction是操作如Init、SetBits。整个函数名只允许一个下划线。6.3 库函数分类以 TIM 为例类别函数作用初始化配置PPP_Init()按结构体参数初始化外设PPP_DeInit()外设所有寄存器恢复默认值PPP_StructInit()用默认值填充初始化结构体使能控制PPP_Cmd()总开关使能 / 失能整个外设PPP_DMAConfig()配置外设的 DMA 功能中断与事件PPP_ITConfig()配置使能 / 失能特定中断源状态标志PPP_GetFlagStatus()检查事件标志位是否置位PPP_ClearFlag()清除事件标志位PPP_GetITStatus()检查特定中断是否发生PPP_ClearITPendingBit()清除中断挂起标志命名规律Config结尾一般是对某项功能配置Get是读取 / 检查状态Clear是清除标志。以 GPIO 为例体会三个函数的区别函数作用类比GPIO_DeInit()复位端口所有寄存器为默认值把房子恢复成毛坯房GPIO_StructInit()给结构体填一组安全默认值拿到标准户型图GPIO_Init()按你改好的结构体实际配置硬件寄存器按方案施工6.4 变量编码规则与常用类型固件库在stm32f10x_type.h里定义了常用类型typedefsignedlongs32;typedefsignedshorts16;typedefsignedchars8;typedefenum{FALSE0,TRUE!FALSE}bool;typedefenum{RESET0,SET!RESET}FlagStatus;// 标志位置位 / 复位typedefenum{DISABLE0,ENABLE!DISABLE}FunctionalState;// 功能使能 / 失能typedefenum{ERROR0,SUCCESS!ERROR}ErrorStatus;// 错误成功 / 出错外设结构体与外设声明以 SPI 为例typedefstruct{vu16 CR1;u16 RESERVED0;vu16 CR2;u16 RESERVED1;/* ... */}SPI_TypeDef;#definePERIPH_BASE((u32)0x40000000)#defineAPB2PERIPH_BASE(PERIPH_BASE0x10000)#defineSPI1_BASE(APB2PERIPH_BASE0x3000)#defineSPI1((SPI_TypeDef*)SPI1_BASE)这里的vu16同样是基于volatile的「易变无符号 16 位」和前面__IO是同一思路——外设寄存器必须防优化。本章小结与思考题核心知识地图开发流程与工具链编译器 / 汇编器 / 链接器 / 模拟器 / Flash 编程器 / 调试器Keil MDK ST-LINK V2处理器启动过程片上 Flash 与 BootROM、复位流程、启动代码 5 步、应用程序 3 部分输入输出接口外设概念与初始化 4 步程序映像向量表 C 启动代码 程序代码 C 库代码C 语言开发 ARM 应用数据类型 / 10 类运算符 / 流程控制 / 外设寄存器映射与volatile/ CMSIS固件库寄存器直操 vs 库函数、PPP_Action命名、函数分类、变量编码规则。本文由课程课件与讲稿整理供学习交流使用。如需转载或用于教学请注明出处。欢迎在评论区交流讨论。
返回列表