STM32黑线循迹小车实战:L293D驱动与PWM差速控制

发布时间:2026/11/14 5:00:16

STM32黑线循迹小车实战:L293D驱动与PWM差速控制 简介针对STM32F103C8T6智能小车黑线循迹运动实验这是一份可直接编译运行的完整工程源代码适合正在学习STM32嵌入式开发或智能车循迹控制的初学者参考。程序基于Keil4开发处理器为STM32F103C8T6小车端采用L293D电机驱动芯片与TT直流减速电机配合红外循迹模块完成黑线识别与差速纠偏已由作者在自用小车上亲测可用。压缩包共44个文件、约356KB包含8个h头文件、3个c源文件以及hex烧录文件、uvproj工程文件另附txt程序说明文档和L293D电机驱动电路jpg图片工程内SYSTEM、OBJ、USER等目录划分明确既能直接下载烧录也方便在Keil4中二次修改。当前已有1791人学习下载对于正在做智能小车课程设计、电子竞赛备赛或初次接触STM32电机控制的同学来说这份源码能帮助快速跑通黑线循迹功能并理解L293D驱动、TT电机调速与循迹传感器配合的完整实现思路从代码到硬件接线都有对应材料整体循环逻辑清晰也适合在工程基础上继续扩展其他智能功能。1. 黑线循迹小车还是那个熟悉但常翻车的入门项目我拆过几十块STM32F103C8T6最小系统板但每次有人拿STM32F103C8T6智能小车黑线循迹运动实验程序源代码来问我都要先问一句白底黑线还是黑底白线这决定了红外模块的输出极性判断也决定了你后面是改代码还是改硬件。这套源码是STM32标准外设库风格的工程目标芯片STM32F103C8T6驱动芯片L293D电机是TT直流减速电机配合至少两个红外循迹模块做双路检测。开发环境锁定Keil4压缩包里放了SYSTEM、OBJ、USER三个目录、L293D电机驱动电路.jpg和程序说明必看.txt是典型的学校实验室出品但工程可复用性比网上的零散例程高不少。它适合谁刚学会GPIO点灯、想跑通第一个闭环运动控制的人也适合为工创赛智能物流小车练手先把黑线循迹吃透的人。真正决定小车跑得好不好的往往不是代码量而是供电和阈值。2. 硬件选型与接线L293D和红外循迹模块的坑先排干净2.1 引脚分配与GPIO输入模式循迹模块TCRT5000常见数字输出白底输出高电平黑线输出低电平。但有一些套件的模块是反逻辑所以第一步不是连IO而是把模块放白纸上用万用表量输出脚。如果是0V则说明输出低有效后面代码必须反转。原源码用的双路检测通常占用两个普通GPIO我习惯用PB0左、PB1右这样能把PA9/PA10的串口留出来做调试输出。GPIO初始化如下// 双路循迹传感器输入初始化 GPIO_InitTypeDef GPIO_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); GPIO_InitStructure.GPIO_Pin GPIO_Pin_0 | GPIO_Pin_1; GPIO_InitStructure.GPIO_Mode GPIO_Mode_IPU; // 上拉输入适配开漏输出 GPIO_InitStructure.GPIO_Speed GPIO_Speed_50MHz; GPIO_Init(GPIOB, GPIO_InitStructure);这段代码把PB0和PB1配置成上拉输入。GPIO_Mode_IPU之所以重要是因为TCRT5000模块的数字输出通常是三极管开漏结构内部集电极开路必须有外部上拉才能把高电平拉到稳定电位。GPIO_Speed对输入模式其实没有影响但保留它可以让后续把引脚复用成TIM功能时少一次配置遗漏。如果模块输出是推挽结构上拉输入也兼容改成浮空输入也可以但抗干扰差一些。2.2 L293D驱动电路与电源接法L293D是双电源工作的四通道半桥驱动VCC1是逻辑电源接5VVCC2是电机电源接电池正极。源码里如果只给一个电源电机一启动STM32会反复复位这是黑线循迹实验里最常见的问题。实验包的L293D电机驱动电路.jpg里一般画得很清楚VCC2与GND之间要有大电容VCC1和VCC2需要共地并且VCC2的电压不能超过36V实际TT电机建议不超过12V。接线参考表信号L293D引脚STM32引脚左电机PWM使能EN1PB5左电机方向1IN1PB6左电机方向2IN2PB7右电机PWM使能EN2PB8右电机方向1IN3PB9右电机方向2IN4PB10左循迹模块输出OUT_LPB0右循迹模块输出OUT_RPB1注意上面是常用引脚分配实际工程以main.c顶部宏为准。L293D用IN1/IN2的电平组合控制电机正反转EN1用PWM控制速度。TT直流减速电机额定电压一般是6V如果电池是7.4V锂电池PWM占空比建议限制在70%左右否则L293D里的功率管长时间满占空比工作发热会非常快外壳烫手后进入热关断小车突然停摆。提示L293D逻辑电源和电机电源必须共地不共地时PWM电流回路建立不起来电机只会嗡嗡叫不转。实验电路图上通常还会看到VCC2引脚对地的电解电容和VCC1对地的陶瓷电容。这两个电容不能省电解电容吸收电机启停时的反向尖峰电流陶瓷电容滤掉逻辑电源的高频噪声。没有它们STM32在电机急停时容易检测到电源跌落而进入硬件错误中断。2.3 红外阈值标定比代码更关键的环节黑线循迹失败七成原因是模块电位器没调好不是代码问题。把小车放在白色地面模块距地面1到2厘米用一字螺丝刀慢慢旋电位器让指示灯刚好熄灭或刚好点亮然后用黑色胶带挡在模块下方确认指示灯状态反转。这个过程要重复三次每次都从偏移大的方向旋回来消除电位器机械回差。如果模块使用的是可调电位器旋向不一致时最笨但有效的方法是白底调灯亮、黑线调灯灭把检测点卡在黑白交界处。你也可以在代码里加数字滤波同一引脚连续读三次三次一致才认为状态有效。这个消抖对双路循迹很重要因为黑白交界处传感器输出会在几百微秒内跳变直接导致小车沿锯齿路径跑。传感器安装高度也同样关键。距离地面太近模块外壳容易刮到跑道上的不平处电位器产生机械偏移太高黑色胶带的反光不足以触发翻转。我一般用铜柱把模块固定在小车底板离地约1.5厘米固定螺丝用尼龙材质而不是金属因为金属螺丝的反光会把传感器状态拉回高电平造成误判。3. 从库函数到主循环GPIO读入与L293D差速转向3.1 压缩包目录与程序说明文件解压后根目录有SYSTEM、OBJ、USER三个文件夹外加程序说明必看.txt和L293D电机驱动电路.jpg。SYSTEM里是delay、sys、usart三个基础模块提供延时和串口重定向OBJ是编译输出目录默认把生成的.hex放这里USER里的main.c、stm32f10x_it.c才是逻辑主体。先读程序说明.txt里面写了目标芯片、驱动芯片、电池电压等关键参数避免拿51的思路来改STM32代码。这套目录结构里有一个容易被忽略的细节USER工程里会定义USE_STDPERIPH_DRIVER宏Keil的魔术棒 - C/C - Preprocessor Symbols里如果没有这个宏标准外设库的dma、gpio头文件会少一部分映射编译报一堆typedef找不到。打开工程第一件事检查这个宏而不是直接点Rebuild。3.2 电机PWM初始化的参数选择主循环控制速度前要先让定时器输出两路PWM。用TIM2的CH1/CH2初始化代码// 电机PWM初始化1kHz周期1ms RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_AFIO, ENABLE); GPIO_InitStructure.GPIO_Pin GPIO_Pin_0 | GPIO_Pin_1; GPIO_InitStructure.GPIO_Mode GPIO_Mode_AF_PP; GPIO_InitStructure.GPIO_Speed GPIO_Speed_50MHz; GPIO_Init(GPIOA, GPIO_InitStructure); TIM_TimeBaseStructInit(TIM_TimeBaseInitStructure); TIM_TimeBaseInitStructure.TIM_Prescaler 72 - 1; // 72MHz时钟分频到1MHz TIM_TimeBaseInitStructure.TIM_Period 1000 - 1; // 1MHz/1000 1kHz TIM_TimeBaseInit(TIM2, TIM_TimeBaseInitStructure); TIM_OCInitStructure.TIM_OCMode TIM_OCMode_PWM1; TIM_OCInitStructure.TIM_OutputState TIM_OutputState_Enable; TIM_OCInitStructure.TIM_Pulse 0; TIM_OC1Init(TIM2, TIM_OCInitStructure); TIM_OC2Init(TIM2, TIM_OCInitStructure); TIM_Cmd(TIM2, ENABLE);这里预分频必须配置成72-1因为STM32F103C8T6默认系统时钟72MHz。TIM_Period设为1000-1得到1kHz的PWM频率。L293D在这个频率下开关损耗和噪声平衡较好如果降到500Hz电机会更响但发热略低升到10kHz占空比分辨率下降且驱动芯片发热明显。TIM_Pulse初始为0防止上电瞬间电机猛冲等主循环里设置好方向引脚后再给PWM。PWM频率的选择还要看定时器的分辨率TIM_Period 1000时占空比从0到999步进足够做60%-70%的精细调节。如果要用PID建议把Period提到5000这样每次调节的最小步进是0.02%避免PID输出震荡时电机一顿一顿的。3.3 双路循迹主循环差速转向状态表循迹核心在主循环里做状态判断。这段逻辑用标准外设库读引脚// 主循环读传感器并计算左右PWM while (1) { uint8_t left GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_0); uint8_t right GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1); if (left 0 right 0) // 两传感器都在黑线上 { TIM_SetCompare1(TIM2, 600); // 直行 TIM_SetCompare2(TIM2, 600); drive_forward(); } else if (left 0 right 1) // 左传感器压线车偏右 { TIM_SetCompare1(TIM2, 300); // 左轮降速 TIM_SetCompare2(TIM2, 650); drive_forward(); } else if (left 1 right 0) // 右传感器压线车偏左 { TIM_SetCompare1(TIM2, 650); TIM_SetCompare2(TIM2, 300); drive_forward(); } else // 两传感器都在白底丢了线 { TIM_SetCompare1(TIM2, 350); TIM_SetCompare2(TIM2, 350); spin_in_place(); // 原地旋转找线 } }这段逻辑里最容易被忽略的是“两传感器都压线”的问题窄黑线不可能同时压住两个传感器所以直行状态实际应该对应左右都白。但这又和丢线状态冲突。双路模块的标准做法是左右都白时短暂维持上一次转向方向或者原地旋转。上面代码选择原地旋转适合90°直角弯如果跑大弯建议改成“左转优先”来回扫线。传感器状态与动作对应如下左PB0右PB1小车状态左PWM右PWM00黑白交界纠偏60060001偏右左转30065010偏左右转65030011直线丢失旋转找线350350注意1和0的极性取决于模块是否反逻辑。如果模块黑线输出高就在宏里把LINE_ACTIVE_LEVEL改成1主循环判断改成 LINE_ACTIVE_LEVEL不要逐行改if。这样的宏定义带来的好处是换一套小车底盘时只需要改引脚映射和有效电平不用碰控制算法。还要注意上文drive_forward和spin_in_place两个函数源码里一般会封装成L293D的IN引脚组合。方向引脚和PWM比较值需要分开设置因为PWM更新后方向动作立即生效如果两个轮子方向不一致小车会原地打转而非转向。4. 实战调试Keil4工程编译与循迹抖动参数修正4.1 Keil4工程打开与烧录设置用Keil uVision4打开USER文件夹下的.uvproj工程文件。打开后第一次编译一般会报找不到STLink驱动只有这个原因。在Options for Target - Device确认芯片是STM32F103C8T6Debug里选择ST-Link或J-LinkUtilities里勾选算法文件。如果Keil4装过标准外设库直接编译会生成OBJ下的hex。命令行方式下载也可以STM32_Programmer_CLI -c portSWD modeNRST STM32_Programmer_CLI -w OBJ\\line_follow.hex -v -rst说明这里-c portSWD表示使用SWD四线下载modeNRST是常见复位时序-w写入hex并校验-v对比写入内容-rst下载后复位运行。如果下载器找不到芯片先查BOOT0是不是接地再查L293D逻辑电源是否和STM32共地。Keil4在Windows 10及以上偶尔会有工程文件关联异常。如果双击.uvproj没反应用管理员身份运行Keil再打开如果编译报“Unknown type name u8”这类错误多半是SYSTEM目录的头文件没有加进Include Paths。C8T6是64KB Flash编译出现ROM不足时把调试器里的Download Function勾掉一部分或者把优化级别从Level 0调到Level 2立即能省下不少空间。4.2 画龙、甩尾、冲出赛道的调参表小车在弯道和直道的动态不同不能靠一个占空比调完。建议按照下表顺序操作现象最先检查参数参考直道画龙左右电机空载转速差快侧PWM减50弯道冲出转向差速不够大650/300改成700/250原地旋转太猛丢线PWM过高350降到280电机啸叫PWM频率不合适1kHz降到500Hz电池没电后无力电池电压下降全速阈值升到700调参时只动一个参数改一次跑一圈不要同时动转向差速和直行速度。差速越大转向角速度越大但过大的差速会让车尾甩起来尤其在低速高摩擦地面。直行速度设定在60%左右比较合适也就是占空比600/1000这样给小电池电压波动留了余量。判断左右电机是否一致最简单的办法是把两个轮子悬空调用一个固定PWM输出函数看两轮转速是否一样。快的那一侧在程序里把PWM调低10%。如果两边转速差异超过15%更换电机因为差速控制算法默认两电机特性一致差异大时黑线永远走不直。4.3 用串口输出传感器状态SYSTEM里的usart模块已经把printf重定向好主循环里加一条printf(L:%d R:%d PWM1:%d PWM2:%d\r\n, (int)left, (int)right, (int)last_pwm1, (int)last_pwm2);注意printf重定向到串口需要勾选Keil的Use MicroLIB否则浮点支持会占大量FlashC8T6只有64KB很容易超。调试时串口波特率通常设置为115200PA9发送、PA10接收。串口输出的意义在于你可以拿着小车贴着黑线缓慢推过赛道看日志里的left/right状态变化是否符合预期。如果状态序列是01 - 11 - 10说明传感器间距太大需要把两个模块往中间收一点。最后推荐加一个弯道减速的细节。检测到左或右传感器压线后不要直接把PWM从600切到300否则车尾会因为惯性甩出去。常见做法是做一个斜坡函数// 弯道减速斜坡每次主循环降低50最小不过200 if (turning_speed target_speed) { turning_speed - 50; TIM_SetCompare1(TIM2, turning_speed); }逻辑说明这个斜坡让电机从600降到300的过程有4~5个控制周期每周期约10ms车体有足够时间把重心移到前轮而不是瞬间甩尾。参数50和200要根据实际跑道摩擦系数调整太慢则进弯前不够减速太快等于瞬间切换。5. 进阶扩展把双路循迹升级成灰度检测与FreeRTOS任务化5.1 灰度传感器的动态阈值改造双路数字量只输出0/1在阳光直射下阈值点飘得厉害。常见做法是换成四路灰度模块把模拟输出接到PA0~PA3用ADC规则组采集。上电时做一次自动校准记录白底和黑线的基准值再取中间值作为判断阈值// 灰度传感器阈值校准白黑基准取中值 uint16_t white_ref[4], black_ref[4], threshold[4]; for (int i 0; i 4; i) { threshold[i] (white_ref[i] black_ref[i]) / 2; } // 运行中读取灰度值并判断是否压线 if (adc_value[i] threshold[i]) { line_state | (1 i); }逻辑说明灰度值越黑数值越小所以小于阈值认为是黑线。校准最好在赛道现场进行一次白底基准取小车正常跑动时采集到的最小值防止地砖纹理或阴影导致白底值波动。四路灰度比双路多的不只是状态而是可以通过左右两路灰度值的差计算小车中心与黑线的横向偏移这个偏移量就是PID控制器的误差输入。5.2 把裸机循环改成FreeRTOS任务如果你要把这个源码扩展成工创赛智能物流小车裸机while循环很快会被超声波、摄像头、电机闭环占满。FreeRTOS移植后建议拆两个任务// 5ms周期采样传感器10ms周期控制电机 void vSensorTask(void *arg) { for (;;) { read_line_sensors(); vTaskDelay(5); } } void vMotionTask(void *arg) { for (;;) { update_line_follow_pwm(); vTaskDelay(10); } }任务间通过全局结构体或者队列传递line_state不要在中断里直接修改PWM比较值。运动任务优先执行传感器任务可以被打断。栈空间上运动任务因为调用printf要分配256 words以上否则调用完第一次printf任务栈就溢出HardFault_Handler入口会被踩进去。提示FreeRTOS的SysTick会抢占业务代码是正常的但PWM定时器中断优先级不能低于FreeRTOS可管理的最低优先级否则占空比更新会被SysTick延迟出现电机抽动。5.3 一层感知一层运动摄像头循迹复用差速模型黑线循迹、电磁三轮、摄像头循迹区别只在上层感知下层的L293D差速控制完全可以复用。摄像头方案里把vSensorTask替换成摄像头处理任务输出一个left_offset值再映射到左右PWM差其余逻辑不需要动。这也是拆这套源码最大的价值先在一个简单的双路循迹上验证差速模型再往复杂感知方向迁移不用重写电机驱动。如果你还有时间可以把灰度模块的四路状态归一化成一个-100到100的偏差值然后套一个位置式PID。P项负责回正I项负责消除长时间跑偏的稳态误差D项抑制过弯时的抖动。PID参数从P30开始调I和D先置0等P项让小车不冲出赛道后再慢慢加D到15左右最后加I。这个调法在大多数TT电机小车上都能稳定跑。本文还有配套的精品资源点击获取
延伸阅读

