寒假由于时间较长,自己就想做一个智能车玩玩,第一个做的是蓝牙小车,不过比较简单,我就不打算具体另开一个部分,到时候挑一些与避障小车不一样的部分说一下。我这个小车行走速度比较慢,还是存在一定瑕疵,线也比较凌乱,大体功能可以实现。
先贴小车的图片:
小车主要分为几个部分:
1.STM32F103RCT6,当然用来执行各种命令的,因为用的是学习板,占地比较大,以后有时间改成C8T6的核心板,这样占地小一些。
2.L298N电机驱动模块,主要用来驱动左右两个电机,这个模块下面介绍。
3.电池,这里我买的是一款航模电池 1800mah 20c 焊接好T插头的电池,输出的是12V电压
4.分电板,主要是把电池的12V分成一个12V和一个5V输出
5.舵机,用的是SG90模拟舵机,这个舵机是花费时间最多的,因为总和我想要的结果不太一样
6.超声波测距模块,用的是HC-SR04模块,具体使用我上一篇说过了
7.蓝牙模块,用的HC-05这个蓝牙模块主要是用在蓝牙小车,后来放在避障小车上用来输出前方障碍物数据
8.小车底盘+电机,这个在某宝上一搜就有,而且驱动方法都差不多。我这里用的是直流电机。还有步进电机,但是我没有采用
这一篇我主要来说一下怎样让小车跑起来,就是电机怎么动。
想让小车跑起来,最重要的就是电机,电机驱动主要是需要两个接口。
这个是我用的小车电机的说明,其中我们想要驱动电机就是在“电机线-”和“电机线+”一个高电压一个低电压,就会实现电机的转动,这里高低电压的接法没有要求接法不同只是影响正反转而已。至于其他的与编码器相关的可以不用连线,这些是测量电机转速的。在电压一定的情况下,电流越大,相应的转速也应该越快。
正常用L298N模块可以驱动电机并达到很高的转速,我这里尝试了一下直接用单片机驱动,发现也可以使电机转起来但是速度相比较用模块来说要慢很多,我猜测应该和电流有很大关系。
这个就是L298N驱动模块的样子
其中前面三个端子 ,中间的接地,注意要与单片机共地,否则将无法正常驱动电机,左面的端子是+12V输入,用来给L298N模块供电,就是电源输出的+12V电压,右面的端子是+5V输出,注意不要把这5V当作输入,建议不要用这个5V做为单片机的供电端。
左右两边各有两个端子,分别是OUT1,OUT2和OUT3,OUT4,用来驱动两路电机,这里只需要把OUT与电机的的电机线连接到一起即可完成。
在前面三个端子旁边有四个排针,这四个排针是用来控制左右两边的OUT,这四个排针分别是IN1 IN2 IN3 IN4 ,排针左右两边有两个用跳帽盖上的排针,分别是ENA ENB目的是使能那四个IN。用跳帽连接默认是已经使能,拔下跳帽另外加信号就可以继续使用。
这是不同情况下的电机转动情况,后期可以用两种方法调节电机速度:
一.给EN使能,之后调节IN1 IN2 之间的电压差(通过调节PWM波占空比的方式实现)来控制速度。
二.是控制EN的电压也是通过条件PWM占空比来实现。我这里采用的就是这种方法。
这里我初始化四个IO口分别来控制四个IN端,来控制其正转反转。
void gpio_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOB, ENABLE); //使能PA,PB端口时钟
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4; //PA.4 端口配置
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //IO口速度为50MHz
GPIO_Init(GPIOA, &GPIO_InitStructure); //根据设定参数初始化GPIOA.4
// GPIO_SetBits(GPIOA,GPIO_Pin_4); //PA.4 输出高
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5; //PA.5 端口配置, 推挽输出
GPIO_Init(GPIOA, &GPIO_InitStructure); //推挽输出 ,IO口速度为50MHz
//GPIO_SetBits(GPIOA,GPIO_Pin_5); //PA.5 输出高
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10; //PB.10端口配置, 推挽输出
GPIO_Init(GPIOB, &GPIO_InitStructure); //推挽输出 ,IO口速度为50MHz
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11; //PB.11 端口配置, 推挽输出
GPIO_Init(GPIOB, &GPIO_InitStructure); //推挽输出 ,IO口速度为50MHz
}
这里我把几种电机转动情况来分类用函数来调用
void stop()
{
PAout(4)=0;
PAout(5)=0;
PBout(10)=0;
PBout(11)=0;//停止
}
void front()
{
PBout(10)=0;
PBout(11)=1;//前进
PAout(4)=0;
PAout(5)=1;
}
void left()
{
PAout(4)=0;
PAout(5)=1;
PBout(10)=1;
PBout(11)=0;//左转
}
void right()
{
PAout(4)=1;
PAout(5)=0;
PBout(10)=0;
PBout(11)=1;//右转
}
void turnleft()
{
PAout(4)=0;
PAout(5)=1;
PBout(10)=0;
PBout(11)=0;//一电机停止 左转
}
void turnright()
{
PAout(4)=0;
PAout(5)=0;
PBout(10)=0;
PBout(11)=1;//一电机停止 右转
}
void back()
{
PAout(4)=1;
PAout(5)=0;
PBout(10)=1;
PBout(11)=0;//后退
}
这样就可以驱动轮胎转动,也就可以驱动小车任意方向运动。
下面是我设置了两路PWM波给两个EN信号,来控制电机速度,进而控制小车速度,我只用了普通的定时器来输出PWM波,没有用到高级定时器的刹车功能。
PWM1:
#include "pwm.h"
//PWM输出初始化
//arr:自动重装值
//psc:时钟预分频数
void TIM3_PWM4_Init(u16 arr,u16 psc)
{
GPIO_InitTypeDef GPIO_InitStructure;
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_OCInitTypeDef TIM_OCInitStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);//
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB , ENABLE); //使能GPIO外设时钟使能
//设置该引脚为复用输出功能,输出TIM3 CH4的PWM脉冲波形
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1; //TIM_CH4
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);
TIM_TimeBaseStructure.TIM_Period = arr; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值 80K
TIM_TimeBaseStructure.TIM_Prescaler =psc; //设置用来作为TIMx时钟频率除数的预分频值 不分频
TIM_TimeBaseStructure.TIM_ClockDivision = 0; //设置时钟分割:TDTS = Tck_tim
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //TIM向上计数模式
TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure); //根据TIM_TimeBaseInitStruct中指定的参数初始化TIMx的时间基数单位
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2; //选择定时器模式:TIM脉冲宽度调制模式2
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //比较输出使能
TIM_OCInitStructure.TIM_Pulse = 0; //设置待装入捕获比较寄存器的脉冲值
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; //输出极性:TIM输出比较极性高
TIM_OC4Init(TIM3, &TIM_OCInitStructure); //根据TIM_OCInitStruct中指定的参数初始化外设TIMx
TIM_CtrlPWMOutputs(TIM3,ENABLE); //MOE 主输出使能
TIM_OC4PreloadConfig(TIM3, TIM_OCPreload_Enable); //CH4预装载使能
TIM_ARRPreloadConfig(TIM3, ENABLE); //使能TIMx在ARR上的预装载寄存器
TIM_Cmd(TIM3, ENABLE); //使能TIM3
}
PWM2:
#include "pwm2.h"
//PWM输出初始化
//arr:自动重装值
//psc:时钟预分频数
void TIM3_PWM3_Init(u16 arr,u16 psc)
{
GPIO_InitTypeDef GPIO_InitStructure;
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_OCInitTypeDef TIM_OCInitStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);//
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB , ENABLE); //使能GPIO外设时钟使能
//设置该引脚为复用输出功能,输出TIM1 CH1的PWM脉冲波形
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0; //TIM_CH1
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);
TIM_TimeBaseStructure.TIM_Period = arr; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值 80K
TIM_TimeBaseStructure.TIM_Prescaler =psc; //设置用来作为TIMx时钟频率除数的预分频值 不分频
TIM_TimeBaseStructure.TIM_ClockDivision = 0; //设置时钟分割:TDTS = Tck_tim
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //TIM向上计数模式
TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure); //根据TIM_TimeBaseInitStruct中指定的参数初始化TIMx的时间基数单位
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2; //选择定时器模式:TIM脉冲宽度调制模式2
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //比较输出使能
TIM_OCInitStructure.TIM_Pulse = 0; //设置待装入捕获比较寄存器的脉冲值
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; //输出极性:TIM输出比较极性高
TIM_OC3Init(TIM3, &TIM_OCInitStructure); //根据TIM_OCInitStruct中指定的参数初始化外设TIMx
TIM_CtrlPWMOutputs(TIM3,ENABLE); //MOE 主输出使能
TIM_OC3PreloadConfig(TIM3, TIM_OCPreload_Enable); //CH3预装载使能
TIM_ARRPreloadConfig(TIM3, ENABLE); //使能TIMx在ARR上的预装载寄存器
TIM_Cmd(TIM3, ENABLE); //使能TIM3
两路PWM控制占空比
TIM_SetCompare3(TIM3,2400);//左轮电机转速
TIM_SetCompare4(TIM3,2530);//右轮电机转速
控制占空比函数数值越大转速越慢,在实际中,会遇到小车两个轮转速不一样,这里我没有用算法来调整,我采用手动调整到大概可以走直线,硬件的缺陷用软件来弥补。
到现在就可以用单片机+L298N模块来驱动电机,可以实现小车的任意方向行驶啦
具体代码之后上传