STM32F103RCT6 实验代码之舵机+超声波避障小车(二)舵机+超声波

舵机+超声波简介

补充一下上一篇博客遗漏掉的一个问题,一般电机的PWM都是有一些频率限制的,而我的直流电机是10Khz来进行驱动的。

一.SG90舵机

在这里插入图片描述
这个是我买的舵机+超声波模块,这个舵机是SG90模拟舵机,网上有很多的资料
模拟电机与数字电机的区别
SG90的驱动是靠不同占空比的50hz的PWM波来控制
0度——>2.5%
45度——>5%
90度——>7.5%
135度——>10%
180度——>12.5%
不同的占空比对应转过不同的角度
我在网上找了一些文章,大概有两种实现操控舵机的方法,第一种是直接输入不同的占空比来控制转过的角度,第二种是连续改变占空比来改变角度,首先我来初始化一下PWM波:

#include "sg90.h"


//PWM输出初始化
//arr:自动重装值
//psc:时钟预分频数
void sg90_Init(void)
{  
	 GPIO_InitTypeDef GPIO_InitStructure;
	TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
	TIM_OCInitTypeDef  TIM_OCInitStructure;

	RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1, ENABLE);// 
 	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA , ENABLE);  //使能GPIO外设时钟使能
	                                                                     	

   //设置该引脚为复用输出功能,输出TIM1 CH2的PWM脉冲波形
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11; //TIM_CH2
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;  //复用推挽输出
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);

	
	TIM_TimeBaseStructure.TIM_Period = 199; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值	 80K
	TIM_TimeBaseStructure.TIM_Prescaler =7199; //设置用来作为TIMx时钟频率除数的预分频值  不分频
	TIM_TimeBaseStructure.TIM_ClockDivision = 0; //设置时钟分割:TDTS = Tck_tim
	TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;  //TIM向上计数模式
	TIM_TimeBaseInit(TIM1, &TIM_TimeBaseStructure); //根据TIM_TimeBaseInitStruct中指定的参数初始化TIMx的时间基数单位

 
	TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1 ; //选择定时器模式:TIM脉冲宽度调制模式2
	TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //比较输出使能
	TIM_OCInitStructure.TIM_Pulse = 0; //设置待装入捕获比较寄存器的脉冲值
	TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low; //输出极性:TIM输出比较极性高
	TIM_OC4Init(TIM1, &TIM_OCInitStructure);  //根据TIM_OCInitStruct中指定的参数初始化外设TIMx

  TIM_CtrlPWMOutputs(TIM1,ENABLE);	//MOE 主输出使能	

	TIM_OC4PreloadConfig(TIM1, TIM_OCPreload_Enable);  //CH3预装载使能	 
	
	TIM_ARRPreloadConfig(TIM1, ENABLE); //使能TIMx在ARR上的预装载寄存器
	
	TIM_Cmd(TIM1, ENABLE);  //使能TIM1
 
   
}

接下来是两种不同的控制方法:
第一种:
在.h文件里宏定义不同占空比的PWM波

#ifndef __SG90_H
#define __SG90_H
#include "sys.h"

#define   SG90_Right_90     TIM_SetCompare4(TIM1, 195)		
#define   SG90_Right_45		TIM_SetCompare4(TIM1, 190)		
#define   SG90_Front		TIM_SetCompare4(TIM1, 185)		
#define   SG90_Left_45		TIM_SetCompare4(TIM1, 180)		
#define   SG90_Left_90		TIM_SetCompare4(TIM1, 175)

void sg90_Init(void);

#endif

之后再main函数里进行操作:

while(1)
	{
		
		SG90_Left_90;
		delay_ms(700);
		SG90_Left_45;
		delay_ms(700);
		SG90_Front;
		delay_ms(700);
		SG90_Right_45;
		delay_ms(700);
		SG90_Right_90;
		delay_ms(700);
	}

延时时间需要自己调整
第二种:
需要一个for语句来实现
这种方法主要是通过一点点改变占空比来使舵机转动

int i;
for(i=175;i<195;i++)
{
	TIM_SetCompare4(TIM1, i);
	delay_ms(50);
}

