问题描述
我提供了以下 C 代码来测试 60V DC motor 比率为 16:1 的 gear box 和增量编码器,以查看代码是否通过读取脉冲信号作为 μcontroller 的反馈来计算速度编码器。微控制器是 ATmega88A-UA。编码器 RE56-3-1000 TI 表示 1000 ppr。 特殊场合的直流电机需要以 3-4 转/分的速度运行以完成特定任务。 在较低的速度下,例如低于 5V,电机的启动电压为 1.8V,软件似乎计算出不正确的速度值。 低速时模式为TCCR1B == 0x02,速度值相对较高,255-1071,2V-5V,频率751Hz -3.6kHz。
当模式切换到 TCCR1B == 0x01 时,计算/观察到的速度值(通过调试)范围为 471 - 1140 at 8V - 20V,频率为 6.2kHz - 17.4kHz。
我还观察到模式 TCCR1B 在 0x01 和 0x02 之间不断切换,特别是在 2kHz 和 4kHz 之间的频率。
有没有人了解不正确的值计算可能会带来什么麻烦。是否存在某种时序问题?使用的速度变量是一个无符号整数。我需要你的帮助。 事情很可能没有得到很好的解释。我会尽力给出正确的答案。产品链接包含在说明中。 任何形式的帮助/建议将不胜感激。谢谢
#include "iom88.h"
#include "main.h"
unsigned int Test_Speed_low; //For testing only!!
unsigned int Test_Speed_1; //For testing only!!
unsigned int Test_Speed_2; //For testing only!!
unsigned int Test_Value_Timer_2; //For testing only!!
int main(void)
{
// 1 rpm -> 1000 / 60 = 16,6 pulse/s -> 1000 / 16,6 = 60,25 ms/pulse
// 10 rpm -> 10000 / 60 = 166 pulse/s -> 1000 / 166 = 6,025 ms/pulse
// 120 rpm -> 120000 / 60 = 2000 pulse/s -> 1000 / 2000 = 0,5 ms/pulse
// 2000 rpm -> 2000000 / 60 = 33333 pulse/s -> 1000 / 33333 = 0,03 ms/pulse
// 8 Mhz -> 0,125 us / Takt
// Timer1 CLK/8 -> 1 us / Timer cycle -> 65535 * 1 us = 62 ms -> ~ 1 rpm
// If pulse is faster than 0.5 ms then measure 10 im pulses with
// Timer1 CLK/1 -> 0,125 us / Timer cycle -> 65535 * 0,125 us = 8192 us = 8 ms
// 120 rpm -> 120000 / 60 = 2000 pulse/s -> 10 pulse = 5 ms -> 5000 us / 0,125 us = 40000 Timerticks
// 2000 rpm -> 2000000 / 60 = 33333 pulse/s -> 10 pulse = 0,3 ms -> 300 us / 0,125 us = 2400 Timerticks
PORTB = 0x00; // No Pullups
DDRB = 0x03; // PORTB_Bit0 = 1k3,Bit1 = 0R
PORTC = 0;
DDRC = 0x1F; // Out_0-4 Outputs 47k,22k,11k,5k6,2k7
PORTD = 0x20; // Pull-Up T1 (Bit5)
DDRD = 0x00;
ACSR = 0x80; // disable Analog Comparator
DIDR1 = 0x03; // disable input register on AIN0/AIN1
ADCSRA = 0xC7; // Clock / 128 slower is not possible,no INT
ADMUX = 0xC5; // Int 1,1V Ref,ADC5
DIDR0 = 0x20; // disable input register on ADC5
TCCR0A = 0x02; // T0 CRC mode
TCCR0B = 0x06; // T0 External clock source on falling edge
OCR0A = 1; // CRC int after 1 clocktick
TimsK0 = 0x02; // Timer 0 Compare match A int
TCCR1A = 0x00; // T1 normal mode
TCCR1B = 0x02; // clk/8 source on T1
TimsK1 = 0x01; // Timer 1 Overflow int
Value_TCCR1B = TCCR1B;
Value_Timer1 = 60000;
Min_Timer1 = 60000;
Max_Timer1 = 0;
Counter_Timer1 = 0;
Test_Speed_1 = 0; //For testing only!!
Test_Speed_2 = 0; //For testing only!!
Test_Speed_low = 0; //For testing only!!
EAL = 1;
while(1)
{
if (TCCR1B == 0x02) {
if (Value_Timer1 <= 500) { // < 0,5 ms / pulse
OCR0A = 8; // CRC int after 8 clocktick
TCNT0 = 0; // Reset timer 0 value
TCCR1B = 0; // stop T1
TCNT1 = 0; // Reset timer 1 value
TCCR1B = 1; // clk/1 source on T1
Value_TCCR1B = TCCR1B;
}
} else {
if (Value_Timer1 > 40000) { // > 0,5 ms / 10 pulse //Slow Mode
OCR0A = 1; // CRC int after 1 clocktick
TCNT0 = 0; // Reset timer 0 value
TCCR1B = 0; // stop T1
TCNT1 = 0x1FF;
TCCR1B = 2; // clk/8 source on T1
Value_TCCR1B = TCCR1B;
}
}
if (Counter_Timer1 > 2) {
if (Value_Timer1 < Min_Timer1) {
Min_Timer1 = Value_Timer1;
}
if (Value_Timer1 > Max_Timer1) {
Max_Timer1 = Value_Timer1;
}
if (Counter_Timer1 > 1000) {
Counter_Timer1 = 0;
Min_Timer1 = 60000;
Max_Timer1 = 0;
}
}
if (TCCR1B == 0x02)
{
Test_Value_Timer_2 = (Value_Timer1/10); //For testing
Speed = (60000/Test_Value_Timer_2); // 0,1 RPM,at 60000 uS -> 1 RPM
Test_Speed_low = Speed;
}
else
{
if (Value_Timer1 > 10000)
{
Test_Value_Timer_2 = (Value_Timer1/100); //For Testing
Speed = (48000/Test_Value_Timer_2); // 1 RPM,at 5000 uS -> 120 RPM
Test_Speed_1 = Speed; //For testing only!!
}
else
{
Test_Value_Timer_2 = (Value_Timer1/10); //For testing
Speed = (48000/Test_Value_Timer_2); // 1 RPM,at 5000 uS -> 120 RPM
Speed *= 10;
Test_Speed_2 = Speed; //For testing only!!
}
}
}
}
#pragma vector=INT0_vect
__interrupt void INT0_int(void)
{
}
#pragma vector=INT1_vect
__interrupt void INT1_int(void)
{
}
#pragma vector=TIMER0_COMPA_vect
__interrupt void TIMER0_COMPA_int(void)
{
TCCR1B = 0; // Timer1 Stop
Value_Timer1 = TCNT1;
TCNT1 = 0; // Reset timer 1 value
TCCR1B = Value_TCCR1B;
Counter_Timer1++;
Counter_Int = 1;
}
#pragma vector=TIMER1_OVF_vect
__interrupt void TIMER1_OVF_int(void)
{
if (Timer1_OVF == 2) // Speed < 1,0 RPM
{
Speed = 0;
}
else
{
Timer1_OVF++;
}
}
解决方法
我会说代码存在一些问题,即使不是很清楚代码如何读取编码器。
首先,当您知道 TCNT1 的值(大约为 0)时,只能在中断中处理 TCNT1。否则,您会覆盖计数器并导致数据丢失。
其次,您观察到 TCCR1B 的切换速度非常快,但本不该如此。这种切换机制可能需要一些延迟,否则当电机速度接近阈值时,事情就会变得不稳定。
第三,也许不相关,但是:Timer1_OVF 什么时候清零?
最后,在我看来,所有代码都过于复杂,编写和修补没有明确的意图。例如,变量 Value_TCCR1B 似乎没有用...
如果可能,我会尝试重新开始:以几种不同的速度运行电机,并使用 TCCR1B 的两个可能值测量它们;然后仔细查看数据并尝试重新制定算法。
只是一个想法;祝你好运!