☰
《ESP32+直流电机》第 0 章 底层概念地基:电、数字、芯片、程序
2026/10/18 15:28:17 网站建设 项目流程

本章不写代码。它回答四个"为什么":
①芯片为什么会听 0 和 1 的话?②0 和 1 在电路里长什么样?
③程序是怎么被芯片"记住"并"执行"的?④烧录/编译/运行到底分别做了什么?
每一节都从最底层讲起,把概念彻底拆开。看懂这一章,
后面所有章节里的名词都不会再"悬空"。
本章只是打地基、不动手;等到第 2 章真正接线动手前,请先翻一遍 0.10 的安全红线。


0.1 电的基础:电压、电流、GND

电压 = 水位差

把电想象成水管里的水。要让水流动,两个位置要有"高度差"——这就像电压。
电压的单位是伏特(V)。它衡量的是"两个点之间的电势差"。

  • 3.3V:A 点比 B 点高 3.3 伏。
  • 0V:两个点一样高,没有"推水"的力。

电流 = 水流量

水从高处流向低处,就是电流(单位:安培 A)。电流是"电子的流动"。
电流永远从高电压流向低电压(就像水往低处流)。

GND = 共同的"0 米海平面"

电路需要一个统一的"0 米基准",叫GND(Ground,地)。所有电压都是
“相对于 GND 说的”。一块板子只有一个 GND。

共地= 两块板子的 GND 用一根线连在一起,让它们有同一个海平面。

  • 共地了:A 板的 3.3V 和 B 板的 GND,才有确定的 3.3V 差,信号才准确。
  • 不共地:A 板说"高",B 板不知道"相对谁高"——信号就乱了。
    这是新手第一大坑,第 8 章会再次出现。

0.2 为什么芯片只懂 0 和 1?(从晶体管讲起)

晶体管:一个"电子开关"

芯片里最基本的零件叫晶体管(transistor)。你可以把它理解成
一个用小电信号控制通断的开关:给控制脚一个小信号,它就决定另一条路上
的大电流是通还是断(L298N 内部就是这种东西,第 8 章细讲):

控制脚 ──► ══开关══
  • 控制脚电压高 → 开关闭合(导通);
  • 控制脚电压低 → 开关断开(截止)。

一个开关只有两种状态:通/断。用1 表示通、0 表示断。
这就是"0 和 1"的物理来源——它不是一个抽象概念,而是电路里真实存在的
“通"和"断”。

逻辑门:把开关组合成"会算数的电路"

几个晶体管组合起来,就能实现"如果 A 和 B 都通,输出才通"这类规则,
这叫逻辑门(logic gate):

  • 与门 AND:A 和 B 都是 1,输出才 1;
  • 或门 OR:A 或 B 有一个是 1,输出就是 1;
  • 非门 NOT:输入 1,输出 0(反过来)。

亿万个逻辑门组合在一起,就成了 CPU(能算数)、内存(能记住)、
和各种外设(能控制引脚)。

结论:芯片听不懂中文,但它内部全是由"通/断"开关组成的电路。
所以我们用 0 和 1 跟它说话,它就能直接"听懂"。

0.3 数字信号在电路里长什么样?——电平

芯片的引脚输出"1"和"0",实际是输出电压:

数字电压名字
1约 3.3V高电平 HIGH
0约 0V低电平 LOW

电平不是"绝对电压",是"范围"

接收方判断高低,不是量到正好 3.3V,而是看电压落在哪个范围:

3.3V ┤■■■■■■■■ 高电平区(认为读到 1) │ ↑ 阈值(约 1.5~2V 左右;这是简化模型,精确的双阈值模型见 4.1) 0V ─┴■■■■■■■■ 低电平区(认为读到 0)
  • 高于阈值 → 读到 1;
  • 低于阈值 → 读到 0。
  • 这里的阈值就像考试的及格线:分数(电压)到了线就算"过"(读成 1),
    没到就算"不过"(读成 0),接收方只认这条线,不去量精确电压。而电压
    越过这条线的瞬间,就是下一节要讲的"沿"。
  • 只要不落在中间"模糊区",信号就是可靠的。这就是数字信号抗干扰的原因:
    一点点噪声不会让它从"高"变成"低"。

ESP32 引脚电平

这里说的"引脚",全书也常写成GPIO(General Purpose Input/Output,
通用输入输出脚)——就是芯片外壳上那一根根你能自由读写的金属脚,
编号 GPIO0、GPIO1……本书很多实验就是在这些脚上收发电平。

  • 输出:高电平 3.3V,低电平 0V(不能输出 5V);
  • 输入:能承受的电压上限约 3.3V(部分引脚 5V 容忍)。5V 直接接普通 GPIO
    会烧芯片
    ——因为输入电路是按 3.3V 设计的。
    所谓"5V 容忍(5V-tolerant)",是指少数加固过的引脚即使输入 5V 也不会烧,
    但普通引脚不在此列——拿不准就一律当成"不能碰 5V"最安全。

