STM32F103ZET6智能小车设计:从PWM调速到六种玩法全解析

发布时间:2026/10/21 7:57:48
STM32F103ZET6智能小车设计:从PWM调速到六种玩法全解析
简介这是一份基于STM32F103ZET6单片机的智能小车完整设计文档面向嵌入式、自动化及物联网方向学生适合用作课程设计或毕业设计的参考资料。文档系统讲解了PWM调速、红外循迹、红外避障、超声波避障、障碍物跟随、红外遥控等功能的实现原理与编程方法涵盖定时器输出PWM控制电机转速、红外传感器识别路径与障碍、超声波测距及遥控解码等关键技术并配有硬件电路设计说明、程序流程图和软件代码讲解帮助读者从硬件搭建到代码调试完整体验开发流程。内容还介绍了Keil开发环境与mcuisp下载工具的使用以及实验调试步骤和常见问题处理思路。资源包共1个doc文件大小34.15MB文档共65页、约1.5万字结构清晰既能快速了解整体方案也可按章节深入二次开发。目前已有1357人学习下载是STM32智能车实战中一套高性价比的完整案例。1. 一份能跑的 STM32F103ZET6 智能小车设计文档从 PWM 调速到六种玩法都齐了做课设或者入门嵌入式小车最怕的不是写代码而是拿到一份只有源码没有硬件说明的压缩包焊完板子发现引脚对不上、供电又不足最后跑不起来。这份基于 STM32F103ZET6 单片机的智能小车设计文档65 页、一万五千多字把硬件电路、软件代码、程序流程图全部放在一起覆盖 PWM 调速、红外循迹、红外避障、超声波避障、障碍物跟随、红外遥控六项功能适合正在做课程设计、毕业设计或者想自己从零搭一台小车的从业者。我拆完这份文档的直观感受是它不是一个只能看不能用的 PPT 式论文而是能让你照着原理图接线、对着流程图写状态机、最终把车跑起来的那种资料。2. STM32F103ZET6 的资源盘点与选型逻辑为什么这颗芯片被选中2.1 512K Flash 与 64K RAM对智能小车来说真的够用吗STM32F103ZET6 是完整的 144 引脚封装Cortex-M3 内核最高工作频率 72MHz程序存储器 512K 字节SRAM 64K 字节供电范围 2V 到 3.6V工作温度 -40℃ 到 85℃。对于智能小车这种场景代码通常只有几十 KB主要占空间的是循迹判据、避障状态机、遥控解码表和调试用的字符串常量512K Flash 完全够用64K RAM 也远不会到紧张的程度。更关键的是它有 8 个 16 位定时器其中两个是电机控制定时器PWM 输出通道数量足够覆盖四个直流减速电机。实际做小车时我们常用定时器产生四路 PWM 信号分别控制左右两侧电机的速度。72MHz 主频下定时器预分频和自动重装值可以灵活配置PWM 频率通常在 10kHz 到 20kHz 之间这个区间既能避开 audible noise又能让电机驱动芯片的开关损耗保持在可接受范围。文档里对定时器资源的利用是比较合理的没有出现多个功能抢同一个定时器的情况这是新手容易踩坑的地方。2.2 144 个引脚里实际用到多少IO 分配的艺术STM32F103ZET6 有 112 个多功能双向 I/O 口几乎所有端口都容忍 5V 输入。很多人会觉得 144 脚是大材小用但实际上智能小车外设数量不少四路红外循迹、两路红外避障、一路超声波触发与回波、舵机 PWM、红外遥控接收头、OLED 或 LCD1602 显示、四个电机的方向脚和使能脚粗算下来需要二十多路 GPIO。如果选 48 脚的 STM32F103C8T6IO 数量会非常紧张尤其在还要预留扩展接口的情况下。文档的核心板设计思路是围绕 STM32F103ZET6 搭最小系统把 IO 全部引出到排针再通过驱动板上的接口排母对接。这种做法的好处是模块之间解耦核心板坏了只换核心板驱动板电路问题不影响单片机最小系统。我在做类似项目时也习惯把 GPIO 分配先做成表格再开始画原理图否则焊完线才发现两个功能模块共用了同一个引脚就只能飞线解决。2.3 L293D 电机驱动芯片老方案但仍值得用文档选用的电机驱动芯片是 L293D这是一款比较老的芯片但至今在很多教学项目中仍然常见。L293D 是双全桥驱动器每通道输出电流 600mA峰值 1.2A内部自带续流二极管可以省掉外围的二极管阵列。相比更现代的 TB6612L293D 的缺点是导通压降大、效率低5V 供电时输出到电机的电压会低于电源电压但它的优势是逻辑电源和电机电源可以分开、直插封装方便手工焊接、芯片价格便宜、资料多。对于四路直流减速电机每个电机额定电流通常在几百毫安以内L293D 四通道刚好驱动四个电机每个电机用一路全桥。文档中驱动板包含电源电路、电机驱动电路、功能模块接口这个架构是典型的模块化设计。如果后续要换更大功率电机可以整板替换驱动部分核心板不用动。2.4 供电架构两节 18650 如何变出 5V 和 3.3V 两条电压轨整车的动力源是两节 18650 锂离子电池串联电压约 7.4V。驱动板上有电源电路先产生 5V 给 L293D 逻辑部分和传感器模块使用再通过 AMS1117 线性稳压器降到 3.3V 给 STM32F103ZET6 单片机供电。文档中明确写了核心板从驱动板取 5V然后经 AMS1117 转 3.3V这样核心板本身不需要额外接电池。这里有个需要注意的细节AMS1117 的输入输出压差在 7.4V 降到 3.3V 时接近 4.1V如果整机电流较大线性稳压器的功耗会集中发热所以文档把 5V 作为中间电压轨让大电流负载尽量走 5V3.3V 只供给单片机和小功率传感器。我在实际调试中给电池充满电时电压能到 8.1V 左右AMS1117 输入端最好加一个 100uF 电解电容否则电机启动瞬间的电压跌落容易让单片机复位。3. 硬件电路拆解核心板、驱动板、循迹避障板如何分工协作3.1 核心板最小系统时钟、复位、下载接口一个都不能省文档的核心板设计围绕 STM32F103ZET6 的最基本工作条件展开。最小系统包含电源电路、复位电路、外部晶振电路、程序下载接口、USB 接口、指示 LED 和模式选择接口。外部晶振用的是 8MHz经过内部 PLL 倍频后得到 72MHz 系统时钟这个频率是 F103 系列的上限倍频系数通常设为 9。复位电路是典型的 RC 复位复位引脚通过 10K 电阻上拉到 3.3V并联一个 100nF 电容到地。下载接口用的是 SWD 还是 JTAG文档没有强制指定常用的做法是留出 4 个引脚SWDIO、SWCLK、GND、3.3V加上复位脚共 5 针这样用 ST-Link 下载速度比串口 ISP 快很多也稳定很多。文档里的程序下载软件用的是 mcuisp属于串口 ISP 下载方式这种方案只需要 UART 引脚和 BOOT0 引脚可以配合适合没有 ST-Link 的场景。3.2 驱动板接口逻辑PWM 输入、方向控制脚与电机接线对应关系驱动板是连接核心板与执行机构的中枢。L293D 的每个通道有使能脚和两个输入脚使能脚接 PWM 信号用于调速两个输入脚决定电机正转、反转还是刹车。对于四路电机需要四路 PWM 和八个方向控制脚另外还要注意 GND 必须与单片机共地。以下是我整理的一套典型引脚对应关系与文档中模块化思路一致信号方向说明PWM_LEFT / PWM_RIGHT单片机 → L293D 使能脚调节左右侧电机速度IN1~IN4单片机 → L293D 输入脚控制电机正反转5V / GND驱动板 → 传感器模块红外传感器供电3.3V / GND驱动板 → 核心板单片机电源接线时最容易出问题的是左右侧电机的方向定义。如果左侧电机的 IN1 接高电平是前进右侧电机接高电平却是后退小车一启动就会原地打转。我在调试这种四驱小车时习惯先在代码里写一个短测试程序逐个通道正转一秒、反转一秒确认四个轮子的方向都一致之后再调 PWM 差速。3.3 红外循迹避障板四路循迹与两路避障的检测原理文档中的红外循迹避障板集成了四路红外循迹电路和两路红外避障电路。循迹的基本原理是红外发射管发出的红外光照射到地面黑色轨迹线反射率低白色地面反射率高接收管接收到的反射光强不同经过比较器电路处理后输出高电平或低电平。四路探头通常排成一排放在小车前部中间两路用于对准黑线左右两路用于检测偏移方向。避障电路原理类似只是发射管和接收管朝向前方。当障碍物出现在正前方时红外光被障碍物反射回接收管输出电平变化单片机据此判断前方有阻挡。这里有一个天生的局限红外避障对黑色物体不敏感因为黑色吸收红外光反射率极低所以文档在红外避障之外还设计了超声波避障方案作为补充超声波对颜色不敏感测距更精确。4. 常见问题排查硬件焊接和供电环节的五个典型坑4.1 核心板不上电AMS1117 输出不是 3.3V 而是 5V现象给驱动板接上 18650 电池后测量 AMS1117 输出脚电压等于输入电压大约 7V 多核心板无法工作。原因AMS1117 的 GND 引脚没有形成完整回路或者输入端和输出端的滤波电容漏接导致芯片处于异常状态。另一个常见原因是芯片引脚焊反AMS1117 封装上输入输出相邻方向搞错后输出直接等于输入。解决先用万用表确认封装方向AMS1117 的丝印缺口对应第一脚。再检查输入端对地是否有一个 10uF 以上电容输出端对地是否有 100nF 电容。如果两个电容都在且方向正确断开负载后测量输出端空载电压应为 3.3V。4.2 四轮转速不一致小车直线行驶跑偏现象PWM 占空比设置完全相同的数值但小车前进时明显往一侧偏速度越快偏得越厉害。原因四个直流减速电机的机械特性存在个体差异即使同样电压下转速也不完全相同。另外 L293D 四个通道的导通压降有离散性电池电压在负载波动下也会影响实际输出。解决不能只依赖软件给相同的 PWM 值需要在实际行驶中做校准。我一般会在代码里加一个速度校准参数数组先让小车直线空转观察偏转方向把偏慢一侧的 PWM 占空比逐步上调直到直线行驶稳定。文档虽然没有细讲校准方法但它的模块化结构允许在驱动板接口处更换电机排查很方便。4.3 循迹传感器在强光下失灵白线黑线判断翻转现象室内正常光照下循迹工作正常搬到窗边或者阳光直射区域小车开始乱转传感器输出全部接近同一电平。原因红外循迹模块的红外接收管对自然光中的红外成分敏感强光干扰会淹没反射信号的差异。不同批次的红外对管一致性也有差异阈值电压固定后某些探头在黑白线之间的输出摆幅不够。解决最有效的办法是给传感器加遮光罩让探头贴近地面且避免环境光直射。同时把比较器的阈值调整到黑白线输出的中间值而不是固定用厂家默认电位器位置。文档中的红外循迹电路有四个探头每个探头独立可调调试时用万用表分别测黑线、白线下的输出电压取中间值作为翻转阈值。4.4 电机启动瞬间单片机复位系统重启现象小车静止时一切正常按下启动按钮后还没动起来就复位了反复重启。原因电机启动电流是堵转电流的数倍瞬间从电池抽取大电流导致电源电压跌落。如果供电线路较长、导线细或者电池内阻偏大跌落幅度会更明显。当电压跌到 AMS1117 的 dropout 范围以下时STM32 供电不稳直接复位。解决在电池输出端并联一个大容量电解电容比如 470uF 到 1000uF作为瞬态能量缓冲。同时把电机电源和逻辑电源的布线分开L293D 的电机电源引脚和逻辑电源引脚各走各的路径最后在某一点汇合。文档中驱动板把电源电路和电机驱动分区域布局就是为了减少这种相互干扰。4.5 用 mcuisp 下载程序失败提示无法连接现象第一次使用 mcuisp 串口下载时软件能打开串口但一直提示连接不上芯片重试多次依然失败。原因STM32F103ZET6 的串口 ISP 下载需要 BOOT0 引脚拉高、BOOT1 拉低复位后进入系统存储器模式。很多下载失败案例都是 BOOT0 跳线没有接对或者串口的 TX/RX 没有交叉连接还有是波特率设置过高导致握手失败。解决把 BOOT0 跳线拨到 1保持 BOOT1 为 0手动按一下复位按键让芯片重新进入 ISP 模式。串口线必须交叉单片机 TX 接 USB 转串口的 RX单片机 RX 接 USB 转串口的 TX。mcuisp 里波特率先选 115200连接成功之后再考虑提速。5. 软件实现要点PWM 调速、循迹判据与超声波避障状态机5.1 定时器输出 PWMTIM2 四通道配置与占空比计算文档用到 STM32F103ZET6 的定时器输出多路 PWM这是整个调速功能的基础。常见做法是用 TIM2 的 CH1 到 CH4或者 TIM3 的四通道配合预分频和自动重装寄存器生成 10kHz 到 20kHz 的 PWM 波。PWM 占空比由比较寄存器 CCRx 决定占空比越大电机平均电压越高转速越快。下面是一段典型的 PWM 初始化代码使用 TIM2 四通道输出分别接 L293D 的四个使能脚// PWM_Init: 初始化 TIM2 四路 PWM 输出 void PWM_Init(void) { GPIO_InitTypeDef GPIO_InitStructure; TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; TIM_OCInitTypeDef TIM_OCInitStructure; // 使能 TIM2 和 GPIOA 时钟 RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); // PA0~PA3 复用推挽输出对应 TIM2_CH1~CH4 GPIO_InitStructure.GPIO_Pin GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3; GPIO_InitStructure.GPIO_Mode GPIO_Mode_AF_PP; GPIO_InitStructure.GPIO_Speed GPIO_Speed_50MHz; GPIO_Init(GPIOA, GPIO_InitStructure); // 72MHz 经 72 分频得到 1MHz 计数频率 // 自动重装值 100输出 10kHz PWM TIM_TimeBaseStructure.TIM_Period 100 - 1; TIM_TimeBaseStructure.TIM_Prescaler 72 - 1; TIM_TimeBaseStructure.TIM_ClockDivision 0; TIM_TimeBaseStructure.TIM_CounterMode TIM_CounterMode_Up; TIM_TimeBaseInit(TIM2, TIM_TimeBaseStructure); // 四路 PWM 模式 1占空比由 CCRx 决定初始 50% TIM_OCInitStructure.TIM_OCMode TIM_OCMode_PWM1; TIM_OCInitStructure.TIM_OutputState TIM_OutputState_Enable; TIM_OCInitStructure.TIM_Pulse 50; TIM_OCInitStructure.TIM_OCPolarity TIM_OCPolarity_High; TIM_OC1Init(TIM2, TIM_OCInitStructure); TIM_OC2Init(TIM2, TIM_OCInitStructure); TIM_OC3Init(TIM2, TIM_OCInitStructure); TIM_OC4Init(TIM2, TIM_OCInitStructure); TIM_Cmd(TIM2, ENABLE); }这段代码的逻辑是先把 72MHz 系统时钟做 72 分频定时器计数频率变成 1MHz再让计数器从 0 数到 99每 100 个计数溢出一次所以 PWM 频率是 10kHz。CCR 写入 50对应占空比 50%电机处于半速。如果要把转速提到 80%修改 CCR 为 80 即可。实际使用时方向控制脚 IN1 到 IN4 不能和 PWM 使能脚共用。PWM 只控制通断时间占比方向由另一组 GPIO 决定。文档中的程序流程也按这个思路组织先配置 GPIO 方向脚再初始化 PWM最后进入主循环根据传感器状态修改 CCR 值。5.2 循迹逻辑四路探头读数与转向差速的对应关系循迹功能是智能小车最经典的场景。四路红外探头水平排列中间两路瞄准黑线左右两路负责检测偏移。当所有探头都在白底区域时说明小车已经完全偏离线路需要原地回退或大角度转向。常见判据是中间两路压线时直行左侧探头出线时说明车辆右偏需要加大左轮速度反之亦然。// Track_Follow: 根据四路循迹传感器输出控制左右轮速度 void Track_Follow(uint8_t s1, uint8_t s2, uint8_t s3, uint8_t s4) { // s1~s4 从左到右排列输出 0 表示压黑线1 表示在白底 if (s2 0 s3 0) { // 中间两路压线直行左右轮等速 Set_Speed(BASE_SPEED, BASE_SPEED); } else if (s1 0) { // 最左侧探头压线说明车头右偏左轮减速或右轮加速 Set_Speed(BASE_SPEED - TURN_DELTA, BASE_SPEED TURN_DELTA); } else if (s4 0) { // 最右侧探头压线说明车头左偏右轮减速或左轮加速 Set_Speed(BASE_SPEED TURN_DELTA, BASE_SPEED - TURN_DELTA); } else { // 四路全白丢失黑线原地转圈找回 Set_Speed(-BASE_SPEED, BASE_SPEED); } }这里 Set_Speed 函数内部会根据参数正负决定 IN1 到 IN4 的方向电平再根据传入速度值修改 TIM2 的 CCR1 到 CCR4。TURN_DELTA 是转向修正常量一般取 BASE_SPEED 的 30% 到 50%太小了修正强度不够小车会冲出黑线太大了车辆左右摇摆明显。文档中把这个逻辑画成了程序流程图顺着流程走一遍就很容易理解状态切换条件。循迹调试最直接的方法是先用串口把四路探头数值打出来让小车手动推着走一圈确认每个探头在黑白区域的输出稳定再启用自动循迹。不要直接铺上赛道就跑否则一旦探头阈值不对小车会立刻乱窜而且很难判断是硬件问题还是逻辑问题。5.3 超声波避障带舵机扫描的前方障碍物判断文档中的超声波避障方案把测距模块装在车头通过舵机控制朝向。平时超声波模块正对前方连续测量距离当距离小于安全阈值时小车停车然后舵机向左转 60 度测一次、向右转 60 度测一次比较两侧距离哪边空旷就往哪边转向从而实现绕开障碍物。// Obstacle_Avoid: 超声波测距并决策转向方向 void Obstacle_Avoid(void) { float dist Get_Distance(); // 前方障碍距离, 单位 cm if (dist SAFE_DISTANCE) { Set_Speed(BASE_SPEED, BASE_SPEED); // 安全距离外直行 return; } Set_Speed(0, 0); // 停车 Servo_Set_Angle(60); // 舵机转向左边 delay_ms(300); // 等待舵机到位 float dist_left Get_Distance(); Servo_Set_Angle(-60); // 舵机转向右边 delay_ms(300); float dist_right Get_Distance(); Servo_Set_Angle(0); // 舵机回正 if (dist_left dist_right) { Set_Speed(-BASE_SPEED * 0.5, BASE_SPEED * 0.5); // 向左旋转 } else { Set_Speed(BASE_SPEED * 0.5, -BASE_SPEED * 0.5); // 向右旋转 } }Get_Distance 函数通过依次拉高和拉低 TRIG 引脚产生一个 10us 以上的起始脉冲然后测量 ECHO 引脚高电平持续的时间声速 340m/s时间除以 58 就是厘米距离。舵机控制用的是另一个定时器输出 50Hz 的 PWM脉宽在 0.5ms 到 2.5ms 之间对应 0 度到 180 度。这里要注意舵机相对超声波模块的安装方向转左还是转右必须在车上实测确认不是所有舵机安装方向都一致。超声波避障的红外避障逻辑在文档中也有红外避障更适合近距离检测超声波适合中远距离两者结合可以覆盖更全面。在程序里把红外避障的判断放在超声波之前先用快速响应的红外检测紧急情况再用超声波做精细避障路径规划实用效果更好。6. 串口打印调试法把小车跑稳的最后一道保障调试智能小车时我最常用的工具不是示波器而是串口打印。STM32F103ZET6 有多个 USART 接口随便选一个空闲的串口通过 USB 转 TTL 连接到电脑把传感器原始值、PWM 占空比、当前模式状态循环输出到串口终端就能实时看到小车内部状态变化。// 串口发送一个字符串, 用于调试输出 void Debug_Send(const char *str) { while (*str) { USART_SendData(USART1, *str); while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) RESET); } }这段代码的核心是利用 USART1 的发送数据寄存器状态位逐字节发送字符串。调试时在主循环里每隔 100ms 调用一次打印四路循迹探头的电平、超声波距离、左右轮 PWM 值这样小车出问题的时候看串口数据就能定位是传感器异常还是控制逻辑异常。我的习惯是每次改动硬件接线或者换了新电机都强制走一遍串口校准流程把四路探头阈值重新测一遍把左右轮等速 PWM 值重新配一遍确认数据无误后再跑完整功能。有一次我换了一组新电池电压高了 0.8V循迹阈值全部漂移如果不看串口数据根本想不到是电源问题导致传感器翻转点改变。从那以后我每次启动调试都先开串口助手看 30 秒数据再放车上路这个习惯帮我少走了很多弯路希望帮到你。本文还有配套的精品资源点击获取