简介:基于CPLD的智能小车循迹控制系统课程设计报告书,面向电子技术、EDA课程设计及可编程逻辑器件初学者,完整展示从任务要求到系统实现的全流程设计思路。内容涵盖红外循迹模块、电源模块、CPLD控制模块、驱动模块与直流电机等核心电路的工作原理,重点介绍红外传感器经LM339电压比较器向CPLD传递偏离信号、通过PWM与主控模块控制L298N驱动电机实现自动循迹的设计方法。资源包内为1个doc文档,大小约3.79MB,包含报告正文、电路结构图、CPLD顶层电路图、仿真波形、实验照片与调试记录等完整图文素材,可直接作为同类课题的报告撰写与硬件调试参考。已有77人浏览学习,文档章节结构清晰,从任务与要求、方案论证到实验结果分析、调试问题解决与改进建议均有涉及,适合用于快速理解智能循迹小车的CPLD实现方案并借鉴其文档组织方式。
1. 用 CPLD 而不是单片机做循迹小车,到底在学什么?
拿到“智能小车循迹”课程设计题目,很多人第一反应是上 STM32F103ZET6 或 51 单片机,毕竟库函数、例程一抓一大把。但题目里“用可编程逻辑器件 FPGA/CPLD 设计实现”这一条,直接把要求拉回到了数字电路基本功:你要自己定义传感器电平怎么变成速度字,PWM 占空比由谁产生,组合逻辑和时序逻辑的边界在哪里。这份课设以 EPM7128SLC84-6 为核心,用四路红外对管加 LM339 电压比较器识别黑线,再用 L298N 驱动左右两组直流电机,最终实现直行、左小弯、左大弯、右小弯、右大弯和停车。想抄的人能拿到完整 VHDL 结构;想搞懂的人也能从这份设计中看到一套最小可复用的 CPLD 控制骨架,后续换摄像头循迹、五路循迹传感器,也只是换输入前端和状态表而已。
2. 四路红外 + LM339 + L298N:循迹小车硬件链路与电平约定
2.1 红外循迹模块:为什么黑线对应低电平
红外循迹模块的原理并不复杂:发射管一直发出红外光,白线反射强,接收管光电流大,输出端电压高;黑色胶带吸收红外光,接收管截止,输出端电压低。CPLD 不能直接读模拟电压,所以输出端要接一个 LM339 电压比较器,把传感器电压和 2.5V 参考电压做比较。检测到白线时输出高电平,检测到黑线时输出低电平,这样p1~p4就成了 CPLD 能直接处理的标准逻辑电平。
这个模块最容易被忽略的是参考电压的稳定性。很多实验板上用两个 10kΩ 电阻分压产生 2.5V,看起来很干净,但白纸反光率和黑胶带材质不同,固定阈值在上午和下午都不一样。常见做法是把分压电阻换成可调电位器,调到“白纸输出高、黑胶带输出低”的中间位置。我一般还会把探头离地控制在 1cm 以内,距离太高时接收管收到的环境光会盖过反射光,导致误判。
四个传感器在小车底盘上的排列顺序要和 VHDL 里的位序完全对应:p1是最左外侧,p2是左内侧,p3是右内侧,p4是最右外侧。原文里写P1~P4从左到右、1表示压线,如果你的接线把左右反过来,仿真时从波形编辑器里看不出问题,一上板小车就会对着黑线反向跑。
2.2 电机驱动:L298N 的 H 桥和使能端
L298N 是一个双 H 桥驱动器,内部包含两路独立输出。OUT1/OUT2接一组电机,OUT3/OUT4接另一组。这份设计买了四个直流电机,左右各两个,采用两两并联的方式:左侧两个电机并联到一个 H 桥,右侧两个并联到另一个 H 桥。这样每一侧的两个轮子转速一致,控制起来相当于只有左右两个执行器。
L298N 的逻辑功能可以放进一张表里:
| ENA(ENB) | IN1(IN3) | IN2(IN4) | 电机状态 |
|---|---|---|---|
| H | H | L | 正转 |
| H | L | H | 反转 |
| H | 同 IN2 | 同 IN1 | 快速停止 |
| L | X | X | 停止 |
原设计把IN1/IN2固定为高电平和低电平,电机永远只朝前进方向转,CPLD 只改变ENA和ENB上的 PWM 占空比来调速。这个接线方式最简单,但代价就是不能后退,也没法实现“一侧轮前进、一侧轮后退”的大半径回旋。如果你希望小车能在极端路径下救回来,就得把IN1/IN2也交到 CPLD 手里。
2.3 电源与共地:别让比较器参考电压漂掉
电源模块在报告里写得很简短:L298N 输出一路电压供逻辑和电机,再供给循迹模块。实际搭电路时要分开看:电机电源VS接电池组,L298N 的逻辑电源VSS取 +5V,CPLD 开发板和 LM339 也从 +5V 取电。电机启动瞬间电流很大,如果功率地和控制地没有在单点汇合,地线上的压降会把 LM339 的 2.5V 参考电压拉偏。
所以我一般会在电机端子两端并联一个 100nF 瓷片电容和一个 100μF 电解电容,再把电机外壳接地。传感器电源入口也加一颗 10μF 退耦电容。调试时如果发现小车一转起来红外 LED 就乱闪,不要先怀疑程序,先量一下电机启动瞬间的电源波形,大概率是地弹。
CPLD 的 I/O 电平也要确认。EPM7128SLC84-6 的 I/O 电压由VCCIO决定,如果板子工作在 3.3V,输出高电平约 3.3V,接 L298N 的逻辑输入通常没问题,因为 L298N 的高电平阈值只有约 2.3V。但反过来,如果传感器模块输出是 5V,而 CPLD 的引脚不兼容 5V,就需要加电平转换或限流电阻。
2.4 信号流与 PWM 频率选择
整条控制链路是:四路比较器输出 → CPLD 的p1~p4→ctl组合逻辑输出两路 4 位速度字 → 两路pwm计数器 → 两路pwmout→ L298N 的ENA/ENB→ 左右轮电机。CPLD 是并行执行,传感器状态变化到速度字更新的延迟只是组合逻辑门延迟加 PWM 计数器当前周期,不像单片机需要轮询,这是 CPLD 方案最大的优势。
但 PWM 计数器本身会吃掉一部分响应时间。原文用的是 4 位计数器,从 0 数到 9 回绕,也就是说 10 个时钟周期才产生一个完整 PWM 周期。如果直接用 4MHz 晶振,PWM 频率是 400kHz,对直流电机来说太高,但 L298N 开关损耗会变大。另一个问题是速度字精度只有 10%,直行和转弯之间的调节颗粒不够细。把这些总结成管脚约束示例,放到 Quartus 的.qsf文件里就是下面这样,实际引脚号必须按你自己的原理图改:
set_location_assignment PIN_134 -to clk set_location_assignment PIN_27 -to p[1] set_location_assignment PIN_28 -to p[2] set_location_assignment PIN_29 -to p[3] set_location_assignment PIN_30 -to p[4] set_location_assignment PIN_98 -to pwm_left set_location_assignment PIN_99 -to pwm_right说明:Tcl 里的PIN_134、PIN_27只是示例,EPM7128 的物理引脚要用 Quartus 的 Pin Planner 对照硬件原理图逐脚确认。p[1]这种写法是总线形式,如果你的 VHDL 端口名是p1、p2这种分散信号,就把它们拆开单独约束。下载器用 HW-USBN-2A 或 USB-Blaster 都可以,接 JTAG 时注意TCK、TDO、TMS、TDI、GND方向不能接反。
3. CPLD 内部逻辑:PWM 发生器与循迹状态表的 VHDL 实现
3.1 PWM 模块:4 位计数器产生 0~9 档占空比
PWM 模块的核心是十进制加减计数器。每次clk上升沿,计数器从 0 加到 9,然后回 0。当计数值小于速度字a时输出高电平,否则输出低电平,因此输出占空比正好是a/10。速度字是 4 位二进制,例如"1000"等于十进制 8,占空比 80%;"0011"等于十进制 3,占空比 30%。
library ieee; use ieee.std_logic_1164.all; use ieee.std_logic_unsigned.all; entity pwm is port( clk : in std_logic; a : in std_logic_vector(3 downto 0); pwmout : out std_logic ); end pwm; architecture cond of pwm is signal count : std_logic_vector(3 downto 0); begin process(clk) begin if rising_edge(clk) then if count = "1001" then count <= "0000"; else count <= count + 1; end if; if count < a then pwmout <= '1'; else pwmout <= '0'; end if; end if; end process; end cond;这段代码里,count <= count + 1和pwmout <= ...都是非阻塞赋值。在同一个进程里,后面的if count < a用的是时钟沿之前的旧count值,所以不会出现当拍计数清零导致输出异常。std_logic_unsigned让count + 1可以直接做无符号加法,这在 MAX+PLUS II 的 VHDL 环境里很常见。
要注意的是这个模块没有复位信号。仿真时如果count初始值是U,count + 1可能一直停在U;实际 CPLD 上电后寄存器状态虽然可能为 0,但依赖上电默认值不是好习惯。更稳妥的写法是增加一个异步复位rst_n,在复位时把count清成"0000"。作为课设演示,不加复位也能跑,但后面想加到 8 位 PWM 时,最好先把复位补上。
3.2 控制模块:状态表与速度字的组合逻辑映射
主控制模块ctl把四个传感器输入p1~p4拼接成一个 4 位向量TEMP,然后按黑线位置输出左右轮速度字。它没有时钟,是纯组合逻辑,传感器一变输出就变。原文里定义的转向规则和 VHDL 判断条件可以整理成一张状态表。
LIBRARY IEEE; USE IEEE.STD_LOGIC_1164.ALL; USE IEEE.STD_LOGIC_UNSIGNED.ALL; USE IEEE.STD_LOGIC_ARITH.ALL; ENTITY ctl IS PORT( p1, p2, p3, p4 : IN STD_LOGIC; temp1, temp2 : OUT STD_LOGIC_VECTOR(3 DOWNTO 0) ); END ctl; ARCHITECTURE behave OF ctl IS BEGIN PROCESS(p1, p2, p3, p4) VARIABLE TEMP : STD_LOGIC_VECTOR(3 DOWNTO 0) := "0000"; BEGIN TEMP := p1 & p2 & p3 & p4; IF TEMP = "0000" THEN temp1 <= "1000"; temp2 <= "1000"; -- 四路全白,直行 ELSIF TEMP = "0010" THEN temp1 <= "1000"; temp2 <= "0011"; -- 右内侧压线,左快右慢,右小弯 ELSIF TEMP = "0001" OR TEMP = "0011" THEN temp1 <= "1001"; temp2 <= "0001"; -- 右侧压线,左快右慢,右大弯 ELSIF TEMP = "0100" THEN temp1 <= "0011"; temp2 <= "1000"; -- 左内侧压线,左慢右快,左小弯 ELSIF TEMP = "1000" OR TEMP = "1100" THEN temp1 <= "0001"; temp2 <= "1001"; -- 左侧压线,左慢右快,左大弯 ELSE temp1 <= "0000"; temp2 <= "0000"; -- 全黑或其他异常,停车 END IF; END PROCESS; END behave;这段代码的逻辑说明:p1 & p2 & p3 & p4用&拼接,TEMP的最高位是p1,最低位是p4。所以"0100"表示p2=1,也就是左内侧压线,小车已经偏到黑线右边,需要向左修正;"0010"表示p3=1,右内侧压线,需要向右修正。TEMP是变量,:=是立即赋值,不会产生寄存器。
| TEMP | 传感器状态 | temp1(左轮) | temp2(右轮) | 动作 |
|---|---|---|---|---|
| "0000" | 四路全白 | 1000(8) | 1000(8) | 直行 |
| "0100" | 左内压线 | 0011(3) | 1000(8) | 左小弯 |
| "1000"/"1100" | 左外/左侧双路压线 | 0001(1) | 1001(9) | 左大弯 |
| "0010" | 右内压线 | 1000(8) | 0011(3) | 右小弯 |
| "0001"/"0011" | 右外/右侧双路压线 | 1001(9) | 0001(1) | 右大弯 |
| 其他 | 全黑/交叉/悬空 | 0000(0) | 0000(0) | 停车 |
从表里能看到大弯和小弯的区别:小弯左右速度字是 8 和 3,差值 5;大弯是 9 和 1,差值 8。差值越大,两侧轮速差异越明显,转弯半径越小。temp1决定左轮 PWM 占空比,temp2决定右轮 PWM 占空比。比如左小弯时左轮 30%、右轮 80%,车身自然会向左转。
3.3 顶层电路图:把 ctl 和 PWM 连起来
在 MAX+PLUS II 里新建一个 gdf 文件,把ctl和两个pwm符号拖进去。p1~p4是顶层输入,ctl的temp1接到左 PWM 的a,temp2接到右 PWM 的a,两个 PWM 的clk接同一个时钟。最后的输出pwm_l和pwm_r分别接 L298N 的ENA和ENB。
这一步看起来只是连线,但决定了下载后能不能跑。如果生成的网表里clk没有被锁定到实际晶振引脚,综合器会随便分配一个引脚,而实验箱上那个引脚没有时钟输入,小车自然不转。答案就是报告里提到的“CLK 输入管脚没有锁定”。所以顶层 gdf 里不要漏掉clk输入,管脚分配时先锁时钟,再锁传感器和 PWM 输出。
仿真时可以用波形编辑器给p1~p4加激励,观察temp1、temp2和pwmout。我一般会先仿真ctl的组合逻辑,确认状态表正确;再仿真完整顶层,确认 PWM 占空比。比如给TEMP=0100,左 PWM 输出应该是 30% 占空比,右 PWM 是 80%,在波形上数高电平周期即可验证。
4. 下载与调试:CLK 管脚、传感器阈值和车速标定
4.1 从 MAX+PLUS II 到 Quartus 的下载流程
EPM7128 老芯片用 MAX+PLUS II 很顺手,但新电脑上更多用 Quartus II 兼容旧工程。下载流程大致是:把所有.vhd加入工程,把 gdf 设为顶层,编译通过后再分配管脚。管脚分配这一步不要跳过,也不要依赖自动分配。
常见做法是先在顶层 gdf 里给每个输入输出命名,然后在 Quartus 的 Assignment Editor 里逐一填写 Location。如果工程是 Tcl 脚本管理,也可以把管脚约束写进.qsf:
set_global_assignment -name FAMILY "MAX7000S" set_global_assignment -name DEVICE EPM7128SLC84-6 set_location_assignment PIN_83 -to clk set_location_assignment PIN_54 -to p_i[0] set_location_assignment PIN_55 -to p_i[1] set_location_assignment PIN_56 -to p_i[2] set_location_assignment PIN_57 -to p_i[3] set_location_assignment PIN_70 -to pwm_l set_location_assignment PIN_71 -to pwm_r说明:这里的p_i[0]到p_i[3]是向量写法,对应 VHDL 里的p1~p4时可以在顶层端口定义成std_logic_vector(3 downto 0),或者直接写四个独立信号。引脚号是示意,实际要以你的原理图为准。分配完管脚后重新编译,打开 Pin-Out File 检查每个信号是否都在预期引脚上。
下载时使用 JTAG 方式。老的并口 ByteBlaster 已经很少见,现在很多下载器如 HW-USBN-2A 也能识别 MAX7000S 器件。连接顺序为TCK、TDO、TMS、TDI、GND,部分板子还有VCC参考脚,不接也可以工作。
提示:下载后如果小车没有任何反应,先不要怀疑程序,先量 CPLD 的
clk引脚有没有时钟信号,再看pwm_l和pwm_r有没有波形。CLK 没锁脚是这类课设里最常见的翻车点。
4.2 速度字、占空比和实际车速的换算
速度字是 4 位二进制,PWM 模块把它当成 0~9 的十进制数。标定之前先把每个速度字对应的占空比列成表,这是后面调参的基础。
| 速度字 | 二进制 | 十进制 | PWM 占空比 | 典型用途 |
|---|---|---|---|---|
| 0 | 0000 | 0 | 0% | 停车 |
| 1 | 0001 | 1 | 10% | 最低速/大弯内轮 |
| 3 | 0011 | 3 | 30% | 小弯内轮 |
| 8 | 1000 | 8 | 80% | 直行/小弯外轮 |
| 9 | 1001 | 9 | 90% | 大弯外轮 |
标定的方法是把小车架空,示波器探头接在 PWM 输出和 GND 之间,测高电平宽度。直流电机端电压的平均值等于占空比乘以电源电压,所以 80% 占空比在 5V 电源下大约是 4V。如果电机最低启动电压是 1V,10% 的占空比可能让轮子不转,这时候大弯内轮的速度字要从 1 改成 2 或 3,否则内轮抱死,转弯效果反而差。
实际调整时不要死守原文给的 8 和 3。直线速度太快容易冲出跑道,就先降直行档位,比如从"1000"改成"0111"或"0110";转弯不够就加大内外轮差值,比如把大弯从 9/1 改成 9/2,小弯从 8/3 改成 8/4。每次只改一个档位,改完就放在跑道上试一圈。
4.3 冲出跑道、抖动和反转问题排查
调试中出现的问题基本集中在传感器阈值、速度档位和地线噪声三块。下面这张表可以直接当排错清单用:
| 现象 | 可能原因 | 检查手段 |
|---|---|---|
| 小车完全不转 | CPLD 下载失败/CLK 没锁脚/L298N 使能悬空 | 示波器测 PWM 输出,测 ENA/ENB 电平 |
| 直行冲出去,转弯不响应 | 内外轮速度差太小,转弯时间不够 | 加大差值,如 8/3 改为 9/2 |
| 黑线附近来回摆头 | LM339 阈值太灵敏,传感器抖动 | 调电位器降灵敏度,或加迟滞比较器 |
| 转弯后进入白线仍继续转弯 | 组合逻辑只认当前电平,没有记忆上次方向 | 在 ctl 中增加丢线记忆状态 |
| 电机一启动 CPLD 就复位 | 地线不干净,电机启停拉低电源 | 加滤波电容,功率地与控制地单点汇合 |
| 传感器全部接反,方向反着跑 | p1~p4 位序和底盘安装相反 | 对照状态表,交换 p1/p4 或 p2/p3 |
报告里提到“小车在转弯地方总会冲出跑道”,我遇到过类似情况。原因通常是直线占空比太高,小车进入弯道时速度还没降下来,红外探头已经越过黑线。解决办法不是把转弯速度调得更猛,而是把直行速度降到赛道能承受的范围,再配合丢线记忆,让小车在短暂全白时还能保持上一个转向状态。
4.4 环境光与传感器一致性调整
原文建议“在白地板上贴黑线”,这是最稳妥的。黑色电工胶带最好是哑光的,不要用带反光的胶带。每路传感器都要单独标定:先把小车放在白色区域,调该路电位器让输出刚好为高;再用黑胶带完全遮住探头,调同一电位器让输出翻转为低。四路都做一遍之后再上电跑车。
如果现场没有示波器,可以用板上的 LED 观察比较器输出。LM339 输出低电平时 LED 灭,高电平时 LED 亮。当小车慢慢推过黑线时,四个 LED 应该按 p1→p2→p3→p4 的顺序依次变化。如果中间少了一个,那一路探头高度可能不对,或者电位器偏了。这一步在装底盘前做最省时间。
5. 进阶技巧:迟滞比较器、丢线记忆与示波器验证
5.1 用迟滞比较器消除传感器抖动
LM339 是开集输出,直接拿 2.5V 固定阈值比较时没有回差。小车压线时接收管电压是缓变的,输出会在阈值附近反复翻转,CPLD 就可能在一个弯道里连续判断多次方向。一个低成本改法是利用 LM339 的开集输出:在输出端到同相输入端接一个 100kΩ~1MΩ 的正反馈电阻,让阈值产生约 200mV 迟滞。翻转一旦发生,输出会保持稳定,直到信号明显越过另一个阈值。如果不想动硬件,就把 ctl 改成时钟驱动,连续采样两拍,只有连续读到相同电平时才更新状态。
5.2 丢线记忆:把纯组合逻辑改成状态机
原版ctl是全组合逻辑,TEMP=0000时无条件直行。高速过弯时,小车很可能因为惯性冲出黑线,四路全白,结果它直行冲得更远。更稳的做法是加一个寄存器记录上一次有效转向,当检测到全白时延续上一次转向,直到再次看到黑线。大致框架如下:
if rising_edge(clk) then if TEMP = "0000" then temp1 <= last_left; -- 全白丢线,沿用上次速度字 temp2 <= last_right; else case TEMP is when "0100" => temp1 <= "0011"; temp2 <= "1000"; last_left <= "0011"; last_right <= "1000"; -- 其他状态同理 when others => null; end case; end if; end if;说明:last_left和last_right是新增的 4 位寄存器信号,在每次正常检测到黑线时更新。丢线时查表不复位,而是继续输出上一次的速度字。这个技巧在电磁循迹和摄像头循迹里也很常用,本质是给控制器加了一点“惯性”。
5.3 用示波器验证 PWM 时序
示波器是这套系统最有价值的调试工具。把探头接到pwm_l,地线夹接 CPLD 的 GND,触发电平设为 1.65V,然后看高电平和低电平宽度。对 4 位计数器,一个完整周期是 10 个clk,速度字 8 时高电平应占 8 个时钟周期。如果看到占空比不是预期值,先用万用表量ctl输出的temp1引脚电平,再反向排查传感器状态。
报告中提到仿真波形存在“判断延时”,这个延时主要来自 PWM 计数器要等当前计数周期结束。把clk频率降低到 1kHz~10kHz,判断反应会慢但占空比更稳定;把clk提高到几 MHz,PWM 频率又太高。折中做法是给ctl单独用较低时钟,PWM 用较高时钟,两者分开。
5.4 把 IN1/IN2 交给 CPLD,实现倒车大转弯
原文硬件把 L298N 的IN1/IN2固定成前进方向,所以不能实现“一侧轮前进、一侧轮后退”的大转弯。改进方法是增加两路 CPLD 输出dir_l和dir_r,分别控制左右两侧 H 桥的方向。正转时IN1=1, IN2=0,反转时IN1=0, IN2=1。当需要更小的转弯半径时,让一侧轮反转,另一侧正转,差速效果比 9/1 速度字更明显。需要注意的是,反转瞬间电流方向突变,PWM 占空比不要直接从 80% 跳到 0,最好做一两个时钟周期的斜坡过渡,否则电机齿轮会受力冲击,电源也会被拉低。
把 4 位 PWM 计数器换成 8 位unsigned,再把速度字从常数表改成由循迹状态查表得到的 0~255 占空比数值,这套系统就不再只是课设,而是一台可以加 PID 调参的试验车。
本文还有配套的精品资源,点击获取