项目框图
ST-link接线
实物图:
正面:
反面:
相关内容
使用L9110S电机模块
电机驱动模块L9110S详解 | 良许嵌入式
一、让小车动起来
新建文件夹智能小车项目
在里面复制19-串口打印功能
重命名为01-让小车动起来
新建文件夹motor,如下图所示
打开项目
加载文件
先更改引脚
然后分装函数
前进,后退,左转,右转,停止函数
PB0、PB1引脚控制的是左轮:
当PB0引脚为高电平,PB1引脚为低电平的时候,左轮向前走
当PB0引脚为低电平,PB1引脚为高电平的时候,左轮向后走
当PB0引脚与PB1引脚电平相同的时候,左轮停止
PB2、PB10引脚控制的是右轮
当PB2引脚为高电平,PB10引脚为低电平的时候,右轮向前走
当PB2引脚为低电平,PB10引脚为高电平的时候,右轮向后走
当PB2引脚与PB10引脚电平相同的时候,右轮停止
#include "motor.h"//初始化电机函数
void motor_init(void)
{GPIO_InitTypeDef gpio_initstruct;//打开时钟__HAL_RCC_GPIOB_CLK_ENABLE(); // 使能GPIOB时钟//调用GPIO初始化函数gpio_initstruct.Pin = GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_10; // 两个LED对应的引脚gpio_initstruct.Mode = GPIO_MODE_OUTPUT_PP; // 推挽输出gpio_initstruct.Pull = GPIO_PULLUP; // 上拉gpio_initstruct.Speed = GPIO_SPEED_FREQ_HIGH; // 高速HAL_GPIO_Init(GPIOB, &gpio_initstruct);motor_stop();
}
//前进
void motor_go_forward(void)
{//左轮HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_SET);HAL_GPIO_WritePin(GPIOB, GPIO_PIN_1, GPIO_PIN_RESET);//右轮HAL_GPIO_WritePin(GPIOB, GPIO_PIN_2, GPIO_PIN_SET);HAL_GPIO_WritePin(GPIOB, GPIO_PIN_10, GPIO_PIN_RESET);
}
//后退
void motor_go_backward(void)
{//左轮HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_RESET);HAL_GPIO_WritePin(GPIOB, GPIO_PIN_1, GPIO_PIN_SET);//右轮HAL_GPIO_WritePin(GPIOB, GPIO_PIN_2, GPIO_PIN_RESET);HAL_GPIO_WritePin(GPIOB, GPIO_PIN_10, GPIO_PIN_SET);
}
//左转
void motor_go_left(void)
{//左轮HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_RESET);//想要一个轮子不动,就让它两个引脚都等于reset或者setHAL_GPIO_WritePin(GPIOB, GPIO_PIN_1, GPIO_PIN_RESET);//右轮HAL_GPIO_WritePin(GPIOB, GPIO_PIN_2, GPIO_PIN_SET);HAL_GPIO_WritePin(GPIOB, GPIO_PIN_10, GPIO_PIN_RESET);
}
//右转
void motor_go_right(void)
{//左轮HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_SET);HAL_GPIO_WritePin(GPIOB, GPIO_PIN_1, GPIO_PIN_RESET);//右轮HAL_GPIO_WritePin(GPIOB, GPIO_PIN_2, GPIO_PIN_RESET);HAL_GPIO_WritePin(GPIOB, GPIO_PIN_10, GPIO_PIN_RESET);
}
//停止
void motor_stop(void)
{//左轮HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_RESET);HAL_GPIO_WritePin(GPIOB, GPIO_PIN_1, GPIO_PIN_RESET); //右轮HAL_GPIO_WritePin(GPIOB, GPIO_PIN_2, GPIO_PIN_RESET);HAL_GPIO_WritePin(GPIOB, GPIO_PIN_10, GPIO_PIN_RESET);
}
二、串口控制小车
注意:串口控制小车与蓝牙控制小车类似
想要实现的效果是:
M1:前进
M2:后退
M3:左转
M4:右转
其他M:停止
复制01-让小车动起来
重命名为02-串口控制小车
将蓝牙模块的文件粘贴到改项目中进行修改
打开项目文件
加载文件
将蓝牙模块的串口2代码粘贴到02项目中
将接收到的串口2的数据打印到串口1中
编译
调试代码
烧录
记得把串口2连接上,用两个串口助手显示结果
串口助手1->连接串口1->用于接收串口2发送过来的数据->波特率是115200
串口助手2->连接串口2->用于发送数据(M1\M2\M3\M4\其他数据)->波特率是9600
注意:在烧录完代码之后,一切准备就绪,发现在串口2中发送输入,串口1接收不到,这时候不要慌,先把两个串口助手断开,然后再次选择串口号,连接串口,这时候在输入数据,就会有反应了。
注意:如果你的电脑中usb接口不足,板子选择使用面包板接电就会出现一个小错误,面包板中只有一个3V3在供电,这个时候,使用杜邦线给32板子接电,就会出现在串口2中输入M1前进之后再输入M2后退,小车会停下来,串口助手1中会显示hello word。如下图所示:
再次输入M2小车会后退,这是因为供电不足,电压不稳,所以想要达到输入M1就是前进,输入M2就是后退,解决办法:需要使用USB外接电源供电。
三、蓝牙控制小车
将上述的串口控制小车中的串口2所接的引脚替换成蓝牙模块
然后手机连接蓝牙
输入数据,小车就会行动
要注意蓝牙所设置的波特率,不然不能正确接收数据
蓝牙当初所设置的波特率是115200,所以要在主函数中调用蓝牙初始化函数时更改波特率
四、点动控制小车
当摁下前进按键的时候,小车就会疯狂的向前走,那么现在要实现,当摁一下前进,小车就前进一下,实现点动控制小车。
复制项目文件02,重命名为,点动控制小车
打开项目
只添加这两行代码即可
剩下的在软件上完成
原理:
在主函数的while函数中,有一个停止的函数,小车最开始的状态就是停止状态,想让小车开始移动,就要打断这个while循环,所以就要用到中断,所以点动的代码要在中断函数中写,所以在中断回调函数中,有让其移动的代码,将这个代码延时一下,比如10ms。那么程序在执行完移动代码之后就会执行这个延时函数,然后再回到while循环中执行停止小车的函数,所以就实现了,10ms的点动,而,需要长时间的移动小车,就要在软件中找补了。
在串口助手中
(把蓝牙摘下去换上串口2,记得串口2的波特率已经改为115200了)
串口助手2可以使用数据流循环发送,设置好发送的间隔,就可以实现长时间的移动。
设置的时间太长,没办法将小车动作连续起来
时间太短,小车跑的太快
其实这个整套下来,像是将片段的小车移动整合起来变成一个连续的小车移动
将小车独立出来的接线:
现在小车的串口2连接蓝牙模块
ST-LINK和串口1可以在烧录完代码之后拔掉
32的5V电压和GND可以连接面包板的正负极
面包板的电压计算:四块1.5V的电压=1.5*4=6V
五、PWM调速控制小车
小车接收到不同的电压值,跑的速度也是不一样的
电压越大,速度越快(并不是电压越大越好,小心烧掉板子)
为了避免小车一开始速度就很快可以使用PWM调速
小车的电机与32板子连接的电压是3.3V,所以小车全速前进的时候电压是3.3V,如果把电压降低至2V小车的速度就会降下来,1.5V更慢。
复制01项目,重命名为04-PWM调速控制小车
复制呼吸灯实验中BSP文件中的pwm文件夹
粘贴到04-PWM调速控制小车的BSP文件中
打开项目,加载文件
记得加载HAL库中的定时器文件tim.c,tim_er.c
编译无错
修改pwm文件
将这个的TIM4修改为TIM2,TIM2的两个通道在这里用到
因为TIM4的两个通道GPIO口可能会占用到
这里就要注意,做项目前要对资源进行合理的分配,这很重要
这里需要更改电机的引脚
想要达成每个电机使用不同的通道
这里不用看PB引脚的通道,因为左轮用一个通道,右轮用一个通道,所以PB引脚的通道并不重要,不需要关注。
这里注意,通用定时器
代码修改如下:
这里注意,原来我pwm_init函数中的参数是psc在前,arr在后,然后主函数中声明pwm_init();时写的参数20-1,72-1,这里传参的顺序错了,所以在这里pwm文件中调换了psc和arr的顺序。
复习CCR是什么?
主函数代码如下:
产生的疑问?
STM33F103C8T6智能小车项目中使用循迹+PWM调速小车时,小车电机实际接线的引脚是PA0接A-1A、PB1接A-1B、PA1接B-1A、PB10接B-1B和PWM定义的接口引脚是PA0和PA1,但是在小车电机文件中定义的引脚是PB0接A-1A、PB1接A-1B、PB2 接B-1A、PB10接B-1B,这时候使用pwm调节CRR的值可以控制小车车速,但是为什么项目所定义的电机文件中的引脚和小车实际接线的引脚不一致的情况下,小车还可以动?
DeepSeek回答
关于STM32F103C8T6智能小车项目中,电机实际接线与代码中定义的引脚不一致,但小车仍然可以动的原因。我需要仔细分析这个问题,并给出合理的解释。
首先,用户提到实际接线是PA0接A-1A,PB1接A-1B,PA1接B-1A,PB10接B-1B。而代码中定义的引脚是PB0接A-1A,PB1接A-1B,PB2接B-1A,PB10接B-1B。但使用PWM调节CCR(应该是PWM的捕获/比较寄存器)来控制车速,小车却能动。这看起来矛盾,因为引脚定义不一致,按理说电机可能不会正常工作,或者完全不动。
我需要考虑几个可能性:
硬件接线错误,但代码中错误地配置了其他引脚:比如,虽然代码定义的是PB0,但实际可能错误地配置了PA0,导致两者巧合地对应。这可能是因为代码中的初始化部分错误地将PB0配置为PA0,或者存在其他配置问题。
复用功能或引脚映射:STM32的某些引脚可能有复用功能,或者通过重映射功能改变了实际控制的引脚。例如,定时器的通道可能被映射到不同的引脚,导致代码中定义的PB0实际上输出到了PA0。
代码中的引脚配置与实际不符,但部分引脚正确:例如,虽然A-1A和B-1A的引脚定义错误,但A-1B和B-1B的PB1和PB10正确,这样可能有一半的电机驱动正确,而另一半可能被其他因素(如共地或短路)导致电机仍能转动,但表现异常。
PWM信号通过其他方式传递:可能存在飞线或电路板上的短路,导致PWM信号从代码定义的引脚传输到了实际接线的引脚。
接下来,我需要进一步分析这些可能性。首先检查代码中的GPIO初始化部分,查看是否正确配置了引脚。例如,如果代码中错误地将PB0配置为PWM输出,但实际硬件连接到了PA0,但PA0可能被配置为其他功能(如普通GPIO输出),这时候如果PA0被设置为高电平,可能电机一直全速运行,而PWM调节无效。但用户提到可以通过调节CCR控制车速,这说明PWM可能确实在起作用。
另一种可能是,虽然代码中定义的引脚是PB0、PB2,但实际使用的定时器通道对应的引脚是PA0和PA1。例如,定时器2的通道1和2可能默认映射到PA0和PA1,而代码中错误地配置了PB0和PB2,但由于没有重映射,实际输出仍然在PA0和PA1。这种情况下,即使代码中试图配置PB0和PB2为PWM,但由于这些引脚对应的定时器通道不同,可能无法输出,但用户的情况却可以工作,这似乎矛盾。
或者,用户可能使用了不同的定时器,例如,PA0和PA1属于定时器2,而PB0、PB1属于定时器3,但代码中错误地配置了定时器3到PB0,但实际硬件连接到了定时器2的PA0,这时候如果代码中错误地配置了定时器3,但实际没有启用,而PA0可能被其他代码(如直接GPIO控制)驱动,这可能无法解释PWM调速的有效性。
可能需要考虑代码中的具体配置,比如使用的定时器和通道,以及引脚重映射情况。例如,STM32的某些定时器通道可以重映射到其他引脚,如果代码中启用了重映射,那么实际输出引脚会改变。例如,定时器3的通道1默认是PA6,但部分重映射后可能是PB4,完全重映射可能是PC6。如果用户代码中配置了重映射,但实际硬件连接到了默认引脚,或者相反,可能导致引脚不一致但信号仍然输出。
另外,用户提到调节CCR可以控制车速,这说明PWM信号确实在正确的引脚上输出。因此,可能的情况是,虽然代码中定义的引脚名称和实际接线不一致,但实际配置的定时器通道对应的物理引脚是正确的。例如,代码中定义PB0,但实际配置的是定时器3通道1,而该通道的引脚可能是PB0,但硬件连接到了PA0,这似乎矛盾,除非存在硬件错误或飞线。
或者,用户可能混淆了引脚的定义,例如在代码中错误地命名了引脚,但实际配置的是正确的引脚。比如,代码中将电机A的PWM引脚定义为PB0,但在初始化时实际配置的是PA0,这可能是代码中的注释或变量名错误,而实际代码正确。
另一个可能性是,电机驱动模块的使能端被固定启用,例如ENA和ENB被接高电平,此时电机的方向由IN1和IN2控制,而PWM信号实际上控制的是速度,但若方向引脚配置正确,即使PWM引脚错误地输出高电平,电机也会全速运转。但用户提到可以调节速度,所以PWM必须有效。
综合来看,最可能的解释是代码中实际配置的引脚与变量名或注释中的定义不一致,但初始化部分正确配置了实际接线的PA0和PA1。例如,虽然在代码的注释或变量名中写的是PB0和PB2,但初始化函数中正确地设置了PA0和PA1作为PWM输出。这可能是一个代码书写错误,但实际配置正确,导致电机正常运行。
或者,可能存在引脚复用的情况,例如PA0和PB0都被配置为同一个定时器的不同通道,但这种情况在STM32中不太可能,除非使用了不同的定时器。
需要进一步考虑STM32F103的定时器通道和引脚映射。例如,查看PA0和PB0分别属于哪个定时器的哪个通道。PA0对应定时器2的通道1,而PB0可能属于定时器3的通道3,或者其他定时器。如果代码中配置了定时器2的通道1,那么无论变量名如何,实际输出会在PA0,而如果变量名错误地写成了PB0,但初始化代码正确,那么电机就能正常工作。
因此,问题可能出在代码中的引脚定义变量名与实际初始化时使用的引脚不一致,但初始化部分正确配置了实际引脚。例如:
在代码中可能有:
#define MOTOR_A_PWM_PIN PB0
#define MOTOR_B_PWM_PIN PB2
但在初始化时,错误地配置了PA0和PA1:
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0; // 实际是PA0
或者使用了正确的定时器配置,例如Timer2的通道1和2,对应的PA0和PA1,而变量名错误地写成了PB0和PB2。
因此,尽管代码中的定义和实际接线不一致,但由于初始化部分正确配置了PA0和PA1,所以PWM信号正确输出,电机能够运行,并且通过调节CCR(即定时器的捕获/比较寄存器)改变占空比,从而控制速度。
总结起来,可能的原因是代码中引脚定义的变量名或注释错误,但实际的硬件初始化和定时器配置正确,导致虽然文档或变量名不一致,但实际信号输出正确,因此小车可以正常运行。
📅 时效性分析:用户问题不依赖时效性数据,但为确保准确性,当前分析基于 2025年02月25日 的技术背景。
🚗 问题核心
在引脚定义与实际接线不一致的情况下,小车仍能运行的根本原因在于 代码与硬件实际信号流的匹配性。以下是关键分析:
🔧 原因解析
1️⃣ PWM信号实际生效的引脚与代码定义不同
可能场景:
代码中虽然名义上定义引脚为 PB0 和 PB2,但底层驱动(如定时器配置)可能错误/隐式地指向了 PA0 和 PA1。
示例:
c
Copy Code
// 代码中名义定义引脚为 PB0(错误)
#define MOTOR_A_PWM_PIN PB0
// 实际初始化时误配置为 PA0(正确)
TIM_OC1Init(TIM2, &pwm_config); // TIM2_CH1 默认映射到 PA0
结果:PWM信号通过 PA0/PA1 输出,与硬件接线一致,因此可调速。
2️⃣ 部分引脚功能重合或短路
可能场景:
若 PB0 与 PA0 在硬件上被短接(如PCB设计错误或飞线),代码对 PB0 的操作会实际影响 PA0。
部分电机驱动模块(如L298N)的使能端(ENA/ENB)直接接高电平,此时方向引脚(如A-1A/A-1B)的PWM占空比仍可调速。
3️⃣ 代码未严格绑定引脚定义
可能场景:
电机驱动代码通过 定时器通道(如 TIM2_CH1)控制PWM,而非直接依赖引脚名。
示例:
c
Copy Code
// 实际控制PA0的是TIM2_CH1,无论引脚名如何定义
HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_1); // PA0输出PWM
📊 验证方法
示波器/逻辑分析仪检测:
检查 PA0 和 PB0 的PWM波形,确认实际生效的引脚。
代码调试:
跟踪PWM初始化代码,验证定时器通道与引脚的映射关系(参考《STM32F103参考手册》的定时器复用功能表)。
硬件排查:
检查是否存在 PB0 与 PA0、PB2 与 PA1 的意外短路。
🛠️ 解决方案建议
统一代码与硬件定义:
修改代码中的引脚定义,使其与实际接线一致(如将 PB0 改为 PA0)。
明确定时器通道映射:
在代码中显式配置引脚重映射(如需),避免隐式依赖默认映射。
示例:
c
Copy Code
GPIO_PinRemapConfig(GPIO_FullRemap_TIM2, ENABLE); // 显式重映射TIM2通道
添加注释与文档:
在代码和硬件手册中明确记录引脚对应关系,减少后续维护成本。
✅ 总结
引脚定义不一致但小车能运行,本质是 信号流在硬件或代码层被隐式修正。需通过硬件检测和代码审计定位具体原因,最终确保定义与实物的严格一致。
六、循迹小车
循迹模块详解 | 良许嵌入式
注意这里的接线改回去了,要是不改回去会出错,小车会不要电的一直跑
复制01重命名05-循迹小车
循迹模块和红外传感器模块很像,所以复制智能台灯的ia_sensor
粘贴到05文件的BSP文件中,并且重命名为track
打开文件
加载文件
七、循迹+PWM调速
上面循迹小车中,可能会出现由于地图太小,小车太大,太沉,电机电压够足导致小车速度太快从而小车在拐弯的时候,冲出地图跑道。
为了避免这这种情况的发生,可以选择换一张大地图、把小车重量减轻、体积减轻、让小车变得更灵活,或者把小车电机电压减少,小车速度放慢。
这里使用PWM调速小车
使用PWM的接线
复制04pwm文件重命名为06-循迹+pwm调速控制小车
将05中的track文件粘贴到06中
打开工程,加载文件
这里注意改变arr和psc的值
因为原来的20-1,72-1,步子太大了
上述中小车可以设置的pwm波为0-20,如果设置9太快了,如果设置8太慢了,所以让它步子小一点,这样可以设置的值就更精确了
具体的数值,需要结合实际情况自己设定,这里95的速度只符合我这里
八、跟随小车
跟随小车要达到的效果是:
当两边的跟随模块都检测到东西的时候,就向前走,当右边检测到东西的时候,就向右走,否则向左走,当都未检测到东西的时候,就停止。
复制01,重命名为07-跟随小车
将循迹模块的track重命名为followu
打开项目,加载文件,将所有的track重命名为follow
更改引脚
代码如下:
九、避障小车
复制项目文件01,重命名为08-避障小车
复制超声波实验和舵机实验的文件,粘贴至如下图所示
接线如图所示:
打开项目
加载文件
要加入定时器文件
舵机代码可以分装成左转,右转,中间,方便看