更多相关文章

2026/11/14 5:00:16

PyTorch转TensorRT实战:torch2trt原理、踩坑与生产环境选型指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/11/12 14:24:35

Oracle 19c RAC安装卡在SSH互信?临时替换scp解决INS-06006

先交代一下环境:Oracle Linux 8.3,两个节点,装的是 Oracle 19c RAC,Grid 和 Database 都是 19c。前面所有环境准备都做完了,结果在 OUI(Oracle Universal Installer)的 SSH Connectivity 这一步…

2026/11/8 18:04:53

CNN矩阵分解协同过滤:内容特征融合的电影推荐系统

简介:面向电影推荐场景的算法学习与毕设项目资料包,围绕“CNN矩阵分解协同过滤”构建完整推荐流程,适合人工智能、电子信息、计算机等相关专业学生用于课程设计、毕业设计或项目初期演示。压缩包共28个文件、约7.4MB,包含6个Pytho…

2026/11/14 4:57:14

沈阳特种电工作业体检,上岗必备别等考不了才后悔

沈阳特种电工作业体检,上岗必备别等考不了才后悔 你是不是也担心学历不高,沈阳特种电工作业体检过不了,连报名资格都没有?别急,这篇文章告诉你,只要准备到位,学历不是限制你拿证的门槛, 上岗必备 的特种作业操作证,照样能拿到手。…

