📑 本页目录

计算机系统 · 第八章配套讲义 · 全书终章

让程序真正
控制一块电路第八章 · ARM硬件与软件系统设计开发

先看清系统,再配时钟和引脚;用定时器、中断与通信把输入变成动作。每个寄存器都说明用途,每个算式都写出条件,每道题都有可对照的解法。

覆盖原讲义8.1—8.9全部小节,以及8.1—8.60全部习题。页码对应独立第八章PDF共188页:目录1—2页、正文3—186页、习题186—188页。

64节完整讲解25幅交互SVG60道原题详解16道综合短测

开发流程 → 最小系统与时钟 → GPIO → 定时器 → 中断 → USART → SPI/I²C

下载配套应用源码
从“系统要完成什么”开始 →

回到第七章:程序、调用与ABI → 返回教程合集 →

先分清要求,再开始学

算得对、说得清,再写出有边界的程序

这一章原文188页,横跨工具、硬件、软件和六类外设。不要把它当成188页同样重要的知识点。先抓住一个重复出现的工作链:选对型号与引脚 → 供电与时钟正常 → 配置模式与参数 → 启动外设 → 处理状态/中断 → 核对结果与异常。

学习层次 需要做到什么 对应内容
必须独立算、独立解释 写位掩码,算时钟/定时/PWM/波特率,读串行波形,判中断优先级 15—19、24—44、46—50、53—60
必须能改成程序 配GPIO,组织计时状态机,写短ISR、UART收发与有界等待,明确依赖 28、39、45、51—52、62及全部程序题
知道位置并能查表 外设冷门模式、完整寄存器/API清单、DMA通道、封装复用 各外设节、API附录
认识用途与边界 工具品牌、旧芯片、OS名单、开发流程与测试 01—14、20—23;会做相关简答题,不背广告参数

“只需认识”仍然要看一遍。 它让你能解释系统选型和简答题;不要求把全部OS移植一遍,也不要求背每个库函数的形参。每节开头分别写了“掌握要求”和“背景为什么要讲”。

建议按五轮读:01—23建立系统地图;24—28做GPIO;29—45打通定时与中断;46—62打通通信与存储;最后完成16道短测和60道原题。一次连续啃完会加重混乱,一章文件完整保留,学习时分段完成。

本章主型号固定为STM32F103RCT6。正文常用72MHz算例;配套应用代码主动切到HSI 8MHz,减少外部晶振依赖。它们各有明确条件,不能混套PSC、ARR、BRR。芯片系列、封装、时钟树、LED有效电平写在前面,是为了让答案能算对、程序有可核对的前提。

代码包提供原创应用文件和共用模块;依赖官方SPL/CMSIS设备头、启动文件及对应库源。本环境完成的主机检查不代替目标工具链构建、勘误核对和实板验证。I²C单字节/双字节接收尾部必须遵守F1手册时序,本讲义不把省略这一步的伪代码包装成通用驱动。

第八章 · 第01节

全章主线:让输入经过程序,变成可观察的输出

8.1.1;第3页;习题8.1

掌握要求:能画系统框图,区分核、片上外设、板上器件和软件。

背景为什么要讲:后面的寄存器最终都服务于这一条数据路径,不先认清对象容易只记函数名。

嵌入式系统面向一个具体任务:按键输入、采集传感器、计算、控制灯或电机、与其他设备通信。硬件提供供电、时序、存储和接口,软件决定什么时候做什么。不是所有嵌入式系统都需要操作系统,也不是装有Arm芯片就自动实现了功能。

图8-1把系统职责分清,才能从题意选外设。箭头表示数据或控制关系,不表示具体引脚接线。

原图8.1的外围按用途认识:输入有按键、开关、传感器与ADC;输出有显示、PWM、DAC、功率驱动与执行器;通信有USART、SPI、I²C、USB、CAN、以太网及电平转换;支撑有电源、时钟、复位、下载调试、外扩存储器。ADC把模拟量变数字,DAC反过来;GPIO并不能直接代替模拟转换或大功率驱动。

本章主讲STM32F103,算例默认芯片STM32F103RCT6(LQFP64、高密度,256KB Flash、48KB SRAM)。涉及C8/RB、中低密度或其他型号时另行说明。先看型号、封装、实际引脚,再谈“有几个定时器”。不要把这章的F1寄存器直接照搬到F4的MODER。

第八章 · 第02节

生态系统:芯片之外还需要哪些支持

8.1.2;第4—5页;习题8.2

掌握要求:能把硬件、软件、文档三类资源对应到开发任务。

背景为什么要讲:原章列举的产品与库是认知目录,不要求背所有年代和宣传数据。

生态系统把芯片、评估板、探针、工具链、设备库、参考例程与文档接起来。ST例子包含ST-LINK/J-Link等下载调试设备,评估/探索/Nucleo等开发板;MDK、IAR、GCC等工具;CMSIS、标准外设库、Cube/HAL/LL、USB、网络、文件系统、GUI、RTOS等软件组件,以及数据手册、参考手册、应用笔记和勘误。

数据手册查“这颗芯片有什么、引脚和电气限制”;参考手册查“外设寄存器怎样工作”;勘误查“这一硅版本有什么已知问题”。 原理图决定板子实际上怎么接,软件例程不能替代它。

收益是少造轮子、缩短适配、便于查错与复用;代价是仍要核对芯片、库、编译器、例程版本是否相配。书里的旧软件名称、函数数量和“最新”均按历史认识,不拿来判断今天是否值得购买某工具。

第八章 · 第03节

交叉开发:主机写程序,目标机运行程序

8.1.3 开发环境;第5—6页;习题8.3、8.5

掌握要求:解释宿主机、目标机、连接通道;区分编译器运行平台和代码目标平台。

背景为什么要讲:延续第七章,补上硬件和下载调试这一端。

宿主机提供编辑、构建、符号调试和文件管理;目标板包含MCU、存储、接口与实际被测对象;两者通过SWD/JTAG、串口、USB或网络等通道交换程序和调试信息。交叉编译器在PC运行,生成目标架构机器码;交叉调试器在PC显示目标寄存器、变量、栈和执行位置。

代码经历源文件→目标文件→链接映像→烧写/装载→执行。地址脚本规定段布局;映像入口、硬件复位向量与源码main不同。Linux应用工具链和裸机MCU工具链的运行环境不同,不能只因都有Arm前缀就混用。

区别于一般桌面应用:目标资源受限,依赖具体引脚/供电/时钟,常需烧写Flash与运行库初始化,实时行为受中断和外设影响。普通桌面链接也有布局规则,原书“只有嵌入式链接才有链接脚本”不宜绝对化。

第八章 · 第04节

工具清单按职责记,不按广告词记

8.1.3 开发工具;第6—10页;习题8.4

掌握要求:能区分PCB、PLD和软件工具;认识原书所有主要类别。

背景为什么要讲:软件版本、品牌历史只需认识;选择工具的理由是能完成当前步骤。

PCB工具做原理图、封装、网表、布局布线与电气检查;原书举Protel/Altium、OrCAD。单面、双面、多层指导电层结构,层数不是软件复杂度的直接评分。PLD/FPGA工具做HDL/原理图输入、综合、仿真、布局布线、配置与在线调试;原书举QuartusⅡ、ISE,以及VHDL/Verilog、IP、DSP Builder、SOPC等概念。

软件工具中,UltraEdit/Source Insight属于编辑与代码浏览;编译/汇编/链接分别把高级语言、汇编、多文件组合变成映像;GDB等做调试。MDK把μVision、编译/汇编、链接、库、下载与调试组合起来;IAR EWARM包含C-SPY等组件。MicroLIB、RTX、中间件和烧写算法各是不同角色。

原章还提MPLAB、WinAVR、Android SDK、Xcode,它们代表其他目标平台工具生态,不意味着都用来编译F103程序。本章无需安装全部工具,也不要求背旧版本支持清单。所谓“模拟全部外设”“自动生成完善启动”都有具体目标和工具支持边界。

第八章 · 第05节

五种调试方式与JTAG/SWD信号

8.1.3 调试;第10—15页;习题8.5

掌握要求:认识软件模拟器、ROM监控器、ROM仿真器、在线仿真器、片上调试的区别。

背景为什么要讲:旧设备形式会在简答题出现,实际练习主要理解片上调试。

软件模拟器在主机模拟目标指令/部分外设,便于提前验证,但不等于真实电源和时序;ROM监控器是目标驻留的调试程序,会占资源并需能先运行;ROM仿真器用可控硬件模拟ROM内容;在线仿真器是历史上较强的硬件观察/替代形式;片上调试利用芯片内部调试逻辑,经探针控制真实目标。

JTAG常见TCK时钟、TMS状态控制、TDI输入、TDO输出,nTRST复位TAP(可选);目标系统NRST与TAP复位不能混同。VTref是目标电平参考,不随意当作探针给目标大电流供电。F103的SWD用PA13/SWDIO、PA14/SWCLK,并需要GND、参考电压,NRST有助于连接复位调试。

探针不必替代MCU;硬件断点/跟踪资源有限,某些观察与暂停会影响实时系统。原章J-Link/ULINK下载速率、接口数量是产品版本背景,不背为固定标准。烧写后要做脱离调试器的复位测试,避免程序其实依赖调试器装RAM或半主机输出。

第八章 · 第06节

从需求到发布:五步都要有可检查的产物

8.1.4;第15—27页;习题8.6

掌握要求:说清需求、设计、实现、测试、发布分别产出什么。

背景为什么要讲:这部分不直接算寄存器,但能回答设计题为什么要声明输入、时长、误差和边界。

需求分析形成规格:输入、输出、功能、性能、环境、功耗、尺寸、重量、成本。功能是“做什么”,非功能是“多快、多准、多省、多少成本”。硬实时强调截止期约束,软实时允许一定程度的偶发迟到,但也需要可量化目标。

系统设计确定架构、软硬件划分、芯片和外设选择、模块或任务与接口;实现把这些变成原理图、PCB、资源表和代码;测试依规格核验,发布固化映像、版本与文档,并保留升级/维护办法。

图8-2用手环设计贯穿全流程;每阶段都要留下可检查的结果。

原书倾向把硬件说成更快但不灵活、软件更灵活但较慢,这是设计取舍的起点而不是定律。实际比较看吞吐、延迟、功耗、成本、可升级性、开发周期与现有IP。不能只按“CPU位数越大、主频越高越好”选型。

第八章 · 第07节

手环实例:把需求落到引脚、总线与模块

8.1.4 手环贯穿实例;第16—24页

掌握要求:能照此方法拆解一个小系统;不要求完成计步算法产品。

背景为什么要讲:原章用同一个实例贯穿五阶段,保留它是为了看见设计过程如何相互约束。

手环的输入包括按键和三轴加速度,输出为OLED,数据处理包含计步和步数到距离/热量的转换。原书选STM32F103C8T6、ADXL345、SSD1306屏;ADXL345通过SPI/I²C取样,屏通过I²C,按键接GPIO/EXTI。ADXL345量程文字“±169”应理解并核对为±16g这一类加速度单位,不能照抄成无单位数字。

先做资源表:谁占哪个引脚、哪个外设、地址、供电与中断;同一I²C总线可共享,但器件地址、上拉、电平要兼容。再拆主模块、OLED驱动、传感器驱动、按键驱动、计步算法、单位转换。主循环处理取样/计算/显示,按键ISR只记录切屏请求。

带RTOS时按紧迫程度、周期、耦合与通信需求划任务:关键且紧迫功能及时处理,耗时计算放适当低优先级,相同触发/周期、固定顺序或紧密相关功能可合并,过细增切换成本、过粗降低响应。加RTOS不自动保证实时性。

原规格的六个月续航要由平均电流验证:210mAh/约4380h≈48µA,尚未计电池折损。MCU、传感器、屏、稳压静态电流都需进入预算;100mg整机重量与原文屏重量等明显不自洽,必须回到真实器件核对,不能当成已验证设计。

第八章 · 第08节

测试不只是“灯亮了”:黑盒、白盒与集成

8.1.4 系统测试/发布;第24—27页

掌握要求:会列正常、边界、错误输入与组合测试;会看波形和时间。

背景为什么要讲:工程设计题的可靠性来自验证,不来自长代码或宣传性的“稳定”。

黑盒按输入输出规格测试,白盒按代码路径/覆盖检查,灰盒结合内部结构与外部行为。单元测试检查驱动/算法,集成测试检查接口组合,系统测试检查整机,回归测试确认修改没有破坏已有功能;验收按需求决定能否交付。

仪器按问题选择:万用表查电压与通断,示波器看电平、边沿、纹波,逻辑分析仪解数字协议,调试器看变量/寄存器与断点,模拟器提前检算法。仿真不能替代实际板上的电气与时序验证。

手环至少检查0/最大步数、不同运动与噪声、按键抖动、屏切换、总线失联和电池衰减。外设程序至少检查时钟、引脚、状态标志、超时、溢出和错误恢复。发布要记录芯片/库/编译器版本、连接和映像校验,不把开发机偶然可运行当作可交付。

第八章 · 第09节

三类处理器:S3C2440A、LPC2132、STM32F103

8.2.1—8.2.3;第28—36页

掌握要求:认识内核与存储/外设结构差别,不混用启动和中断模型。

背景为什么要讲:前两颗是原章历史平台,用于建立对比;实作聚焦F103。

平台 核与主要资源 为什么要认识
S3C2440A ARM920T,MMU、指令/数据Cache、存储控制器,LCD/相机、USB、NAND、DMA等 外扩SDRAM/Flash、较复杂系统与启动
LPC2132 ARM7TDMI-S,64KB Flash、16KB SRAM,VIC、定时/PWM、ADC/DAC、UART/I²C/SPI等 A32/旧Thumb、PINSEL与GPIO控制模型
STM32F103 Cortex-M3,片上Flash/SRAM、AHB/APB、DMA、NVIC和多类外设 本章最小系统与C开发

S3C2440A有片内控制器不代表“不需外围器件”:仍需供电、时钟、存储等。LPC2132不是Cortex-M,VIC不能直接套NVIC函数。F103无MMU,不能因为前者能跑某Linux环境就认为F103有同样资源。

原书列出的频率、引脚、5V耐受、低功耗、串口、ADC/DAC数量等只对应特定型号。认识核、片内存储、互连、外设和封装五个维度即可;冷门容量表会查,不要求背所有芯片的所有寄存器地址。

第八章 · 第10节

LPC2132的PINSEL:引脚名不等于固定功能

8.2.2 引脚连接模块、PINSEL0/1/2;第30—33页;习题8.7背景

掌握要求:理解用字段选择引脚功能,写字段时保留其他引脚配置。

背景为什么要讲:这一模式与F103的AFIO不同,保留比较以免把所有Arm芯片当一种寄存器。

引脚连接模块在GPIO、UART、SPI、捕获等内部功能与物理焊盘之间选择。PINSEL0/1中的两位字段可控制相应引脚的复用;字段编码需查该脚的功能表,不能以“01必是UART”类推全部引脚。PINSEL2涉及更高端口/跟踪等特殊选择,不能简单当作无限延伸的相同表。

修改引脚p的选择,常用“清该字段→写新编码”,不整寄存器覆盖别的引脚。GPIO方向、置位、清位、读取又属于端口控制寄存器职责。一个物理脚同一时刻不能让两个输出外设争驱动。

F103通过CRL/CRH设置模式,AFIO_MAPR选择支持的重映射,AFIO_EXTICR选择EXTI输入来源;不是照搬LPC的PINSEL。掌握通用思想,实际位域以目标手册为准。

第八章 · 第11节

F103内部结构:核、总线、存储和外设

8.2.3;第33—36页;习题8.8

掌握要求:会说明I-Code、D-Code、系统总线、DMA、矩阵与APB桥。

背景为什么要讲:解释为什么GPIO、定时器和串口要开不同的RCC时钟。

CPU通过I-Code路径取指、D-Code路径访问相应代码区域数据,系统总线连接其他系统访问;DMA有自己的数据搬运路径。总线矩阵在主设备与存储/总线目标之间仲裁,AHB-APB桥连接较低速控制外设。

F103功能模块包括系统/复位/时钟/电源、Flash/SRAM、GPIO/AFIO、DMA、定时器/看门狗、NVIC/EXTI、通信USART/SPI/I²C/CAN/USB,以及ADC、部分型号DAC、RTC等模拟/备份功能。器件名的容量等级决定实际模块是否存在。

GPIO、AFIO、USART1、TIM1、SPI1常挂APB2;TIM2—7、USART2/3、SPI2/3、I²C等对应APB1(以实际型号为前提);DMA在AHB侧。EXTI没有一个需要你去打开的“RCC_EXTI时钟位”;GPIO→EXTI映射需要GPIO与AFIO的时钟。

第八章 · 第12节

最小系统:五块支撑,加上必要的存储

8.3.1—8.3.3;第36—40页;习题8.9—8.11

掌握要求:能画F103最小系统和连接清单,知道外扩存储不一定必需。

背景为什么要讲:这是整章硬件设计题的基础,不必记某张评估板所有阻容数值。

最小系统提供CPU能启动、执行、存储、复位并下载/调试的条件。通用框图含处理器、程序/数据存储、电源、时钟、复位、调试;F103片内已有Flash/SRAM与RC,普通小应用不一定外扩存储,也不强制外接高速晶振。

图8-3按功能画出连接,省略了去耦电容数量与封装引脚。实际接线以芯片数据手册和板图为准。

本章RC/LQFP64设计:各VDD/VSS按实际引脚全部连接3.3V/GND,每对电源近旁去耦;VDDA/VSSA正确供电去耦,不用ADC也不能随意悬空;VBAT不用电池时按手册接主电源;NRST按键拉低、附近适当电容;BOOT0下拉,保留选择入口;PA13/14、GND、VTref、NRST接SWD。需要精确高速时钟再加HSE晶体/负载网络,需RTC独立维持才配LSE与后备电源。

这是功能级连接示意,实际PCB按该封装数据手册补齐每个电源脚、电气约束和晶体负载计算,不从“最少7个元件”口号反推省掉去耦。S3C2440A另需内核/I/O多电源和外部NAND/NOR/SDRAM等;其4KB Steppingstone在NAND启动时先容纳引导,再由软件建立更大运行空间,不是所有程序永远在4KB内运行。

第八章 · 第13节

电源、PVD与备份域:几种电压不是一件事

8.3.2电源、8.3.3电源;第37、40—46页

掌握要求:区分VDD、VDDA、VBAT、VREF和内部调压器;知道PVD与POR/PDR的不同。

背景为什么要讲:电气细节不要求背全表,但它决定系统能否可靠运行。

VDD/VSS供应数字侧及内部调压器,VDDA/VSSA供应模拟相关部分,VBAT支持RTC/备份域,VREF提供ADC参考(独立引脚是否存在看封装)。内部核心电压不是让你把外部3.3V接到任意“1.8V”节点。各电源脚按数据手册和去耦建议处理,电流预算、纹波、上升时间和地回流同样重要。

POR/PDR在电源不满足复位阈值时保持/触发复位;PVD是可配置阈值监测,可通过PVDO与EXTI16通知软件,PWR_CR.PLS选择PVD阈值,不是随意改变POR/PDR固定阈值。原表PWR_CR包含DBP、PLS、PVDE、CSBF、CWUF、PDDS、LPDS;PWR_CSR含EWUP、PVDO、SBF、WUF,按“控制/清标志”和“读取状态”分清。

后备域写入通常先使能PWR/BKP时钟并解除DBP保护。备份寄存器数量依容量等级,不把原书“42个”套在C8上。5V耐受也由具体引脚与工作模式决定,不能把任意模拟脚、未上电引脚、开漏上拉都接5V。LED串限流,继电器等需专门驱动/保护,20mA之类数字不等于任意脚可长期随便输出。

第八章 · 第14节

Sleep、Stop、Standby:保留什么,醒来从哪里走

8.3.3低功耗;第42—46页

掌握要求:比较时钟、RAM、唤醒与恢复路径,不背典型电流为保证值。

背景为什么要讲:节能不是简单关CPU,外设、备份域和唤醒机制仍要协调。

