# 嵌入式系统原理

## 引言

1. 面向高速数据处理和海量数据存储的通用计算机系统，如intel的CPU系列

2. 嵌入式计算机系统，如misp430 、arm cortex-M3控制器

嵌入式系统是以应用为核心，以计算机技术为基础，软硬件可裁剪，对功能、可靠性、成本、体积、功耗等适应应用系统对功能等严格要求的专用计算机系统。

将微型计算机主要功能部件集成于一块芯片内，成为单片微型计算机，简称单片机，SCM。单片机在国外通常称为微控制器，MCU。

传统的8位单片机是低级嵌入式系统，而基于ARM32位单片机是较高级的嵌入式系统。

一般由嵌入式微处理器、外围硬件设备、嵌入式操作系统以及用户的应用程序等四个部分组成。就是嵌入到对象体的专用计算机系统。



### 嵌入式的应用领域

智能灰尘

SPEC

无源感知网络 battery wireless

家庭智能管理系统

智慧医疗

交通管理环境监测

信息家电

机器学习

两个电梯系统投票

### 嵌入式系统和通用计算机相比有哪些特点



1. 功耗低、集成度高

2. 嵌入式系统和具体应用有机地结合在一起

3. 软件按一般固化再存储器芯片或单片机本身，而不是存贮与磁盘等载体中。

4. 嵌入式系统本身不具备自居开发能力，即使设计完成以后用户通常也不是能对其中的程序功能进行修改的，必须有一套开发工具和环境。

按处理器位宽可分为 4 位、8 位、16 位、32 位、64 位系统。一般来讲，位宽越大，性能越强。

实时性：非实时、硬实时和软实时

按表现形式分：芯片级嵌入、模块级嵌入、系统级嵌入

嵌入式处理器按技术特点，分为嵌入式微处理器、嵌入式微控制器、嵌入式数字信号处理器、嵌入式片上系统、嵌入式双核处理器和嵌入式多核处理器6类。

基于处理器（通用处理器和嵌入式处理器）的设备称为计算机。于是分成两部分：通用计算机和嵌入式系统，嵌入式系统也称为嵌入式计算机。

按控制技术的复杂度，嵌入式系统分为无操作系统控制的嵌入式系统、

小型操作系统控制的操作系统和大型操作系统控制的嵌入式系统三种。 



### 嵌入式系统软件

一般包含四个层面：

板级支持包、嵌入式操作系统、中间件和应用软件

应用层、中间件层、文件系统RTOS、驱动层HAL

嵌入式操作系统

嵌入式支撑软件

嵌入式应用软件

Flash不可易失性，断电后程序仍然存在

ARM RISC-V Intelx86 MISC AMD NAVDIA intel                                                                                                             

汇编和时钟         ——时间基准

常用的主流处理器架构                                                                                                                                                                                                                                                                                                                                                                                                 树莓派——利用python编程，但是基于Linux操作系统。

模块化、函数式

微控制器

微处理器

处理器：外部设备没有集成

微控制器：会把外部设备集成

#### 嵌入式系统硬件

以嵌入式处理器为中心，有存储器、I/O设备、通信模块以及电源等必要的外围接口组成。

存储器：包括内部存储器（内存）和外部存储器（外存）。内存是电路板上的半导体存储器件，外存包括硬盘、光盘、U盘等。

内部存储器按照掉电后数据是否消失可分为易失性存储器和非易失性存储器。 

易失性：及断电数据即消失，ESRAM、SDRAM。非易失性：Flash。

## 汇编语言——ARM



| 名称                             | 含义                                   |
| -------------------------------- | -------------------------------------- |
| **MSP（Main Stack Pointer）**    | 主堆栈指针，用于中断、异常、启动时使用 |
| **PSP（Process Stack Pointer）** | 进程堆栈指针，用于任务/RTOS 线程       |

系统上电复位时，MCU 会做两件事：

1. 从 Flash 地址 0x00000000 取出 **第一个字（Word）**
    放入 **MSP** 作为堆栈起始地址（一般是 SRAM 顶端）
2. 执行复位向量指向的 Reset_Handler

ARM既可指英国的芯片设计公司ARM公司，也可指采用ARM技术的芯片。

ARM芯片本质上是一个32位精简指令集（RISC）处理器架构，其广泛地应用在许多嵌入式处理器设计中。

ARM仅提供IP核，而非完整芯片。只做设计，不做产品。

load与store都是相对于CPU

因此load是把内存的内容的存入cpu寄存器

store是把cpu内寄存器的内容写入内存

ARM是统一编址的。



不会放在内存，因为是常量（#define）

会放到falsh存储器里去

DCB/DCD是常量

Flash 非易失性存储器IROM

IRAM 内存

即只提供Cortex-M内核，并不提供存储器、I/O、时钟和复位等外设。并不能构成一块完整的芯片。面向低成本、高性能 的微控制器MCU。



<img src="https://chenalna.oss-cn-hangzhou.aliyuncs.com/img/image-20251106234144270.png" alt="image-20251106234144270" style="zoom:50%;" />

芯片制造商即SQC

在单片机中，有意法半导体(ST)，德州仪器(TI)、恩智浦（NXP）等

ST：意法半导体，M:MCU，32：32位单片机



STM32的产品线十分丰富



<img src="https://chenalna.oss-cn-hangzhou.aliyuncs.com/img/image-20251110200053733.png" alt="image-20251110200053733" style="zoom:50%;" />

本课程选择STM32F1，最高工作频率为72MHZ

