作者: | 来源:互联网 | 2023-08-24 09:53
一、前言
由于单片机硬件资源稀缺,有些时候无法使用正交编码的方式获取速度,可使用单脉冲ETR功能获取转速,通过软件设定的转速判定方向。其他方案缺点:比如使用外部中断方式,来一个信号触发一次中断,在中断程序里实现计数累加,但由于被统计的信号频率较高,而中断本身也是需要时间的,往往导致有些脉冲没被统计而发生丢数的问题。况且,CPU这样频繁地去响应中断还会衍生出其它系统性问题。
对于这种情况,我们可以将被统计信号连接到定时器的ETR脚,并作为定时器的计数时钟,择时读取计数器的值和溢出次数即可。这样既避免了CPU频繁进中断而无法应对别的事情的困境,也避免了因CPU优先忙于别的事情而来不及响应外部中断导致计数出错的麻烦。对于STM32(GD32)来讲,从ETR脚引入时钟信号,可以有两种模式。
第一种模式,即外部时钟1模式,此时来自ETR脚的信号经过滤波、边沿检测和极性选择后,以触发信号的角色连接到从模式控制器,并作为定时器的时钟源,即下图中的1路。
第二种模式,即外部时钟2模式,来自ETR脚的时钟信号经过极性选择、分频、滤波后不经过从模式控制器,而是像内部时钟源一样直接为计数器提供计数时钟,即下图中的2路。
二、编码器配置
1、GPIO配置
static void GPIOConfiguration(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB |RCC_APB2Periph_AFIO, ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB2Periph_TIM1, ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable, ENABLE); // 关闭JTAG功能
// GPIO config
GPIO_InitStructure.GPIO_Pin = ENCODER_LEFT_A_PIN;// | ENCODER_LEFT_B_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_PinRemapConfig(GPIO_PartialRemap1_TIM2, ENABLE);//重映射TIM2的CH1、CH2到PA15和PB3
// GPIO config
GPIO_StructInit(&GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = ENCODER_RIGHT_A_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
}
2、Timer配置
static void encoderConfiguration(void)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1 | RCC_APB1Periph_TIM2, ENABLE);
TIM_DeInit(ENCODER_LEFT_TIMER);
TIM_DeInit(ENCODER_RIGHT_TIMER);
TIM_TimeBaseStructure.TIM_Prescaler = 0x0;
TIM_TimeBaseStructure.TIM_Period = 65535;
TIM_TimeBaseStructure.TIM_ClockDivision = 0x0;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(ENCODER_LEFT_TIMER, &TIM_TimeBaseStructure);
TIM_TimeBaseInit(ENCODER_RIGHT_TIMER, &TIM_TimeBaseStructure);
//选择ETR通过外部时钟2输入时钟
TIM_ETRClockMode2Config(ENCODER_LEFT_TIMER, TIM_ExtTRGPSC_OFF, TIM_ExtTRGPolarity_NonInverted, 0);
TIM_ETRClockMode2Config(ENCODER_RIGHT_TIMER, TIM_ExtTRGPSC_OFF, TIM_ExtTRGPolarity_NonInverted, 0);
TIM_ClearITPendingBit(ENCODER_LEFT_TIMER,TIM_IT_Update);
TIM_ClearITPendingBit(ENCODER_RIGHT_TIMER,TIM_IT_Update);
TIM_ITConfig(ENCODER_LEFT_TIMER,TIM_IT_Update,ENABLE);
TIM_ITConfig(ENCODER_RIGHT_TIMER,TIM_IT_Update,ENABLE);
TIM_SetCounter(ENCODER_LEFT_TIMER, 0);
TIM_SetCounter(ENCODER_RIGHT_TIMER, 0);
TIM_Cmd(ENCODER_LEFT_TIMER, ENABLE);
TIM_Cmd(ENCODER_RIGHT_TIMER, ENABLE);
}
3、部分函数接口可供参考
函数名称 | 功能 |
---|
TIM_DeInit | 恢复缺省配置 |
TIM_TimeBaseInit | 时基单元初始化 |
TIM_TimeBaseStructInit | 赋给结构体变量一个默认值 |
TIM_Cmd | 使能计数器 |
TIM_ITConfig | 使能中断输出信号 |
TIM_InternalClockConfig | 选择内部时钟 |
TIM_ITRxExternalClockConfig | 选择ITRx其他定时器的时钟 |
TIM_TIxExternalClockConfig | 选择TIx捕获通道的时钟 |
TIM_ETRClockMode1Config | 选择ETR通过外部时钟1输入的时钟 |
TIM_ETRClockMode2Config | 选择ETR通过外部时钟2输入的时钟 |
TIM_ETRConfig | 单独配置ETR引脚的预分频器,极性,滤波器 |
TIM_PrescalerConfig | 单独配置预分频值 |
TIM_CounterModeConfig | 改变计数器的计数模式 |
TIM_ARRPreloadConfig | 自动重装器预装功能配置 |
TIM_SetCounter | 给计数器写入一个值 |
TIM_SetAutoreload | 给自动重装器写入一个值 |
TIM_GetCounter | 获取当前计数器的值 |
TIM_GetPrescaler | 获取当前预分频器的值 |
三、转速获取
由于采用的ETR单脉冲获取转速故无法感知反向,在故编码器反馈的速度会一直为正的。在此项目中获取转速的频率为250HZ,g_encoder.right_encoder_delta为两次读取的脉冲数的差,g_encoder.right_encoder_delta * ENCODER_UPDATE_FREQUENCY及得到脉冲的频率。在宏定义中有以下参数,脉冲的频率除以理论一圈的脉冲数就能得到抹布的转速。
#define WHEEL_PPR 1 // ETR不倍频
#define WHEEL_POLAR_NUM 4 // 极对数(4对8极)
#define GERA_RATIO 30.82 //减速比
#define PULSE_PER_CIRCLE (s32)(WHEEL_POLAR_NUM * WHEEL_PPR * GERA_RATIO) //493.12 一圈理论的脉冲数
转速1获取
static void updateLeftEncoder(void)
{
TMopMotor left_mop;
getMopMotorInfor(&left_mop);
if(g_encoder.left_first_measure_flag == FALSE)
{
g_encoder.left_encoder = (int16_t) ENCODER_LEFT_TIMER->CNT;
g_encoder.left_encoder_delta = g_encoder.left_encoder - g_encoder.last_left_encoder ;
// Counter down, the pulse decrease
g_encoder.last_left_encoder = g_encoder.left_encoder;
g_encoder.left_speed = (s32)g_encoder.left_encoder_delta * ENCODER_UPDATE_FREQUENCY;
g_encoder.left_rpm = g_encoder.left_speed * 60 / PULSE_PER_CIRCLE ;
g_encoder.left_encoder_sum += g_encoder.left_encoder_delta;
}
else
{
g_encoder.left_first_measure_flag = FALSE;
g_encoder.left_encoder = 0;
g_encoder.last_left_encoder = g_encoder.left_encoder;
g_encoder.left_encoder_delta = g_encoder.left_encoder - g_encoder.last_left_encoder;
g_encoder.left_encoder_sum = g_encoder.left_encoder;
g_encoder.left_encoder_overflow = 0;
g_encoder.left_speed = 0;
ENCODER_LEFT_TIMER->CNT = g_encoder.left_encoder;
}
}
转速2获取
static void updateRightEncoder(void)
{
TMopMotor right_mop;
getMopMotorInfor(&right_mop);
if(g_encoder.right_first_measure_flag == FALSE)
{
g_encoder.right_encoder = ENCODER_RIGHT_TIMER->CNT;
g_encoder.right_encoder_delta = (g_encoder.right_encoder - g_encoder.last_right_encoder);
// Counter down, the pulse decrease
g_encoder.last_right_encoder = g_encoder.right_encoder;
g_encoder.right_speed = (s32)g_encoder.right_encoder_delta * ENCODER_UPDATE_FREQUENCY;
g_encoder.right_rpm = g_encoder.right_speed * 60 / PULSE_PER_CIRCLE ;
g_encoder.right_encoder_sum += g_encoder.right_encoder_delta;
}
else
{
g_encoder.right_first_measure_flag = FALSE;
g_encoder.right_encoder = 0;
g_encoder.last_right_encoder = g_encoder.right_encoder;
g_encoder.right_encoder_delta = g_encoder.right_encoder - g_encoder.last_right_encoder;
g_encoder.right_encoder_sum = g_encoder.right_encoder;
g_encoder.right_encoder_overflow = 0;
g_encoder.right_speed = 0;
ENCODER_RIGHT_TIMER->CNT = g_encoder.right_encoder;
}
四、PID调节
实际最终的PID参数需根据实际项目进行调整,已确保响应快、抗干扰能力强。
1.PID调试一般原则
a.在输出不振荡时,增大比例增益P。
b.在输出不振荡时,减小积分时间常数Ti。
c.在输出不振荡时,增大微分时间常数Td。
2.一般步骤
a.确定比例增益P
确定比例增益P 时,首先去掉PID的积分项和微分项,一般是令Ti=0、Td=0(具体见PID的参数设定说明),使PID为纯比例调节。输入设定为系统允许的最大值的60%~70%,由0逐渐加大比例增益P,直至系统出现振荡;再反过来,从此时的比例增益P逐渐减小,直至系统振荡消失,记录此时的比例增益P,设定PID的比例增益P为当前值的60%~70%。比例增益P调试完成。
b.确定积分时间常数Ti
比例增益P确定后,设定一个较大的积分时间常数Ti的初值,然后逐渐减小Ti,直至系统出现振荡,之后在反过来,逐渐加大Ti,直至系统振荡消失。记录此时的Ti,设定PID的积分时间常数Ti为当前值的150%~180%。积分时间常数Ti调试完成。
c.确定积分时间常数Td
积分时间常数Td一般不用设定,为0即可。若要设定,与确定 P和Ti的方法相同,取不振荡时的30%。
d.系统空载、带载联调,再对PID参数进行微调,直至满足要求