模式 核心认识 唤醒后重点
Sleep CPU暂停,许多外设仍可运行 从正常执行流继续,具体WFI/WFE条件看配置
Stop 主要高速时钟关闭,SRAM/寄存器保留 常以HSI恢复,需重建原系统时钟
Standby 主要调压域关闭,普通SRAM/寄存器丢失,备份域按供电维持 接近复位启动,检查待机/唤醒来源

SCR.SLEEPDEEP选择浅/深睡,SLEEPONEXIT控制异常返回后的休眠,SEVONPEND与事件唤醒相关;PWR_CR.PDDS/LPDS选择Standby/Stop及调压模式。WFE会先消费已有事件标志而可能不真正睡下;WFI/WFE不等于无条件延时。

原书WFE/SEVONPEND的一些0/1描述相互冲突,且混入HDMI CEC、LSE驱动USART等其他系列能力,不能据此给F103配置不存在的独立外设时钟。Stop不意味着RTC/IWDG一定停止。清挂起标志、选择唤醒源、处理竞态、恢复时钟都必须按F103手册;典型µA值取决于电压、温度、引脚与外设配置。DBGMCU保持调试会改变功耗测量结果。

第八章 · 第15节

复位与启动模式:BOOT1/BOOT0别写反

8.3.3复位/启动;第46—47、56—59页;习题8.12—8.13

掌握要求:系统、电源、备份域复位分清;会写三种启动组合。

背景为什么要讲:复位入口决定后面的代码是否有机会执行。

系统复位来源有NRST、IWDG/WWDG、软件SYSRESETREQ与相关低功耗选项;电源复位包含上电/掉电和待机退出等;备份域可由BDRST或相应供电丢失复位。RCC_CSR复位来源标志帮助诊断,不能看到main又开始就认定只有按了复位键。选项字节带n前缀的位常有反向语义,按手册核对,别照原文“置1必复位”推断。

BOOT0 BOOT1 选择的启动区域
0 任意 用户Flash
1 0 系统存储器中的出厂Bootloader
1 1 SRAM

原表最后一行把SRAM写成BOOT0=0是错误,应为1、1。BOOT1通常与PB2复用,启动采样和之后GPIO用途要分阶段理解。0地址映射改变启动别名,不会把Flash本体永久搬到RAM。系统Bootloader支持哪些接口看具体型号/版本,不承诺任意型号任意串口都能ISP。

第八章 · 第16节

五类时钟源与真正的上电默认

8.3.3时钟;第47—52页;习题8.14

掌握要求:认识HSI/HSE/LSI/LSE/PLL;区分复位默认和启动代码配置后的72MHz。

背景为什么要讲:所有定时与波特率计算都依赖真实时钟,不能只记一个72MHz。

HSI是内部约8MHz RC,启动快、无需晶体;HSE是外部高速晶体/时钟,常见8MHz,晶体模式与旁路有不同连接/频率限制;LSI是低速内部RC,常供IWDG/RTC,频率精度较低;LSE常为32.768kHz晶体,供精确低功耗RTC;PLL由已有时钟倍频,不是独立“凭空产生频率”的晶振。

F103硬件复位后主时钟从HSI开始,不是天然72MHz。 SystemInit或应用可以选择HSE→PLL后达到72MHz;它是否这样做取决于模板配置与HSE是否就绪。HSI/2经PLL的路径在常规F103倍频上限下不能简单得到72MHz,原书把它说成内部RC直接实现72MHz不严谨。

晶体负载电容依据负载参数、杂散电容和板布线计算,不能看到20pF就一律照装。CSS监测HSE故障并可引发NMI/切换备用HSI,故障处理需要清相应标志并恢复/降级。HSE/PLL等待必须有超时策略。

第八章 · 第17节

一棵时钟树算到底:先总线,再定时器

图8.22及说明;第48—55页;习题8.15—8.16

掌握要求:计算SYSCLK、HCLK、PCLK、TIMxCLK、ADC、USB与SysTick时钟。

背景为什么要讲:同一个“72MHz芯片”上不同外设的时钟可以不同。

图8-4固定SYSCLK=HCLK=72MHz、PCLK2=72MHz。分频1的情况会超过F103的APB1上限,图中明确提示。

典型HSE8MHz×9→SYSCLK72MHz,AHB/1→HCLK72MHz;APB1/2→PCLK1=36MHz,APB2/1→PCLK2=72MHz。APB1常规上限36MHz,APB2/HCLK72MHz;ADC常规上限14MHz,PCLK2/6给12MHz;USB需要合适的48MHz路径(如PLL72/1.5)。

APB分频为1,定时器时钟=PCLK;APB分频不为1,相关定时器时钟通常=2×PCLK。 所以PCLK1=36MHz时TIM2仍可72MHz,不意味着USART2也自动倍频。TIM1/8在APB2侧同样按该规则。SysTick可取HCLK或HCLK/8;MCO可输出选定时钟(PLL常为/2)便于测量。

RTC可选LSE、LSI或HSE/128;IWDG用LSI。SDIO、FSMC、DAC、ADC3、TIM8等只在相应型号存在。分频匹配性能约束,倍频允许用较低外部源获得内部高速,使能门控减少无用功耗;这也是树看起来复杂的原因。

第八章 · 第18节

RCC配置顺序与九类寄存器

8.3.3时钟管理表8.13—8.17;第53—55页

掌握要求:会说配置顺序,识别开时钟和设置参数两步;具体全位图以查读为主。

背景为什么要讲:源码中大多数“外设没反应”首先要排查时钟未开、切换未成功。

向上切换到72MHz前,先保证合适的Flash等待周期/预取配置、总线分频与输入条件;开启HSE并等就绪,关闭/正确配置PLL后开启并等锁定,切换SW后读SWS确认,最后按需开GPIO/外设时钟。不能在PLL正作为系统源时随便改倍频;失败要降级或报告。

RCC寄存器 用途
CR HSI/HSE/PLL开关、就绪、校准、CSS
CFGR SW/SWS、AHB/APB/ADC分频、PLL源/倍频、USB与MCO
CIR 时钟就绪/CSS中断的标志、使能与清除
APB2RSTR、APB1RSTR 对应外设复位
AHBENR、APB2ENR、APB1ENR 总线侧外设时钟门控
BDCR LSE、RTC选择/使能、备份域复位
CSR LSI、复位来源与清除

SPL对应RCC_HSEConfig、RCC_WaitForHSEStartUp、RCC_PLLConfig/PLLCmd、RCC_HCLKConfig/PCLKxConfig、RCC_SYSCLKConfig/GetSYSCLKSource、RCC_GetClocksFreq和RCC_xPeriphClockCmd。GetClocksFreq返回总线频率,TIM时钟还要检查APB倍频,不能直接拿PCLK替代。

本章配套源码为便于复现,进入main后明确切换到HSI8MHz,AHB/APB均不分频,所有PSC/BRR按8MHz算;正文原书72MHz算例明确另列。内部RC的时间精度受器件误差影响,公式正确不等于实验计时零误差。

第八章 · 第19节

从向量到main:SystemInit不是用户主函数

8.3.3下载/启动代码;图8.25—8.26;第55—60页

掌握要求:解释初始MSP、复位向量、SystemInit、运行库初始化与main的顺序。

背景为什么要讲:本章应用文件要接到正确的官方启动模板,不能只有一个main就声称硬件启动条件完整。

选择启动区域后,硬件从向量[0]取初始MSP,从向量[1]取复位入口。入口带Thumb标识位,指令实际位于偶地址。Reset_Handler按模板调用SystemInit及C运行库入口,拷贝初始化RW数据、清ZI、建立必要运行环境后进入main。

图8-5上电复位时按启动配置选择映射;首字是MSP,次字是复位入口指针。不是在向量位置放一条B指令。

原文System_Init、_main等排版不能作为实际符号名背诵;Arm工具模板常见SystemInit、__main,GCC模板有相应的Reset_Handler与库启动流程。STACK/HEAP/RESET/.text是常见段组织,不保证所有编译器文件完全同样写法。heap是否需要取决于程序,不用malloc也可以无堆。

本章示例源码使用SPL3.5/CMSIS的stm32f10x.h与startup_stm32f10x_hd.s、STM32F10X_HD目标;加入官方system与必要库源。不要混用C8的md启动、中断向量和RC的容量。SWD下载、系统Bootloader下载、IAP是不同路径,已初始化RAM中的程序也须有合适的装载和向量设置。

第八章 · 第20节

软件四层与引导的两阶段

8.4.1;第60—63页;习题8.17—8.19

掌握要求:解释驱动、OS、中间件、应用;能说明加载与直接Flash执行的区别。

背景为什么要讲:这些分层帮助组织大程序,不要求小LED例子强行包含所有层。

驱动层含板级初始化、系统所需与应用专用驱动;OS层提供调度、内存、设备、时间等服务,网络/文件/GUI可按需组合;中间件提供协议、文件、图形等复用功能,原章CORBA/Java/DCOM是历史举例;应用层实现任务。

工作流程为板级初始化→引导/升级选择→系统初始化→应用对象/任务初始化→持续运行。Bootloader可将映像从Flash/SD卡/网络加载至RAM,或跳到Flash原地执行;下载模式用于安装/升级。典型复杂系统分stage1(CPU相关最小初始化、准备RAM/栈、转stage2)与stage2(设备、内存布局、内核/文件映像、参数、跳内核)。原书U-Boot、RedBoot、vivi属于这类背景。

不是每颗MCU都要两个stage或先启动Linux。 代码依赖实际存储与运行库;普通F103小程序在Flash执行即可。嵌入式软件强调硬件协同、交叉工具、资源限制、实时/可靠、固化与可维护性,但不是所有程序都执行于ROM、所有系统都要求同一实时等级。

第八章 · 第21节

操作系统:提供机制,不自动消灭截止期问题

8.4.2;第63—65页;习题8.20

掌握要求:区分RTOS与一般OS,认识原章各名称与选择维度。

背景为什么要讲:本章不要求移植内核,但需知道何时引入以及付出什么资源。

OS管理任务/调度、内存与外围设备,可提供同步、时间、文件、网络与UI。嵌入式OS强调可裁剪、资源受限、硬件适配和稳定,实时系统还要求可分析的响应。实时不是“平均很快”,而是截止期与最坏情况约束。

MQX、µC/OS-II、VxWorks、ThreadX、QNX、Nucleus等代表不同嵌入式/实时生态;嵌入式Linux强调进程/内存、VFS、驱动、网络和用户空间,调度策略取决于配置;Windows CE、PalmOS、Symbian等按历史认识;Android/iOS是更复杂的平台例子,原章旧Eclipse/Java/Objective-C开发路径不作为当前选型建议。

选择看目标架构/MMU、RAM/Flash、任务/中断与最坏延迟、驱动/库、工具、许可、维护和团队熟悉度。32位MCU也可以裸机,RTOS也不必具备桌面式MMU。 常规Linux平台与F103资源不能等同;不要沿用原书把“内核态”当作简单调度优先级高低的说法。

第八章 · 第22节

四种开发模式与轮询/前后台

8.4.3—8.4.4;第66—70页;习题8.21—8.22

掌握要求:比较寄存器、SPL、OS、中间件;解释轮询和中断前后台结构。

背景为什么要讲:它们不是互斥阵营,一个工程可以分层组合。

寄存器方式直接操作位域,控制精确但需理解底层;SPL封装寄存器,减少重复,仍要懂时钟、引脚、状态与边界;OS提供任务/同步调度;CubeMX等配置工具与HAL/LL、FreeRTOS、LwIP、FatFS、USB/图形等组件帮助组织工程。生成代码不等于已实现业务,也不能把SPL函数和HAL结构体混写。

轮询结构是初始化后循环检查输入和条件,最坏响应取决于整轮耗时;前后台把短紧急工作放ISR,主循环处理较长任务;带OS把功能分为任务并通过明确同步机制配合。中断里长时间delay/printf会拖慢其他响应;RTOS也会遇到优先级反转、阻塞和栈不足。

考试比较写“控制程度、可维护性、开销、资源与适用场景”;不简单说寄存器永远最快、库永远臃肿、OS永远可靠。原题“非工程方式”主要考软件组织框架(轮询/前后台、带OS),并不是所有这些程序都可以跳过编译链接工程。

第八章 · 第23节

固件库工程与函数命名:把重复目录压成规则

8.4.4、图8.29—8.30;8.8.6命名总结;第70—72、164—166页;习题8.23—8.25

掌握要求:会找到配置/驱动/中断文件,区分StructInit、Init、Cmd、Flag、IT、DMA。

背景为什么要讲:原章大量函数表用同一规则组织,完整名称在文末查读索引保留。

CMSIS核心头提供核定义,设备头stm32f10x.h提供寄存器、地址、中断号;system文件提供时钟相关启动;stm32f10x_ppp.c/.h实现外设驱动,misc处理相关核配置;conf头选择外设包含,main.c写应用,it.c/.h写ISR与声明。库文件加入工程、头路径与宏定义三件事都要做,include不等于链接了实现。

工程流程:选目标/工具→加入启动、system、库与应用→设头路径、宏、存储与下载→编译链接→下载→断点/波形测试→复位独立验证。build增量、rebuild全量,优化会改变源级单步效果。

PPP_DeInit复位外设状态;PPP_StructInit填结构默认值;PPP_Init把结构写进外设;PPP_Cmd开功能;GetFlagStatus读标志;GetITStatus通常还结合对应中断使能;ITConfig开中断源;ClearFlag/ClearITPendingBit按规定清标志;DMACmd开请求,不替你配好DMA控制器。每种外设有例外,不能由名字推断所有标志都能写0或写1清除。

后文给常用设置和例子,附录列原章API与寄存器目录并标注“会写/会读/查表”。不需要把所有函数原型背下来;写题必须能选对负责当前动作的那几个。

第八章 · 第24节

GPIO内部:输出开关、输入采样和锁存不是同一条路

8.5.1—8.5.2;第73—75页

掌握要求:解释推挽、开漏、输入上/下拉与模拟输入;区分ODR和IDR。

背景为什么要讲:这是八种模式的共同电路,不必分开背八段相似文字。

推挽用互补输出器件主动驱动高/低;开漏只主动拉低,写1表示释放,高电平靠上拉与其他设备状态。输入路径用数字输入缓冲/施密特触发等观察焊盘电平,上/下拉决定无人驱动时的默认状态;模拟输入关闭相应数字输入路径以减少干扰与消耗。原书“肖特基触发器”应区别于施密特触发器概念。

图8-6开漏写1是释放,外部仍可拉低;推挽写1再外接强拉低会产生冲突,不能当作正常连接。示意逻辑,不模拟电流。

ODR是输出锁存值,IDR是输入采样。开漏写1、外部另一个器件拉低时,ODR=1而IDR可为0,并不矛盾。开漏允许多设备“任何一方拉低则为低”的线与连接;推挽输出不能随意并联争驱动。上拉大小影响上升时间和低电平电流,不能无限减小或增大。

复用输出由外设提供数据,普通GPIO输出由ODR提供;外设输入仍需正确引脚模式与映射。配置复用输出但没启外设不保证产生预期波形。电平耐受与驱动电流查具体引脚,GPIO并非功率放大器。

第八章 · 第25节

八种模式一张表,50MHz不等于自动输出方波

8.5.2;第74—79页;习题8.26

掌握要求:能根据LED、按键、ADC、USART、SPI、I²C选模式。

背景为什么要讲:做题要从信号方向和驱动方式选择,名字只是结果。

模式 特点 典型用途
AIN 模拟输入,数字输入路径关闭 ADC输入
IN_FLOATING 无内部上/下拉,外部决定电平 有明确驱动的USART_RX等
IPU 内部上拉 按下接地的按键
IPD 内部下拉 按下接VDD的按键
Out_PP 普通推挽 LED/逻辑控制
Out_OD 普通开漏 软件I²C等符合电气约束的线与
AF_PP 外设复用推挽 USART_TX、SPI主SCK/MOSI
AF_OD 外设复用开漏 I²C SCL/SDA

SPI主机MISO是输入,不能看到“SPI脚”就全配成推挽;从机MISO是输出。F1的接收引脚通常不需要像F4一样配置AF编号。I²C不是把SDA配浮空就完成了双向驱动,应采用合适开漏与上拉。

2/10/50MHz输出速度档控制驱动/边沿能力,并不自行产生相应频率。输入模式的速度字段不负责输入取样频率。选满足负载与信号完整性的较低档;不要把“按5—10倍选”当所有线路的硬规则。复位后多数普通GPIO为浮空输入,PA13/14等调试脚有特殊默认,不是所有脚默认普通输出。

第八章 · 第26节

CRL/CRH四位一脚,BSRR按位置位/清位

8.5.3;第79—83页;习题8.28

掌握要求:会算引脚字段和掩码;知道BSRR是写命令,不用|=回读。

背景为什么要讲:这是本章最需要独立写对的寄存器题。

CRL管0—7,CRH管8—15;每脚4位,低2位MODE,高2位CNF。MODE=00输入,01输出10MHz,10输出2MHz,11输出50MHz;输出时CNF=00普通推挽、01开漏、10复用推挽、11复用开漏。输入时CNF=00模拟、01浮空、10上/下拉,ODR相应位选上拉1/下拉0。

图8-7CRL中PC6占bit27:24;每个引脚四位,只有目标字段被替换。上/下拉还需用ODR选择。

PC6在CRL的bit27:24;50MHz普通推挽是0011:

RCC->APB2ENR |= (1u<<4);  /* IOPCEN */
GPIOC->CRL = (GPIOC->CRL & ~(0xFu<<24)) | (0x3u<<24);
GPIOC->BSRR = 1u<<6;       /* PC6置1 */
GPIOC->BSRR = 1u<<(6+16);  /* PC6清0 */

IDR读焊盘,ODR读写输出锁存,BSRR低16位写1置对应脚,高16位写1清对应脚;写0不影响其他脚。BRR低16位写1清位。BSRR=(1u<<5)|(1u<<(4+16))一次让脚5高、脚4低,避免ODR读改写的竞争。并发复合业务仍需设计同步,不能据此说任意多步操作都原子。

LCKR按规定锁键序列锁CRL/CRH配置直到复位,不锁死所有输出数据。AFIO还有EVCR、MAPR、EXTICR1—4。原书PD15的0x08000000少移一位,应按bit31:28写0x80000000;&=~(1u<<4)不是只适用于8位的&=0xEF。

第28题提供寄存器版和SPL版完整应用文件;它们是同一配置的两种表达,不能同时编译两个main。

第八章 · 第27节

复用与重映射:改变连线,不增加物理焊盘

8.5.2重映射/事件;第76—79页;习题8.7、8.27

掌握要求:会说明GPIO模式、外设开关、MAPR选择三者配合;保留SWD。

背景为什么要讲:原书引脚表较长,以代表例子讲规则,完整可用组合仍查本封装。

USART1默认PA9/PA10,支持时可重映射到PB6/PB7;USART2从PA2/PA3到PD5/PD6要看封装是否引出。重映射不是任意外设任意接脚,也不凭空增加引脚或允许两个输出同时占一个脚。

步骤:先使能相关GPIO、AFIO与外设时钟;确保不与其他功能冲突;设置MAPR对应选择,配置TX为AF_PP、RX为合适输入,初始化并使能外设。SPL用GPIO_PinRemapConfig,EXTI来源则用GPIO_EXTILineConfig,它们不是一回事。

图8-8释放JTAG脚与关闭SWD有不同后果;普通外设重映射同样要查AFIO_MAPR和封装脚。

释放JTAG但保留SWD用GPIO_Remap_SWJ_JTAGDisable,原文GPIO_Remap_SWJ_Disable会关闭包括SWD在内的调试能力,不能用它表达“只关闭JTAG”。GPIO事件输出经EVCR等配置;SEV与EXTI事件、普通GPIO输出波形不要混为一谈。

第八章 · 第28节

LED与按键:从电路决定有效电平

8.5.4两个实例;第83—89页

掌握要求:会写开时钟、配置、读写;分清“按住亮”和“按一次翻转”。

背景为什么要讲:最简单的电路也要先明确极性,不能把setbit直接等同于灯亮。

原例3.3V→限流电阻→LED→PA8,因此PA8低时灯亮、高时灭。PC5按键接地,配上拉,按下读0、松开读1。原章LED闪烁用空循环延时,时间受优化与主频影响,不适合5秒这种明确计时要求;可用定时器/SysTick代替。

