平衡车(二)——让小车能够动起来

上一篇文章已经能够把从mpu6050的数据通过stm32上传到匿名上位机进行实时波形显示,这一篇就来谈谈怎么让一个小车按照我们想要的状态动起来,这一篇也算是我弄了好几天才明白的东西做一个大概的介绍,算是来自一个小白的“吐槽”吧,哈哈哈,不多说,进入主题!(本文不介绍每一个模块的具体原理,只是介绍一下怎么用! )
首先我们先来了解一下我们要使用的电机:编码器电机,如图:
在这里插入图片描述这是一款增量式输出的霍尔编码器,好用但有点贵,哈哈。电机线+电机线-是PWM的控制接口,来决定输出的速度是多少。其实如果不要使用编码器的话,完全可以当成一个普通的直流电机来使用。所以我们就用定时器3来控制PWM输出,代码如下:

void TIM3_PWM_Init(u16 arr,u16 psc)
{  
		GPIO_InitTypeDef GPIO_InitStructure;
		TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
		TIM_OCInitTypeDef  TIM_OCInitStructure;

		RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);// 
		RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOB , ENABLE);  //使能GPIO外设时钟使能																																 

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

		GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0|GPIO_Pin_1 ; //TIM_CH1
		GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;  //复用推挽输出
		GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
		GPIO_Init(GPIOB, &GPIO_InitStructure);

		TIM_TimeBaseStructure.TIM_Period = arr; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值         80K
		TIM_TimeBaseStructure.TIM_Prescaler =psc; //设置用来作为TIMx时钟频率除数的预分频值  不分频
		TIM_TimeBaseStructure.TIM_ClockDivision =0; //设置时钟分割:TDTS = Tck_tim
		TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;  //TIM向上计数模式
		TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure); //根据TIM_TimeBaseInitStruct中指定的参数初始化TIMx的时间基数单位
		//TIM3通道一
		TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; //选择定时器模式:TIM脉冲宽度调制模式1
		TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //比较输出使能
		TIM_OCInitStructure.TIM_Pulse = 0; //设置待装入捕获比较寄存器的脉冲值
		TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; //输出极性:TIM输出比较极性高
		
		TIM_OC1Init(TIM3, &TIM_OCInitStructure);  //根据TIM_OCInitStruct中指定的参数初始化外设TIMx
		//TIM3通道二
		TIM_OCInitStructure.TIM_OutputState =TIM_OutputState_Enable;
		TIM_OCInitStructure.TIM_Pulse = 0;
		TIM_OC2Init(TIM3, &TIM_OCInitStructure);
		
		TIM_OC2PreloadConfig(TIM3, TIM_OCPreload_Enable);
		//TIM3通道三
		TIM_OCInitStructure.TIM_OutputState =TIM_OutputState_Enable;
		TIM_OCInitStructure.TIM_Pulse = 0;
		TIM_OC3Init(TIM3, &TIM_OCInitStructure);
		
		TIM_OC3PreloadConfig(TIM3, TIM_OCPreload_Enable);
		//TIM3通道四
		TIM_OCInitStructure.TIM_OutputState =TIM_OutputState_Enable;
		TIM_OCInitStructure.TIM_Pulse = 0;
		TIM_OC4Init(TIM3, &TIM_OCInitStructure);
		
		TIM_OC4PreloadConfig(TIM3, TIM_OCPreload_Enable);

		TIM_CtrlPWMOutputs(TIM3,ENABLE);        //MOE 主输出使能        
		TIM_ARRPreloadConfig(TIM3, ENABLE); //使能TIMx在ARR上的预装载寄存器
		TIM_Cmd(TIM3, ENABLE);  //使能TIM1
TIM3_PWM_Init(7199,0);//10KHZ  //主函数的初始化。使用时这段话不要放在一起,我这是为了介绍。
TIM_SetCompare1(TIM3,x)  //使用方法,注意X不要大于7199,毕竟7199已经是满转状态
TIM_SetCompare2(TIM3,x)
TIM_SetCompare3(TIM3,x)
TIM_SetCompare4(TIM3,x)

