【车辆行程系统】:基于Stm32F103VET6的车辆辅助系统(直流电机测速、中科微GPS测试、红外遥控按键输入开机密码设置、A4950&TB6612驱动编写、超声波撞车报警)

主控芯片
车模,仅供参考!

 项目目标:

        最近写了个车辆的辅助系统,现将系统几个主要部分做一个刨析记录,希望对有同等记录的同学,有个帮助!

        基于Stm32F103VET6的车辆辅助系统,主要分为以下几个部分:

  1. 直流电机控制(A4950&TB6612驱动)
  2. 直流电机测速(A4950&TB6612驱动)
  3. 中科微GPS测试
  4. 超声波撞车报警
  5. 开机密码设置(红外遥控按键输入

主控板子用的是野火家的

        资料链接:野火产品资料下载中心 — 野火产品资料下载中心 文档 (embedfire.com)


(红外遥控按键输入)开机密码设置:

           

        使用的红外遥控、主控板(野火stm32F103vet6,目前此板子已经更新换代,没有这么老板子了,有部分缺陷,不方便指出,不建议大家使用,建议大家使用最新板子,连客服都说已经没有这种老板子的资料了)

        本文编写的是一个六位数的密码输入(类似于一个密码锁),当密码输入正确,进入主页面,密码输入错误时,文字飘红警告,且可以重新输入密码。

        红外遥控作为输入设备,以下提供一点思路代码,无法将整个工程贴给大家,所以贴部分代码

/* 密码 输入*/


void Bicycle_EnterLCD(void)
{
	static int key_E = 0,a = 0,sum = 0,key_b = 0,key_c = 0,count = 0;
	static int i;
	static int Ent_b = 0;
	static char dispBuff[100];
	unsigned char pswd[6] = {"123456"};
	char in_pswd[6] = {"000000"};
	int Ment = 1; //判断是否密码正确 1-正确 0-错误
	
	while(1)
	{
		key_E = Inf_KeyScan();
		switch(key_E)
		{
			case 0: key_b = 0;break;
			case 1: key_b = 1;break;
			case 2: key_b = 2;break;
			case 3: key_b = 3;break;
			case 4: key_b = 4;break;
			case 5: key_b = 5;break;
			case 6: key_b = 6;break;
			case 7: key_b = 7;break;
			case 8: key_b = 8;break;
			case 9: key_b = 9;break;
			default: key_b = 100;break;
		}
		if( key_E == 11)
			key_c = 11;
		if(key_b >= 0  &&  key_b<= 9 && key_c == 11)
		{
			a = key_b;
			switch(Ent_b)
			{
				case 0:a*=100000;ILI9341_DispStringLine_EN_CH(LINE(5),"           Pswd:*         ");break;
				case 1:a*=10000;ILI9341_DispStringLine_EN_CH(LINE(5),"           Pswd:**         ");break;
				case 2:a*=1000;ILI9341_DispStringLine_EN_CH(LINE(5),"           Pswd:***       ");break;
				case 3:a*=100;ILI9341_DispStringLine_EN_CH(LINE(5),"           Pswd:****      ");break;
				case 4:a*=10;ILI9341_DispStringLine_EN_CH(LINE(5),"           Pswd:*****     ");break;
				case 5:a*=1;ILI9341_DispStringLine_EN_CH(LINE(5),"           Pswd:******    ");break;
			}
			sum += a;
			sprintf(in_pswd,"%d",sum);
			Ent_b+=1;
			a = 0;
			key_c = 0;
			if(Ent_b == 6)
			{
				for(i=0;i<6;i++)
				{
					if(in_pswd[i] == pswd[i])
					{
						count = 1;
					}
					else
					{
						count = 2;
					}
				}
				if(count == 1)
				{
					ILI9341_DispStringLine_EN_CH(LINE(10),"                                       ");
					ILI9341_DispStringLine_EN_CH(LINE(10),"   Congratulations! Correct password!");
					Car_Flag = 3;
					delay_ms(1500);
					LCD_SetColors(WHITE,BLACK);
					ILI9341_Clear(0,0,LCD_X_LENGTH,LCD_Y_LENGTH);	/* 清屏,显示全黑 */						
					break;
				}
				if(count == 2)
				{
					LCD_SetTextColor(RED);
					ILI9341_DispStringLine_EN_CH(LINE(10),"                                       ");
					ILI9341_DispStringLine_EN_CH(LINE(10),"       What a pity! Wrong password!");
					delay_ms(1500);
					Ent_b = 0;
					count = 0;
					sum = 0;
					for (i = 0; i < strlen(in_pswd); i++)
						in_pswd[i] = '\0' ; 
					LCD_SetColors(WHITE,BLACK);
					ILI9341_Clear(0,0,LCD_X_LENGTH,LCD_Y_LENGTH);	/* 清屏,显示全黑 */	
					Bicyle_TwoLCD();
					Bicycle_EnterLCD();
				}
			}
		}
	if(count == 0)
	{
		/* 提醒 用户输入密码 */
		LCD_SetColors(MAGENTA,BLACK);
		LCD_SetFont(&Font8x16);
		ILI9341_DispStringLine_EN_CH(LINE(10),"   Please enter Pswd -> Main Page!!");	
	}
		LCD_SetTextColor(GREEN);
		sprintf(dispBuff,"%d ",key_E);
		LCD_ClearLine(LINE(0));
		ILI9341_DispStringLine_EN_CH(LINE(0),dispBuff);
	}

}

红外遥控程序(部分程序,占主要部分,有参考价值):

#include "stdio.h"
#include "./IrDa/bsp_irda.h" 
#include "./systick/bsp_SysTick.h"
#include "./led/bsp_led.h"

uint32_t frame_data=0;    /* 一帧数据缓存 */
uint8_t  frame_cnt=0;
uint8_t  frame_flag=0;    /* 一帧数据接收完成标志 */

 /**
  * @brief  配置嵌套向量中断控制器NVIC
  * @param  无
  * @retval 无
  */
static void NVIC_Configuration(void)
{
  NVIC_InitTypeDef NVIC_InitStructure;
  
  /* Configure one bit for preemption priority */
  NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);
  
  /* 配置P[A|B|C|D|E]11为中断源 */
  NVIC_InitStructure.NVIC_IRQChannel = IRDA_EXTI_IRQN;
  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 5;
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  NVIC_Init(&NVIC_InitStructure);
}

 
/* 初始化红外接收头1838用到的IO */
void IrDa_Init(void)
{
	GPIO_InitTypeDef GPIO_InitStructure; 
	EXTI_InitTypeDef EXTI_InitStructure;

	/* config the extiline clock and AFIO clock */
	RCC_APB2PeriphClockCmd(IRDA_GPIO_CLK | RCC_APB2Periph_AFIO,ENABLE);
												
	/* config the NVIC */
	NVIC_Configuration();

	/* EXTI line gpio config */	
  GPIO_InitStructure.GPIO_Pin = IRDA_GPIO_PIN;       
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;	 
  GPIO_Init(IRDA_GPIO_PORT, &GPIO_InitStructure);

	/* EXTI line mode config */
  GPIO_EXTILineConfig(IRDA_GPIO_PORT_SOURCE, IRDA_GPIO_PIN_SOURCE); 
  EXTI_InitStructure.EXTI_Line = IRDA_EXTI_LINE;
  EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
  EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling; //下降沿中断
  EXTI_InitStructure.EXTI_LineCmd = ENABLE;
  EXTI_Init(&EXTI_InitStructure); 
}

