STM32F103C8T6通过超声波控制舵机(超声波测距串口输出,通用定时器中断,PWM波控制舵机,系统定时器延时)

        本次我们使用到了STM32F103C8T6最小系统板作为我们的主控板,通过超声波模块感应接收距离来控制舵机的旋转,可用于超声波感应来控制垃圾桶开盖。

        这次我们需要用到的包括了超声波模块HC-SR04,舵机SG90以及我们的STM32的最小系统板。

       一.首先是超声波模块的使用,如图所示超声波模块共有4个引脚分别是VCC,Trig,Echo,GND。在接线上,VCC,GND分别对应接在板子的电源和地上,Trig接在PB11,Echo接在PB10上。

        上面为超声波工作的时序图。超声波工作时超声波模块的触发角(Trig)输入至少10us以上的高电位,即可发送超声波,在触发角(Trig)发送超声波到“响应”角(Echo)接收到传回的超声波这段时间内,“响应”角(Echo)位始终呈现出一个高电平的状态,因为如此,可以从“响应”角(Echo)持续高电平脉冲的时间来换算出被测物的距离。

超声波测距步骤为

1.配置GPIO引脚结构体(Trig,Echo)。

2.配置定时器结构体

3.配置定时器NVIC中断结构体(这是用到了一个TIM4的中断来测定“响应”角(Echo)持续高电平脉冲的时间)。

4.开启时钟(定时器,GPIO)

5.Trig引脚输出高电平(至少10us),然后关闭。

6.等待“响应”角(Echo)输入高电平开始,定时器打开-开始定时器计数

7.等待“响应”角(Echo)输入高电平结束(即为低电平),定时器关闭-关闭定时器计数

最后我们就可以通过公式换算得到超声波感应的距离了。

即距离 = 高电平持续时间 * 声速(340m/s)/2;(这里除以2是因为超声波的传输是发送后接触到物体后反弹,故实际距离应是超声波的传输距离除以2)。

接下来是超声波函数的代码编写,首先我们需要在HC_SR04.h中宏定义一个Trig发送函数和一个Echo接收函数。方便我们调用。我们还用到了系统定时器SysTick.h。

SysTick.h

#include "SysTick.h"
#include "stm32f10x.h"

void ms_delay(uint32_t ms)
{
		uint32_t i;
		SysTick_Config(72000);
		for(i=0;i<ms;i++)
		{
			
			while (!((SysTick->CTRL)&(1<<16)) );
			
		}
		SysTick->CTRL &= ~SysTick_CTRL_ENABLE_Msk;
}

void us_delay(uint32_t us)
{
		uint32_t i;
		SysTick_Config(72);
		for(i=0;i<us;i++)
		{
			
			while (!((SysTick->CTRL)&(1<<16)) );
			
		}
		SysTick->CTRL &= ~SysTick_CTRL_ENABLE_Msk;
}

HC_SR04.h

#ifndef __HC_SR04_H	//条件编译,防止重复编译
#define __HC_SR04_H

#include "stm32f10x.h"
void HC_SR04_config(void);
void Open_TIM4(void);
void Close_TIM4(void);
int GetEcho_time(void);
float Getlength(void);
//Echo读取引脚电平状态
#define ECHO_Receive	GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_10)
//Trig发送设置高低电平,这里每一句末尾的“\”起到行与行之间的连接作用
#define TRIG_Send(a) 	if(a)\
						GPIO_SetBits(GPIOB, GPIO_Pin_11);\
						else\
						GPIO_ResetBits(GPIOB, GPIO_Pin_11)

#endif

HC_SR04.c 

#include "HC_SR04.h"
#include "stm32f10x.h"
#include "SysTick.h"

uint16_t mscount;//记录中断次数

void HC_SR04_config(void)
{
	GPIO_InitTypeDef GPIO_hcsr04init;
	TIM_TimeBaseInitTypeDef TIM_hcsr04init;
	NVIC_InitTypeDef NVIC_hcsr04init;
	
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4,ENABLE);
	NVIC_PriorityGroupConfig( NVIC_PriorityGroup_1);

	//Trig PB11
	GPIO_hcsr04init.GPIO_Mode = GPIO_Mode_Out_PP;
	GPIO_hcsr04init.GPIO_Pin = GPIO_Pin_11;
	GPIO_hcsr04init.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOB, &GPIO_hcsr04init);

	//Echo PB10
	GPIO_hcsr04init.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空输入模式(用于默认值不确定时)
	GPIO_hcsr04init.GPIO_Pin = GPIO_Pin_10;
	GPIO_Init(GPIOB, &GPIO_hcsr04init);

	TIM_hcsr04init.TIM_ClockDivision = TIM_CKD_DIV1;//不分频
	TIM_hcsr04init.TIM_CounterMode = TIM_CounterMode_Up;//设置为向上计数模式
	TIM_hcsr04init.TIM_Period = 1000-1;
	TIM_hcsr04init.TIM_Prescaler = 72-1;
	TIM_TimeBaseInit(TIM4, &TIM_hcsr04init);
	TIM_ITConfig(TIM4,TIM_IT_Update,ENABLE);//定时器中断配置,TIM_IT_Update为允许溢出,更新标志位
	TIM_Cmd( TIM4,DISABLE);//先关闭定时器,Trig触发后打开
    //NVIC中断控制器(TIM4)
	NVIC_hcsr04init.NVIC_IRQChannel = TIM4_IRQn;//TIM4通道,设置中断源
	NVIC_hcsr04init.NVIC_IRQChannelPreemptionPriority = 0;//抢占优先级
	NVIC_hcsr04init.NVIC_IRQChannelSubPriority = 0;//子优先级
	NVIC_hcsr04init.NVIC_IRQChannelCmd = ENABLE;//使能
	NVIC_Init(&NVIC_hcsr04init);

}