图8-93.3V→510Ω→LED→PA8;拉低时点亮。实际电流还取决于LED压降和输出低电平。

限流估算 R=(VDD−VF)/I。例如3.3V、红LED约2.0V、510Ω,电流约2.55mA;无需为了“标准20mA”强行把小指示灯推到极限。串联电阻和极性必须出现在设计图里。

SPL过程是RCC_APB2PeriphClockCmd→填GPIO_InitTypeDef(Pin/Mode/Speed)→GPIO_Init→ReadInputDataBit/SetBits/ResetBits等。结构体先StructInit或完整初始化;不要留下TIM/USART示例那样的未初始化成员。按住亮可直接轮询电平;按一次翻转要检测一次按压并消抖,原章EXTI实例在第45节展开。

第八章 · 第29节

定时器家族:先查芯片有没有,再用功能包含关系

8.6.1;第89—91页

掌握要求:比较基本、通用、高级、看门狗与SysTick。

背景为什么要讲:原章用整个系列的最大资源介绍,不代表每颗F103都有TIM1—8。

基本TIM6/7只有基本时基、更新请求和相关DAC触发,无外部捕获/PWM通道;通用TIM2—5具有时基、4路捕获/比较、PWM、单脉冲、编码器等;高级TIM1/8增加互补输出、死区、刹车、重复计数等。RC高密度与C8中密度的定时器数量不同,C8通常没有TIM5—8及DAC。

IWDG由独立LSI驱动,计数器/分频与重装用于超时复位,需按实际LSI误差估算;WWDG要求在窗口内喂狗,过早和过晚都可能复位;SysTick为Cortex-M的24位递减时基,不是RCC下的一个TIM。调试冻结会改变观察到的运行状态。

高级定时器仍通常是4个捕获/比较通道,其中前三个可有互补引脚,因此能出现7路输出;不把“三相六路”说成6个独立时基。4通道共享同一计数器与ARR,并不各自拥有任意互不相关的PWM频率。

第八章 · 第30节

时基公式:两个加一从哪里来

8.6.2;第91—94页;习题8.29—8.30

掌握要求:从时钟算计数频率、更新周期和PSC/ARR,检查16位范围。

背景为什么要讲:这是定时器做题的第一核心。

fCNT=fTIMPSC+1,Tup=(PSC+1)(ARR+1)fTIM

向上计数包含0到ARR共ARR+1个状态,PSC存“分频数−1”。例如fTIM=72MHz,PSC=35999、ARR=1999给1秒;ARR=999则0.5秒。若fTIM=8MHz,可用PSC=7999、ARR=499给0.5秒。先写时钟来源,不凭芯片宣传主频代入。

图8-10向上计数边沿对齐模型。寄存器值加1得到分频数和计数次数,CKD不代替PSC。

PSC/ARR常规为16位数0—65535,对应分频1—65536;CNT是当前值,不是软件每次加1。ARR=0等特殊配置须看硬件行为,常规示例使用有效正周期。CEN使能计数;计数到ARR之后的溢出/重载才构成完整周期,不是“刚看到CNT=ARR即又少一个tick”。

第八章 · 第31节

更新、预装载与影子:值写进去,何时生效

8.6.2;第92—94页

掌握要求:解释PSC、ARR、CCR缓冲与UEV;区分事件、标志、中断请求。

背景为什么要讲:它直接影响首周期和动态改变PWM是否平稳。

软件可访问预装载值,硬件真正工作的活动值可在更新边界装载。PSC具有缓冲;ARR由ARPE控制,输出比较CCR由相应OCxPE控制;高级定时器另有RCR。通用定时器并不因原书一句话就拥有RCR。

写ARR新值并开ARPE时,通常等下一次UEV装入活动值;若在某次更新ISR内才改,那个更新时刻已经过去,不能以为它追溯影响刚开始的周期。UG可软件产生更新以装入设置,也可能置UIF;启动前应按所选URS/UDIS行为产生UG并清不需要的挂起标志,再开中断/CEN。

UEV是硬件事件,UIF是状态,UIE是请求使能,NVIC又是核侧使能。关中断不等于停止计数,事件发生也不等于CPU一定进入ISR。 原文ARPE/APRE、影子传送方向、URS/中断条件有混写处,按“软件写预装载→事件装入活动值”记住因果。

第八章 · 第32节

三种计数与四类时钟:外部脉冲不一定来自晶振

8.6.3、8.6.8;第96—100、130—132页;习题8.29—8.30

掌握要求:比较上、下、中央对齐;认识CK_INT、ITR、TI、ETR。

背景为什么要讲:计数器既能测时间,也能数外部事件。

向上0→ARR→重载;向下ARR→0→重载;中央对齐往返,DIR由硬件体现当前方向。中央对齐完整往返的周期不能直接照搬向上模式ARR+1公式;在常见有效设置下按2×ARR个计数节拍理解并核对更新点/比较标志选择。

时钟可来自内部CK_INT;其他定时器经ITR0—3触发;TI1/TI2的滤波/边沿路径;ETR外部触发路径。外部时钟模式1利用从控制器触发作计数时钟,模式2用ECE控制ETR计数;不能把它们简单叠加成两种时钟同时随意“加速”。

滤波由采样频率和连续稳定样本数共同决定,CR1.CKD主要影响数字滤波/死区相关时基,不是PSC的替代品,不会因为CKD_DIV2就把CNT时钟一律再除2。原文把数字滤波简单解释为奈奎斯特后“滤掉所有高频”不准确。ITR实际对应哪个主定时器查对应连接表,不背成所有TIM同一编号。

第八章 · 第33节

输入捕获:硬件锁住时刻,软件读取结果

8.6.3普通输入捕获;第100—102页;习题8.31—8.32

掌握要求:能算周期/频率、回绕差值,设置TIM2_CH2;知道过捕获意味着数据丢失。

背景为什么要讲:这是定时器做题的第二核心,捕获不靠ISR执行瞬间读CNT来猜时间。

信号经TIx→滤波/边沿→输入映射→捕获预分频,边沿到来时硬件把CNT锁进CCRx,置CCxIF;是否中断/DMA取决于配置。IC1/IC2可以接同一TI用于不同边沿,输入脚与捕获通道不是同一个概念。

图8-11计数1MHz、ARR65535、间隔严格小于65536tick。算模差;同值不能区分零间隔和整圈,不能凭图猜多圈。

同极性两次边沿差ΔC:T=ΔC/fCNT, f=fCNT/ΔC。若ARR=65535、确保间隔小于65536tick,可用16位无符号减法处理一次回绕:前65000、后464,差1000tick;1MHz计数即1ms、1000Hz。没有通用的“差值都再加1”;若跨多圈则要累计溢出,若捕获每N个边沿一次也要纳入比例。

CCxOF表示上次捕获尚未处理又有新捕获,不是“CNT正常溢出”。发现它应标记丢样/重新建立基准,不能继续假定相邻边沿无缺失。ISR慢不影响已锁的第一时刻,但可能造成覆盖。

SPL的TIM_ICInitTypeDef使用Channel、ICPolarity、ICSelection、ICPrescaler、ICFilter等成员,原文TIM_ICMode并不是该版本结构成员。第32题给PA1/TIM2_CH2的完整设置,计数率明确为1MHz,测量间隔限制写在答案中。

第八章 · 第34节

PWM输入:一条信号,两次捕获,一个复位基准

8.6.3 PWM输入;第102—103页;习题8.33

掌握要求:知道CH1/CH2配对映射,计算周期、高电平时间与占空比。

背景为什么要讲:它减少软件换极性带来的时序风险,但占用共享计数资源。

典型TI1输入:IC1同沿测相邻上升沿周期、IC2反沿测高电平宽度;从模式reset让上升沿建立计数起点。TI2也有对应的配对方式。硬件先锁存相应时刻,再执行配置的复位;两个通道不是各自测两条独立信号。

图8-12这里输入已经是两个边沿间的捕获间隔。高电平不能大于周期;与ARR整圈计数的加1规则区分。

在常见reset测量定义下,CCRperiod是经过的tick数,CCRhigh为高电平tick数:f=fCNT/CCRperiod,D=CCRhigh/CCRperiod。例如1MHz下周期2000tick、高电平500tick,f=500Hz、D=25%。不把原例的(CCR+1)/(CCR+1)当通用公式;ARR+1属于计数上限周期,捕获CCR属于时间戳/间隔,它们来源不同。

第一捕获尚未有完整周期可能无效;0/100%没有所需的两种边沿,信号停止不能永远保留旧频率当新结果,需要超时判无信号。滤波延迟、计数分辨率和溢出范围都会影响精度。

第八章 · 第35节

比较输出与PWM:从CNT<CCR画出波形

8.6.3比较/PWM/单脉冲;第103—107页;习题8.34—8.35

掌握要求:会从模式、极性、ARR、CCR推波形与占空比。

背景为什么要讲:这是定时器第三核心,不能把输出频率和ISR翻灯频率混同。

比较匹配时可冻结、置有效、置无效、翻转,另有强制无效/有效、PWM1/2共8类。匹配标志和中断请求受各自条件控制,硬件输出不是靠ISR逐位翻GPIO。强制输出可改变参考状态但不意味着比较逻辑全停止。

向上、PWM1、高有效时:CNT<CCR输出高,否则低;周期ARR+1tick,高电平CCRtick。因此 D=CCR/(ARR+1)。ARR=49、CCR=47,D=94%,不是48/50。CCR=0得到0%,CCR>ARR可为100%,还须确认寄存器可表示;PWM2/反极性输出会反相。

图8-13向上计数、PWM1、高有效OC。CNT

同一TIM通道共享频率,CCR各控占空比。输出还需GPIO_AF、通道CCxE、高级TIM的MOE等链路。低有效LED的亮时间是低电平占比,94%高电平对应约6%点亮时间,并非94%亮度。

翻转模式每次匹配翻转一次,完整方波周期常需两次翻转,不能把事件率直接当方波频率。单脉冲OPM在触发后延时并产生规定脉宽,可自动停下;编码器接口通过两相输入推方向/位置,速度还需计时间;霍尔与电机接口作为背景认识,暂不设计功率电机控制器。

第八章 · 第36节

高级TIM:互补、死区、刹车与重复计数

8.6.4;第107—108页

掌握要求:解释为什么需要死区和MOE;认识RCR与BKIN。

背景为什么要讲:基础习题主要用普通TIM,电机相关能力保留用途与学习边界。

三对互补输出可驱动相应驱动级,切换时留两路都关断的死区,防止上下桥臂交叉导通;刹车输入可让输出进入规定的安全状态。BDTR的DTG编码控制死区,MOE总输出使能与OSSI/OSSR等决定关闭/空闲行为,极性还有CCxP/CCxNP。

RCR允许多个周期后再产生更新请求/装载节奏,通常涉及RCR+1计数及初始时序,不能只说“恰好RCR次”。输出通道、互补脚、完整驱动器是三层概念,MCU脚不直接承受电机功率。

TIM1/8的CK_INT来自APB2对应定时器时钟,通用TIM2—5常在APB1;但共享系统时钟、引脚与总线,原书“完全不共享任何资源”应理解为独立计数器功能,不是完全无系统资源联系。

第八章 · 第37节

主从同步:谁发TRGO,谁选TRGI,收到后做什么

8.6.5;第109—113页

掌握要求:分清MMS、TS、SMS及reset/trigger/gated/external-clock行为。

背景为什么要讲:复杂同步不要求全写,但应读懂PWM测量与定时器级联。

主定时器用MMS选择TRGO来源,如UG/使能/更新/比较/OCxREF等;从定时器用TS选择ITR0—3、TI1F_ED、TI1FP1、TI2FP2或ETRF等输入,再用SMS决定动作。

reset:有效触发重置计数与相关分频状态;trigger:触发启动;gated:有效电平期间允许计数;external clock1:触发脉冲作计数时钟;encoder:双相编码器专用。触发和计数时钟可以分属不同输入,外部模式2可在允许组合下与门控/reset/trigger配合,不随意与编码器或外部时钟1同时开启。

两TIM级联可让上游周期事件驱动下游累计,或让多个输出同时开始;不能从“芯片内相连”推断任意主从都接同一个ITR编号。原文某些trigger示例提前置CEN,已经让计数开始,不能再声称完全由首个外部边沿启动。配置前先决定时钟源、触发源、触发动作三件事。

第八章 · 第38节

TIM寄存器与库:按数据路径找,不背整页位图

8.6.6;第113—124页;8.6.8

掌握要求:找到时基、通道、请求、触发、输出五组配置。

背景为什么要讲:原章长表作为完整查读目录保留,基础做题只需会选择关键组。

组 寄存器/结构 关键动作
时基 CR1、PSC、CNT、ARR;TimeBase结构 计数方式、分频、周期、预装载、CEN
通道 CCMR1/2、CCER、CCR1—4;IC/OC结构 输入映射/滤波、输出模式、极性/使能、捕获/比较
请求 DIER、SR、EGR IRQ/DMA请求、状态、软件生成事件
触发 CR2、SMCR TRGO、TS、SMS、外部滤波/分频
高级 RCR、BDTR 重复节奏、MOE、死区、刹车
DMA批量 DCR、DMAR 定时器DMA突发访问选择,不等同DMA控制器通道

TIM_TimeBaseStructInit/Init配置时基;TIM_ICStructInit/ICInit配置输入;TIM_OCStructInit/OCxInit配置输出;TIM_SetComparex改CCR;TIM_Cmd开计数;TIM_ITConfig/GetITStatus/ClearITPendingBit管请求;InternalClock/ETR/TIx/ITRx、SelectInputTrigger/SlaveMode/OutputTrigger/MasterSlaveMode管时钟触发;ARR/OCxPreloadConfig控制生效边界;高级TIM_CtrlPWMOutputs设置MOE,不应用在TIM3上假装它有BDTR。

初始化结构先填默认,再改所需项;TimeBase的ClockDivision与RepetitionCounter也要有确定值,后者仅相关高级TIM有意义。捕获/输出的CCR身份随模式改变,不是同时测输入又输出不相关波形。

第八章 · 第39节

三个原例:500ms、20kHz/94%、SysTick

8.6.7—8.6.8;第124—133页

掌握要求:独立核对三个例子的参数、首周期与有效电平。

背景为什么要讲:把公式落到程序,修正SysTick的位宽与减一错误。

500ms闪烁在72MHz下PSC=35999、ARR=999;一次亮500ms、灭500ms才算一次完整闪烁,CountOfToggle应每1秒加1。原例printf到ITM需要实际调试/跟踪设置,不能在没有调试器时无限等端口可写;用观察窗口或已配置串口也可核对。

20kHz PWM:72MHz/72→1MHz,ARR49给50µs,CCR47高电平47µs。TIM1_CH1在PA8,需AF_PP、时基/OC结构、预装载、UG、通道与MOE。低有效LED的点亮占比为6%。硬件输出配置后不需在主循环反复写电平。

SysTick的CTRL/LOAD/VAL/CALIB分别控制使能/中断/时钟/COUNTFLAG、重载、当前计数、校准。24位最大周期为 224/72MHz≈233.0ms,原书的23位和116ms错误。10ms应LOAD=720000−1;CMSIS用SysTick_Config(SystemCoreClock/100)会按接口完成减一并检查范围。COUNTFLAG读CTRL或写VAL可清,不是每次重载自动清;中断挂起是另一状态。CALIB只读能力/参考信息不保证任何模板都可无条件直接拿来定时。

第八章 · 第40节

中断向量:地址表,不是跳转指令表

8.7.1;第133—136页;习题8.37、8.42—8.43

掌握要求:会定位复位与IRQ向量,知道核异常区和外部IRQ数量依目标。

背景为什么要讲:原章把多个型号和“槽位/异常/来源”计数混写,需要分开。

M系列向量首字为MSP初值,第二字为Reset_Handler指针,后面为异常处理入口/保留项。外部IRQ k对应向量偏移 4(16+k)。复位偏移0x04;Flash向量基址0x08000000时,该项位于0x08000004,不是所有情形固定在唯一物理地址。

F103RC高密度通常IRQn=0—59对应60个外部IRQ槽;C8中密度至42,共43。系统向量区16个字含MSP和保留项,不能全叫16个可用内核异常。原文84=16+68、又说60用户IRQ,不能当所有F103统一答案。 题42按原文口径可识别其84/16/68说法,正式设计按所选器件启动表核对;不把EXTI19根线再逐根加到NVIC总数里。

NVIC ISER/ICER使能/除能,ISPR/ICPR置/清pending,IABR读active,IPR优先级;STIR写目标IRQ号产生软件触发,不是“每位对应IRQ的两个字位图”。置pending也不是把当前正在运行的ISR立即暂停。

第八章 · 第41节

抢占与子优先级:数值越小,优先级越高

8.7.1、8.7.3;第134、136、139—142页;习题8.39—8.40

掌握要求:会按4位实现和分组算范围,判断能否抢占及等待顺序。

背景为什么要讲:这是中断做题的核心,不能只背一个“优先级高先响应”。

F103实现4位可编程优先级,共16个编码,通常放在优先级字节高4位。SPL分组0到4,对应抢占位0到4、子位4到0:组2是2+2,各范围0—3;组4是4+0。CMSIS NVIC_SetPriority常接未左移的逻辑优先级,不能把写寄存器用的0x50直接当逻辑5传所有API。

图8-14假设当前分组有足够的抢占/子级位且未屏蔽。数小优先;子优先级只决定同时挂起的选取,不能使同抢占级嵌套。

更高抢占优先级可打断较低抢占ISR;相同抢占级不因子级更高就嵌套。多个pending可服务时,先抢占级、再子级、再架构规定的异常号顺序。子级决定排队,不负责打断。分组是全局配置,不是给每个IRQ选一套自己的分组。

NMI/HardFault/Reset等固定角色不同,不能用普通外设优先级任意盖过它们。PRIMASK、BASEPRI(M3支持)、外设请求使能与NVIC使能是不同屏蔽层;图的判断假定没有额外屏蔽和更高异常约束。

第八章 · 第42节

ISR与普通函数:接口简单,返回机制不同

8.7.1建立过程;第134—136页;习题8.38、8.41

掌握要求:能建立一个中断并说明现场、栈、处理/清源、异常返回。

背景为什么要讲:编译器帮你写了部分序言,但不能因此忽略共享数据和栈。

普通C ISR通常无参数/返回,名字必须匹配向量表中的弱默认符号;放在it.c还是其他文件不是硬件要求,同名定义只能有一个。硬件异常进入自动保存R0—R3、R12、LR、PC、xPSR等基本帧(可能另有对齐/扩展条件);编译器负责其使用的保存寄存器,LR包含EXC_RETURN语义,不能把异常入口当普通BL调用分析。

建立顺序:有正确向量/栈→配置外设/引脚→清旧请求→设置NVIC分组/优先级→使能外设源和核IRQ→实现匹配ISR。ISR读取来源、尽快取数据/清正确源、记录事件,把较长工作交主循环。

图8-15简化普通Thread→IRQ过程;硬件基本帧保存R0—R3、R12、LR、PC、xPSR,其他寄存器遵守软件约定。

若异常打断使用PSP的线程,硬件初始入栈可在PSP;Handler使用MSP。原书“一切现场只在MSP”过度简化。函数共享变量用合适volatile与同步;volatile不等于复合操作原子。中断中delay等另一个低优先级中断推进的时间可能死锁,printf/等待总线也会拉长最坏延迟。

第八章 · 第43节

EXTI:同号脚选一个,中断与事件分两路

8.7.2;第136—139页;习题8.44—8.48

掌握要求:能画GPIO→AFIO→EXTI→NVIC;会分清IMR和EMR。

背景为什么要讲:名字“外部”也包含芯片内部接入的专用信号。

F103这一范围EXTI0—15来自GPIO,同号PAx/PBx/PCx等共用一根线、同一时刻选一个;16为PVD,17为RTC闹钟,18为USB唤醒。连接型等其他F1品种可能有扩展,别把所有系列都限制为19。

图8-16EXTI5同一时刻只选一个端口来源。EXTI5—9共享NVIC入口,每条pending独立清除。