/* 获取高电平的时间 */
uint8_t Get_Pulse_Time(void)
{
  uint8_t time = 0;
  while( IrDa_DATA_IN() )
  {
    time ++;
    Delay_us(2);     // 延时 20us
    if(time == 250)
      return time;   // 超时溢出   
  }
  return time;
}

/*
 * 帧数据有4个字节,第一个字节是遥控的ID,第二个字节是第一个字节的反码
 * 第三个数据是遥控的真正的键值,第四个字节是第三个字节的反码
 */
uint8_t IrDa_Process(void)
{
  uint8_t first_byte, sec_byte, tir_byte, fou_byte;  
  
  first_byte = frame_data >> 24;
  sec_byte = (frame_data>>16) & 0xff;
  tir_byte = frame_data >> 8;
  fou_byte = frame_data;
  
  /* 记得清标志位 */
  frame_flag = 0;
  
  if( (first_byte==(uint8_t)~sec_byte) && (first_byte==IRDA_ID) )
  {
    if( tir_byte == (u8)~fou_byte )
      return tir_byte;
  }
  
  return 0;   /* 错误返回 */
}

/*********************************************END OF FILE**********************/

如对代码有所疑惑,可以联系博主一起探讨技术,或有不对之处,请指出,立即改正!!!


直流电机控制(A4950&TB6612驱动)

仅供学习,不可商用
仅供学习,不可商用

         

双路A4950驱动,接线框图
双路TB6612驱动,接线框图

        博主本人在最开始的时候,是准备以TB6612驱动来做,结果买成了A4950驱动,于是两个驱动都研究了一下,基本控制了车子的驱动。

        以上接线框图来自于官方的例程,如果与本文提供程序不符请谅解!由于野火开发板个别引脚未引出,导致定时器未能使用,于是导致程序引脚定义与接线框图不一致!

        不能提供官方所给程序,于是贴自己的,请大家尊重知识,尊重编写程序的每一个程序员!

以下程序如有引脚不一致的地方,请忽略,本人亲测代码无问题:

电机驱动代码(TB6612):

#include "tb6612.h"


//***************************TB6612配置***************************//
void TB6612_Init(void)
{
	TB6612_PWM_Init(3600, 1);//PWM频率初始化20KHz
	TB6612_GPIO_Config();    //电机驱动IO配置
}
	

//***************************PWM频率及占空比初始化***************************//
//=====初始化PWM 20KHZ 高频可以防止电机低频时的尖叫声
// ARR= 3599 时频率为20Khz
//PB0控制PWMA--left motor,PB1控制PWMB--right motor。STBY直接拉高
//arr:自动重装寄存器,psc分频系数
//PWM的频率 = 72MHz/ARR/PCS 例如  20K = 72M/3600/1 =  20K
void TB6612_PWM_Init(u32 arr, int psc)
{
	TIM_OCInitTypeDef TIM_OCInitSructure;
	TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
	GPIO_InitTypeDef    GPIO_InitStructure;
	
	//配置pwm输出端口
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
	GPIO_InitStructure.GPIO_Pin =  GPIO_Pin_8| GPIO_Pin_9;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;		         // 复用推挽输出
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_Init(GPIOB, &GPIO_InitStructure);
	
	//初始化定时器
	
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE);
	
	TIM_TimeBaseStructure.TIM_Period = arr-1;                   //自动重新装载寄存器周期的值澹ㄥ计数值澹)
	TIM_TimeBaseStructure.TIM_Prescaler = psc-1;                  //时钟分频系数
	TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;     //对外部时钟进行采样的时钟分频
	TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //向上计数
	TIM_TimeBaseInit(TIM4,&TIM_TimeBaseStructure);              //参数初始化

	TIM_ClearFlag(TIM4, TIM_FLAG_Update);
	TIM_ITConfig(TIM4, TIM_IT_Update, ENABLE);
	//设置通道3 pwm参数
	TIM_OCInitSructure.TIM_OCMode = TIM_OCMode_PWM1;
	TIM_OCInitSructure.TIM_OutputState= TIM_OutputState_Enable;
	TIM_OCInitSructure.TIM_Pulse = 0;                            //占空比=
	TIM_OCInitSructure.TIM_OCPolarity = TIM_OCPolarity_High;     //当定时器计数值小于CCR1_Val时为高电平
	TIM_OC3Init(TIM4, &TIM_OCInitSructure);                      //参数初始化
	TIM_OC3PolarityConfig(TIM4, TIM_OCPreload_Enable);           //开始输出pwm

	//设置通道4 pwm参数
	TIM_OCInitSructure.TIM_OCMode = TIM_OCMode_PWM1;
	TIM_OCInitSructure.TIM_OutputState= TIM_OutputState_Enable;
	TIM_OCInitSructure.TIM_Pulse = 0;                            //占空比= 
	TIM_OCInitSructure.TIM_OCPolarity = TIM_OCPolarity_High;     //当定时器计数值小于CCR1_Val时为高电平
	TIM_OC4Init(TIM4, &TIM_OCInitSructure);                      //参数初始化
	TIM_OC4PolarityConfig(TIM4, TIM_OCPreload_Enable);           //开始输出pwm

	TIM_ARRPreloadConfig(TIM4, ENABLE);                          //启动自动重装
	TIM_Cmd(TIM4, ENABLE);                                       //启动定时	
}