0.4 电平的"变化":上升沿与下降沿(重要概念!)

信号不只是"停在高/低",还会变化。这个变化过程有两个关键"时刻":

电压 3.3V ┤ ┌────────┐ │ │ │ ← 这段平台期间电平一直为高,叫"高电平期间" │ │ │ 0V ─┴────────┘ └──────── ↑ ↑ 上升沿 下降沿 (低→高的瞬间)(高→低的瞬间)

上升沿(Rising Edge)

信号从低变高的那个瞬间——电压从低电平区跨越阈值、进入高电平区。

下降沿(Falling Edge)

信号从高变低的那个瞬间——电压从高电平区跌回低电平区。

为什么"沿"这么重要?(关键逻辑)

"沿"是一个瞬间事件,电平是一个持续状态。区别在于:

  • 电平触发:只要电压是"高",就一直在"触发"(像一直按着门铃);
  • 边沿触发:只在"变高的那一瞬间"触发一次(像按一下门铃响一声)。

硬件里有很多"只认沿"的机制,例如:

  • 中断:按钮按下产生下降沿,芯片只在沿上进入中断一次
    (中断的注册、ISR 规则、与任务通信的完整讲解,见第 15 章)。
    打个比方:中断就像你正在写作业时突然来电铃响——你会放下笔先去接电话,
    接完再回来继续写。芯片也一样,手头活先暂停,跳去处理这件"急事"(这段
    临时去干的代码就叫 ISR,Interrupt Service Routine,中断服务程序)。
  • 计数器:每次上升沿数一次数(数脉冲);
  • 同步电路:芯片内部所有逻辑都在时钟的上升沿统一"拍板"
    (见 0.5)。

记法:电平 = 状态,沿 = 变化的一瞬间。看时序图时,
“上跳沿”=上升沿,“下跳沿”=下降沿。
第 4 章我们会实际遇到"抖动"——那是沿在短时间内反复出现多次。

0.5 时钟:芯片内部的"节拍器"

芯片里的动作不是随便乱来的,而是按照一个时钟(clock)信号,
一拍拍地做。时钟就是一个非常稳定的
方波
(一种在高、低两级之间
规则切换、画出来像一排正方形的波形,见下),每秒跳很多次:

高 ┤┌┐ ┌┐ ┌┐ ┌┐ │││ ││ ││ ││ 低 ─┘└─┘└─┘└─┘└─► 时间 ↑↑ ↑↑ 每两次上升沿之间 = 1 个时钟周期
  • 时钟每秒跳的次数叫频率,单位Hz(赫兹)。1Hz = 每秒 1 拍。
  • 我们芯片的 CPU 时钟是160MHz= 每秒跳 1.6 亿拍。反过来算,一拍只占
    1 ÷ 160,000,000 秒 = 6.25 纳秒(ns)——1 纳秒是十亿分之一秒。
  • 芯片内部逻辑(加法、比较、搬运)在每个时钟上升沿统一执行一步。

理解时钟的意义:它让"亿万个开关"步调一致,就像一支乐队跟着
指挥的拍子演奏。没有时钟,信号会乱。

操作系统也靠时钟:FreeRTOS(跑在芯片上的一个小型实时操作系统,
负责把"多件同时要做的事"排好队、分时间片轮着干)用时钟节拍(tick)
决定"下一个该谁跑",让多个程序轮流使用同一个 CPU——第 13 章展开。

0.6 寄存器:芯片里"能记住数"的小格子

【十六进制小前置】本节开始要频繁看到0x开头的地址(如0x60004004)。
0x是"十六进制"的记号:逢 16 进一,用 0~9 和 A~F 共 16 个符号写一个数位,
每 1 个十六进制位刚好对应 4 个二进制位。它只是把一长串 0/1 缩写得更短,
和十进制是同一个数的不同写法(就像0x41= 65 = 二进0100 0001)。
现在只要认出"0x开头=十六进制、代表一个数或一个地址"就够用,
详细换算第 1 章 1.1 手把手教。

第 1 章会反复用到一个词:寄存器(register)。现在讲清它的本质。

每个寄存器 = 一组能记住 0/1 的电路单元(叫触发器,flip-flop)。

  • 8 个触发器排在一起,就能记住一个 8 位数字(0~255);这里的一个"位"就是
    bit(binary digit 的缩写,一个 0 或一个 1,是最小的一格信息)。
  • 每个寄存器在芯片内部有一个地址,CPU 可以往它"写"或"读"。

