使用 ATmega88

问题描述

我提供了以下 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 的两个可能值测量它们;然后仔细查看数据并尝试重新制定算法。

只是一个想法;祝你好运!