//***************************占空比调节***************************//
//占空比 = TIMx_CCRx / TIMx_ARR
//moto_r:右轮电机,moto_l:左轮电机.   数值 0-3600
void TB6612_PWM_Out(u16 moto_l, u16 moto_r)
{
	
	TIM_OCInitTypeDef TIM_OCInitSructure;
	
	TIM_OCInitSructure.TIM_OCMode = TIM_OCMode_PWM1;
	TIM_OCInitSructure.TIM_OutputState= TIM_OutputState_Enable;
	//CH3 左电机
	TIM_OCInitSructure.TIM_Pulse = moto_l;               //占空比= ccr/3600
	TIM_OC3Init(TIM4, &TIM_OCInitSructure);              //参数初始化
	TIM_OC3PolarityConfig(TIM4, TIM_OCPreload_Enable);   //开始输出pwm
	
	//CH4 右电机
	TIM_OCInitSructure.TIM_Pulse = moto_r;               //占空比= ccr /3600
	TIM_OC4Init(TIM4, &TIM_OCInitSructure);              //参数初始化
	TIM_OC4PolarityConfig(TIM4, TIM_OCPreload_Enable);   //开始输出pwm
	
	TIM_ARRPreloadConfig(TIM4, ENABLE);                  //启动自动重装
	 
}	


void TB6612_GPIO_Config(void)
{		
		/*定义一个GPIO_InitTypeDef类型的结构体*/
		GPIO_InitTypeDef GPIO_InitStructure;
		RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOB, ENABLE);                                	  /*开启GPIO的外设时钟*/																   
		GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12 | GPIO_Pin_13 | GPIO_Pin_14 | GPIO_Pin_15;	    /*选择要控制的GPIO引脚*/	
		GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;                                          /*设置引脚模式为通用推挽输出*/   
		GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;                                         /*设置引脚速率为50MHz */ 
		GPIO_Init(GPIOB, &GPIO_InitStructure);                                                 /*调用库函数,初始化GPIO*/	
}

//以下单独定义每个轮子运动状态,如果不对可以修改里面的参数
//比如左轮前进,AIN1(0),AIN2(1).如果往后转了,就改成AIN1(1),AIN2(0)

void Motor_Left(u8 state)
{
	if(state == GO)      //左电机前进
	{
	  AIN1(0);
		AIN2(1);
	}
	if(state == BACK)   //左电机后退
	{
		AIN1(1);
		AIN2(0);
	}
	if(state == STOP)  //停转
	{
	  AIN1(1);
		AIN2(1);
	}
}

void Motor_Right(u8 state)
{
	if(state == GO)      //右电机前进
	{
	  BIN1(0);
		BIN2(1);
	}
	if(state == BACK)   //右电机后退
	{
		BIN1(1);
		BIN2(0);
	}
	if(state == STOP)  //停转
	{
	  BIN1(1);
		BIN2(1);
	}
}

//以下定义了小车整体的运动方向。转弯靠左右电机差速实现。

void Car_Go(void)
{
	//小车前进
	//左电机前进      右电机前进
	Motor_Left(GO);   Motor_Right(GO);
}

void Car_Back(void)
{
	//小车后退
	//左电机后退     右电机后退
	Motor_Left(BACK);   Motor_Right(BACK);
	
}

void Car_Right(void)
{
	//小车右转圈
	//左电机前进      右电机后退
	Motor_Left(GO);   Motor_Right(BACK);
}

void Car_Left(void)
{
	//小车左转圈
	//左电机后退      右电机前进
	Motor_Left(BACK);   Motor_Right(GO);
}

void Car_Stop(void)
{
	//小车停车
	//左电机停止      右电机停止
	Motor_Left(STOP);   Motor_Right(STOP);
}

电机驱动代码(A4950):


#include "AT4950.h"


//***************************AT4950配置***************************//
void AT4950_Init(void)
{
	AT4950_PWM_Init(3600, 1);//PWM频率初始化20KHz
	AT4950_GPIO_Config();    //电机驱动IO配置
}
	

//***************************PWM频率及占空比初始化***************************//
//=====初始化PWM 20KHZ 高频可以防止电机低频时的尖叫声
// ARR= 3599 时频率为20Khz
//PB0控制PWMA--left motor,PB1控制PWMB--right motor。STBY直接拉高
//arr:自动重装寄存器,psc分频系数
//PWM的频率 = 72MHz/ARR/PCS 例如  20K = 72M/3600/1 =  20K
void AT4950_PWM_Init(u32 arr, int psc)
{
	TIM_OCInitTypeDef TIM_OCInitSructure;
	TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
	GPIO_InitTypeDef    GPIO_InitStructure;
	
	//配置pwm输出端口
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
	GPIO_InitStructure.GPIO_Pin =  GPIO_Pin_8| GPIO_Pin_9;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;		         // 复用推挽输出
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_Init(GPIOB, &GPIO_InitStructure);
	
	//初始化定时器
	
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE);
	
	TIM_TimeBaseStructure.TIM_Period = arr-1;                   //自动重新装载寄存器周期的值澹ㄥ计数值澹)
	TIM_TimeBaseStructure.TIM_Prescaler = psc-1;                  //时钟分频系数
	TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;     //对外部时钟进行采样的时钟分频
	TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //向上计数
	TIM_TimeBaseInit(TIM4,&TIM_TimeBaseStructure);              //参数初始化

	TIM_ClearFlag(TIM4, TIM_FLAG_Update);
	TIM_ITConfig(TIM4, TIM_IT_Update, ENABLE);
	//设置通道3 pwm参数
	TIM_OCInitSructure.TIM_OCMode = TIM_OCMode_PWM1;
	TIM_OCInitSructure.TIM_OutputState= TIM_OutputState_Enable;
	TIM_OCInitSructure.TIM_Pulse = 0;                            //占空比=
	TIM_OCInitSructure.TIM_OCPolarity = TIM_OCPolarity_High;     //当定时器计数值小于CCR1_Val时为高电平
	TIM_OC3Init(TIM4, &TIM_OCInitSructure);                      //参数初始化
	TIM_OC3PolarityConfig(TIM4, TIM_OCPreload_Enable);           //开始输出pwm

	//设置通道4 pwm参数
	TIM_OCInitSructure.TIM_OCMode = TIM_OCMode_PWM1;
	TIM_OCInitSructure.TIM_OutputState= TIM_OutputState_Enable;
	TIM_OCInitSructure.TIM_Pulse = 0;                            //占空比= 
	TIM_OCInitSructure.TIM_OCPolarity = TIM_OCPolarity_High;     //当定时器计数值小于CCR1_Val时为高电平
	TIM_OC4Init(TIM4, &TIM_OCInitSructure);                      //参数初始化
	TIM_OC4PolarityConfig(TIM4, TIM_OCPreload_Enable);           //开始输出pwm

	TIM_ARRPreloadConfig(TIM4, ENABLE);                          //启动自动重装
	TIM_Cmd(TIM4, ENABLE);                                       //启动定时	
}