2026/11/14 4:57:14

大港区考电工证费用多少钱?外省证能转吗?实操干货全在这里

大港区考电工证费用多少钱?外省证能转吗?实操干货全在这里 之前考的证在外省能不能转过来?这几乎是每个想考电工证的人都会问的问题,尤其是像你在郑州金水区工作的建筑工人,换个城市、换个工地,证件的“迁移”问题就显得尤为重要。现在我们来聊一聊…

2026/11/14 4:57:14

扶风电工证在哪考怎么报名 证书过期了不知道怎么复审延期

扶风电工证在哪考怎么报名 证书过期了不知道怎么复审延期 证书过期了不知道怎么复审延期?你不是一个人!很多人拿到扶风电工证后,往往忽略了它的有效期和复审流程,等到要用的时候才发现证件已经失效,严重影响工作和职业发展。其实, 扶风电工证…

2026/11/14 4:57:14

航天城考电工证保姆级教程:外省证书能转吗?别再踩坑了

航天城考电工证保姆级教程:外省证书能转吗?别再踩坑了 之前考的证在外省能不能转过来?这个问题困扰了太多人,尤其是像你这样已经在南阳从事公路工程的电工,可能因为之前在别的地方考的证,现在到了航天城,不知道能不能继续用,或者怎么转过来。别急,这…