小结一下:
①两种方法均可使舵机转动,如果采用固定角度测距的话推荐第一种,但是在实验过程中发现第二种似乎更稳定一些,这个结论没有仔细推敲。可以根据自己的实验来选择。
②舵机操作的本质就是控制不同占空比的PWM波,其实并不难,重点注意一下延时函数的设置,不恰当的延时函数会导致舵机转过角度的不理想

二.超声波数据处理

超声波的使用我其他的博客提到过,有两种思路。
第一种就是根据HC-SR04的使用说明,在GPIO口检测高电平是开启定时器,低电平再关闭,可以根据这个思路自己写一个函数。
第二种就是采用正点原子的输入捕获例程更改,我这里用的是第二种方法。

我们采用超声波测距的方法来进行判断车的移动,这里我参考了其他人的博客
博客传送门
这里把代码贴出来:

#include "gpio.h"
#include "sys.h"
#include "delay.h"
#include "pwm.h"
#include "pwm2.h"
#include "usart2.h"
#include "timer.h"
#include "sg90.h"

float dis;
float disleft;
float disright;
extern u8  TIM4CH3_CAPTURE_STA;		//输入捕获状态		    				
extern u16	TIM4CH3_CAPTURE_VAL;	//输入捕获值
int main()
{
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);// 设置中断优先级分组2
	My_USART2_Init();
		sg90_Init();
	
	delay_init();
	gpio_Init();
	TIM3_PWM3_Init(7199,0);//TIM3 输出PWM
	TIM3_PWM4_Init(7199,0);//TIM3 输出PWM
		
		TIM_SetCompare3(TIM3,2400);//左轮电机转速
		TIM_SetCompare4(TIM3,2530);//右轮电机转速
	while(1)
	{
		dis=count_length();
		SG90_Front;
		delay_ms(700);
		if(dis>60.00)
		{
		front();
		
		}
		else
		{
			stop();
		SG90_Right_45;
		delay_ms(900);
		disright=count_length();
			printf("right:%f cm\r\n",disright);
		SG90_Left_45;
		delay_ms(900);
		disleft=count_length();
			printf("left:%f cm\r\n",disleft);
			SG90_Front;
			delay_ms(900);
			if(disleft>=disright)
			{
				do                        
					{
					
					delay_ms(10);
					dis=count_length();	//重复测前方的距离同时左转
					delay_ms(10);						
					left();
					}
					while(dis<30.00);		//一直转到前方距离大于30cm	
				}
			if(disleft<disright)
			{
				do                        
					{
					
					delay_ms(10);
					dis=count_length();	//重复测前方的距离同时左转
					delay_ms(10);						
					right();
					}
					while(dis<30.00);		//一直转到前方距离大于30cm	
				}
		}
		
	}



}
		
		
		

	








这是main函数的代码,主体思路是检测前方距离是否小于规定距离,如果大于就前进,如果小于就左右测距比较大小,之后舵机摆正向距离大的方向转直到前方距离再次大于规定距离。
上一篇博客可以让小车运动起来,这一篇可以根据前方的距离来控制小车的运动。到这里就可以基本实现了 舵机+超声波避障的小车的功能,但是在制作过程中出现了一个问题就是无法正确的控制舵机,舵机的左右转动总是不按我的想法来,而且看上去是执行好几遍while才会让舵机转动起来,这里大概有几种原因:
①功率不太够,最好不要采用单片机的的5V输出有些不太稳定,我这里采用的是L298N的5V输出来给舵机供电。
②注意中断,有可能是中断在某个位置循环不太能出来
③注意一下舵机,我的舵机有些瑕疵手动转动某些地方会有一些卡顿,注意这种情况也许会有影响,而且这个问题比较隐蔽
④延时函数的时间配值,我觉得延时函数的影响还是存在的

		dis=count_length();
		SG90_Front;
		delay_ms(700);

下图是大概的连线图,标注了单片机信号的方向:
连线图
关于蓝牙部分之后补充

原创文章 5 获赞 8 访问量 1764

猜你喜欢

转载自blog.csdn.net/ljw__/article/details/105617069