![image-20251110202314074](https://chenalna.oss-cn-hangzhou.aliyuncs.com/img/image-20251110202314074.png)

STM32F103系列微控制器名称示例

![image-20251110200135005](https://chenalna.oss-cn-hangzhou.aliyuncs.com/img/image-20251110200135005.png)

嵌入式系统的软件，一般固化于嵌入式存储器中。分为无操作系统和带操作系统两种

无操作系统

引导程序

一般由汇编语言编写，在嵌入式系统上电后运行，完成自检、存储映射、时钟系统和外设接口配置等硬件初始化操作。

应用程序

一般由C语言编写，直接架构在硬件之上，在引导程序之后运行。

带操作系统

包括设备驱动层、操作系统层、应用软件层。

可靠性高，开发周期短，易于移植和扩展

**其它典型嵌入式系统**

FreeRTos

嵌入式Linux

Android

Windows CE

VxWorks

虽然STEM32单片机是32位系统，但并不建议嵌入操作系统，因为操作系统对系统的硬件有一定要求。功能比较简单的系统，不建议使用操作系统，毕竟操作系统也占用系统资源，需要开发者对操作系统的原理有比较好的掌握。

STFM32F103系统架构：

主要由4个驱动单元和4个被动单元构成，它们彼此之间通过一个多级的AHB（高性能总线）构架相互连接。

4个驱动单元：内核数据总线、系统总线、通用DMA1、通用DMA2；

4个被动的单元：内部SRAM,内部FLASH，连接所APB（高级外设总线）设备的AHB到APB桥

![image-20251110204734425](https://chenalna.oss-cn-hangzhou.aliyuncs.com/img/image-20251110204734425.png)

**集成电路封装类型和引脚个数**

DIP-40

**DIP** = **Dual In-line Package**，双列直插封装

**40** = 有 **40 个引脚**

LQFP-48

**LQFP** = **Low-profile Quad Flat Package**，低矮型四方扁平封装

**48** = 有 **48 个引脚**

QFN 64

- **QFN** = Quad Flat No-lead（无引脚四方扁平封装）
- **64** = 64 个焊盘

BGA-324 

**BGA** = **Ball Grid Array**，球栅阵列封装

**324** = 有 **324 个焊球**

BGA-1190

- **19×19** = 焊球矩阵排列 19 行 × 19 列
- **1190** = 焊球总数 **1190 个**

**存储器映射**

存储器本身不含有地址信息，它的地址是由芯片厂商或用户分配，给存储器分配地址的过程称为存储器映射，如果再分配一个地址就叫重映射。

32位操作系统寻址空间是4GB。

程序存储器、数据存储器、寄存器和I/O端口在同一个顺序的4GB地址空间。

在微控制器里，flash存储C语言存储的机器代码，ram存储数据。flash和ram统一编址。

flash是只读的。你只能在最开始的时候写入数据，在这之后不能修改。

除法转化成整数的除法和移位

RAM时间复杂度空间复杂度

```
x86 x64 arm64的区别
我们常说的高通 865，麒麟990 不是 CPU 是 SoC（System On Chip），SoC 除了 CPU 外，还有 GPU，还有可选的浮点数加速器，专用于深度模型的加速器，等等。除此以外，SoC 还包括运存，基带芯片等等一系列集成式的东西，比电脑上的CPU集成度更高。
ARM 和各个 SoC 的关系：所有 ARM 架构的 CPU 都是 ARM 公司授权的，授权的形式是 IP 核，各个品牌得到授权，生产自己的 SoC。

高通公司的 SoC 对 ARM 的 IP 核做了二次包装，把 32位的 CPU 命名为 Krait 系列，把 64 位的 CPU 命名为 Kryo 系列。例如 骁龙855 使用的 CPU 是 Kryo 485，实际是由 Cortex-A55 + Cortex-A76 实现的。

手机CPU的ARM架构
ARM是英国ARM公司提供一种CPU架构的知识产权，目前主流的手机和平板电脑都采用ARM架构，套用一句话：ARM不生产芯片，只提供一个芯片设计的Idea。
可以说，作为一家不生产芯片的芯片厂商，ARM却在全球范围内支撑起了各种嵌入式设备、智能手机、平板电脑、智能穿戴和物联网设备的运行，只是ARM每年都会从构建上述设备体内的上亿颗处理器中“抽成”，严格遵守薄利多销的运营模式。

主流的手机/平板品牌，绝大数是采用ARM架构，当然现在ARM也进军PC市场。

高通骁龙(snapdragon)
三星(Exynos)
联发科(Helio)
华为(麒麟)
苹果 (A11，A7，A6，M1)
Intel
Nvidia
安卓apk/lib 目录下的几个文件夹，代表不同的架构：

arm64-v8a

armeabi-v7a

x86

IOS模拟器
4s-5: i386
5s-7s Plus: x86_64
真机(iOS设备):
armv6: iPhone、iPhone 2、iPhone 3G、iPod Touch(第一代)、iPod Touch(第二代)
armv7: iPhone 3Gs、iPhone 4、iPhone 4s、iPad、iPad 2
armv7s: iPhone 5、iPhone 5c (静态库只要支持了armv7,就可以在armv7s的架构上运行)
arm64(注:无armv64): iPhone 5s、iPhone 6、iPhone 6 Plus、iPhone 6s、iPhone 6s Plus、 iPhone 7 、iPhone 7 Plus、iPad Air、iPad Air2、iPad mini2、iPad mini3、iPad mini4、iPad Pro

电脑CPU的x86架构
主流品牌：

Inter(英特尔)
AMD
比如操作系统区分

Windows 10 (Multiple Editions) (x64) - DVD (Chinese-Simplified)
Windows 10 (Multiple Editions) (x86) - DVD (Chinese-Simplified)
X86源于英特尔几十年前出品的CPU型号8086（包括后续型号8088/80286/80386/80486/80586）。
8086以及8088被当时的IBM采用，制造出了名噪一时的IBM PC机，从此个人电脑风靡一时。你如果年龄不是很小，可能听说过早年的386电脑、486电脑乃至586电脑的说法，就是从这来的。后来英特尔注册了奔腾品牌，不再沿用686、786……这样的命名，但后来的奔腾、奔腾2、奔腾3、奔腾4，以及我们熟悉的酷睿架构，都是从当初的80X86一脉相承下来的架构，只是不断优化、扩充功能、提升性能而已。而其他X86处理器厂商，比如AMD、威盛、全美达（已退出X86领域）等，其产品也都兼容X86架构。

X64是X86_X64的简称

之所以叫X86，是因为实在应用得过于广泛导致X86直接成为了其代名词。

X32和X64------这两个概念你可以大致的认为它们是居于X86之下（之内）的两个子概念。

它们指的不是CPU架构，而是CPU寄存器、运算器能访问、处理的数据位宽，以及与此相关的一整套CPU设计规范。
X32 CPU------32位的CPU（32bit的CPU）; X64 CPU------64位的CPU（64bit的CPU）
简单的说，X32的X86 CPU只能处理32位的数据、运行32位的操作系统；

X64的 CPU则可以处理64位以及32位的数据、运行32位以及64位的操作系统------X64向下兼容X32。

奔腾直到奔腾4早期型号的CPU，都是X32的X86架构；

从后期的奔腾4 CPU开始直至今天的酷睿i架构的CPU，都是X64的X86架构。

当然，AMD的处理器目前也都是X64的X86架构。

ARM与X86架构对比
Intel和ARM的处理器除了最本质的复杂指令集（CISC)和精简指令集（RISC）的区别之外，下面我们再从以下几个方面对比下ARM和X86架构。

一、制造工艺
ARM和Intel处理器的一大区别是ARM从来只是设计低功耗处理器，Intel的强项是设计超高性能的台式机和服务器处理器。

二、64位计算
对于64位计算，ARM和Intel也有一些显著区别。Intel并没有开发64位版本的x86指令集。64位的指令集名为x86-64（有时简称为x64），实际上是AMD设计开发的。Intel想做64位计算，它知道如果从自己的32位x86架构进化出64位架构，新架构效率会很低，于是它搞了一个新64位处理器项目名为IA64。由此制造出了Itanium系列处理器。
同时AMD知道自己造不出能与IA64兼容的处理器，于是它把x86扩展一下，加入了64位寻址和64位寄存器。最终出来的架构，就是 AMD64，成为了64位版本的x86处理器的标准。IA64项目并不算得上成功，现如今基本被放弃了。Intel最终采用了AMD64。Intel当前给出的移动方案，是采用了AMD开发的64位指令集（有些许差别）的64位处理器。

而ARM在看到移动设备对64位计算的需求后，于2011年发布了ARMv8 64位架构，这是为了下一代ARM指令集架构工作若干年后的结晶。为了基于原有的原则和指令集，开发一个简明的64位架构，ARMv8使用了两种执行模式，AArch32和AArch64。顾名思义，一个运行32位代码，一个运行64位代码（详情戳）。ARM设计的巧妙之处，是处理器在运行中可以无缝地在两种模式间切换。这意味着64位指令的解码器是全新设计的，不用兼顾32位指令，而处理器依然可以向后兼容。

三、异构计算
ARM的big.LITTLE架构是一项Intel一时无法复制的创新。在big.LITTLE架构里，处理器可以是不同类型的。传统的双核或者四核处理器中包含同样的2个核或者4个核。一个双核Atom处理器中有两个一模一样的核，提供一样的性能，拥有相同的功耗。ARM通过big.LITTLE向移动设备推出了**异构计算。这意味着处理器中的核可以有不同的性能和功耗。当设备正常运行时，使用低功耗核，而当你运行一款复杂的游戏时，使用的是高性能的核。

参考自：CPU的构架之ARM和Intel的区别（x86/x64/ARM64/ARM）

armv7和armv8对比
摘自：ARM架构-维基百科

架构	别名	处理器家族
ARMv7	armeabi-v7a	ARM Cortex-A、ARM Cortex-M、ARM Cortex-R
ARMv8	arm64-v8a	Cortex-A35、Cortex-A50系列[14]、Cortex-A72、Cortex-A73
armv7和armv8的区别对照表
特性	ARM V8	ARM V7
指令集	64位指令集 AArch64， 并且兼容32位指令集 AArch32	32位指令集 A32 和16位指令集 T16
支持地址长度	64位	32位
通用寄存器	31个 x0-x30（64位）或者 w0-w30（32位）	15个, r0-r14 (32位)
异常模式	4层结构 EL0-EL3	2层结构vector table
NEON	默认支持	可选支持
LAPE	默认支持	可选支持
Virtualization	默认支持	可选支持
big.LITTLE	支持	支持
TrustZone	默认支持	默认支持
SIMD寄存器	32个 X 128位	32个 X 64位

Google Play下载应用时的架构
比如我在下载google play上的应用，有下面这些选项：



解释一下每个的架构区别

armeabi: 第5代、第6代的ARM处理器，早期的手机用的比较多。
armeabiv-v7a: 第7代及以上的 ARM 处理器
arm64-v8a: 第8代、64位ARM处理器，2016年之后中高端的手机，比如骁龙8系列，麒麟9系列，联发科1000+
x86: 平板、模拟器用得比较多。
x86_64: 64位的平板。
查看手机的arm架构
方法一

下载Devcheck，安装后，在系统信息这一栏中点击，可以查看当前的架构

方法三

查看手机的SOC，查找该SOC的信息，比如A55+A76，然后去查找对应的ARM信息

方法二

配置好adb环境，在cmd下输入以下命令：adb shell getprop ro.product.cpu.abi
查看打印的字符，比如：arm64-v8a

更多手机硬件相关的知识可参考这篇《Android 深度学习基础 – 系统和硬件》


```

![img](https://img2020.cnblogs.com/blog/363476/202012/363476-20201215200324714-454955033.png)

## CM3 内核结构

中断包含系统调用，即系统调用由中断实现

工作模式状态

寄存器、总线接口

存储器组织和映射

异常和中断



最小系统核心板电路

![](https://chenalna.oss-cn-hangzhou.aliyuncs.com/img/image-20260121214116264.png)

电源为开发板各模块提供电源



![image-20260121214201375](https://chenalna.oss-cn-hangzhou.aliyuncs.com/img/image-20260121214201375.png)

STM32F103工作电源为3.3V ，分数字IO电源和模拟IO电源。数字IO外设和模拟IO外设可以根据不同的应用使用不同供电电源或相同的供电电源

复位电路

![image-20260121214417852](https://chenalna.oss-cn-hangzhou.aliyuncs.com/img/image-20260121214417852.png)

![image-20260121214425312](https://chenalna.oss-cn-hangzhou.aliyuncs.com/img/image-20260121214425312.png)

STM32F103使用低电平复位方式，NRST引脚复位一般需至少持续20us低电平，可以使芯片复位成功。在上电瞬间，电容充电，NRST出现短暂的低电平，能保证芯片处于复位状态，该低电平持续时间由电阻和电容共同决定

时钟电路

![image-20260121215028528](https://chenalna.oss-cn-hangzhou.aliyuncs.com/img/image-20260121215028528.png)

时钟电路为芯片工作提供稳定的时钟源，可使用内部时钟源或外部时钟源。STM32F103内部时钟源采用RC振荡电路，可用于芯片的工作时钟源，但是RC电路受温湿度影响较大，精度较低。外部时钟电路通常采用晶体振荡器实现，相对RC电路来说精度更高，受温湿度影响较小

**编程接口（或调试接口）**

![image-20260121215220684](https://chenalna.oss-cn-hangzhou.aliyuncs.com/img/image-20260121215220684.png)

STM32F103具有标准的JTAG调试接口，JTAG信号线包含JTRST、TMS、TCK、TDI和TDO等引脚

STM32F103提供了简化的SWD接口，仅使用了TMS和TCK两个信号







## 时钟系统

时钟（Clock）是时序逻辑的基础，用于触发逻辑单元状态的更新，具有特定频率。为CPU和外设提供基准时钟信号，程序的运行及外设的状态更新均以时钟信号的变换为同步基础。处理器使用时钟信号的同步可能会在任一上升沿，下降沿，或双边沿，取决于不同芯片的工作方式。

RC振荡器



![image-20260121215956355](https://chenalna.oss-cn-hangzhou.aliyuncs.com/img/image-20260121215956355.png)

![image-20260121220003505](https://chenalna.oss-cn-hangzhou.aliyuncs.com/img/image-20260121220003505.png)



在嵌入式系统开发中，为节约成本，处理器可以选择由内部RC振荡电路产生时钟，但RC时钟温漂较大，不适合对时间要求比较精确的场合。另外，RC振荡频率随着电源电压VDD变化而变化，以及工艺参数的差异和电阻电容本身的容差对振荡器频率的影响，导致同一型号不同芯片的振荡器频率也不尽相同。



 晶振是一种把电能和机械能相互转化在共振的状态下工作，以提供稳定、精确的单频振荡的晶体，它是时钟电路中最重要的部件。晶振是石英晶体谐振器（quartz crystal oscillator）的简称，它能够产生处理器（CPU）执行指令所必须的时钟信号，时钟信号频率越高，通常CPU的运行速度也就越快。

​    晶振之所以能做振荡电路（谐振）是基于它的压电效应。在晶片的两个极板间加一电场，会使晶体产生机械变形；反之，若在极板间施加机械力，又会在相应的方向上产生电场，这种现象称为压电效应。如在极板间所加的是交变电压，就会产生机械变形振动，同时机械变形振动又会产生交变电场。一般来说，这种机械振动的振幅是比较小的，其振动频率则是很稳定的。但当外加交变电压的频率与晶片的固有频率（决定于晶片的尺寸）相等时，机械振动的幅度将急剧增加，这种现象称为压电谐振，因此石英晶体又称为石英晶体谐振器，其特点是频率稳定度很高。

n晶振分为无源晶振叫做crystal（晶体），有源晶振则叫做oscillator（振荡器）。无源晶振需要借助于时钟电路才能产生振荡信号，自身无法振荡；有源晶振是一个完整的谐振振荡器，通常包括石英（或其晶体材料）晶体谐振器，陶瓷谐振器，LC谐振器等。

**时钟技术——PLL**

由于制作工艺与成本原因，晶振做不到很高频率，而在嵌入式系统中，往往为了实现高速运行，需要高频时钟信号支持。锁相环(Phase Locked Loop：简称PLL)是一种振荡器中的反馈技术。在电路工作过程中，输出信号频率与输入信号频率相等时，输出电压与输入电压保持固定相位差值，即输出电压与输入电压的相位被锁住，进而提供稳定时钟信号输出。PLL技术被广泛应用于高频电路。

锁相环通常由鉴相器（PD,Phase Detector）、环路滤波器（LF,Loop Filter）和压控振荡器（VCO）三部分组成，锁相环组成的原理框图如图所示。

![image-20260121220541969](https://chenalna.oss-cn-hangzhou.aliyuncs.com/img/image-20260121220541969.png)



STM32有2个外部时钟源和2个内部时钟源，用户可根据实际需求来配置时钟。STM32片上各个外设接口时钟由内部或外部高速时钟分频或倍频得到。

最好利用CPU内部的时钟，因为外部时钟会造成干扰

无源：时钟

有源：晶振

时钟源

- HSI：高速内部时钟，8MHZ

- HSE：高速外部时钟,4MHZ~16MHZ,实验平台8MHZ晶振

- LSI：低速内部时钟，40KHZ

- LSE：低速外部时钟,32.768KHZ

- PLL：锁相环倍频输出，时钟输入源为HSI/2,HSE,HSE/2，倍频选择为2~16，最大不超过72MHZ

内部时钟采用RC振荡电路

HSI（High Speed Internal Clock Source）高速内部时钟

典型频率值为8MHz

\- 可作为 系统时钟 SYSCLK 或 PLL 的输入   

HSE（High-Speed External）高速外部时钟   4~16 MHz（实验平台通常 8 MHz）

   \- 外接晶振或陶瓷谐振
\- 精度高于 HSI
\- 用于 系统主时钟 SYSCLK 或 PLL 输入，适合对定时要求高的应用   

LSI（Low-Speed Internal）低速内部时钟   约 40 kHz（内部 RC 振荡器）  

 \- 通常用于 独立看门狗（IWDG） 或 低功耗定时器
\- 不适合 RTC 精确计时，因为精度低   

LSE（Low-Speed External）低速外部时钟   32.768 kHz（晶振）  

 \- 用于 RTC（实时时钟），时间精确
\- 也可用于低功耗模式下的计时   

PLL（Phase-Locked Loop）锁相环倍频输出   输入：HSI/2、HSE、HSE/2
倍频：2~16。

注意PLL 本身并不是独立的“时钟源”，但在 STM32 等 MCU 时钟系统里，它是 一个倍频模块，用来生成系统主时钟 SYSCLK，最大不超过 72 MHz  

 \- 将低频时钟倍频，生成高速系统时钟 SYSCLK
\- 提供 MCU 核心和总线高频工作时钟   

外部时钟：从外部通过连接晶振的方式获取时钟源，其中HSE和LSE是外部时钟源





![image-20251110210333685](https://chenalna.oss-cn-hangzhou.aliyuncs.com/img/image-20251110210333685.png)

三种不同的时钟源可被用来驱动系统时钟(SYSCLK)：

●HSI振荡器时钟

●HSE振荡器时钟

●PLL时钟

这些设备有以下2种二级时钟源：

●40kHz低速内部RC，可以用于驱动独立看门狗和通过程序选择驱动RTC。RTC用于从停机/待机模式下自动唤醒系统。

●32.768kHz低速外部晶体也可用来通过程序选择驱动RTC(RTCCLK)。

当不被使用时，任一个时钟源都可被独立地启动或关闭，由此优化系统功耗。





STM32系统时钟

![image-20260121221947873](https://chenalna.oss-cn-hangzhou.aliyuncs.com/img/image-20260121221947873.png)

RTC和IWDG时钟

实时时钟（RTC，Real Time Clock）为人们提供精确的实时时间，或者为电子系统提供精确的时间基准，目前实时时钟芯片大多采用精度较高的晶体振荡器作为时钟源。STM32F103可选择LSE、LSI或HSE的128分频三者之一作为工作时钟。

 独立看门狗（IWDG，Independent Watch Dog）是一个12位的递减计数器，当计数器的值从某个值一直减到0的时候，系统就会产生一个复位信号，即 IWDG_RESET。如果在计数没减到0之前，刷新了计数器的值的话，那么就不会产生复位信号，这个动作就是我们经常说的喂狗。其时钟源来自LSI

![image-20260121222209850](https://chenalna.oss-cn-hangzhou.aliyuncs.com/img/image-20260121222209850.png)

通过MCO可以给别的芯片提供时钟，节省晶振、节约成本，还能改善EMI。可选择HSI、HSE、SYSCLK、PLLCLK/2这四者之一作为主时钟在IO引脚上输出，由寄存器RCC_CFGR的MCO位控制

![image-20260121222224848](https://chenalna.oss-cn-hangzhou.aliyuncs.com/img/image-20260121222224848.png)

![image-20260121222228156](https://chenalna.oss-cn-hangzhou.aliyuncs.com/img/image-20260121222228156.png)

![image-20260121222234729](https://chenalna.oss-cn-hangzhou.aliyuncs.com/img/image-20260121222234729.png)



AHB总线时钟

nARM有专门的外设时钟，AHB、APB1、APB2外设总线连接了不同外设，提供不同的时钟，以适应不同外设的需要。

![image-20260121223412071](https://chenalna.oss-cn-hangzhou.aliyuncs.com/img/image-20260121223412071.png)

APB1总线时钟的输入为系统时钟SYSCLK，通过设置寄存器RCC_CFGR的PPRE1位(预分频因子1、2、4、8或16)，得到APB1总线钟时钟输出。APB1总线时钟在外设时钟使能情况下为外设提供时钟信号，并且通过乘法器为TIM2~TIM7提供工作时钟。

![image-20260121223625448](https://chenalna.oss-cn-hangzhou.aliyuncs.com/img/image-20260121223625448.png)

APB2总线时钟的输入为系统时钟SYSCLK，通过设置寄存器RCC_CFGR的PPRE2位(预分频因子1、2、4、8或16)，得到APB2总线钟时钟输出。APB2总线时钟在外设时钟使能情况下为外设提供时钟信号，通过2、4、6、8分频后输出至ADC，并且通过乘法器为TIM1，TIM8提供工作时钟。

![image-20260121223749031](https://chenalna.oss-cn-hangzhou.aliyuncs.com/img/image-20260121223749031.png)



USB时钟直接由PLL时钟作为时钟源输入，再经USB预分频后作为USB时钟。通过寄存器RCC_CFGR的USBPRE位，设置USB工作时钟频率。



![image-20260121224054394](https://chenalna.oss-cn-hangzhou.aliyuncs.com/img/image-20260121224054394.png)

引脚定义



电源引脚、GPIO引脚和系统功能引脚

单片机最小系统加上ISP（In System Programming）下载电路。也就是让微控制器运行起来。

CPU滤波电容

外接晶振模块

串口通信模块

启动设置电路

1. 用户闪存，即芯片内置Flash
2. SRAM，芯片内置RAM
3. 系统存储器，Boot loader，即是一个ROM区，烧程序 







## I/O模块

LED 指示灯模块



![image-20251110230623526](https://chenalna.oss-cn-hangzhou.aliyuncs.com/img/image-20251110230623526.png)



数码管模块

采用动态扫描的方式控制

采用共阳极接法

abcdefgh （位选）八段分别连接到 CPU 的 PD0、PD1、PD2、PD3、PD4、PD5、PD6、PD7 。（段选）DIG1-4 分别接至 CPU 的 PC110、PC11、PC12、PC13。

![image-20251110230220307](https://chenalna.oss-cn-hangzhou.aliyuncs.com/img/image-20251110230220307.png)



![image-20251110230325448](https://chenalna.oss-cn-hangzhou.aliyuncs.com/img/image-20251110230325448.png)



![image-20251125175013922](https://chenalna.oss-cn-hangzhou.aliyuncs.com/img/image-20251125175013922.png)



![image-20251125175324710](https://chenalna.oss-cn-hangzhou.aliyuncs.com/img/image-20251125175324710.png)





静态显示：

![image-20251125215201271](https://chenalna.oss-cn-hangzhou.aliyuncs.com/img/image-20251125215201271.png)

动态显示：

![image-20251125175747117](https://chenalna.oss-cn-hangzhou.aliyuncs.com/img/image-20251125175747117.png)



由于四个数码管数据线采用共用方式，四个数码管

在某一时刻只能控制其中某一个显示，或者四个数码管同时显示相同内容。显然，在大部分

场合时要求显示不同内容，所以只能通过对 4 给数码管进行分时控制控制，循环显示不同内

容，在保证一定的扫描速度的情况下，人的眼睛是看不出来只亮了其中一个数码管(其原理

类似显示器的刷新功能)。



**按键模块**

<img src="https://chenalna.oss-cn-hangzhou.aliyuncs.com/img/image-20251110230350180.png" alt="image-20251110230350180" style="zoom: 50%;" />

![image-20251110230413815](https://chenalna.oss-cn-hangzhou.aliyuncs.com/img/image-20251110230413815.png)

蜂鸣器模块

![image-20251110231008104](https://chenalna.oss-cn-hangzhou.aliyuncs.com/img/image-20251110231008104.png)





## GPIO 通用目的输入输出口

GPIO General process input & output

计算机输入输出接口是CPU与[外部设备](https://baike.baidu.com/item/外部设备)之间交换信息的连接电路，它们通过总线与CPU相连，简称I/O接口。GPIO（general purpose intput output）是通用输入输出端口的简称，可以通过寄存器来配置引脚输入、输出或其它功能。

三态缓冲器（Three-state buffer）

又称为三态门、三态驱动器，其三态输出受到使能输出端的控制。当使能输出有效时，器件实现正常逻辑状态输出（逻辑0、逻辑1），当使能输入无效时，输出处于高阻状态，即等效于与所连的电路断开。它分输入缓冲器和输出缓冲器两种。前者的作用是将外设送来的数据暂时存放，以便处理器将它取走；后者的作用是用来暂时存放处理器送往外设的数据。有了缓冲器，就可以使高速工作的[CPU](https://baike.baidu.com/item/CPU)与慢速工作的外设起协调和缓冲作用，实现数据传送的同步。



锁存器(Latch)

是一种对[脉冲](https://baike.baidu.com/item/脉冲/1938481)电平敏感的[存储单元](https://baike.baidu.com/item/存储单元/8727749)[电路](https://baike.baidu.com/item/电路/33197)，它们可以在特定输入脉冲电平作用下改变状态。锁存，就是把[信号](https://baike.baidu.com/item/信号/19190844)暂存以维持某种电平状态。锁存器的最主要作用是[缓存](https://baike.baidu.com/item/缓存/100710)，其次完成高速的控制器与慢速的外设的不同步问题，最后是解决一个 I/O 口既能输出也能输入的问题。锁存器是利用电平控制数据的输入，它包括不带使能控制的锁存器和带使能控制的锁存器。



施密特触发器

密特触发器不同于一般触发器，采用电位触发方式，其状态由输入信号电位维持；对于负向递减和正向递增两种不同变化方向的输入信号，施密特触发器有不同的阈值电压。当输入电压高于正向阈值电压，输出为高;当输入电压低于负向阈值电压，输出为低。当输入在正负向阈值电压之间，输出不改变。只有当输入电压发生足够的变化时，输出才会变化，这种双阈值动作被称为迟滞现象，表明施密特触发器有记忆性。从本质上来说，施密特触发器是一种双稳态多谐振荡器。

OC门

集电极开路（OC，Open Collector）门是一种晶体管开关管输出结构，其原理如图3- 21所示，即一个晶体管在集电极与电源没有接通，当基极有输入高电平信号1时，集电极和发射极导通，集电极输出电平为低电平，相当于将集电极与地直接相连，集电极输出电压约为0。当基极输入信号为低电平0时，集电极与发射极断开，集电极输出的虽然是逻辑1，但由于集电极没有与电源相连，集电极处于悬浮状态。



推挽电路

一般是指两个互补晶体管受同一信号的控制,总是在一个晶体管导通的时候另一个截止，此电路结构称为推挽式输出电路。推挽电路使用两个参数相同的三极管（NPN与PNP）或MOSFET（NMOS与PMOS），电路工作时，两只对称的开关管每次只有一个导通，所以导通损耗小、效率高。输出既可以向负载灌电流，也可以从负载抽取电流。推挽式输出级既提高电路的负载能力，又提高开关速度。

n引脚输入：指信号从外部电路经CPU引脚传输至CPU内部。为了实现信号的输入，CPU输入接口电路通常会采用类似于图3- 23所示的电路原理。数据信号经IO引脚、施密特触发器、锁存器、三态门输入至总线。为了防止输入信号电压偏高或偏低造成电路损害，电路可增加电压钳位功能。信号经旁路电容滤波后，送入施密特触发器整形，滤掉二个阈值电压范围内之噪声干扰后输出至锁存器，并在CP时序脉冲作用下，将数据输入至三态缓冲器。CPU读取IO数据时，控制三态缓冲器输出数据至内部总线，完成基本输入功能。

引脚输出

引脚输出指信号从CPU内部经CPU引脚传输至外部电路。为了实现引脚信号的输出，CPU输出接口电路通常会采用类似于图3- 24所示的电路原理。该接口电路采用推挽方式输出，CPU将数据经总线写入D触发器（缓冲器）,在输出端口使能的情况下，D触发器输出端Q的状态控制一对推挽管驱动电路输出。



GPIO 是微控制器数字输入输出的基本模块，可以实现微控制器与外部设备的数字交换。当微控制器没有足够的I/O引脚或片内存储器时，GPIO还可实现串行和并行通讯、存储器扩展等复用功能。

最多可以提供112个多功能双向I/O引脚。

每个端口有16个I/O引脚。

引脚命名采用端口号+引脚号方式 例如PA0。

<img src="https://chenalna.oss-cn-hangzhou.aliyuncs.com/img/image-20251110231753531.png" alt="image-20251110231018104" style="zoom:50%;" />

GPIO工作模式

输入模式

- 输入浮空

​	![image-20251110232724609](https://chenalna.oss-cn-hangzhou.aliyuncs.com/img/image-20251110232724609.png)

- 输入上拉

  把电位拉高，比如拉到Vcc

- 输入下拉

  把电位拉低

- 模拟输入

  上拉电阻和下拉电阻开关均关闭。

  ![image-20251110232852606](https://chenalna.oss-cn-hangzhou.aliyuncs.com/img/image-20251110232852606.png)

输出模式

- 开漏输出	

  不可能输出高电平
  
  把CPU的信号输出到引脚

​	<img src="https://chenalna.oss-cn-hangzhou.aliyuncs.com/img/image-20251110232947849.png" alt="image-20251110232947849" style="zoom: 80%;" />

​	P-MOS :PNP

​	N-MOS:NPN

​	相当于P-MOS管是无效的

输出端连接于N-MOS的漏极，要得到高电平状态需要上拉电阻才行，适合于电流型的驱动，其吸收电流的能力相对强。

可以输出0，也可以外接电源输出高电平



- 开漏复用

  输出复用功能的输出（片上外设的输出而不是CPU输出）

  ![image-20251110233743599](https://chenalna.oss-cn-hangzhou.aliyuncs.com/img/image-20251110233743599.png)

- 推挽式输出

​	可与输出高、低电平，连接数字器件；推挽结构一般是两个MOS管分别受到互补的信号控制，总是在一个MOS管导通时另一个截止。（输出控制有个反向器，输入0输出1）

​	![image-20251110233912495](https://chenalna.oss-cn-hangzhou.aliyuncs.com/img/image-20251110233912495.png)

- 推挽式复用

  同理，来自于片上外设。





![](https://chenalna.oss-cn-hangzhou.aliyuncs.com/img/image-20251110234423706.png)





![image-20251110234431151](https://chenalna.oss-cn-hangzhou.aliyuncs.com/img/image-20251110234431151.png)



GPIO输出速度

STM32微控制器的GPIO引脚工作于某个输出模式下，通常还需设置其输出速度。这个输出速度指的是I/O端口驱动电路的响应式速度，而不是输出信号的速度，输出信号的速度取决于软件程序。

CPU给出输出信号，经过驱动电路才达到I/O端口的引脚。

驱动电路有个速度和输出信号的速度不一样。

STM32微控制器I/O管脚内部有多个响应速度不同的驱动电路。

复用功能重映射，把某些外设的 “复用功能“从”默认引脚”转移到“备用引脚“上。

 一般推荐I/O引脚的输出速度是其输出信号速度5-10倍。

GPIO 控制寄存器

nGPIO的每个I/O端口位具有输入、输出、以及复用功能。每个GPIO端口有两个32位配置寄存器(GPIOx_CRL，GPIOx_CRH)、两个32位数据寄存器(GPIOx_IDR，GPIOx_ODR)、一个32位置位/复位寄存器(GPIOx_BSRR)、一个16位复位寄存器(GPIOx_BRR)和一个32位锁定寄存器(GPIOx_LCKR)。

输入模式配置

![image-20260121231356333](https://chenalna.oss-cn-hangzhou.aliyuncs.com/img/image-20260121231356333.png)

输出模式配置

![image-20260121231427769](https://chenalna.oss-cn-hangzhou.aliyuncs.com/img/image-20260121231427769.png)

端口配置低寄存器（GPIOX_CRL）、高寄存器（GPIOX_CRH）

因为一个引脚要4位，而一个字的数据32位只能配置8个，因此要分高低。



一共7个端口，每个端口16个引脚



为什么配置位要保留11？为什么上拉/下拉共用10？

PxODR寄存器配置成1



端口输入寄存器(GPIOx_IDR)(x=A,...,E)

引脚状态的输入信号读到寄存器中，输入的引脚为高电平则为1

端口输出寄存器（GPIOX_ODR）（x=A,...,E）

CPU数据写到寄存器上，然后更新到引脚



端口位设置/清零寄存器（GPIOX_BSRR）

端口位清除寄存器（GPIOX_BRR）

 端口配置锁定寄存器（GPIO_LCKR）(x=A...E)

是配置锁定不是点评锁定

APB2外设时钟使能寄存器（RCC_APB2ENR）

默认关闭



1. 打开时钟

2. 工作在输出模式，设置成输出模式，通用推挽输出。因为我们既要输出成高电平，也要设置成低电平。而且是CPU向端口输出。

3. 设置成8个全亮，设置GPIOC_ODR 。

​	如果要单个设置则设置GPIOC_BSRR。



### 阻塞

在[嵌入式开发](https://so.csdn.net/so/search?q=嵌入式开发&spm=1001.2101.3001.7020)中，我们经常需要处理多个任务，比如测距、闪灯、响应用户按键、处理串口接收等等。如果你在主循环中使用 `delay()` 之类的阻塞函数，那这些任务就只能一个一个做，[效率低下](https://so.csdn.net/so/search?q=效率低下&spm=1001.2101.3001.7020)，体验不佳。

**非阻塞式编程**，就是为了解决这个问题的一种思路。它避免在主循环中使用任何会“卡住 CPU”的阻塞操作，从而允许 CPU 每时每刻都能轮询检查所有任务是否需要处理，实现“多任务调度”的效果。

```C
	while(1){
		if (KEY_Scan())
		{t=(t+1)%3;}
		{
			switch (t)
			{
				case 0:
					LED_L2R();
					break;
				case 1:
					LED_R2L();
					break;
				case 2:
					LED_M2LR();			
					break;
			}
			
	}
void LED_L2R(void)
{
    // D2 -> D3 -> D4 -> D5
    GPIOE_BSRR = (1 << (8 + 16));  // ?? D2
    delay_ms(300);
    GPIOE_BSRR = (1 << (9 + 16));  // ?? D3
    delay_ms(300);
    GPIOE_BSRR = (1 << (10 + 16)); // ?? D4
    delay_ms(300);
    GPIOE_BSRR = (1 << (11 + 16)); // ?? D5
    delay_ms(300);

    // ???? LED
    GPIOE_BSRR = (1 << 8)|(1 << 9)|(1 << 10)|(1 << 11); // ??
    delay_ms(500);
}
		
```

这个时候，CPU 就被 `delay_ms()` 卡死了，什么事也干不了。

在这个问题中，CPU会一直处理点灯的效果，和按钮按下处理阻塞。



```C
if (SysTickCounter - lastToggleTime >= 1000) {
    lastToggleTime = SysTickCounter;
    toggle_LED();
}
```

CPU 每次循环只判断一下时间是否到了，LED 闪烁的同时，主循环还能继续干别的事。



#### 阻塞 vs 非阻塞：到底有什么区别

| 类型     | 示例                         | 优点                   | 缺点                         |
| -------- | ---------------------------- | ---------------------- | ---------------------------- |
| 阻塞式   | `delay(1000);`               | 写起来简单直观         | 会让 CPU 等待，浪费资源      |
| 非阻塞式 | `if (millis - last >= 1000)` | 不卡主循环，适合多任务 | 写起来需要设计思路、变量管理 |

非阻塞的核心思路

非阻塞的核心是：**“记录上一次事件发生的时间，并每次循环中判断是否满足条件。”**

用人话解释：

- 如果你要等一分钟，就不要站着等（阻塞）；
- 而是每隔几秒瞄一眼表，看时间到了没有（非阻塞）。





## 封装与库函数

ST公司向用户提供了STM32标准库函数，又称为STM32固件库。

它包括所有标准外设的驱动程序，是位于寄存器和用户之间的预定义代码，由程序、数据结构和各种宏定义组成。

只需要学习调用方法，不需研究源代码实现。

**函数RCC_APB2PeriphClockCmd**

<img src="https://chenalna.oss-cn-hangzhou.aliyuncs.com/img/image-20251110235330412.png" alt="image-20251110235330412" style="zoom: 80%;" />

默认情况下时钟是没打开的，想要打开一个外设就必须打开它的时钟。

![image-20251110235507409](https://chenalna.oss-cn-hangzhou.aliyuncs.com/img/image-20251110235507433.png)

n涉及寄存器列表：

GPIOA口包括GPIOA_CRL、GPIOA_CRH、GPIOA_IDR、GPIOA_ODR、GPIOA_BSRR、GPIOA_BRR、GPIOA_LCKR共七个寄存器，实际操作GPIOA_CRH、GPIOA_BSRR和GPIOA_BRR三个寄存器。

n（1）寄存器地址确定

GPIOA的地址范围为0x4001 0800 - 0x4001 0BFF

GPIOA_CRH地址为0x40010804，GPIOA_BSRR地址为0x40010810，GPIOA_BRR地址为0x40010814。

n（2）配置端口寄存器

根据引脚端口配置高寄存器(GPIOx_CRH) (x=A..E)描述，配置引脚相关功能，包括引脚工作模式（输入/输出），引脚输出模式（推挽/开漏），引脚上拉/下拉，引脚工作输出速度等。在本例中，配置引脚工作模式为推挽输出，速度50M，配置数据位MODE14[1:0]=0b11,CNF14[1:0]=0b00。

（3）控制引脚电平输出

通过端口位设置/ 清除寄存器(GPIOx_BSRR) (x=A..E)的第14位进行置位（写1）操作或通过端口位清除寄存器(GPIOx_BRR) (x=A..E)进行复位（写1）操作。

**GPIO_Init**

GPIO初始化函数

![image-20251117234857213](https://chenalna.oss-cn-hangzhou.aliyuncs.com/img/image-20251117234857213.png)



![image-20251117235834352](https://chenalna.oss-cn-hangzhou.aliyuncs.com/img/image-20251117235834352.png)

![image-20251117235842782](https://chenalna.oss-cn-hangzhou.aliyuncs.com/img/image-20251117235842782.png)

**GPIO_Write**

![image-20251118000145349](https://chenalna.oss-cn-hangzhou.aliyuncs.com/img/image-20251118000145349.png)

**GPIO_SetBits**(置1)

![image-20251118000456656](https://chenalna.oss-cn-hangzhou.aliyuncs.com/img/image-20251118000456656.png)

**GPIO_RsetBits** 置0

**GPIO_WriteBit** 

![image-20251118001803215](https://chenalna.oss-cn-hangzhou.aliyuncs.com/img/image-20251118001803215.png)

**GPIO输入库函数**



![image-20251125173233603](https://chenalna.oss-cn-hangzhou.aliyuncs.com/img/image-20251125173233603.png)



![image-20251125173508738](https://chenalna.oss-cn-hangzhou.aliyuncs.com/img/image-20251125173508738.png)





![image-20251125173830405](https://chenalna.oss-cn-hangzhou.aliyuncs.com/img/image-20251125173830405.png)

GPIO_ReadOutputData同理





void PA14Operate()

{

  GPIO_InitTypeDef GPIO_InitStructure;

  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_14;

  GPIO_InitStructure.GPIO_Mode =GPIO_Mode_Out_PP;  

  GPIO_InitStructure.GPIO_Speed = 

​                    GPIO_Speed_50MHz;  

  GPIO_Init(GPIOA, &GPIO_InitStructure);      

  GPIO_SetBits(GPIOA,GPIO_Pin_14);   

  GPIO_ResetBits(GPIOA,GPIO_Pin_14); 

}



n1. 在设置一个外设前，必须调用以下一个函数来使能它的时钟：

nRCC_AHBPeriphClockCmd(RCC_AHBPeriph_PPPx, ENABLE); 

nRCC_APB2PeriphClockCmd(RCC_APB2Periph_PPPx, ENABLE); 

nRCC_APB1PeriphClockCmd(RCC_APB1Periph_PPPx, ENABLE); 

n2. 可以调用函数 PPP_Deinit(..)来把外设 PPP 的所有寄存器复位为缺省值：PPP_DeInit(PPP) 

n3. 在外设设置完成以后，继续修改它的一些参数，可以参照如下步骤：

nPPP_InitStucture.memberX = valX; 

nPPP_InitStructure.memberY = valY; 

nPPP_Init(PPP, &PPP_InitStructure);

## SysTick 定时器

大多数操作系统需要一个硬件定时器来产生操作系统需要的滴答中断，作为整个系统的时基。

SysTick定时器能产生中断，CM3为它专门开出一个异常类型，并且在向量表中有它。它使得操作系统和其他系统软件在CM3器件间的移植变得简单。

4个寄存器控制SysTick定时器。

由于SystemTick定时器是内核的器件。因此相关说明在内核参考手册当中。

SysTick定时器被捆绑在NVIC中，用于产生SYSTICK异常（异常号：15）。

所有CM3产品间对其处理都是相同的。它使操作系统和其它系统软

件在CM3器件间的移植变得简单多。

SysTick控制及状态寄存器（地址：0xE000_E010）

​	**STK_CSR**

![image-20251125085739032](https://chenalna.oss-cn-hangzhou.aliyuncs.com/img/image-20251125085739032.png)

外部时钟源，系统时钟/8.

内核时钟：系统时钟，72MHZ

**STK_LOAD**

![image-20251125171437658](https://chenalna.oss-cn-hangzhou.aliyuncs.com/img/image-20251125171437658.png)

**STK_VAL**

![image-20251125171553186](https://chenalna.oss-cn-hangzhou.aliyuncs.com/img/image-20251125171553186.png)





SysTick->CTRL=0x01;(控制寄存器)









## 中断

在程序运行过程中，由于系统外部、内部或者正在运行的程序本身产生紧急、异常的随机事件，处理器在执行完当前指令后立即中止现行程序的运行，自动转入相应的处理程序(中断服务程序)，待处理完后，再返回原来的程序运行，这个过程称为程序中断。

随机：意味着随时都可能产生一个或多个，需随时能处理

中断可分为可屏蔽中断和不可屏蔽中断两类

 向量中断和非向量中断

**中断的一般过程**

①中断源发出中断请求；

②处理器判断当前是否允许中断和该中断源是否被屏蔽；

③优先权排队；

④处理器执行完当前指令或当前中断服务程序可被抢占，则立即停

止当前程序，保护断点地址和处理器当前状态，转入相应的中断服务

程序；

⑤执行中断服务程序；

⑥恢复被保护的状态，执行“中断返回”指令回到被中断的程序



![image-20260121194301543](https://chenalna.oss-cn-hangzhou.aliyuncs.com/img/image-20260121194301543.png)

中断的作用

1.  提高CPU工作效率
2. 实时处理
3. 故障处理
4. 分时操作

**中断源**：能引发中断的事件。通常，中断源都与外设有关。

如按键按下和释放、定时器溢出、串口收到数据等。相关的外设有键盘、定时器、串口等。

**中断请求标志位**：每个中断源都有它对应的中断标志位。

一旦该中断发生，它的中断标志位就会被置位。如果中断标志位被清除，那么它对应的中断不会被响应。因此，一般在中断服务程序结束后要将对应的中断标志位清零。

**中断屏蔽**

可以设置响应的中断屏蔽位，禁止相应某个中断

- 可屏蔽中断

- 不可屏蔽中断

  电源故障、内存出错、总线出错

中断处理大致分为：中断请求、中断响应和中断返回

中断优先级和中断嵌套

同时发生的中断或者嵌套发生的中断，应该优先响应哪一个中断？

**中断嵌套**

当系统执行一个中断服务时，又有新的中断事件发生而产生了新的中断请求。（逐级返回）

### **STM32F103中断系统**

**优先级的定义**

它会影响一个异常是否能被响应，以及何时可以响应。

优先级的数值越小，则优先级越高。CM3 支持中断嵌套，使得高优先级异常会抢占(preempt)低优先级异常。

有 3 个系统异常：复位（Reset，-3），NMI（不可屏蔽异常，-2） 以及硬 fault（-1），它们有固定的优先级，并且它们的优先级号是负数，从而高于所有其它异常。

所有其它异常的优先级则都是可编程的（但不能编程为负数）。

当某种异常或中断被触发，程序计数器指针（PC）将被赋值，该值为异常向量表所填写的服务函数地址（地址+1）。

**嵌套向量中断控制器NVIC**

NVIC集成在ARM Cortex-M3内核中。

Cortex-M3内核支持256个中断，其中包含了16个内核中断（异常）和240个外设中断，并且具有256级的可编程中断设置。

 支持 128级抢占。但是，绝大多数 CM3 芯片都会精简设计。

支持84个异常，包括16个内部异常和68个非内核异常中断。

中断优先级寄存器NVIC_IPRx宽度为8位，每个外部中断可配置的优先级为0~255，

在STM32中，只用到了它的高4位。分组属性如下：

抢占属性：可以打断其它正在执行的中断，这时就出现了中断嵌套。

响应属性：如果两个中断的抢占优先级相同，并且他们同时申请中断，则响应属性

较高的中断优先处理。

用Q表示抢占优先级，用X表示响应优先级，有以下5种方式：

（1）XXXX： 4位全部用来配置响应优先级，16种中断向量都具有不同的响应优先

级；

（2）QXXX：抢占优先级有两种，编号0或1，响应优先级有8种；

（3）QQXX：抢占优先级有四种，编号00、01、10、11；响应优先级也有四种，编

号00、01、10、11；

（4）QQQX：抢占优先级有8种，编号000~111；响应优先级有两种，编号为0、1；

n 

（5）QQQQ：4位全部用于配置抢占优先级，16种中断向量都具有抢占属性；

- 中断响应时处理器状态的自动保存，无须额外指令；
- 中断返回时处理器状态的自动恢复，无需额外指令；
- 支持嵌套和向量中断；
- 支持中断尾链技术。

**优先级分组**

中断优先级被分成了**抢占优先级（组优先级）**和**响应优先级（亚优先级、子**

**优先级）**

抢占优先级决定了抢占行为：当系统   正在响应某异常 L 时，如果

来了抢占优先级更高的异常 H，则 H 可以抢占 L。

子优先级则处理“内务” ：当抢占优先级相同的异常有不止一个

悬起时，就优先响应亚优先级最高的异常。

每个外部中断都有一个对应的优先级寄存器，每个寄存器占用 8

位（STM32只用高四位）。4 个相临的优先级寄存器拼成一个 32

位寄存器。

中断优先级寄存器阵列 0xE000_E400 – 0xE000_E4EF 手册90

![image-20260121213437195](https://chenalna.oss-cn-hangzhou.aliyuncs.com/img/image-20260121213437195.png)

中断配置基础

每个外部中断都在NVIC的下列寄存器中“挂号”

- 使能与除能寄存器 Enable/Disable
- 悬起与解悬寄存器 Pending/clear Pending
- 优先级寄存器 Priority
- 活动状态寄存器 Active

另外，下列寄存器也对中断处理有重大影响

- 异常掩蔽寄存器（PRIMASK,FAULTMASK以及BASEPRI）
- 向量表偏移量寄存器 SCB->VTOR
- 软件触发中断寄存器 软中断类型
- 优先级分组位段 group priority

**中断的悬起与解悬、使能与除能**

如果中断发生时，正在处理同级或高优先级异常，或者被掩蔽，

则中断不能立即得到响应。此时中断被悬起。中断的悬起状态可

以通过“中断设置悬起寄存器(SETPEND)”和“中断悬起清除寄存

器(CLRPEND)”来读取，还可以写它们来手工悬起中断。

SETPENDs:0xE000_E200 – 0xE000_E21C ;

中断#n悬起（异常号16+n）

最后224-239，SETPEND7,16个悬起

 CLRPENDs:0xE000E280 - 0xE000_E29C

CM3 中可以有 240 对使能位／除能位，每个中断拥有一对。 

这 240 个对子分布在 8 对 32 位寄存器中（最后一对没有用完）

。欲使能一个中断，你需要写 1 到对应 SETENA 的位中；欲除能

一个中断，你需要写 1 到对应的 CLRENA 位中；如果往它们中写

0，不会有任何效果。

SETENAs: xE000E100 - 0xE000E11C ;

CLRENAs:0xE000E180 - 0xE000E19C

SERTENA0 ，中断0-31的使能寄存器，共32个使能位， 中断#n使能（异常号16+n）

最后一个224-239，16个使能位

中断

| 名称            | 类型 | 地址             | 复位值 | 描述                           |
| --------------- | ---- | ---------------- | ------ | ------------------------------ |
| SETPENDn（0-7） | R/W  | 0xE000_E200+(4N) | 0      | 中断32n-31+32n的悬起寄存器     |
| SETENAn         | R/W  | 0xE000_E100+(4N) | 0      | 中断32n-31+32n的使能寄存器     |
| PRI_n（0-239）  | R/W  | 0xE00_E400+(N)   | 0      | 外中断#239优先级               |
| ACTIVE7         | R0   | 0xE000_E300+(4N) | 0      | 中断32n-31+32n的活动状态寄存器 |



向量表偏移

| 位段 | 名称      | 类型 | 复位值 | 描述                                |
| ---- | --------- | ---- | ------ | ----------------------------------- |
| 29   | TBLBASE   | RW   | 0      | 向量表是在Code区(0)还是在RAM区（1） |
| 15   | ENDIANESS | R    | -      | 向量表的起始地址                    |



如果需要动态地更改向量表。对于任何期间来水哦，向量表的起始处必须包含以下向量：

- 主堆栈指针MSP的初始值
- 复位向量
- NMI
- 硬fault服务例程

可以在SRAM开出一块用于存储向量表，然后再引导完成后，就可以启用内存中的向量表，实现向量可动态调整的功能。

系统异常优先级寄存器阵列 0xE000_ED18 － 0xE000_ED23

![image-20260121205434606](https://chenalna.oss-cn-hangzhou.aliyuncs.com/img/image-20260121205434606.png)

**活动状态**

每个外部中断都有一个活动状态位。在处理器执行了其 ISR 的第

一条指令后，它的活动位就被置 1，并且直到 ISR 返回时才硬件

清零。由于支持嵌套，允许高优先级异常抢占某个ISR。然而，哪

怕一个中断被抢占，其活动状态也依然为 1。

ACTIVE 寄存器族 0xE000_E300_0xE000_E31C

**PRIMASK与FAULTMASK BASEPRI寄存器**

PRIMASK 用于除能在 NMI 和硬 fault 之外的所有异常，它有效地

把当前优先级改为 0（可编程优先级中的最高优先级）。

MRS：Move to Register from System

MSR：Move to System from Register

该寄存器

可以通过 MRS 和 MSR 以下例方式访问：

1. 关中断（CPSID i）

MOV R0, #1

MSR PRIMASK, R0

2. 开中断（CPSIE i）

MOV R0, #0

MSR PRIMASK, R0

FAULTMASK更绝，它把当前优先级改为‐1。这么一来，连硬fault

都被掩蔽了。使用方案与PRIMASK的相似。但要注意的是，

FAULTMASK会在异常退出时自动清零

在更精巧的设计中，需要对中断掩蔽进行更细腻的控制——只掩蔽

优先级低于某一阈值的中断——它们的优先级在数字上大于等于某

个数。那么这个数存储在哪里？就存储在BASEPRI中。不过，如果

往BASEPRI中写0，则另当别论——BASEPRI将停止掩蔽任何中断。

例如，如果你需要掩蔽所有优先级不高于0x60的中断，则可以如

下编程：

MOV R0, #0x60

MSR BASEPRI, R0

如果需要取消 BASEPRI 对中断的掩蔽，则示例代码如下：

MOV R0, #0

MSR BASEPRI, R0

STM32中断过程

![image-20260121205744057](https://chenalna.oss-cn-hangzhou.aliyuncs.com/img/image-20260121205744057.png)

#### 软件中断

软件中断，包括手工产生的普通中断，能以多种方式产生。最简

单的就是使用相应的SETPEND寄存器；而更专业更快捷的作法，

则是通过使用软件触发中断寄存器STIR，如表所示。

| 位段 | 名称  | 类型 | 复位值 | 描述                                                         |
| ---- | ----- | ---- | ------ | ------------------------------------------------------------ |
| 8：0 | INTID | W    | -      | 影响编号为INTID的外部中断，其悬起位被置位，写入8，则悬起IRQ#8 |



软件触发中断寄存器STIR（地址：0xE000_EF00）。

注意：系统异常（NMI，faults，PendSV等），不能用此法悬起。

而且缺省时就不允许用户程序改动NVIC寄存器的值。如果确实需要

，必须先在NVIC的配置和控制寄存器(0xE000_ED14)中，把比特1（

USERSETMPEND）置位，才能允许用户级下访问NVIC的STIR。

#### 中断向量表

按中断号从小到大依次存放所有中断处理程序的入口地址。

![image-20260121211432149](https://chenalna.oss-cn-hangzhou.aliyuncs.com/img/image-20260121211432149.png)

中断向量



#### 中断服务函数

在启动代码文件startup_stm32f10x_hd.s有预先定义

用户开发自己的中断程序可以在stem32f10_it.c用C语言编写

STM32定义的中断服务函数，在启动文件和stm32f10x_it.c以PPP_IRQHanlder命名

### DMA原理

DMA(Direct Memory Access，直接内存存取)允许内存和内存、内

存和外设之间直接传输大批量数据，在传输过程中不需要CPU 的

读写操作干预，节省了CPU的资源，让CPU更加高效处理其它操作。

DMA传输通常分为四个阶段：请求、响应、传输、结束四个阶段

（1）DMA请求

外设向DMA控制器发送一个请求信号，DMA控制器根据通道的优先权响应请求，同时向总线仲裁器提出总线请 求。当CPU执行完当前总线周期即可

释放总线控制权。DMA优先权管理分2类：

1）软件：每个通道的优先权可以通过寄存器中设置，包括高优先级、中等

优先级、低优先级等。

2）硬件：如果2个请求有相同的软件优先级，则根据硬件确定优先顺序。

（2）DMA响应

当DMA控制器获得总线仲裁器的响应后，DMA开始访问外设。

DMA控制器立即发送给外设一个应答信号。当从DMA控制器得到

应答信号时，外设立即释放它的请求。一旦外设释放了这个请求

，DMA控制器同时撤销应答信号，DMA控制器获得系统总线控制

权。

（3）DMA传送

每个DMA传送由3个操作组成：

1）从外设数据寄存器或者从指定地址的存储器单元执行加载操作；

2）存数据到外设数据寄存器或者存数据到指定地址的存储器单元；

3）执行一次DMA寄存器的递减操作。该寄存器包含未完成的操作数目。

如果开启DMA 半传输中断使能，在传输过半时将产生中断事件。

（4）DMA结束

传输完成和传输错误时产生中断，告诉CPU DMA传输完毕，DMA释放系统

总线交由CPU控制。

### **STM32外部中断**

外部引脚向CPU申请中断——引起电平变化

STM32并没有完全使用Cortex-M3内核的全部中断，只是用了其中的一部

分。外部中断/事件控制器由19条端口线组成，其中0-15线由GPIO端口复

用产生，线EXTIx(x=0-15)由多路选择器选择一个GPIO口的x位作为中断

源。不同端口同一序号的端口线，只能设置一个做为中断. 

EXTI控制器的主要特性如下：

● 每个中断/事件都有独立的触发和屏蔽

● 每个中断线都有专用的状态位

● 支持多达19个中断/事件请求

● 检测脉冲宽度低于APB2时种宽度的外部信号



有19路外部信号，支持19个外部中断

另外三种其他的外部中断/事件控制器的连接如下：

● EXTI线16连接到PVD输出

● EXTI线17连接到RTC闹钟事件

● EXTI线18连接到USB唤醒事件

需要配置PC5为外部中断

![image-20260121213104858](https://chenalna.oss-cn-hangzhou.aliyuncs.com/img/image-20260121213104858.png)

用户手册86页

配置EXTI5为[0010]

外部中断/事件控制器EXTI内部有19套外部中断

EXIT的外部中断/事件输入线也有19根。EXIT0-18；

前16个外部引脚输入。

任何一个时刻（A-G）只能有一个向CPU申请中断





外部中断触发原理



![image-20251130172508460](https://chenalna.oss-cn-hangzhou.aliyuncs.com/img/image-20251130172508460.png)

GPIO信号有高低电平之分，中断涉及到从低电平到高电平或者高电平到低电平的区分，配置触发方式。（这一块对CPU来说，属于外部设备，与芯片厂家有关）

APB外设接口



边沿检测器

事件与中断

- 一路信号（中断）会被送至NVIC向CPU产生中断请求，至于CPU如何响应，由用户编写或系统默认的对应的中断服务程序决定。
- 另一路信号（事件）会向其它功能模块（定时器、USART/DMA等）发送脉冲触发信号。

GPIO既可以输出CPU的信号，也可以输出其它外设的信号。



**STM32中断相关库函数**

中断的处理是一个内外协同的过程，所以其库函数分为NVIC相关库函数和EXTI库函数，分别位于misc.c和stm32f10x_exti.c

NVIC

![image-20251130211030551](https://chenalna.oss-cn-hangzhou.aliyuncs.com/img/image-20251130211030551.png)



![image-20251130211112013](https://chenalna.oss-cn-hangzhou.aliyuncs.com/img/image-20251130211112013.png)

![image-20251201120307944](https://chenalna.oss-cn-hangzhou.aliyuncs.com/img/image-20251201120307944.png)

![image-20251130211427328](https://chenalna.oss-cn-hangzhou.aliyuncs.com/img/image-20251130211427328.png)



![image-20251130211750690](https://chenalna.oss-cn-hangzhou.aliyuncs.com/img/image-20251130211750690.png)





EXTI函数

![image-20251130212725897](https://chenalna.oss-cn-hangzhou.aliyuncs.com/img/image-20251130212725897.png)



![image-20251130213926436](https://chenalna.oss-cn-hangzhou.aliyuncs.com/img/image-20251130213926436.png)



EXTI_Line：0-18 外部中断线

EXTI_Mode:被使能线路的模式

EXTI_Trigger：被使能线路的边沿触发模式

EXTI_LineCmd：定义选中路线的新状态。它可以被设置为ENABLE或者DISABLE。

![image-20251130215106468](https://chenalna.oss-cn-hangzhou.aliyuncs.com/img/image-20251130215106468.png)

![image-20251130215115045](https://chenalna.oss-cn-hangzhou.aliyuncs.com/img/image-20251130215115045.png)

![image-20251130215123995](https://chenalna.oss-cn-hangzhou.aliyuncs.com/img/image-20251130215123995.png)







![image-20251130225822941](https://chenalna.oss-cn-hangzhou.aliyuncs.com/img/image-20251130225822941.png)





![image-20251130225851800](https://chenalna.oss-cn-hangzhou.aliyuncs.com/img/image-20251130225851800.png)





![image-20251130231535143](https://chenalna.oss-cn-hangzhou.aliyuncs.com/img/image-20251130231535143.png)



## 定时器

微控制器中的定时器本质上是一个计数器，可以对内部脉冲或者外部输入进行计数，不仅具有基本的延时/计时功能，还有输入捕获、输出比较和PWM波形输出。



半桥

基本定时器（TIM6和TIM7）

通用定时器（TIM2-TIM5）

高级定时器（TIM1/TIM8)

![image-20251208232158709](https://chenalna.oss-cn-hangzhou.aliyuncs.com/img/image-20251208232158709.png)

 

TIM6和TIM7各包含一个16位自动装载计数器，由各自的可编程预分频器驱动，它们可以为通用定时器提供时间基准，特别地可以为数模转换器DAC提供时钟，在更新事件（计数器溢出）时产生DMA请求/中断。

![image-20251208232654039](https://chenalna.oss-cn-hangzhou.aliyuncs.com/img/image-20251208232654039.png)



定时到哪个数字就放在自动重装载寄存器中。

每来一个脉冲计数器+1

预分频器、计数器、自动重装载寄存器



1. 时基单元 自动重装载的16位累加计数器；计数器的时钟通过预分频器得到。软件可以读写计数器（TIMx_CNT）/自动重装载寄存器（TIMx_ARR）（1-100s就是装载了100）和预分频寄存器（TIMx_PSC）,即使计数器运行时也可操作。

2. 时钟源，基本定时器TIM6和TIM7只有一个时钟源，内部时钟源CK_INT。基本定时器的TIMxCLK来源于APB1的预分频器的输出，系统默认情况下APB1的时钟频率位36MHZ,而挂接在APB1总线的TIMxCLK的频率是APB1总线频率的2倍，即72MHZ.

3. 预分频器

    系数介于1-2^16,通过一个16位寄存器（TIMx_PSC的计数实现分频）因为TIMx_PSC控制预分频寄存器具有缓冲作用，可以在运行过程中改变它的值，新的预分频数值将在下一个更新事件中起作用。

   ![image-20251209154147717](https://chenalna.oss-cn-hangzhou.aliyuncs.com/img/image-20251209154147717.png)

CK_PSC:内部时钟脉冲。

预分频控制寄存器为0时，一个时钟脉冲计一个数（CK_CNT与计数器寄存器），不分频。

写入1，并不是立刻有效，而是在下一次更新事件（UEV）到来之后才更新，预分频缓冲器才改为1。

然后就是两个时钟脉冲记一次数

如果是0的话一个数，1的话两个数，预分频计数器（0，1）

预分频器的控制寄存器的值是N，则分频系数是N+1

4. 计数模式

​	基本定时器只有向上计数工作模式。

![image-20260105234149687](https://chenalna.oss-cn-hangzhou.aliyuncs.com/img/image-20260105234149687.png)

​	脉冲计数器TIMx_CNT从0累加计数到自动重装载数值（TIMx _ARR寄存器），然后重新从0开始计数并产生一个计数器溢出事件。

延时时间公式：$time=(TIMx\_ARR+1)\times(TIMx\_PSC+1)/TIMxCLK$

0-36记了37个数



![image-20251209161238209](https://chenalna.oss-cn-hangzhou.aliyuncs.com/img/image-20251209161238209.png)

预分频寄存器和计数周期都可以看作是分频

$72MHZ/2000/36000=1HZ$

计数个数3600，PSC2000

### 通用定时器

TIM2-5是一个通过可编程的预分频器驱动的16位自动装载计数器构成。它适用于多种场合，包括测量输入信号的脉冲长度（输入捕获）或者产生输出波形（输出比较和PWM）。

1. 16位向上、向下、向上/向下自动装载计数器
2. 4个独立通道
   1. 输入捕获
   2. 输出比较哦
   3. PWM生成
   4. 单脉冲模式输出
3. 产生一个计数器溢出事件，产生中断/DMA

增加了4个捕获/比较寄存器TIMx_CCR

向下计数

从TIMx_ARR的预设值开始向下计数到0，然后重新开始计数。

向上向下计数

中央对齐模式或者双向计数模式

计数器从0开始向上计数到自动加载的值（TIMx_ARR） -1,产生一个计数器溢出事件，然后向下计数到1并且产生一个计数器下溢事件；然后再从0开始重新计数。

时钟选择

内部时钟（CK_INT）

外部时钟模式1：

外部输入捕获引脚（TIx）

外部时钟模式2：

外部触发输入（ETR）

内部触发输入：

(ITRx)

捕获比较通道

捕获比较寄存器（影子寄存器）

包括捕获的输入部分（数字滤波、多路复用和御）和输出部分（比较器和输出控制）

输入部分对相应TIx输入信号采样，产生一个信号，作为百货控制或者输入出发，进入捕获寄存器ICxPS

输出部分产生一个中间波形OCxRef作为基准，链的末端决定最终输出信号的极性。

向上计数和捕获寄存器的值进行比较，如果二者匹配的时候，就反转，输出一定频率的周期和波形

二者不可以同时使用。

捕获：对引脚捕获上升沿或者下降沿

捕获引脚的上升沿或者下降沿，检测到时，帮当前的值记下来，

比较：寄存器的值，如果二者匹配，

是一个复用的概念。

通用定时器的工作模式



n配置

nTIMx_CCMR1（手册写错了）寄存器中的CC1S用于设置输入的通道选择

nTIMx_CCMR1寄存器中写入IC1F设置滤波器，设置滤波器所需的带宽，例如设置为b0011，则需连续采样2^3次以确认是否为抖动信号

n在TIMx_CCER寄存器中写入CC1P设置信号上升沿或者下降沿捕获

nTIMx_CCMR1寄存器的IC1PS设置预分配

nTIMx_CCER寄存器的CC1E=1开启捕获功能

n置TIMx_DIER寄存器中的CC1IE位允许相关中断，CC1DE位允许DMA请求

n捕获发生时

n计数器的值被传送到TIMx_CCR1

nCC1IF标志被设置(中断标志)，如设置了CC1IE位，则会产生一个中断

n如设置了CC1DE位，则还会产生一个DMA请求。

PWM模式

Pulse Width Modulation

脉冲宽度调制，简称脉宽调制

它是利用微处理器的数字输出来对模拟电路进行控制的一种非常有效的计数，控制简单

PWM是一种对模拟信号电平进行数字编码的方法。通过高分辨计数器的使用，方波的占空比被调制用来对一个具体模拟信号的电平进行编码

![image-20260106001447424](https://chenalna.oss-cn-hangzhou.aliyuncs.com/img/image-20260106001447424.png)



PWM的实现方法主要有传统的数字电路、微控制器普通I/O模拟和微控制器的PWM直接输出

有TIMx_ARR寄存器确定频率,TIMx_CCRx寄存器确定占空比

![image-20260106001806728](https://chenalna.oss-cn-hangzhou.aliyuncs.com/img/image-20260106001806728.png)

TIMx_CNT不断和捕获寄存器进行比较。如果X<A，输出高电平

X≥A输出低电平

TIMx-CNT清零并重新开始计数

输出信号周期为（N+1）xTCK_cnt

PWM输出信号的占空比为A/(N+1)



## **通信接口**

串口通信(Serial Communication)，是指外设和计算机间，通过数据信号线、地线等，按位进行传输数据的一种通讯方式

1）UART（通用异步接收发送）串口通信的数据格式

2）通信方式

单工模式（Simplex Communication），只支持一个方向通讯

半双工模式（Half Duplex），允许发送或者接收，但是同一时刻只能是一个方向

全双工模式（Full Duplex），可以发送和接收同时存在

3）数据校验方式：

偶校验（even），加入校验位后数据中1的个数为偶数

奇校验（odd），加入校验位后数据中1的个数为奇数

4）停止位，停止位有1，1.5，2

5）波特率（Baud rate），常见的波特率有4800、9600、19200，115200等

### UART/USART

并行通信是指使用多条数据线传输数据

优点：传输速度快

缺点：占用更多IO口，传输距离短，易受外界干扰。

UART（通用异步收发器）串口通信的数据格式/通信时序

起始位、数据位、校验位、停止位和空闲位等构成，数据传输以帧为单位，起始位、数据位、校验位和停止位构成了一个数据帧。

异步通信，发送和接收方必须使用相同的波特率。

![image-20251201234049250](https://chenalna.oss-cn-hangzhou.aliyuncs.com/img/image-20251201234049250.png)



串行通信：使用一条数据线传输数据。

优点：通信线少，传输距离长

缺点：数据传输速度慢。

 

异步通信

数据传送按帧传输，帧之间可有空闲位。

优点：硬件要求低，实现时简单灵活。

缺点：工作速度低。

同步通信

由1-2个同步字符和多字节数据位组成，多字节数据间无空隙。

优点：传输速度快

缺点：硬件要求高，适用数据批量传送。



2. 通信方式（串行通信的制式）

单工模式（Simplex Communication），只支持一个方向通讯。系统组成以后，发送方和接收方固定。

半双工模式（Half Duplex），允许发送或者接收，但是同一时刻只能是一个方向，不能同时接收和发送。通信系统中只有一个信道。

全双工模式（Full Duplex），可以发送和接收同时存在。 

3. 数据校验方式：

偶校验（even），加入校验位后数据中1的个数为偶数

奇校验（odd），加入校验位后数据中1的个数为奇数

累加和校验：

发送方数据块求和接收方数据块求和

循环冗余码校验：

以特定数去除作二进制数的数据块，将余数作校验码附在数据块后一起发送

4）停止位，停止位有1，1.5，2

5）波特率（Baud rate），每秒传输数据的位数，1波特=1bps.常见的波特率有4800、9600、19200，115200等



![image-20251202000424136](https://chenalna.oss-cn-hangzhou.aliyuncs.com/img/image-20251202000424136.png)

**RS485是半双工，**

**RS422全双工模式，就是2个485**

串行通信

并行通信



针对UART串行通信接口，定义了其接口标准RS232，主要包括信号的电平定义，接

口的机械特性等。

RS232

- RS232电平标准（数据终端之间）

-3~-15V 逻辑“1”

3~15V 逻辑“0”

常见的RS232通讯中，电压为驱动芯片供电电压的2倍，例如驱动芯片工作

电压为3V，则逻辑1为-6V左右，逻辑0为6V左右

- TTL电平标准（对CPU而言）

VCC 逻辑“1”

0V 逻辑“0”

**RS232物理接口**

![image-20251201235927736](https://chenalna.oss-cn-hangzhou.aliyuncs.com/img/image-20251201235927736.png)

LSB最低比特位

USART(通用 同步 异步收发器，Universal Synchronous/Asynchronous Receiver/Transmitter)

同步模式需要引脚：

CK:发送器时钟输出

IrDA模式需要引脚：

RX：接收

TX：发送

接收发送引脚必须交叉相连



RS485

![image-20260121233106438](https://chenalna.oss-cn-hangzhou.aliyuncs.com/img/image-20260121233106438.png)

![image-20260121233053707](https://chenalna.oss-cn-hangzhou.aliyuncs.com/img/image-20260121233053707.png)

SW_RX

nRTS

nCTS

SCLK

CK



二者必须共同接地

硬件流控制模式需要引脚：

提供了一种与工业标准NRZ异步串行数据格式的外部设备进行全双工

支持同步单向通信和半双工单线通信

允许多处理器通信，DMA方式

分数波特率发生器



![image-20251202000237052](https://chenalna.oss-cn-hangzhou.aliyuncs.com/img/image-20251202000237052.png)

寄存器



![image-20260121233204444](https://chenalna.oss-cn-hangzhou.aliyuncs.com/img/image-20260121233204444.png)

USART中断：

发送期间的中断事件：发送完成（TC）、清除发送（CTS）、发送数据寄存器空（TXE，指的是发送方数据全部发出）（用于并行通信）

接受期间：空闲总线检测（IDLE）、接受数据寄存器非空（RXNE）等等

![image-20251209171847421](https://chenalna.oss-cn-hangzhou.aliyuncs.com/img/image-20251209171847421.png)

USART相关寄存器

状态寄存器USART_SR

USART_DR

USART_BRR

USART_CR1

USART_CR2

USART_CR3

USART_GTPR

相关库函数

![image-20251202001839744](https://chenalna.oss-cn-hangzhou.aliyuncs.com/img/image-20251202001839744.png)





![image-20251202001850067](https://chenalna.oss-cn-hangzhou.aliyuncs.com/img/image-20251202001850067.png)

![image-20251202001958570](https://chenalna.oss-cn-hangzhou.aliyuncs.com/img/image-20251202001958570.png)





![image-20251202002011197](https://chenalna.oss-cn-hangzhou.aliyuncs.com/img/image-20251202002011197.png)



![image-20251202002023614](https://chenalna.oss-cn-hangzhou.aliyuncs.com/img/image-20251202002023614.png)





![image-20251202002033715](https://chenalna.oss-cn-hangzhou.aliyuncs.com/img/image-20251202002033715.png)



![image-20251202002045995](https://chenalna.oss-cn-hangzhou.aliyuncs.com/img/image-20251202002045995.png)

![image-20251202002057039](https://chenalna.oss-cn-hangzhou.aliyuncs.com/img/image-20251202002057039.png)

![image-20251202002103314](https://chenalna.oss-cn-hangzhou.aliyuncs.com/img/image-20251202002103314.png)

![image-20251202002109937](https://chenalna.oss-cn-hangzhou.aliyuncs.com/img/image-20251202002109937.png)

![image-20251202002115408](https://chenalna.oss-cn-hangzhou.aliyuncs.com/img/image-20251202002115408.png)





	USART_InitTypeDef USART_InitStructure; 
	//Òý½ÅÊ±ÖÓ
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
	//´®¿ÚÊ±ÖÓ
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);


  /*
  *  USART1_TX -> PA9 , USART1_RX ->	PA10
    */				
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;	         
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; 
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; 
    GPIO_Init(GPIOA, &GPIO_InitStructure);		   

  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;	        
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;  
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; 
  GPIO_Init(GPIOA, &GPIO_InitStructure);
	
		//´®¿Ú1³õÊ¼»¯
	USART_InitStructure.USART_BaudRate = 115200;
	USART_InitStructure.USART_WordLength = USART_WordLength_8b;
	USART_InitStructure.USART_StopBits = USART_StopBits_1;
	USART_InitStructure.USART_Parity = USART_Parity_No;
	USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
	USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
	USART_Init(USART1, &USART_InitStructure);
	
	USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);
	USART_ITConfig(USART1, USART_IT_TC, ENABLE);//¿ÕÏÐÖÐ¶Ï
	USART_ClearITPendingBit(USART1, USART_IT_TC);//Çå³ýÖÐ¶ÏTCÎ»
	USART_ClearITPendingBit(USART1, USART_IT_RXNE);//Çå³ýÖÐ¶ÏTCÎ»
	USART_Cmd(USART1, ENABLE);		





1. 点灯

2. UART来通信串口





1. RS232/RS485

2. IIC

同步串行接口

如果说Gate=0，SDA/SCL=0

如果GATE=1,有VCC提供，SDA/SCL=1

（PNP）

Gate/Drain/Source

总线仲裁功能



MSbF（irst）

3. CAN

4. SPI

打开端口时钟

配置GPIO模式

USART串口初始化

USART中断初始化

### SPI

串行外设接口（Serial Peripheral Interface 串行外设接口）

nSPI通信主要是主从方式通信。这种模式通常只有一个主机和一个或者多个从机，标准的SPI是4根线，分别是SSEL(片选，也写作CS)、SCLK(时钟，也写作SCK)、MOSI(主机输出从机输入(Master Output/Slave Input)和MISO(主机输入从机输出Master Input/Slave Output)。

n时钟信号线SCLK只能由主设备控制，从设备不能控制。这样的传输方式有一个优点，在数据位的传输过程中可以暂停，也就是时钟的周期可以为不等宽，因为时钟线由主设备控制，当没有时钟跳变时，从设备不采集或传送数据。SPI还是一个数据交换协议：因为SPI的数据输入和输出线独立，所以允许同时完成数据的输入和输出。

nSPI接口的一个缺点：没有指定的流控制，没有应答机制确认是否接收到数据。



高速全双工串行同步通信接口，SPI采用主/从（Master/Slave）模式，支持一个或多个从设备。

SCK (Serial Clock),即时钟线，由主设备产生。

MOSI（Master Output Slave Input）

主设备数据输出/从设备数据输入

MISO反之

SS（Slave Select），SPI从设备选择信号线。

SCK/MOSI/MISO直接相连。

SPI接口还有包含一个串行移位寄存器。

两个设备的数据交换

由于串行，最多只有两根数据线，一根发送数据线，一根接收数据线。

SPI互连——一主一从

![image-20260107111343536](https://chenalna.oss-cn-hangzhou.aliyuncs.com/img/image-20260107111343536.png)

一个SPI主设备和一个SPI从设备进行通信

主设备SS置高电平，从设备SS接地。

不需要像USA交叉，因为在信号命名的时候已经表明了数据传输的方向

一主多从

一个SPI主设备可以和多个SPI从设备进行通信。

SPI设备共享时钟线和数据线。

任意时刻只能由一个主设备和从设备，因此采用分时的选中从设备。

在主设备使用多个GPIO引脚来选择不同的SPI从设备。





![image-20251209174321363](https://chenalna.oss-cn-hangzhou.aliyuncs.com/img/image-20251209174321363.png)



STM32F103的SPI模块允许MCU与外部设备以半/全双工、同步、串行方式通信。通常，它被配置为主模式，并为外部从设备提供通信时钟。



主要由波特率发送器、收发控制、数据的存储转移三部分组成

![image-20251209174713764](https://chenalna.oss-cn-hangzhou.aliyuncs.com/img/image-20251209174713764.png)

时钟信号的相位和极性

![image-20260107111931511](https://chenalna.oss-cn-hangzhou.aliyuncs.com/img/image-20260107111931511.png)

时钟极性CPOL：空闲时候的电平

时钟相位CPHA：跳变沿进行数据位的存取

n有四种模式。

nCPOL(Clock Polarity)是时钟的极性。通信的整个过程分为空闲时刻和通信时刻，如果空闲状态SCK是低电平，那么就是 CPOL=0;如果SCK在数据发送之前和之后的空闲状态是高电平，那么就是CPOL=1；

nCPHA是时钟相位，CPHA=0，表示数据采样从一个时钟的先沿开始。CPHA=1，表示数据采样从一个时钟的后沿开始。

n

CPOL=0时，第一个时钟的先沿是上升沿，后沿是下降沿。CPOL=1时，第一个时钟的先沿是下降沿，后沿是上升沿

4种时序

| SPI时序模式 | CPOL时钟极性 | CPHA时钟相位 | 空闲时SCK电平 | 采样时刻   |
| ----------- | ------------ | ------------ | ------------- | ---------- |
| 模式0       | 0            | 0            | 低电平        | 第一跳变沿 |
| 模式1       | 0            | 1            | 低电平        | 第二跳变沿 |
| 模式2       | 1            | 0            | 高电平        | 第一跳变沿 |
| 模式3       | 1            | 1            | 高电平        | 第二跳变沿 |

数据帧格式

SPI_CR1寄存器的LSBFIRST，输出数据位可以MSB在先或LSB在先。

SPI_CR1寄存器的DFF位，每个数据帧可以是8位或16位

数据帧格式决定发送/接收的数据长度

![image-20260122001722233](https://chenalna.oss-cn-hangzhou.aliyuncs.com/img/image-20260122001722233.png)

SPI的主模式、从模式配置

![image-20251209175258988](https://chenalna.oss-cn-hangzhou.aliyuncs.com/img/image-20251209175258988.png)

    SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;//SPI设置为双线双向全双工
    SPI_InitStructure.SPI_Mode = SPI_Mode_Master;//设置为主SPI
    SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;//设置SPI的数据大小:SPI发送接收8位帧结构
    SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low;//选择了串行时钟的稳态:时钟悬空高
    SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge;//数据捕获于第二个时钟沿
    SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;//(SPI_NSS_Soft)此时NSS引脚可以配置成普通GPIO去控制从设备
    SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_2;//Fclk/2
    SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB; /* Initialize the SPI_FirstBit member */
    SPI_Init(SPI2, &SPI_InitStructure);
    SPI_Cmd(SPI2, ENABLE);



![image-20251209175315050](https://chenalna.oss-cn-hangzhou.aliyuncs.com/img/image-20251209175315050.png)

| 0xAB | Read Device ID              | 读取设备ID（需先发送3个空字节）       |
| ---- | --------------------------- | ------------------------------------- |
| 0x90 | Read Manufacturer/Device ID | 读取制造商和设备ID                    |
| 0x20 | Sector Erase                | 擦除一个扇区（4KB）                   |
| 0x02 | Page Program                | 写入一个页（256字节）                 |
| 0x03 | Read Data                   | 读取数据                              |
| 0x06 | Write Enable                | 写使能（必须在使用写/擦除操作前执行） |

### IIC

nI2C是单片机系统中最常用的串行通信接口之一。I2C(Inter-Integrated Circuit BUS) 集成电路总线，该总线由NXP（原PHILIPS）公司设计，多用于主控制器和从器件间的主从通信，在小数据量场合使用，传输距离短，任意时刻只能有一个主机等特性。

![image-20260121235122204](https://chenalna.oss-cn-hangzhou.aliyuncs.com/img/image-20260121235122204.png)

I2C，Inter-Integrated Circuit，集成电路总线

双向、二线制、同步串行总线

是一种串行通信总线协议，用于连接微控制器、传感器、存储器和其它外设

I2C串行总线概述

由数据线和时钟线SDA/SCL

上拉电阻使其在空闲时段是高电平

采用双向传输方式，主从设备可以在双向通信

允许多个器件，支持多主机通信

![image-20260106002607135](https://chenalna.oss-cn-hangzhou.aliyuncs.com/img/image-20260106002607135.png)

n（1）只要求两条总线线路，一条是串行数据线SDA，一条是串行时钟线SCL。（I2C是半双工，而不是全双工）。

n（2）每个连接到总线的器件都可以通过唯一的地址和其它器件通信，主机/从机角色和地址可配置，主机可以作为主机发送器和主机接收器。

n（3）I2C是真正的多主机总线，如果两个或更多的主机同时请求总线，可以通过冲突检测和仲裁防止总线数据被破坏。





n仲裁过程：

n逐位仲裁： 仲裁不是在整个数据包结束后进行的，而是在传输过程中逐位（Bit by Bit） 进行的，从起始条件（S）后的第一个位（即地址的最高位MSB）开始。

n竞争过程：

n所有主设备同时发送当前要传输的位。

n每个主设备在发送时钟（SCL）的高电平期间，读取总线（SDA）的实际状态。

n赢得仲裁： 如果一个主设备发送的是‘1’（释放SDA），并且读回的也是‘1’；或者发送的是‘0’（拉低SDA），并且读回的也是‘0’，那么它仍然保持在竞争中，没有被仲裁掉。

n失去仲裁： 如果一个主设备发送的是‘1’，但从总线上读回的是‘0’，它立即知道自己失去了仲裁。因为总线上有另一个设备发送了‘0’，而‘0’在“线与”逻辑中优先级更高。

n退出机制：

n一旦某个主设备检测到自己失去仲裁，它必须立即停止对数据线（SDA）的任何驱动，将其切换为高阻态（输入模式），从而释放总线。

n

n关键点： 失去仲裁的主设备不会产生一个停止条件，因为这可能会干扰赢得仲裁的主设备正在进行的通信。它必须悄悄地退出。 



I2C协议层



通过I2C的器件地址来确定对象

数据位的有效性规定

SCL高电平，SDA保持稳定

SCL低电平，才允许变化

SCL高电平，SDA的任何跳变都会被识别成为一个起始信号或停止信号

n起始和结束条件

n起始条件（S）：当SCL为高电平的时候，SDA线上由高到低的跳变被定义为起始条件。

n结束条件（P）：当SCL为高电平的时候，SDA线上由低到高的跳变被定义为停止条件。

n起始和终止信号都是由主机发出。总线在起始条件之后，视为忙状态，在停止条件之后被视为空闲状态



![image-20260106002812862](https://chenalna.oss-cn-hangzhou.aliyuncs.com/img/image-20260106002812862.png)



数据传送格式

每个字节为8位，，高尾在前，低位在后

以一个字节为单位

必须等待接收方返回一个应答响应信号

响应脉冲器件，接收方将SDA拉低为应答信号，接收方使SDA维持高电平为非应答信号。

总线寻址

DA3-DA9：4位器件地址是I2C总线器件固有的地址编码

A2-A0：3位引脚地址用于相同地址器件识别

R/W：数据传送方向，读和写

n**应答**

  **每当主机向从机发送完一个字节的数据，主机总是需要等待从机给出一个应答信号，以确认从机是否成功接收到了数据，从机应答主机所需要的时钟仍是主机提供的，应答出现在每一次主机完成8个数据位传输后紧跟着的时钟周期，低电平0表示应答，1表示非应答**

![image-20260122000912808](https://chenalna.oss-cn-hangzhou.aliyuncs.com/img/image-20260122000912808.png)



## AD-DA数字模拟转换

ADC

模拟数字转换器

采样、量化、编码。

每隔T时刻记录一次数值

量化

把每个时刻的采样点呀表和市委指定的最小单位的整数倍

通常是2^n个

量程

FSR，

是指ADC所能转换的模拟输入电压的范围，分为单极性和双极性两种类型

分辨率

ADC所能分辨的最小模拟输入两

精度

ADC的数字输出的模拟输入值和理论上模拟输入之差

转换时间

主要类型

逐次逼近式

将模拟输入量逐次与UREF/2比较

双积分式

V/F变换式

它由软件或硬件出发，在ADC时钟ADCLK得到驱动下对规则通道或诸如通道中的模拟信号进行采样、量化和编码

![image-20260106004602219](https://chenalna.oss-cn-hangzhou.aliyuncs.com/img/image-20260106004602219.png)



18个模拟输入通道，可测量16个外部模拟信号和2个内部信号

规则组和注入组

外部触发转换

转换可以由外部事件出发（定时器捕获，EXTI线）

如果设置了EXTTRIG控制位，则外部事件就能够出发转换

规则组和注入组转换结束时能产生中断

DMA因为规则通道转换的值存储在一个仅有的数据寄存器当中，所以当转换多个规则通道时需要使用DMA



D/A，或者DAC

运算放大器

虚短

虚断 

计算机折半思想，逐次逼近

DMA

DMA是一种完全由硬件执行数据交换的工作方式。它由DMA控制器而不是CPU控制在存储器和存储器、存储器和外设之间的批量数据传输

一个DMA控制器有若干个数据传输链路，称为DMA流

这些连接在同一DMA流上的多个外设可以分时复用这条DMA传输链路，但同一时刻，一条DMA传输链路上只能有一个外设进行MDA数据传输。



## RTOS实时系统

正常程序流程
中断发生

中断是前台
while循环是后台
状态保存到CPU里？

第一个中断就是Reset_Handler栈指针（函数指针）




平均硬度复杂性
平均失效时间
平均MTFF
MTIR
优先级反转

优先级继承、








_INCLUDE 
是否启用函数
#if
#endif

INCLUDE_xsEMAPHOREgETmUTEXhOLDER





config 配置

基于操作系统，就是优先级抢占的方式

纯只有前后台系统，分为循环和中断的后台与前台



## OLED显示屏

LCD是背光的，通上电会亮

OLED是自发光的，通上电如果不控制依然不会亮

和LCD显示屏不一样

硬件SPI连接方式

![](https://chenalna.oss-cn-hangzhou.aliyuncs.com/img/image-20260107113314423.png)

模拟SPI连接方式

![image-20260107113335335](https://chenalna.oss-cn-hangzhou.aliyuncs.com/img/image-20260107113335335.png)

## 程序开发



嵌入式系统设计 5 个阶段：系统需求分析、系统设计、系统集成和调试、系统测试、系统维护。

系统设计阶段是根据系统需求分析的结果，设计出满足用户需求的嵌入式

系统产品。系统设计主要包括体系结构设计、硬件平台的选择、软件平台选择、硬件与软件的划分、嵌入式系统设计方法 5 个方面



**嵌入式系统测试方法** 

答：一般来说，嵌入式系统测试方法有黑盒测试和白盒测试两种方法。黑盒测试

也称功能测试，包括极限情况测试、异常测试、边界测试等；白盒测试也称覆盖

测试，包括语句测试、分支测试、判定和覆盖测试等。 

**嵌入式系统测试策略** 

答：单元测试；集成测试；系统测试和确认测试。

DA数模转换器
软件实现：1024/3.3*3.0,sin函数
硬件实现：src、dma

USART1->SR	RXNE 位 = 1
USART1->DR	0x41（ASCII ‘A’）


要求条理清晰、层次分明，知识点记录详细全面，不少于800字。

计数功能
Timer
Counter
如果外部的时钟不是稳定的
那么单纯的定时就没有用了
555定时器
多通道捕获
CNT/CCR
但是GPIO中断时不准确的
capture

比较输出功能
compare output
产生PWM信号占空比
死区插入
死去控制
互补输出
一个低一个就高
减少发热

 a/0.6
移位
a*1024/3.6*1024
=(a*284)>>10

每次都
原地址每次递增
AD实验，读取温度通道，算出通道，显示在数码管上，1s读一次

DA实验，每一次取一个结果放到内存里

第一行设置出输出并且其为0
其它行设置为浮空输入
列输入
就可以判断哪个按键输入了

再把第二行设置成输出并且为0
其它行设置为浮空输入
浮空上拉
浮空下拉
模拟输入接按键

  

再显示在数码管上。按钮分布：
第一行PE7/KEY8
第二行PE6/KEY7
第三行PE5/KEY6
第四行PE4/KEY5

可以逐行扫描

第一列KEY1 第二列KEY2 第三列 KEY3 第四列KEY4而我的数码管RCC->APB2ENR|=3<<4; //使能 PORTC 和 PORTD 时钟 
GPIOC->CRH&=0xFF0000FF; 
GPIOC->CRH|=0x00333300; //PC.10 11 12 13 推挽输出 
// GPIOC->ODR|=15<<10; //PC.10 11 12 13 输出高
GPIOD->CRL&=0X00000000; 
GPIOD->CRL|=0X33333333;//PD.0 1 2 3 4 5 6 7 推挽输出 
// GPIOD->ODR |= 0xFF; //PD.0 1 2 3 4 5 6 7 输出高
GPIOD->BSRR = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3; //输出高
电平
GPIOD->BSRR = GPIO_Pin_4 | GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7;
GPIOC->BSRR = GPIO_Pin_10 | GPIO_Pin_11 | GPIO_Pin_12 | GPIO_Pin_13;下图是开发板数码管模块原理图，abcdefgh 八段分别连接到 CPU 的 PD0、PD1、PD2、
如下图所示，四个数码管共同分装在一个整体外壳中，其内部每个数码管的 8 段为 8 个
LED 灯，LED 灯采用共阳极接法设计。由于四个数码管数据线采用共用方式，四个数码管
在某一时刻只能控制其中某一个显示，或者四个数码管同时显示相同内容。显然，在大部分
场合时要求显示不同内容，所以只能通过对 4 给数码管进行分时控制控制，循环显示不同内
容，在保证一定的扫描速度的情况下，人的眼睛是看不出来只亮了其中一个数码管(其原理
类似显示器的刷新功能)。
PD3、PD4、PD5、PD6、PD7 。DIG1-4 分别接至 CPU 的 PC10、PC11、PC12、PC13。 请帮我完成输入按钮（每一行有4个按钮，可以显示数字1-4），输出到数码管 

一个是先判断再扫描。
一个是先扫描再判断。

如果是乘方（a^b）直接转换成b位（a-1）的a进制数可快了（例如2^3=111=8）

每隔10毫秒


LastCOde上一次键码
CurrentCode当前键码
如果current=last，说明按键持续按下
age：当前键码检测到多少次（寿命）

age++
如果不等，age清零

滤波100ms十次key_age=10,连续十次都是一个键码，于是抖被消掉了

0-FF 255

改成中断的方式，把所有的处理放在定时器里

5ms、10ms


key：按键扫描每10ms必做一次，不能做长延时
keyscan：display（） 1000ms/40=25
要提升频率
systick定个5ms
或者按键区定一个终端，systick只做一个扫描。

通过一盏灯来调试

两个按键同时按下会有短路的风险


串口
一头插在电脑上一头插在

SSCOM

第一行设置出输出并且其为0
其它行设置为浮空输入
列输入
就可以判断哪个按键输入了

再把第二行设置成输出并且为0
其它行设置为浮空输入


再显示在数码管上

查零复用


push-pull