//打开定时器4
void Open_TIM4(void)
{
	TIM_SetCounter(TIM4,0);//开启定时器计数,从0开始
	mscount = 0;//设置中断次数初始为0
	TIM_Cmd( TIM4,ENABLE);//使能,打开定时器4

	
}

//关闭定时器4
void Close_TIM4(void)
{
		TIM_Cmd( TIM4,DISABLE);//失能,关闭定时器

	
	
}

void TIM4_IRQHandler(void)
{
	if( TIM_GetITStatus( TIM4, TIM_IT_Update ) != RESET)//假如发生中断
	{
		
			TIM_ClearITPendingBit( TIM4, TIM_IT_Update);//清除中断标志位
			mscount++;//每发生一次中断加1,定时器中断一次为1ms
	}

	
}

//获取Echo角的高电平脉冲持续时间
int GetEcho_time(void)
{
	uint32_t t=0;
	t = mscount*1000;//将ms*1000换算为us
	t +=  TIM_GetCounter(TIM4);//获取不足一次中断的时间
	TIM4->CNT = 0;
	ms_delay(50);
	
	return t;//返回时间t
}

//获得超声波测距距离
float Getlength()
{
	int i =0;
	uint32_t t =0;
	float length =0;
	float sum =0;
	
	for(i=0;i<5;i++)//计算5次提高运算精度
	{
			TRIG_Send(1);//拉高触发角电平
			us_delay(20);//延时至少10us
			TRIG_Send(0);//拉低触发角电平
			while(ECHO_Receive ==0);    //注意这里要加分号,这里是一个跳电平的操作,只有满足条件才能向下运行
				Open_TIM4();//打开定时器4,开始计数
			while(ECHO_Receive ==1);    //同上
				Close_TIM4();//关闭定时器4,结束计数
			t = GetEcho_time();//获取“响应”角(Echo)持续高电平脉冲的时间t
			length = ((float)t/58.0);//固定计算超生波距离公式:时间(us)/58.0
			sum=sum+length;//获取5次超声波测距的总和
	}
	length = sum/5.0;//sum/5获取超声波所测距离长度
	return length;//返回长度

2.接下来配置Usart.c使得通过超声波测距显示的距离可以打印在串口助手上。如图所示

 Usart.c

#include "stm32f10x.h"
#include "usart.h"

void usart_init(void)
{
	
	GPIO_InitTypeDef gpioinitStructure;
	USART_InitTypeDef usart_initStructure;
	NVIC_InitTypeDef NVICinitStructure;
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);

	
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);

	
	//1.配置PA9 TX
	gpioinitStructure.GPIO_Mode=GPIO_Mode_AF_PP;
	gpioinitStructure.GPIO_Pin=GPIO_Pin_9;
	gpioinitStructure.GPIO_Speed=GPIO_Speed_50MHz;
	
	GPIO_Init(GPIOA,&gpioinitStructure );
	//2.PA10 RX	
	gpioinitStructure.GPIO_Mode=GPIO_Mode_IN_FLOATING;
	gpioinitStructure.GPIO_Pin=GPIO_Pin_10;
	
	GPIO_Init(GPIOA,&gpioinitStructure );
	
	
	usart_initStructure.USART_BaudRate=115200;
	usart_initStructure.USART_HardwareFlowControl=USART_HardwareFlowControl_None;
	usart_initStructure.USART_Mode=USART_Mode_Rx | USART_Mode_Tx;
	usart_initStructure.USART_Parity=USART_Parity_No;
	usart_initStructure.USART_StopBits=USART_StopBits_1;
	usart_initStructure.USART_WordLength=USART_WordLength_8b;
	
	USART_Init(USART1, &usart_initStructure);
	USART_ITConfig(USART1, USART_IT_RXNE , ENABLE );

  USART_Cmd(USART1, ENABLE);

	NVICinitStructure.NVIC_IRQChannel = USART1_IRQn;
	NVICinitStructure.NVIC_IRQChannelPreemptionPriority = 1;
	NVICinitStructure.NVIC_IRQChannelSubPriority = 1;
	NVICinitStructure.NVIC_IRQChannelCmd = ENABLE;
	
	NVIC_Init(&NVICinitStructure);

	
}
//发送字符
void USART_SendByte(USART_TypeDef* USARTx, uint16_t Data)
{
	USART_SendData(USARTx,Data);
	while( USART_GetFlagStatus(USARTx, USART_FLAG_TXE) == RESET);


	
	
}
//发送字符串
void  USART_SendStr(USART_TypeDef* USARTx, char *str)
{
	uint16_t i = 0;
	do{
		USART_SendByte(USARTx,*(str+i));
		i++;
		
	}while(*(str+i) !='\0');
		
	
	while( USART_GetFlagStatus(USARTx, USART_FLAG_TC) == RESET);

}