GPIO时钟/输入模式与AFIO时钟准备后,EXTICR选择来源;RTSR/FTSR选上/下沿,SWIER可软件触发。中断路经pending/IMR到NVIC与ISR;事件路经EMR产生事件/唤醒请求,通常不进ISR。IMR开中断、EMR开事件,原表注释写反要改。

事件不是“自动连接任意TIM/USART/DMA”的万能路由,是否能触发某外设看芯片的内部连线。PR为写1清,不要PR |= mask,否则回读到的别线pending也可能被一起清。NVIC清pending不一定清掉EXTI实际源。共享ISR必须分别检查相关线,不能只看“进了中断”就假定是自家按键。

第八章 · 第44节

EXTI/NVIC配置字段与共享向量

8.7.3—8.7.5;第139—142、150—151页

掌握要求:会选IRQ名,设置映射和触发;API和寄存器位图会查。

背景为什么要讲:16根GPIO线不等于16个独立ISR入口。

EXTI0—4各独立IRQ;5—9共EXTI9_5_IRQHandler,10—15共EXTI15_10_IRQHandler。PC5应选择EXTI_Line5、GPIO_PortSourceGPIOC/GPIO_PinSource5、EXTI9_5_IRQn,不能把GPIO号5写成IRQn5。

EXTI_InitTypeDef的Line/Mode/Trigger/LineCmd决定线、Interrupt/Event、边沿和开关;NVIC_InitTypeDef含IRQChannel、PreemptionPriority、SubPriority、Cmd。GPIO_EXTILineConfig控制AFIO_EXTICR,EXTI_Init控制EXTI,NVIC_Init控制核侧,职责不能互换。

寄存器完整组:EXTI IMR、EMR、RTSR、FTSR、SWIER、PR;AFIO EXTICR[4]每寄存器低16位四组4位,对应四个脚号,端口A/B/C编码0/1/2等;NVIC各组见第40节。原书手写NVIC_Type中STIR数组等不准确,实作使用设备/CMSIS头定义,不照着重新拼结构体。

第八章 · 第45节

按键翻灯与定时器中断:一次动作只处理一次

8.7.4—8.7.5两个实例;第142—151页;习题8.36、8.49

掌握要求:能写中断翻灯和非阻塞红绿灯;时间变化与按钮抢占有明确规则。

背景为什么要讲:综合题把前面的GPIO、TIM、EXTI连接起来,不能靠一个长delay糊过去。

原例PC5按键、PA8低有效LED,选择下降沿后ISR记录一次事件;主循环处理翻转,消抖明确约定20ms。原章另有TIM定时闪烁实例,计时器ISR每次更新取/清标志并推进计数;计数LED完整亮灭次数与单次翻转次数不同。

本章红绿灯示例统一用PA8红、PA9绿(与USART1_TX冲突,所以和串口例分开工程),每灯串510Ω接3.3V,PC5按键接地上拉。第36题5秒/5秒循环;第49题红20秒、绿30秒,按键被接受后立即切色并重新开始新颜色时长。主循环不阻塞,所以按钮不会等一个20秒delay结束才响应。

图8-17演示主循环先处理已消抖的按键,再判断定时到期。每次切换都从当前时刻重计时,ISR不做长延时。

时间用TIM2的1ms中断累计32位tick,差值用无符号减法处理回绕,要求比较区间小于约一半计数空间。按钮事件和计时竞争时的优先级定义为“先处理按钮并重置阶段起点,再判断超时”;反复按压按消抖后独立有效按压处理。配套文件给完整main与ISR,不在ISR里做秒级等待。

第八章 · 第46节

UART一帧:起始、低位先发、校验、停止

8.8.1—8.8.2;第151—154页;习题8.50—8.51

掌握要求:会画8N1、8E1/8O1并计算位时间与有效速率。

背景为什么要讲:两端“波特率一样”还不够,数据位/校验/停止位也要一样。

USART在UART异步收发之外还支持相应同步等功能;常用异步模式不传SCK,接收器根据自身时钟与起始沿采样。TX空闲高,起始0,数据通常LSB先发,可加奇/偶校验,停止为高。8N1共10bit/字节;8数据+校验+1停止共11bit。

图8-18空闲高、起始低、数据低位先发、停止高;线宽按位周期。图为逻辑电平,不是RS-232正负电压。

偶校验使数据与校验位总1数为偶数,奇校验反之。F103的CR1.M决定8/9位字长,开启校验后它占用所选字长的最高位,所以8个有效数据位+校验需选择9位字长;8位字长+校验只有7个有效数据位。

USART1在APB2,其他相应串口在APB1,实际UART4/5等看芯片。CTS/RTS是硬件握手;TX/RX功能与智能卡、IrDA、LIN、多处理器地址唤醒、单线半双工、同步SCLK各有配置限制,先知道用途,基础题不必实现全部协议。同步的CPOL/CPHA/LBCL与SPI有相似名称,但不能因此把USART同步协议当作任意SPI设备替代。

第八章 · 第47节

波特率:用USART自己的PCLK,不用TIM倍频

8.8.2波特率、BRR;第153、159页

掌握要求:能算BRR与实际误差,知道16倍采样的关系。

背景为什么要讲:这是串口做题的计算核心,时钟源错会让字符串全成乱码。

F103常规16倍采样下:baud=fPCLK/(16USARTDIV),USARTDIV=整数+小数/16;BRR把整数放高12位、小数放低4位。等价地可先算并取合适整数BRR≈PCLK/baud,再检查实际波特率与误差。

72MHz/115200=625,BRR=0x0271,USARTDIV=39+1/16;36MHz/9600=3750,BRR=0x0EA6。USART不享受定时器APB倍频。本章源码HSI8MHz,9600时BRR取833,实际约9603.84;115200时取69,实际约115942,误差需结合双方时钟预算判断。

发送一个字节的时间是整帧bit数/baud。9600、8N1约1.0417ms/字节,绝不是1/9600秒。传输层无效位和上层协议头都会降低有效载荷吞吐;程序队列再大也不能让物理线路无限加速。

第八章 · 第48节

TXE、TC、RXNE:寄存器空与线路发完不同

8.8.2收发/8.8.5寄存器;第153—160页

掌握要求:按DR和移位寄存器解释收发,正确区分标志与清除序列。

背景为什么要讲:原书一处说读SR清RXNE,按此写驱动会丢数据或反复中断。

发送:CPU/DMA写TDR→转入发送移位寄存器→TX逐位发送。TXE表示TDR能接下一字;TC表示发送移位过程结束且数据寄存器也空。等TXE可连续喂数据;换RS485方向、关串口/时钟前要确认TC等相应完成条件。

图8-19DMA完成只表示内存搬运完成,线路末位可能仍在发送。F103没有可编程的多字节UART FIFO。

接收:RX→接收移位→RDR→RXNE→CPU/DMA读DR,通常清RXNE。DR是同一访问地址映射不同读/写缓冲功能,不表示收发必须共用一个物理存储字而无法全双工。未及时读下一字节导致ORE;PE/FE/NE/IDLE等用规定序列处理,典型F1为先读SR再读DR清相关错误/空闲状态。不能仅仅读SR就声称数据取走了。

寄存器组SR、DR、BRR、CR1、CR2、CR3、GTPR;CR1管UE/TE/RE、字长、校验和部分IRQ,CR2管停止/同步/LIN,CR3管CTS/RTS、DMA、错误、半双工、IrDA/智能卡,GTPR用于相关保护时间/分频。不同标志的清除方法不是一个“都ClearFlag”口诀。

第八章 · 第49节

串口中断和DMA:处理来源与缓冲区所有权

8.8.3—8.8.4;第154—156页

掌握要求:识别共用USART IRQ里的来源;区分DMA完成与线路完成。

背景为什么要讲:连续数据不能每个字节都靠主循环慢慢等,但DMA也不是无需管理。

同一个USART的TXE、TC、RXNE、IDLE、CTS、LBD与PE/FE/NE/ORE等来源共用IRQ;ISR根据状态和对应使能分别处理。接收尽快读数据、记录错误/排队;发送队列空时关闭TXEIE,否则TXE一直为1会反复进入。使用中断发送通常不必每字节等TC。

DMA发送是RAM→DR,接收是DR→RAM;外设地址固定、RAM通常递增,设宽度、方向、长度、通道与优先级,再启外设DMA请求。USART1在本系列常用DMA1_CH4发送、CH5接收,通道映射与冲突必须查表,不是任意外设同时用同一DMA通道都能独立工作。

DMA的TC表示内存搬运结束,最后一字可能仍在移位器;线发完看USART_TC。接收定长、循环DMA和IDLE分包各有缓冲切换与边界问题;不能一边DMA写同一字节一边主循环无协议地读改它。出现溢出要计数/报告,不能偷偷覆盖当作可靠接收。

第八章 · 第50节

题54的关键:F103没有那种多字节硬件FIFO

习题8.54;8.8数据路径;第154、188页

掌握要求:区分硬件FIFO、单数据缓冲与软件环形队列;能实现中断收发与满/空处理。

背景为什么要讲:原题含通用UART硬件假设,不能为了迎合题目捏造F103 FIFO寄存器。

F103普通USART有移位器和数据缓冲,没有16550一类可配置深度的硬件RX/TX FIFO。对带FIFO的UART,ISR可在硬件有数据时循环读、可写时循环填;对F103,同一抽象通常一次只能读到当前就绪的数据。软件环形队列可缓冲CPU消费速度变化,但不能防止硬件来不及服务时的ORE。

图8-20用8格小模型:head指下一个写位置,tail指下一个读位置,留一格区分满空,最多存7字节;源码为128格/127字节。

软件队列用head/tail和容量N,保留一格判空/满时有效容量N−1;生产者推进head,消费者推进tail。接收满采用“丢新并计数”,不覆旧;发送满让调用者知道未入队,待之后重试。ISR不能因为队列满一直等主循环腾空间。

配套uart_irq.c/.h给F103中断驱动完整应用实现:RX/TX独立队列、错误/丢字节计数、发送队列空时关闭TXEIE,主循环提交发送与开启TXEIE放在保存/恢复PRIMASK的临界区,避免竞态。适用范围是单主循环调用者+USART ISR;多任务并发需额外锁/所有权设计。若考试要求真正利用FIFO,另给通用伪代码模板并明确应换用带FIFO的目标。

第八章 · 第51节

串口例子:发字符串、printf重定向与回显

8.8.5—8.8.7;第156—167页;习题8.52

掌握要求:能配置PA9/10和USART1,发送字符串至终止符;知道printf不是硬件自动支持。

背景为什么要讲:原例重复代码多,用初始化/发送/接收/适配四个部分串起来。

PA9为TX配AF_PP,PA10为RX配合适输入;开GPIOA与USART1时钟,先填USART_StructInit,再设置BaudRate、WordLength、StopBits、Parity、Mode、HardwareFlowControl并Init/Cmd。同步参数单独由USART_ClockInitTypeDef等接口处理,原书把同步字段全塞进同一个USART_InitTypeDef不准确;GPIO_PinAFConfig也不是F1 SPL的通用配置入口。

最小发送函数先等TXE,写DR;循环发送字符直到C字符串0,最后若需要再等TC。第52题发送you are welcome,不把终止0也发送,是否换行由题设另定。查询回显先判断RXNE再读,处理错误,再送回。

printf需要工具链的输出重定向,例如某些Arm库的fputc、某些GCC配置的_write;MicroLIB是工具库选择,不能说勾选一次就适用于所有编译器。原例先清TC又在第一次发送前等TC,可能永远等不到;连续发送宜按TXE。实际USB转串口也有3.3V/5V/RS232不同电平版本,TX↔RX并共地,不能只按芯片名字判断模块电平。

第八章 · 第52节

两台MCU通信:先规定协议与结束,再谈代码

习题8.53;原USART连接与回显例;第160—167、187页

掌握要求:会画交叉接线,明确缓冲、帧界、长度、校验/错误策略与结束条件。

背景为什么要讲:没有协议,单独能收一个字节不等于已经完成双机系统。

图8-21TX交叉接RX并共地,两节点运行同一协议。ACK不再回复ACK;示例有3秒截止,不承诺任意异步启动都能成功。

同电平两板TX_A→RX_B、TX_B→RX_A,GND连接,双方波特率/数据格式一致。电源不要未经确认直接互连;RS232需MAX3232等电平转换,不能直连MCU逻辑脚。

本章给一个明确的教学协议:ASCII行,以\n结束、\r忽略、最长63字节;双方启动各发一次HELLO,收到HELLO仅回复ACK,收到ACK不再回复。收到对方HELLO且自己的请求得到ACK后,该端完成;3秒未完成报告超时,不无限重发造成回环。每端用独立RX/TX环形缓冲,溢出或过长帧丢弃到下一换行并计错。

这没有额外CRC/重传,适合受控短线教学实验;若需求包含更强抗错,扩展为长度+序号+CRC+确认/超时重试,并说明重复包处理。不能把“收到任意ACK字样”用于复杂多请求系统。配套双机main与驱动完整提供,两端用同一个程序分别烧写即可。

第八章 · 第53节

SPI四根线与四种模式:CPHA不是永远上升沿

8.9.1;第167—169、177—178页;习题8.55

掌握要求:判主从信号方向、CPOL/CPHA与位序,算SCK。

背景为什么要讲:SPI没有统一设备命令格式,必须同时看从器件数据手册。

主机产生SCK;MOSI主出从入,MISO主入从出,NSS/CS通常低有效选设备。从机接收来自MOSI,发送到MISO,原章从模式接收文字写反要改。主机SCK=PCLK/2、4、8…256,F103常规上限按具体型号/电气条件约18MHz,不能用原文泛论100MHz作为F103保证。

图8-22画前4位,圆点为采样边沿;数据在线上应满足建立/保持时间,图是逻辑示意。主机MISO输入、MOSI输出。

CPOL定空闲电平;CPHA=0在第一个跳变边沿采样,CPHA=1在第二个边沿采样。模式0/1/2/3即(0,0)/(0,1)/(1,0)/(1,1),所以首沿可能上升也可能下降。位序、8/16位帧、片选保持时间、器件模式一起匹配。

双线全双工时每移一位同时收一位;主机要读必须发dummy产生时钟,主机只写也要消耗收到的垃圾字节以免OVR。单线半双工、双线只收是另一些模式,不能把全双工结论不加范围地套到所有配置。

第八章 · 第54节

SPI多从机、NSS与环形交换

8.9.1、图8.74;第168—171、178页;习题8.56—8.57

掌握要求:会画一主五从、说明三态与片选;分清两移位器回环与菊链。

背景为什么要讲:设备拓扑决定协议与GPIO数量,不是把所有CS短在一起就能独立寻址。

图8-23SCK/MOSI/MISO共享;独立低有效CS。未选从机必须释放MISO,不能把全部NSS并在一起。

一主五从可以共享SCK/MOSI/MISO,通常每从机单独CS,未选中者MISO须高阻(若器件不支持则隔离/改拓扑)。选一台、按它的模式/速度发完整事务、等待末位完成再释放。不靠把未选从机RXONLY设1来通用解决输出冲突,应按NSS和器件三态规则。

SSM=1时SSI提供SPI模块内部NSS状态,主模式通常保持合适高态以避免MODF;这不自动拉动板上五根CS。GPIO软件片选与硬件NSS输出是两种安排,硬件NSS不保证自动给每个字节生成符合任意器件要求的脉冲。

原图8.74的“环”是主从两个移位器闭合成数据交换回路:主MOSI到从输入、从MISO回主,一组时钟后双方位串交换。多从机菊链是器件支持下将前一输出接后一输入、一次移完整链;并非任意SPI Flash都支持菊链。

第八章 · 第55节

SPI状态、DMA与寄存器目录

8.9.1—8.9.2、8.9.4;第170—174、177页

掌握要求:会写发一字节且收一字节的过程,分清TXE、RXNE、BSY。

背景为什么要讲:SPI和USART名字相似,完成标志与清错顺序不能混用。

发前等TXE,写DR;等RXNE,读DR;事务收尾按模式确认TXE与BSY=0再释放CS/关外设。OVR常按先读DR再读SR清,MODF/CRCERR等各有规定序列;中断TXE/RXNE/ERR共用该SPI的IRQ。不能把BSY当软件可写的复位位。

CR1管方向、CRC、DFF、SSM/SSI、位序、SPE、BR、MSTR、CPOL/CPHA;CR2管IRQ/DMA与SSOE;SR状态;DR读/写不同缓冲;CRCPR与RXCRCR/TXCRCR管CRC。I2SCFGR/I2SPR为共享I²S相关配置,SPI的SCK分频在CR1.BR,不是I2SPR。

DMA TXE驱动RAM→DR,RXNE驱动DR→RAM,长全双工一般两方向同时管理,先准备接收避免丢首字。DMA_TC仍不代表末位已在线上完成,收尾要查SPI状态。原书SPI_InitTypeDef*未分配对象却用点号,是错误;应该定义实体结构体、StructInit后填参并传地址。

第八章 · 第56节

SPI Flash:指令、地址、页与擦除单位

8.9.3;第174—177页;W25Q64例

掌握要求:解释WREN、BUSY轮询、页边界与擦后读回验证;会算容量。

背景为什么要讲:不必背全指令表,但必须知道驱动不是“发一个字节就存好了”。

W25Q64的64Mbit=8MiB;256B页×32768页=8MiB,4KiB扇区=16页,32/64KiB块是其他擦除单位,原文“整片2MB”错误。常用事务包括读JEDEC ID、读设备ID/唤醒、读状态、写使能、读数据、页编程、扇区擦除、掉电;确切命令、地址宽度、电压与ID依后缀版本。

写前WREN,必要时擦除,发命令+地址+当前页剩余范围内的数据,拉高CS启动内部操作,再限时轮询BUSY直到空闲,读回比较。页编程不跨页,从页内偏移p起最多256−p字节;任意长写入需分块循环。Flash不能任意把0重新写成1,通常需相应擦除;页编程和扇区擦除的粒度不能互换。

图8-24每个事务都不跨页。Flash还需按器件规则擦除、写使能和等待忙;EEPROM每页写后等待内部写完成。

原例连接PA2做CS、PA5 SCK、PA6 MISO输入、PA7 MOSI,USART1做打印。软件拆main、spi_w25q64驱动、usart驱动;检测ID→擦指定测试扇区→读验证→写/读比较→输出结果→可选掉电。测试只用明确的空白测试区域,不默认整片已有内容可擦。原书没有展开完整器件驱动,本讲义补出分页算法和事务责任,命令时序仍查所用器件手册。

第八章 · 第57节

I²C两根线:开漏、上拉、地址与第九位ACK

8.9.5、8.9.8;第178—180、185—186页;习题8.58、8.60

掌握要求:会画开始/停止、7位地址+R/W、ACK/NACK和重复开始。

背景为什么要讲:它比SPI少片选线,但并非少了一切协议管理。

SCL/SDA采用开漏并合适上拉,空闲高;SCL高时SDA下降为START、上升为STOP,数据在时钟低时改变、高时稳定,按MSB先发。8数据位后第9时钟由接收方ACK拉低或NACK释放。主/从指控制权与地址角色,发/收指数据方向,不是主机永远只发。

图8-25固定7位地址0x50。线上地址字节按MSB先发,第8位R/W,第9个时钟由接收方ACK/NACK;SDA是开漏共享线。

7位地址A在线上形成(A<<1)|R/W。AT24C02 A2:A0=000时地址0x50,写地址字节0xA0、读0xA1。不是把0xA0再当7位地址左移一次。SPL Send7bitAddress接口使用的地址参数约定要看实现,通常传左移后的地址并由Direction定最低位。

10位寻址、General Call、SMBus/PMBus、PEC、仲裁等认识用途即可。不是所有7位地址都按“厂商4位+用户3位”,这是某类器件的安排。接收者也可能NACK,不保证从机永远必须ACK;多主机通过仲裁决定本次事务归属,从机可按支持能力时钟拉伸。

第八章 · 第58节

I²C控制器时钟、四种角色和寄存器

8.9.5;第178—180页

掌握要求:区分PCLK输入与SCL,认识主发/主收/从发/从收。

背景为什么要讲:I²C硬件时序参数不同于USART的BRR。