2026/11/14 4:57:14

无锡哪里可以审电工证值不值得考?避开这些坑才能稳过

无锡哪里可以审电工证值不值得考?避开这些坑才能稳过 怕考不过白交培训费?你不是一个人。很多在无锡准备报考电工证的学员,都担心自己花了几千块培训费,最后还是没通过考试。但你有没有想过, 值不值得考 这个问题,其实比你想象中更重要。…

2026/11/14 4:52:14

溆浦考电工证在哪里考最新政策解读

溪浦考电工证在哪里考 内部消息告诉你怎么搞定学历不高怕报不上名 你是不是也担心自己学历不高,考不了电工证?别急,我跟你说,这事儿根本不是事儿。 内部消息…

2026/11/13 22:33:32

电工证丢失还能补吗?千万别踩坑,丢了证等于失业

电工证丢失还能补吗?千万别踩坑,丢了证等于失业 你正在找工作,但手里的电工证突然丢了,这下怎么办?别急,先别慌, 电工证丢失还能补吗 ?答案是肯定的,但 千万别踩坑 ,补办流程如果不了解,可能会耽误你上岗、甚至影响到你的收入。…

2026/11/14 0:06:39

土木特种工考证官方报名入口怎么找 之前考的证在外省能不能转过来

土木特种工考证官方报名入口怎么找 之前考的证在外省能不能转过来 之前考的证在外省能不能转过来?这个问题我太熟悉了,作为一名干了十几年的特种作业证书办理从业者,每年都会遇到不少像你一样在外地考了证,回来却发现无法直接使用的学员。别急,今天我就…

2026/11/14 0:06:39

电工证要多久多钱保姆级教程:工地太忙根本没时间复习考试

电工证要多久多钱保姆级教程:工地太忙根本没时间复习考试 工地太忙根本没时间复习考试,你不是一个人。在信阳浉河区,很多刚进入工地的电工都面临这样的困境:工作强度大,时间碎片化,根本没有系统复习考试的机会。但电工证是上岗的硬门槛,…

2026/11/13 22:32:11

面向复杂光照与角度的宠物面部识别鲁棒性增强策略

在智慧养宠时代,基于视觉的宠物身份识别技术正逐步从实验室走向家庭应用。然而,与配合度高的人类不同,宠物是典型的“非合作生物识别对象”——它们不会主动配合摄像头调整姿态,更无法控制光照环境。实际应用中,我们常…

2026/11/13 12:14:39

SSM电子书店源码全解析:结构、原理、部署与排错实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
☎咨询二维码 ☎ ↑