基于STM32f4的编码器应用

简介

(1)增量型:
每转过单位的角度就发出一个脉冲信号,通常为A相、B相(某些包括Z相)输出。A相、B相为相互延迟1/4周期的脉冲输出(即正交信号),根据延迟关系可以区别正反转,而且通过取A相、B相的上升和下降沿可以进行2或4倍频。Z相为单圈脉冲,即每圈发出一个脉冲,常用于校正累计误差。
(2)绝对值型:
对应一圈,每个基准的角度发出一个唯一与该角度对应二进制的数值,通过外部记圈器件可以进行多个位置的记录和测量。

编码器波形测试

有时候我们新买一个编码器,我们可以通过示波器查看编码器的脉冲波形;给编码器接上电源,示波器地与编码器地连接,编码器输出端与示波器探头连接即可。

代码

Encoder_Timer_Overflow计数溢出次数,设置为389*4是因为我的编码器的390线的,方便计算。编码器模式设置为模式3。

#include "encode.h"
#include "sys.h"

int Encoder_Timer_Overflow;                                      //编码器溢出次数(每389*4溢出一次)
u16 Previous_Count;                                              //上次TIM3->CNT的值

void TIM3_Encode_init(u16 arr,u16 psc)
{
	GPIO_InitTypeDef GPIO_InitStructure;
	NVIC_InitTypeDef  NVIC_InitStructure;
  TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
  TIM_ICInitTypeDef TIM_ICInitStructure;    

  RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);    
  RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);  
		
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7;          //GPIOA6和GPIOA7
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;                    //复用模式
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;              //速度100MHz
  GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;                //浮空	
  GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;                  //推挽复用输出
  GPIO_Init(GPIOA, &GPIO_InitStructure);                          //初始化PA6和PA7

  GPIO_PinAFConfig(GPIOA,GPIO_PinSource6,GPIO_AF_TIM3);           //GPIOA6复用为定时器3通道1
	GPIO_PinAFConfig(GPIOA,GPIO_PinSource7,GPIO_AF_TIM3);           //GPIOA7复用为定时器3通道2
	
  TIM_TimeBaseStructure.TIM_Period = arr; 	                      //(编码器线数-1)*4	四倍频原理
	TIM_TimeBaseStructure.TIM_Prescaler=psc;                        //定时器分频
	TIM_TimeBaseStructure.TIM_CounterMode=TIM_CounterMode_Up;       //向上计数模式
	TIM_TimeBaseStructure.TIM_ClockDivision=TIM_CKD_DIV1;           //时钟分频因子,不分频
	TIM_TimeBaseInit(TIM3,&TIM_TimeBaseStructure);                  //初始化TIM3
	
	TIM_ICInitStructure.TIM_Channel=TIM_Channel_1;                  //选择输入端IC1映射到TI1上
  TIM_ICInitStructure.TIM_ICPolarity=TIM_ICPolarity_Rising;	      //上升沿捕获
  TIM_ICInitStructure.TIM_ICSelection=TIM_ICSelection_DirectTI;   //映射到TI1上
  TIM_ICInitStructure.TIM_ICPrescaler=TIM_ICPSC_DIV1;	            //配置输入分频,不分频 
  TIM_ICInitStructure.TIM_ICFilter =6;                            //配置输入滤波器
  TIM_ICInit(TIM3,&TIM_ICInitStructure);
	
	TIM_ICInitStructure.TIM_Channel=TIM_Channel_2;                  //选择输入端IC2映射到TI2上
  TIM_ICInitStructure.TIM_ICPolarity=TIM_ICPolarity_Rising;	      //上升沿捕获
  TIM_ICInitStructure.TIM_ICSelection=TIM_ICSelection_DirectTI;   //映射到TI2上
  TIM_ICInitStructure.TIM_ICPrescaler=TIM_ICPSC_DIV1;	            //配置输入分频,不分频 
  TIM_ICInitStructure.TIM_ICFilter=6;                             //配置输入滤波器
  TIM_ICInit(TIM3,&TIM_ICInitStructure);
	
	TIM_EncoderInterfaceConfig(TIM3,TIM_EncoderMode_TI12,TIM_ICPolarity_Rising,TIM_ICPolarity_Rising );//编码器配置(定时器、编码模式、上升沿、上升沿)
		
  NVIC_InitStructure.NVIC_IRQChannel=TIM3_IRQn;                   //定时器3中断分组配置
	NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;                   //使能
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=0x01;      //抢占优先级1
	NVIC_InitStructure.NVIC_IRQChannelSubPriority =0x02;            //响应优先级2
	NVIC_Init(&NVIC_InitStructure);                                 //配置定时器3
		
	TIM_ITConfig(TIM3,TIM_IT_Update,ENABLE);                        //允许定时器3更新中断
	TIM_Cmd(TIM3,ENABLE);                                           //使能定时器3
}

