☰
STM32与Proteus智能小车仿真:从PWM调速到循迹避障完整实战
2026/10/19 2:41:45 网站建设 项目流程

简介:面向嵌入式初学者的STM32与Proteus联合仿真智能小车项目,以STM32F103C6为主控,结合直流电机与PWM调速,实现前进、后退及转弯控制,可在无实物条件下完成硬件电路搭建与代码验证。压缩包共155个文件,约6.08MB,包含HAL库C源码、头文件、Keil/MDK工程文件、Proteus仿真工程(pdsprj)及编译生成的hex/axf文件,另附IAR工程备份与核心配置文件,便于对照学习工程结构和恢复开发状态。目前已有10231人学习,适合希望掌握STM32 GPIO、定时器PWM、电机驱动及Proteus仿真调试流程的开发者。资源覆盖从初始化代码到电机控制逻辑的完整工程框架,也包含项目配置文件与工作空间备份,可帮助读者快速理解嵌入式小车项目的整体组织方式,并在此基础上扩展循迹、避障等功能,为后续机器人开发打下基础。

1. 需求定位:先用Proteus跑通智能小车,再决定要不要上实物

做智能小车,摆在面前的第一问是直接买套件还是先在Proteus里仿真。我的回答很直接:先仿真。原因不是省钱这么单薄,而是仿真能把调试过程变得可逆——改参数、看波形、重新加载hex,三步就能完成一轮验证;实物一上电,焊死的线、烧掉的引脚、失控的舵机都会让排查成本成倍上升。这套基于STM32及Proteus智能小车仿真方案,本质是把「控制逻辑验证」和「硬件成本投入」解耦,适合正在做嵌入式课程设计、毕业设计的学生,也适合想先验证驱动方案再画板子的开发者。它的核心目标是:在纯软件环境里跑出一辆能循迹、能调速、能复现故障的智能小车模型,并且每一行代码、每一根连线都可以在答辩时讲清楚。

2. 总体架构设计:电机驱动、传感器采集与时钟引脚的模块划分

智能小车仿真最容易犯的错,是一上来就把所有元件拖进画布开始接线。真正常用的做法是先掰开模块,把每个模块的职责、供电关系、信号流向写清楚,再进Proteus动手。这样到了后面排查故障时,才知道该怀疑哪个子电路,而不是把整个工程推倒重来。

2.1 四个模块的职责划分与数据流

按功能可以把整个仿真系统切成四块:主控模块、电机驱动模块、传感器采集模块、人机指示模块。

主控模块以STM32F103C8作为核心,负责产生两路PWM信号、读取两个循迹传感器的电平、执行决策逻辑。电机驱动模块用L298N双H桥芯片,接收主控的PWM和方向信号,驱动左右两个直流电机。传感器采集模块用两个按键模拟实际循迹传感器的数字输出,按下表示检测到黑线,释放表示在白底上。人机指示模块用两只LED显示当前左右传感器状态,方便答辩现场直接看到输入信号是否到达引脚。

数据流是这样的:STM32通过定时器通道向L298N的使能端输出PWM,方向引脚决定电机正反转;传感器电平进入PB0、PB1后,主控根据左右轮偏差调整两路PWM占空比,形成闭环。供电关系上要注意:L298N的逻辑电源与电机电源必须共地,否则仿真里波形正常,实际跑起来就是玄学问题。

模块核心器件Proteus元件名关键信号
主控STM32F103C8STM32F103C8PA0/PA1输出PWM,PB0/PB1采集传感器
电机驱动L298NL298NENA/ENB接PWM,IN1~IN4接方向
电机直流减速电机模型MOTOR-DCOUT1/OUT2、OUT3/OUT4
传感器数字电平输出BUTTON+上拉电阻按下=低电平,模拟压黑线
指示LEDLED-RED传感器状态可视化

2.2 Proteus仿真电路的搭建步骤

画电路图时我习惯按「先主控、再驱动、再采样、最后指示」的顺序放置元件。在Proteus元件库搜索STM32F103C8,放置后先把VDD、VDDA、VBAT接到VCC网络,VSS、VSSA接到GND,这一不做好,后面芯片根本不放正。接着搜索L298N,它的VS接12V电机电源,VSS接5V逻辑电源,两个地都接到同一个GND。

电机接法要注意:左右电机分别接在OUT1/OUT2和OUT3/OUT4上,电机的两端不要并接错位。PWM信号从PA0接到L298N的ENA,PA1接到ENB,方向控制脚PB4~PB7分别接IN1~IN4。占空比通过使能端控制,这是L298N的标准用法:IN1/IN2决定正反转,ENA上的PWM决定平均电压。

