完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
上篇主要是讲一些基础的东西,中篇讲了如何制作循迹,本篇讲一下制作避障小车。
七,如何用使用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 当然循迹和避障的策略都是我自己为了完成任务写的,比较简单,能够实现循迹避障功能,到后面你已经会操作各个模块后,自己写个更好的循迹避障策略是完全没有问题的,或者是用我的工程,里面的各模块函数也都写好了,直接调用就行。 |
|
|
|
只有小组成员才能发言,加入小组>>
调试STM32H750的FMC总线读写PSRAM遇到的问题求解?
1617 浏览 1 评论
X-NUCLEO-IHM08M1板文档中输出电流为15Arms,15Arms是怎么得出来的呢?
1543 浏览 1 评论
977 浏览 2 评论
STM32F030F4 HSI时钟温度测试过不去是怎么回事?
683 浏览 2 评论
ST25R3916能否对ISO15693的标签芯片进行分区域写密码?
1595 浏览 2 评论
1863浏览 9评论
STM32仿真器是选择ST-LINK还是选择J-LINK?各有什么优势啊?
644浏览 4评论
STM32F0_TIM2输出pwm2后OLED变暗或者系统重启是怎么回事?
515浏览 3评论
531浏览 3评论
stm32cubemx生成mdk-arm v4项目文件无法打开是什么原因导致的?
504浏览 3评论
小黑屋| 手机版| Archiver| 电子发烧友 ( 湘ICP备2023018690号 )
GMT+8, 2024-11-22 05:12 , Processed in 0.740757 second(s), Total 80, Slave 62 queries .
Powered by 电子发烧友网
© 2015 bbs.elecfans.com
关注我们的微信
下载发烧友APP
电子发烧友观察
版权所有 © 湖南华秋数字科技有限公司
电子发烧友 (电路图) 湘公网安备 43011202000918 号 电信与信息服务业务经营许可证:合字B2-20210191 工商网监 湘ICP备2023018690号