基于STM32的智能巡线小车设计
一、项目概述
本次项目为制作一个自动巡线小车,要求制作的小车能够巡场地黑线跑完整个赛道。赛道黑线宽20-30mm,包括直线、弧线、直角弯、锐角弯和交叉路口,根据完成时间和重启次数进行考察。
依据题目要求我们采用可调节探测距离的红外光电传感器作为探测器、利用L298N作为直流电机驱动、利用STM32F103作为主控板、采用9V可充电锂电池供电、以打孔塑料板作为车身、直流电机提供动力。
二、小车结构设计
巡线小车整体结构设计包括传感器模块、电机驱动模块、32开发板和车身。
2.1 传感器模块
因为场地黑线与白色地板对光线的反射系数不同,所以可以根据不同道路情况下反射光的强弱来进行赛道识别,正确巡线。本次我们采用红外光电反射传感器进行巡线检测。
该传感器模块对环境光线适应能力强,其具有一对红外线发射与接收管,发射管发射出频率的红外线,当检测方向遇到障碍物(反射面)时,红外线反射回来被接收管接收,经过比较器电路处理之后,绿色指示灯会亮起,同时信号输出接口输出数字信号(一个低电平信号),可通过电位器旋钮调节检测距离,有效距离范围2-30cm,工作电压为3.3V-5V。该传感器的探测距离可以通过电位器调节、具有干扰小、便于装配、使用方便等特点。

在车头安装了三个红外光电反射传感器,如图:

2.2 电机驱动模块
本次选用L298N作为驱动芯片和直流电机。L298N是ST公司生产的一种高电压、大电流电机驱动芯片。该芯片采用15脚封装。主要特点是:工作电压高,最高工作电压可达46V;输出电流大,瞬间峰值可达3A,持续工作电流2A;内含两个H桥的高电压大电流全桥式驱动器,可以用来驱动直流电动机;采用标准逻辑电平信号控制;具有两个使能控制端,在不受输入信号影响的情况下允许或禁止器件工作有一个逻辑电源输入端,使内部逻辑电路部分在低电压下工作;并且可以外接检测电阻,将变化量反馈给控制电路。使用L298N驱动电机,该芯片可以驱动两个二相电机,可以直接通过电源来调节输出电压。电机驱动模块L298N实物及电机转动状态编码如图


2.3 开发板控制
本次选用ALIENTEK MiniSTM32作为巡线小车开发板控制模块。
ALIENTEK MiniSTM32是一款迷你型的开发板,外观尺寸约8cm* 10cm大小,该开发板具有64个输入输出I/O口,可满足电机转动、pwm输出调速、传感器信号采集、延时等诸多功能。

2.4 车身
本次选用双层塑料板构成车身,塑料板上的打孔可方便地固定开发板、电池及传感器,夹层间可放置电机和驱动;动力采用四个直流电机,四驱结构可有效提高小车运动的稳定性,兼具一定的灵活性.

三、电气电路设计
3.1 驱动电路

驱动电路利用L298N驱动电机,将两个L298N的输入口连接起来可以直接控制一侧的电机,从而减少I/O口的使用及同步操作电机;利用使能端输入pwm波控制电机的转速从而实现调速功能,I1~4控制一侧轮子的前进后退,从而实现小车的前进、后退、转弯功能;利用L298N的5V输出端为开发板以及传感器供电。
pwm1、pwm2分别接开发板PA9、PA11,I1~4分别接PA0~3口
3.2 传感器电路

传感器电路利用红外光电传感器的电气特性,在探测到黑线时OUT输出低电平,未探测输出为高电平;经多次调试,该传感器距离地面距离为8mm至15mm探测效果最佳;通过适当调节传感器的灵敏度可以使小车在赛道上成果检测到黑线和赛道外区域。
传感器利用L298N 5V输出供电,输出端分别接开发板的PB 0~2口。
3.3 开发板连接电路

