基于STM32F103C8T6的循迹避障小车完整制作过程(详细) |
您所在的位置:网站首页 › scratch避障小车程序 › 基于STM32F103C8T6的循迹避障小车完整制作过程(详细) |
接中篇
基于STM32F103C8T6的循迹避障小车完整制作过程(详细)----中篇(第456点) 上篇主要是讲一些基础的东西,中篇讲了如何制作循迹,本篇讲一下制作避障小车。 七,如何用使用pwm让舵机旋转到相应的角度这位博主讲的很详细: stm32 智能避障小车(二)之sg90 我对他其中的一些关键信息再说明一下: 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,先来看看舵机和云台是怎么接在一起的?
我是参考的这位博主的介绍: STM32的超声波测距程序 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()这个函数,函数的返回值即为距离。 其它关于代码的设置我就不讲了,注释里已经很详细了,或者去看一下这个博客 STM32的超声波测距程序 (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] do //舵机摆正 { SG90_Front; delay_ms(10); length_res[0] =Senor_Using(); //重复测前方的距离同时左转 delay_ms(10); CarLeft(); } while(length_res[0] SG90_Front; delay_ms(10); length_res[0] =Senor_Using(); //重复测前方的距离同时右转 delay_ms(10); CarRight(); } while(length_res[0] |
今日新闻 |
推荐新闻 |
CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3 |