//***************************占空比调节***************************//
//占空比 = TIMx_CCRx / TIMx_ARR
//moto_r:右轮电机,moto_l:左轮电机.   数值 0-3600
void AT4950_PWM_Out(u16 moto_l, u16 moto_r)
{
	
	TIM_OCInitTypeDef TIM_OCInitSructure;
	
	TIM_OCInitSructure.TIM_OCMode = TIM_OCMode_PWM1;
	TIM_OCInitSructure.TIM_OutputState= TIM_OutputState_Enable;
	//CH3 左电机
	TIM_OCInitSructure.TIM_Pulse = moto_l;               //占空比= ccr/3600
	TIM_OC3Init(TIM4, &TIM_OCInitSructure);              //参数初始化
	TIM_OC3PolarityConfig(TIM4, TIM_OCPreload_Enable);   //开始输出pwm
	
	//CH4 右电机
	TIM_OCInitSructure.TIM_Pulse = moto_r;               //占空比= ccr /3600
	TIM_OC4Init(TIM4, &TIM_OCInitSructure);              //参数初始化
	TIM_OC4PolarityConfig(TIM4, TIM_OCPreload_Enable);   //开始输出pwm
	
	TIM_ARRPreloadConfig(TIM4, ENABLE);                  //启动自动重装
	 
}	


void AT4950_GPIO_Config(void)
{		
		/*定义一个GPIO_InitTypeDef类型的结构体*/
		GPIO_InitTypeDef GPIO_InitStructure;
		RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOB, ENABLE);                                	  /*开启GPIO的外设时钟*/																   
		GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12 | GPIO_Pin_13 ;	    /*选择要控制的GPIO引脚*/	
		GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;                                          /*设置引脚模式为通用推挽输出*/   
		GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;                                         /*设置引脚速率为50MHz */ 
		GPIO_Init(GPIOB, &GPIO_InitStructure);                                                 /*调用库函数,初始化GPIO*/	
}

//以下单独定义每个轮子运动状态,如果不对可以修改里面的参数
//比如左轮前进,AIN1(0),AIN2(1).如果往后转了,就改成AIN1(1),AIN2(0)

//***************************占空比调节***************************//
//占空比 = TIMx_CCRx / TIMx_ARR
//moto_r:右轮电机,moto_A:左轮电机.   数值 0-3600
void A4950_PWM_OutA(u16 motorA)
{
	TIM4 -> CCR3 = motorA;	 
}	
void A4950_PWM_OutB(u16 motorB)
{  
	TIM4 -> CCR4 = motorB;	 
}

//以下单独定义每个轮子运动状态,如果不对可以修改里面的参数
void MotorA_Control(u8 state,u16 pwm)
{
	if(state == GO)      //电机1前进
	{
	  A4950_PWM_OutA(pwm);
		AIN1(0);
	}
	if(state == BACK)   //电机1后退
	{
		A4950_PWM_OutA(3600-pwm);
		AIN1(1);
	}
	if(state == STOP)  //停转
	{
	  A4950_PWM_OutA(0);
		AIN1(0);
	}
}

void MotorB_Control(u8 state,u16 pwm)
{
	if(state == GO)      //电机2前进
	{
	  A4950_PWM_OutB(pwm);
		BIN1(1);
	}
	if(state == BACK)   //电机2后退
	{
		A4950_PWM_OutB(3600-pwm);
		BIN1(0);
	}
	if(state == STOP)  //停转
	{
	  A4950_PWM_OutB(0);
		BIN1(0);
	}
}