void TIM3_IRQHandler(void)                                        //定时器3中断服务函数
{
	if(TIM_GetITStatus(TIM3,TIM_IT_Update)==SET)                    //溢出中断
	{   
		Encoder_Timer_Overflow++;     		
	}
	TIM_ClearITPendingBit(TIM3,TIM_IT_Update);                      //清除中断标志位
}

u32 Read_Encoder(void)
{
  u32 Count;                                                      //一段时间内转过的脉冲数
  u16 Current_Count;                                              //当前TIM3->CNT的值
	u16 Enc_Timer_Overflow_one;	                                   

	Enc_Timer_Overflow_one=Encoder_Timer_Overflow;                  
  Current_Count = TIM_GetCounter(TIM3);                           //获得当前TIM3->CNT的值
  Encoder_Timer_Overflow=0;                                       //清零,方便下次计算
  Count = (u32)(Current_Count - Previous_Count + (Enc_Timer_Overflow_one) * (4*ENCODER_PPR));   //计算出一个时间转过的脉冲数
  Previous_Count = Current_Count;  
  return(Count);
}

主函数:

#include "sys.h"
#include "delay.h"
#include "usart.h"
#include "led.h"
#include "lcd.h"
#include "motor.h"
#include "timer.h"
#include "pwm.h"
#include "key.h"
#include "encode.h"

extern int Encoder_Timer_Overflow;

int PWM=100;
u32 encode;
float speed,t;

int main(void)
{ 
	int k;
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);  //设置系统中断优先级分组2
	delay_init(168);                                 //初始化延时函数
	uart_init(115200);		                           //初始化串口波特率为115200
	LED_Init();					                             //初始化LED
	KEY_Init();					                             //初始化按键
 	LCD_Init();                                      //初始化LCD FSMC接口
	TIM3_Encode_init((ENCODER_PPR)*4,1-1);         //编码器初始化
	TIM4_PWM_Init(100-1,84-1);                       //84M/100/84=10KHZ  //GPIOB6复用为定时器4  
	TIM5_Int_Init(500-1,8400-1);                     //84M/8400=10Khz,10Khz/500 = 20hz 1秒    
	while(1)
	{
		k=KEY_Scan(0);                                 //得到键值	
	  if(k)
		{
			switch(k)                                    //根据不同按键返回值执行不同任务
			{																
			  case KEY0_PRES:                            //KEY0键增加电机转速                                           
            PWM+=20;
				    if(PWM>100)
					    PWM=0;
						break;				
			  case KEY1_PRES:                            //KEY1键用减小电机转速
            PWM-=20;
				    if(PWM<0)
					    PWM=100;
						break;						
			}
		}	
		TIM_SetCompare1(TIM4,PWM);  
	}
}

void TIM5_IRQHandler(void)                          //定时器5中断服务函数
{
	if(TIM_GetITStatus(TIM5,TIM_IT_Update)==SET)      //溢出中断
	{  
		encode=Read_Encoder();                          //获得编码器的值
		printf("编码器脉冲数为:%d\r\n",encode);
		speed=encode/390.0f/4.0f/0.05f;
		printf("电机转速为:%f\r\n",speed);		
	}
	TIM_ClearITPendingBit(TIM5,TIM_IT_Update);        //清除中断标志位
}

读取编码器的子函数放在定时中断中,可以方便计算出中断时间内编码器的单位时间转数值。