前言
本篇主要针对实现小车各功能的过程中具体的实现原理、程序算法以及遇到的问题进行统一的解释与说明。至此,结合上一篇的结构模块,已经完成了该智能小车项目的所有工作,其中必然还有很多可以优化的地方,但考虑到做此项目的最终目的是更深入的理解嵌入式编程,所以此项目到此为止,要去开拓更广阔的的天地啦。
驱动控制
驱动模块PWM配置
要想实现对小车的速度控制,就需要对L298N电机驱动模块的使能引脚输入PWM波,改变占空比的大小便可以调节小车速度的快慢 该PWM配置如下:
/**
* @brief: 初始化PWM输出配置,利用TIM3同时输出4路PWM波,控制4个电机转速
* @param: arr-自动重装载值 psc-时钟预分频系数
* @retval NONE
* @Others:GPIO_PinAFConfig函数必须分步进行复用,不能用一个复用函数并在一起,不然只有一路输出
**/
void TIM3_PWM_Init(u16 arr,u16 psc)
{
GPIO_InitTypeDef GPIO_InitStructure;
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_OCInitTypeDef TIM_OCInitStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE); //TIM3时钟使能
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE); //使能GPIOA时钟
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE); //使能GPIOB时钟
//GPIO_PinAFConfig函数必须分步进行复用,不能用一个复用函数并在一起,不然只有一路输出
GPIO_PinAFConfig(GPIOA,GPIO_PinSource6,GPIO_AF_TIM3); //GPIOA6复用为定时器3
GPIO_PinAFConfig(GPIOA,GPIO_PinSource7,GPIO_AF_TIM3); //GPIOA7复用为定时器3
GPIO_PinAFConfig(GPIOB,GPIO_PinSource0,GPIO_AF_TIM3); //GPIOB0复用为定时器3
GPIO_PinAFConfig(GPIOB,GPIO_PinSource1,GPIO_AF_TIM3); //GPIOB1复用为定时器3
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; //复用功能
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz; //速度100MHz
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; //推挽复用输出
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; //上拉
GPIO_Init(GPIOA,&GPIO_InitStructure); //初始化PF9
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; //复用功能
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz; //速度100MHz
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; //推挽复用输出
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; //上拉
GPIO_Init(GPIOB,&GPIO_InitStructure);
TIM_TimeBaseStructure.TIM_Prescaler=psc; //定时器分频
TIM_TimeBaseStructure.TIM_CounterMode=TIM_CounterMode_Up; //向上计数模式
TIM_TimeBaseStructure.TIM_Period=arr; //自动重装载值
TIM_TimeBaseStructure.TIM_ClockDivision=TIM_CKD_DIV1;
TIM_TimeBaseInit(TIM3,&TIM_TimeBaseStructure);//初始化定时器3
//初始化TIM3 PWM模式
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; //选择定时器模式:TIM脉冲宽度调制模式2
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //比较输出使能
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low; //输出极性:TIM输出比较极性低
TIM_OC1Init(TIM3, &TIM_OCInitStructure); //根据T指定的参数初始化外设TIM3OC1
TIM_OC2Init(TIM3, &TIM_OCInitStructure); //根据T指定的参数初始化外设TIM3OC2
TIM_OC3Init(TIM3, &TIM_OCInitStructure); //根据T指定的参数初始化外设TIM3OC3
TIM_OC4Init(TIM3, &TIM_OCInitStructure); //根据T指定的参数初始化外设TIM3OC4
TIM_OC1PreloadConfig(TIM3, TIM_OCPreload_Enable); //使能TIM3在CCR1上的预装载寄存器
TIM_OC2PreloadConfig(TIM3, TIM_OCPreload_Enable); //使能TIM3在CCR1上的预装载寄存器
TIM_OC3PreloadConfig(TIM3, TIM_OCPreload_Enable); //使能TIM3在CCR1上的预装载寄存器
TIM_OC4PreloadConfig(TIM3, TIM_OCPreload_Enable); //使能TIM3在CCR1上的预装载寄存器
TIM_ARRPreloadConfig(TIM3,ENABLE);
TIM_Cmd(TIM3, ENABLE); //使能TIM3
}
驱动实现
现在相当于已经完成了驱动模块的使能,要想使小车运动,需要对IN1~IN4引脚的电平进行操作 step1:IO口初始化
/**
* @brief: 初始化控制电机正反转及刹车的IO口
* @param: NONE
* @retval: NONE
* @Others: 选择IO时注意不要和STM32的硬件接口冲突,比如我一开始选择的IO占用了
LCD屏的一个接口,导致LCD初始化不正常
**/
void CAR_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOF, ENABLE);//使能GPIOF时钟
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOG, ENABLE);//使能GPIOG时钟
//IO初始化设置
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1 | GPIO_Pin_3 | GPIO_Pin_5 | GPIO_Pin_7 | GPIO_Pin_15 | GPIO_Pin_13 | GPIO_Pin_14;//LED0和LED1对应IO口
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;//普通输出模式
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;//推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;//100MHz
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;//上拉
GPIO_Init(GPIOF, &GPIO_InitStructure);//初始化GPIO
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;//普通输出模式
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;//推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;//100MHz
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;//上拉
GPIO_Init(GPIOG, &GPIO_InitStructure);//初始化GPIO
GPIO_SetBits(GPIOF,GPIO_Pin_1 | GPIO_Pin_3 | GPIO_Pin_5 | GPIO_Pin_7 | GPIO_Pin_15 | GPIO_Pin_13 | GPIO_Pin_14);
GPIO_SetBits(GPIOG,GPIO_Pin_1);
}
step2:驱动功能函数:包括前进、后退、转向、停车
/**
* @brief: 档位速度转换函数,设置1-4档,将档位线性的转换为相应的PWM波输出
* @param: 档位值
* @retval:pulse
* @Others: NONE
**/
u16 gear_trans(u16 gear)
{
u16 Pulse=0;
switch(gear)
{
case 0x00:
Pulse=500;
break;
case 0x01:
Pulse=350;
break;
case 0x02:
Pulse=300;
break;
case 0x03:
Pulse=200;
break;
case 0x04:
Pulse=100;
break;
default:
break;
}
return Pulse;
}
/**
* @brief: 前进函数,控制小车前进
* @param: 档位值
* @retval:NONE
* @Others: NONE
**/
void drive(u16 gear)
{
u16 Pulse;
Pulse=gear_trans(gear);
GPIO_ResetBits(GPIOF,GPIO_Pin_1);
GPIO_SetBits(GPIOF,GPIO_Pin_3);
GPIO_SetBits(GPIOF,GPIO_Pin_5);
GPIO_ResetBits(GPIOF,GPIO_Pin_7);
GPIO_SetBits(GPIOF,GPIO_Pin_15);
GPIO_ResetBits(GPIOG,GPIO_Pin_1);
GPIO_SetBits(GPIOF,GPIO_Pin_14);
GPIO_ResetBits(GPIOF,GPIO_Pin_13);
TIM_SetCompare1(TIM3,Pulse); //修改比较值,修改占空比
TIM_SetCompare2(TIM3,Pulse); //修改比较值,修改占空比
TIM_SetCompare3(TIM3,Pulse); //修改比较值,修改占空比
TIM_SetCompare4(TIM3,Pulse); //修改比较值,修改占空比
}
/**
* @brief: 后退函数
* @param: 档位值
* @retval:NONE
* @Others: NONE
**/
void reverse(u16 gear)
{
u16 Pulse;
Pulse=gear_trans(gear);
GPIO_SetBits(GPIOF,GPIO_Pin_1);
GPIO_ResetBits(GPIOF,GPIO_Pin_3);
GPIO_ResetBits(GPIOF,GPIO_Pin_5);
GPIO_SetBits(GPIOF,GPIO_Pin_7);
GPIO_ResetBits(GPIOF,GPIO_Pin_15);
GPIO_SetBits(GPIOG,GPIO_Pin_1);
GPIO_ResetBits(GPIOF,GPIO_Pin_14);
GPIO_SetBits(GPIOF,GPIO_Pin_13);
TIM_SetCompare1(TIM3,Pulse); //修改比较值,修改占空比
TIM_SetCompare2(TIM3,Pulse); //修改比较值,修改占空比
TIM_SetCompare3(TIM3,Pulse); //修改比较值,修改占空比
TIM_SetCompare4(TIM3,Pulse); //修改比较值,修改占空比
}
/**
* @brief: 停车函数
* @param: NONE
* @retval:NONE
* @Others: NONE
**/
void stop(void)
{
GPIO_ResetBits(GPIOF,GPIO_Pin_1);
GPIO_ResetBits(GPIOF,GPIO_Pin_3);
GPIO_ResetBits(GPIOF,GPIO_Pin_5);
GPIO_ResetBits(GPIOF,GPIO_Pin_7);
GPIO_ResetBits(GPIOF,GPIO_Pin_15);
GPIO_ResetBits(GPIOG,GPIO_Pin_1);
GPIO_ResetBits(GPIOF,GPIO_Pin_14);
GPIO_ResetBits(GPIOF,GPIO_Pin_13);
}
/**
* @brief: 左转函数,利用一侧车轮抱死转弯
* @param: 转弯档位
* @retval:NONE
* @Others: NONE
**/
void left_move(u16 gear_change)
{
u16 Pulse;
Pulse=gear_trans(gear_change);
GPIO_ResetBits(GPIOF,GPIO_Pin_1);
GPIO_SetBits(GPIOF,GPIO_Pin_3);
GPIO_SetBits(GPIOF,GPIO_Pin_5);
GPIO_ResetBits(GPIOF,GPIO_Pin_7);
GPIO_SetBits(GPIOF,GPIO_Pin_15);
GPIO_ResetBits(GPIOG,GPIO_Pin_1);
GPIO_SetBits(GPIOF,GPIO_Pin_14);
GPIO_ResetBits(GPIOF,GPIO_Pin_13);
TIM_SetCompare1(TIM3,500); //修改比较值,左侧车轮抱死
TIM_SetCompare2(TIM3,500); //修改比较值,左侧车轮抱死
TIM_SetCompare3(TIM3,Pulse); //修改比较值,修改占空比
TIM_SetCompare4(TIM3,Pulse); //修改比较值,修改占空比
}
/**
* @brief: 右转函数,利用一侧车轮抱死转弯
* @param: 转弯档位
* @retval:NONE
* @Others: NONE
**/
void right_move(u16 gear_change)
{
u16 Pulse;
Pulse=gear_trans(gear_change);
GPIO_ResetBits(GPIOF,GPIO_Pin_1);
GPIO_SetBits(GPIOF,GPIO_Pin_3);
GPIO_SetBits(GPIOF,GPIO_Pin_5);
GPIO_ResetBits(GPIOF,GPIO_Pin_7);
GPIO_SetBits(GPIOF,GPIO_Pin_15);
GPIO_ResetBits(GPIOG,GPIO_Pin_1);
GPIO_SetBits(GPIOF,GPIO_Pin_14);
GPIO_ResetBits(GPIOF,GPIO_Pin_13);
TIM_SetCompare1(TIM3,Pulse); //修改比较值,修改占空比
TIM_SetCompare2(TIM3,Pulse); //修改比较值,修改占空比
TIM_SetCompare3(TIM3,500); //修改比较值,右侧车轮抱死
TIM_SetCompare4(TIM3,500); //修改比较值,右侧车轮抱死
}
/**
* @brief: 左转函数,利用一侧前进一侧后退转弯
* @param: 转弯档位
* @retval:NONE
* @Others: NONE
**/
void left_move_2(u16 gear_change)
{
u16 Pulse;
Pulse=gear_trans(gear_change);
GPIO_SetBits(GPIOF,GPIO_Pin_1);
GPIO_ResetBits(GPIOF,GPIO_Pin_3);
GPIO_ResetBits(GPIOF,GPIO_Pin_5);
GPIO_SetBits(GPIOF,GPIO_Pin_7);
GPIO_SetBits(GPIOF,GPIO_Pin_15);
GPIO_ResetBits(GPIOG,GPIO_Pin_1);
GPIO_SetBits(GPIOF,GPIO_Pin_14);
GPIO_ResetBits(GPIOF,GPIO_Pin_13);
TIM_SetCompare1(TIM3,Pulse); //修改比较值,修改占空比
TIM_SetCompare2(TIM3,Pulse); //修改比较值,修改占空比
TIM_SetCompare3(TIM3,Pulse); //修改比较值,修改占空比
TIM_SetCompare4(TIM3,Pulse); //修改比较值,修改占空比
}
/**
* @brief: 右转函数,利用一侧前进一侧后退转弯
* @param: 转弯档位
* @retval:NONE
* @Others: NONE
**/
void right_move_2(u16 gear_change)
{
u16 Pulse;
Pulse=gear_trans(gear_change);
GPIO_ResetBits(GPIOF,GPIO_Pin_1);
GPIO_SetBits(GPIOF,GPIO_Pin_3);
GPIO_SetBits(GPIOF,GPIO_Pin_5);
GPIO_ResetBits(GPIOF,GPIO_Pin_7);
GPIO_ResetBits(GPIOF,GPIO_Pin_15);
GPIO_SetBits(GPIOG,GPIO_Pin_1);
GPIO_ResetBits(GPIOF,GPIO_Pin_14);
GPIO_SetBits(GPIOF,GPIO_Pin_13);
TIM_SetCompare1(TIM3,Pulse); //修改比较值,修改占空比
TIM_SetCompare2(TIM3,Pulse); //修改比较值,修改占空比
TIM_SetCompare3(TIM3,Pulse); //修改比较值,修改占空比
TIM_SetCompare4(TIM3,Pulse); //修改比较值,修改占空比
}
/**
* @brief: 前进函数,主要用于遥控模式
* @param: pulse值
* @retval:NONE
* @Others: NONE
**/
void drive_pulse(int pulse)
{
GPIO_ResetBits(GPIOF,GPIO_Pin_1);
GPIO_SetBits(GPIOF,GPIO_Pin_3);
GPIO_SetBits(GPIOF,GPIO_Pin_5);
GPIO_ResetBits(GPIOF,GPIO_Pin_7);
GPIO_SetBits(GPIOF,GPIO_Pin_15);
GPIO_ResetBits(GPIOG,GPIO_Pin_1);
GPIO_SetBits(GPIOF,GPIO_Pin_14);
GPIO_ResetBits(GPIOF,GPIO_Pin_13);
TIM_SetCompare1(TIM3,pulse); //修改比较值,修改占空比
TIM_SetCompare2(TIM3,pulse); //修改比较值,修改占空比
TIM_SetCompare3(TIM3,pulse); //修改比较值,修改占空比
TIM_SetCompare4(TIM3,pulse); //修改比较值,修改占空比
}
/**
* @brief: 后退函数,主要用于遥控模式
* @param: pulse值
* @retval:NONE
* @Others: NONE
**/
void reverse_pulse(int pulse)
{
GPIO_SetBits(GPIOF,GPIO_Pin_1);
GPIO_ResetBits(GPIOF,GPIO_Pin_3);
GPIO_ResetBits(GPIOF,GPIO_Pin_5);
GPIO_SetBits(GPIOF,GPIO_Pin_7);
GPIO_ResetBits(GPIOF,GPIO_Pin_15);
GPIO_SetBits(GPIOG,GPIO_Pin_1);
GPIO_ResetBits(GPIOF,GPIO_Pin_14);
GPIO_SetBits(GPIOF,GPIO_Pin_13);
TIM_SetCompare1(TIM3,pulse); //修改比较值,修改占空比
TIM_SetCompare2(TIM3,pulse); //修改比较值,修改占空比
TIM_SetCompare3(TIM3,pulse); //修改比较值,修改占空比
TIM_SetCompare4(TIM3,pulse); //修改比较值,修改占空比
}
/**
* @brief: 转弯函数,主要用于遥控模式
* @param: pulse1-左侧,pulse2-右侧
* @retval:NONE
* @Others: NONE
**/
void turn_pulse(int pulse1,int pulse2)
{
GPIO_ResetBits(GPIOF,GPIO_Pin_1);
GPIO_SetBits(GPIOF,GPIO_Pin_3);
GPIO_SetBits(GPIOF,GPIO_Pin_5);
GPIO_ResetBits(GPIOF,GPIO_Pin_7);
GPIO_SetBits(GPIOF,GPIO_Pin_15);
GPIO_ResetBits(GPIOG,GPIO_Pin_1);
GPIO_SetBits(GPIOF,GPIO_Pin_14);
GPIO_ResetBits(GPIOF,GPIO_Pin_13);
TIM_SetCompare1(TIM3,pulse1); //修改比较值,修改占空比
TIM_SetCompare2(TIM3,pulse1); //修改比较值,修改占空比
TIM_SetCompare3(TIM3,pulse2); //修改比较值,修改占空比
TIM_SetCompare4(TIM3,pulse2); //修改比较值,修改占空比
}
写到这里,你已经可以让你的小车动起来了,舒坦
巡线功能
这里我采用了4个巡线传感器,左右两侧各一个,中间部分布两个,采用的传感器个数不同、分布位置不同,那相应的实现巡线的算法也就不同,具体算法问题可以查阅相关资料
巡线传感器初始化
对需要接受巡线传感器信号的IO口进行配置
/**
* @brief: 循迹传感器所接IO口初始化
* @param: NONE
* @retval:NONE
* @others: 最初循迹模块上面开关指示灯常亮的原因是选错了IO口,用了PC0和PC2
换成PF2和PF4就好啦,why?
**/
void Trace_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOF, ENABLE); //使能GPIOF时钟
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_2 | GPIO_Pin_4 | GPIO_Pin_6;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN; //复用功能
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz; //速度100MHz
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; //推挽复用输出
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_DOWN; //上拉
GPIO_Init(GPIOF,&GPIO_InitStructure);
}
信号接收与传递
对巡线传感器采集到的线路信息进行采集并记录
/**
* @brief: 循迹模块扫描函数,参照STM32按键实验
* @param: mode,==1表示支持连续按,==0表示不支持连续按
* @retval: 4个循迹模块的编号,返回1表示1号传感器压线
* @others: NONE
**/
u8 TRACE_Scan(u8 mode)
{
static u8 trace_up=1;//按键按松开标志
if(mode)trace_up=1; //支持连按
if(trace_up&&(TRACE1==1||TRACE2==1||TRACE3==1||TRACE4==1))
{
delay_ms(10);//去抖动
trace_up=0;
if(TRACE1==1)return 1;
else if(TRACE2==1)return 2;
else if(TRACE3==1)return 3;
else if(TRACE4==1)return 4;
}
else if(TRACE1==0&&TRACE2==0&&TRACE3==0&&TRACE4==0)trace_up=1;
return 0;// 无按键按下
}
巡线实现
此处只进行了矩形巡线的编写,如果需要实现其他形状线路的巡线,可以查阅相关资料
/**
* @brief: 循迹执行模块(矩形巡线),根据检测到的传感器信号做出相应动作
* @param: NONE
* @retval:NONE
* @others: 由于此项目主要用来练习编程能力,so此处只编写了矩形巡线
并没有对其他线型情况以及各种算法做深入探究
**/
void TRACE_Implement(void)
{
extern u16 GEAR;
u8 Edge_Flag=0;//矩形90度角检测,检测到后此标志位置1
u8 trace_scan;
trace_scan=TRACE_Scan(0);//得到压线的传感器的编号
if(trace_scan)
{
switch(trace_scan)
{
case TRACE1_SCAN: //检测到最左侧传感器
left_move_2(GEAR+2);//以较大速度左转
Edge_Flag = 1;//90度标志位置1
delay_ms(150);//转向90度结束
left_move(GEAR);//小左转
break;
case TRACE2_SCAN: //检测到最右侧传感器
right_move_2(GEAR+2);
Edge_Flag = 1;
delay_ms(150);
right_move(GEAR);
LED0=!LED0;
break;
case TRACE3_SCAN: //检测到中间左侧传感器
if(Edge_Flag==1)//说明刚刚经过90度弯
{
right_move(GEAR);//中间左侧传感器压线时向右转
Edge_Flag=0;
}
else
{
left_move(GEAR);//没有经过90度弯,中间左侧传感器压线时向左转
}
LED1=!LED1;
break;
case TRACE4_SCAN: //检测到中间右侧传感器
if(Edge_Flag==1)
{
left_move(GEAR);
Edge_Flag=0;
}
else
{
right_move(GEAR);
}
LED0=!LED0;
LED1=!LED1;
break;
}
}else delay_ms(10);
}
避障功能
如前所述,我们利用舵机和超声波传感器搭建了避障云台,现在需要对其进行初始化并完成测距和避障
舵机PWM配置
舵机的角度是由PWM波来控制的,给舵机输入不同占空比的PWM波,便可以使其到达不同的角度位置
/**
* @brief: 配置驱动避障云台舵机的PWM输出
* @param: arr-自动重装载值 psc-时钟预分频系数
* @retval:NONE
* @others: NONE
**/
void TIM4_PWM_Init(u16 arr,u16 psc)
{
GPIO_InitTypeDef GPIO_InitStructure;
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
TIM_OCInitTypeDef TIM_OCInitStructure;
/* 开启时钟 */
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB,ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4,ENABLE);
GPIO_PinAFConfig(GPIOB,GPIO_PinSource6,GPIO_AF_TIM4); //GPIOB6复用为定时器4
/* 配置GPIO的模式和IO口 */
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_6;// PB6
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
GPIO_Init(GPIOB,&GPIO_InitStructure);
//TIM4定时器初始化
TIM_TimeBaseInitStructure.TIM_Period = arr; //PWM 频率=72000/(199+1)=36Khz//设置自动重装载寄存器周期的值
TIM_TimeBaseInitStructure.TIM_Prescaler = psc;//设置用来作为TIM4时钟频率预分频值
TIM_TimeBaseInitStructure.TIM_ClockDivision = 0;
TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up; //TIM向上计数模式
TIM_TimeBaseInit(TIM4, &TIM_TimeBaseInitStructure);
//PWM初始化 根据TIM_OCInitStruct中指定的参数初始化外设TIM4
TIM_OCInitStructure.TIM_OCMode=TIM_OCMode_PWM1;
TIM_OCInitStructure.TIM_OutputState=TIM_OutputState_Enable;//PWM输出使能
TIM_OCInitStructure.TIM_OCPolarity=TIM_OCPolarity_Low;
TIM_OC1Init(TIM4,&TIM_OCInitStructure);
//注意此处初始化时TIM_OC1Init而不是TIM_OCInit,否则会出错。因为固件库的版本不一样
TIM_OC1PreloadConfig(TIM4, TIM_OCPreload_Enable);//使能TIM4在CCR1上的预装载寄存器
TIM_Cmd(TIM4,ENABLE);//使能TIM4外设
}
超声波传感器初始化配置
/**
* @brief: 超声波传感器初始化,
* @param: NONE
* @retval:NONE
* @others: NONE
**/
void HCSR04_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
EXTI_InitTypeDef EXTI_InitStructure;
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOG , ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE);//使能SYSCFG时钟
GPIO_InitStructure.GPIO_Pin = TRIG_PIN;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
GPIO_Init(SONAR_PORT, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = ECHO_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN;
GPIO_Init(SONAR_PORT, &GPIO_InitStructure);
SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOG, GPIO_PinSource3); // 中断线以及中断初始化配置
EXTI_ClearITPendingBit(EXTI_Line3);
/* 外部中断初始化,用于接收检测信号 */
EXTI_InitStructure.EXTI_Line = EXTI_Line3;
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising;
EXTI_InitStructure.EXTI_LineCmd = ENABLE;
EXTI_Init(&EXTI_InitStructure);
/* 外部中断优先级设置 */
NVIC_InitStructure.NVIC_IRQChannel = EXTI3_IRQn;//外部中断3
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;//使能外部中断通道
NVIC_Init(&NVIC_InitStructure);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);
/* TIM2中断初始化,用于计数从而计时 */
TIM_TimeBaseInitStructure.TIM_Prescaler = 83;//分频系数83,频率为1MHz,理论测量精度0.34mm
TIM_TimeBaseInitStructure.TIM_Period = 50000;//计数周期50000,相当于0.05s,最大测量范围17m
TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);
TIM_ITConfig( //使能或者失能指定的TIM中断
TIM2, //TIM2
TIM_IT_Update | //TIM 中断源
TIM_IT_Trigger, //TIM 触发中断源
ENABLE //使能
);
TIM_Cmd(TIM2, DISABLE);
TIM_ClearFlag(TIM2, TIM_FLAG_Update);
/* TIM2中断优先级设置 */
NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn; //TIM2中断
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能
NVIC_Init(&NVIC_InitStructure);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM5,ENABLE);
/* TIM5中断初始化,用于启动测距函数 */
TIM_TimeBaseInitStructure.TIM_Prescaler = 83;
TIM_TimeBaseInitStructure.TIM_Period = 100;
TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseInit(TIM5, &TIM_TimeBaseInitStructure);
TIM_ITConfig( //使能或者失能指定的TIM中断
TIM5, //TIM5
TIM_IT_Update | //TIM 中断源
TIM_IT_Trigger, //TIM 触发中断源
ENABLE //使能
);
TIM_Cmd(TIM5, ENABLE);
TIM_ClearFlag(TIM5, TIM_FLAG_Update);
/* TIM5中断优先级设置 */
NVIC_InitStructure.NVIC_IRQChannel = TIM5_IRQn; //TIM5中断
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 3;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道被使能
NVIC_Init(&NVIC_InitStructure);
}
测距功能
根据HC-SR05的使用说明,需要触发一个不小于10us的高电平,下面的函数实现这个功能
/*Distance measure--------------------------------------------------*/
/**
* @brief: 启动测距功能函数,触发超声波传感器
* @param: NONE
* @retval:NONE
* @others: 此函数内部的延时函数必须是可重入的,因为放在了TIM5的中断函数内部
STM32例程里面的延时函数不可重入,so 此处自定义了可重入的延时函数
**/
void HCSR04_StartMeasure(void)
{
GPIO_SetBits(SONAR_PORT, TRIG_PIN);
delay_us_unpre(40); // The width of trig signal must be greater than 10us.
//出现在定时器5的中断函数中,所以不能用系统延时
// 自定义普通延时函数,已解决延时失效问题
GPIO_ResetBits(SONAR_PORT, TRIG_PIN);
//printf("Test start!");//printf函数也是不可重入函数,开启后导致中断失效
}
对计数器计数值进行处理得到测距距离
/**
* @brief: 测距函数
* @param: TIM2计数值
* @retval:测距距离,units:cm
* @others: NONE
**/
float HCSR04_GetDistance(u32 count)
{
float distance;
// distance = measurement/2/1000*340 = measurement/59 (cm) measurement-units:us
distance = (float)count / 58.8 ;
return distance;
}
避障的实现
此处涉及一定的算法,理解起来很简单的算法,即什么时候就算检测到障碍物,检测到障碍物之后要进行什么样的操作等等,当然可以用不同的方式实现避障,可以自行研究和优化
/**
* @brief: 利用得到的测距距离进行相应操作完成避障任务
* @param: NONE
* @retval:NONE
* @others: 最初的实时性得不到解决是因为该函数用到了大量延时,现已改为根据测距距离
进行相应操作的实现方式,实时性问题得以解决
**/
void Obstacle_avoid(void)
{
extern u16 GEAR;
if(UltrasonicWave_Distance<30)
{
reverse(GEAR);
while(UltrasonicWave_Distance<=40);
stop();
TIM_SetCompare1(TIM4, 181);//探测仪左转45度
delay_ms(500);
if(UltrasonicWave_Distance<30)
{
TIM_SetCompare1(TIM4, 191);//探测仪右转45度
delay_ms(500);
if(UltrasonicWave_Distance<30)
{
reverse(GEAR);
while(UltrasonicWave_Distance<=40);
right_move_2(GEAR+1);
delay_ms(500);
TIM_SetCompare1(TIM4, 186);//探测仪回零位
drive(GEAR);
}
else
{
right_move_2(GEAR+1);
delay_ms(500);
TIM_SetCompare1(TIM4, 186);//探测仪回零位
drive(GEAR);
}
}
else
{
left_move_2(GEAR+1);
delay_ms(500);
drive(GEAR);
TIM_SetCompare1(TIM4, 186);//探测仪回零位
}
}
}