传感器部分不用做复杂的光电转换电路。在Proteus里做黑线反射检测很别扭,直接用按键加一个10k上拉电阻到3.3V,按键另一端接地。按下时PB0读到低电平,释放时读到高电平。这样做的好处是逻辑和真实红外循迹模块一致——实际电路里比较器输出的也是数字电平,只不过这里省掉了运放和电位器,答辩时把这个替代逻辑说清楚,导师通常不会刁难。

2.3 引脚选择与时钟配置背后的原因

为什么PWM偏偏选PA0、PA1而不是随意挑两个普通GPIO?因为PA0和PA1分别映射到TIM2的通道1和通道2,一个定时器就能同时产生两路独立PWM,省定时器资源。PA9和PA10不建议用,它们是USART1的默认引脚,仿真里虽然不冲突,但后面如果扩展串口调试就会打架。PB4/PB5默认是JTAG引脚,如果要用它们做方向控制,需要在初始化里关闭JTAG复用,只保留SWD,否则电平会被调试接口拉死。这个坑我在实际工程里踩过,后面会专门写。

时钟配置是另一个容易翻车的地方。STM32F103C8使用内部8MHz RC振荡器时,如果代码按外部晶振初始化,会让所有定时器周期全部跑偏。仿真里没有实体晶振,我就统一走内部时钟HSI,PLL倍频到72MHz。定时器挂在APB1总线上,要注意APB1分频系数为2时,定时器时钟是36MHz的2倍,也就是72MHz。熟练掌握这个倍数关系,PWM频率计算才不用靠猜。

3. 核心实现:PWM调速、L298N控制与循迹决策的代码落地

仿真电路只是骨架,真正让小车动起来的是固件。这一章把代码分成初始化、电机控制接口、循迹决策三部分,每一部分都给出能直接抄进工程的关键片段,并说明参数为什么要这么取。电机转速不是玄学,它就是定时器频率和占空比的数学结果。

3.1 初始化:时钟、GPIO与定时器PWM的参数计算

初始化顺序按时钟→GPIO→定时器展开。先配好72MHz主频,再设置引脚复用模式,最后初始化TIM2输出PWM。PA0和PA1需要配置成复用推挽输出,才能把定时器波形引到引脚上。

void TIM2_PWM_Init(uint16_t arr, uint16_t psc) { /* 打开TIM2和GPIOA的时钟 */ RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); /* PA0/PA1复用为TIM2_CH1/CH2,推挽输出 */ GPIO_InitTypeDef GPIO_InitStructure; 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_TimeBaseInitTypeDef TIM_TimeBaseStructure; TIM_TimeBaseStructure.TIM_Period = arr; /* 自动重装载值 */ TIM_TimeBaseStructure.TIM_Prescaler = psc; /* 预分频值 */ TIM_TimeBaseStructure.TIM_ClockDivision = 0; TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure); TIM_OCInitTypeDef TIM_OCInitStructure; TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM; TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; TIM_OCInitStructure.TIM_Pulse = 0; /* 初始占空比为0 */ TIM_OC1Init(TIM2, &TIM_OCInitStructure); TIM_OC2Init(TIM2, &TIM_OCInitStructure); TIM_Cmd(TIM2, ENABLE); }

调用时这样写:TIM2_PWM_Init(99, 71)。PWM频率 = 72MHz / (71+1) / (99+1) = 10kHz。为什么取10kHz而不是1kHz?L298N内部的开关管和电机模型在这个频率下工作最顺滑,频率太低会听到电机一顿一顿的声音,频率太高仿真步长会导致波形失真。占空比分辨率是1/100,也就是每1%一档,对小车调速足够精细。

3.2 电机控制接口:方向逻辑与占空比更新的拆分

电机控制不能散落在主循环里写,要封装成一个接口,便于上层逻辑调用。方向引脚用PB4~PB7,两两一组控制左右电机正反转。L298N的规则是:IN1高、IN2低时正转,反过来反转,IN1和IN2相同则刹车。

