模块学习笔记—(1)编码器减速电机

模块学习笔记—(1)编码器减速电机

编码器电机作用

编码器电机转动可以产生脉冲信号,根据脉冲信号,可以得出轮胎的转动速度、轮胎的位移,电机正反转等。

电机介绍

我的编码器电机是130TT减速电机,电机轴转一圈可以产生13个脉冲信号输出,电机减速比为1:120,所以减速电机的输出轴旋转一圈,实际可以产生的脉冲为13*120=1560个。在通过STM32F4编码器接口的4倍频就是6240个脉冲也即是轮子转一圈会有6240个脉冲。(4倍频就是一个脉冲可以被检测到4次,增大脉冲的检测精度)

模块学习笔记—(1)编码器减速电机_第1张图片

测速实现方案

第一种测速方案是计算一定脉冲下所用时间来得出电机速度
第二种测速方案是是一定时间下所产生的脉冲数量来计算。这里选择第二种。

1.捕获脉冲:

使用一个定时器TIM4的编码器模式接收电机编码器的脉冲,另一个定时器TIM3负责在一定时间的读取编码器脉冲的值。这样,就可以得出一定时间下的电机的脉冲数了。我这里取100ms取值一次。

2. 脉冲处理:

那么知道了一定时间下的电机的脉冲数怎么可以测到电机的速度呢?这里我们已经知道了减速电机的输出轴旋转一圈会产生1560个脉冲,四倍频就是6240个,所以可以根据比例来求出轮子的位移(事先用尺子测出轮子一周的长度,比如说100ms有2000个脉冲,那么2000除6240得到比例,再用这个比例乘以轮子的长度求出位移,然后这是在100ms下走的路程,最后用位移除以时间100ms就可以得到电机的速度啦

3.代码实现(STM32F407)

TIM4(脉冲计数),TIM3(时间计数)初始化

#include "sys.h"
#include "delay.h"
#include "led.h"
#include "usart.h"
	u32 mcs;
	float cnt,cnts,speed;
  int g;
void TIM3_Int_Init(u16 arr,u16 psc)
{
	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
	NVIC_InitTypeDef NVIC_InitStructure;
	
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);  ///使能TIM3时钟
	
  TIM_TimeBaseInitStructure.TIM_Period = arr; 	//自动重装载值
	TIM_TimeBaseInitStructure.TIM_Prescaler=psc;  //定时器分频
	TIM_TimeBaseInitStructure.TIM_CounterMode=TIM_CounterMode_Up; //向上计数模式
	TIM_TimeBaseInitStructure.TIM_ClockDivision=TIM_CKD_DIV1; 
	
	TIM_TimeBaseInit(TIM3,&TIM_TimeBaseInitStructure);//初始化TIM3
	
	TIM_ITConfig(TIM3,TIM_IT_Update,ENABLE); //允许定时器3更新中断
	TIM_Cmd(TIM3,ENABLE); //使能定时器3
	
	NVIC_InitStructure.NVIC_IRQChannel=TIM3_IRQn; //定时器3中断
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=0x00; //抢占优先级1
	NVIC_InitStructure.NVIC_IRQChannelSubPriority=0x03; //子优先级3
	NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;
	NVIC_Init(&NVIC_InitStructure);
	
}

void Encoder_Init_TIM4(void)
{
     GPIO_InitTypeDef         GPIO_InitStructure; 
    TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
    TIM_ICInitTypeDef TIM_ICInitStructure;
    NVIC_InitTypeDef NVIC_InitStructure;

    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4,ENABLE); 
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB,ENABLE);//开启GPIOB时钟
  
    GPIO_PinAFConfig(GPIOB,GPIO_PinSource6,GPIO_AF_TIM4);//PB6引脚复用
    GPIO_PinAFConfig(GPIOB,GPIO_PinSource7,GPIO_AF_TIM4);//PB7引脚服用
	
	  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6|GPIO_Pin_7; //GPIOB6,GPIOB7
	  GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_AF;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_OType = GPIO_OType_OD;
    //GPIO_InitStructure.GPIO_PuPd  = GPIO_PuPd_NOPULL ;
	  GPIO_InitStructure.GPIO_PuPd  = GPIO_PuPd_UP;
    GPIO_Init(GPIOB,&GPIO_InitStructure); 

