ADC、Flash、SPI、watchdog
ADC
ADC(Analog-to-Digital Converter), 即模拟信号 - 数字信号转换器
在STM32F103C8T6中, 同样具有ADC功能.
以我们的芯片为例, 也存在2个片上外设ADC, 即ADC1和ADC2, 这两个ADC片上外设都挂载在APB2总线上.
我们的ADC片上外设, 是一种具有12位逐次逼近型ADC,ADC转换的本质是不断的电压比较
如何把0~4095映射到0~3.3?
把3.3分成4096份,每份3.3/4095=8.06e-4,2000的离散值对应的电压是1.61
2v对应的离散值是4095/3.3*2=2481,对应的二进制是1001 1011 0.001
下面就是实现了4095/3.3*2这个过程,想一想二进制除法的实现
初始化:SAR 从最高位(MSB,即第11位)开始试探,其余位清零。
例如,首次试探值:
1000 0000 0000
(即 2048,对应中间电压)。
比较判断:
若 VIN≥VDACVIN≥VDAC,保留该位为
1
,否则置0
。
逐位逼近:
移至下一位(如第10位),重复比较,直到最低位(LSB)。
完成转换:
经过 12次比较 后,SAR 中的值即为最终数字输出。
在配置使用ADC时, 通道是一个重要数据, 而ADC的通道和固定的引脚绑定.
// 在每次AD转换前配置规则组,这样可以灵活更改AD转换的通道
// 第一个参数: 采用ADC1还是ADC2
// 第二个参数: 使用那个通道, 比如ADC_Channel_1表示PA1通道; ADC_Channel_4表示PA4通道
// 第三个参数: 规则组可以有最多16个通道,可以指定该通道在规则组中的采样顺序
// 第四个参数: 对输入电压采样的时间长短; ADC_SampleTime_55Cycles5表示采样55.5个ADC时钟节拍ADC_RegularChannelConfig(ADC1, ADC_Channel_4, 1, ADC_SampleTime_55Cycles5);// 软件触发一次ADC1采样ADC_SoftwareStartConvCmd(ADC1, ENABLE);
// 等待采样结束: 采样是否结束, 高根据寄存器EOC标志位来判断while (ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC) == RESET);
规则组 和 注入组:在ADC中我们可以把采样分组, 无论是规则组还是注入组, 它的本质都是去记录采样的通道(要依次采样那几个通道, 以组划分)
注意: 注入组的优先级要比规则组高,注入组最多可以配置同时采集4个通道, 并且每个通道的采集数据分别用一个寄存器存储。而规则组最多可以同时采集16个通道, 但是16个通道的采集数据, 共用一个寄存器存储, 有可能产生数据覆盖
连续转化: 上一次采集完毕, 立马进行下一次采集(完全不需要额外的软件触发或者硬件触发)
扫描模式:每次触发, 就把组内的配置的所有通道的数据都采集一遍
// ADC初始化ADC_InitTypeDef ADC_InitStructure;
// ADC模式选择: 暂时选择默认独立工作模式(即只使用ADC1)ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;
// 数据对齐(和采样数据精度相关)ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
// 选择触发采集模式(不用外部硬件触发, 使用软件触发)ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
// 不进行连续转化, 不开启, 必须触发过来, 才进行采样ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;
// 不开启扫描模式, 因为我们准备每个组设置一个采样通道(通过设置, 采样, 修改设置, 采样的方式)ADC_InitStructure.ADC_ScanConvMode = DISABLE;
// 开启的通道数(给扫描模式指明扫描几个通过个数用的)ADC_InitStructure.ADC_NbrOfChannel = 1;
// 初始化ADC_Init(ADC1, &ADC_InitStructure);// 启动ADC
Flash
主存储器: 用来存放程序代码和一些不可变数据(特点是断电不丢失)
起始地址0x08000000 当前默认64KB(0x08000000~0x0800FFFF)
信息块: 是STM32内部Flash额外划分的一部分, 主要用于存放设备的用户配置数据或校准数据.该区域通常是只读的, 不能随意修改.
接口寄存器: 用于控制和配置STM32的外设(如GPIO、USART、SPI、I2C、ADC等)
// addr: 页首地址
void erasePage(uint32_t addr) {FLASH_Unlock(); //解锁FLASH_ErasePage(addr); //页擦除FLASH_Lock(); //加锁
}
看门狗
看门狗(Watchdog Timer, WDT)是一种硬件定时器, 用于防止系统死机或者长时间运行异常代码, 避免程序跑飞.
独立看门狗
假设今天预分频设为4分频, 重装值设置为111111111111 -> 4095,求超时时间是多少?
40kHz/4=10kHz,所以0.1ms一个时钟节拍,所以超时时间是409.5ms
IWDG_ReloadCounter(); // 喂狗喂狗的本质, 就是通过键寄存器的修改(IWDG_KR), 要求递减计数器进行计数值的重装.避免计数值减为0.
窗口看门狗
窗口看门狗(WWDG)是一种 基于时间窗口 的看门狗定时器,主要用于 高可靠性嵌入式系统(如汽车电子、工业控制),确保程序在 严格的时间范围内 进行喂狗(刷新),避免 过早或过晚 喂狗导致的系统异常。
窗口看门狗 vs 独立看门狗(IWDG)
特性 | 窗口看门狗(WWDG) | 独立看门狗(IWDG) |
---|---|---|
时钟源 | 来自 APB1(PCLK1) | 独立低速时钟(LSI,~32kHz) |
复位条件 | 喂狗时间 不在窗口内 | 超时未喂狗 |
时间精度 | 较高(依赖系统时钟) | 较低(依赖 LSI) |
窗口约束 | 必须 在最小和最大时间之间 喂狗 | 只需 在超时前 喂狗 |
应用场景 | 对 时序严格 的任务(如安全控制) | 防 程序跑飞(通用看门狗) |
36MHz/4096/4=2197Hz第一次4096分频,第二次4分频
一个节拍0.46ms,WWDG_CR64个节拍复位64*0.46=29.44ms,最晚的时间
假设WWDG_CFR的值是112,并且一旦,它的数值小和喂狗时就会复位
计数器(WWDG_CR):
- 7 位递减计数器(T6~T0),当
T6
从 1→0 时触发复位(即0x40
→0x3F
)。 - 喂狗通过写入
WWDG_CR
重置计数器值。
配置寄存器(WWDG_CFR):
- 设置 窗口值(W6~W0),定义喂狗的 允许时间窗口。
- 设置 预分频器(WDGTB) 调整时钟频率。
时间窗口
最小时间(上窗口 → “喂狗允许的起始点”):由
WWDG_CFR
的窗口值(W)决定,喂狗 不能早于 计数器值 > W。最大时间(下窗口 → “喂狗截止点”):计数器值从初始值递减到
0x3F
(即 T6=0)时复位,喂狗 不能晚于 此点。
SPI
SPI协议的核心特性包括高位优先(MSB First)的数据传输方式、可配置的时钟极性(CPOL)和相位(CPHA), 以及灵活的时钟速率调整能力. 时钟极性(CPOL)决定了时钟信号在空闲状态下的电平(高或低), 而时钟相位(CPHA)则定义了数据采样相对于时钟边沿的位置. 通过组合CPOL和CPHA, SPI支持四种不同的工作模式(Mode0至Mode3),以适应不同设备的需求.此外,SPI协议允许主机通过软件或硬件方式控制片选信号(NSS/CS)从而实现对多个从机的灵活管理.
MISO:Master Input Slave Output/主设备数据输入,从设备数据输出
MOSI:Master Output Slave Input/主设备数据输出,从设备数据输入
SCK:Serial Clock/时钟信号,由主设备产生
CS:Chip Select/片选信号,由主设备控制
提问两台从机可以同时给,主机发消息吗?
不可以,会产生冲突
需要注意的是:
以W25Q64为例, 其总共占用64Mbit即8M字节
在其内部又以"块"->"扇区"->"页"来划分 :一个块=64K字节=16扇区、一个扇区=4K字节=16页
一个页=256字节
并且在对其进行操作的时候要求先擦除, 再写入
擦除要以扇区为单位
对W25Q64进行操作的时候:
读取数据过程: 发送读取命令->发送地址->发送数据(是为了读数据)
写入数据过程: 发送写入命令->发送地址->发送写入内容
SPI 通过 时钟极性(CPOL) 和 时钟相位(CPHA) 定义数据传输时序,共有 4 种模式:
模式 | CPOL | CPHA | 时钟空闲状态 | 数据采样边沿 |
---|---|---|---|---|
0 | 0 | 0 | 低电平 | 上升沿 |
1 | 0 | 1 | 低电平 | 下降沿 |
2 | 1 | 0 | 高电平 | 下降沿 |
3 | 1 | 1 | 高电平 | 上升沿 |
MQTT
MQTT(Message Queuing Telemetry Transport)是一种轻量级的 发布/订阅(Pub-Sub) 消息协议,专为 低带宽、高延迟或不可靠网络 环境设计。
正常来讲,HTTP头部很大,这样就会导致许多问题,MQTT的轻量级,就体现在头部轻量
MQTT vs HTTP 对比
特性 | MQTT (消息队列遥测传输) | HTTP (超文本传输协议) |
---|---|---|
协议类型 | 发布/订阅(Pub-Sub)模型 | 请求/响应(Request-Response)模型 |
设计目标 | 低带宽、高延迟、不可靠网络(IoT 优化) | 通用 Web 通信(文档传输) |
连接方式 | 长连接(基于 TCP + 可选 TLS) | 短连接(默认无状态,HTTP/2 支持长连接) |
消息大小 | 极轻量(头部仅 2 字节起) | 较大(HTTP 头部通常几百字节) |
通信模式 | 异步(Broker 中转消息) | 同步(客户端主动请求,服务端响应) |
QoS 支持 | ✅ 3 个级别(0/1/2) | ❌ 无原生 QoS(依赖 TCP 重传) |
实时性 | ⚡ 高(低延迟推送) | ⏳ 较低(需轮询或 WebSocket) |
适用场景 | IoT、传感器数据、实时控制 | Web 页面、REST API、文件传输 |
典型端口 | 1883(明文)、8883(TLS) | 80(HTTP)、443(HTTPS) |
广播/多播支持 | ✅(Topic 订阅) | ❌(需额外协议如 WebSocket) |
消息保留 | ✅(Broker 可存储最后一条消息) | ❌(无状态,需额外实现) |
连接开销 | ⚡ 极低(适合嵌入式设备) | ⚠️ 较高(HTTP 头部 + Cookie 等) |