void Motor_Control(uint8_t left_dir, uint8_t left_duty, uint8_t right_dir, uint8_t right_duty) { /* 左电机方向:IN1=PB4, IN2=PB5 */ GPIO_WriteBit(GPIOB, GPIO_Pin_4, left_dir ? Bit_SET : Bit_RESET); GPIO_WriteBit(GPIOB, GPIO_Pin_5, left_dir ? Bit_RESET : Bit_SET); /* 右电机方向:IN3=PB6, IN4=PB7 */ GPIO_WriteBit(GPIOB, GPIO_Pin_6, right_dir ? Bit_SET : Bit_RESET); GPIO_WriteBit(GPIOB, GPIO_Pin_7, right_dir ? Bit_RESET : Bit_SET); /* 占空比写入比较寄存器,范围0~99 */ TIM_SetCompare1(TIM2, left_duty); TIM_SetCompare2(TIM2, right_duty); }

注意left_duty的范围必须小于等于初始化时的arr值,也就是99。如果传入100,实际占空比反而变成0,这在调试时会非常困惑。我通常会在函数入口加一个限幅判断,把传入值钳位到[0, 99]。方向与占空比分开传参的好处是,循迹决策层只需要关心「往哪个方向转、转多少」,不需要知道底层寄存器怎么操作。

3.3 循迹决策:双传感器输入与差速回正策略

循迹逻辑是整车的判断核心。传感器安装在车头左右两侧,黑线在正中时两个传感器都读到高电平,车直行。当左传感器压到黑线,说明车已经向右偏,需要向左修正,具体表现是左轮减速或右轮加速。反过来右传感器压线则向右修正。两轮速度差越大,回正越快,但过大容易摆动,实际值要靠仿真反复试。

void LineFollower_Task(void) { uint8_t left = GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_0); uint8_t right = GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1); if (left == 0 && right == 0) { /* 两个传感器都压黑线,说明进入十字线或停车点,减速慢行 */ Motor_Control(1, 40, 1, 40); } else if (left == 0) { /* 左偏,向左回正:左轮加速,右轮减速 */ Motor_Control(1, 70, 1, 35); } else if (right == 0) { /* 右偏,向右回正:右轮加速,左轮减速 */ Motor_Control(1, 35, 1, 70); } else { /* 全部丢线:停车等待 */ Motor_Control(0, 0, 0, 0); } }

主循环里以20ms为周期调用这个任务函数,相当于50Hz的控制频率。20ms的间隔既能让传感器电平稳定,又不会让小车响应过于迟钝。直行速度取60,转向时一侧提到70一侧降到35,差速大约35%,经过仿真验证转弯半径最小且不甩尾。这些参数不是一拍脑袋定的,是在Proteus里反复跑直线和弯道场景调出来的。答辩时被问到「为什么是35不是50」,可以理直气壮说这是仿真标定结果。

4. 常见问题与排查:固件加载、电机失控等五个翻车点修复

仿真平台和真实硬件的踩坑路径不太一样,但更值得写出来。下面这五个问题是Proteus里最常见的,基本覆盖了从加载固件到运行失控的全过程。每一条都按「现象、原因、解决」的结构记录,也是我当初一步步排查出来的血泪经验。

4.1 STM32芯片加载hex后完全没反应

这是第一个翻车点。现象是点击运行后芯片引脚上没有任何波形,LED不亮,电机不动,仿佛程序根本没烧进去。

原因通常有两个。第一,MDK工程没有勾选生成hex文件,默认只输出axf,Proteus里选了axf文件虽然能加载,但运行行为不稳定。第二,hex文件放在带中文或空格过多的路径下,Proteus解析固件失败。

解决方法是回到MDK的Options for Target界面,在Output页勾选Create HEX File,重新编译后在工程目录的Objects文件夹下找到hex文件。在Proteus里双击STM32F103C8芯片,在Program File一栏选择该hex,确保路径全英文。改完后再运行,用示波器点一下PA0,立刻能看到方波。

4.2 电机不转,但PWM波形正常

现象是示波器上PWM波形漂亮完整,L298N输入引脚也有信号,但两个MOTOR-DC纹丝不动。这时最容易怀疑仿真模型坏了,其实是接线和使能问题。

原因是L298N的ENA、ENB使能脚悬空了。这个芯片的使能端不能靠内部上拉自动开启,悬空时输出级全关断,PWM进多少都白搭。另一个原因是电机供电VS没有真正接12V,或者VS和VSS没有共地。

解决方法是把ENA和ENB分别用导线连接到3.3V或5V,确认VS接12V、VSS接5V,并且两个电源的GND连到同一网络。如果用了PWM调速,ENA就不要接固定高电平,而是接PA0。这一点我在2.2节接线步骤里特别强调过,真出了问题优先查共地。

4.3 电机转速不受控,占空比改大反而停转