//定时器设置-------------------------------------------------------------  
  
  TIM_TimeBaseInitStructure.TIM_Period = 60000;  //重装载值
    TIM_TimeBaseInitStructure.TIM_Prescaler=0x0;  //预分频
    TIM_TimeBaseInitStructure.TIM_CounterMode=TIM_CounterMode_Up; //向上计数
    TIM_TimeBaseInitStructure.TIM_ClockDivision=TIM_CKD_DIV1; //时钟分割

    TIM_TimeBaseInit(TIM4,&TIM_TimeBaseInitStructure);//初始化TIM3

//编码器模式设置--------------------------------------------------------------                 

    TIM_EncoderInterfaceConfig(TIM4,TIM_EncoderMode_TI12,TIM_ICPolarity_Rising, TIM_ICPolarity_Rising);//计数模式3

    TIM_ICStructInit(&TIM_ICInitStructure); 
    TIM_ICInitStructure.TIM_ICFilter = 10;//滤波器值
    TIM_ICInit(TIM4, &TIM_ICInitStructure);
//溢出中断设置--------------------------------------------------------------  
    TIM_ITConfig(TIM4,TIM_IT_Update,ENABLE); //允许TIM3溢出中断

    NVIC_InitStructure.NVIC_IRQChannel=TIM4_IRQn; 
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=0x01; 
    NVIC_InitStructure.NVIC_IRQChannelSubPriority=0x01; 
    NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;
    NVIC_Init(&NVIC_InitStructure);

 //Reset counter-----------------------------------------------
  TIM_SetCounter(TIM4,0); //TIM3->CNT=0
  TIM_Cmd(TIM4, ENABLE); 


}

void TIM4_IRQHandler(void)
{
		if(TIM_GetITStatus(TIM4,TIM_IT_Update)==SET)//溢出中断
	{
	//这里为空,不需要处理    				   				     	    	
	}				   
	TIM_ClearITPendingBit(TIM4,TIM_IT_Update);  //清除中断标志位
}

//定时器3中断服务函数
void TIM3_IRQHandler(void)
{
	

	 cnt=TIM_GetCounter(TIM4);
	TIM_SetCounter(TIM4,0);
	TIM_SetCounter(TIM3,0);
	if(TIM_GetITStatus(TIM3,TIM_IT_Update)==SET) //溢出中断
	{
		
 LED1=!LED1;//DS1翻转
	g=1;
	}	
	
	TIM_ClearITPendingBit(TIM3,TIM_IT_Update);  //清除中断标志位
	TIM_ClearITPendingBit(TIM4,TIM_IT_Update);  //清除中断标志位
}

下面的是主函数(串口检测数据)

int main(void)
{ 
 
//	u8 t;
//	u8 len;	
//	u16 times=0;

    Encoder_Init_TIM4();
    TIM3_Int_Init(1000,8400);
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置系统中断优先级分组2
	delay_init(168);		//延时初始化 
	uart_init(115200);	//串口初始化波特率为115200
	LED_Init();		  		//初始化与LED连接的硬件接口  
	En_Init();
    pwm_Init(7200-1,0);
	
		
	while(1)
	{
		
		LED0=!LED0;
		if(g==1)
		{
				if(cnt<30000)
   cnt=cnt/6240;	
        else if(cnt>30000)
		{
		cnt=60000-cnt;
		cnt=cnt/6420;
		}
	 	cnt=0.2*cnt;
		 speed=cnt/0.1;	
			printf("%f\n", cnt);
		    printf(" ");
		    printf("%f\r\n",speed);
			g=0;
		}
		IN1=0;
		IN2=1;
		IN3=1;
		IN4=0;
		TIM_SetCompare3(TIM5,5000);
		TIM_SetCompare4(TIM5,5000);
	//	printf("%d\r\n", mcs);
		
		

	}
}

注意的地方

1

这里有个要注意的地方是定时器中断函数中尽可能短,不要太多代码,而且定时器中断函数中不要用printf函数,可能会造成卡死。因为printf本身就用到中断函数。

2

还有一点是电机正转的话编码器模式是从0开始计数,如果反转的话从设置的重装载开始递减计数,我这里就是从60000开始,如何有2000个脉冲,则计数器CNT的值为58000。为了判断出正反转,我的解决方法是CNT在1至30000为正转,30000~60000为反转(因为我这个项目中100ms内正转不会有30000个脉冲产生)。缺点是都是牺牲数据范围来读取。还有一种办法是读取DIR来判断 但有小概率误判现象。用DIR在极端情况下会出现误判,比如一直处于正反转。

3

TIM4中的中断函数要写出来,不然可能会卡死。当然,也可以禁止TIM4的中断,这样就不需要写中断函数了。

你可能感兴趣的:(笔记,简介)