最近几天一直在关注运动控制器的事,纠结这个电子齿轮是怎么实现的,就用STM32试了一下。
我的想法是捕获每个输入脉冲的周期,然后根据齿轮比计算输出脉冲的周期实现输出。测试程序贴在下面。其中用TIM2完成编码器四倍频的程序,同时捕获四倍频后单个脉冲的周期。TIM1实现通过比较输出的PWM模式1实现根据齿轮比换算后的脉冲输出。
我的编码器是600线,四倍频后是2400个脉冲每转,步进电机设定为1200步每转,这样为了编码器和步进电机同速度旋转,只要输出脉冲周期是输入脉冲周期的2倍就可以了,其他的齿轮比就是一个乘法,一个除法,计算也很快的。
目前的问题是,这样做速度基本可以说是同步了,但是位置并不能保证同步,不是严格意义上的电子齿轮。不知道现在的电子齿轮实际是怎么实现的,查了资料看应该是FPGA来实现,思路是这样吗?如果是我也试一下。希望大家多多指点。
#define PPSIN (GPIOA->IDR & 0x03)
u32 tempTIMIndex;
u32 tempTIM[4];
uint32_t ppState;
uint32_t ppState2;
const uint32_t ppStateTab[4] = {0x02, 0x00, 0x03, 0x01};
//const uint32_t ppStateTab[4] = {0x01, 0x03, 0x00, 0x02};
//编码器输入配置
void SSINInit(void)
{
RCC->APB2ENR |= RCC_APB2ENR_IOPAEN;//使能GPIOA的时钟
RCC->APB1ENR |= RCC_APB1ENR_TIM2EN;//使能TIM2的时钟
GPIOA->ODR |= GPIO_ODR_ODR0 + GPIO_ODR_ODR1;
GPIOA->CRL &= ~((GPIO_CRL_MODE0_0 * mode_reset) + (GPIO_CRL_MODE1_0 * mode_reset));
GPIOA->CRL |= ((GPIO_CRL_MODE0_0 * pull_in) + (GPIO_CRL_MODE1_0 * pull_in));
//时基模式
TIM2->CNT = 0;
TIM2->PSC = 0;
TIM2->ARR = 0xffff;
TIM2->CR1 = TIM_CR1_CKD_1+TIM_CR1_URS;//边沿向上计数,选择更新源
//信号输入、滤波
TIM2->CCMR1 = TIM_CCMR1_CC1S_0//IC1映射在TI1
//+ TIM_CCMR1_IC1F_3//采样频率及滤波长度
//+ TIM_CCMR1_IC1F_2
//+ TIM_CCMR1_IC1F_1
//+ TIM_CCMR1_IC1F_0
+ TIM_CCMR1_CC2S_0//IC2映射在IT2
//+ TIM_CCMR1_IC2F_3//采样频率及滤波长度
//+ TIM_CCMR1_IC2F_2
//+ TIM_CCMR1_IC2F_1
//+ TIM_CCMR1_IC2F_0
;
TIM2->CCER = TIM_CCER_CC1E//捕获
+ TIM_CCER_CC2E
//+ TIM_CCER_CC1P
//+ TIM_CCER_CC2P
;
TIM2->SMCR = TIM_SMCR_TS_0//配置从模式为复位模式
+ TIM_SMCR_TS_2
+ TIM_SMCR_SMS_2
;
//配置中断
NVIC_SetPriority(TIM2_IRQn, 1);
NVIC_EnableIRQ(TIM2_IRQn);
//初始化并开启
TIM2->EGR |= TIM_EGR_UG;//初始化缓冲寄存器
TIM2->SR &= ~(TIM_SR_CC1IF + TIM_SR_CC2IF + TIM_SR_UIF);//清除CC1 CC2捕获中断标志、更新中断标志
TIM2->DIER |= (TIM_DIER_CC1IE + TIM_DIER_CC2IE + TIM_DIER_UIE);//开启CC1 CC2捕获中断、更新中断
TIM2->CR1 |= TIM_CR1_CEN;//开启TIM2
}
//编码器输入中断
void TIM2_IRQHandler(void)
{
if (TIM2->SR & TIM_SR_CC1IF)
{
TIM2->SR &= ~TIM_SR_CC1IF;
ppState = PPSIN;
if (ppStateTab[ppState] == ppState2)
{
Y10 = !Y10;
ppState2 = ppState;
TIM2->CCER ^= TIM_CCER_CC1P;//切换边沿
TIM2->SMCR &= ~TIM_SMCR_TS_0;//切换复位信号
TIM2->SMCR |= TIM_SMCR_TS_1;
//totalCounter = ((TIM2OverCounter << 16) + TIM2->CCR1) / 72;
//TIM2OverCounter = 0;
tempTIMIndex++;
if (tempTIMIndex > 3)
{
tempTIMIndex = 0;
}
tempTIM[tempTIMIndex] = TIM2->CCR1;
totalCounter = (tempTIM[0] + tempTIM[1] + tempTIM[2] + tempTIM[3]) >> 2;
TIM1->ARR = totalCounter << 1;
TIM1->CCR1 = totalCounter;
}
}
if (TIM2->SR & TIM_SR_CC2IF)
{
TIM2->SR &= ~TIM_SR_CC2IF;
ppState = PPSIN;
if (ppStateTab[ppState] == ppState2)
{
Y10 = !Y10;
ppState2 = ppState;
TIM2->CCER ^= TIM_CCER_CC2P;//切换边沿
TIM2->SMCR &= ~TIM_SMCR_TS_1;//切换复位信号
TIM2->SMCR |= TIM_SMCR_TS_0;
//totalCounter = ((TIM2OverCounter << 16) + TIM2->CCR2) / 72;
//TIM2OverCounter = 0;
tempTIMIndex++;
if (tempTIMIndex > 3)
{
tempTIMIndex = 0;
}
tempTIM[tempTIMIndex] = TIM2->CCR2;
totalCounter = (tempTIM[0] + tempTIM[1] + tempTIM[2] + tempTIM[3]) >> 2;
TIM1->ARR = totalCounter << 1;
TIM1->CCR1 = totalCounter;
}
}
if (TIM2->SR & TIM_SR_UIF)
{
TIM2->SR &= ~TIM_SR_UIF;
TIM2OverCounter++;
}
}
//脉冲输出配置
void PWMTimerInit(void)
{
RCC->APB2ENR|= RCC_APB2ENR_IOPAEN//使能GPIOA的时钟
+RCC_APB2ENR_TIM1EN;//使能TIM1的时钟
//端口配置
GPIOA->CRH&=~(GPIO_CRH_MODE8_0*mode_reset);
GPIOA->CRH|= (GPIO_CRH_MODE8_0*afio_pp_10);//PA8 TIM1CH1 复用10M输出
//时基模式
TIM1->CNT = 0;
TIM1->PSC = 0;//
TIM1->ARR = 0xffff;//
TIM1->RCR = 0;
TIM1->CR1 = TIM_CR1_ARPE;//使用预装入,边沿向上计数
//比较模式
TIM1->CCR1 = 0x8000;//脉冲低电平宽度,,值加18宽度加1uS
TIM1->CCMR1 = TIM_CCMR1_OC1PE//OC1使能比较寄存器预加载
+(TIM_CCMR1_OC1M_2+TIM_CCMR1_OC1M_1);//OC1使用PWM模式1
//使能输出
TIM1->BDTR = TIM_BDTR_MOE;//TIM1主输出使能
TIM1->CCER = TIM_CCER_CC1E;//OC1输出使能
//配置中断
//NVIC_SetPriority(TIM1_UP_IRQn,15);
//NVIC_EnableIRQ(TIM1_UP_IRQn);
//初始化并开启
TIM1->EGR |= TIM_EGR_UG;//初始化缓冲寄存器
//TIM1->SR &= ~TIM_SR_UIF;//清更新中断标志
//TIM1->DIER |= TIM_DIER_UIE;//开启更新中断
TIM1->CR1 |= TIM_CR1_CEN;//开启TIM1
}
|