代码亲测有效,请大家耐心测试!!!


 直流电机测速(硬件图如上文

        直流电机测速,首先是用的官方的程序,官方是那定时器2和定时器3来测电机编码器的编码器值,由于野火板子的问题,无法驱动,后我将引脚去修修改改也是能跑。‘

        官方:定时器2、定时器3来控制计数两个电机的编码器值,定时器1来控制时间,最后用编码器值和时间来测得速度!

        我自己:由于无法使用两个定时器,于是只用了定时器2,用的是PA0、PA1引脚,控制在5ms中算定时器的编码器值,然后用编码器的值和固定的5ms来算出速度,以下提供的自己的代码只是一个dome,希望大家完善一下,个人不提供太完整!

官方程序:

#include "Encoder.h"


//***************************定时器2初始化 ,使用编码器功能***************************//
//左电机编码器计数
//PA15----接 编码器A相 或者电机驱动的B1A标识
//PB3 ----接 编码器B相 或者电机驱动的B1B标识
void Encoder_Init_TIM2(void)
{
	GPIO_InitTypeDef GPIO_InitStructure; 
	TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
	TIM_ICInitTypeDef TIM_ICInitStructure;   

	//GPIO功能时钟使能
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB | RCC_APB2Periph_AFIO, ENABLE);
	GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable, ENABLE);     //禁用JTAG
	GPIO_PinRemapConfig(GPIO_FullRemap_TIM2, ENABLE);

	//配置IO口为复用功能-定时器通道
	GPIO_InitStructure.GPIO_Pin =  GPIO_Pin_3;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;        //复用功能
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;	//速度100MHz
	GPIO_Init(GPIOB, &GPIO_InitStructure);

	GPIO_InitStructure.GPIO_Pin =  GPIO_Pin_15;
	GPIO_Init(GPIOA, &GPIO_InitStructure);

	//TIM时钟使能
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);

	//Timer configuration in Encoder mode 
	TIM_DeInit(TIM2);
	TIM_TimeBaseStructInit(&TIM_TimeBaseStructure);

	TIM_TimeBaseStructure.TIM_Prescaler = 0x0;  // No prescaling 
	TIM_TimeBaseStructure.TIM_Period = ENCODER_TIM_PERIOD;  
	TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;
	TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;   
	TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);

	TIM_EncoderInterfaceConfig(TIM2, TIM_EncoderMode_TI12, TIM_ICPolarity_Falling, TIM_ICPolarity_Falling);
	TIM_ICStructInit(&TIM_ICInitStructure);
	TIM_ICInitStructure.TIM_ICFilter = 0;
	TIM_ICInit(TIM2, &TIM_ICInitStructure);

	//Reset counter
	TIM2->CNT = 0;

	TIM_Cmd(TIM2, ENABLE);   
}


//***************************定时器3初始化 ,使用编码器功能***************************//
//左电机编码器计数
//PB4----接 编码器A相 或者电机驱动的B2A标识
//PB5----接 编码器B相 或者电机驱动的B2B标识
void Encoder_Init_TIM3(void)
{
		GPIO_InitTypeDef GPIO_InitStructure; 
	TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
	TIM_ICInitTypeDef TIM_ICInitStructure;   

	//GPIO功能时钟使能
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB | RCC_APB2Periph_AFIO, ENABLE);

	//配置IO口为复用功能-定时器通道
	GPIO_InitStructure.GPIO_Pin =  GPIO_Pin_4 | GPIO_Pin_5;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;        //复用功能
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;	//速度100MHz
	GPIO_Init(GPIOB, &GPIO_InitStructure);

	//TIM时钟使能
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);

	GPIO_PinRemapConfig(GPIO_PartialRemap_TIM3 , ENABLE); //这个就是重映射功能函数

	//Timer configuration in Encoder mode 
	TIM_DeInit(TIM3);
	TIM_TimeBaseStructInit(&TIM_TimeBaseStructure);

	TIM_TimeBaseStructure.TIM_Prescaler = 0x0;  // No prescaling 
	TIM_TimeBaseStructure.TIM_Period = ENCODER_TIM_PERIOD;  
	TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;
	TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;   
	TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure);

	TIM_EncoderInterfaceConfig(TIM3, TIM_EncoderMode_TI12, TIM_ICPolarity_Rising, TIM_ICPolarity_Rising);
	TIM_ICStructInit(&TIM_ICInitStructure);
	TIM_ICInitStructure.TIM_ICFilter = 0;
	TIM_ICInit(TIM3, &TIM_ICInitStructure);

	//Reset counter
	TIM_SetCounter(TIM3,0);

	TIM_Cmd(TIM3, ENABLE);  
	
	
}


/**************************************************************************
函数功能:单位时间读取编码器计数
入口参数:定时器
返回  值:速度值
**************************************************************************/
int Read_Encoder(u8 TIMX)
{
    int Encoder_TIM;    
   switch(TIMX)
	 {
	   case 2:  Encoder_TIM= (short)TIM2 -> CNT;  TIM2 -> CNT=30000;break;
		 case 3:  Encoder_TIM= (short)TIM3 -> CNT;  TIM3 -> CNT=30000;break;	
		 //case 4:  Encoder_TIM= (short)TIM4 -> CNT;  TIM4 -> CNT=30000;break;	
		// case 5:  Encoder_TIM= (short)TIM5 -> CNT;  TIM5 -> CNT=30000;break;
		 default:  Encoder_TIM=0;
	 }
		return Encoder_TIM;
}


//定时器1:
#include "car.h"

u8 temp1=0;
u8 OLED_Timer1=0;//时间片,更新ADC
u8 OLED_Timer2=0;//时间片,更新速度
u8 Encoder_Timer = 0;

u8 OLED_ADC_Flag=0;
u8 OLED_Speed_Flag=0;

u16 ADC_BAT_Val,ADC_BAT_temp;
u16 ADC_SYS_Val,ADC_SYS_temp;

int Encoder_L,Encoder_R;

//Time1定时器1中断服务函数
//10ms定时
void TIM1_UP_IRQHandler(void)
{
	if(TIM_GetFlagStatus(TIM1, TIM_IT_Update) != RESET)   //时间到了
	{
		TIM_ClearITPendingBit(TIM1, TIM_FLAG_Update);//清中断
		LED_Flash(50);//500ms闪烁一次
		
		temp1++;
		OLED_Timer1++;
		OLED_Timer2++;
		Encoder_Timer++;
		ADC_BAT_temp += Get_BAT_ADC();//ADC 采样 电池电压
		ADC_SYS_temp += Get_SYS_ADC();//aDC 采样 系统电压
		
		if(Encoder_Timer>=5)//电机转速50ms采样一次
		{
			Encoder_Timer = 0;
			Encoder_R=Read_Encoder(2)-30000;                   //读取编码器,计算出变化量
			Encoder_L=-((Read_Encoder(3)-30000));                   //读取编码器,计算出变化量,负号是因为两个电机 相对位置180°
			
			if(Encoder_R < 0)
			{
				Encoder_R = -Encoder_R;
			}
			if(Encoder_L < 0)
			{
				Encoder_L = -Encoder_L;
			}
			
		}
		
		
		
		if(temp1>=10)//100ms
		{
			temp1=0;
			ADC_BAT_Val = ADC_BAT_temp/10;
			ADC_SYS_Val = ADC_SYS_temp/10;
			ADC_BAT_temp = 0;
			ADC_SYS_temp = 0;
		}
		
		if(OLED_Timer1 >= 100)//1S 更新一次ADC
		{
			OLED_Timer1=0;
			OLED_ADC_Flag = 1;
		}	
		
	}
		
}