F103 I²C挂APB1,CR2.FREQ描述PCLK1的MHz数,CCR/TRISE等配置总线时钟及上升时间;标准100k、快速400k都需满足输入时钟/电气限制。经典标准模式公式常为SCL≈PCLK1/(2×CCR),快速模式依DUTY采用不同高低比,不能把I2C_DutyCycle_2误认为整个总线固定50%。

生成START后可成为主模式,SR2.MSL/TRA等反映角色与方向。主发送写从设备,主接收读从设备;从发送回应主读,从接收回应主写。DR是数据,CR1管PE/START/STOP/ACK/POS等,CR2管输入频率与IRQ/DMA,OAR1/2本机地址,SR1/SR2状态,CCR时序,TRISE上升时间。

自己的OAR地址和要访问的EEPROM地址是不同对象。RX/TX数据缓冲、移位与时序控制由硬件协作,不要手动在SDA高低间模拟同时又启着硬件驱动。模拟I²C可作为独立方案,但要满足相同开漏/采样/超时规范。

第八章 · 第59节

I²C事件序列:发完地址,还没发完数据

8.9.5主发送、事件与中断;第179—181页

掌握要求:读懂EV5/6/8,知道结束读数据要提前安排NACK/STOP。

背景为什么要讲:F1硬件I²C接收尾部有特殊顺序,不能用一条通用while糊过去。

主发送:START→SB/EV5→写地址→ADDR/EV6→按先读SR1再读SR2清ADDR→按TXE写数据→最后确认BTF等条件→STOP。ADDR匹配不是数据全部传完;TXE说明DR空,BTF才涉及字节传输完成条件。10位地址另有ADD10/EV9及规定的头部/地址阶段。

主接收常需“先写寄存器地址→Repeated START→读地址→接收数据”,最后一个字节由主机NACK并STOP。F1在1字节、2字节、多字节收尾时,ACK/POS、清ADDR、BTF/DR读取与STOP的顺序不同;不建议凭通用I2C_CheckEvent循环臆造万能接收函数。具体采用官方驱动/应用说明并处理勘误。

事件IRQ I2Cx_EV集中SB/ADDR/ADD10/STOPF/BTF与RXNE/TXE,相关使能ITEVTEN/ITBUFEN;错误IRQ I2Cx_ER集中BERR/ARLO/AF/OVR/PECERR/TIMEOUT/SMBALERT,受ITERREN等控制。原文ITEVFEN等拼写需核对为实际位名。查事件有时会读SR2并清ADDR,调试器盯寄存器也可能影响状态序列。

第八章 · 第60节

I²C DMA、超时、仲裁与总线恢复

8.9.5 DMA、8.9.8要点;第181—182、185—186页

掌握要求:能说明DMA仍需协议管理,错误时不会永远阻塞。

背景为什么要讲:读写存储器的可靠性很大部分来自失败路径,而非只看成功演示。

DMA控制器配置DR地址、RAM地址、方向、长度、宽度、优先级与通道,I²C再开DMAEN等请求;LAST/ACK/STOP与DMA尾部时序需按接收场景配合。DMA完成只说明搬运计数结束,不自动代表总线产生了正确STOP或外部器件内部写周期结束。

AF可能表示地址/数据NACK,ARLO表示仲裁丢失,BERR表示异常总线条件,OVR意味着数据不及时,TIMEOUT等提供相应错误信息。每个等待要有截止时间;区分器件忙的NACK与真实不存在/断线;失败后按状态停止/复位外设、恢复引脚与协议,不能遇到多主机ARLO就强拉线干扰获胜者。

单主机总线卡住时,可按器件支持与厂商建议进行时钟脉冲/STOP恢复、外设复位等,但不能盲目推挽拉高与别的设备争用。I²C中断不必一律设全系统最高优先级;应按最坏延迟与整机实时任务安排。短线、上拉、总线电容、电平、勘误同样关键。

第八章 · 第61节

I²C库函数与参数:实体结构体,不是空指针

8.9.6;第182—183页

掌握要求:会找到初始化、起止、地址、数据、事件、ACK与DMA接口。

背景为什么要讲:函数名不难,真正容易错的是地址约定和硬件序列。

I2C_InitTypeDef包含ClockSpeed、Mode、DutyCycle、OwnAddress1、Ack、AcknowledgedAddress等;ClockSpeed是频率整数量,不能把结构定义写成uint16_t I2C_InitStructure.I2C_ClockSpeed。应定义实体I2C_InitTypeDef cfg;,StructInit后填值,I2C_Init(I2C1,&cfg);不能声明未指向对象的cfg指针却用点号写成员。

接口按动作分:DeInit/StructInit/Init/Cmd;GenerateSTART/STOP;Send7bitAddress/SendData/ReceiveData;CheckEvent/GetLastEvent/GetFlagStatus;AcknowledgeConfig;ITConfig/GetITStatus/ClearITPendingBit;DMACmd等。原文DcInit、Send7bitaddress大小写是需要核对的笔误。

ClearFlag并不允许对任何状态任意清,ADDR/STOPF等有规定读写序列;操作函数的返回值与事件掩码不保证整个事务结束。HAL/LL函数命名与状态机另有约定,本章SPL名字主要为读原讲义,不直接混写成一个工程。

第八章 · 第62节

AT24C02:256字节、8字节页、写后ACK轮询

8.9.7;第183—185页

掌握要求:会计算页内剩余、分段写,解释随机读与写周期等待。

背景为什么要讲:这是I²C例子将总线协议连接到真实器件存储规则。

AT24C02类器件2Kbit=256B,典型8B页,共32页;A2/A1/A0选择低地址位,WP控制写保护,版本的电压/速度/页长以实物数据手册为准。PB6/PB7配置I²C1_AF_OD与上拉,USART1另外负责输出,不把芯片内部存储地址和0x50设备地址混用。

字节写:START→设备写地址→字节存储地址→数据→STOP,随后内部写周期,器件可能NACK。页写重复数据但不能越过该8B页;跨页应分块并在每块后限时ACK轮询。随机读通常先写存储地址,再Repeated START切读,最后一字节NACK+STOP。

例如从地址6写6字节,应分2+4,而不是一次跨过页边界让设备回卷覆盖。源码包给通用分页拆分函数;I2C_EE_Init、BufferRead、ByteWrite、PageWrite、WaitEepromStandbyState、BufferWrite分别承担配置/读/字节/页/等待/长写职责。原章流程为写→等内部完成→读回→比较→串口报告;仅收到总线ACK不等于数据已正确存入。

第八章 · 第63节

从长表里抽出做题流程,再保留完整查读目录

8.1—8.9全章寄存器/函数表与小结

掌握要求:能把任何外设程序拆为时钟、引脚、参数、数据、状态、IRQ/DMA、收尾。

背景为什么要讲:原章长达188页,很多篇幅重复列相似函数;合并规则能减少记忆但不丢目录。

写程序固定检查:目标型号有该模块/引脚→开相应总线时钟→配置GPIO/重映射→完整填写结构或位域→清旧标志→启外设/请求/NVIC→读写并处理状态→超时/溢出/错误→安全结束。没有外部引脚的定时器时基不必为了流程完整凭空配置某个GPIO。

寄存器先判R/W、只读、写1清、写0清、读副作用和保留位,再选赋值或读改写。BSRR、EXTI_PR、NVIC的置/清命令寄存器不能和普通配置寄存器使用同一写法。函数StructInit仅填RAM结构,Init才操作硬件;Cmd只开功能,RCC时钟也不是同一个开关。

文末API与寄存器查读索引覆盖原章各外设函数组、初始化结构及控制/状态寄存器,不要求全默写。冷门LIN、智能卡、IrDA、CRC、SMBus、编码器、霍尔、死区、RTOS与旧调试工具都已说明用途;需要深入实作时再查相应手册,而非从本章例子想当然拼代码。

第八章 · 第64节

全章过关:八类能力与源码使用条件

全部正文与习题8.1—8.60;第3—188页

掌握要求:独立解释系统、画电路、算时钟、写外设代码,并检查运行边界。

背景为什么要讲:这是全书最后一章,把前七章的存储、总线、指令、异常与C程序接到实物。

过关不是背188页。你应能:画最小系统;说明引导与软件层;算时钟树;配置GPIO与复用;算TIM/PWM并读捕获;建立NVIC/EXTI;分析UART/SPI/I²C波形和缓冲;写完整应用并检查初值、共享数据与超时。

配套应用以F103RC高密度官方SPL/CMSIS工程模板为环境,应用源文件完整提供,不把第三方启动/库文件重新捆绑成声称已经实板运行的工程。main显式选HSI8MHz,TIM与USART参数同步;同题多个实现、不同外设示例分别构建,避免PA9既接绿灯又作为USART_TX。

第28题给寄存器/SPL两版;32题给TIM2_CH2捕获;36/49给自动/按键红绿灯;52给字符串发送;53给双机握手;54给可复用软件环形缓冲+中断驱动。原Flash/EEPROM例补接口流程、页规则和可测试分页算法,具体器件完整驱动需按后缀数据手册实现。每个“可运行”判断都附依赖与验证范围,不用省略号代替题目核心逻辑。

建议分七次:系统与开发01—11;最小系统/时钟12—19;软件架构20—23;GPIO24—28;TIM29—39;中断40—45;通信46—62,最后做全部60题和自测。接下来的网站目录按章节保留,可随时回第六/七章补寻址、条件、ABI与栈。

原书逐项对照

所有小节、实例与小结的去处

原书覆盖主题讲解位置
8.1.1—8.1.4概述、生态、交叉环境、PCB/PLD/软件工具、五种调试、JTAG/SWD、开发流程、手环与测试01、02、03、04、05、06、07、08
8.2.1—8.2.3S3C2440A、LPC2132与PINSEL、STM32F103资源与结构09、10、11
8.3.1—8.3.3最小系统一般模型、S3C2440实例、F103供电/低功耗/复位/BOOT/时钟/RCC/启动12、13、14、15、16、17、18、19
8.4.1—8.4.4软件层次、引导阶段、OS、开发模式/流程、SPL目录与命名20、21、22、23
8.5.1—8.5.4GPIO特点、内部结构、八种模式、CRL/CRH/IDR/ODR/BSRR、复用/重映射、API和LED/按键例24、25、26、27、28
8.6.1—8.6.8TIM总览、基本/通用/高级、计数/捕获/PWM输入输出、时钟同步、寄存器/API、例程、SysTick/看门狗与小结29、30、31、32、33、34、35、36、37、38、39
8.7.1—8.7.5NVIC、优先级、异常帧、EXTI、接口与共享入口、按键/红绿灯及小结40、41、42、43、44、45
8.8.1—8.8.7USART帧、波特率/BRR、收发通路、中断、DMA、寄存器/API、字符串/printf/双机/环形队列与小结46、47、48、49、50、51、52
8.9.1—8.9.4SPI原理、模式、主从/多从、接口/中断/DMA、W25Q64例程与小结53、54、55、56
8.9.5—8.9.8I²C原理、电气/地址/时序、寄存器/事件/DMA/接口、AT24C02与小结57、58、59、60、61、62
8.1—8.60全部原题、计算条件、代码文件与边界逐题 →

工具截图改为职责解释;寄存器位图改为字段规则与查阅目录;重复库调用按功能归纳。旧芯片、冷门模式、OS与测试背景均保留用途和深度,不以考试低频为由删除。

会用的背规则,少用的知道去哪查

寄存器与标准外设库索引

这张表把原章逐项罗列的接口按职责重新放在一起。不要求默写所有函数原型;要求知道初始化、配置、使能、读写、状态、中断/DMA各由谁完成。下面采用STM32F10x SPL常见命名,确切参数、通道、有效位和芯片适用性查匹配版本头文件。库函数是寄存器操作的封装,不自动完成整套系统接线和时钟规划。

模块 寄存器与字段地图 SPL / CMSIS接口职责
RCC CR源就绪;CFGR系统源/预分频/PLL;APB1ENR/APB2ENR/AHBENR外设时钟;复位/备用域/状态 RCC_HSEConfig、RCC_WaitForHSEStartUp、RCC_PLLConfig/Cmd、RCC_SYSCLKConfig、RCC_GetSYSCLKSource、RCC_HCLKConfig、RCC_PCLK1Config/PCLK2Config、RCC_ADCCLKConfig、RCC_USBCLKConfig、RCC_GetClocksFreq;APB/AHB时钟和外设复位命令
PWR/BKP CR/CSR电源控制、PVD、备份访问;BKP_DRx保存数据 PWR_BackupAccessCmd、PWR_PVDLevelConfig/PVDCmd、PWR_EnterSTOPMode/STANDBYMode、PWR_GetFlagStatus/ClearFlag;BKP_ReadBackupRegister/WriteBackupRegister。Sleep入口用CMSIS WFI/WFE等表达,不虚构SPL函数
GPIO/AFIO CRL/CRH模式,IDR真实引脚,ODR锁存,BSRR/BRR写命令,LCKR锁定;AFIO_MAPR/EXTICR GPIO_DeInit/AFIODeInit、GPIO_Init/StructInit、ReadInputDataBit/Data、ReadOutputDataBit/Data、SetBits/ResetBits/WriteBit/Write、PinLockConfig、PinRemapConfig、EXTILineConfig、EventOutputConfig/Cmd、ETH_MediaInterfaceConfig(仅适用器件,不能据此给F103RC增加以太网)
TIM时基/计数 CR1/CR2、SMCR、DIER、SR、EGR、CNT、PSC、ARR;RCR仅高级 TIM_DeInit、TimeBaseInit/StructInit、Cmd、ITConfig、DMAConfig/DMACmd、InternalClockConfig、ITRxExternalClockConfig、TIxExternalClockConfig、ETRClockMode1Config/2Config、ETRConfig、PrescalerConfig、CounterModeConfig、SetCounter/GetCounter、SetAutoreload、GetPrescaler、ARRPreloadConfig
TIM捕获 CCMR1/2选择/滤波/分频,CCER极性/使能,CCR1—4捕获锁存,SR过捕获 TIM_ICInit/ICStructInit、PWMIConfig、SetIC1Prescaler…SetIC4Prescaler、GetCapture1…GetCapture4、SelectInputTrigger、SelectSlaveMode、SelectMasterSlaveMode、SelectHallSensor、EncoderInterfaceConfig
TIM比较/PWM CCR1—4比较,CCMRx的OCxM/OCxPE,CCER输出极性/使能;BDTR死区/刹车/MOE(高级) TIM_OC1Init…OC4Init、OCStructInit、OC1PreloadConfig…OC4PreloadConfig、OC1FastConfig…OC4FastConfig、OC1PolarityConfig…OC4PolarityConfig、SetCompare1…SetCompare4、CCxCmd、CCxNCmd、SelectOCxM、BDTRConfig/StructInit、CtrlPWMOutputs、OCIdleStateConfig相关参数
TIM事件/同步 EGR事件触发,CR2的MMS,SMCR的TS/SMS/MSM,SR状态,DIER中断/DMA TIM_GenerateEvent、SelectOutputTrigger、SelectOnePulseMode、UpdateDisableConfig、UpdateRequestConfig、SelectCOM、CCPreloadControl、ClearOC1Ref…ClearOC4Ref、GetFlagStatus/ClearFlag、GetITStatus/ClearITPendingBit;COM/互补/重复计数按高级定时器边界使用
SysTick/看门狗 SysTick CTRL/LOAD/VAL/CALIB;IWDG KR/PR/RLR/SR;WWDG CR/CFR/SR SysTick_Config、SysTick_CLKSourceConfig;IWDG_WriteAccessCmd、SetPrescaler/SetReload、ReloadCounter/Enable;WWDG_SetPrescaler/SetWindowValue/Enable/SetCounter、EnableIT/GetFlagStatus/ClearFlag。不要把喂狗写成无条件掩盖死机
NVIC/SCB ISER/ICER、ISPR/ICPR、IPR;SCB AIRCR/VTOR/SHPR;PRIMASK等屏蔽 SPL NVIC_PriorityGroupConfig/NVIC_Init/NVIC_SetVectorTable/NVIC_SystemLPConfig;CMSIS NVIC_SetPriorityGrouping、EncodePriority、SetPriority、EnableIRQ/DisableIRQ、SetPendingIRQ/ClearPendingIRQ、GetPendingIRQ;__get_PRIMASK/__disable_irq/__set_PRIMASK等。分组常量与原始PRIGROUP编码不可混传
EXTI IMR中断、EMR事件、RTSR/FTSR边沿、SWIER软件触发、PR挂起 EXTI_DeInit、EXTI_Init/StructInit、GenerateSWInterrupt、GetFlagStatus/ClearFlag、GetITStatus/ClearITPendingBit;GPIO来源另用GPIO_EXTILineConfig配置AFIO
USART SR、DR、BRR、CR1/2/3、GTPR;UE/TE/RE/M/PCE/PS/STOP;TXE/TC/RXNE/PE/FE/NE/ORE/IDLE USART_DeInit、Init/StructInit、ClockInit/ClockStructInit、Cmd、SendData/ReceiveData、ITConfig/DMACmd、GetFlagStatus/ClearFlag、GetITStatus/ClearITPendingBit;SendBreak、SetAddress、WakeUpConfig/ReceiverWakeUpCmd、LINBreakDetectLengthConfig/LINCmd、HalfDuplexCmd、SetGuardTime/Prescaler、SmartCardCmd/NACKCmd、IrDAConfig/Cmd。特殊模式认识用途即可,清标志仍按SR/DR等专门顺序
SPI CR1的BR/CPOL/CPHA/MSTR/SPE/SSI/SSM/DFF/LSBFIRST/BIDIMODE/RXONLY;CR2中断/DMA/SSOE;SR TXE/RXNE/BSY/OVR/MODF/CRCERR;DR/CRCPR/RXCRCR/TXCRCR SPI_I2S_DeInit、SPI_Init/StructInit、SPI_Cmd、SPI_I2S_ITConfig/DMACmd/SendData/ReceiveData、SPI_DataSizeConfig、BiDirectionalLineConfig、NSSInternalSoftwareConfig、SSOutputCmd、TransmitCRC/CalculateCRC/GetCRC/GetCRCPolynomial、SPI_I2S_GetFlagStatus/ClearFlag/GetITStatus/ClearITPendingBit;I²S初始化接口不等同普通SPI分频
I²C CR1 START/STOP/ACK/POS/PE/SWRST;CR2 FREQ/ITEVTEN/ITBUFEN/ITERREN/DMAEN/LAST;OAR1/2、DR、SR1/2、CCR、TRISE I2C_DeInit、Init/StructInit、Cmd、DMACmd/DMALastTransferCmd、GenerateSTART/STOP、AcknowledgeConfig、OwnAddress2Config/DualAddressCmd、GeneralCallCmd、ITConfig、SendData/ReceiveData、Send7bitAddress、ReadRegister、SoftwareResetCmd、NACKPositionConfig、SMBusAlertConfig、TransmitPEC/PECPositionConfig/CalculatePEC/GetPEC、ARPCmd、StretchClockCmd、FastModeDutyCycleConfig、GetLastEvent/CheckEvent、GetFlagStatus/ClearFlag、GetITStatus/ClearITPendingBit

冷门接口为什么还要提

USART同步时钟、LIN、半双工、智能卡、IrDA,以及I²C的SMBus/PEC/ARP,都是原书接口目录的一部分:知道它们属于什么协议/能力,避免把USART当成只会8N1、把I²C当成只会EEPROM;目前做基础GPIO、红绿灯和串口题不需要掌握完整协议栈。TIM编码器、霍尔、单脉冲、互补/刹车/死区与同步则在电机和测控中有用途,原理和模式边界必须认识,不要求把每种组合写一遍。

查库时必须保留的判断

同名GetFlagStatus和GetITStatus不总是等价:后者往往还检查使能位。ClearFlag也不意味着所有标志都能直接写0清。USART的错误/IDLE、SPI的OVR、I²C的ADDR等各有专门读写顺序。先查寄存器语义,再选库调用;绝不把全部状态统一做按位或/按位与。

原书表格列出了多种产品共有的库接口;例如ETH媒体接口、部分高级TIM或特殊串口能力,不代表本章选用的F103RC一定具备。这里把名称作为查阅入口,具体适用范围由设备头与数据手册限定。计数/捕获/PWM、IRQ与DMA的完整配置顺序在对应正文中说明。

