基于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); //清除中断标志位
}
读取编码器的子函数放在定时中断中,可以方便计算出中断时间内编码器的单位时间转数值。