正点原子STM32 mini板PA0~3设定采用推挽输出模式,输出高低电平控制电机正反转;PA9、PA11采用复用推挽输出,输出占空比可调的pwm波控制电机转速;电源采用L298N 5V输出供电,并与其共地。
四、软件设计
小车软件设计方面采用可移植模块化设计,分为主函数(调用各模块功能实现项目要求)、传感器模块(定义I/O工作方式、赛道情况判断)、调速模块(调节PWM波占空比,改变电机转速)、运动模块(实现前进、左转、右转停止等功能)、延时模块(实现延时功能)。
转向判断:



4.1 主函数
main.c
#include "pwm.h"
#include "delay.h"
#include "operate.h"
#include "sensor.h"
int main()
{
SysTick_Init();
operate_init();
pwm_init(899,40); // pwm f=2kHz
sensor_init();
delay_ms(1000);
forward ();
delay_ms(300);
while(1)
{
switch (situation())
{
// case 0:
// {
// stop();
// delay_ms(20);
// }
// break;
case 1:
{
forward ();
delay_ms(20);
}
break;
case 2:
{
turn_left();
delay_ms(20);
}
break;
case 3:
{
turn_right();
delay_ms(20);
}
break;
// case 4:
// {
// backward();
// delay_ms(20);
// }
// break;
default: break;
}
}
}
4.2 传感器模块
sensor.h
#ifndef __SENSOR_H_
#define __SENSOR_H_
#include "stm32f10x.h"
//传感器初始化函数
void sensor_init(void);
//道路情况函数
int situation(void);
#endif
sensor.c
#include "sensor.h"
volatile int left_state;
volatile int mid_state;
volatile int right_state;
void sensor_init()
{
//配置传感器接收引脚
GPIO_InitTypeDef GPIO_Struct;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
GPIO_Struct.GPIO_Mode=GPIO_Mode_IN_FLOATING;
GPIO_Struct.GPIO_Pin=GPIO_Pin_0|GPIO_Pin_1|GPIO_Pin_2;
GPIO_Struct.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOB,&GPIO_Struct);
}
int situation()
{
//读取传感器的数值
left_state = GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_2);
mid_state = GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_1);
right_state = GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_0);
if(!left_state && !mid_state && !right_state | !left_state && mid_state && !right_state | left_state && !mid_state && right_state) //111 010 101前进
{
return 1;
}
if(left_state && !right_state) //100 110 左转
{
return 2;
}
4.3 调速模块
pwm.h
#ifndef __PWM_H_
#define __PWM_H_
#include "stm32f10x.h"
//PWM初始化函数
void pwm_init(unsigned int arr, unsigned int psc);
#endif
pwm.c
#include "pwm.h"
void pwm_init(unsigned int arr,unsigned int psc)
{
//定义GPIO TIM 通道 结构体
GPIO_InitTypeDef GPIO_InitStructure;
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_OCInitTypeDef TIM_OCInitStructure;
//使能定时器、引脚时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_AFIO , ENABLE);
//初始化PWM引脚
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8|GPIO_Pin_11;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
//初始化定时器
TIM_TimeBaseStructure.TIM_Period = arr;
TIM_TimeBaseStructure.TIM_Prescaler =psc;
TIM_TimeBaseStructure.TIM_ClockDivision = 0;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM1, &TIM_TimeBaseStructure);
//初始化通道
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2;
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStructure.TIM_Pulse = 0;
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
TIM_OC1Init(TIM1, &TIM_OCInitStructure);
TIM_OC4Init(TIM1,&TIM_OCInitStructure);
//输出使能
TIM_CtrlPWMOutputs(TIM1,ENABLE);
TIM_OC1PreloadConfig(TIM1, TIM_OCPreload_Enable);
TIM_OC4PreloadConfig(TIM1, TIM_OCPreload_Enable);
TIM_ARRPreloadConfig(TIM1, ENABLE);
TIM_Cmd(TIM1, ENABLE);
}
4.4 运动模块
operate.h
#ifndef __OPERATE_
#define __OPERATE_
#include "stm32f10x.h"
void operate_init(void);
void forward(void);
void backward(void);
void turn_left(void);
void turn_right(void);
void stop(void);
#endif
operate.c
#include "operate.h"
#include "pwm.h"
#include "delay.h"
//操作初始化
void operate_init()
{
GPIO_InitTypeDef GPIO_Struct;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOD,ENABLE);
GPIO_Struct.GPIO_Mode=GPIO_Mode_Out_PP;
GPIO_Struct.GPIO_Pin=GPIO_Pin_0|GPIO_Pin_1|GPIO_Pin_2|GPIO_Pin_3;
GPIO_Struct.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIO_Struct);
GPIO_ResetBits(GPIOA,GPIO_Pin_0|GPIO_Pin_1|GPIO_Pin_2|GPIO_Pin_3);
}
void forward()
{
operate_init();
TIM_SetCompare1(TIM1,500);
TIM_SetCompare4(TIM1,500);
GPIO_SetBits(GPIOA,GPIO_Pin_1|GPIO_Pin_3);
GPIO_ResetBits(GPIOA,GPIO_Pin_0|GPIO_Pin_2);
}
void backward()
{
operate_init();
TIM_SetCompare1(TIM1,450);
TIM_SetCompare4(TIM1,450);
GPIO_SetBits(GPIOA,GPIO_Pin_0|GPIO_Pin_2);
GPIO_ResetBits(GPIOA,GPIO_Pin_1|GPIO_Pin_3);
}
void turn_left()
{
operate_init();
TIM_SetCompare1(TIM1,350);
TIM_SetCompare4(TIM1,750);
GPIO_SetBits(GPIOA,GPIO_Pin_1|GPIO_Pin_2);
GPIO_ResetBits(GPIOA,GPIO_Pin_0|GPIO_Pin_3);
}
void turn_right()
{
operate_init();
TIM_SetCompare1(TIM1,750);
TIM_SetCompare4(TIM1,350);
GPIO_SetBits(GPIOA,GPIO_Pin_0|GPIO_Pin_3);
GPIO_ResetBits(GPIOA,GPIO_Pin_1|GPIO_Pin_2);
}
void stop()
{
pwm_init(899,40);// F=2khz
operate_init();
TIM_SetCompare1(TIM1,600);
TIM_SetCompare4(TIM1,600);
GPIO_ResetBits(GPIOA,GPIO_Pin_0|GPIO_Pin_1|GPIO_Pin_2|GPIO_Pin_3);
4.5 延时模块
delay.h
#ifndef __DELAY_
#define __DELAY_
#include "stm32f10x.h"
void SysTick_Init(void);
void delay_ms(volatile unsigned long ms);
void delay_us(volatile unsigned long us);
#endif
delay.c
#include "delay.h"
//定义全局变量 滴答计时器
volatile unsigned long time_delay;
//计时器初始化函数
void SysTick_Init(void)
{
if(SysTick_Config(SystemCoreClock/1000))
{
while(1);
}
NVIC_SetPriority(SysTick_IRQn, 0x0);
}
//毫秒延时函数
void delay_ms(volatile unsigned long ms)
{
if (SysTick_Config(SystemCoreClock/1000))
{
while(1);
}
time_delay=ms;
while(time_delay);
SysTick->CTRL=0x00;
SysTick->VAL =0X00;
}
//微秒延时函数
void delay_us(volatile unsigned long us)
{
if (SysTick_Config(SystemCoreClock/1000000))
{
while(1);
}
time_delay=us;
while(time_delay);
SysTick->CTRL=0x00;
SysTick->VAL =0X00;
}
void SysTick_Handler(void)
{
if(time_delay)
{
time_delay--;
}
}
附录