外设寄存器:GPIO、PWM(Pulse Width Modulation,脉冲宽度调制——
用"高电平占多少时间"的比例来表示强弱,比如让灯半亮、让电机半速,
第 7 章细讲)这些硬件模块,它们的"控制旋钮"就是寄存器。
比如"让 GPIO2 输出高",本质是"往地址 0x60004004 的某一位写 1"。
(不同芯片的外设基址不同:0x3F40… 是初代 ESP32的地址段,
本书用的 ESP32-S3 外设寄存器在0x6000…段——别拿旧芯片的地址对号入座。)

ESP-IDF 的驱动函数(如gpio_set_level)就是封装好的"往寄存器写位"的动作。
你可以不用手写寄存器,但要理解:函数背后就是寄存器读写。

我们写的代码 │ gpio_set_level(GPIO_NUM_2, 1) ▼ 驱动函数:算出引脚对应的寄存器地址和位号 ▼ 往地址 0x60004004 的 bit2 写 1 ▼ 硬件电路看到该位为 1 → 输出高电平 3.3V

0.7 程序是怎么"被记住"和"被执行"的?

存储:Flash 里躺着一串指令

我们写代码 → 编译成"芯片指令"(每条指令是一个数字,比如
0x1100 0x2F00)→ 存进Flash(掉电也不丢的存储)。
固件就是这一整串指令。

执行:CPU 的"取指-译码-执行"循环

开机后 CPU 从 Flash 一条条读指令:

① 取指(Fetch):从 Flash 取出下一条指令 ② 译码(Decode):翻译成"要做什么"(加法?搬数据?) ③ 执行(Execute):真的去做 ④ 回到 ①(PC 指针指向下一条)

CPU 里有个叫PC(程序计数器)的小寄存器,记着"下一条指令在哪"。
遇到跳转指令(if/循环/函数调用),PC 会被改到别的位置。

所以"程序在跑"= CPU 在不停地"取指-译码-执行"。
日志里的Calling app_main()意思是:CPU 已经把 PC 指到了
我们app_main函数的第一条指令。

内存映射:Flash、SRAM、外设都住在"一张地址表"里

芯片把所有东西(Flash、SRAM、寄存器)编上统一的门牌号(地址),
CPU 按地址访问。这就像小区里每栋楼都有门牌号:所谓映射,就是
“给远处仓库里的东西预先编好本地门牌号”——哪怕实物在别处,CPU 只要喊门牌号
(地址)就能找到它,不必关心它真正住在哪。

地址范围住着什么
0x3FC8…内置 SRAM(草稿纸)
0x4200…Flash(课本,代码在这里)
0x6000…外设寄存器(旋钮们)
0x3C00…外部存储映射窗口(Flash 数据 / PSRAM 都从这里映射进来,PSRAM 就是"大仓库")

0.8 编译、烧录、运行:三件事分别做了什么

步骤在做什么(底层)产物
编辑你写人话(C++)源文件 .cpp
编译翻译官把 C++ 变成芯片指令目标文件 .o → 链接 → .elf
打包按烧录格式整理成 .bin.bin 文件
烧录通过 USB/串口把 .bin 里的字节写进 Flash 的对应地址Flash 里的数据
运行芯片复位 → 引导程序启动 → CPU 取指执行芯片在干活

烧录的底层过程(知道大概即可)

  1. 电脑用 esptool 通过串口和芯片里出厂自带的ROM 引导程序说话;
  2. 让芯片进入"下载模式"(所以有些板子要按 BOOT 键);
  3. 把 .bin 按地址一块块写入 Flash;
  4. 写完校验(Hash of data verified),复位芯片开始运行。

这是"有线"烧录。产品上线后,固件更新通常走网络下载,叫
OTA(空中升级),原理与安全要求见第 18 章。

开机启动链(日志里看到的每一步)

① ROM 引导:芯片出厂自带的程序(无法改写) ESP-ROM:esp32s3-20210327 ② 二级引导:加载真正的固件(IDF 自带) boot: ESP-IDF v6.0.1 2nd stage bootloader ③ 初始化:认出 Flash/PSRAM/CPU,分配内存 octal_psram: Found 8MB PSRAM device cpu_start: cpu freq: 160000000 Hz ④ 进入我们的程序 main_task: Calling app_main()

看到Calling app_main(),说明烧录成功、程序真的在跑了。
之后日志里再有报错,是程序逻辑问题,不是烧录问题——这两件事
必须分清(这也是本书最初闹的误会)。

0.9 串口:芯片和电脑怎么"打电话"

底层原理:一高一低,一秒传多少位

