嵌入式相关
嵌入式相关
ZEROKO14嵌入式相关知识点
嵌入式系统:以应用为中心、软硬件可裁剪的专用计算机系统,嵌入到设备中执行特定任务(如智能手环、工业控制器)
特点:
- 实时性:工业控制需毫秒级响应(如机器人紧急制动)
- 低功耗:电池设备需优化能耗(如物联网传感器)
- 资源受限:内存/处理器性能远低于PC(ESP8266仅80KB RAM)
基础知识
电路基础
基尔霍夫定律
基尔霍夫电流定律(KCL):在集总参数电路中,任意时刻,对任意节点流出或流入该节点电流的代数和等于零
$$ i_1+i_2+i_3=i_4+i_5 $$
基尔霍夫电压定律(KVL):在集总参数电路中,任一时刻,沿任一闭合路径绕行,各支路电压的代数和等于零
$$ U_{S1}-U_{S2}-U_2-U_1=0 $$
模拟数字电路
- 数字电路是处理数字信号的电路。
数字信号:数字信号是离散的电信号。只有0和1区分。 - 模拟电路是处理模拟信号的电路。
模拟信号:模拟信号是连续的电信号。以电压为纵坐标,时间t为横坐标为例,函数曲线是连续的,不存在突然阶跃的情况
单片机基础
计算机的最小组成
计算机的组成非常复杂,但其基本单元非常简单
- 笔记本电路板上有很多芯片,一个芯片就是一个系统
- 一个系统由很多模块组成,如加法器,乘法器等
- 一个模块由很多逻辑门组成,如非门,与门,或门等
- 逻辑门由晶体管组成,如PMOS管和NMOS管灯
芯片 => 很多模块组成 => 很多逻辑门组成 => 很多晶体管组成
现代智能手机芯片中的晶体管数量已经达到数百亿级别
硬件组成三要素
| 组件 | 作用 | 实例 |
|---|---|---|
| 传感器 | 感知物理量(温度/光照/位置)并转为电信号 | 光电传感器、编码器 |
| 控制器 | 处理传感器数据并决策(核心为MCU或SoC) | STM32、ESP32 |
| 执行器 | 执行控制指令(机械运动/开关控制) | 直流电机、气缸 |
控制器
SoC
系统级芯片
System on Chip: SoC 是将整个计算机系统的各个组件(如 CPU、GPU、内存、I/O 接口、通信模块等)集成到单个芯片上的解决方案
典型实例:
- Raspberry Pi:使用 Broadcom SoC 的单板计算机,适合各种计算和嵌入式应用。
- 手机芯片(如 Qualcomm Snapdragon、Apple A 系列):集成了 CPU、GPU、通信模块(如 4G/5G)、图像处理器等。
- NVIDIA Jetson 系列:用于人工智能和机器学习的 SoC,集成了强大的 GPU 和处理能力。
- ESP32:虽然它也可以被视为 MCU,但它集成了 Wi-Fi 和蓝牙功能,因此在某些上下文中也可以被视为 SoC。
特点:
- 功能强大,集成度高
- 支持多种处理任务和外设
- 通常具有较高的功耗和性能
- 适合需要高性能和多功能的应用
MCU
微控制器单元
定义:MCU 是一种集成电路,通常包括一个或多个处理核心、内存(RAM 和 ROM)、输入输出端口(I/O)和其他外设(如定时器、ADC 等)
用途:MCU 通常用于控制应用,如家电、汽车、工业控制等。它们适合处理简单的控制任务和实时处理
特点
- 功耗低
- 处理能力较弱,适合简单任务
- 通常具有较小的存储空间
- 设计简单,适合嵌入式系统
典型实例:
- Arduino:基于 AVR 或 ARM 微控制器的开发板,广泛用于教育和原型设计。
- STM32 系列:STMicroelectronics 提供的 32 位微控制器,广泛应用于嵌入式系统。
- PIC 微控制器:Microchip Technology 提供的系列微控制器,常用于工业控制和消费电子产品。
- 8051 微控制器:一种经典的 8 位微控制器架构,应用于许多嵌入式系统。
软件核心层级
- 裸机开发:直接操作寄存器控制硬件(C语言点灯实验)
- RTOS(实时操作系统)
- 必要性: 多任务调度(如同时处理Wi-Fi通信 + 电机控制)
- 常用系统: FreeRTOS(ESP8266兼容),Zephyr
- 通信协议
- 设备间: I²C、SPI、UART(传感器与控制器通信)
- 网络层: MQTT(物联网云通信)、Modbus(工业控制)
FreeRTOS架构解析
FreeRTOS 通过精妙的分层架构、确定性调度策略和硬件级优化实现实时低延迟与优先级抢占,其框架设计可通过以下结构图及核心机制解析
FreeRTOS通过 分层微内核+硬件加速调度+中断优先级隔离 实现硬实时
抢占式调度确保高优先级任务即刻响应
优先级继承消除资源竞争延迟
FromISR API 缩短中断到任务唤醒路径
Tickless模式兼顾低功耗与定时精度
开发者需合理配置
FreeRTOSConfig.h(如调度策略、堆大小)并优化任务优先级分配,以发挥其极致实时性能
设计哲学:微内核架构仅包含调度、任务同步等必要功能,内核代码精简至几KB,通过
FreeRTOSConfig.h动态裁剪模块(如关闭定时器以节省资源)
1 | graph TD |
任务调度机制(状态转换图)
1 | stateDiagram-v2 |
核心调度策略
优先级抢占
- 高优先级任务就绪时立即抢占低优先级任务
- 优先级位图算法(uxTopReadyPriority)在常数时间内定位最高优先级任务
时间片轮转
同优先级任务共享CPU时间(通过SysTick中断触发切换)
调度确定性保障
任务切换时间<=1μs(Cortex-M),依赖硬件加速的上下文保存(PendSV异常自动压栈R0-R3等)
低延迟实现机制
中断管理框架
1 | flowchart TB |
中断优先级分层:
- 不可屏蔽中断(优先级≤
configMAX_SYSCALL_INTERRUPT_PRIORITY):禁止调用API,响应延迟极低(如电机控制) - 可屏蔽中断(优先级>阈值):可安全调用API,但可能被内核短暂阻塞
优先级继承解决资源竞争
1 | sequenceDiagram |
互斥锁(Mutex)动态提升低优先级任务的优先级,避免优先级反转导致高优先级任务阻塞
低功耗与实时性平衡(Tickless模式)
空闲时关闭SysTick中断,由高精度RTC唤醒并补偿节拍计数,减少功耗且不牺牲调度精度
开发工具链
| 工具类型 | 代表软件 | 用途 |
|---|---|---|
| IDE | PlatformIO(vscode插件)、Keil | 代码编辑/编译/调试 |
| 调试器 | J-Link、ST-Link | 硬件程序烧录与实时调试 |
| 仿真器 | Proteus、QEMU | 无硬件时的逻辑验证 |
自动化设备开发路径
阶段1:基础实践(2-4周)
硬件入门:
- 购买ESP32开发板(兼容Wi-Fi/蓝牙)。
- 完成实验:GPIO控制LED、ADC读取温湿度传感器。
软件框架:
- 用PlatformIO搭建开发环境(支持VS Code)。
- 编写C程序驱动外设(如按键触发电机)。
阶段2:系统进阶(4-8周)
RTOS实战:
- 在ESP32上移植FreeRTOS,创建多任务(任务1:传感器采集;任务2:网络上报)。
通信协议:
- 通过ESP32的Wi-Fi发送传感器数据至云平台(MQTT协议)。
阶段3:自动化设备集成(8+周)
- 项目案例:智能浇水系统
伪代码示例:土壤湿度控制
1 | void task_sensor(void *pvParams) { |
硬件配置:
- 传感器:土壤湿度传感器 → ESP32 → 继电器控制水泵。
资源推荐
- 书籍:《嵌入式系统设计与实践》(O’Reilly)
- 课程:Coursera《Embedded Hardware and Operating Systems》
- 社区:GitHub(开源硬件项目)、EEVblog(硬件调试技巧)
关键提示:
从软件工程师到硬件融合的转型核心在于:
- 动手为先:拆解旧家电(如遥控车)理解电路设计。
- 渐进式学习:从Arduino快速原型到专业RTOS开发。
- 利用现有优势:C/C++能力可快速切入驱动开发,网络知识助力物联网应用。
通过此路径,您可在3-6个月内构建完整的自动化设备开发能力,将软件经验转化为硬件创新力!
ESP开发板对比
| 特性 | ESP8266 | ESP32(基础款) | ESP32-C3(推荐替代) |
|---|---|---|---|
| 处理器 | 单核 80MHz | 双核 240MHz | 单核 RISC-V 160MHz |
| 内存 | 80KB RAM | 520KB RAM | 400KB RAM |
| 无线功能 | Wi-Fi 4 (802.11b/g/n) | Wi-Fi 4 + 蓝牙4.2/BLE | Wi-Fi 4 + 蓝牙5.0 |
| ADC精度/数量 | 1路(10位,0-1V量程) | 18路(12位,0-3.3V) | 6路(12位) |
| PWM可用通道 | 8路 | 16路 | 4路 |
| 典型成本 | 10-15元 | 20-30元 | 15-20元 |
- ESP8266:仅用于“触发型”场景(如收数据→发指令),复杂逻辑(如电机PID控制)必选ESP32。
- ESP32全系:新项目无脑入!ESP32-C3是ESP8266的理想升级,ESP32-S3则满足AI等前沿需求
- 避坑点:ESP8266的ADC仅1路且量程窄(0-1V),直接接传感器易烧毁,务必分压电路
GPIO
GPIO(General-Purpose Input/Output,通用输入输出接口)是嵌入式系统中最基础的硬件交互通道,可直接连接传感器、执行器或其他电子模块,实现芯片与物理世界的双向通信
硬件结构上看: 每个 GPIO 对应芯片的物理引脚(如 ESP8266 的 D0~D8),内部通过寄存器控制状态(无需外接复杂电路即可驱动 LED 或读取按钮)
GPIO 是嵌入式开发的“手脚” —— 通过它:
感知世界(输入)→ 接收按钮、传感器信号。
操控设备(输出)→ 驱动电机、点亮灯光。
行动建议:
入门套件推荐:购买 ESP32 开发板 + GPIO 实验套件(含按钮/LED/光敏电阻),花费约¥50。
快速验证思路:用 Tinkercad 等电路仿真工具设计逻辑,再动手焊接。
掌握 GPIO 后,您即可将 C++ 算法能力转化为实体自动化设备的控制力!
仿真软件
| 层级 | 语言/工具 | 作用 | 用户操作示例 |
|---|---|---|---|
| 用户编写代码 | Arduino C/C++ | 控制虚拟硬件(如点亮LED、读取传感器) | void loop() { digitalWrite(13, HIGH); delay(1000); } |
| 仿真环境内核 | C++/Java/Python | 模拟硬件行为(如生成虚拟电压信号) | 后台运行,用户不可见(如Tinkercad用WebAssembly编译运行) |
| 电路可视化 | 图形化拖拽界面 | 构建虚拟电路(连线、元件选择) | 拖入Arduino UNO板 → 连接LED到D13引脚 → 开始仿真 |
主流仿真工具
Tinkercad Circuits(最适合初学者)
操作流程:
① 浏览器打开 Tinkercad→ 创建电路设计
② 拖入虚拟Arduino(如UNO/ESP32)和元件(如LED、传感器)
③ 用C++写控制代码(基于Arduino库) → 点击仿真按钮
1 | // 示例:虚拟按钮控制LED |
优势:无需安装,支持ESP32/ESP8266仿真(含Wi-Fi模拟)
Proteus(专业级仿真)
代码流程:
① 在Arduino IDE用C++编译出HEX文件
② 在Proteus中导入HEX到虚拟MCU → 模拟传感器信号
适用场景:电机控制时序验证、ADC精度测试等精密仿真
fritzing
Virtual Breadboard
VBB 电路仿真,仅支持win10系统
WOKWi
可以模拟包括ESP32在内的微控制器仿真
基本电子元器件
灯泡
LED
与灯泡的不同在于有单向导通性,建议最大值为20mA,不烧坏LED也不影响寿命
三色RGB发光二极管
可以理解为三个LED合成一体,有共阳极和共阴极两种,发光是RGB三色不同比例的叠加
滑动开关/多位开关/轻触按键
多位开关: 多个滑动开关合并到一块
轻触按键: 按下后状态改变,松开后恢复
1a和1b 与 2a和2b是默认连通的,按下按钮就会全连通
常见的电池
- 纽扣电池 常见CR系列为锂锰扣式电池 提供一个3V的电压,常见的cr2032系列
- 干电池 一种以糊状电解液来产生直流电的化学电池,标称电压均为1.5V,日常使用型号有1号、5号、7号
- 9V电池 积层电池 主要用于仪表测学仪器供电 电池容量小,电压高,内部化学反应迟經的电电流小能使用很长时间
- 水果电池 利用水果中的化学物质和金属片发生反应产生电能的一种电池,电压很低
其他可以提供电压的设备
- Arduino Uno R3 根据接线可以提供3.3V或5V的电压
- 电源 能提供0~30V的电压,注意电流波轮是做了一个限流的作用而不是让你设置电流
测量仪器
- 万用表 用于测量电压,电流与电阻
- 示波器
- 函数生成器
电位器 - 可调电阻
改变接入电路的电阻阻值
人体红外检测模块PIR
在安防领域中,PIR探测器的全称就是Passive Infrared Deteetor,即被动式红外探测器或身体感应器
三个引脚: 电源,接地,信号 如果探测到移动,信号会输出电流
继电器
一种电控制器件,可以理解成是用小电流控制大电流的一种自动开关
示波器 可以将数据收集起来,以曲线形式显示
蜂鸣器
可以发出声音
可以分有源和无源(是否有震荡源)蜂鸣器
有源蜂鸣器给高电平会自动响,无源蜂鸣器需要给震荡的信号才能响
Tinkercad仿真中提供的是有源蜂鸣器
输入的pmw电压频率直接决定音调。频率越高,音调越高(声音越尖细);频率越低,音调越低(声音越低沉)
红外接收器
最简单的自保持电路
舵机
舵机是一种位置(角度)伺服驱动器。它的核心功能是:接收一个控制信号,并精确地转动到并保持在那个指定的角度位置
当说“舵机”时,通常就是指那种三线、用PWM控制、内部集成了控制电路和电位器的位置伺服电机。它是伺服电机大家族中最常见、最易于使用的一员
伺服电机: 拥有闭环控制的电机
与普通电机的区别:普通直流电机通常只能控制“正转/反转/速度”,而舵机可以控制“转到特定角度”(比如精确转到30度、90度、180度)
与步进电机的区别:舵机内部集成了电机、减速齿轮、控制电路和反馈系统(如电位器),形成了一个闭环控制,所以它通常比开环控制的步进电机精度更高、扭矩更大、更易用。
红色是正极(通常4.8-6V),棕色是地线,橙色是PWM信号线(用于接收控制信号)
- 切勿将舵机的VCC直接接到Arduino的5V引脚上,尤其是扭矩较大的舵机!Arduino板载的5V稳压器无法提供足够电流,可能导致板子重启或烧毁。
- 务必使用外部电源(如电池组、独立的5V电源模块)为舵机供电,并将此外部电源的GND与Arduino的GND共地(连接在一起)
使用场景
舵机的“角度保持”特性使其非常适合需要精确定位的场景:
- 机器人关节:机器人的手臂、腿、头部、手指的转动。
- 航模/车模:控制飞机舵面(故名“舵机”)、汽车转向。
- 摄像头云台:控制摄像头上下左右转动,进行追踪。
- 智能门锁:控制锁舌的弹出和收回。
- 喂食器/开关:作为机械臂,远程控制一个物理开关的按下或松开
控制原理
舵机的控制需要一种特殊的PWM信号,其特点是:
- 频率:通常为 50Hz(即周期为20ms)。
- 脉冲宽度(高电平持续时间):决定了舵机的角度。
- 0.5ms 的脉冲宽度 -> 0° 位置
- 1.5ms 的脉冲宽度 -> 90° 位置
- 2.5ms 的脉冲宽度 -> 180° 位置
1 | xychart-beta |
脉冲宽度在0.5ms至2.5ms之间线性变化,舵机的角度就会在0°至180°之间相应变化
像Arduino这样的平台提供了现成的 Servo库,它可以自动帮我们处理这些底层细节
注意事项
- 不要手动扭动:在断电时,不要用力强行手动旋转舵机轴,极易损坏内部的齿轮和电位器。
- 注意扭矩:选择舵机时要注意其扭矩(kg·cm) 参数,扭矩太小可能带不动你的机械结构。
- 供电要充足:务必使用外部电源供电,确保电源能提供足够的电流(单个舵机工作电流可达几百mA,动作时更大)。
- 机械限位:很多舵机(如180°舵机)有物理限位,编程时不要试图让它转到超出范围的角度(如
myServo.write(200)),否则会听到齿轮“打齿”的噪音,长期会损坏舵机。
电子电路
上拉和下拉电阻
用于确定引脚状态
上拉和下拉电阻的核心目的,是为按键(一个机械开关)在未被按下时,提供一个确定的、稳定的电平状态(高电平或低电平),防止其引脚处于不确定的“浮空”状态,从而避免误触发和电路不稳定
为什么需要他们
原因是浮空状态的危害
当我们把按键连接到微控制器(如 Arduino, STM32, 51单片机)的 GPIO 引脚时,理想情况是:
- 按键断开 -> 引脚读到一种明确电平(比如 3.3V)
- 按键闭合 -> 引脚读到另一种明确电平(比如 0V)
但问题在于,当一个引脚什么都不连接(即按键断开时)时,它处于高阻抗状态,也叫“浮空”状态。这个引脚的电平是不确定的,极易受到周围环境(如静电、电磁干扰)的影响,会在高电平和低电平之间随机波动。微控制器会因此读到混乱的信号,导致程序误以为按键被频繁地按下和释放
上拉/下拉电阻就是为了解决这个“浮空”问题而存在的
| 特性 | 上拉电阻 (Pull-up) | 下拉电阻 (Pull-down) |
|---|---|---|
| 默认电平 | 高电平 (1) | 低电平 (0) |
| 有效电平 | 低电平有效 (按下为0) | 高电平有效 (按下为1) |
| 常见应用 | 更常见,因为很多 MCU 内部集成上拉电阻 | 同样可用,但内部集成下拉的情况较少 |
| 电路影响 | 在按键按下时,Vcc 通过电阻到 GND 形成回路,有微小电流消耗 | 同样有微小电流消耗 |
| 选择依据 | 取决于你的程序逻辑和 MCU 特性 | 取决于你的程序逻辑和 MCU 特性 |
👆🏻上图中偏左的为上拉电阻,偏右的为下拉电阻
- 优先使用上拉电阻:因为绝大多数微控制器(如Arduino, STM32, ESP32)的GPIO引脚都内部集成了可软件开启的上拉电阻。你只需要在代码中配置一下,无需外接物理电阻,非常方便。
- Arduino示例:
pinMode(pin, INPUT_PULLUP); - STM32 HAL示例:
GPIO_InitStruct.Pull = GPIO_PULLUP;
- Arduino示例:
- 如果你的MCU没有内部下拉电阻,而你需要的逻辑又是“高电平有效”,那么就需要外接一个下拉电阻。
电阻值选择:通常选择 4.7kΩ 到 10kΩ 的电阻。阻值太大会无法有效拉平干扰;阻值太小则在按键按下时会产生过大的不必要的电流消耗(费电),并且可能烧坏端口
PWM引脚
Arduino板上端口数字前带~表示他可以输出pwm信号
PWM是如何产生的?
微控制器(MCU)内部有多个专门的硬件模块,叫做定时器/计数器。它们就像一个个独立的小时钟,可以精确地计数和计时。
- 频率(Frequency):由定时器的计数速度决定。定时器计数得多快,一个PWM周期的完成就有多快,从而决定了PWM的频率。
- 占空比(Duty Cycle):在同一个定时器内,有专门的比较寄存器。当定时器的计数值小于比较寄存器的值时,输出高电平;大于时,输出低电平。改变比较寄存器的值,就能改变高电平的时间比例,即占空比。
关键点:一个定时器可以同时控制多个输出通道(通常2到4个),为这些通道提供相同的计数基准(即相同的频率),但每个通道都有自己独立的比较寄存器(即可以有不同的占空比)。
为什么同一块板子上的PWM引脚频率会不同?
因为你的开发板(比如常见的Arduino Uno、STM32等)的MCU芯片内部有多个定时器,而不是只有一个。
引脚分组:板子上的PWM引脚并不是平等的,它们被分组连接到不同的定时器上。
例如,在Arduino Uno(基于ATmega328P)上:
- 引脚 5 和 6 由Timer0控制,默认频率约 976 Hz。
- 引脚 9 和 10 由Timer1控制,默认频率约 488 Hz。
- 引脚 3 和 11 由Timer2控制,默认频率约 488 Hz。
这些定时器在出厂时或被Arduino库初始化为不同的默认值,所以它们产生的默认PWM频率就不同。
你也可以自己改变频率:通过编程,你可以重新配置这些定时器(比如改变它的预分频器设置),从而改变其频率。但请注意:
- 如果你改变了Timer1的频率,那么所有由Timer1控制的引脚(如Arduino的9和10脚)的频率都会一起改变。
- 由一个定时器控制的多个引脚,频率必然相同,但占空比可以各自独立设置。
为什么要这样设计?(这样设计的好处)
这种设计主要是为了灵活性和功能分工,主要原因有以下几点:
兼顾精度与性能:
- 高频PWM:适合控制开关电源、LED调光(消除人眼可见的闪烁)等需要快速响应的场合。
- 低频PWM:适合控制舵机(Servo)、生成音频信号等需要精确脉冲宽度的场合。Arduino默认用Timer1(16位定时器)来产生更稳定的低频PWM,就是因为它的精度更高,更适合驱动舵机。
独立控制,互不干扰:
- 假设你有一个项目,需要同时:
- 用高频PWM控制一个电机的转速(比如25kHz)。
- 用50Hz的PWM信号控制一个舵机。
- 如果所有PWM引脚都必须是同一个频率,你就无法同时完成这两个任务。
- 有了多个独立定时器,你就可以将Timer0设置为高频率控制电机,同时将Timer1设置为50Hz来控制舵机,两者互不干扰。
资源分配与优化:
- 芯片设计者提供了多个不同位宽(8位、16位)和功能的定时器,以满足各种不同的应用需求。让用户可以根据项目需要,灵活分配这些硬件资源。
总结
- 不同频率:是因为它们背后连接的是不同的定时器硬件。
- 同一频率:连接在同一个定时器上的多个PWM引脚,必须有相同的频率。
- 为什么要这样:为了提供灵活性,允许用户为不同的任务(电机控制、舵机控制、LED调光等)同时设置不同的最佳频率,让各个功能模块互不干扰地工作。
设置的引脚10写入“100”是什么意思?为什么是方波?
在Arduino编程中,使用 analogWrite(pin, value)函数来向标有~的PWM引脚写入值。这个 value的取值范围是 0 到 255。
analogWrite(10, 0):表示占空比为 **0%**,引脚持续输出低电平(0V)。analogWrite(10, 255):表示占空比为 **100%**,引脚持续输出高电平(5V)。analogWrite(10, 100):表示占空比约为 **100/255 ≈ 39.2%。这意味着在一个PWM周期内,引脚只在39.2%的时间输出高电平(5V),其余60.8%**的时间输出低电平(0V)。
所以,您并没有命令引脚“一直输出高电平”,而是命令它“以大约39%的占空比快速开关”
输出电压 ≈ (100 / 255) × 5V ≈ 0.392 × 5V ≈ 1.96V
这正好完美匹配了您电压表上1.96V的读数! 电压表告诉你的是:“这个引脚输出的平均电压是1.96V”,而示波器告诉你的是:“这个引脚是通过快速在5V和0V之间切换来产生这个1.96V平均电压的”
1 | //如果期望的是一条稳定的5V直线,不应该使用 `analogWrite`,而应该使用数字写入函数: |
| 函数 | digitalWrite() | analogWrite() |
|---|---|---|
| 输出信号类型 | 纯数字信号 (Digital) | 脉冲宽度调制信号 (PWM - 并非真实模拟) |
| 物理电平 | HIGH= Vcc (如5V/3.3V) | LOW= 0V (GND) |
| 快速切换于 HIGH 和 LOW 之间 | ||
| 电压可变性 | ❌ 只有2种固定电平 | ✅ 平均电压可变 (0V ~ Vcc) |
| 硬件依赖 | 任何通用I/O引脚 (GPIO) | 仅限带 ~ 标记的PWM引脚 (需硬件定时器支持) |
二者的应用场景对比
digitalWrite():纯二进制控制
场景 典型应用 为什么用? 开关类器件 - 继电器通断 只需“开/关”状态,无需中间值 - 晶体管/MOSFET开关 状态指示 - LED电源灯亮灭 视觉/听觉只需二元状态 - 蜂鸣器报警鸣叫 数字通信 - SPI时钟线(SCK) 协议要求精确的0/1跳变 - I²C起始信号 按钮读取 检测按键是否按下(HIGH/LOW) 输入本身就是数字信号
analogWrite():模拟效果仿真
场景 典型应用 PWM的优势 调光/调色 - LED呼吸灯 ✅ 无极调节亮度/颜色,无级变速平滑 - RGB灯颜色渐变 电机调速 - 直流电机转速控制 ✅ 精确控制功率输出,替代笨重的模拟电路 - 风扇风力调节 舵机角度控制 机器人关节舵机(50Hz PWM信号) ✅ 用脉冲宽度精确映射角度 (0°~180°) 简易音频合成 生成方波音乐(如8-bit游戏音效) ✅ 频率可调,占空比改变音色 加热功率控制 电热丝/加热片温控 ✅ 高效调节发热功率
digitalWrite是控制世界的开关analogWrite是调节万物的旋钮
PWM是“假”模拟:
Arduino Uno/Nano 没有真正的DAC(数模转换器)。analogWrite()只是通过PWM高速开关模拟出中间电压效果。需外接DAC芯片才能输出真实连续电压(如音频输出)。分辨率限制:
标准Arduino PWM为8位 (0-255共256级),精细度有限。STM32等高端MCU可达16位(0-65535),精度提升256倍!引脚冲突风险:
修改PWM频率(如 TCCR1B寄存器)会影响同一定时器的所有引脚!例如改Timer1频率后,Pin9和Pin10行为会同时变化。驱动能力≠输出类型:
无论digitalWrite还是analogWrite,最大输出电流仅约40mA。驱动大功率设备(电机/灯带)必须加晶体管/MOSFET驱动电路!
PWM引脚的”bug”
以ARDUINO UNO板子为例,会有下面的情况:
- 舵机影响9和10引脚pwm
- 蜂鸣器和红外接收影响3和11引脚pwm
原因是: Arduino UNO (基于ATmega328P芯片) 只有3个独立的硬件定时器
Arduino UNO的PWM引脚是这样分配给各个定时器的:
- Timer0 -> 控制引脚 5 和 6
- Timer1 -> 控制引脚 9 和 10
- Timer2 -> 控制引脚 3 和 11
一些复杂的库(如舵机库Servo.h、红外接收库IRremote.h)为了实现其功能,需要接管(Reconfigure) 整个定时器。
- **舵机库 (
Servo.h) 与 引脚9和10 (Timer1)**:- 舵机需要非常精确的50Hz PWM信号。
- 为了方便和保证精度,
Servo.h库默认会接管16位的Timer1,将其强制设置为产生50Hz的PWM模式。
更强大的板子(如ESP32, STM32, Arduino Mega)情况好得多
例如,ESP32有高达16个定时器,STM32F4系列有14个以上,Arduino Mega有6个
更简单的板子(如ATTiny85)情况更糟
如ATTiny85,只有1~2个定时器
模拟引脚
在Arduino等开发板上,通常标有 **”A0”, “A1”, “A2”…**。
其背后连接的是一个叫做 ADC (Analog-to-Digital Converter,模数转换器) 的硬件模块
模拟引脚 (Analog Input Pins) -> 负责读取外部的模拟世界(如温度、光线强度)。
PWM引脚 (Digital PWM Pins) -> 负责模拟输出一个中间值,去控制外部设备。
| 特性 | 模拟输入引脚 (A0, A1, A2…) | PWM引脚 (D3, D5, D6, D9, D10, D11 ~) |
|---|---|---|
| 核心功能 | 读取外部模拟电压值 | 输出脉冲宽度调制信号 |
| 信号类型 | 输入 | 输出 |
| 工作方式 | ADC (模数转换器) | 定时器产生特殊方波 |
| 测量/控制对象 | 物理世界的连续变化量 | 输出效果的“平均强度” |
| 分辨率 | 10位 (0-1023) | 8位 (0-255) |
| 返回值/参数 | int value = analogRead(A0); |
analogWrite(9, value); |
| 典型应用 | 读取电位器、光敏电阻、温度传感器 | 控制LED亮度、电机转速、舵机角度 |
原理
模拟引脚的实现依赖于一个叫做 ADC (Analog-to-Digital Converter,模数转换器) 的硬件模块。它的任务是将一个连续的模拟电压信号,转换成一个离散的数字数值,以便MCU的数字核心能够读取和处理
当你执行 analogRead(A0)时,底层硬件悄然完成以下操作:
- MCU内部的多路选择器将A0引脚连接到ADC模块。
- ADC启动一次转换。
- 采样保持电路捕获A0引脚上的瞬时电压。
- ADC进行量化,将电压值转换为0-1023之间的数字。
- 转换完成,结果被存入数据寄存器。
analogRead()函数从这个寄存器中取出这个值并返回给你的程序。
详细如下:
采样 (Sampling)
ADC并不是持续不断地测量电压,而是以一个固定的时间间隔(由采样频率决定),快速地“瞥一眼”输入引脚上的电压值,并把这个瞬间的电压值捕获下来
采样率 (Sample Rate)。根据奈奎斯特采样定理,采样频率必须至少是输入信号最高频率的2倍,才能无失真地还原原始信号。但对于大多数Arduino测量的传感器(如缓慢变化的温度、光线),默认的采样率已经足够
量化 (Quantization)
捕获到的模拟电压值可以是某个范围内的任意值(如3.1415926… V)。但ADC需要把它“归类”到一个有限的、离散的等级中去。这就是分辨率的概念
分辨率 (Resolution),通常用位数 (bits) 表示。
- Arduino UNO的ADC是 10位 的。这意味着它可以将 0V至5V 的参考电压(Vref)划分为 2¹⁰ = 1024 个离散的等级(从0到1023)。
- 每个等级代表的电压差是: 5V / 1024 ≈ 0.00488V (4.88mV)
编码 (Encoding)
将量化后得到的等级数值,转换成一个二进制数字,并存储到MCU的寄存器中,等待程序通过
analogRead()函数来读取最终在代码中得到的
int sensorValue = analogRead(A0);里的sensorValue,就是这个编码后的数字值(0-1023)
相关参数
分辨率 (Resolution)
- 定义:ADC能区分的最小电压变化能力,位数越高,分辨率越高。
- 举例:
- 10位 (Arduino UNO):范围 0-1023,最小电压步进 ~4.88mV。
- 12位 (STM32, ESP32):范围 0-4095,最小电压步进 ~1.22mV(假设5V参考电压)。精度是10位的4倍!
- 影响:分辨率决定测量的精细度,高分辨率ADC能探测到更微弱的电压变化。
参考电压 (Reference Voltage, Vref)
- 定义:ADC用于比较和量化的基准电压,通常是MCU的工作电压(如Arduino UNO为5V)。
- 重要性:测量小电压范围(如0-1V)时,若使用5V参考电压,精度会大幅浪费。
- 解决方案:许多MCU允许改变参考电压,使用
analogReference()函数可改变Arduino的参考电压,提高精度。
采样速度 (Conversion Speed)
- 定义:ADC完成一次采样-量化-编码过程所需时间。
- 影响:决定能测量多快变化的信号,捕获音频信号(20kHz)需高采样速度(>40kHz),而测量温度等缓慢变化的数据,低速ADC也足够。
$$
i_1+i_2+i_3=i_4+i_5
$$
$$
U_{S1}-U_{S2}-U_2-U_1=0
$$





