一.前言闲谈
前几天24年的电赛刚刚过去,整个四天三夜也是非常记忆深刻的。我们队选择的是H题,我想只要是做控制的,看到这题目列表都会毫不犹豫的选择H题。因为相比其他题目而言H题简直是太简单了,以至于我们小队的几个人在早上8点之后拿到题目,都有点怀疑这个题目是不是有点问题,怎么可能这么简单。就这个题第一眼看过去感觉都比平衡车都简单,但仔细的看过里面的一些规则要求,以及深刻的用过MSPM0G3507这个板子之后你就会发现这个板子没那么简单。说来也是巧合,我们当时准备模块的时候其他控制需要用上的基本所有的模块都已经准备好了,但唯独没有准备MPU6050。就这一点当时给我们造成了十分阻力,让我们在前期有了想去做其他题的准备,但是回头一想我们都准备了这么久的车,再去做其他类型的题目,那之前的准备岂不是白白准备的。就这样整整弄了三天左右,能够达到所有的基本要求,发挥项目也能稳定下来。唯一缺点就是整个系统的速度提不上去,这个也是本次整个电赛唯一的遗憾吧,没能吧速度提上去。好了闲话少谈,接下来回归正题,说一下编码器测速。
二编码器测速的操作原理
首先我们得清楚一点的是:带编码器的电机转一圈,它所输出的脉冲数都是一定的,也就是编码器的分辨率。通过这一点,我们就可以在固定时间内测编码器的脉冲数,这样就可得到电机的转速了。通过这一特点我们就可以得到单倍频的实际小车速度了,设p为电机转动一圈所得到的脉冲数,时间t内得到脉冲数为m。我们所求的转速a如下公式一所示:
这个只是实现了一个简单测速的功能,二倍频,四倍频就是在此基础之上利用编码器的上升沿和下降沿等等物理特性所得到的,这里久不再次描述了,只是简单了解以下即可,我们的目的还是懂得其原理,会利用就行了。
三.STM32端的四倍频编码器测速
以下是STM32测速的具体代码:
#include "encoder.h"
#include "stm32f4xx.h"
#include "pwm.h"
#include "pid.h"
#include"usart.h"
int readshuduzuo,readshuduyou;
int tiaochu=0,c=0;
void Encoder_TIM4_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB ,ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6|GPIO_Pin_7; //TIM4_CH1 TIM4_CH2
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_Init(GPIOB, &GPIO_InitStructure);
GPIO_PinAFConfig(GPIOB,GPIO_PinSource6,GPIO_AF_TIM4);
GPIO_PinAFConfig(GPIOB,GPIO_PinSource7,GPIO_AF_TIM4);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4,ENABLE);
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInitStructure.TIM_Period = 0xffff;
TIM_TimeBaseInitStructure.TIM_Prescaler = 0x00;
TIM_TimeBaseInit(TIM4,&TIM_TimeBaseInitStructure);
//±àÂëÆ÷ģʽ
TIM_ICInitTypeDef TIM_ICInitStructure;
TIM_EncoderInterfaceConfig(TIM4,TIM_EncoderMode_TI12,TIM_ICPolarity_Rising, TIM_ICPolarity_Rising);//TIM_ICPolarity_Rising²¶»ñ·¢ÉúÔÚICxµÄÉÏÉýÑØ
TIM_ICStructInit(&TIM_ICInitStructure); //½«½á¹¹ÌåÖеÄÄÚÈÝȱʡÊäÈë
TIM_ICInitStructure.TIM_ICFilter = 0;//Â˲¨Æ÷Öµ
TIM_ICInit(TIM4, &TIM_ICInitStructure);
TIM_ITConfig(TIM1,TIM_IT_Update,ENABLE);
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel=TIM4_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority=1;
NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;
NVIC_Init(&NVIC_InitStructure);
TIM_SetCounter(TIM4, 8400);
TIM_Cmd(TIM4, ENABLE);
}
void TIM4_IRQHandler(void)
{
if(TIM_GetITStatus(TIM4,TIM_IT_Update)==SET) //Òç³öÖжÏ
{
}
TIM_ClearITPendingBit(TIM4,TIM_IT_Update); //Çå³ýÖжϱê־λ
}
//±àÂëÆ÷ÓÒ tim3 abÏࣺa67
void Encoder_TIM3_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA ,ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6|GPIO_Pin_7; //TIM4_CH1 TIM4_CH2
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_PinAFConfig(GPIOA,GPIO_PinSource6,GPIO_AF_TIM3);
GPIO_PinAFConfig(GPIOA,GPIO_PinSource7,GPIO_AF_TIM3);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInitStructure.TIM_Period = 0xffff;
TIM_TimeBaseInitStructure.TIM_Prescaler = 0x00;
TIM_TimeBaseInit(TIM3,&TIM_TimeBaseInitStructure);
//±àÂëÆ÷ģʽ
TIM_ICInitTypeDef TIM_ICInitStructure;
TIM_EncoderInterfaceConfig(TIM3,TIM_EncoderMode_TI12,TIM_ICPolarity_Rising, TIM_ICPolarity_Rising);//TIM_ICPolarity_Rising²¶»ñ·¢ÉúÔÚICxµÄÉÏÉýÑØ
TIM_ICStructInit(&TIM_ICInitStructure); //½«½á¹¹ÌåÖеÄÄÚÈÝȱʡÊäÈë
TIM_ICInitStructure.TIM_ICFilter = 0;//Â˲¨Æ÷Öµ
TIM_ICInit(TIM3, &TIM_ICInitStructure);
TIM_ITConfig(TIM3,TIM_IT_Update,ENABLE);
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel=TIM3_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority=1;
NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;
NVIC_Init(&NVIC_InitStructure);
TIM_SetCounter(TIM3, 0);
TIM_Cmd(TIM3, ENABLE);
}
void TIM3_IRQHandler(void)
{
if(TIM_GetITStatus(TIM3,TIM_IT_Update)==SET) //Òç³öÖжÏ
{
}
TIM_ClearITPendingBit(TIM3,TIM_IT_Update); //Çå³ýÖжϱê־λ
}
void TIM2_Int_Init(u16 arr,u16 psc)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,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(TIM2,&TIM_TimeBaseInitStructure);//³õʼ»¯TIM3
TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE); //ÔÊÐí¶¨Ê±Æ÷3¸üÐÂÖжÏ
TIM_Cmd(TIM2,ENABLE); //ʹÄܶ¨Ê±Æ÷3
NVIC_InitStructure.NVIC_IRQChannel=TIM2_IRQn; //¶¨Ê±Æ÷3ÖжÏ
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=0x03; //ÇÀÕ¼ÓÅÏȼ¶1
NVIC_InitStructure.NVIC_IRQChannelSubPriority=0x03; //×ÓÓÅÏȼ¶3
NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;
NVIC_Init(&NVIC_InitStructure);
}
void TIM2_IRQHandler(void)//¶¨Ê±(10ms)¶ÁÈ¡±àÂëÆ÷Öµ(¼´ËÙ¶È),³¬Éù²¨²â¾à
{
if(TIM_GetITStatus(TIM2,TIM_IT_Update)==SET) //Òç³öÖжÏ
{ shu=shu+1;
readshuduzuo=(int16_t)TIM_GetCounter(TIM4);
readshuduyou=(int16_t)TIM_GetCounter(TIM3);
TIM_SetCounter(TIM4,0);
TIM_SetCounter(TIM3,0);
if(biaozhi==1&&tiaochu==0){
v=Velocity(mubiaoshudu,readshuduzuo,readshuduyou);
r=Vertical(167,y);
Set_Pwm(r+v,-r+v);
}
//1Í£Ö¹
if (a1==1 && x==2 && biaozhi==1)
{ tiaochu=1;
//Set_Pwm(0,0);
Velocity_Ki=0;
v=Velocity(0,readshuduzuo,readshuduyou);
Set_Pwm(v,v);
if(readshuduzuo==0 ||readshuduyou==0)
{
Set_Pwm(0,0);
}
}
//2Í£Ö¹
if (a1==2 && x==4 && biaozhi==1&&da==0)
{ tiaochu=1;
Set_Pwm(0,0);
}
//3Í£
if (a1==3 && w==5 && biaozhi==1&&da==1)
{Serial_SendByte(1);
c=1;
tiaochu=1;
Vertical_Kp=0;
Vertical_Kd=0;
Set_Pwm(0,0);
}
if (a1==0&&x==1&&biaozhi==1&&da==0)
{Serial_SendByte(1);
c1=1;
tiaochu=1;
Vertical_Kp=0;
Vertical_Kd=0;
Velocity_Kp=-20000;
Velocity_Ki=0;
v=Velocity(0,readshuduzuo,readshuduyou);
Set_Pwm(v,v);
}
}
TIM_ClearITPendingBit(TIM2,TIM_IT_Update); //Çå³ýÖжϱê־λ
}
TIM2_Int_Init(8400-1,50-1); 定时器2的重装载值和分频系数(定时10ms)
利用TIM3,TIM4作为编码器模式读取脉冲数,在TIM2中得到左右两边的具体编码器值。这里简单说明一下为什么还需要一个定时器TIM2来定时10ms去读取两边编码器的值,并且在读取之后又立马清零。TIM2每格10ms记录一次左右两边的脉冲数,这样就像相当于得到了你所要控制物体(电机)的速度,如果将此时的脉冲数除以电机转动一圈的脉冲数,让后再除以10ms,你就可以得到此时电机转动的真实速度了。清零是为了下一次测速的准确性。
四MSPM0G3507端的二倍频测速
以下是基于MSPG3507端的编码器测速源码:
#include "econder.h"
#include <ti/driverlib/m0p/dl_interrupt.h>
#include "ti/driverlib/dl_gpio.h"
uint8_t x,y,z,sum,x1,y1,z1,sum1;
int32_t left_count=0,right_count=0;
void Exit_Init(void)
{
NVIC_EnableIRQ(GPIOB_INT_IRQn);
}
void GROUP1_IRQHandler(void)
{
switch(DL_Interrupt_getPendingGroup(DL_INTERRUPT_GROUP_1))
{
case DL_INTERRUPT_GROUP1_IIDX_GPIOB:
if(DL_GPIO_getEnabledInterruptStatus(GPIOB, DL_GPIO_PIN_16))
{
x=1;//AÖжϣ¬Ôòx=1;BÖжϣ¬Ôòx=0;
//ÖжϷ¢Éúʱ£¬AÏà¸ßµçƽ£¬y=1£»·´Ö®y=0;
if(DL_GPIO_readPins(GPIOB,DL_GPIO_PIN_16)) y=1;
else y=0;
//ÖжϷ¢Éúʱ£¬BÏà¸ßµçƽ£¬z=1£»·´Ö®z=0;
if(DL_GPIO_readPins(GPIOB,DL_GPIO_PIN_0)) z=1;
else z=0;
sum=x+y+z;//ÇóºÍÅжÏת¶¯·½Ïò£¬Å¼ÊýÕýת£¬ÆæÊý·´×ª
if(sum==0||sum==2) left_count++;
else left_count--;
DL_GPIO_clearInterruptStatus(GPIOB, DL_GPIO_PIN_16);
}
if(DL_GPIO_getEnabledInterruptStatus(GPIOB, DL_GPIO_PIN_0))
{
x=0;//AÖжϣ¬Ôòx=1;BÖжϣ¬Ôòx=0;
//ÖжϷ¢Éúʱ£¬AÏà¸ßµçƽ£¬y=1£»·´Ö®y=0;
if(DL_GPIO_readPins(GPIOB,DL_GPIO_PIN_16)) y=1;
else y=0;
//ÖжϷ¢Éúʱ£¬BÏà¸ßµçƽ£¬z=1£»·´Ö®z=0;
if(DL_GPIO_readPins(GPIOB,DL_GPIO_PIN_0)) z=1;
else z=0;
sum=x+y+z;//ÇóºÍÅжÏת¶¯·½Ïò£¬Å¼ÊýÕýת£¬ÆæÊý·´×ª
if(sum==0||sum==2) left_count++;
else left_count--;
DL_GPIO_clearInterruptStatus(GPIOB, DL_GPIO_PIN_0);
}
//
if(DL_GPIO_getEnabledInterruptStatus(GPIOB, DL_GPIO_PIN_20))
{
x1=1;//AÖжϣ¬Ôòx=1;BÖжϣ¬Ôòx=0;
//ÖжϷ¢Éúʱ£¬AÏà¸ßµçƽ£¬y=1£»·´Ö®y=0;
if(DL_GPIO_readPins(GPIOB,DL_GPIO_PIN_20)) y1=1;
else y1=0;
//ÖжϷ¢Éúʱ£¬BÏà¸ßµçƽ£¬z=1£»·´Ö®z=0;
if(DL_GPIO_readPins(GPIOB,DL_GPIO_PIN_13)) z1=1;
else z1=0;
sum1=x1+y1+z1;//ÇóºÍÅжÏת¶¯·½Ïò£¬Å¼ÊýÕýת£¬ÆæÊý·´×ª
if(sum1==0||sum1==2) right_count++;
else right_count--;
DL_GPIO_clearInterruptStatus(GPIOB, DL_GPIO_PIN_20);
}
if(DL_GPIO_getEnabledInterruptStatus(GPIOB, DL_GPIO_PIN_13))
{
x1=0;//AÖжϣ¬Ôòx=1;BÖжϣ¬Ôòx=0;
//ÖжϷ¢Éúʱ£¬AÏà¸ßµçƽ£¬y=1£»·´Ö®y=0;
if(DL_GPIO_readPins(GPIOB,DL_GPIO_PIN_20)) y1=1;
else y1=0;
//ÖжϷ¢Éúʱ£¬BÏà¸ßµçƽ£¬z=1£»·´Ö®z=0;
if(DL_GPIO_readPins(GPIOB,DL_GPIO_PIN_13)) z1=1;
else z1=0;
sum1=x1+y1+z1;//ÇóºÍÅжÏת¶¯·½Ïò£¬Å¼ÊýÕýת£¬ÆæÊý·´×ª
if(sum1==0||sum1==2) right_count++;
else right_count--;
DL_GPIO_clearInterruptStatus(GPIOB, DL_GPIO_PIN_13);
}
break;
}
}
由于TI的MSPM0G3507这款办卡必须得用图形化界面来初始化和配置一系列的IO口定时器,PWM,中断,等等。其只有7个定时器,虽然也有编码器模式,但是不管如何配置只能够配置出来一个编码器。所有只能用IO口输入捕获的方式来测速了,第一个编码的AB项为PB0、16,第二个编码的AB项为PB20,13只需要将这四个io口配置成为输入方式,拉低就可以利用了。当然还有最后重要的一点还是多长时间读取一次,也就是定时器如何设置。具体如下:
#include "tim.h"
#include <ti/driverlib/dl_timera.h>
#include "ti/driverlib/dl_gpio.h"
#include "usart.h"
#include "usart1.h"
#include "kme.h"
#include "led.h"
extern int32_t left_count;
extern int32_t right_count;
extern int32_t left_now;
extern int32_t right_now;
extern int32_t Speed;
extern int cx;
void TimerA1_Init(void)
{
NVIC_EnableIRQ(TIMA1_INT_IRQn);
DL_Timer_startCounter(TIMA1);
}
void TIMA1_IRQHandler(void)
{
switch (DL_TimerA_getPendingInterrupt(TIMA1))
{
case DL_TIMERA_IIDX_ZERO:
// receive_data_usart();
//ÉãÏñͷʶ±ð
cx=USART_RX_BUF[0];
//±àÂëÆ÷¶ÁÈ¡ËÙ¶È
left_now=-left_count;
right_now=-right_count;
Speed=(left_now+right_now)/2;
//
kfp_x.source=cx;
kfp_x.out=KalmanFilter(&kfp_x,kfp_x.source);
//±àÂëÆ÷ËÙ¶ÈÇå0
left_count=0;
right_count=0;
break;
default:
break;
}
}
配置图如下:
以上就是所需的一些具体配置图。本次的编码器测速具体操作也就如上所示了,希望这些对大家有所帮助。