先算,再展开

16道综合短测

  1. 复位默认HSI和模板配置后的72MHz是什么关系?
  2. BOOT0/1怎样选择SRAM?向量首两个字分别是什么?
  3. HCLK72MHz、APB1除2时,TIM2和USART2各拿到多少时钟?
  4. PC6的CRL字段在哪?50MHz普通推挽是什么4位编码?
  5. 开漏写1,IDR还能是0吗?为什么?
  6. TIM时钟72MHz,PSC35999、ARR999,更新周期多少?
  7. ARR49/CCR47的PWM1高有效占空比多少?低有效LED亮时间占比多少?
  8. 1MHz捕获,前65000、后464、ARR65535且间隔小于一圈,周期/频率多少?
  9. PWM输入测得周期2000tick、高500tick,D是多少?要不要统一加1?
  10. 同抢占级、不同子级能嵌套吗?F103实现几位优先级?
  11. PC5与PA5能同时独立接入EXTI5吗?清PR为什么不能|=?
  12. UART 0x53、8数据+偶校验+1停止共有几bit,P是多少,F103选几位字长?
  13. DMA_TX完成就能关USART/SPI吗?各需再查什么?
  14. SPI模式2在哪个边沿采样?主机MISO是什么方向?
  15. I²C地址0x50的读/写线上字节是多少?AT24C02从6写6字节怎样分页?
  16. F103的“硬件FIFO”和软件环形缓冲有什么区别?127字节队列满后应怎样处理?
展开答案与过关要求
  1. 硬件先HSI;启动/应用成功配置后才可72MHz,必须检查源与分频。
  2. BOOT0=1、BOOT1=1;首字MSP、第二字Reset入口指针。
  3. PCLK1=36MHz;TIM2因APB分频非1而72MHz,USART2仍36MHz。
  4. CRL bit27:24,0011;不影响其他脚,先清掩码再写字段。
  5. 可以,1表示释放,外部设备可拉低;ODR与IDR含义不同。
  6. (36000×1000)/72000000=0.5秒。
  7. 47/50=94%高,低有效点亮时间约6%。
  8. (464−65000) mod65536=1000tick,1ms、1000Hz;多圈需另计。
  9. 25%;捕获间隔不统一加1,和ARR周期来源不同。
  10. 不因子级发生嵌套;4位,共16编码,分组分配抢占/子位。
  11. 不可,来源选一个;PR写1清,|=回读可能把别线pending一并清除。
  12. 11bit,0x53有4个1故偶P=0;9位字长保留8有效数据位。
  13. 不可以;USART查TC,SPI按模式查TXE与BSY=0等收尾条件。
  14. CPOL1/CPHA0,首沿是下降沿并采样;主MISO输入。
  15. 写0xA0、读0xA1;AT24C02拆2+4并逐页等待内部写完成。
  16. F103无多字节可配置硬件FIFO;软件队列在RAM、靠ISR及时搬运,不能防止硬件ORE;RX满丢新计数或按明确协议处理,TX满向调用者报告重试,ISR不阻塞等主循环。

过关:时钟、字段、公式与波形能独立算,程序题能补齐依赖、入口与边界;背景名称会解释用途即可,不要求从头移植OS或复刻所有冷门外设模式。

第186—188页全部60题

原题答题卡与完整应用文件

原题保留,答案按F103RC与明确时钟条件核对。应用程序与共用模块在本页、源码ZIP中都可查看;官方设备头、库和启动文件需从匹配的SPL模板提供。

习题8.1 / 系统组成

一个典型的嵌入式系统有哪些部分?请简述之;其外围硬件又有哪些?例举几个实例。

展开解题过程与代码

硬件是处理器/存储/总线与外围,软件是引导、驱动、可选OS/中间件和应用。外围输入有按键、ADC/传感器,输出有显示、PWM/DAC/执行器驱动,通信有串口/SPI/I²C/CAN/USB等,支撑有电源、复位、时钟、调试与外扩存储。按图7类用途举例,不只答“CPU+软件”。

回到第01节完整讲解 →

习题8.2 / 生态系统

嵌入式生态系统有何特点?会有何好处?请阐述个人的理解。

展开解题过程与代码

硬件、软件、文档及合作方参考方案组成配套环境,减少适配/验证成本、提高复用。例:开发板+ST-LINK、CMSIS/SPL/Cube组件、数据手册/参考手册/勘误。收益以版本和目标匹配为前提,不是所有示例都能无改动复用。

回到第02节完整讲解 →

习题8.3 / 交叉环境

嵌入式的开发与通用计算机系统相比,有何独特之处?交叉开发环境的主要组成部分和 特点是什么?

展开解题过程与代码

宿主机运行编辑/构建/调试工具,目标机运行生成的目标代码,连接负责下载与状态交换。与桌面应用相比,需考虑硬件资源/电平/时钟/存储布局/实时与固化;交叉是主机/目标环境不同,不等于“必须Linux”。

回到第03节完整讲解 →

习题8.4 / 开发工具

常用的嵌入式系统开发硬件和软件工具有哪些?请简述一些实例。

展开解题过程与代码

硬件:原理图/PCB、仿真、PLD/FPGA、仪器与探针;软件:编辑器、交叉编译/汇编、链接/定位、库、调试、下载。原书例有Protel/OrCAD、Quartus/ISE、UltraEdit/Source Insight、MDK/IAR/GNU;说清用途比抄品牌堆砌更重要。

回到第04节完整讲解 →

习题8.5 / 交叉调试

嵌入式系统的交叉调试工具有哪些特点?一般有那几种?

展开解题过程与代码

调试器通常在主机、被调程序在目标;可单步/断点/寄存器内存/调用栈/跟踪。类别:软件模拟、ROM监控、ROM仿真、在线仿真、片上调试。比较真实硬件依赖、资源占用、时序准确与成本;不宣称任何探针都完全不影响目标。

回到第05节完整讲解 →

习题8.6 / 开发五阶段

简述嵌入式系统的开发过程一般有哪几步,都完成什么样的工作。

展开解题过程与代码

需求分析→规格;系统设计→架构、软硬件划分、器件/模块/接口;实现→电路/PCB/代码;测试→按功能与性能/边界验证;发布→固化映像、版本、说明、维护升级。手环可依次落到取样/按键/显示、总线资源、驱动和计步、精度/功耗测试。

回到第06节完整讲解 →

习题8.7 / 引脚复用

何为引脚功能复用?有何意义?如何实现,请以 STM32F103 为例,举例具体说明。

展开解题过程与代码

同一物理脚可在GPIO与若干外设信号间选择,节省引脚并改善布线;不能同一时刻争驱动。F103 USART1_TX用PA9需GPIO_AF_PP、USART1时钟/配置;支持的重映射可改PB6,另需AFIO与MAPR。复用模式和重映射是两步。

回到第27节完整讲解 →

习题8.8 / 芯片架构

STM32F103 微处理器架构分为几部分,各部分功能为何?请简要说明。

展开解题过程与代码

按核与总线、存储、片上功能模块三层描述;原书系统互连具体列I-Code、D-Code、系统总线、DMA总线、总线矩阵、AHB-APB桥。核执行、Flash/SRAM存程序数据,DMA搬运,桥连接GPIO/TIM/USART等;电源/时钟/复位和NVIC也要交代。

回到第11节完整讲解 →

习题8.9 / 通用最小系统

简述微处理器最小硬件系统的内涵,并画出一个典型最小系统的结构示意图。

展开解题过程与代码

处理器+必需程序/数据存储+供电/去耦+时钟+复位+下载调试;GPIO/外设作为应用接口。参看第12节SVG,画出各块到MCU的连接。片内已有Flash/RAM就不一定再外扩;最少元件不是省去必需电源脚。

回到第12节完整讲解 →

习题8.10 / F103最小系统

STM32F103 最小硬件系统由哪几部分构成?

展开解题过程与代码

芯片、电源及模拟/后备供电、时钟、NRST、BOOT选择、SWD/JTAG。内部RC可先运行,外接HSE/LSE按精度/RTC需求选择;Flash/SRAM片内有。所有VDD/VSS/VDDA/VSSA/VBAT按封装连好,不只画一个抽象3.3V。

回到第12节完整讲解 →

习题8.11 / 最小系统设计

请设计一个 STM32 最小系统。

展开解题过程与代码

选STM32F103RCT6/LQFP64:3.3V接全部VDD、VDDA,地接VSS/VSSA,按数据手册逐脚去耦,VBAT无电池时接主电源;BOOT0下拉并可选系统/SRAM,NRST按键拉低和合适电容;PA13/14、GND、VTref、NRST接SWD;需要精确72MHz再配8MHz HSE晶体与计算的负载电容。第12节给结构图,第13—19节给细节。这是功能级设计,PCB须用该封装引脚表、电气条件和器件值复核。

回到第12节完整讲解 →

习题8.12 / 复位

STM32F103 的复位电路有何功能?常见的复位方式有哪些?

展开解题过程与代码

保证电源/时序未稳定或故障时处于确定初态,重新从复位入口启动。F103分系统、电源、备份域复位;来源含NRST、看门狗、软件、POR/PDR、待机退出及相应低功耗选项等。RCC_CSR标志用于识别,不把所有复位都说成全部RAM清零。

回到第15节完整讲解 →

习题8.13 / BOOT组合

STM32 的启动模式有几种?BOOT 引脚有几个?分别如何设置?

展开解题过程与代码

BOOT0=0、BOOT1任意→用户Flash;BOOT0=1、BOOT1=0→系统存储器Bootloader;BOOT0=1、BOOT1=1→SRAM。两个启动控制输入,BOOT1通常复用PB2;原表SRAM的BOOT0=0是笔误。

回到第15节完整讲解 →

习题8.14 / 时钟源

STM32F103 的时钟源有哪几种产生方式?各有什么特点?实际应用中一般如何做以实现 系统的时钟驱动。

展开解题过程与代码

HSI内部约8MHzRC:快、少器件、精度较低;HSE外部高速晶体/时钟:可更准确,需电路;LSI内部低速RC:看门狗/RTC;LSE外部32.768kHz:精确RTC;PLL倍频已有源。硬件复位先用HSI,模板可再切HSE+PLL,不能把上电默认写72MHz。

回到第16节完整讲解 →

习题8.15 / 系统时钟路径

结合时钟树 STM32 的系统时钟是怎么产生的?

展开解题过程与代码

HSE8MHz→可选预分频→PLL选源→×9得72MHz→SW选择PLL→SYSCLK;AHB/1得HCLK72,APB1/2得PCLK1=36,APB2/1得PCLK2=72。配置时等就绪/锁定并读SWS确认,同时满足Flash等待周期和总线/ADC限制。

回到第17节完整讲解 →

习题8.16 / 时钟树设计理由

参照 STM32 的时钟树,请做以下思考,并解释 1)设计成这种形式的主要目的和理由为 何?2)从左至右,相关时钟依次可分为大致哪几种?3)时钟输出的使能有何意义,并 以一个实例(如 TIM)说明一下大体流程。

展开解题过程与代码

目的是灵活匹配速度/精度/功耗与各模块上限,支持备用源和时钟门控。由左到右可按源/系统/总线与外设时钟划分。TIM2例:选SYSCLK与AHB/APB1分频→按APB倍频算fTIM→APB1ENR开TIM2→PSC/ARR与UG→清标志→CEN;门控开关不是PSC本身。

回到第18节完整讲解 →

习题8.17 / 软件分层

简述嵌入式软件的分层体系结构及大概工作流程。

展开解题过程与代码

驱动处理板级/设备,OS提供资源与任务服务,中间件复用协议/文件/GUI,应用实现业务。流程是复位/板级→引导或升级→系统→应用对象初始化→运行;无OS时中间几层可简化,并非强制都有。

回到第20节完整讲解 →

习题8.18 / 引导加载

简述嵌入式软件系统引导和加载过程。

展开解题过程与代码

Bootloader准备硬件和存储布局、选择正常启动/下载升级,映像可从Flash/外存加载RAM或在Flash执行。复杂平台stage1做最小CPU/RAM/栈并转stage2,stage2准备设备、映像/参数并跳内核。F103小程序不强制U-Boot/Linux两阶段。

回到第20节完整讲解 →

习题8.19 / 嵌入式软件特点

相比一般软件,嵌入式软件有哪些自己独有的特点?

展开解题过程与代码

硬件耦合、资源受限、常需固化/交叉开发、强调实时/可靠/功耗、需控制代码大小并便于维护。不是所有嵌入式软件都硬实时、都在ROM执行或都用OS,答“特点”要有适用条件。

回到第20节完整讲解 →

习题8.20 / OS功能

嵌入式操作系统有哪些工作特点?完成哪些主要功能?以实例略述自己的理解。

展开解题过程与代码

管理任务、调度、内存、外设与时间,可配同步/文件/网络/UI。嵌入式强调裁剪、目标适配、资源与实时响应;例RTOS将短ISR取样与显示任务分离,但还要分析优先级、栈、阻塞与最坏响应。认识原章OS名称,不背旧认证/授权或市场数量。

回到第21节完整讲解 →

习题8.21 / 开发模式比较

以 STM32 为例,描述程序开发的一些主要模式,各自有什么特点,优势和缺点都体现 在哪些地方?实际应用中该如何取舍。

展开解题过程与代码

寄存器控制直接但底层负担大;SPL/HAL等库提高可读复用但需理解参数/状态;OS组织任务与同步、消耗资源;配置器/中间件加快初始化和组件集成。可组合使用,按复杂度/实时/资源/团队取舍,不以“用了库一定慢”定论。

回到第22节完整讲解 →

习题8.22 / 非工程框架

非工程方式进行嵌入式软件开发,流程大致有几种?其框架为何?

展开解题过程与代码

原题主要考无OS的循环轮询、前后台中断驱动,以及有OS的任务框架。轮询初始化+while检查条件;前后台ISR做短紧急工作、主循环做长处理;OS创建任务/对象后调度。无论框架,生成目标程序仍需正确构建/链接/启动环境。

回到第22节完整讲解 →

习题8.23 / SPL工程步骤

以 STM32 固件库为基础利用工程方式,进行嵌入式软件应用开发,有哪些主要步骤? 请略述之。

展开解题过程与代码

获取匹配设备/库/工具→选官方模板与启动/system→配目标/宏/头路径/存储/下载→加入驱动与应用/ISR→构建链接→下载调试→脱离调试复位测试。只include头不等于库实现进了工程;注意重复main/ISR。

回到第23节完整讲解 →

习题8.24 / 固件库架构

请查阅相关资料,并结合学习中的体会,描述对 STM32 固件库体系架构的理解。

展开解题过程与代码

CMSIS核心提供核接口,设备头提供寄存器/IRQ定义,system提供系统初始化;各stm32f10x_ppp驱动封装外设,misc处理部分核配置,conf选择包含;main写业务,it文件承载ISR。参看第23节和文末索引,库是寄存器与应用之间的接口,不替代目标手册。

回到第23节完整讲解 →

习题8.25 / 函数命名

ST 固件库中,函数的命名规则有哪些特点?

展开解题过程与代码

外设前缀+动作:StructInit填结构默认值,Init应用结构,DeInit复位寄存器,Cmd使能模块,Send/Receive搬数据,GetFlag/GetIT查状态,ITConfig配中断,Clear按规定清源,DMACmd开请求。具体原型/可清标志各外设不同,不把PPP通用规则当万能函数。

回到第23节完整讲解 →

习题8.26 / 八种GPIO模式

STM32 F103 的 GPIO 有哪 8 种工作模式?略述每种模式的特点。

展开解题过程与代码

模拟、浮空、上拉、下拉输入;普通推挽、普通开漏、复用推挽、复用开漏输出。分别对应ADC、明确外部驱动、按下接地/接VDD、LED/控制、线与/模拟I²C、USART_TX/SPI输出、硬件I²C。参看第25节完整表,50MHz是驱动档。

回到第25节完整讲解 →

习题8.27 / 重映射

GPIO 的复用功能重映射有何意义?如何实现的,举一个例子说明。

展开解题过程与代码

把支持的外设信号从默认引脚改到备用引脚,缓解冲突/布线,不增加物理脚。开GPIO/AFIO/外设时钟,设MAPR和GPIO模式,配置外设。例USART1 PA9/10→PB6/7;保留SWD释放JTAG用JTAGDisable而非全部SWJ_Disable。

回到第27节完整讲解 →

习题8.28 / PC6两版代码

如果使用 GPIOC6 接口为推挽方式输出,最高频率 50MHz,分别使用寄存器模式和库函 数模式,如何进行编程。

展开解题过程与代码

本题的完整应用文件如下;加入官方F103RC高密度SPL/CMSIS启动模板。共用board.h/board.c、工程条件和接线见“配套源码”节,不能同时编译其他题的main或同名ISR。默认时钟明确为HSI8MHz,未经实板验证。

#include "stm32f10x.h"
int main(void)
{
    RCC->APB2ENR |= 1u<<4;
    GPIOC->BSRR=1u<<6;
    GPIOC->CRL=(GPIOC->CRL&~(15u<<24))|(3u<<24);
    for(;;) { }
}
#include "stm32f10x.h"
#include "stm32f10x_rcc.h"
#include "stm32f10x_gpio.h"
int main(void)
{
    GPIO_InitTypeDef g;
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC,ENABLE);
    GPIO_SetBits(GPIOC,GPIO_Pin_6);
    GPIO_StructInit(&g);
    g.GPIO_Pin=GPIO_Pin_6; g.GPIO_Mode=GPIO_Mode_Out_PP;
    g.GPIO_Speed=GPIO_Speed_50MHz; GPIO_Init(GPIOC,&g);
    for(;;) { }
}

PC6为CRL bit27:24,编码0011;GPIOC时钟位在APB2。先设输出锁存高可避免使能瞬间不希望的低有效脉冲。两版二选一,均不自动输出50MHz波形。

回到第26节完整讲解 →

习题8.29 / 计数模式

通用定时器有哪三种计数模式?各自特点为何?

展开解题过程与代码

向上0→ARR重载,向下ARR→0重载,中央对齐往返且DIR由硬件改变。向上/下一个周期通常ARR+1tick;中央完整往返不能机械套该式,需按2×ARR及更新/比较设置分析。

回到第32节完整讲解 →

习题8.30 / 四类计数时钟

通用定时器有 4 种时钟来源,请简述。

展开解题过程与代码

CK_INT内部RCC时钟;ITR0—3来自其他定时器;TI1/TI2输入滤波/边沿;ETR外触发路径。外部模式1经TRGI/从控制器,模式2用ECE/ETR;不是任意组合都可同时启用。

回到第32节完整讲解 →

习题8.31 / 捕获过程

简述通用定时器的输入捕获过程。

展开解题过程与代码

GPIO/TI→滤波/边沿→通道映射与捕获分频→硬件CNT锁到CCR→CCIF;按使能产生IRQ/DMA→软件读取时间戳、处理CCOF。周期用同沿差,脉宽用异沿差,需处理回绕/分频/丢样与测量范围。

回到第33节完整讲解 →

习题8.32 / TIM2_CH2测频

参照书中例子,采用 TIM2 通道 2 进行频率测量,利用库函数实现其设置。

展开解题过程与代码

本题的完整应用文件如下;加入官方F103RC高密度SPL/CMSIS启动模板。共用board.h/board.c、工程条件和接线见“配套源码”节,不能同时编译其他题的main或同名ISR。默认时钟明确为HSI8MHz,未经实板验证。 本题用q32/board_capture.c,不要加入定义另一个TIM2_IRQHandler的common/board.c。信号接PA1,1MHz计数;两次上升沿间隔须小于65536µs,信号停止时结果保持旧值,应用需另加无信号超时。