串口(UART)通信就是一根线轮流变高变低,把 0 和 1 一个一个传过去。

电脑 ──TX──► RX 芯片 (TX=发送脚,RX=接收脚) 电脑 ◄──RX── TX 芯片
  • 双方约定波特率(baud rate):每秒传多少位。我们全用115200。
    可以验算:一帧 = 1 起始位 + 8 数据位 + 1 停止位 = 10 位,
    115200 ÷ 10 = 每秒约 11520 个字符——发一行 60 字的日志只要几毫秒。
  • 传一个字符(如字母 A = 0x41 = 0100 0001)要按帧来:
    这里0x41(十六进制)与0100 0001(二进制)是同一个数的两种写法,
    只是缩写长度不同,第 1 章 1.1 会把这两种写法互相换算讲透。
空闲(高) 起位 数据位(LSB在前) 停止位 ──────┐┌──────┬──┬──┬──┬──┬──┬──┬──┬──┬──── ││ 0 │ 1 │ 0 │ 0 │ 0 │ 0 │ 1 │ 0 │ 0 │ 1 │ ← 0x41 加上起止位 └┘ ↑ 开始 ↑ 8 个数据位(bit0 先发) ↑ 停止

注意数据位的顺序:0x41 = 0b01000001,最低位(bit0 = 1)先发,
所以线上依次是1 0 0 0 0 1 0 0——你收到的第一个数据位其实是 bit0,
而不是肉眼最容易认的"最左边那位"。收发双方都默认这个顺序,才能对上。

  • 起始位:线从高变低(一个下降沿),告诉对方"要开始了";
  • 数据位:8 个 0/1;
  • 停止位:回到高,一个字符结束。

这就是"串口"名字的来历:**一个比特接一个比特(串行)**地传。
对比"并行"(8 根线同时传 8 位):串口省线但慢。

日志就是通过串口"喊"出来的

ESP_LOGI(TAG, "L298N 直流电机演示启动")把文字转成字符,再按上面的帧格式
从 USB 串口发到电脑,监视器(monitor)收到后显示在屏幕上。
看日志 = 读芯片通过串口发来的话。

日志只是串口的"单向喊话"。串口还能与模块(GPS、串口屏、AT 指令设备)
双向收发数据——编程用法见第 14 章。

0.10 安全须知(动手前的红线)

  1. 先断电再接线:改线时电路处于未知状态,带电操作可能短路烧件。
  2. 共地:开发板 GND 与驱动板 GND 必须相连(0.1 的原理)。
  3. 电源别接反:正负极接反,电流方向反了,芯片/驱动板可能烧毁。
  4. 别用 GPIO 直接带电机:GPIO 只能输出几毫安,电机要几百毫安甚至
    几安培——不是"力气小"的问题,而是电流不够会烧坏引脚输出电路。
    电机必须经过驱动板(第 8 章)。
  5. 5V 别接普通 GPIO:输入电路按 3.3V 设计(0.3)。

0.11 遇到问题怎么办(底层思路)

  1. 分层定位:问题在"电源层/接线层/烧录层/程序层"哪一层?
    • 没电、没共地 → 电源/接线层;
    • 烧不进 → 烧录层;
    • 烧进了但行为不对 → 程序层。
  2. 看日志:芯片自己会报。绝大多数问题日志直接给出线索。
  3. 看完整报错:报错文本里的函数名、地址都是线索(第 11 章教翻译)。
  4. 搜索:把报错里最独特的词 +esp32搜,全世界都有人踩过。

0.12 想一想 + 小测验

想一想:

  1. 为什么芯片只用 0 和 1?请用"晶体管只有通/断两种状态"来回答。
  2. 上升沿和"高电平"有什么区别?
  3. 看到日志Calling app_main()能说明什么?不能说明什么?

小测验:

  1. 电压的比喻是"水位差",那么电流的比喻是____。
  2. 信号从高变低的那一瞬间叫什么?(A. 上升沿 B. 下降沿 C. 高电平)
  3. 串口里"起始位"的作用是什么?
  4. 程序被 CPU 执行的三个步骤按顺序是:、、____。

0.13 本章总结

  • 0 和 1 = 晶体管的通/断,芯片全由这种开关组成;
  • 电平 = 电压范围(3.3V 高、0V 低),沿 = 变化的瞬间;
  • 时钟 = 节拍器,让所有逻辑统一行动;
  • 寄存器 = 能记住数的电路,控制硬件就是写寄存器;
  • 固件 = Flash 里的指令,CPU 取指-译码-执行;
  • 串口 = 一比特一比特传 0/1,日志就是通过它喊出来的。

下一章,我们学写代码的语言 C++——它最终会变成这一串串 0 和 1。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询