上篇主要是讲一些基础的东西,中篇讲了如何制作循迹,本篇讲一下制作避障小车。
七,如何用使用pwm让舵机旋转到相应的角度
我对他其中的一些关键信息再说明一下:
1,接线
橙色信号线
红色正极
棕褐色负极
2,舵机控制对pwm的要求
舵机的控制需要一个20ms的时基脉冲,该脉冲的高电平部分一般为0.5ms~2.5ms范围内的角度控制脉冲部分:
t = 0.5ms——————-舵机会转动 0 °
t = 1.0ms——————-舵机会转动 45°
t = 1.5ms——————-舵机会转动 90°
t = 2.0ms——————-舵机会转动 135°
t = 2.5ms——————-舵机会转动180°
我设置的是在转90度的时候为舵机的正前方,这样就能让舵机左右转了。
好了,经过上面的分析我们可以开始写程序了,思路如下:
利用定时器输出一个占空比可调的pwm,且这个pwm的周期为20ms。
下面看一下我的sg90.c文件:
#include “sg90.h”
void SG90_pwm_init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
TIM_OCInitTypeDef TIM_OCInitStructure;
/* 开启时钟 */
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);
/* 配置GPIO的模式和IO口 */
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_1;// PA1
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF_PP;//复用推挽输出
GPIO_Init(GPIOA,&GPIO_InitStructure);
//TIM3定时器初始化
TIM_TimeBaseInitStructure.TIM_Period = 199; //PWM 频率=72000/(199+1)=36Khz//设置自动重装载寄存器周期的值
TIM_TimeBaseInitStructure.TIM_Prescaler = 7199;//设置用来做为TIMx时钟频率预分频值
TIM_TimeBaseInitStructure.TIM_ClockDivision = 0;//设置时钟分割:TDTS = Tck_tim
TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up; //TIM向上计数模式
TIM_TimeBaseInit(TIM2, & TIM_TimeBaseInitStructure);
//PWM初始化 //根据TIM_OCInitStruct中指定的参数初始化外设TIMx
TIM_OCInitStructure.TIM_OCMode=TIM_OCMode_PWM1;
TIM_OCInitStructure.TIM_OutputState=TIM_OutputState_Enable;//PWM输出使能
TIM_OCInitStructure.TIM_OCPolarity=TIM_OCPolarity_Low;
TIM_OC2Init(TIM2,&TIM_OCInitStructure);
//注意此处初始化时TIM_OC1Init而不是TIM_OCInit,不然会出错。由于固件库的版本不同。
TIM_OC2PreloadConfig(TIM2, TIM_OCPreload_Enable);//使能或者失能TIMx在CCR1上的预装载寄存器
TIM_Cmd(TIM2,ENABLE);//使能或者失能TIMx外设
}
下面是sg90.h文件内容:
#ifndef __SG90_H
#define __SG90_H
#include “stm32f10x.h”
#include “delay.h”
#define SG90_Right_90 TIM_SetCompare2(TIM2, 195) //右转90度
#define SG90_Right_45 TIM_SetCompare2(TIM2, 190)
#define SG90_Front TIM_SetCompare2(TIM2, 185) //舵机摆正
#define SG90_Left_45 TIM_SetCompare2(TIM2, 180) //左转45度
#define SG90_Left_90 TIM_SetCompare2(TIM2, 175)
void SG90_pwm_init(void); //舵机pwm初始化
#endif
我来解释一下舵机代码比较关键的几个参数设置:
1,引脚怎么接?
PA1接舵机橙色线。
2,设置周期为20ms
TIM_TimeBaseInitStructure.TIM_Period = 199;
TIM_TimeBaseInitStructure.TIM_Prescaler = 7199;
通过这两句话,根据周期计算公式:
PWM周期为 (7200*200)/72000000=0.02=20ms
3,设置pwm模式和初始极性
TIM_OCInitStructure.TIM_OCMode=TIM_OCMode_PWM1;
TIM_OCInitStructure.TIM_OCPolarity=TIM_OCPolarity_Low;//初始极性为低
PWM1模式的意思如下:
当计时器值小于比较器设定值时则TIMX输出脚此时输出有效低电位。
当计时器值大于或等于比较器设定值时则TIMX输出脚此时输出高电位。
4,通过TIM_SetCompare2设置占空比
前面已经说了,我所设置的正前方是舵机转90度的时候,下面解释一下怎么通过设置占空比来设置方向吧。比如下面这句函数:
#define SG90_Front TIM_SetCompare2(TIM2, 185) //舵机摆正
由于高电平t = 1.5ms舵机会转动 90°,计数值TIM_Period设置的为200,比较值为185,所以高电平占比为(15/200)x20ms=1.5ms。
类似的要设置其它的高电平占比也是这样设置了。
八,如何配置定时器使用超声波模块测距
1,先来看看舵机和云台是怎么接在一起的?
类似这种,把超声波模块插在云台上,云台的作用其实就是把超声波模块固定在舵机上,再把模块的线引出来而已,舵机的线单独接或者接在云台上都是可以的。
2,超声波模块怎么用?
HC-SR04基本工作原理:
(1)采用IO口TRIG触发测距,给最少10us的高电平信呈。
(2)模块自动发送8个40khz的方波,自动检测是否有信号返回;
(3)有信号返回, 通过IO口ECHO输出一个高电平, 高电平持续的时间就是超声波从发射到返回的时间。 测试距离=(高电平时间*声速(340M/S))/2。
3,程序怎么写?
如果要写测距的话要观察测的是否准确,因为我手里正好有一块OLED屏,我就讲测得的距离放到了屏幕上,就可以直接观察测的距离了,你如果手里没有屏幕也可以用个串口程序讲数值传到电脑上去,只是这个我还没尝试过,这里就不介绍了。
下面贴一下测距文件cs.c里的内容:
#include “cs.h”
#include “stm32f10x.h”
#include “delay.h”
#include “usart.h”
/*记录定时器溢出次数*/
uint overcount=0;
/*设置中断优先级*/
void NVIC_Config(void)
{
NVIC_InitTypeDef NVIC_InitStructer;
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
NVIC_InitStructer.NVIC_IRQChannelPreemptionPriority=0;
NVIC_InitStructer.NVIC_IRQChannelSubPriority=0;
NVIC_InitStructer.NVIC_IRQChannel=TIM4_IRQn;
NVIC_InitStructer.NVIC_IRQChannelCmd=ENABLE;
NVIC_Init(&NVIC_InitStructer);
}
/*初始化模块的GPIO以及初始化定时器TIM2*/
void CH_SR04_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructer;
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructer;
RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOB, ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE);
/*TRIG触发信号*/
GPIO_InitStructer.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_InitStructer.GPIO_Mode=GPIO_Mode_Out_PP;
GPIO_InitStructer.GPIO_Pin=GPIO_Pin_8;
GPIO_Init(GPIOB, &GPIO_InitStructer);
/*ECOH回响信号*/
GPIO_InitStructer.GPIO_Mode=GPIO_Mode_IN_FLOATING;
GPIO_InitStructer.GPIO_Pin=GPIO_Pin_9;
GPIO_Init(GPIOB, & GPIO_InitStructer);
/*定时器TIM2初始化*/
TIM_DeInit(TIM4);
TIM_TimeBaseInitStructer.TIM_Period=999;//定时周期为1000
TIM_TimeBaseInitStructer.TIM_Prescaler=71; //分频系数72
TIM_TimeBaseInitStructer.TIM_ClockDivision=TIM_CKD_DIV1;//不分频
TIM_TimeBaseInitStructer.TIM_CounterMode=TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM4,&TIM_TimeBaseInitStructer);
TIM_ITConfig(TIM4,TIM_IT_Update,ENABLE);//开启更新中断
NVIC_Config();
TIM_Cmd(TIM4,DISABLE);//关闭定时器使能
}
float Senor_Using(void)
{
float length=0,sum=0;
u16 tim;
uint i=0;
/*测5次数据计算一次平均值*/
while(i!=5)
{
PBout(8)=1; //拉高信号,做为触发信号
delay_us(20); //高电平信号超过10us
PBout(8)=0;
/*等待回响信号*/
while(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_9)==RESET);
TIM_Cmd(TIM4,ENABLE);//回响信号到来,开启定时器计数
i+=1; //每收到一次回响信号+1,收到5次就计算均值
while(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_9)==SET);//回响信号消失
TIM_Cmd(TIM4,DISABLE);//关闭定时器
tim=TIM_GetCounter(TIM4);//获取计TIM4数寄存器中的计数值,一边计算回响信号时间
length=(tim+overcount*1000)/58.0;//经过回响信号计算距离
sum=length+sum;
TIM4-》CNT=0; //将TIM4计数寄存器的计数值清零
overcount=0; //中断溢出次数清零
delay_ms(10);
}
length=sum/5;
return length;//距离做为函数返回值
}
void TIM4_IRQHandler(void) //中断,当回响信号很长是,计数值溢出后重复计数,用中断来保存溢出次数
{
if(TIM_GetITStatus(TIM4,TIM_IT_Update)!=RESET)
{
TIM_ClearITPendingBit(TIM4,TIM_IT_Update);//清除中断标志
overcount++;
}
}
然后下面是cs.h文件:
#ifndef __CS_H
#define __CS_H
#include “stm32f10x.h”
#include “delay.h”
#include “sys.h”
#define uint unsigned int
#define TRIG_Send PBout(8)
#define ECHO_Reci PBin(9)
void CH_SR04_Init(void); //超声波模块相关配置初始化
float Senor_Using(void); //测距函数,返回值即为距离
void NVIC_Config(void); //中断配置
#endif
下面简单介绍怎么使用:
(1):引脚怎么接?
PB8接Trig口,PB9接Echo口。
(2):怎么用?
主函数中调用CH_SR04_Init()这一个函数即可初始化测距的相关配置,
测距直接用调用Senor_Using()这个函数,函数的返回值即为距离。
其它关于代码的设置我就不讲了,注释里已经很详细了
(3):使用例子
我用了oled屏幕显示,这段程序在我上传的代码里没有,仅仅用来测试的而已,但是我上传的代码已经将oled初始化的代码在主函数里调用了,所以在主函数也可以直接用oled屏幕。
下面是我的测试程序:
//头文件
#include “stm32f10x.h”
#include “led.h”
#include “moter.h”
#include “xunji.h”
#include “sg90.h”
#include “delay.h”
#include “sys.h”
#include “oled.h”
#include “bmp.h”
#include “cs.h”
int main(void)
{
char str[20]; //用来存放浮点数字符
float length_res[5]; //用来存放测距结果
SystemInit(); // 配置系统时钟为72M
delay_init(); //延时初始化
xunji_config(); //循迹初始化
TIM3_PWM_Init(); //电机pwm TIM3
SG90_pwm_init(); //舵机pwm TIM2
CH_SR04_Init(); //超声波定时器 TIM4
OLED_Init(); //oled显示初始化
while(1)
{
SG90_Front; //舵机摆正
length_res[0] =Senor_Using(); //测前方距离放在数组里
num2char(str,length_res[0],3,3); //将浮点数转为字符
OLED_ShowString(44,24,str,16);
OLED_ShowString(0,2,“distance is”,16);
OLED_Refresh();
}
}
效果是这样的
好了,测距已经准备就绪了,下面就可以开始写避障函数啦。
九,完成避障小车的制作
这里的任务主要就是编写主函数里的循环了。
设计思路如下:
1,舵机向前摆正,测量正前方的距离,如果距离小于30cm就停下来。
2,停下后,舵机检测左边45度和右边45度的距离,比较这两个距离。
3,假如左边的距离比右边大,就用一个do-while循环,使舵机摆正不断测量前方距离,同时小车缓慢左转,一直转到前方距离大于30cm,小车继续向前,循环继续。
下面是主函数:
int main(void)
{
char str[20]; //用来存放浮点数字符
float length_res[5]; //用来存放测距结果
SystemInit(); // 配置系统时钟为72M
delay_init(); //延时初始化
xunji_config(); //循迹初始化
TIM3_PWM_Init(); //电机pwm TIM3
SG90_pwm_init(); //舵机pwm TIM2
CH_SR04_Init(); //超声波定时器 TIM4
OLED_Init(); //oled显示初始化
while(1)
{
SG90_Front; //舵机摆正
delay_ms(100);
length_res[0] =Senor_Using(); //测前方距离放在数组里
delay_ms(100);
if(length_res[0]》30.00) //若是前方距离大于30cm 前进
{
CarGo();
}
if(length_res[0]《30.00) //若是前方距离小于30厘米 停车测左右距离
{
CarStop();
SG90_Left_45; //舵机左转45度测距
delay_ms(700);
length_res[1] =Senor_Using(); //把测量结果放进数组
SG90_Right_45; //舵机右转45度测距
delay_ms(700);
length_res[4] =Senor_Using(); //把测量结果放进数组
SG90_Front; //舵机摆正
delay_ms(100);
if(length_res[1]》length_res[4]) //若是左边的距离大于右边的距离
{
do //舵机摆正
{
SG90_Front;
delay_ms(10);
length_res[0] =Senor_Using(); //重复测前方的距离同时左转
delay_ms(10);
CarLeft();
}
while(length_res[0]《30.00); //一直转到前方距离大于30cm
}
if(length_res[1]《length_res[4]) //若是右边的距离大于左边的距离
{
do
{
SG90_Front;
delay_ms(10);
length_res[0] =Senor_Using(); //重复测前方的距离同时右转
delay_ms(10);
CarRight();
}
while(length_res[0]《30.00); //一直转到前方距离大于30cm
}
}
}
}
好了,到此为止,循迹避障小车已经设计完成了。
下面总结一下如何接线的:
电机驱动:
A6----IN1
A7----IN2
B0----IN3
B1----IN4
循迹模块:(从左到右为1234)
B4----第1个循迹模块的D0
B5----第2个循迹模块的D0
B6----第3个循迹模块的D0
B7----第4个循迹模块的D0
舵机:
A1----舵机橙色线
超声波模块:
PB8----Trig
PB9----Echo
oled(iic协议):
A8----SCL
A9----SDA
当然循迹和避障的策略都是我自己为了完成任务写的,比较简单,能够实现循迹避障功能,到后面你已经会操作各个模块后,自己写个更好的循迹避障策略是完全没有问题的,或者是用我的工程,里面的各模块函数也都写好了,直接调用就行。
上篇主要是讲一些基础的东西,中篇讲了如何制作循迹,本篇讲一下制作避障小车。
七,如何用使用pwm让舵机旋转到相应的角度
我对他其中的一些关键信息再说明一下:
1,接线
橙色信号线
红色正极
棕褐色负极
2,舵机控制对pwm的要求
舵机的控制需要一个20ms的时基脉冲,该脉冲的高电平部分一般为0.5ms~2.5ms范围内的角度控制脉冲部分:
t = 0.5ms——————-舵机会转动 0 °
t = 1.0ms——————-舵机会转动 45°
t = 1.5ms——————-舵机会转动 90°
t = 2.0ms——————-舵机会转动 135°
t = 2.5ms——————-舵机会转动180°
我设置的是在转90度的时候为舵机的正前方,这样就能让舵机左右转了。
好了,经过上面的分析我们可以开始写程序了,思路如下:
利用定时器输出一个占空比可调的pwm,且这个pwm的周期为20ms。
下面看一下我的sg90.c文件:
#include “sg90.h”
void SG90_pwm_init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
TIM_OCInitTypeDef TIM_OCInitStructure;
/* 开启时钟 */
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);
/* 配置GPIO的模式和IO口 */
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_1;// PA1
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF_PP;//复用推挽输出
GPIO_Init(GPIOA,&GPIO_InitStructure);
//TIM3定时器初始化
TIM_TimeBaseInitStructure.TIM_Period = 199; //PWM 频率=72000/(199+1)=36Khz//设置自动重装载寄存器周期的值
TIM_TimeBaseInitStructure.TIM_Prescaler = 7199;//设置用来做为TIMx时钟频率预分频值
TIM_TimeBaseInitStructure.TIM_ClockDivision = 0;//设置时钟分割:TDTS = Tck_tim
TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up; //TIM向上计数模式
TIM_TimeBaseInit(TIM2, & TIM_TimeBaseInitStructure);
//PWM初始化 //根据TIM_OCInitStruct中指定的参数初始化外设TIMx
TIM_OCInitStructure.TIM_OCMode=TIM_OCMode_PWM1;
TIM_OCInitStructure.TIM_OutputState=TIM_OutputState_Enable;//PWM输出使能
TIM_OCInitStructure.TIM_OCPolarity=TIM_OCPolarity_Low;
TIM_OC2Init(TIM2,&TIM_OCInitStructure);
//注意此处初始化时TIM_OC1Init而不是TIM_OCInit,不然会出错。由于固件库的版本不同。
TIM_OC2PreloadConfig(TIM2, TIM_OCPreload_Enable);//使能或者失能TIMx在CCR1上的预装载寄存器
TIM_Cmd(TIM2,ENABLE);//使能或者失能TIMx外设
}
下面是sg90.h文件内容:
#ifndef __SG90_H
#define __SG90_H
#include “stm32f10x.h”
#include “delay.h”
#define SG90_Right_90 TIM_SetCompare2(TIM2, 195) //右转90度
#define SG90_Right_45 TIM_SetCompare2(TIM2, 190)
#define SG90_Front TIM_SetCompare2(TIM2, 185) //舵机摆正
#define SG90_Left_45 TIM_SetCompare2(TIM2, 180) //左转45度
#define SG90_Left_90 TIM_SetCompare2(TIM2, 175)
void SG90_pwm_init(void); //舵机pwm初始化
#endif
我来解释一下舵机代码比较关键的几个参数设置:
1,引脚怎么接?
PA1接舵机橙色线。
2,设置周期为20ms
TIM_TimeBaseInitStructure.TIM_Period = 199;
TIM_TimeBaseInitStructure.TIM_Prescaler = 7199;
通过这两句话,根据周期计算公式:
PWM周期为 (7200*200)/72000000=0.02=20ms
3,设置pwm模式和初始极性
TIM_OCInitStructure.TIM_OCMode=TIM_OCMode_PWM1;
TIM_OCInitStructure.TIM_OCPolarity=TIM_OCPolarity_Low;//初始极性为低
PWM1模式的意思如下:
当计时器值小于比较器设定值时则TIMX输出脚此时输出有效低电位。
当计时器值大于或等于比较器设定值时则TIMX输出脚此时输出高电位。
4,通过TIM_SetCompare2设置占空比
前面已经说了,我所设置的正前方是舵机转90度的时候,下面解释一下怎么通过设置占空比来设置方向吧。比如下面这句函数:
#define SG90_Front TIM_SetCompare2(TIM2, 185) //舵机摆正
由于高电平t = 1.5ms舵机会转动 90°,计数值TIM_Period设置的为200,比较值为185,所以高电平占比为(15/200)x20ms=1.5ms。
类似的要设置其它的高电平占比也是这样设置了。
八,如何配置定时器使用超声波模块测距
1,先来看看舵机和云台是怎么接在一起的?
类似这种,把超声波模块插在云台上,云台的作用其实就是把超声波模块固定在舵机上,再把模块的线引出来而已,舵机的线单独接或者接在云台上都是可以的。
2,超声波模块怎么用?
HC-SR04基本工作原理:
(1)采用IO口TRIG触发测距,给最少10us的高电平信呈。
(2)模块自动发送8个40khz的方波,自动检测是否有信号返回;
(3)有信号返回, 通过IO口ECHO输出一个高电平, 高电平持续的时间就是超声波从发射到返回的时间。 测试距离=(高电平时间*声速(340M/S))/2。
3,程序怎么写?
如果要写测距的话要观察测的是否准确,因为我手里正好有一块OLED屏,我就讲测得的距离放到了屏幕上,就可以直接观察测的距离了,你如果手里没有屏幕也可以用个串口程序讲数值传到电脑上去,只是这个我还没尝试过,这里就不介绍了。
下面贴一下测距文件cs.c里的内容:
#include “cs.h”
#include “stm32f10x.h”
#include “delay.h”
#include “usart.h”
/*记录定时器溢出次数*/
uint overcount=0;
/*设置中断优先级*/
void NVIC_Config(void)
{
NVIC_InitTypeDef NVIC_InitStructer;
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
NVIC_InitStructer.NVIC_IRQChannelPreemptionPriority=0;
NVIC_InitStructer.NVIC_IRQChannelSubPriority=0;
NVIC_InitStructer.NVIC_IRQChannel=TIM4_IRQn;
NVIC_InitStructer.NVIC_IRQChannelCmd=ENABLE;
NVIC_Init(&NVIC_InitStructer);
}
/*初始化模块的GPIO以及初始化定时器TIM2*/
void CH_SR04_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructer;
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructer;
RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOB, ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE);
/*TRIG触发信号*/
GPIO_InitStructer.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_InitStructer.GPIO_Mode=GPIO_Mode_Out_PP;
GPIO_InitStructer.GPIO_Pin=GPIO_Pin_8;
GPIO_Init(GPIOB, &GPIO_InitStructer);
/*ECOH回响信号*/
GPIO_InitStructer.GPIO_Mode=GPIO_Mode_IN_FLOATING;
GPIO_InitStructer.GPIO_Pin=GPIO_Pin_9;
GPIO_Init(GPIOB, & GPIO_InitStructer);
/*定时器TIM2初始化*/
TIM_DeInit(TIM4);
TIM_TimeBaseInitStructer.TIM_Period=999;//定时周期为1000
TIM_TimeBaseInitStructer.TIM_Prescaler=71; //分频系数72
TIM_TimeBaseInitStructer.TIM_ClockDivision=TIM_CKD_DIV1;//不分频
TIM_TimeBaseInitStructer.TIM_CounterMode=TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM4,&TIM_TimeBaseInitStructer);
TIM_ITConfig(TIM4,TIM_IT_Update,ENABLE);//开启更新中断
NVIC_Config();
TIM_Cmd(TIM4,DISABLE);//关闭定时器使能
}
float Senor_Using(void)
{
float length=0,sum=0;
u16 tim;
uint i=0;
/*测5次数据计算一次平均值*/
while(i!=5)
{
PBout(8)=1; //拉高信号,做为触发信号
delay_us(20); //高电平信号超过10us
PBout(8)=0;
/*等待回响信号*/
while(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_9)==RESET);
TIM_Cmd(TIM4,ENABLE);//回响信号到来,开启定时器计数
i+=1; //每收到一次回响信号+1,收到5次就计算均值
while(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_9)==SET);//回响信号消失
TIM_Cmd(TIM4,DISABLE);//关闭定时器
tim=TIM_GetCounter(TIM4);//获取计TIM4数寄存器中的计数值,一边计算回响信号时间
length=(tim+overcount*1000)/58.0;//经过回响信号计算距离
sum=length+sum;
TIM4-》CNT=0; //将TIM4计数寄存器的计数值清零
overcount=0; //中断溢出次数清零
delay_ms(10);
}
length=sum/5;
return length;//距离做为函数返回值
}
void TIM4_IRQHandler(void) //中断,当回响信号很长是,计数值溢出后重复计数,用中断来保存溢出次数
{
if(TIM_GetITStatus(TIM4,TIM_IT_Update)!=RESET)
{
TIM_ClearITPendingBit(TIM4,TIM_IT_Update);//清除中断标志
overcount++;
}
}
然后下面是cs.h文件:
#ifndef __CS_H
#define __CS_H
#include “stm32f10x.h”
#include “delay.h”
#include “sys.h”
#define uint unsigned int
#define TRIG_Send PBout(8)
#define ECHO_Reci PBin(9)
void CH_SR04_Init(void); //超声波模块相关配置初始化
float Senor_Using(void); //测距函数,返回值即为距离
void NVIC_Config(void); //中断配置
#endif
下面简单介绍怎么使用:
(1):引脚怎么接?
PB8接Trig口,PB9接Echo口。
(2):怎么用?
主函数中调用CH_SR04_Init()这一个函数即可初始化测距的相关配置,
测距直接用调用Senor_Using()这个函数,函数的返回值即为距离。
其它关于代码的设置我就不讲了,注释里已经很详细了
(3):使用例子
我用了oled屏幕显示,这段程序在我上传的代码里没有,仅仅用来测试的而已,但是我上传的代码已经将oled初始化的代码在主函数里调用了,所以在主函数也可以直接用oled屏幕。
下面是我的测试程序:
//头文件
#include “stm32f10x.h”
#include “led.h”
#include “moter.h”
#include “xunji.h”
#include “sg90.h”
#include “delay.h”
#include “sys.h”
#include “oled.h”
#include “bmp.h”
#include “cs.h”
int main(void)
{
char str[20]; //用来存放浮点数字符
float length_res[5]; //用来存放测距结果
SystemInit(); // 配置系统时钟为72M
delay_init(); //延时初始化
xunji_config(); //循迹初始化
TIM3_PWM_Init(); //电机pwm TIM3
SG90_pwm_init(); //舵机pwm TIM2
CH_SR04_Init(); //超声波定时器 TIM4
OLED_Init(); //oled显示初始化
while(1)
{
SG90_Front; //舵机摆正
length_res[0] =Senor_Using(); //测前方距离放在数组里
num2char(str,length_res[0],3,3); //将浮点数转为字符
OLED_ShowString(44,24,str,16);
OLED_ShowString(0,2,“distance is”,16);
OLED_Refresh();
}
}
效果是这样的
好了,测距已经准备就绪了,下面就可以开始写避障函数啦。
九,完成避障小车的制作
这里的任务主要就是编写主函数里的循环了。
设计思路如下:
1,舵机向前摆正,测量正前方的距离,如果距离小于30cm就停下来。
2,停下后,舵机检测左边45度和右边45度的距离,比较这两个距离。
3,假如左边的距离比右边大,就用一个do-while循环,使舵机摆正不断测量前方距离,同时小车缓慢左转,一直转到前方距离大于30cm,小车继续向前,循环继续。
下面是主函数:
int main(void)
{
char str[20]; //用来存放浮点数字符
float length_res[5]; //用来存放测距结果
SystemInit(); // 配置系统时钟为72M
delay_init(); //延时初始化
xunji_config(); //循迹初始化
TIM3_PWM_Init(); //电机pwm TIM3
SG90_pwm_init(); //舵机pwm TIM2
CH_SR04_Init(); //超声波定时器 TIM4
OLED_Init(); //oled显示初始化
while(1)
{
SG90_Front; //舵机摆正
delay_ms(100);
length_res[0] =Senor_Using(); //测前方距离放在数组里
delay_ms(100);
if(length_res[0]》30.00) //若是前方距离大于30cm 前进
{
CarGo();
}
if(length_res[0]《30.00) //若是前方距离小于30厘米 停车测左右距离
{
CarStop();
SG90_Left_45; //舵机左转45度测距
delay_ms(700);
length_res[1] =Senor_Using(); //把测量结果放进数组
SG90_Right_45; //舵机右转45度测距
delay_ms(700);
length_res[4] =Senor_Using(); //把测量结果放进数组
SG90_Front; //舵机摆正
delay_ms(100);
if(length_res[1]》length_res[4]) //若是左边的距离大于右边的距离
{
do //舵机摆正
{
SG90_Front;
delay_ms(10);
length_res[0] =Senor_Using(); //重复测前方的距离同时左转
delay_ms(10);
CarLeft();
}
while(length_res[0]《30.00); //一直转到前方距离大于30cm
}
if(length_res[1]《length_res[4]) //若是右边的距离大于左边的距离
{
do
{
SG90_Front;
delay_ms(10);
length_res[0] =Senor_Using(); //重复测前方的距离同时右转
delay_ms(10);
CarRight();
}
while(length_res[0]《30.00); //一直转到前方距离大于30cm
}
}
}
}
好了,到此为止,循迹避障小车已经设计完成了。
下面总结一下如何接线的:
电机驱动:
A6----IN1
A7----IN2
B0----IN3
B1----IN4
循迹模块:(从左到右为1234)
B4----第1个循迹模块的D0
B5----第2个循迹模块的D0
B6----第3个循迹模块的D0
B7----第4个循迹模块的D0
舵机:
A1----舵机橙色线
超声波模块:
PB8----Trig
PB9----Echo
oled(iic协议):
A8----SCL
A9----SDA
当然循迹和避障的策略都是我自己为了完成任务写的,比较简单,能够实现循迹避障功能,到后面你已经会操作各个模块后,自己写个更好的循迹避障策略是完全没有问题的,或者是用我的工程,里面的各模块函数也都写好了,直接调用就行。
举报