一、小车模型设计
1)硬件搭配:四轮小车底盘、五路红外循迹传感器、L298N两路电机驱动模块、STM32F103RC最小系统板、电源、降压模块。
2)说明:这里只用到了两路电机驱动模块,是因为我将同一边的电机进行了并联;使用的电源应大于7V,才能有效的驱动小车行驶;降压模块的作用是将电压调节到适合单片机最小系统板工作的5V供电。
二、循迹程序设计
(在最后面我提供了工程文件的下载链接,大家可以下载验证)
1)需要配置的外设:五个GPIO输入口,两个定时器输出PWM波(这里只有两路也可以只配置一个定时器,因为一个定时器可以输出4路PWM波);
2)GPIO口配置(五路输入):
// 端口宏定义:
#define TRACK_PORT GPIOA//选择A端口
#define TRACK_PORT_CLK RCC_APB2Periph_GPIOA//端口A时钟
#define TRACK_INFRARED_L2_PIN GPIO_Pin_9//左2传感器对应的引脚
#define TRACK_INFRARED_L1_PIN GPIO_Pin_8//左1传感器对应的引脚
#define TRACK_INFRARED_M_PIN GPIO_Pin_7//中间传感器对应的引脚
#define TRACK_INFRARED_R1_PIN GPIO_Pin_6//右1传感器对应的引脚
#define TRACK_INFRARED_R2_PIN GPIO_Pin_5//右2传感器对应的引脚
// 端口模式配置函数:
void Track_Init(void)//循迹端口初始化
{
GPIO_InitTypeDef GPIO_InitStructure;//GPIO结构体定义
RCC_APB2PeriphClockCmd(TRACK_PORT_CLK ,ENABLE);//打开端口时钟
GPIO_InitStructure.GPIO_Pin = TRACK_INFRARED_M_PIN | TRACK_INFRARED_L1_PIN | TRACK_INFRARED_L2_PIN |
TRACK_INFRARED_R1_PIN | TRACK_INFRARED_R2_PIN;//配置传感器读取引脚
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;//配置为输入上拉模式
GPIO_Init(TRACK_PORT,&GPIO_InitStructure);//初始化端口
3)定时器TIM配置:
//定时器配置宏定义:
void Motor_Init(void)
{
uint16_t PrescalerValue;//分频参数变量
GPIO_InitTypeDef GPIO_InitStruct;//GPIO配置结构体定义
TIM_TimeBaseInitTypeDef TIM_TimeBaseStruct;//TIM配置结构体定义
TIM_OCInitTypeDef TIM_OCInitStruct;//TIM输出结构体定义
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3 | RCC_APB1Periph_TIM2,ENABLE);//开启TIM2、TIM3的时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB | RCC_APB2Periph_GPIOA | RCC_APB2Periph_AFIO,ENABLE);//开启GPIO的时钟与端口复用时钟
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1;//配置TIM3的CH3和CH4输出引脚
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;//模式设定为复用模式
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;//设定引脚速率
GPIO_Init(GPIOB,&GPIO_InitStruct);//根据结构体参数初始化GPIOB
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_2 | GPIO_Pin_3;//配置TIM2的CH3和CH4输出引脚
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;//模式设定为复用模式
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;//设定引脚速率
GPIO_Init(GPIOA,&GPIO_InitStruct);//根据结构体参数初始化GPIOA
TIM_DeInit(TIM3);//复位TIM3
TIM_DeInit(TIM2);//复位TIM2
PrescalerValue = (uint16_t) (SystemCoreClock / 4000000 ) - 1;//分频系数计算
TIM_TimeBaseStruct.TIM_Period = SPEED_PERIOD;//重载寄存器的值,定时周期
TIM_TimeBaseStruct.TIM_Prescaler = PrescalerValue;//预分频
TIM_TimeBaseStruct.TIM_ClockDivision = TIM_CKD_DIV1;//时钟切割
TIM_TimeBaseStruct.TIM_CounterMode = TIM_CounterMode_Up;//向上计数
TIM_TimeBaseInit(TIM3,&TIM_TimeBaseStruct);//使用结构体参数初始化TIM3
TIM_TimeBaseInit(TIM2,&TIM_TimeBaseStruct);//使用结构体参数初始化TIM2
TIM_OCInitStruct.TIM_OCMode = TIM_OCMode_PWM1;//PWM模式为1
TIM_OCInitStruct.TIM_OutputState = TIM_OutputState_Enable;//使能TIM得输出
TIM_OCInitStruct.TIM_Pulse = STARTER_SPEED;//比较寄存器的值
TIM_OCInitStruct.TIM_OCPolarity = TIM_OCPolarity_High;//通道1的有效电平为高电平;
TIM_OC3Init(TIM3,&TIM_OCInitStruct);//使用结构体参数初始化TIM3的CH3输出PWM波
TIM_OC3Init(TIM2,&TIM_OCInitStruct);//使用结构体参数初始化TIM2的CH3输出PWM波
TIM_OC4Init(TIM3,&TIM_OCInitStruct);//使用结构体参数初始化TIM3的CH4输出PWM波
TIM_OC4Init(TIM2,&TIM_OCInitStruct);//使用结构体参数初始化TIM2的CH4输出PWM波
TIM_OC3PreloadConfig(TIM3,TIM_OCPreload_Enable);//允许随时修改TIM3 CCR3比较寄存器的值;
TIM_OC4PreloadConfig(TIM3,TIM_OCPreload_Enable);//允许随时修改TIM3 CCR4比较寄存器的值;
TIM_OC3PreloadConfig(TIM2,TIM_OCPreload_Enable);//允许随时修改TIM2 CCR3比较寄存器的值;
TIM_OC4PreloadConfig(TIM2,TIM_OCPreload_Enable);//允许随时修改TIM2 CCR4比较寄存器的值;
TIM_Cmd(TIM3,ENABLE);//开启TIM3
TIM_Cmd(TIM2,ENABLE);//开启TIM2
}
4)电机驱动函数:
/*******
功 能:左边电机驱动函数
形 参:cont1 配置CCR3比较寄存器的值(占空比调整)
cont2 配置CCR4比较寄存器的值(占空比调整)
返回值: 无
说 明:当cont1大于0,cont2为0时,电机正转,反之则反转(这里仔细理解一下,后面函数会使用到)
******/
void Motor_L_Cont(uint16_t cont1,uint16_t cont2)
{
TIM3->CCR3 = cont1;
TIM3->CCR4 = cont2;
}
/*******
功 能:右边电机驱动函数
形 参:cont1 配置CCR3比较寄存器的值(占空比调整)
cont2 配置CCR4比较寄存器的值(占空比调整)
返回值: 无
说 明:当cont1大于0,cont2为0时,电机正转,反之则反转(这里仔细理解一下,后面函数会使用到)
******/
void Motor_R_Cont(uint16_t cont1,uint16_t cont2)
{
TIM2->CCR3 = cont1;
TIM2->CCR4 = cont2;
}
/*******
功 能:双边电机驱动函数
形 参:L_speed 设置左电机前进的转速
R_speed 设置右电机前进的转速
返回值: 无
说 明:这里调用左电机驱动和右电机驱动函数,
******/
void Motor_Speed_Adjust(uint16_t L_speed,uint16_t R_speed)
{
Motor_L_Cont(L_speed,0);
Motor_R_Cont(R_speed,0);
}
/*******
功 能:小车旋转函数(左右电机反方向转动)
形 参:fx 为1时,逆时针转动(左电机反转,右电机正转)
为0时,顺时针转动(左电机正转,右电机反转)
返回值: 无
说 明:这里使用到的STARTER_SPEED,为宏定义的初始占空参数,这里调用左电机驱动和右电机驱动函数,
******/
void Motor_Opposite(u8 fx)//旋转
{
if(fx)
{
Motor_L_Cont(0,STARTER_SPEED);
Motor_R_Cont(STARTER_SPEED,0);
}
else
{
Motor_L_Cont(STARTER_SPEED,0);
Motor_R_Cont(0,STARTER_SPEED);
}
}
/********
功 能:后退,左右电机反转
形 参:无
返回值: 无
说 明:这里使用到的STARTER_SPEED,为宏定义的初始占空参数,这里调用左电机驱动和右电机驱动函数,
******/
void Motor_Retreat(void)
{
Motor_L_Cont(0,STARTER_SPEED);
Motor_R_Cont(0,STARTER_SPEED);
}
5)循迹函数:
/********
功 能:读取5个传感器输出的状态
形 参:无
返回值:state 传感器状态
说 明:传感器检测到白线输出1,黑线输出0。。
因为使用的时GPIOA端口的5到9引脚,这里直接读出GPIOA所有引脚的状态,
然后将得到的数据向右移动5位,去掉0到4引脚的数据,
再与上0x001f,得到5到9引脚的状态,
******/
u8 Get_Infrared_State(void)
{
u8 state = 0;
state = (u8)(GPIO_ReadInputData(TRACK_PORT) >> 5) & 0x001f;
return state;
}
/********
功 能:循迹调整函数
形 参:无
返回值: 无
说 明:根据获取到的传感器输出状态,进行左右电机的速度调整
******/
void Track_Adjust(void)
{
u8 flag = 1;//是否复位标志
u8 state = 0;//状态获取变量定义
while(flag)
{
state = Get_Infrared_State();//获取传感器状态
switch(state)
{
case 0b00000:
case 0b10001:
case 0b00001:
case 0b10000: Motor_Speed_Adjust(0,0); break;//停止
case 0b10011: Motor_Speed_Adjust(STARTER_SPEED-100,STARTER_SPEED+100); break;//右偏1级
case 0b10111: Motor_Speed_Adjust(STARTER_SPEED-250,STARTER_SPEED+250); break;//右偏2级
case 0b00111: Motor_Speed_Adjust(STARTER_SPEED-400,STARTER_SPEED+400); break;//右偏3级
case 0b01111: Motor_Opposite(L); break;//右偏4级,
case 0b11001: Motor_Speed_Adjust(STARTER_SPEED+100,STARTER_SPEED-100); break;//左偏1级
case 0b11101: Motor_Speed_Adjust(STARTER_SPEED+250,STARTER_SPEED-250); break;//左偏2级
case 0b11100: Motor_Speed_Adjust(STARTER_SPEED+400,STARTER_SPEED-400); break;//左偏3级
case 0b11110: Motor_Opposite(R); break;//左偏4级
case 0b11011: Motor_Speed_Adjust(STARTER_SPEED,STARTER_SPEED);flag = 0; break;//正常
case 0b11111: Motor_Speed_Adjust(0,0); break;
default: Motor_Speed_Adjust(0,0); break;
}
}
}
工程文件下载链接:
链接:https://pan.baidu.com/s/1SUit597TkdzdcgemiXCrsA
提取码:sdsa
(好的,到这里就完了。写的非常简单,不足的地方请各位不吝赐教)