51单片机循迹避障小车:STC89C52+L298N驱动与传感器方案
简介基于STC89C52单片机与L298N驱动芯片的智能循迹避障小车毕设/课设文档资料适合电子信息、自动化等专业学生用于课程设计、毕业设计或单片机入门实践。内容覆盖系统方案论证、硬件电路设计、软件流程与调试方法重点讲解红外对管检测黑线与障碍物、L298N电机驱动及PWM调速实现可帮助读者快速搭建完整的小车控制系统。压缩包内共1个doc文档约434KB包含摘要、绪论、方案设计、硬件设计、软件设计、安装调试及参考文献等完整章节目录结构清晰便于按模块查阅。目前已有541人学习下载。文档对STC89C52单片机特性、L298N驱动原理、红外传感器应用等关键知识点均有详细解析并给出主程序、循迹模块、避障模块的设计思路适合作为毕设写作参考或课设实操蓝本。1. 循迹避障小车为什么非从 STC89C52L298N 这套组合入手第一次把 STC89C52 和 L298N 接在一起的小车通常会遇到“车能上电但电机就是不转”原因往往不是芯片坏了而是共地没接、使能端悬空或逻辑电源档位选错。这套标题所代表的毕设/课设方案本质是把一辆小车拆成四个可独立验证的模块STC89C52 负责逻辑决策L298N 负责驱动直流减速电机红外对管负责循迹HC-SR04 负责避障。它不是性能最优的架构却是把“传感器采集 → 单片机判断 → 功率输出”这条链路暴露得最清晰的组合适合正在做课设、毕设的同学也适合刚接手 51 项目的工程师把驱动和感知的基本功补齐。接下来的内容按接线、循迹、避障、验证的顺序推进先把 L298N 的供电和 PWM 说透。2. L298N 电机驱动模块用 STC89C52 的定时器模拟 PWM 驱动直流电机L298N 是双 H 桥驱动器内部是两个 H 桥电路分别控制左右两路电机。之所以在 STC89C52 方案里反复出现是因为它的供电逻辑简单VS 接电机电源VSS 接 5V 逻辑电源GND 与单片机共地之后就能用普通 IO 口直接控制正反转。STC89C52 的 IO 输出电流不足以驱动电机L298N 在这里起的是功率放大和方向隔离的作用。2.1 双 H 桥的供电分配与 STC89C52 的 IO 电流限制L298N 模块板上通常有两路电源标注一路是电机电源丝印常见为 VMS 或 12V另一路是逻辑电源VSS 或 5V。电机电源接电池正极电压范围常见 6V 到 12V逻辑电源 VSS 接稳定的 5V。STC89C52 的工作电压是 5V所以 VSS 可以直接从同一组 5V 稳压输出取电。这里的第一个关键是共地电池的 GND、L298N 的 GND、STC89C52 的 GND 三者必须接在一起否则 IO 口的高电平没有参考基准表现为电机忽转忽停或者完全不动。第二个关键是电流分工。STC89C52 的准双向 IO 口在输出高电平时驱动能力很弱灌电流虽然可以到 20mA 左右但拉电流通常只有几百微安到几毫安根本推不动直流电机这种感性负载。L298N 的作用是把单片机的小信号放大成电机需要的电流IN1~IN4 只消耗微安级的逻辑电流。所以不需要在中间加三极管或光耦IO 口到 L298N 输入引脚之间可以直接连线更不需要加限流电阻。2.2 最小接线表电源、逻辑输入与使能端的接法L298N 模块、STC89C52 和直流减速电机的接线我一般这样接L298N 引脚STC89C52 / 电源说明VMS电机电源电池 7.4V 或 9V 正极给电机和 H 桥功率部分供电VSS逻辑电源5V 稳压输出STC89C52 也用同一路 5VGND电池负极、单片机 GND三处共地IN1P1.0左电机方向 AIN2P1.1左电机方向 BIN3P1.2右电机方向 AIN4P1.3右电机方向 BENAP1.4 或接 5V左电机使能接 PWM 可调速ENBP1.5 或接 5V右电机使能接 PWM 可调速OUT1/OUT2左电机两根线正反接决定正转反转OUT3/OUT4右电机两根线同上IO 口的选择有讲究。P0 口在 STC89C52 上是开漏结构不加外部上拉电阻时高电平不稳定所以驱动电路不要放在 P0。P3 口上还有 RXD/TXD 和外部中断留出来给超声波模块和串口调试更合适。P1 口作为 DIP-40 封装的准双向 IO上电默认是高电平直接接 L298N 的逻辑输入正好符合模块“高电平使能”的习惯。2.3 用定时器 1 在 STC89C52 上模拟 PWM 调速STC89C52 没有硬件 PWM 外设常见的做法是用定时器中断模拟。我一般让定时器 1 工作在 8 位自动重装模式周期固定 100 个计数占空比则由一个全局变量pwm_duty_left控制。下面这段代码驱动左电机右电机同理#include reg52.h // L298N 控制引脚 sbit IN1 P1^0; sbit IN2 P1^1; sbit ENA P1^4; unsigned char pwm_duty_left 60; // 占空比 60% unsigned char pwm_cnt 0; void timer1_init(void) { TMOD 0x0F; // 只清 Timer1 的位保留 Timer0 设置 TMOD | 0x20; // Timer1 模式 28 位自动重装 TH1 256 - 100; // 周期 100 个计数 TL1 256 - 100; ET1 1; // 开 Timer1 中断 TR1 1; // 启动 Timer1 } void timer1_isr(void) interrupt 3 { pwm_cnt; if (pwm_cnt pwm_duty_left) ENA 1; else ENA 0; if (pwm_cnt 100) pwm_cnt 0; } void motor_left_forward(void) { IN1 1; IN2 0; }TMOD 0x0F保留低四位是为了不影响后面 HC-SR04 用到的 Timer0 配置。TH1 256 - 100把溢出周期设为 100 个机器周期12MHz 晶振下每 100us 进入一次中断PWM 基频约 100Hz对减速电机足够平滑。pwm_duty_left的取值范围是 0 到 10060 表示 60% 占空比。改这个全局变量就能在线调速。需要提醒的是中断里只改 ENA不要动 IN1/IN2。方向引脚电平变化太快会在电机换向瞬间产生大电流尖峰容易把模块上的续流二极管烧掉。先停车再换向是常规操作。2.4 上电不转、单侧无力L298N 最常见的 3 个坑第一个坑是使能端悬空。L298N 模块默认带有跳线帽如果拔掉了跳线帽又没有接任何电平ENA/ENB 悬空输出就是高阻态电机自然不转。检查时先看跳线帽是否扣紧或者把 ENA/ENB 直接接 5V。第二个坑是电机电源电压过低。很多课设电池标称 7.4V但镍氢电池在低压报警时实际掉到 6V 以下L298N 内部 H 桥的饱和压降会吃掉 1.5V 到 2V到电机端可能只剩 4V 多车表现为单侧能转、负重就停。用万用表量 OUT1 和 OUT2 之间的电压空载时接近 VMS 就正常。第三个坑是 PWM 基频过高。有些同学把 TH1 装得很小中断频率跑到 5kHz 以上电机电感在高速斩波下发热电流波形变差转速反而下降。减速电机常见做法是 100Hz 到 500Hz太低会听到明显的“哒哒”声太高反而无力。3. 循迹传感器布局与阈值判定TCRT5000 数字量模块怎么接 STC89C52循迹的本质是检测黑线和白色地面的反射差异。黑色吸收红外光白色反射红外光所以传感器读出的电压会明显不同。常见做法是买带 LM393 比较器的 TCRT5000 模块输出是 TTL 电平直接接 STC89C52 的 GPIO不需要自己搭放大电路。3.1 为什么选 TCRT5000 而不是光敏电阻光敏电阻对环境光太敏感室内灯光一变阈值就要重新调。TCRT5000 是红外发射管加光电晶体管的组合模块自带固定波长红外光配合 LM393 比较器可以把模拟量在模块内部就变成 0/1。STC89C52 的 ADC 是额外扩展的用数字量模块可以省掉 ADC 采样代码直接在 GPIO 上读电平课程设计最常见的做法。模块上的电位器调节比较器参考电压。顺时针拧到底灵敏度变高更容易把浅色误判成黑线逆时针拧到底灵敏度变低黑线可能读不出来。调节目标是在黑白分界处刚好翻转。3.2 三路循迹的安装位置与逻辑判定表三路是性价比最高的组合。三个 TCRT5000 模块横向排开间距 2cm 左右安装在车头前方 1cm 到 2cm 处离地高度 1cm 到 2cm。中间一个对准黑线中心左右两个稍微超出线宽。安装后要保证三个传感器镜头在同一个水平面否则弯道时两边的检测距离不一致。模块输出低电平表示检测到黑线高电平表示在白底上。三个信号的全部组合和对应动作S1(左)S2(中)S3(右)含义动作111全白可能冲出赛道直行车身回正110线在右侧边缘右转101线在正中直行100线偏右右转011线偏左左转010线在左侧边缘左转001交叉路口或脱线继续直行000全黑停车或恢复直行这个表的前提是模块输出低电平为黑不同模块可能相反接好后先打印判断一次再写逻辑。3.3 数字量输出模块的阈值调整步骤调阈值不需要写代码拿一个小螺丝刀在空地上操作就行。步骤是把车放在纯白色地面用螺丝刀调电位器直到指示灯刚好熄灭再把传感器放到黑线上指示灯应该刚好亮起。两边余量合适的话黑白两种状态下电压差至少在 1.5V 以上这样运行中受环境光干扰也不会频繁翻转。我一般会在调整后把模块对着光线变化比较大的窗口再试一次。如果指示灯在黑色和白色间抖动说明模块离地太高或者电位器调得太临界。常规做法是把离地高度降到 1cm 左右重新调。3.4 GPIO 读取与连续确认消抖代码直接在 while 循环里读 IO 电平会受抖动和地面灰度影响。简单可靠的思路是连续采样 5 次5 次结果一致才认为状态稳定#include reg52.h sbit S1 P2^0; // 左 sbit S2 P2^1; // 中 sbit S3 P2^2; // 右 unsigned char read_track_state(void) { unsigned char i, sample, result 0; for (i 0; i 5; i) { sample 0; if (S1 1) sample | 0x01; if (S2 1) sample | 0x02; if (S3 1) sample | 0x04; result sample; // 只保留最后一次采样 // 实际项目可以在这里统计高电平次数 } return result; }这段代码的要点是STC89C52 的准双向口作为输入前要先向端口写 1否则读到的全是被内部场效应管拉低的电平。result的 bit0、bit1、bit2 分别对应左、中、右传感器返回值和上一节的判定表逐位对照。采样间隔里插入 1ms 左右延时等红外发射管和比较器响应稳定后再读下一次能滤掉大部分地面毛刺。如果想更稳可以改成计数器方式每个传感器统计 5 次采样中高电平出现次数大于等于 4 次才判为高这样对黑白交界处的临界抖动有更强的容忍度。课设赛道上用简化版已经足够关键是不要在一次采样后就改变转向否则跨界线上的一瞬间会把车拐反。3.5 环境光干扰与地面反光的缓冲处理放在瓷砖地面做循迹时可能遇到传感器在暗色瓷砖上误判成黑线。原因是瓷砖花纹对红外反射率分布不均。常见做法是让传感器离地更近缩小检测范围同时把比较器电位器调到“黑线亮、白地灭”的中间偏临界位置。如果教室灯光是荧光灯管红外模块本身滤除了可见光干扰但仍然要留意阳光直射的那几秒钟强红外会在白地上把输出拉到黑线电平。我在课设里遇到这种情况会直接给模块镜头加一段黑色热缩管只留出正下方 5mm 的检测窗口效果立竿见影。4. HC-SR04 超声波避障定时器 0 测距与循迹避障的状态切换避障和循迹侧重点不同循迹保证车跟着黑线走避障保证黑线上突然出现障碍物时车不停下来。HC-SR04 量程在 2cm 到 400cm用在桌面小车场景足够。4.1 HC-SR04 的测距时序与 STC89C52 定时器选择HC-SR04 工作流程单片机的 Trig 引脚输出一个 10us 以上的高电平触发信号模块内部发 8 个 40kHz 超声波脉冲然后 Echo 引脚输出一个高电平高电平持续时间就是声波往返时间。距离等于高电平时间乘以声速 340m/s 再除以 2。Echo 高电平最长可达 20ms 左右对应约 3.4m 量程。如果用延时函数死等 Echo 拉低整个单片机都会被占住循迹就没法做了。常见做法是用外部中断或者定时器 0 测量高电平脉宽。STC89C52 的 Timer0 是 16 位计数器12MHz 晶振下最大计时 65.5ms覆盖 HC-SR04 的量程没问题。4.2 查询方式测距代码TR0 启停与距离换算#include reg52.h sbit TRIG P3^4; sbit ECHO P3^5; void delay_10us(unsigned char n) { while (n--); } unsigned int get_distance_cm(void) { unsigned int t; TRIG 1; delay_10us(2); // 20us大于规格书要求的10us TRIG 0; while (ECHO 0); // 等待Echo变高 TH0 0; TL0 0; TR0 1; // 开始计时 while (ECHO 1); // 等待Echo变低 TR0 0; // 停止计时 t (TH0 8) | TL0; return t / 58; // 单位cm }t / 58是常用的工程近似12MHz 晶振下 Timer0 每 1us 计一个数声速往返换算后每个计数值约 0.017cm所以 t 除以 58 得到厘米。更精确的写法是t * 17 / 1000但取整除法在 C51 里开销大一些精度要求不高时用 58 就够了。这段代码有一个隐患如果超声波模块没接好或障碍物太远ECHO 可能一直为高程序会卡死在while (ECHO 1)里。实际课设里我一般给等待加超时保护检测到溢出中断就退出。做法是在主循环里开个标志位Timer0 溢出中断里置 1主程序每过一个周期清一次。4.3 循迹和避障的状态机先停车再转向这里给出一段状态跳转核心逻辑#define MODE_TRACK 0 #define MODE_STOP 1 #define MODE_AVOID 2 void main_loop(void) { static unsigned char mode MODE_TRACK; // 默认循迹 if (mode MODE_TRACK) { if (get_distance_cm() 20) mode MODE_STOP; // 进入停车判断 else track_follow(); // 执行循迹动作 } else if (mode MODE_STOP) { motor_stop(); if (get_distance_cm() 20) mode MODE_AVOID; // 确认前方还有障碍 } else if (mode MODE_AVOID) { if (turn_around_obstacle()) // 绕过障碍后回到线 mode MODE_TRACK; } }状态机的核心思想是避障优先级高于循迹。第一次测到障碍小于 20cm 时先停车再测一次做到确认。如果连续两次都在 20cm 以内说明不是偶然抖动才进入避障动作。避障动作本身是一个完整的小车操纵序列后退一点、向左或向右转 90 度、直行越过障碍侧面、再转回来。判断转向方向的常见做法是看哪一侧传感器的读数更大障碍物离左边缘更近就向右转。这个判断不需要角度信息只靠超声波的左右距离差就可以。需要强调get_distance_cm()里面有死等循环一次测距最长可能占用 20ms在这个时间段内车轮不会响应新的决策。所以状态机轮询周期不要设计得太短20ms 级别的控制周期对这个小车来说已经够用。4.4 Echo 引脚电平翻转的检查方法如果小车在避障模式下频繁误转先用万用表量 Echo 引脚空闲电平是不是低。HC-SR04 模块上电后如果 Trig 悬空或初始化顺序错Echo 可能一直保持高电平。常见做法是加一个上电复位流程先给 Trig 一个低电平延时 100ms再进入主循环。这样保证模块完成内部初始化后才开始第一次测距。另外超声波测距本身有 15 度左右的波束角桌腿这类细长障碍物反射能量弱读数会跳变实际课设里两三次测距取最小值比取平均值更接近真实障碍距离。5. 小车验收进阶用串口打印参数标定 PWM 占空比和避障阈值5.1 用串口助手把障碍距离和 PWM 值打印出来STC89C52 的串口 1 可以直接接 USB-TTL 模块波特率设置在 9600 或 4800。初始化代码void uart_init(void) { SCON 0x50; // 模式18位UART允许接收 TMOD 0x0F; // 保留Timer1设置 TMOD | 0x20; // Timer1模式2 TH1 0xFD; // 12MHz晶振9600波特率 TL1 0xFD; TR1 1; }打印格式建议用dist30cm pwm60% dirleft\n这样的文本一行一条记录。观测时让车在黑线上匀速跑上一段看距离数据和 PWM 输出是否符合预期。串口是验证传感器和驱动是否正常的捷径L298N 不转但串口能收到转向指令问题就在驱动板串口收不到距离变化问题就在超声波或接线。用串口把调试信息分离出来比直接改小车代码快得多。5.2 用增量 PID 修正左右轮速差先调 P 再调 I硬件差异导致左右轮实际转速不同车会走弧线。增量 PID 代码int error target_speed - current_speed; int increment kp * (error - last_error) ki * error; pwm_duty increment; last_error error;参数设置建议kp 从 0.1 开始车出现震荡就把 kp 减半。I 项在 P 项稳定后再加入数值从 0.01 起调避免积分饱和导致转向过头。5.3 整车验收清单黑线、障碍物、电池电量三个变量空场地直线测试左右轮同时给 60% PWM看车是否走直。循迹测试在 1m 长的黑线上跑 5 次观察是否脱线。避障测试在线上放一个高度 10cm 以上的障碍物看小车能否在 30cm 以外完成停车和绕行。低压测试在 6.5V 低压下跑一遍确认电机仍有输出、超声波模块仍能正常工作。本文还有配套的精品资源点击获取