用 8051 单片机做一台循迹机器人
一台在裸 8051 上运行的循迹车——并且清楚理解 Arduino 本来会替你隐藏起来的每一层,从复位电路到 PWM 波形。
几乎每一门教单片机的工程课程,最后的作业都是同一个:在 8051 上做一台循迹车。它之所以是个好作业,原因通常没人说——这颗芯片什么都不给你。没有 analogWrite() 让你设定电机转速,没有 analogRead() 读传感器,也没有驱动板用的库。你平时习惯调用的每一层,都要自己建起来。
这就是这条制作路线和 Arduino 循迹车之间的区别。机器人是同一台,深度却不是。你要亲手写出 Arduino 在硅片里已经做好的脉宽调制(PWM)生成器,弄明白为什么 8051 的引脚只有反接才能点亮 LED,并且在买任何东西之前就确切知道,你的电机控制究竟吃掉了多少 CPU。
8051 是一颗 40 脚封装的 8 位 Harvard 架构单片机,通常是带 8 KB 闪存和 256 字节 RAM 的 AT89S52。它没有 PWM 外设,端口是准双向的(读输入之前必须先写 1),引脚吸收的电流比提供的多——所以 LED 要接在吸收端,而电机驱动的 H 桥(高边桥)需要上拉才能被可靠驱动。这些细节听起来像脚注,直到机器人完全不动了,你才发现原因是 EA 悬空,或者复位是高电平有效而你把它接到了低电平。
你从面包板起步,只有芯片、一枚 11.0592 MHz 晶振和两个负载电容、一套复位网络,以及把 EA 拉高——这是真正能执行代码的最小电路。前三篇教程会带你走遍架构与内存映射、通用输入输出(GPIO)与端口对外接口,然后用定时器 0 的 8 位自动重装模式手搓出 PWM:大约 460.8 Hz、5% 的步进,占用大约四分之一的 CPU——这是你没有外设所必须付出的代价。
从这一步开始,路线就和 Arduino 循迹车汇合了:同样把红外(infrared)反射阵列压缩成一个加权误差,同样的 PID(比例-积分-微分)转向,以及同样在浏览器模拟器里找出能干净过弯的增益。技术树把硬件节点和概念节点分开,所以前半程只需要芯片和一支 ISP(在系统编程)烧录器(比如 USBasp),只有最后两个制作节点才需要底盘、电机和传感器阵列。
技术树按序走。前三节点面包板芯片就行,循迹增益浏览器模拟器调,后两步底盘电机要。进度这台设备存。
物料单
| 零件 | 数量 | 约价 | 说明 |
|---|---|---|---|
| AT89S52 40 脚 DIP | 1 | $2 | **在系统可编程。**AT89C51 要手里没有的并行烧录器 |
| USBasp ISP 烧录器 | 1 | $3 | AT89S52 经 SPI 烧——实用就靠它 |
| 11.0592 MHz 晶振 | 1 | $0.50 | **不要用 12 MHz。**见下文——这不是偏好问题 |
| 33 pF 瓷片电容 | 2 | $0.20 | 晶振负载电容 |
| 10 µF 电解 + 10 kΩ 电阻 | 各 1 | $0.20 | 复位网络。这里复位高有效 |
| 10 kΩ 排阻 | 1 | $0.50 | P0 上拉——开漏无它高出不来 |
| 40 脚 ZIF/DIP 座 | 1 | $1 | 芯片绝不直焊;拔它 |
| 红外反射阵列 | 1 | $5–10 | 数字输出,或外 ADC——8051 无模拟输入 |
| L298N | 1 | $3 | |
| TT 减速电机 | 2 | $4 | |
| 两轮底盘 | 1 | $6–10 | |
| 电池包 | 1 | $6 | |
| 面包板杜邦线 | — | $5 | 前三里程碑全这发生 |
合计约 $35–50。
有两件买什么,会决定这个项目是做得开心还是做得痛苦。
**晶振必须是 11.0592 MHz。**8051 是靠定时器 1 溢出来产生串口波特率的,而在 11.0592 MHz 下,每一个标准波特率算出来的重装值都是整数——9600 波特率正好对应 TH1 = 0xFD。用 12 MHz 晶振,对应目标是 9600 却会算出 10417,也就是 8.5% 的误差,远超 UART 大约 3% 的容忍度。于是每一个字符到达时都是坏的。芯片在你想打印点什么来调试之前一切正常,而那恰恰是最糟糕的发现时机。
传感器阵列必须是数字输出,否则你必须外加一颗 ADC,比如 ADS7828 或 ADC0808。8051 完全没有模拟输入。这是这颗芯片从你手里拿走、而 Arduino 白送给你的第一样东西,值得有意识地做取舍:比较器输出更简单,但你会失去那个平滑的比例误差信号,而它正是让 PID 调得好的关键。
最小电路
传感器电机前,面包板光芯片跑代码先。六连接:
| 引脚 | 接到 | 为什么 |
|---|---|---|
40(VCC) |
+5 V | |
20(GND) |
地 | |
31(EA) |
+5 V | 选内部程序存储器。悬空接地芯片从不存在的外部存储器取纹丝不动 |
18、19(XTAL2、XTAL1) |
晶振两端 | 每腿对地 33 pF 电容 |
9(RST) |
+5 V 经 10 µF,对地 10 kΩ | 这里复位高有效——几乎别的单片机反过来 |
EA 悬空是第一块 8051 板子看起来像坏了的头号原因,而且它不产生任何报错、任何指示灯、任何线索。每次都先查它。
在 P1.0 上接一颗 LED 就是这个项目的“hello world”:负极接引脚,正极经一个 330 Ω 电阻接到 +5 V,写 0 点亮。引脚能吸收约 10 mA,却只能提供大约 60 微安,所以反过来接的话它只会发出微弱的光,看起来像是故障。
里程碑制作
前三面包板芯片就行,底盘买前走老远。
| # | 里程碑 | 测试 | 通过长这样 |
|---|---|---|---|
| 1 | 芯片跑 | P1.0 LED 闪 | 闪。不闪先查 EA 别的后 |
| 2 | 时序已验证 | 用计数延迟精确地以 1 Hz 闪烁 | 秒表测 60 次闪烁,误差在一秒以内 |
| 3 | 串口正常 | 以 9600 波特率打印一个计数器 | 文本干净——出现乱码就是晶振不对 |
| 4 | GPIO 进 | P1.1 按键上拉读 | 可靠读。读前写 1 记住 |
| 5 | 定时器中断 | Timer 0 ISR 翻脚,示波器计时 | 周期算的稳 |
| 6 | 软件 PWM | 以 0%、25%、50%、100% 占空比驱动一颗 LED | 亮度明显不同,且没有闪烁 |
| 7 | PWM 开销量出来了 | 在主循环里翻转一个备用引脚并计时 | 你能说出这个中断服务程序占用了多少比例的 CPU |
| 8 | 电机驱动 | 两轮指令占空比正反 | 正反速度,台架 |
| 9 | 传感器读 | 线地面全通道打印 | 每通道两分得出 |
| 10 | 误差信号 | 用手推着机器人横穿黑线 | 加权位置单调变化,没有跳变 |
| 11 | 跟随 | 胶带椭圆跑 | 整圈完 |
里程碑 7 正是这个项目存在的理由。Arduino 的 analogWrite 不花任何代价——PWM 是在硅片里生成的。而这里你是用一个定时器中断手搭出来的,它每秒要触发几万次。把它占用了多少比例的 CPU 量出来,就是这一课的要义:通常是整颗芯片的四分之一,而这正是硬件 PWM 外设帮你省下的东西。
做好长什么样
| 测量 | 这台典型 |
|---|---|
| 11.0592 MHz 机器周期 | 1.085 µs——指令数时序知道 |
| 软件 PWM 频率 | Timer 0 八位自动重装约 460 Hz 5% 步 |
| PWM ISR 吃 CPU | 约 25% |
| 主循环剩余的频率 | 控制环仍有充裕的 200 Hz 以上 |
| RAM 占用 | 256 字节中不到 100 字节——这个约束真实存在,但在这里并不紧张 |
| 跟随性能 | 与 Arduino 版本相当;这台车本身不是重点 |
最后这一点值得直说:**这台机器人不会比 Arduino 循迹车更强。**它是同一台机器人,只是用更费力的方式造出来,而它的回报是理解而不是性能。如果你想做一台更好的循迹车,去调 Arduino 那台。如果你想知道 analogWrite 一直在替你做些什么,那就来造这台。
故障排查
| 现象 | 可能原因 | 解决 |
|---|---|---|
| 芯片彻底没反应 | EA 悬空接地 |
31 脚接 +5 V |
还是没反应 EA 高 |
晶振没起振 | 33 pF 电容晶振焊点查 |
| 不停复位 | RST 拉高 | 高有效——10 µF / 10 kΩ 网络查 |
| 串口打印乱码 | 12 MHz 晶振 | 11.0592 MHz,或接受无串口调试 |
| LED 很暗 | 拉电流接 | 负极接脚;写 0 点亮 |
| P0 读写全无 | 开漏无上拉 | 每脚 10 kΩ 到 +5 V |
| PWM 周期抖 | ISR 重装晚 | 第一句重装或模式 2 |
| 程序回到随机地址 | 堆栈压寄存器组 1 | main() 顶 SP = 0x30; |
| 代码一多行为就诡异 | RAM 用光了 | 总共只有 256 字节——查链接器生成的 map 文件 |
| 烧录器认不出芯片 | ISP 脚占用或复位失控 | 烧录时 P1.5–P1.7 RST 释放 |
| 传感器无用值 | 无 ADC 芯片上模拟阵列 | 数字输出阵列或外 ADC |
下一步去哪
**再加一个定时器,量一量你自己的主循环。**定时器 0 跑着 PWM,定时器 1 就可以用来数控制循环的周期,把“感觉跟手”变成一个数字。在一颗你能数机器周期的芯片上,这个数字是完全可以精确预测的——去验证你的预测。
通过端口 0 接一块 LCD,你就会正面碰上开漏这个问题,外加一个确实比处理器慢得多的设备所带来的时序要求。
然后去做 Arduino 版本——并留意你现在对其中每一行的理解。那一行 analogWrite,在这里就是你要花一个晚上写的那个中断服务程序;那个 analogRead,就是你不得不外加的 ADC。这番对比才是这个项目真正的交付物。
项目路线图
搭建路径
沿着技术树,从零件一路走到一台能正常工作的机器人。每个节点在其前置条件完成后解锁,你的进度会保存在这台设备上。
已完成 0 / 17
元器件
本路径中的教程
- 入门 · 阅读约 30 分钟8051 架构:引脚、存储器映射与寄存器哈佛结构内核、四组端口、256 字节 RAM,以及能按机器周期数出来的时序。
- 入门 · 阅读约 28 分钟8051 GPIO 与端口接口:灌电流,别拉电流为什么 8051 的 LED 要反着接、为什么读引脚之前要先写 1,以及一个端口能驱动多大的负载。
- 中级 · 阅读约 35 分钟用 8051 定时器和软件 PWM 控制电机用定时器 0 中断搭出 PWM,在频率与分辨率之间做出取舍,并驱动 H 桥。
- 入门 · 20 分钟读取用于循迹的红外反射传感器阵列把一排红外传感器的读数变成一个平滑的线位置,用它来控制转向。
- 入门 · 阅读约 18 分钟用 PWM 与 H 桥电路控制直流电机用 PWM、H 桥和符合实际的电流限制,安全地接线并控制一台有刷直流电机。
- 入门 · 阅读约 20 分钟制作循迹机器人:从传感器到 PID 转向从红外反射传感器到加权误差,再到平滑的 PID 转向——循迹机器人究竟是如何工作的。
接线之前先练习
用在线模拟器把它调好
搭建路径会经过一个浏览器实验台。先在这里把行为调对,再把参数迁移到真实的机器人上。
常见问题解答
为什么要在 8051 而不是 Arduino 上做循迹车?
因为 8051 逼着你亲手搭建 Arduino 替你隐藏起来的东西。没有 analogWrite,所以你要自己写 PWM 生成器;没有 pinMode,所以你会弄明白准双向端口究竟是怎么工作的。如果目标是做一台能用的机器人,就用 Arduino。如果目标是理解这台机器人,8051 每小时教给你的东西,比任何一块什么功能都有现成库的开发板都多。
做一台 8051 循迹车需要哪些零件?
一颗 AT89S52 及其晶振和复位电路、一个红外反射传感器阵列、一个 H 桥驱动器(比如 L298N 或 L293D)、两个 TT 减速电机、一个两轮驱动底盘,以及一个能让逻辑供电和电机供电走不同电源轨的电池包。你还需要一个 ISP 烧录器——USBasp 非常便宜,只通过四个引脚就能给 AT89S52 烧录程序。
8051 没有 PWM 硬件,怎么控制电机转速?
用软件生成 PWM。定时器 0 工作在 8 位自动重装模式,每 100 个机器周期中断一次;中断程序对节拍计数,并在每个周期的前 N 个节拍里保持输出为高电平。在 11.0592 MHz 下,这能得到 460.8 Hz 的频率和 5% 的占空比步进,代价是大约四分之一的 CPU——这就是没有 PWM 外设所要付出的代价。
代码可以用 C 写吗,还是必须用汇编?
用 C 完全没问题,这也是现在大多数课程采用的方式。Keil C51 是业界标准,SDCC 是免费的开源替代品。在这颗芯片上,汇编值得读一读——指令集很小,时序也可以逐条计数——但你不必用它写出一整台机器人的程序。
为什么我的 8051 机器人烧录之后完全没反应?
先检查 EA(31 脚)有没有接到 +5 V,这一步要排在一切之前。如果它悬空或接地,芯片会试图从并不存在的外部存储器里取程序,于是什么也不运行,而且没有任何报错。然后再检查复位电路——8051 的复位是高电平有效——以及晶振的两个负载电容。
开始这个项目之前需要先买零件吗?
不需要。循迹逻辑和增益调整都可以在浏览器模拟器里完成,而三篇 8051 教程只需要把芯片插在面包板上。等你准备好给实体机器人接线时,再去买底盘和电机。