现象是让占空比从30改成80,转速反而骤降甚至停转。从波形上看出占空比确实变了,但执行器响应完全反向。

原因是占空比数值超过定时器自动重装载值arr。arr=99时,CCR寄存器如果写入100,PWM输出Pin维持持续高电平,占空比为0%。这是一个隐蔽的逻辑错误,寄存器不会报告异常。

解决方式是在Motor_Control入口做饱和处理,超过99就钳位到99。另一处容易忽略的是TIM_SetCompare1传入的参数类型是uint32_t,如果上层变量是无符号char且被算法计算成0~255范围,需要映射到0~99。我给这个函数加了一个简单的线性缩放:duty = duty * 99 / 255,让调速接口更通用。

4.4 循迹小车左右摆动,像是在画蛇

现象是小车能走,但车头不断左右摇摆,轨迹明显不是直线,而是幅度很大的蛇形线。

原因是差速值过大。循迹决策里转向时一侧70一侧35,差速35%,对急弯有效,但对直线微偏太敏感。传感器一碰到线边缘就猛打方向,越过中线又反向猛打,形成持续振荡。

解决方法是把差速拆成两档。偏离幅度小的时候用缓修正,比如60和45;偏离幅度大的时候才用急修正。双传感器方案其实无法判断偏离大小,所以更实用的办法是降低基础转速,把直行速度从60降到50,同时把转向差速改成25%。仿真里调到这个组合后,蛇形现象基本消失。

4.5 仿真运行几秒后程序复位,LED闪烁

现象是仿真运行得挺流畅,但每隔几秒整个系统就像断电重启一样,GPIO全部回到初始状态。

原因是看门狗没有关闭或喂狗不及时。STM32的IWDG在标准外设库里默认是关闭的,但有些从网上复制的初始化代码会打开看门狗却没有在主循环喂狗。仿真模式下指令执行速度和真实芯片不同,喂狗间隔容易超时触发复位,这才是黑匣子一样的坑。

解决方法是检查SystemInit代码里有没有IWDG相关寄存器操作,如果有,确认主循环每圈调用IWDG_ReloadCounter()。更稳妥的做法是注释掉看门狗初始化代码,仿真阶段根本不需要它。等移植到真实板卡前再按实际情况打开,这是给初学者最大的后悔药。

5. 验收与答辩:从功能测试到演示评分的完整闭环

方案做完了,最后一步是验收。我说过很多次,课程设计和项目答辩的分数差距往往不在功能多少,而在验收过程是否可信。一个能跑但说不清楚为什么这样跑的小车,远不如一个功能列表清晰、测试记录完整的小车得分高。

5.1 验收用例与记录表

建议在答辩前把下面三件事做成一张表格,逐项打勾。第一项测试PWM信号,用Proteus虚拟示波器探针接PA0,确认输出频率约10kHz,且占空比能随TIM_SetCompare1参数变化。第二项测试电机换向,把左电机方向改反,观察MOTOR-DC是否反转。第三项测试循迹决策,依次按下左传感器按键、右传感器按键、双按键,观察电机转速变化是否符合3.3节的决策逻辑。

用例操作预期结果
PWM波形示波器探针接PA010kHz方波,占空比可调
电机转向切换left_dir参数电机反转
循迹修正按下左传感器左轮加速右轮减速
丢线停车释放全部按键电机停止

5.2 演示前强制走一遍的四步检查

每次演示前我都有固定的检查顺序。第一步看hex路径,确认编译时间是最新的,避免演示时加载旧固件。第二步看电源网络,用Proteus的电气规则检查工具扫一遍,确认没有悬空电源引脚本。第三步看传感器按键,逐个按下再释放,确认LED指示对应变化。第四步看示波器,预先切换到PA0通道,让评审看到稳定的PWM波形。这四步走完,演示翻车的概率已经降到很低。

答辩时最常见的追问是「为什么选10kHz」「为什么用L298N不用其他驱动」。回答L298N那题时可以从电流能力切入:它每个通道能驱动2A左右电流,直-flow卡跑得动、余量也够,比一堆分立三极管搭的H桥可靠得多。10kHz的来历用72MHz除以预分频和重装载值现场算给评审看,这比背概念更有说服力。

我说一件自己身上的事:有一次做的某小车Demo在仿真里好好的,一接实物就疯狂复位,查了两天发现是外部晶振型号和代码里HSI配置对不上。从那以后我每次拿到新仿真工程,第一件事不是跑代码,而是先把时钟配置、引脚复用表、电源共地这三项列成清单过一遍,再开始搭电路。希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询