想实现对电机的测速,因此开始接触编码电机。此次采用的是RS365编码器电机。
一、编码电机的初步了解
通过编码电机可以测出速度。常见一般编码电机分成两种,一是光电编码器,另一个是霍尔编码器。有六个接口,两个是电机的正负极,两个是编码器的正负极,还有两个是A,B相。
倍频的概念:如果只采集A/B相的上升/下降沿,即一倍频。若采集A/B的上升和下降沿,即二倍频,最多可以达到四倍频。这样测出来的速度是非常精确的。
具体直观了解可由上图可见。
以上是此次采用的编码电机的各个参数。所以可以得知,减速电机转动一周,可以产生30390个脉冲。若采用四倍频,则可以产生430*390个脉冲,可以大大提高精确度。
软件测量方面,可以采用外部中断捕获上升沿或者下降沿来实现测速。
二、如何测量一定时间内的脉冲
这就要用到TIM的输入捕获功能了,正好之前学习TIM的时候,仅仅涉及到普通PWM波的输出,今天顺便一起学习一下TIMA的输入捕获功能。
1.TIMA
根据手册可知TIMA具有7个输入捕获通道,并且具有广泛的中断功能。
由上图可初步了解定时器A的工作框图。
具体配置方法如下,这个配置方法之中,参阅了许多资料,最后还是只能靠自己一点点啃数据手册,如有不完善,还希望各位一起交流。
第一步:配置IO口
GPIO_setAsPeripheralModuleFunctionInputPin(GPIO_PORT_P1,GPIO_PIN2);
配置P2.0为外设输出
第二步:连续模式初始化
Timer_A_initContinuousModeParam initContParam = {0};
initContParam.clockSource = TIMER_A_CLOCKSOURCE_SMCLK;
initContParam.clockSourceDivider = TIMER_A_CLOCKSOURCE_DIVIDER_1;
initContParam.timerInterruptEnable_TAIE = TIMER_A_TAIE_INTERRUPT_DISABLE;
initContParam.timerClear = TIMER_A_DO_CLEAR;
initContParam.startTimer = false;
Timer_A_initContinuousMode(TIMER_A0_BASE, &initContParam);
第三步:清除比较模式中断
Timer_A_clearCaptureCompareInterrupt(TIMER_A0_BASE,
TIMER_A_CAPTURECOMPARE_REGISTER_1
);
第四步:捕获模式初始化
Timer_A_initCaptureModeParam initCapParam = {0};
initCapParam.captureRegister = TIMER_A_CAPTURECOMPARE_REGISTER_1;
initCapParam.captureMode = TIMER_A_CAPTUREMODE_RISING_EDGE;
initCapParam.captureInputSelect = TIMER_A_CAPTURE_INPUTSELECT_CCIxA;
initCapParam.synchronizeCaptureSource = TIMER_A_CAPTURE_SYNCHRONOUS;
initCapParam.captureInterruptEnable = TIMER_A_CAPTURECOMPARE_INTERRUPT_ENABLE;
Timer_A_initCaptureMode(TIMER_A0_BASE, &initCapParam);
第五步:开始计时,打开全局中断
Timer_A_startCounter( TIMER_A0_BASE,
TIMER_A_CONTINUOUS_MODE
);
__bis_SR_register(LPM0_bits + GIE);
__no_operation();
第六步:中断服务函数
在main.c中,写上输入捕获的中断服务函数
#pragma vector = TIMER0_A1_VECTOR
__interrupt void Timer_A(void)
{
static int CNT = -1;
CNT++;
switch(__even_in_range(TA0IV,14))
{
case 2 :
GPIO_setOutputHighOnPin(GPIO_PORT_P4,GPIO_PIN7);
if(CNT % 2 == 0)
{
edge_count1 = Timer_A_getCaptureCompareCount(TIMER_A0_BASE,TIMER_A_CAPTURECOMPARE_REGISTER_1);
}
else if(CNT % 2 == 1)
{
edge_count2 = Timer_A_getCaptureCompareCount(TIMER_A0_BASE,TIMER_A_CAPTURECOMPARE_REGISTER_1);
}
PWM_Frequency = (unsigned int)(1000000/(abs(edge_count1 - edge_count2) * 0.948));
break;
case 4 : break;
case 10: break;
case 14:break;
default: break;
}
}
中断服务函数中这种编写方式,对于低频50HZ,100HZ是比较准确的。
看效果图
问题:
一旦频率上升超过1Khz,就不准确了。对于此时才用的是MCLK时钟,频率为1.054Mhz,也就是说CNT计数器每次+1,对应的时间是0.948us,按理来说,可测量频率范围应该在0-1.054Mhz之间,而实际可测频率仅有几百HZ,这是为什么呢??
采用一下测量方法,效果依然不好,这是为啥啊啊啊啊啊
下面这部分进行了10倍分频…因此是105400
#pragma vector = TIMER0_A1_VECTOR
__interrupt void Timer_A(void)
{
static int CNT = -1;
CNT++;
switch(__even_in_range(TA0IV,14))
{
case 2 :
PWM_Frequency = 105400/Timer_A_getCounterValue(TIMER_A0_BASE);
Timer_A_clear(TIMER_A0_BASE);
break;
case 4 : break;
case 10: break;
case 14:break;
default: break;
}
}
等我解决后再来继续补充!如果各位大佬有解决办法,也麻烦留言评论一下,谢谢!
问题在昨日晚八点解决!但是具体原因我还是不太清楚为啥。在那之后,我将主频升到16MHZ之后,发现对于频率的测量可以精准到1KHZ误差之内,再改进一下算法,现在误差只有十几赫兹了,代码如下。
基本配置
GPIO_setAsPeripheralModuleFunctionInputPin(GPIO_PORT_P1,GPIO_PIN2);
Timer_A_initContinuousModeParam initContParam = {0};
initContParam.clockSource = TIMER_A_CLOCKSOURCE_SMCLK;
initContParam.clockSourceDivider = TIMER_A_CLOCKSOURCE_DIVIDER_10;
initContParam.timerInterruptEnable_TAIE = TIMER_A_TAIE_INTERRUPT_DISABLE;
initContParam.timerClear = TIMER_A_DO_CLEAR;
initContParam.startTimer = false;
Timer_A_initContinuousMode(TIMER_A0_BASE, &initContParam);
Timer_A_clearCaptureCompareInterrupt(TIMER_A0_BASE,
TIMER_A_CAPTURECOMPARE_REGISTER_1
);
Timer_A_initCaptureModeParam initCapParam = {0};
initCapParam.captureRegister = TIMER_A_CAPTURECOMPARE_REGISTER_1;
initCapParam.captureMode = TIMER_A_CAPTUREMODE_FALLING_EDGE;
initCapParam.captureInputSelect = TIMER_A_CAPTURE_INPUTSELECT_CCIxA;
initCapParam.synchronizeCaptureSource = TIMER_A_CAPTURE_SYNCHRONOUS;
initCapParam.captureInterruptEnable = TIMER_A_CAPTURECOMPARE_INTERRUPT_ENABLE;
Timer_A_initCaptureMode(TIMER_A0_BASE, &initCapParam);
Timer_A_startCounter( TIMER_A0_BASE,
TIMER_A_CONTINUOUS_MODE
);
__bis_SR_register(LPM0_bits + GIE);
__no_operation();
中断服务函数
#pragma vector = TIMER0_A1_VECTOR
__interrupt void Timer_A(void)
{
static unsigned int CNT = 0;
switch(__even_in_range(TA0IV,14))
{
case 2 :
if(CNT % 2 == 0)
{
edge_count1 = Timer_A_getCaptureCompareCount(TIMER_A0_BASE,TIMER_A_CAPTURECOMPARE_REGISTER_1);
}
else if(CNT % 2 == 1)
{
edge_count2 = Timer_A_getCaptureCompareCount(TIMER_A0_BASE,TIMER_A_CAPTURECOMPARE_REGISTER_1);
Timer_A_clear(TIMER_A0_BASE);
}
PWM_Frequency = (unsigned int)(1600000/(float)(abs(edge_count1 - edge_count2)));
break;
case 4 : break;
case 10: break;
case 14:break;
default: break;
}
CNT++;
_BIC_SR_IRQ(LPM0_bits);
}
尤其注意中断服务函数中的最后一句话,作用大概是是能全局中断的同时退出低功耗模式,如果缺少这句话,会使得你的代码一直在跑中断函数,而无法跑主函数!别问我为啥知道,我试了一整个上午才解决!!!望各位避坑!
接下来就是进行测速了!
unsigned int V_detect(unsigned int FRQ)
{
unsigned int speed = 0;
float circle = 3.141*2*0.0325;
speed = (unsigned int)(((float)FRQ/54)*circle);
return speed;
}
这样测速就完成啦!
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)