
简介基于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)电机状态HHL正转HLH反转H同 IN2同 IN1快速停止LXX停止原设计把IN1/IN2固定为高电平和低电平电机永远只朝前进方向转CPLD 只改变ENA和ENB上的 PWM 占空比来调速。这个接线方式最简单但代价就是不能后退也没法实现“一侧轮前进、一侧轮后退”的大半径回旋。如果你希望小车能在极端路径下救回来就得把IN1/IN2也交到 CPLD 手里。2.3 电源与共地别让比较器参考电压漂掉电源模块在报告里写得很简短L298N 输出一路电压供逻辑和电机再供给循迹模块。实际搭电路时要分开看电机电源VS接电池组L298N 的逻辑电源VSS取 5VCPLD 开发板和 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可以直接做无符号加法这在 MAXPLUS II 的 VHDL 环境里很常见。要注意的是这个模块没有复位信号。仿真时如果count初始值是Ucount 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表示p21也就是左内侧压线小车已经偏到黑线右边需要向左修正0010表示p31右内侧压线需要向右修正。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 连起来在 MAXPLUS II 里新建一个 gdf 文件把ctl和两个pwm符号拖进去。p1~p4是顶层输入ctl的temp1接到左 PWM 的atemp2接到右 PWM 的a两个 PWM 的clk接同一个时钟。最后的输出pwm_l和pwm_r分别接 L298N 的ENA和ENB。这一步看起来只是连线但决定了下载后能不能跑。如果生成的网表里clk没有被锁定到实际晶振引脚综合器会随便分配一个引脚而实验箱上那个引脚没有时钟输入小车自然不转。答案就是报告里提到的“CLK 输入管脚没有锁定”。所以顶层 gdf 里不要漏掉clk输入管脚分配时先锁时钟再锁传感器和 PWM 输出。仿真时可以用波形编辑器给p1~p4加激励观察temp1、temp2和pwmout。我一般会先仿真ctl的组合逻辑确认状态表正确再仿真完整顶层确认 PWM 占空比。比如给TEMP0100左 PWM 输出应该是 30% 占空比右 PWM 是 80%在波形上数高电平周期即可验证。4. 下载与调试CLK 管脚、传感器阈值和车速标定4.1 从 MAXPLUS II 到 Quartus 的下载流程EPM7128 老芯片用 MAXPLUS II 很顺手但新电脑上更多用 Quartus II 兼容旧工程。下载流程大致是把所有.vhd加入工程把 gdf 设为顶层编译通过后再分配管脚。管脚分配这一步不要跳过也不要依赖自动分配。常见做法是先在顶层 gdf 里给每个输入输出命名然后在 Quartus 的 Assignment Editor 里逐一填写 Location。如果工程是 Tcl 脚本管理也可以把管脚约束写进.qsfset_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 占空比典型用途0000000%停车10001110%最低速/大弯内轮30011330%小弯内轮81000880%直行/小弯外轮91001990%大弯外轮标定的方法是把小车架空示波器探头接在 PWM 输出和 GND 之间测高电平宽度。直流电机端电压的平均值等于占空比乘以电源电压所以 80% 占空比在 5V 电源下大约是 4V。如果电机最低启动电压是 1V10% 的占空比可能让轮子不转这时候大弯内轮的速度字要从 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是全组合逻辑TEMP0000时无条件直行。高速过弯时小车很可能因为惯性冲出黑线四路全白结果它直行冲得更远。更稳的做法是加一个寄存器记录上一次有效转向当检测到全白时延续上一次转向直到再次看到黑线。大致框架如下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提高到几 MHzPWM 频率又太高。折中做法是给ctl单独用较低时钟PWM 用较高时钟两者分开。5.4 把 IN1/IN2 交给 CPLD实现倒车大转弯原文硬件把 L298N 的IN1/IN2固定成前进方向所以不能实现“一侧轮前进、一侧轮后退”的大转弯。改进方法是增加两路 CPLD 输出dir_l和dir_r分别控制左右两侧 H 桥的方向。正转时IN11, IN20反转时IN10, IN21。当需要更小的转弯半径时让一侧轮反转另一侧正转差速效果比 9/1 速度字更明显。需要注意的是反转瞬间电流方向突变PWM 占空比不要直接从 80% 跳到 0最好做一两个时钟周期的斜坡过渡否则电机齿轮会受力冲击电源也会被拉低。把 4 位 PWM 计数器换成 8 位unsigned再把速度字从常数表改成由循迹状态查表得到的 0~255 占空比数值这套系统就不再只是课设而是一台可以加 PID 调参的试验车。本文还有配套的精品资源点击获取