c - 使用 AVR 设置多个定时器

标签 c timer avr pwm

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

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

  1. 如何将第二个 ISR 设置为独立于第一个 ISR 进行初始化和调用?
  2. 我需要设置第二个计时器吗?如果需要,这样的代码应该是什么样的?

这里是设置代码:

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;
    }
}

最佳答案

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

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

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 的说法,用于设置计时器的回绕(溢出)速率。参见数据表。 ]

其次,如果您只使用两个 Helm 机,还有一种更简单的方法 - 使用定时器的硬件 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 上的引脚。

关于c - 使用 AVR 设置多个定时器,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19344723/

相关文章:

c++ - 为什么我们不能使用两个不同的可互换枚举?作为函数参数?

c - SSL_Read/SSL_Write 中应该调用什么来重新协商

ios - 如何在 0 处停止倒计时? - swift

javascript - 使用 jQuery 检测计时器变量的变化

c - 如何使用 atmega 退出 USART ISR?

c - 我如何发送测量温度的字符,以便在我的串行捕获程序中可以查看它(使用 Realterm 或 Putty)

c - 为什么 Else if 当我有 "ADD_FILE "不工作?

C char 双指针

swift - 使用选择器调用带有参数的函数

echo - Sim900 仅回显命令 - 无响应