我用的是L298N,这个用来驱动这个电机不是很好,发热很大,但是手头上没有tb6612,只能勉强的用一下。这个L298N我是接的外电源,就是想跟stm32的电源分开,但是记住,一定要共地,图片如下:
在这里插入图片描述
通过上面的已经能够让车轮动起来了,但是仅仅是作为一个普通的直流电机,还不是我们选择编码器电机想要的效果,所以我们就需要使用我们刚才没有用到的编码器。编码器是通过AB相来对外输出它检测到的数值。stm32有专门的编码器接口,所以我们无需额外配置,直接来用便可。代码如下:

void Encoder_Init_TIM2(void)
{
	TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;  
  TIM_ICInitTypeDef TIM_ICInitStructure;  
  GPIO_InitTypeDef GPIO_InitStructure;
  RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);//使能定时器4的时钟
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);//使能PB端口时钟
	
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0|GPIO_Pin_1;	//端口配置
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; //浮空输入
  GPIO_Init(GPIOA, &GPIO_InitStructure);					      //根据设定参数初始化GPIOB
  
  TIM_TimeBaseStructInit(&TIM_TimeBaseStructure);
  TIM_TimeBaseStructure.TIM_Prescaler = 0x0; // 预分频器 
  TIM_TimeBaseStructure.TIM_Period = ENCODER_TIM_PERIOD; //设定计数器自动重装值
  TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;//选择时钟分频:不分频
  TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;////TIM向上计数  
  TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);
  TIM_EncoderInterfaceConfig(TIM2, TIM_EncoderMode_TI12, TIM_ICPolarity_Rising, TIM_ICPolarity_Rising);//使用编码器模式3
  TIM_ICStructInit(&TIM_ICInitStructure);
  TIM_ICInitStructure.TIM_ICFilter = 10;
  TIM_ICInit(TIM2, &TIM_ICInitStructure);
  TIM_ClearFlag(TIM2, TIM_FLAG_Update);//清除TIM的更新标志位
  TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);
  TIM_SetCounter(TIM2,0);
  TIM_Cmd(TIM2, ENABLE); 
}

因为一个定时器只能用计数一个编码器,所以还有一个TIM4,这里篇幅不够,可以自行加上。

/**************************************************************************
函数功能:单位时间读取编码器计数
入口参数:定时器
返回  值:速度值
**************************************************************************/
int Read_Encoder(u8 TIMX)
{
    int Encoder_TIM;    
   switch(TIMX)
	 {
	   case 2:  Encoder_TIM= (short)TIM2 -> CNT;  TIM2 -> CNT=0;break;
	   case 4:  Encoder_TIM= (short)TIM4 -> CNT;  TIM4 -> CNT=0;break;	
	   default:  Encoder_TIM=0;
	 }
	   return Encoder_TIM;
}
/**************************************************************************
函数功能:TIM2中断服务函数
入口参数:无
返回  值:无
**************************************************************************/
void TIM2_IRQHandler(void)
{ 		    		  			    
	if(TIM2->SR&0X0001)//溢出中断
	{    				   				     	    	
	}				   
	TIM2->SR&=~(1<<0);//清除中断标志位 	    
}

通过上面的介绍以及代码配置,我们就能真正的使用这一款编码器电机!
这里有一篇文章介绍编码器的,我觉得挺好https://blog.csdn.net/weixin_44430455/article/details/97790029?ops_request_misc={%22request_id%22:%22158174719519724847018744%22,%22scm%22:%2220140713.130056874…%22}
介绍到这里,平衡车可以组装起来了。接下来就是PID的调试了。先上两篇我觉得比较好的文章https://blog.csdn.net/zhaoyuaiweide/article/details/54572483
https://blog.csdn.net/ReCclay/article/details/84789455
篇幅太长了,下一篇文章讲讲平衡车的pid如何使用调试,仅仅是我的一些些想法。

发布了3 篇原创文章 · 获赞 0 · 访问量 238

猜你喜欢

转载自blog.csdn.net/weixin_43872149/article/details/104695148
今日推荐