个人*(希望对你有所帮助):

#include "Encoder.h"

void TIM2_IRQHandler(void)
{
  if (TIM_GetITStatus(TIM2, TIM_IT_Update) != RESET) {      // 定时器更新中断
    TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
  }
  if (TIM_GetITStatus(TIM2, TIM_IT_CC1) != RESET) {    // 输入捕获中断
    TIM_ClearITPendingBit(TIM2, TIM_IT_CC1);
    encoder_count++;
  }
  else if (TIM_GetITStatus(TIM3, TIM_IT_CC2) != RESET) {    // 输入捕获中断
    TIM_ClearITPendingBit(TIM2, TIM_IT_CC2);
    encoder_count++;

  }
  else{
	  encoder_count = 0;
  }
  printf("encoder_count : %d\n",encoder_count);
}


void TIM2_Encoder_Config(void)
{
  GPIO_InitTypeDef GPIO_InitStructure;
  TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
  NVIC_InitTypeDef NVIC_InitStructure;
  TIM_ICInitTypeDef TIM_ICInitStructure;

  RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
  RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);

  // 配置PA0/PA1为TIM2的通道1/2
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_Init(GPIOA, &GPIO_InitStructure);


  TIM_TimeBaseStructure.TIM_Period = 65535;   // 自动重装载值
  TIM_TimeBaseStructure.TIM_Prescaler = 0;     // 预分频系数
  TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;  // 时钟分频因子
  TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;  // 向上计数模式
  TIM_TimeBaseStructure.TIM_RepetitionCounter = 0;  // 重复计数器值
  TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);

  TIM_EncoderInterfaceConfig(TIM2, TIM_EncoderMode_TI12, TIM_ICPolarity_Rising, TIM_ICPolarity_Rising);  // 编码器模式

  
  TIM_ICInitStructure.TIM_Channel = TIM_Channel_1;
  TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising;
  TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI;
  TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;
  TIM_ICInitStructure.TIM_ICFilter = 0;
  TIM_ICInit(TIM2, &TIM_ICInitStructure);

  TIM_ICInitStructure.TIM_Channel = TIM_Channel_2;
  TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising;
  TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI;
  TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;
  TIM_ICInitStructure.TIM_ICFilter = 0;
  TIM_ICInit(TIM2, &TIM_ICInitStructure);

  TIM_ClearFlag(TIM2, TIM_FLAG_Update);
  TIM_ITConfig(TIM2, TIM_IT_Update | TIM_IT_CC1 | TIM_IT_CC2, ENABLE);

  
  NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;
  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 4;
  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 4;
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  NVIC_Init(&NVIC_InitStructure);

  TIM_Cmd(TIM2, ENABLE);
}





void TIM2_Configuration(void)
{
  TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;

  RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);

  TIM_TimeBaseInitStructure.TIM_Prescaler = 0; // 定时器2预分频系数为1
  TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
  TIM_TimeBaseInitStructure.TIM_Period = 0xFFFF; // 定时器2计数器每计一个数就会溢出,自动重装载到0xFFFF
  TIM_TimeBaseInitStructure.TIM_ClockDivision = 0;
  TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;

  TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);

  TIM_EncoderInterfaceConfig(TIM2, TIM_EncoderMode_TI12, TIM_ICPolarity_Rising, TIM_ICPolarity_Rising); // CH1和CH2通道作为编码器输入
  TIM_SetCounter(TIM2, 0); // 定时器2计数器清零
  TIM_Cmd(TIM2, ENABLE); // 启动定时器2
}

void GPIO_Configuration(void)
{
  GPIO_InitTypeDef GPIO_InitStructure;

  RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);

  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1; // GPIOA的PIN0和PIN1作为编码器输入
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;

  GPIO_Init(GPIOA, &GPIO_InitStructure);
}


附图(电路图):

A4950双路原理图

中科微GPS测试

        

图片仅供参考

GPS主要是一个串口的利用,对串口进行一个读取数据。PA9与PA10,波特率一般为9600。

#include "sys.h"
#include "usart.h"	
char rxdatabufer;
u16 point1 = 0;

_SaveData Save_Data;
// 	 
//如果使用ucos,则包括下面的头文件即可.
#if SYSTEM_SUPPORT_UCOS
#include "includes.h"					//ucos 使用	  
#endif 
 

//
//加入以下代码,支持printf函数,而不需要选择use MicroLIB	  
#if 1
#pragma import(__use_no_semihosting)             
//标准库需要的支持函数                 
struct __FILE 
{ 
	int handle; 

}; 

FILE __stdout;       
//定义_sys_exit()以避免使用半主机模式    
int _sys_exit(int x) 
{ 
	x = x; 
} 
//重定义fputc函数 
int fputc(int ch, FILE *f)
{      
	while((USART1->SR&0X40)==0);//循环发送,直到发送完毕   
    USART1->DR = (u8) ch;      
	return ch;
}
#endif 

/*使用microLib的方法*/
 /* 
int fputc(int ch, FILE *f)
{
	USART_SendData(USART1, (uint8_t) ch);

	while (USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET) {}	
   
    return ch;
}
int GetKey (void)  { 

    while (!(USART1->SR & USART_FLAG_RXNE));

    return ((int)(USART1->DR & 0x1FF));
}
*/
 
#if EN_USART1_RX   //如果使能了接收
//串口1中断服务程序
//注意,读取USARTx->SR能避免莫名其妙的错误   	
char USART_RX_BUF[USART_REC_LEN];     //接收缓冲,最大USART_REC_LEN个字节.
//接收状态
//bit15,	接收完成标志
//bit14,	接收到0x0d
//bit13~0,	接收到的有效字节数目
u16 USART_RX_STA=0;       //接收状态标记	  
  