#include "board.h"
#include "stm32f10x_rcc.h"
#include "stm32f10x_gpio.h"
#include "stm32f10x_tim.h"
volatile uint32_t period_us,frequency_hz,overcapture;
static uint16_t previous;
static unsigned have_previous;
void TIM2_IRQHandler(void)
{
    uint32_t sr=TIM2->SR;
    if(sr&(1u<<2)) {
        uint16_t current=(uint16_t)TIM2->CCR2;
        TIM2->SR=(uint16_t)~((1u<<2)|(1u<<10));
        if(sr&(1u<<10)) { ++overcapture; have_previous=0; }
        if(have_previous) {
            uint16_t delta=(uint16_t)(current-previous);
            if(delta) { period_us=delta; frequency_hz=1000000u/delta; }
        }
        previous=current; have_previous=1;
    }
}
int main(void)
{
    GPIO_InitTypeDef g; TIM_TimeBaseInitTypeDef t; TIM_ICInitTypeDef ic;
    if(!board_clock_8mhz()) for(;;) { }
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);
    GPIO_StructInit(&g); g.GPIO_Pin=GPIO_Pin_1;
    g.GPIO_Mode=GPIO_Mode_IN_FLOATING; GPIO_Init(GPIOA,&g);
    TIM_TimeBaseStructInit(&t); t.TIM_Prescaler=7;
    t.TIM_Period=65535; t.TIM_CounterMode=TIM_CounterMode_Up;
    TIM_TimeBaseInit(TIM2,&t);
    TIM_ICStructInit(&ic); ic.TIM_Channel=TIM_Channel_2;
    ic.TIM_ICPolarity=TIM_ICPolarity_Rising;
    ic.TIM_ICSelection=TIM_ICSelection_DirectTI;
    ic.TIM_ICPrescaler=TIM_ICPSC_DIV1; ic.TIM_ICFilter=0;
    TIM_ICInit(TIM2,&ic); TIM2->EGR=1; TIM2->SR=0;
    NVIC_SetPriorityGrouping(3); NVIC_SetPriority(TIM2_IRQn,1);
    NVIC_ClearPendingIRQ(TIM2_IRQn); NVIC_EnableIRQ(TIM2_IRQn);
    TIM_ITConfig(TIM2,TIM_IT_CC2,ENABLE); TIM_Cmd(TIM2,ENABLE);
    for(;;) { } /* inspect period_us/frequency_hz; valid period <65536us */
}

第一次仅建立基准,后续delta转为周期µs与整数Hz;频率取整会有量化误差,精细显示可用浮点或定点。示例1000tick得1000Hz。

回到第33节完整讲解 →

习题8.33 / PWM输入测量

PWM 输入模式是如何工作的?其占空比与周期为何?如何得到,请以一个实际的PWM 输入测量过程解释。

展开解题过程与代码

同一TI映射IC1/IC2,反极性分别捕获周期/高电平,选相应触发让CNT在基准沿reset。1MHz计数时CCRperiod=2000、CCRhigh=500,周期2ms、f=500Hz、D=25%。不能把捕获CCR统一加1;第一不完整周期、0/100%、溢出和无信号需单独处理。

回到第34节完整讲解 →

习题8.34 / 比较输出

简述通用定时器的比较输出过程。

展开解题过程与代码

CNT与CCR匹配→按OC模式改变OCxREF/置标志→经极性、通道使能(高级还含MOE/死区)到GPIO。选择冻结/置有效/无效/翻转/强制/PWM等,GPIO需对应复用。硬件匹配不是等ISR运行才产生输出。

回到第35节完整讲解 →

习题8.35 / PWM占空比

PWM 输出模式中,占空比是怎么得到的?

展开解题过程与代码

向上PWM1高有效时高电平在CNT<CCR,因此D=CCR/(ARR+1),T=(PSC+1)(ARR+1)/fTIM。ARR49、CCR47是94%;反极性/低有效LED另算实际亮时间,原文一处(CCR+1)/(ARR+1)错误。

回到第35节完整讲解 →

习题8.36 / 五秒红绿灯

参照书中例子,设计一个红绿灯系统,要求:红灯亮 5 秒钟,熄灭,切换绿灯亮 5 秒 钟,熄灭,循环往复。给出硬件原理图,并编写代码实现。

展开解题过程与代码

本题的完整应用文件如下;加入官方F103RC高密度SPL/CMSIS启动模板。共用board.h/board.c、工程条件和接线见“配套源码”节,不能同时编译其他题的main或同名ISR。默认时钟明确为HSI8MHz,未经实板验证。 接线见第28/45节电路SVG;加入common/board.c,不用UART驱动。

#include "board.h"
#define RED_MS 5000u
#define GREEN_MS 5000u
#define USE_BUTTON 0
static volatile unsigned press_events;
static volatile uint32_t last_press;
static void button_init(void)
{
    RCC->APB2ENR |= (1u<<4)|1u;  /* GPIOC + AFIO */
    GPIOC->BSRR=1u<<5; gpio_cfg(GPIOC,5,8); /* pull-up */
    AFIO->EXTICR[1]=(AFIO->EXTICR[1]&~(15u<<4))|(2u<<4);
    EXTI->IMR &= ~(1u<<5); EXTI->RTSR &= ~(1u<<5);
    EXTI->FTSR |= 1u<<5; EXTI->PR=1u<<5;
    NVIC_SetPriority(EXTI9_5_IRQn,1);
    NVIC_ClearPendingIRQ(EXTI9_5_IRQn); NVIC_EnableIRQ(EXTI9_5_IRQn);
    last_press=ms-20u; EXTI->IMR |= 1u<<5;
}
void EXTI9_5_IRQHandler(void)
{
    if(EXTI->PR&(1u<<5)) {
        uint32_t now=ms;
        EXTI->PR=1u<<5;
        if((uint32_t)(now-last_press)>=20u && !(GPIOC->IDR&(1u<<5))) {
            last_press=now; ++press_events;
        }
    }
}
int main(void)
{
    unsigned red=1,seen=0; uint32_t start,now;
    if(!board_clock_8mhz()) for(;;) { }
    lights_init(); tick_init(); lights_set(red); start=ms;
    if(USE_BUTTON) button_init();
    for(;;) {
        now=ms;
        if(seen!=press_events) {
            unsigned events=press_events-seen;
            seen+=events; red^=events&1u;
            lights_set(red); start=now; /* accepted press resets phase */
        }
        if((uint32_t)(now-start)>=(red?RED_MS:GREEN_MS)) {
            red^=1u; lights_set(red); start=now;
        }
    }
}

TIM2每1ms推进ms,红/绿阈值均5000ms;BSRR一次切换两灯避免中间两灯同时亮。代码中USE_BUTTON=0,保留的按键处理不会开启。需要保留硬件模板的其他必要异常处理。

回到第45节完整讲解 →

习题8.37 / 向量表

中断向量表作用是什么?

展开解题过程与代码

保存初始MSP和异常处理函数入口,CPU按异常号定位入口并执行。外部IRQ k偏移4×(16+k),地址带Thumb标识;复位向量在基址+4。M系列表项是地址,不是A32风格跳转指令。

回到第40节完整讲解 →

习题8.38 / ISR与函数

ARM 的中断服务程序应用中有哪些特点?其与普通的函数相比有何异同?

展开解题过程与代码

相同点:代码均可由C函数形式组织、遵循编译器约定;不同点:异常硬件进入、自动基本帧/EXC_RETURN、通常无参数返回、受优先级/屏蔽影响,需处理/清实际源、注意共享与嵌套栈。不能只用普通BL→BX LR的模型解释异常现场。

回到第42节完整讲解 →

习题8.39 / 优先级分组

在中断优先级中,抢占优先级和子优先级如何发挥各自作用?实际应用中是怎么分组并 设置的,以 STM32F103 举例说明。

展开解题过程与代码

4有效位分抢占/子优先级,SPL组0—4为0+4到4+0。组2时各0—3;较小数值优先,抢占决定能否嵌套,同抢占级的子优先级只决定多个pending的先后。例A(1,3)、B(2,0),A可抢B;B不能只因子级0抢A。

回到第41节完整讲解 →

习题8.40 / 优先级位数

STM32F103 的中断系统,使用多少位优先级设置?一共支持多少级可编程异常优先级?

展开解题过程与代码

F103实现4位可编程优先级,共16级编码,分组重新分配抢占/子位,不凭空增加总编码。IP寄存器字节高4位有效,CMSIS函数一般传未左移逻辑值;固定异常角色不等于普通IRQ16级都能任意覆盖。

回到第41节完整讲解 →

习题8.41 / 建立中断

简述 STM32F103 建立一个中断的过程。

展开解题过程与代码

正确向量/栈→开GPIO/外设及相关AFIO→配置来源/触发→清旧pending→全局分组/IRQ优先级→外设请求和NVIC使能→匹配名字的ISR取数据/清源/记事件。每层都要接通,NVIC_EnableIRQ不是替所有外设开中断。

回到第42节完整讲解 →

习题8.42 / 数量口径

STM32F103 的中断系统共支持多少个异常?其中包括多少个内部异常和多少个可屏蔽的 非内核异常中断?

展开解题过程与代码

原文口径写84=16+68,另又出现60可用IRQ;不能把这三个数混作统一硬件保证。按本讲义F103RC高密度,外部IRQn0—59共60个槽,系统向量区16个字含MSP和保留;C8中密度通常43个外部IRQ槽。真正设计按该型号startup/IRQn表,EXTI线与IRQ数量不能直接相加。

回到第40节完整讲解 →

习题8.43 / 复位向量位置

STM32F103 复位中断服务程序的地址存放在中断向量表中的哪个位置?

展开解题过程与代码

向量基址+0x04。用户Flash基址0x08000000时是0x08000004,启动别名映射可在0x00000004读取同一入口;第0字是初始MSP,别误当成Reset_Handler地址。

回到第40节完整讲解 →

习题8.44 / GPIO映射EXTI

STM32F103 的外部中断/事件线是如何与端口映射的?

展开解题过程与代码

同号Pxn共享EXTIn,AFIO_EXTICR每4位选一个端口来源,同一时刻不能让PA5与PC5都独立占EXTI5。GPIO配输入,开端口/AFIO时钟;16根GPIO输入再共享7个GPIO EXTI IRQ入口。

回到第43节完整讲解 →

习题8.45 / EXTI线数

STM32F103 的 EXTI 信号线一共有多少根?它们分别对应哪些输入?

展开解题过程与代码

本章F103范围19根:0—15选择GPIO同号脚,16 PVD、17 RTC Alarm、18 USB唤醒。不是19个独立GPIO ISR,其他STM32系列/连接型可不同。

回到第43节完整讲解 →

习题8.46 / EXTI传播路径

EXTI 的外部中断/事件请求的产生和传输过程为何?以 STM32F103 为例,具体说明。

展开解题过程与代码

外部GPIO→AFIO来源→边沿检测(RTSR/FTSR)或软件SWIER→中断pending/IMR→NVIC→CPU ISR;事件经EMR走事件/唤醒路径。按线配置、清PR写1,ISR共享时判断各线,不用NVIC清pending代替清源。

回到第43节完整讲解 →

习题8.47 / 中断与事件

对于 EXTI 来说,中断和事件有哪些区别和相同之处?

展开解题过程与代码

可共享输入/边沿条件,中断路有NVIC/ISR,事件路通常产生硬件事件/唤醒而不执行ISR。IMR和EMR分别控制,不代表任意事件都能直接触发任意DMA/TIM;以具体内部连接为准。

回到第43节完整讲解 →

习题8.48 / EXTI需要的时钟

若要使用 STM32F103 的 EXTI 中断,必须先使能哪个时钟?

展开解题过程与代码

若指GPIO外部中断映射,必须使能APB2的AFIO时钟,并使能所用GPIO端口时钟以配置输入;NVIC本身不是另一个普通RCC外设。F103没有单独的RCC_EXTI_EN位。

回到第43节完整讲解 →

习题8.49 / 按钮可控红绿灯

参照书中例子,设计一个可控红绿灯系统,要求:红灯亮 20 秒钟,熄灭,切换绿灯亮 30 秒钟,熄灭,循环往复;若用按键可立即强行改变当前红绿灯颜色(即若是红灯亮, 改为绿灯亮,绿灯亮改为红灯亮)。给出硬件原理图,编写代码实现。

展开解题过程与代码

本题的完整应用文件如下;加入官方F103RC高密度SPL/CMSIS启动模板。共用board.h/board.c、工程条件和接线见“配套源码”节,不能同时编译其他题的main或同名ISR。默认时钟明确为HSI8MHz,未经实板验证。 接线同第36题;PC5按键接地上拉/下降沿EXTI5,GPIO5—9共享IRQ。按键优先于同时到期计时,接受按压后切色并重启新颜色完整时长;20ms边沿消抖不等于工业级按键滤波。

#include "board.h"
#define RED_MS 20000u
#define GREEN_MS 30000u
#define USE_BUTTON 1
static volatile unsigned press_events;
static volatile uint32_t last_press;
static void button_init(void)
{
    RCC->APB2ENR |= (1u<<4)|1u;  /* GPIOC + AFIO */
    GPIOC->BSRR=1u<<5; gpio_cfg(GPIOC,5,8); /* pull-up */
    AFIO->EXTICR[1]=(AFIO->EXTICR[1]&~(15u<<4))|(2u<<4);
    EXTI->IMR &= ~(1u<<5); EXTI->RTSR &= ~(1u<<5);
    EXTI->FTSR |= 1u<<5; EXTI->PR=1u<<5;
    NVIC_SetPriority(EXTI9_5_IRQn,1);
    NVIC_ClearPendingIRQ(EXTI9_5_IRQn); NVIC_EnableIRQ(EXTI9_5_IRQn);
    last_press=ms-20u; EXTI->IMR |= 1u<<5;
}
void EXTI9_5_IRQHandler(void)
{
    if(EXTI->PR&(1u<<5)) {
        uint32_t now=ms;
        EXTI->PR=1u<<5;
        if((uint32_t)(now-last_press)>=20u && !(GPIOC->IDR&(1u<<5))) {
            last_press=now; ++press_events;
        }
    }
}
int main(void)
{
    unsigned red=1,seen=0; uint32_t start,now;
    if(!board_clock_8mhz()) for(;;) { }
    lights_init(); tick_init(); lights_set(red); start=ms;
    if(USE_BUTTON) button_init();
    for(;;) {
        now=ms;
        if(seen!=press_events) {
            unsigned events=press_events-seen;
            seen+=events; red^=events&1u;
            lights_set(red); start=now; /* accepted press resets phase */
        }
        if((uint32_t)(now-start)>=(red?RED_MS:GREEN_MS)) {
            red^=1u; lights_set(red); start=now;
        }
    }
}

加入common/board.c即可得到完整tick/灯配置。主循环无长阻塞,按键不会等20/30秒delay结束才处理。若持续异常抖动或需严格“按下/释放一次”,可改成稳定状态机,在规格中明确。

回到第45节完整讲解 →

习题8.50 / USART收发

简述 STM32F103 的 USART 数据接收/发送过程。

展开解题过程与代码

发:RAM/CPU→TDR→发送移位器→TX,TXE为TDR空、TC为整帧结束。收:RX→接收移位器→RDR→RXNE→CPU/DMA读DR,及时处理否则ORE。DR同址分读写功能;错误/IDLE典型先读SR再读DR清。

回到第48节完整讲解 →

习题8.51 / 带校验的八位数据帧

如果通过 STM32 串口 1 发送 8 位数据,带一位奇偶校验位,图示发送的格式。

展开解题过程与代码

起始0→d0…d7→奇/偶校验P→停止1,空闲1,共11bit(一个停止位时)。若数据0x53,1位共4个,偶校验P=0、奇P=1;图见第46节SVG。F103需9位字长+校验才能保留8有效数据位。

回到第46节完整讲解 →

习题8.52 / 发送指定字符串

编程实现通过串口 1 发送字符串“you are welcome”。

展开解题过程与代码

本题的完整应用文件如下;加入官方F103RC高密度SPL/CMSIS启动模板。共用board.h/board.c、工程条件和接线见“配套源码”节,不能同时编译其他题的main或同名ISR。默认时钟明确为HSI8MHz,未经实板验证。 只需common/board.c的时钟/GPIO帮助函数;模板中不得重复定义相关ISR,源码README给出组合。PA9接匹配电平串口RX并共地,9600 8N1。

#include "board.h"
static int send_byte(uint8_t c)
{
    uint32_t wait=1000000u;
    while(!(USART1->SR&(1u<<7)) && --wait) { }
    if(!wait) return 0;
    USART1->DR=c; return 1;
}
int main(void)
{
    const char *s="you are welcome"; uint32_t wait;
    if(!board_clock_8mhz()) for(;;) { }
    RCC->APB2ENR |= (1u<<2)|(1u<<14);
    gpio_cfg(GPIOA,9,11); gpio_cfg(GPIOA,10,4);
    USART1->CR1=0; USART1->CR2=0; USART1->CR3=0;
    USART1->BRR=(8000000u+4800u)/9600u;
    USART1->CR1=(1u<<13)|(1u<<3)|(1u<<2); /* 9600 8N1 */
    while(*s) if(!send_byte((uint8_t)*s++)) for(;;) { }
    wait=1000000u;
    while(!(USART1->SR&(1u<<6)) && --wait) { }
    for(;;) { } /* timeout is a bounded failure path; no silent infinite wait */
}

字符串有15个字节,不发送结尾0;9600/8N1约15.625ms,按实际RC/分频有误差。等待循环有次数上限,是防止永久阻塞的教学保护,不是严格毫秒超时。

回到第51节完整讲解 →

习题8.53 / 双机握手与双缓冲

设计一个简单的双机通信系统,要求:在两个 ARM 处理器之间,用 USART 实现,各 自具有收/发缓冲内存区域,协议自定,结束条件自定。给出相关硬件连接图,编写代码 实现。

展开解题过程与代码

本题的完整应用文件如下;加入官方F103RC高密度SPL/CMSIS启动模板。共用board.h/board.c、工程条件和接线见“配套源码”节,不能同时编译其他题的main或同名ISR。默认时钟明确为HSI8MHz,未经实板验证。 两板PA9交叉接对方PA10,GND连接。加入common/board.c、uart_irq.c/.h、ring.h。协议HELLO/ACK、换行、最长63字节、3秒截止见第52节,双方同程序。

#include "board.h"
#include "uart_irq.h"
#include <string.h>
volatile unsigned link_done,link_timeout,protocol_errors;
int main(void)
{
    char line[64]; unsigned used=0,discard=0,got_hello=0,got_ack=0;
    unsigned hello_queued=0,ack_needed=0; uint32_t start; uint8_t c;
    if(!board_clock_8mhz()) for(;;) { }
    tick_init(); uart_init(9600); start=ms;
    for(;;) {
        if(!hello_queued) hello_queued=uart_line("HELLO");
        if(ack_needed && uart_line("ACK")) ack_needed=0;
        while(uart_get(&c)) {
            if(c=='\r') continue;
            if(c=='\n') {
                if(!discard) {
                    line[used]=0;
                    if(!strcmp(line,"HELLO")) { got_hello=1; ack_needed=1; }
                    else if(!strcmp(line,"ACK")) got_ack=1;
                    else ++protocol_errors;
                }
                used=0; discard=0;
            } else if(!discard) {
                if(used<sizeof(line)-1u) line[used++]=(char)c;
                else { discard=1; ++protocol_errors; }
            }
        }
        if(uart_dropped || uart_errors) { link_timeout=1; break; }
        if(got_hello && got_ack && !ack_needed) { link_done=1; break; }
        if((uint32_t)(ms-start)>=3000u) { link_timeout=1; break; }
    }
    /* Interrupts keep draining any queued ACK after the main handshake completes. */
    for(;;) { }
}

RX/TX各独立有效127字节软件队列,ACK不再回应ACK,避免无限回声;完成表示两种消息条件满足且ACK已排入发送队列,不等于最后一位已在线发完,ISR继续排出。异步上电可能在超时内失败,本协议无重试;需要更强上线能力应显式加带序号重试。

回到第52节完整讲解 →

习题8.54 / 通用UART驱动与FIFO前提

编写一个通用的 UART 驱动程序。要求:(1)使用中断方式接收、发送数据;(2)要充分利 用 UART 的硬件接收、发送 FIFO;(3)编写的程序代码要求简洁、高效、可靠。

展开解题过程与代码

先指出平台差异:F103无多字节可编程硬件FIFO。 本章完整F103驱动在配套源码节:独立RX/TX环形队列、RX错误/溢出计数、TXE中断按需开关、临界区保存旧PRIMASK;第54题main是其回显调用。