int fputc(int ch, FILE *f)
{
	USART_SendData(USART1,(uint8_t)ch);
	while( USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);

	return (ch);
	
}

int fgetc(FILE *f)
{
	while( USART_GetFlagStatus(USART1, USART_FLAG_RXNE) == RESET);
	
	return (int) USART_ReceiveData(USART1);

	
}




 3.后面是舵机的使用,其中涉及到PWM波的使用。我们通过配置motor.c来实现舵机的使用,如图棕色为负极线,红色为正极线,橙色为信号线,分别接在地,电源以及我们设置的PB5上。

motor.c

#include "motor.h"
#include "stm32f10x.h"

void motor_config(void)
{
	GPIO_InitTypeDef GPIO_Motorinit;
	TIM_TimeBaseInitTypeDef TIM_Motorinit;
	TIM_OCInitTypeDef TIMPWM_Motorinit;
	
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
	
	GPIO_PinRemapConfig(GPIO_PartialRemap_TIM3, ENABLE);//部分重映射时钟 TIM3

	
	GPIO_Motorinit.GPIO_Mode  = GPIO_Mode_AF_PP;//设置为推挽复用
	GPIO_Motorinit.GPIO_Pin   = GPIO_Pin_5;
	GPIO_Motorinit.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init( GPIOB, &GPIO_Motorinit);
	
	TIM_Motorinit.TIM_ClockDivision = TIM_CKD_DIV1;       //不分频
	TIM_Motorinit.TIM_CounterMode   = TIM_CounterMode_Up; //设置向上计数模式
	TIM_Motorinit.TIM_Period        = 200-1;			  //设置在下一个更新事件装入活动的自动重装载值
	TIM_Motorinit.TIM_Prescaler     = 7200-1;			  //时钟频率预分频值
	TIM_TimeBaseInit(TIM3,&TIM_Motorinit);

	TIMPWM_Motorinit.TIM_OCMode = TIM_OCMode_PWM1;		  //选择定时器模式1
	TIMPWM_Motorinit.TIM_OutputState = TIM_OutputState_Enable;  //比较输出极性
	TIMPWM_Motorinit.TIM_OCPolarity = TIM_OCPolarity_Low; 	//选择有效输出极性,设置低为有效,即CNT < CCR比较值时为有效
	TIM_OC2Init(TIM3, &TIMPWM_Motorinit);
	TIM_OC2PreloadConfig(TIM3, TIM_OCPreload_Enable);
	TIM_Cmd( TIM3, ENABLE);
	
	
	
}

3,最后就是main函数,其中pwmval的取值,分别对应着舵机旋转角度的大小。

#include "stm32f10x.h"
#include "main.h"
#include "usart.h"
#include "motor.h"
#include "Systick.h"
#include "HC_SR04.h"

void delay(uint16_t time)
{
	uint16_t i=0;
	while(time--)
	{
		i=12000;
		while(i--);
	}
}


int  main()
{	
	int pwmval = 195;//设置比较值初始为195
	
	float Length = 0;
	HC_SR04_config();
	usart_init();
    motor_config();
	 



	
	while(1)
	 {
			Length = Getlength();
			printf("%.3f\r\n",Length);//将超声波距离打印到串口上
			ms_delay(500);
		 
		 if(Length < 5)
		 {
				for(pwmval = 195;pwmval >= 155;pwmval-=15)//周期为200,其中pwmval = 175 180 185 190 195时分别对应舵机旋转角度180° 135° 90° 45° 0°
			 {
					TIM_SetCompare2(TIM3, pwmval);//设置pwmval为比较值
					delay(1000);
				 
			 }
			 
			 
		 }
		 else if(Length > 5)
		 {
			 		TIM_SetCompare2(TIM3, pwmval -20);

			 
		 }
		 
   }
 }

最后,我们就可以实现通过超声波测算距离来控制舵机旋转啦。

物联沃分享整理
物联沃-IOTWORD物联网 » STM32F103C8T6通过超声波控制舵机(超声波测距串口输出,通用定时器中断,PWM波控制舵机,系统定时器延时)

发表评论