void uart_init(u32 bound)
{
    //GPIO端口设置
    GPIO_InitTypeDef GPIO_InitStructure;
	USART_InitTypeDef USART_InitStructure;
	NVIC_InitTypeDef NVIC_InitStructure;
	 
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1|RCC_APB2Periph_GPIOA, ENABLE);	//使能USART1,GPIOA时钟
     //USART1_TX   PA.9
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; //PA.9
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;	//复用推挽输出
    GPIO_Init(GPIOA, &GPIO_InitStructure);
   
    //USART1_RX	  PA.10
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
//     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空输入
		GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空输入
    GPIO_Init(GPIOA, &GPIO_InitStructure);  

   //Usart1 NVIC 配置

    NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3 ;//抢占优先级3
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;		//子优先级3
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;			//IRQ通道使能
	NVIC_Init(&NVIC_InitStructure);	//根据指定的参数初始化VIC寄存器
  
   //USART 初始化设置

	USART_InitStructure.USART_BaudRate = bound;//一般设置为9600;
	USART_InitStructure.USART_WordLength = USART_WordLength_8b;//字长为8位数据格式
	USART_InitStructure.USART_StopBits = USART_StopBits_1;//一个停止位
	USART_InitStructure.USART_Parity = USART_Parity_No;//无奇偶校验位
	USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//无硬件数据流控制
	USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;	//收发模式

    USART_Init(USART1, &USART_InitStructure); //初始化串口
    USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);//开启中断
    USART_Cmd(USART1, ENABLE);                    //使能串口 


	CLR_Buf();//清空缓存
}

void USART1_IRQHandler(void)                	//串口1中断服务程序
{
	u8 Res;
#ifdef OS_TICKS_PER_SEC	 	//如果时钟节拍数定义了,说明要使用ucosII了.
	OSIntEnter();    
#endif
	if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) 
	{
		Res =USART_ReceiveData(USART1);//(USART1->DR);	//读取接收到的数据
	
	if(Res == '$')
	{
		point1 = 0;	
	}
		

	  USART_RX_BUF[point1++] = Res;

	if(USART_RX_BUF[0] == '$' && USART_RX_BUF[4] == 'M' && USART_RX_BUF[5] == 'C')			//确定是否收到"GPRMC/GNRMC"这一帧数据
	{
		if(Res == '\n')									   
		{
			memset(Save_Data.GPS_Buffer, 0, GPS_Buffer_Length);      //清空
			memcpy(Save_Data.GPS_Buffer, USART_RX_BUF, point1); 	//保存数据
			Save_Data.isGetData = true;
			point1 = 0;
			memset(USART_RX_BUF, 0, USART_REC_LEN);      //清空				
		}	
				
	}
	
	if(point1 >= USART_REC_LEN)
	{
		point1 = USART_REC_LEN;
	}	
		
		
		
		
		
		
// 		USART_RX_STA|=0x8000;	//接收完成了
// 		if((USART_RX_STA&0x8000)==0)//接收未完成
// 		{
// 			if(USART_RX_STA&0x4000)//接收到了0x0d
// 			{
// 				if(Res!=0x0a)USART_RX_STA=0;//接收错误,重新开始
// 				else USART_RX_STA|=0x8000;	//接收完成了 			//bit31表明是否接收到0x0a(\n)
// 			}
// 			else //还没收到0X0D
// 			{	
// 				if(Res==0x0d)USART_RX_STA|=0x4000;						//bit30表明是否接收到0x0d(\r)
// 				else
// 				{
// 					USART_RX_BUF[USART_RX_STA&0X3FFF]=Res ;
// 					USART_RX_STA++;
// 					if(USART_RX_STA>(USART_REC_LEN-1))USART_RX_STA=0;//接收数据错误,重新开始接收	  
// 				}		 
// 			}
// 		}   		 
   } 
#ifdef OS_TICKS_PER_SEC	 	//如果时钟节拍数定义了,说明要使用ucosII了.
	OSIntExit();  											 
#endif
}


u8 Hand(char *a)                   // 串口命令识别函数
{ 
    if(strstr(USART_RX_BUF,a)!=NULL)
	    return 1;
	else
		return 0;
}

void CLR_Buf(void)                           // 串口缓存清理
{
	memset(USART_RX_BUF, 0, USART_REC_LEN);      //清空
  point1 = 0;                    
}

void clrStruct()
{
	Save_Data.isGetData = false;
	Save_Data.isParseData = false;
	Save_Data.isUsefull = false;
	memset(Save_Data.GPS_Buffer, 0, GPS_Buffer_Length);      //清空
	memset(Save_Data.UTCTime, 0, UTCTime_Length);
	memset(Save_Data.latitude, 0, latitude_Length);
	memset(Save_Data.N_S, 0, N_S_Length);
	memset(Save_Data.longitude, 0, longitude_Length);
	memset(Save_Data.E_W, 0, E_W_Length);
	
}

#endif	

读取经纬度:

void printGpsBuffer()
{
	static char dispBuff[100];
	if (Save_Data.isParseData)
	{
		Save_Data.isParseData = false;
		
		printf("Save_Data.UTCTime = ");
		printf(Save_Data.UTCTime);
		printf("\r\n");

		if(Save_Data.isUsefull)
		{
			Save_Data.isUsefull = false;
			printf("Save_Data.latitude = ");   //纬度
			printf(Save_Data.latitude);
			
			//LCD显示纬度
			LCD_SetTextColor(GREEN);
			sprintf(dispBuff,"   latitud-> %s",Save_Data.latitude);
			LCD_ClearLine(LINE(6));
			ILI9341_DispStringLine_EN_CH(LINE(6),dispBuff);

			printf("\r\n");


			printf("Save_Data.N_S = ");
			printf(Save_Data.N_S);
			printf("\r\n");

			printf("Save_Data.longitude = ");   //经度
			printf(Save_Data.longitude);
			printf("\r\n");

			//LCD显示经度
			LCD_SetTextColor(GREEN);
			sprintf(dispBuff," longitude-> %s",Save_Data.longitude);
			LCD_ClearLine(LINE(7));
			ILI9341_DispStringLine_EN_CH(LINE(7),dispBuff);
			
			printf("Save_Data.E_W = ");
			printf(Save_Data.E_W);
			printf("\r\n");
		}
		else
		{
			printf("GPS DATA is not usefull!\r\n");
			LCD_SetTextColor(RED);
			sprintf(dispBuff," longitude-> ERROR",0.0);
			LCD_ClearLine(LINE(7));
			ILI9341_DispStringLine_EN_CH(LINE(7),dispBuff);
			sprintf(dispBuff,"   latitud-> ERROR",0.0);
			LCD_ClearLine(LINE(6));
			ILI9341_DispStringLine_EN_CH(LINE(6),dispBuff);
		}
	}
}