#include "board.h"
#include "uart_irq.h"
int main(void)
{
    uint8_t c=0; unsigned have=0;
    if(!board_clock_8mhz()) for(;;) { }
    uart_init(9600);
    for(;;) {
        if(!have) have=(unsigned)uart_get(&c);
        if(have && uart_put(c)) have=0; /* do not lose a byte on a full TX queue */
    }
}

对真正带FIFO的硬件,通用ISR的硬件适配层可写成下列伪代码;寄存器名称/深度/错误清除必须按该目标定义。

while 硬件RX_FIFO非空:
    byte = 读FIFO
    若RX软件队列满: 记录丢失; 否则入队
while 硬件TX_FIFO有空位 且TX软件队列非空:
    将一个队列字节写入FIFO
若TX软件队列空: 关闭TX空位中断
按硬件规定处理错误与中断原因

不能拿软件队列冒充满足“硬件FIFO”的题设;若题目指定另一个带FIFO UART,应替换硬件适配层并测试满/空、突发、错误与并发。

回到第50节完整讲解 →

习题8.55 / SPI主从

SPI 的主从模式各有什么特点和区别?简述两种模式下的数据收发过程。

展开解题过程与代码

主供SCK、MOSI输出/MISO输入;从接SCK、MOSI输入/MISO输出,NSS选中。双方CPOL/CPHA、位序、字长一致,从机预备好待发数据;全双工时同一时钟交换两方向,主读发dummy、主只写也处理收到字。原文从模式接收称来自MISO是笔误。

回到第53节完整讲解 →

习题8.56 / 一主五从

1 个 SPI 主设备可以连接 5 个 SPI 从设备吗?如果可以,画出连接图,说明如何操作。

展开解题过程与代码

可以。参看第54节SVG:SCK/MOSI/MISO共享,CS1—CS5各连一从;未选从MISO高阻,每次按事务选一个并等BSY结束再释放。若从机不三态需隔离/其他拓扑;不同从机模式/速度在安全空闲状态切换,不能只把5个NSS绑一起。

回到第54节完整讲解 →

习题8.57 / SPI环形结构

SPI 的环形总线结构有何特点,简述之。

展开解题过程与代码

原图描述主从移位寄存器通过MOSI/MISO闭合,一组时钟完成双向位串交换,没有单独读写总线周期。若指多器件菊链,需每个器件支持移出/串接,发完整链长度并按锁存/片选规则,不是所有SPI芯片天然支持。

回到第54节完整讲解 →

习题8.58 / SPI与I²C比较

SPI 和 I2C 串行总线各自有什么特点?

展开解题过程与代码

SPI常用SCK/MOSI/MISO/CS、推挽、可全双工、通常无地址/ACK,靠CS与器件协议;I²C两线开漏上拉、地址与ACK、半双工共享SDA、多主仲裁/拉伸等更复杂。速度/距离看具体器件与电气,不把SPI100MHz或I²C400k套所有芯片;两者通常面向板内/短距离。

回到第57节完整讲解 →

习题8.59 / SPI注意项

STM32 的 SPI 总线在使用上有哪些需要注意之处?

展开解题过程与代码

核对引脚方向/复用、SCK上限/分频、CPOL/CPHA/字长/位序、NSS/SSI与多从三态;发前TXE、读RXNE,事务结束确认BSY;全双工发送也消耗接收,DMA完成后仍处理末位与错误;按器件命令/页/擦除规则,不用SPI_Init就声称Flash数据已存。

回到第55节完整讲解 →

习题8.60 / I²C注意项

STM32 的 I2C 总线在使用上有哪些需要注意之处?

展开解题过程与代码

合适上拉/电平/电容、7位地址与线上字节/内部存储地址分清、时序参数与PCLK正确、起止/重复起始/ACK/NACK与F1接收尾部顺序正确;超时、NACK/仲裁丢失/总线忙/勘误与恢复;DMA和IRQ资源/优先级合理。EEPROM分8字节页、写后ACK轮询;不保证从机必ACK,也不一律把I²C设最高优先级。

回到第60节完整讲解 →

程序依赖不藏起来

共用模块、工程组合与分页算法

第八章配套应用源码与使用条件

默认芯片:STM32F103RCT6,LQFP64高密度。使用官方STM32F10x SPL 3.5/CMSIS模板、
startup_stm32f10x_hd.s、system_stm32f10x.c、STM32F10X_HD目标宏及对应设备头。
本包只包含本讲义原创应用代码,不捆绑厂商启动文件/库,不是已做实板测试的IDE工程。
各题分开建工程,勿加入多个main或同名ISR。官方it.c里相关默认ISR定义应删除或替换,
不能与本包定义重复。按题目加入所需SPL源并设置头文件路径/USE_STDPERIPH_DRIVER等。

文件组合:
q28:二选一register_main.c / spl_main.c;SPL版需gpio、rcc库源。
q32:capture_main.c + board_capture.c;需rcc、gpio、tim库;common为头文件路径。
q36:main.c + common/board.c。
q49:main.c + common/board.c。
q52:main.c + common/board.c(该文件含未使用tick函数,仍提供相应ISR,避免与默认重复)。
q53:main.c + common/board.c + common/uart_irq.c。
q54:main.c + common/board.c + common/uart_irq.c。
common/ring.h用于中断UART软件队列;algorithms/page_chunks.h提供纯C分页拆分算法。

时钟:除q28不涉及计时,应用main都显式切换到HSI8MHz、AHB/APB无分频。
TIM2 PSC=7,计数1MHz;tick ARR=999产生1ms;串口BRR按8MHz计算。
内部RC有容差,时间精度/通信误差以器件与双方时钟为准。72MHz正文算例不直接套入源码。

接线:PA8红LED、PA9绿LED,各经LED和510欧限流从3.3V接入,低有效;PC5按键接地、内部上拉。
q36无按钮,红/绿各5秒。q49红20秒绿30秒,接受一次下降沿后主循环及时切换并重计时,20ms边沿消抖。
UART题PA9_TX/PA10_RX;与红绿灯题分开,PA9不能同时驱绿灯与串口。板间TX交RX并共地,逻辑电平匹配。
q32输入PA1/TIM2_CH2,正脉冲频率测量,周期须小于65536us;信号停止需应用增加超时,输出会保留最后有效值。

UART适用单主循环+ISR,队列容量128、有效127字节,RX满丢新并计数;TX满返回失败。
没有捏造F103硬件FIFO;带FIFO硬件应另外实现抽象中的drain/fill过程。
q53双方各发HELLO
,收到HELLO回ACK
,不回复ACK;3秒截止;过长行丢到换行。
链路失败后不再处理应用,ISR仍可能排出已排队数据;如需关闭外设,须等待队列/TC并明确停机策略。

验证:纯C队列、分页和协议/定时状态可在主机核对;器件头/库/中断/时钟和引脚必须在实际目标工具链构建与实板验证。
没有声称在本环境中用MDK构建完整目标工程。详见HTML各节和题卡的边界说明。

下面文件与题卡应用文件配套;每题单独构建。不要把LED的PA9和USART1的PA9同时使用。

common/board.h

#ifndef CH8_BOARD_H
#define CH8_BOARD_H
#include "stm32f10x.h"
#include <stdint.h>
int board_clock_8mhz(void);
void gpio_cfg(GPIO_TypeDef *port, unsigned pin, unsigned nibble);
void lights_init(void);
void lights_set(unsigned red);
void tick_init(void);
extern volatile uint32_t ms;
#endif

common/board.c

#include "board.h"
volatile uint32_t ms;
int board_clock_8mhz(void)
{
    uint32_t n=1000000u;
    RCC->CR |= 1u;
    while (!(RCC->CR & 2u) && --n) { }
    if (!n) return 0;
    RCC->CFGR &= ~3u;              /* select HSI */
    n=1000000u;
    while ((RCC->CFGR & 12u) && --n) { }
    if (!n) return 0;
    RCC->CFGR &= ~((15u<<4)|(7u<<8)|(7u<<11));
    RCC->CR &= ~(1u<<24);          /* PLL no longer the system source */
    SystemCoreClock=8000000u;
    return 1;
}
void gpio_cfg(GPIO_TypeDef *p,unsigned pin,unsigned nibble)
{
    unsigned shift=(pin&7u)*4u;
    volatile uint32_t *cr=pin<8u ? &p->CRL : &p->CRH;
    *cr=(*cr & ~(15u<<shift)) | ((nibble&15u)<<shift);
}
void lights_init(void)
{
    RCC->APB2ENR |= 1u<<2;
    GPIOA->BSRR=(1u<<8)|(1u<<9);    /* off before enabling output */
    gpio_cfg(GPIOA,8,2); gpio_cfg(GPIOA,9,2); /* PP 2MHz */
}
void lights_set(unsigned red)
{
    GPIOA->BSRR = red ? ((1u<<(8+16))|(1u<<9))
                      : ((1u<<8)|(1u<<(9+16)));
}
void tick_init(void)
{
    RCC->APB1ENR |= 1u;
    TIM2->CR1=0; TIM2->DIER=0;
    TIM2->PSC=7; TIM2->ARR=999;     /* 8MHz / 8 / 1000 = 1kHz */
    TIM2->EGR=1; TIM2->SR=0; TIM2->CNT=0;
    NVIC_SetPriorityGrouping(3);   /* 4 preempt bits for this device */
    NVIC_SetPriority(TIM2_IRQn,2);
    NVIC_ClearPendingIRQ(TIM2_IRQn); NVIC_EnableIRQ(TIM2_IRQn);
    TIM2->DIER=1; TIM2->CR1=1;
}
void TIM2_IRQHandler(void)
{
    if (TIM2->SR & 1u) { TIM2->SR=(uint16_t)~1u; ++ms; }
}

common/ring.h

#ifndef CH8_RING_H
#define CH8_RING_H
#include <stdint.h>
#define RING_N 128u
typedef struct { volatile uint8_t data[RING_N]; volatile uint16_t head,tail; } Ring;
static inline int ring_put(Ring *r,uint8_t c)
{
    uint16_t next=(uint16_t)((r->head+1u)&(RING_N-1u));
    if(next==r->tail) return 0;
    r->data[r->head]=c; r->head=next; return 1;
}
static inline int ring_get(Ring *r,uint8_t *c)
{
    if(r->head==r->tail) return 0;
    *c=r->data[r->tail]; r->tail=(uint16_t)((r->tail+1u)&(RING_N-1u)); return 1;
}
#endif

common/uart_irq.h

#ifndef CH8_UART_H
#define CH8_UART_H
#include "board.h"
void uart_init(uint32_t baud);
int uart_get(uint8_t *c);
int uart_put(uint8_t c);
int uart_line(const char *s);
extern volatile uint32_t uart_dropped,uart_errors;
#endif

common/uart_irq.c

#include "uart_irq.h"
#include "ring.h"
static Ring rxq,txq;
volatile uint32_t uart_dropped,uart_errors;
void uart_init(uint32_t baud)
{
    RCC->APB2ENR |= (1u<<2)|(1u<<14);
    gpio_cfg(GPIOA,9,11); gpio_cfg(GPIOA,10,4); /* AF_PP 50MHz / floating */
    USART1->CR1=0; USART1->CR2=0; USART1->CR3=0;
    USART1->BRR=(8000000u+baud/2u)/baud; /* valid nonzero baud, 8MHz PCLK2 */
    rxq.head=rxq.tail=txq.head=txq.tail=0;
    uart_dropped=uart_errors=0;
    (void)USART1->SR; (void)USART1->DR;
    NVIC_SetPriorityGrouping(3);
    NVIC_SetPriority(USART1_IRQn,1);
    NVIC_ClearPendingIRQ(USART1_IRQn); NVIC_EnableIRQ(USART1_IRQn);
    USART1->CR1=(1u<<13)|(1u<<3)|(1u<<2)|(1u<<5); /* UE TE RE RXNEIE */
}
int uart_put(uint8_t c)
{
    uint32_t old=__get_PRIMASK(); int ok;
    __disable_irq();
    ok=ring_put(&txq,c);
    if(ok) USART1->CR1 |= 1u<<7;
    __set_PRIMASK(old);
    return ok;
}
int uart_get(uint8_t *c)
{
    uint32_t old=__get_PRIMASK(); int ok;
    __disable_irq(); ok=ring_get(&rxq,c); __set_PRIMASK(old); return ok;
}
int uart_line(const char *s)
{
    /* Atomic all-or-none enqueue; payload <= 63 bytes. No ISR caller. */
    unsigned n=0,i,free; uint32_t old;
    while(s[n] && n<=63u) ++n;
    if(n>63u) return 0;
    old=__get_PRIMASK(); __disable_irq();
    free=(txq.tail-txq.head-1u)&(RING_N-1u);
    if(free<n+1u) { __set_PRIMASK(old); return 0; }
    for(i=0;i<n;++i) (void)ring_put(&txq,(uint8_t)s[i]);
    (void)ring_put(&txq,'\n'); USART1->CR1 |= 1u<<7;
    __set_PRIMASK(old); return 1;
}
void USART1_IRQHandler(void)
{
    uint32_t sr=USART1->SR; uint8_t c;
    if(sr & ((1u<<5)|15u)) {
        c=(uint8_t)USART1->DR;  /* read after SR: service RX / clear errors */
        if(sr&15u) ++uart_errors;
        else if((sr&(1u<<5)) && !ring_put(&rxq,c)) ++uart_dropped;
    }
    if((USART1->SR&(1u<<7)) && (USART1->CR1&(1u<<7))) {
        if(ring_get(&txq,&c)) USART1->DR=c;
        else USART1->CR1 &= ~(1u<<7);
    }
}

algorithms/page_chunks.h

#ifndef CH8_PAGE_CHUNKS_H
#define CH8_PAGE_CHUNKS_H
#include <stdint.h>
#include <stddef.h>
/* Caller supplies a page transaction; it must wait with a timeout for write completion. */
typedef int (*page_writer)(uint32_t address,const uint8_t *data,size_t length);
static inline int write_pages(uint32_t address,const uint8_t *data,size_t length,
                              uint32_t capacity,uint32_t page,page_writer put)
{
    size_t count;
    if(!page || address>capacity || length>capacity-address || !put) return 0;
    if(length && !data) return 0;
    while(length) {
        count=page-(address%page); if(count>length) count=length;
        if(!put(address,data,count)) return 0;
        address+=(uint32_t)count; data+=count; length-=count;
    }
    return 1;
}
#endif

避免原文混淆直接带错答案

原书易错点与适用条件

原书或容易混淆的说法 做题与实践采用的判断
复位即72MHz 硬件从HSI开始;启动文件/应用完成合法配置后才到设定频率
BOOT0=0、BOOT1=1进入SRAM BOOT0=0仍用户Flash;SRAM是BOOT0=1、BOOT1=1
一个F103型号代表所有外设数量/引脚 以具体料号与封装为准。本讲义默认F103RC;C8不能照搬RC的资源
混入USART可由LSE工作、CEC唤醒等文字 不据此给F103配置不存在的功能;查F1参考手册与数据手册
PVD等同于可编程上电复位阈值 PVD是电源监测;POR/PDR、BOR等依器件具体实现区分
GPIO施密特输入写成肖特基 Schmitt是迟滞输入;Schottky是另一种器件概念
SWJ_JTAGDisable关闭SWD 该配置保留SWD;完全SWJ_Disable才同时关闭JTAG/SWD
8位配置字段,或把PC6放CRH 每脚4位;PC6在CRL bit27:24
用PR|=掩码清中断 EXTI_PR写1清,直接赋目标位;读改写可能误清其他已挂起位
CKD作为CNT主分频 CNT主分频由PSC决定;CKD影响数字滤波/死区等时基
对所有捕获值机械加1 ARR整圈计数有ARR+1;边沿捕获间隔按差/模差和模式含义求
PWM的ARR49、CCR47算成约96% PWM1向上、高有效时47/50=94%;低有效LED的亮时间约6%
通用TIM3也需要RCR/MOE 高级定时器有这些控制;TIM3不照搬
72MHz下SysTick最多116ms 24位整周期最大2²⁴/72MHz≈233.017ms;装载值需减1
F103RC有68个外部IRQ,84个向量都是中断 高密度F103RC外部IRQ编号0—59,共60;前16个表项包括MSP和保留项,并非16个有效异常
子优先级高即可打断同抢占级ISR 不能;抢占能力由抢占级和屏蔽条件决定
TIM_ICInit结构里有TIM_ICMode 官方SPL结构使用Channel、Polarity、Selection、Prescaler、Filter等字段;不编造不存在成员
F103串口有可编程多字节硬件FIFO 没有这类FIFO;RAM环形队列靠软件搬运,不能消除硬件接收溢出风险
TXE或DMA完成等于末位发送完 USART收尾看TC;SPI还需相应TXE/BSY与协议条件
主SPI从MOSI接收,从SPI从MISO接收 主接MISO、从接MOSI;同一时钟全双工移位
SPI分频在I2SPR,或F103 SPI都能100MHz 普通SPI用CR1.BR;具体时钟上限看数据手册,不能套通用宣传值
W25Q64容量2MB 64Mbit=8MiB;典型页256字节、扇区4KiB,具体后缀仍需核对
AT24C02每次任意跨页连续写 256字节总容量、8字节写页;分页并等待内部写完成,超页可能回卷
I²C 0x50与0xA0/0xA1是不同从机地址 0x50是7位地址,0xA0/0xA1是加上R/W后的线上字节

旧工具与OS名称保留在对应节作历史与用途背景,不把“最新”“支持全部”等旧宣传描述当作现时事实。原书若把不同STM32子系列的描述拼在一起,先确定本章F103RC的适用范围,再记结论。

全书学完标准

从题意到系统:每一步都能说明白

到这里,全书第一至第八章已经串起来。真正学会的标准是拿到题目先找对象、时钟、引脚、数据格式、状态/中断、边界条件,再写公式或程序。

  1. 不看答案画F103最小系统,解释BOOT选择与前两个向量字;说清楚上电后为什么不保证马上72MHz。
  2. 独立算GPIO字段、APB与TIM时钟、PSC/ARR、捕获频率、PWM、BRR和UART帧长;每个数字写明前提。
  3. 对GPIO、TIM、EXTI、USART、SPI、I²C,各说出时钟、模式、核心寄存器、状态位、异常和清除方式。
  4. 能解释并修改q28、q32、q36、q49、q52、q53、q54;知道哪些文件一起构建,哪些IRQ定义不能重复,哪些引脚互相冲突。
  5. 能指出队列满、输入停止、捕获溢出、通信超时、I²C NACK、存储页边界的处理策略;ISR里不阻塞等待主循环。
  6. 全部60道题至少独立口述一次。对背景题会按职责回答;对计算与程序题必须能重做。

建议练习顺序:8.28 GPIO→8.30—8.32定时/捕获→8.36红绿灯→8.42—8.49中断→8.52—8.54串口→8.55—8.60 SPI/I²C。先完成16道短测,错在哪里就回对应小节,不要从头把所有内容再看一遍。

考试通常要求到配置思路、计算和基础程序;真实板子还要看电气、时钟容差、勘误、构建和测量。工具生态、旧芯片比较、OS与测试流程的价值是让你理解“为什么一个函数正确仍不代表系统能工作”,掌握深度已逐节标出。

整理依据与核对范围

依据用户提供《计算机原理与嵌入式系统讲义》第八章独立PDF(188页)重组,保留8.1—8.9全部小节与60道习题。原文件内容作为学习材料处理。默认STM32F103RCT6;代码时钟另有明确条件。

核对资料:ST RM0008参考手册、ST STM32F103RC型号资料、NXP I²C规范UM10204、Microchip AT24C02C数据手册、Winbond相应W25Q64型号数据手册。具体后缀、电压、硅版本和勘误仍以所用器件文档为准。

25幅SVG与公式、答案、源码ZIP均内嵌;默认图同时用于无脚本阅读和打印,打印展开所有答案。主机检查验证队列、分页与部分协议/定时逻辑;未声称完成MDK目标工程构建或实板验证,真实电气、外设时序、启动与错误路径仍需在目标上检验。

打开计算机系统全书目录 →