基于stm32的红外巡线小车(基础版)
前两天做了一个红外巡线小车,想记录一下整个过程,也让自己掌握的更牢固,同时也分享给大家看看。在此要特别感谢实验室的学长,帮我解决一些问题到晚上一点多,同时帮我优化了控制的逻辑。这个小车比较基础,主要是分享给新手,同时我自己学的也不是很深入,如果文章有错的地方,还请多指正。那么话不多说,我们来看制作过程。
- 硬件篇
- 软件篇
一、硬件篇
1.stmf103c8t6最小系统板
2.l298n电机驱动模块
将控制电机的4个io口接到L298N的逻辑输入接口,输出A和输出B分别连接两个电机。12V供电接电源正极,5V供电接单片机5V供电,gnd接电源负极和单片机gnd。
3.两个红外传感器
红外传感器有三个接口,一个vcc一个gnd,另一个是返回数据。
4.锂电池(7.4V~12V)
5.小车底盘(两个电机)
相关的io口接线在函数中都有配置,在这就不写了
二、软件篇
制作红外巡线小车主要需要红外传感器的函数和电机pwm驱动的函数,另外还需要添加正点原子的delay文件和sys文件。
1红外传感器
首先我们要明白红外传感器的原理,当红外灯检测到黑线时返回给io口的值是0,检测到黑线外(亮光位置)时,返回的值是1。知道了这些,我们就只需要读取io口的值,就可以判断小车的巡线情况。
我在头文件中定义了#define DO1 PAin(1)
#define DO2 PAin(2)
这两句代码就是读取PA1,PA2两个io口的值,这种定义是基于sys库函数的,比较方便。
red.c
#include "red.h"
u8 redray1_findblack;
u8 redray2_findblack;
void REDRAY_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1|GPIO_Pin_2;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
}
void REDRAY_Scan(void)
{
if(DO1==0)redray1_findblack=1;
else redray1_findblack=0;
if(DO2==0)redray2_findblack=1;
else redray2_findblack=0;
}
2.电机驱动(pwm)
首先配置电机的io口,然后配置这些io口的pwm。pwm的配置这里就不降讲了,可以看一下网上的stm32的基础教程,这里讲一下怎么计算pwm的占空比和频率:
首先我们看motor.c中的TIM_SetCompare1(TIM4,pwm1);其中pwm1是捕获比较寄存器值,TIM4_PWM_Init(u16 arr,u16 psc)函数中arr是自动重装载寄存器周期的值,psc是时钟频率除数的预分频值。
知道了这些我们就可以计算占空比和频率,
占空比:pwm1/arr100%
频率:72M / ((arr+1)(psc+1))(单位:Hz)这里主频是72MHz
motor.c
#include "motor.h"
#include "stm32f10x.h"
void MOTOR_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
GPIO_Init(GPIOB, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6|GPIO_Pin_7|GPIO_Pin_8|GPIO_Pin_9; //两个输出端
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);
}
void TIM4_PWM_Init(u16 arr,u16 psc)
{
GPIO_InitTypeDef GPIO_InitStructure;
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_OCInitTypeDef TIM_OCInitStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB | RCC_APB2Periph_AFIO, ENABLE);
//GPIO_PinRemapConfig(GPIO_Remap_TIM4, ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7 | GPIO_Pin_8 | GPIO_Pin_9;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);//初始化GPIO
//初始化TIM4
TIM_TimeBaseStructure.TIM_Period = arr; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值
TIM_TimeBaseStructure.TIM_Prescaler =psc; //设置用来作为TIMx时钟频率除数的预分频值
TIM_TimeBaseStructure.TIM_ClockDivision = 0; //设置时钟分割:TDTS = Tck_tim
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //TIM向上计数模式
TIM_TimeBaseInit(TIM4, &TIM_TimeBaseStructure); //根据TIM_TimeBaseInitStruct中指定的参数初始化TIMx的时间基数单位
//初始化TIM4 Channel1 PWM模式
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; //选择定时器模式:TIM脉冲宽度调制模式1
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //比较输出使能
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; //输出极性:TIM输出比较极性高
TIM_OCInitStructure.TIM_Pulse = 0;
TIM_OC1Init(TIM4, &TIM_OCInitStructure); //根据T指定的参数初始化外设TIM4 OC1
TIM_OCInitStructure.TIM_Pulse = 0;
TIM_OC2Init(TIM4, &TIM_OCInitStructure); //根据T指定的参数初始化外设TIM4 OC2
TIM_OCInitStructure.TIM_Pulse = 0;
TIM_OC3Init(TIM4, &TIM_OCInitStructure); //根据T指定的参数初始化外设TIM4 OC3
TIM_OCInitStructure.TIM_Pulse = 0;
TIM_OC4Init(TIM4, &TIM_OCInitStructure); //根据T指定的参数初始化外设TIM4 OC4
TIM_OC1PreloadConfig(TIM4, TIM_OCPreload_Enable); //使能TIM4在CCR1上的预装载寄存器
TIM_Cmd(TIM4, ENABLE); //使能TIM4
}
void motor_control(u16 pwm1,u16 pwm2,u16 pwm3,u16 pwm4)
{
TIM_SetCompare1(TIM4,pwm1);
TIM_SetCompare2(TIM4,pwm2);
TIM_SetCompare3(TIM4,pwm3);
TIM_SetCompare4(TIM4,pwm4);
}
3.主函数
main.c
小车行进时大致有4种情况,两个红外灯都检测到光亮(直走或者超出黑线范围),左光亮右黑线(前方右转),右光亮左黑线(前方左转),都是黑线(这种情况一般不会出现)。通过if else就可以将小车的基本逻辑表示出来,但是注意上面提到两个红外灯都检测到光亮还可能是超出黑线范围,也就是要拐弯时拐不过来,小车冲过了,所以我们还要考虑这种情况。
我们可以设置一个记录上一次拐弯情况的功能,当超出了黑线范围,但是记录了上一刻的转弯情况,让小车继续转弯,这样就可以回到黑线上。
#include "sys.h"
#include<stdio.h>
#include "red.h"
#include "motor.h"
#include "stm32f10x.h"
#include "delay.h"
extern u8 redray1_findblack;
extern u8 redray2_findblack;
int main(void)
{
u8 stat = 0;
REDRAY_Init();//利用红外对管来检测黑线
delay_init(72); //延时函数初始化
TIM4_PWM_Init(1000-1,720-1);
while(1)
{
REDRAY_Scan();
if(redray1_findblack ==1&&redray2_findblack==0)
{
stat = 0;
motor_control(0,0,0,1000);
}
else if(redray1_findblack ==0&&redray2_findblack==1)
{
stat = 1;
motor_control(1000,0,0,0);
}
else if(redray1_findblack ==1&&redray2_findblack==1)
{
if(stat == 0)
{
motor_control(0,0,0,1000);
}
else
{
motor_control(1000,0,0,0);
}
}
else
{
motor_control(1000,0,0,1000);
}
}
}
另外还有delay函数和sys函数,在网上一搜就能收到,我就不写了。
以上是比较基础的控制逻辑,如果想要更好的控制,可以考虑一下下面这个图
如果能实现小车的每一个状态之间的变换逻辑,那么这个小车就很强大了。