超声波撞车报警

         倒车报警使用的是超声波测距,当距离小于10的时候,蜂鸣器鸣叫!使用的是定时器3来计算距离,具体使用步骤,大家可以参考具体数据手册(stm32f103vet6的定时器3)

        引脚是PA5、PA6

#include "car.h"

/* 超声波设置  */

#define Echo GPIO_Pin_6    //HC-SR04模块的Echo脚接GPIOB6
#define Trig GPIO_Pin_5    //HC-SR04模块的Trig脚接GPIOB5

uint64_t time=0;      //声明变量,用来计时
uint64_t time_end=0;    //声明变量,存储回波信号时间

void HC_SR04_Init(void)
{
	GPIO_InitTypeDef GPIO_InitStructure;          //定义结构体
	
	
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);  //启用GPIOB的外设时钟  

	
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;    //设置GPIO口为推挽输出
  GPIO_InitStructure.GPIO_Pin = Trig;            //设置GPIO口5
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;    //设置GPIO口速度50Mhz
  GPIO_Init(GPIOB,&GPIO_InitStructure);          //初始化GPIOB
  
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD;      //设置GPIO口为下拉输入模式
  GPIO_InitStructure.GPIO_Pin = Echo;            //设置GPIO口6
  GPIO_Init(GPIOB,&GPIO_InitStructure);          //初始化GPIOB
  GPIO_WriteBit(GPIOB,GPIO_Pin_5,0);            //输出低电平
  Delay_us(15);                      //延时15微秒
}

int16_t sonar_mm(void)                  //测距并返回单位为毫米的距离结果
{
  uint32_t Distance,Distance_mm = 0;
  GPIO_WriteBit(GPIOB,Trig,1);            //输出高电平
  Delay_us(15);                    //延时15微秒
  GPIO_WriteBit(GPIOB,Trig,0);            //输出低电平
  while(GPIO_ReadInputDataBit(GPIOB,Echo)==0);    //等待低电平结束
  time=0;                        //计时清零
  while(GPIO_ReadInputDataBit(GPIOB,Echo)==1);    //等待高电平结束
  time_end=time;                    //记录结束时的时间
  if(time_end/100<38)                  //判断是否小于38毫秒,大于38毫秒的就是超时,直接调到下面返回0
  {
    Distance=(time_end*320)/2;            //计算距离,25°C空气中的音速为346m/s
    Distance_mm=Distance/100;            //因为上面的time_end的单位是10微秒,所以要得出单位为毫米的距离结果,还得除以100
  }
  return Distance_mm;                  //返回测距结果
}

float sonar(void)                    //测距并返回单位为米的距离结果
{
  uint32_t Distance,Distance_mm = 0;
  float Distance_m=0;
  GPIO_WriteBit(GPIOB,Trig,1);          //输出高电平
  Delay_us(15);
  GPIO_WriteBit(GPIOB,Trig,0);          //输出低电平
  while(GPIO_ReadInputDataBit(GPIOB,Echo)==0);
  time=0;
  while(GPIO_ReadInputDataBit(GPIOB,Echo)==1);
  time_end=time;
  if(time_end/100<38)
  {
    Distance=(time_end*346)/2;
    Distance_mm=Distance/100;
    Distance_m=Distance_mm/1000;
  }
  return Distance_m;
}

void TIM3_IRQHandler(void)      //更新中断函数,用来计时,每10微秒变量time加1
{
  if (TIM_GetITStatus(TIM3, TIM_IT_Update) == SET)    //获取TIM3定时器的更新中断标志位
  {
    time++;
    TIM_ClearITPendingBit(TIM3, TIM_IT_Update);      //清除更新中断标志位
  }
}



void Timer_Init(void)
{
  TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;      //定义结构体,配置定时器
  NVIC_InitTypeDef NVIC_InitStructure;            //定义结构体,配置中断优先级
	
	
  RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);    //启用TIM3时钟

  TIM_InternalClockConfig(TIM3);                //设置TIM3使用内部时钟
  

  TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;  //设置1分频(不分频)
  TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;  //设置计数模式为向上计数
  TIM_TimeBaseInitStructure.TIM_Period = 10 - 1;      //设置最大计数值,达到最大值触发更新事件,因为从0开始计数,所以计数10次是10-1,每10微秒触发一次
  TIM_TimeBaseInitStructure.TIM_Prescaler = 72 - 1;      //设置时钟预分频,72-1就是每 时钟频率(72Mhz)/72=1000000 个时钟周期计数器加1,每1微秒+1
  TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;    //重复计数器(高级定时器才有,所以设置0)
  TIM_TimeBaseInit(TIM3, &TIM_TimeBaseInitStructure);      //初始化TIM3定时器
  
  TIM_ClearFlag(TIM3, TIM_FLAG_Update);      //清除更新中断标志位
  TIM_ITConfig(TIM3, TIM_IT_Update, ENABLE);    //开启更新中断
  
  NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);        //设置中断优先级分组
  

  NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn;        //指定中断通道
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;        //中断使能
  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;  //设置抢占优先级
  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;      //设置响应优先级
  NVIC_Init(&NVIC_InitStructure);                // https://blog.zeruns.tech
  
  TIM_Cmd(TIM3, ENABLE);              //开启定时器
}

        测距亲测可用!


联系我们:

        公众号:嵌入式up(源码工程提供)

文毕,文章篇幅较大,希望对大家有所帮助!谢谢支持和阅览!!!


既然选择远方,就注定风雨兼程。

猜你喜欢

转载自blog.csdn.net/oxygen23333/article/details/129650128
今日推荐