使用AVR设置多个计时器

时间:2013-10-13 11:32:38

标签: c timer avr pwm

我正在尝试使用Teensy 2.0微控制器(基于ATMEGA32U4 8位AVR 16 MHz)设置两个定时器中断例程,以便独立控制两个伺服电机

经过多次试验 - 我能够在端口C的引脚7上设置一个,但是

  1. 如何将第二个ISR设置为独立于第一个ISR进行初始化和调用?
  2. 我是否需要设置第二个计时器,如果是,那么这些代码会是什么样的?
  3. 以下是设置代码:

    int main(void)
    {
        DDRE = 0xFF; 
    
        TCCR1A |= 1 << WGM12;       // Configure timer 1 for CTC mode 
        TCCR1B = (1<<WGM12) | (1<<CS11) ;
    
        OCR1A = 1000;   // initial            
    
        TIMSK1 |= 1 << OCIE1A;      // Output Compare A Match Interrupt Enable 
        sei();                      // enable interrupts 
    
        // ...code that sets pulseWidth based on app logic variable. 
        // Not showing as its not important
    }
    
    
    ISR(TIMER1_COMPA_vect)
    { 
        if (0 == pulseWidth)
        {
            return;
        }
    
        static uint8_t state = 0;
        int dutyTotal = 20*1000;    
    
        if (0 == state)
        {
            PORTC |= 0b10000000; 
            OCR1A = pulseWidth; 
            state = 1;
        }
        else if (1 == state)
        {
            PORTC &= 0b01111111; 
            OCR1A = dutyTotal - pulseWidth; 
            state = 0;
        }
    }
    

2 个答案:

答案 0 :(得分:1)

虽然很难在不了解更多应用程序的情况下给出明确的答案(例如,什么样的伺服/电机, - 我猜的模型RC类型有1-2ms pule?),有两种方法可以解决这个问题:

首先,在您的代码中,您似乎通过切换PC7手动生成PWM信号。您可以通过增加状态数来添加另一个输出 - 您需要比伺服数量多一个来提供设置脉冲重复频率的间隙。这是一种常见的技术,当你需要驱动大量伺服系统时,因为大多数RC伺服系统并不关心脉冲相位或频率(在极限范围内),只关心脉冲宽度,所以你可以产生一堆不同的脉冲。其他在不同的输出上,而只使用这样的一个计时器(在一种伪代码状态图中):

State 0:
Turn on output 1
Set timer TOP to pulse duration 1.
Go to state 1:

State 1:
Turn off output 1
Turn on output 2
Set timer TOP to pulse duration 1.
Go to state 2:

State 2:
Turn off output 2
Set timer TOP to pulse duration 3.
Go to state 0:

“脉冲持续时间3”设置PRF(脉冲重复频率)。如果你想得到花哨, 您可以将其设置为1 / f-pd1-pd2,以提供恒定的频率。

[“TOP”是AVR,用于设置定时器的包装(溢出)速率。见数据表。 ]

其次,如果您只使用两个伺服器,则有一种更简单的方法 - 使用定时器的硬件PWM功能。 AVR定时器具有内置PWM功能,可以为您进行引脚切换。 mega32上的Timer1有两个PWM输出引脚,可以很好地适用于你的两个伺服系统,然后你根本不需要中断处理程序。 如果您是直接PWM驱动电机(例如通过H桥),这也是正确的解决方案。

为此,您需要将定时器置于PWM模式并启用OC1A和OC1B输出引脚,例如

/*
 * Set fast PWM mode on OC1A and OC1B with ICR1 as TOP
 * (Mode 14)
 */
TCCR1A = (1 << WGM11) | (1 << COM1B1) | (1 << COM1A1);
TCCR1B = (3 << WGM12);

/*
 * Clock source internal, pre-scale by 8
 * (i.e. count rate = 2MHz for 16MHz crystal)
 */
TCCR1B |= (1 << CS11);

/*
 * Set counter TOP value to set pulse repetition frequency.
 * E.g. 50Hz (good for RC servos):
 * 2e6/50 = 40000. N.B. This must be less than 65535.
 * We count from t down to 0 so subtract 1 for true freq.
 */
ICR1 = 40000-1;

/* Enable OC1A and OC1B PWM output */
DDRB |= (1 << PB5) | (1 << PB6);

/* Uncomment to enable TIMER1_OVF_vect interrupts at 50Hz */
/* TIMSK1 = (1 << TOV1); */

/*
 * Set both servos to centre (1.5ms pulse).
 * Value for OCR1x is 2000 per ms then subtract one.
 */
OCR1A = 3000-1;
OCR1B = 3000-1;

免责声明 - 此代码片段已编译但我尚未在实际设备上进行检查,因此您可能需要仔细检查寄存器值。请参阅http://www.atmel.com/Images/doc7766.pdf

上的完整数据表

另外,你的代码中可能有一些拼写错误,TCC1A中不存在WGM12位(你实际上设置了第3位,即FOC1A - “强制比较”,请参阅数据表。)另外,你正在编写DDRE启用端口E上的输出,但切换端口C上的引脚。

答案 1 :(得分:0)

Halzephron,非常感谢你的回答。我没有足够的声誉来标记你的答案,希望其他人会这样做。

以下详细信息:

你能够使用单一的IRS来控制一些伺服系统是完全正确的 - 令人尴尬的是我没有想到这一点,但考虑到你的简单解决方案有多好用 - 我只会用它。

  

...另外,您正在编写DDRE以启用端口E上的输出,但切换端口C上的引脚。

谢谢,我注释掉了这一行,并且它的工作方式相同 - (所以我根本不需要启用输出?)

  

TCC1A中不存在WGM12位(实际上设置了第3位,即FOC1A - “强制比较”,请参见数据表。)

我也删除了它,但是我的代码的其余部分保持不变 - 这导致伺服器移动得更慢,扭矩和抖动的方式也越来越小,即使在到达所需位置之后也是如此。伺服产生一种奇怪的“颤抖”噪音(频率约为10-20,我会说)并且它会颤抖,因此我不明白 - 设置这一点似乎是必要的。

我怀疑每个电机的定时器非常不优雅,所以我非常喜欢你的第二种方法(内置定时器生成的PWM),

  

...如果您直接使用PWM驱动电机(例如通过H桥),这也是正确的解决方案。

非常好奇,为什么?即使用此方法与计时器生成的PWM之间的区别是什么。

在您的代码中,我必须更改以下行才能获得50HZ,否则我之前获得25HZ(使用范围验证) ICR1 = 20000-1; //是40000 - 1;

我注意到另一个与示波器有关的事情是我所生成的定时器生成的PWM代码 - 产生的“矩形”形状比你附加的PWM代码片段少。使用我的代码,信号下降到0需要大约0.5毫秒,并且它与你的信号完全即时(这很棒)。这解决了我一直反对的另一个问题:我可以让模拟伺服系统与我的代码(IRS生成的PWM)一起正常工作,但我尝试过的任何数字伺服 - 只是没有移动,好像它被打破了。我想信号的形状对于数字伺服系统来说至关重要,我从来没有在任何地方阅读过。或者也许是我不知道的其他东西。

作为旁注,另一个奇怪的是我花了很多时间 - 我取消注释了TIMSK1 =(1&lt;&lt;&lt;&lt; TOV1);线,以为我总是需要它 - 但是发生了什么,我的主要功能会被冻结,在第一次调用delay_ms时永远被阻止(...)不确定它是什么 - 但是保持注释会解除阻塞我的主循环(我读到的地方)使用Teensy的示例代码从USB HID获取伺服positin值

再次,非常感谢您的帮助。