需要帮助了解 AVR ATMEGA / ATTINY 定时器镜像输出

电器工程 Arduino 大气压 计时器 服装
2022-01-30 06:28:15

我正在尝试使用 Atmel AVR 微控制器的 Timer1(Arduino 中使用的 AtMega328 或 ATTiny85)来输出两个相互镜像的时钟信号。我试图生成的频率是 1 MHz 到 2 MHz 或更高的变量,这太高了,无法使用代码来切换输出引脚,除非我几乎不想在控制器中做任何其他事情。所以我想直接在相关引脚上使用定时器输出。我正在使用 GCC 工具链,因此不受 arduino 库或语言的限制。

Atmega328 中的 Timer1 有两个与之关联的引脚,我可以从中得到两个相同的 1MHz 到 2MHz 信号。虽然数据表似乎说我可以得到一个倒置的波形,但这让我很困惑。使用 Timer1 的 PWM 设置,我还能够在 1 MHz 处获得两个占空比不同的信号,但两个信号同时变高,较短的信号变低。这不适合我的项目。我什至不需要 PWM 脉冲宽度变化,我只需要两个相同的“时钟”类型的反相信号,仅此而已。

我不是要求任何人为我编写代码来执行此操作,我只需要有人告诉我定时器的哪些模式/标志应该在与定时器相关的两个引脚之一上给我一个简单的反相波形。如果可能的话,我想避免对其中一个输出使用外部反相电路,除非那是唯一的选择。

如果这在 ATTiny 中完全可行,那就更好了。ATTiny 也有 2 个引脚与一个计时器相关联,但我不确定它是否具有与 ATMega 相同的选项。

我已经在 PCB 上连接了一个 20 MHz 晶体和电容器,并且 20 MHz 时钟在 ATMega328 上可靠地工作。在 ATtiny85 PCB 上,我有一个 8 MHz 晶体,它也可以可靠地工作。

请帮忙。谢谢你。


更新:到目前为止,答案和评论中有一些无效的假设,所以也许我应该澄清一下:请注意,在我原来的帖子中,我已经声明我使用的是 20 MHz 时钟,而不是 8 MHz,而且我不需要 PWM .

提供足够高输出频率的唯一模式似乎是CTC 模式,因为 PWM 模式不适用于 2 MHz 输出。有没有办法在 CTC 模式下反转定时器 1 输出 A 或输出 B?

我现在已经切换到标准的 Arduino Uno(ATMega328,16 MHz)而不是我自己的 20 MHz 板来检查我的代码,这是我的代码,用于在 CTC 模式下从引脚 9 和 10 获得稳定的 2 MHz 时钟,定时器1个输出引脚:

#define tick 9
#define tock 10

void setup() {
  pinMode(tick, OUTPUT);  
  pinMode(tock, OUTPUT); 

  TCCR1A = _BV(COM1A0) | _BV(COM1B0) ;   // activate both output pins 
  TCCR1B = _BV(WGM12)| 1;                // set CTC mode, prescaler mode 1

  // various frustrating attempts to invert OC1B failed. What do I put here?

  OCR1A = 3;                             // set the counter max for 2 MHz

}

void loop() {
}

两个引脚的示波器轨迹相同且同步,如何将两个信号中的任何一个反转?数据表中的反转模式在 CTC 模式下似乎没有任何作用。我是不是读错了数据表,还是我最终会被迫使用较低的频率和 PWM 模式?

要在我的原始查询中添加一个特定的“赏金”问题:
那么我需要对上面的代码进行哪些更改,以使其在16 MHz 时钟的最高频率下在引脚 9 和 11 上提供完美反相的信号,无论是那是2 MHz还是不是?

我现在将坚持使用标准的 Arduino Uno,这样我的土布板就不会引入错误模式,因此任何拥有 arduino 的人都可以尝试我上面的代码并确认它是否像我提到的那样工作,而不是像我说的那样工作需要!

2个回答

来自 ATtiny85 数据表:

操作模式,即定时器/计数器和输出比较引脚的行为,由波形生成模式 (WGM0[2:0]) 和比较输出模式 (COM0x[1:0]) 的组合定义位。比较输出模式位不会影响计数序列,而波形生成模式位会影响。COM0x[1:0] 位控制生成的 PWM 输出是否应反相(反相或非反相 PWM)。

表 11-5 显示了如何设置模式。

Mode   WGM  WGM  WGM  Timer/Counter Mode    TOP      Update of    TOV Flag
c0     02   01   00   of Operation                   OCRx at      Set on
==========================================================================
0      0    0    0    Normal                0xFF     Immediate    MAX(1)
1      0    0    1    PWM, Phase Correct    0xFF     TOP          BOTTOM
2      0    1    0    CTC                   OCRA     Immediate    MAX
3      0    1    1    Fast PWM              0xFF     BOTTOM       MAX
4      1    0    0    Reserved              –        –            –
5      1    0    1    PWM, Phase Correct    OCRA     TOP          BOTTOM
6      1    1    0    Reserved              –        –            –
7      1    1    1    Fast PWM              OCRA     BOTTOM       TOP

您需要快速 PWM 模式(因此模式 3 或模式 7)。如果你想改变占空比,听起来像你这样做,你想要模式 7 并通过设置 OCRA 来改变占空比。

表 11-3 显示了如何为快速 PWM 模式设置比较输出模式。

COM0A1/   COM0A0/
COM0B1    COM0B0     Description
===============================================================================
0         0          Normal port operation, OC0A/OC0B disconnected.
0         1          Reserved
1         0          Clear OC0A/OC0B on Compare Match, set OC0A/OC0B at BOTTOM
                     (non-inverting mode)
1         1          Set OC0A/OC0B on Compare Match, clear OC0A/OC0B at BOTTOM
                     (inverting mode)

也就是说,您可以通过设置 COM0A1:COM0A0 = 0b10 设置 OC0A 输出在 Timer 值 == OCR0A 时为低电平,在 Timer 值 == 0x00 时为高电平。反之亦然,设置 COM0A1:COM0A0 = 0b11。同样对于 OC0B、OCR0B、COM0B0、COM0B1。

PWM 频率由 I/O 时钟(听起来像 8MHz)和定时器预分频器设置决定。对于快速 PWM 模式,公式为 f_clk_IO / (N * 256)。

因此,通过将 OCR0A 和 OCR0B 设置为相同的值并将 COM0A1:COM0A0 = 0b10 和 COM0B1:COM0B0 设置为 0b11,您可以将 OC0A 用于“正常”极性,将 OC0B 用于“反相”极性。

更新

鉴于您希望尽快切换输出并且您使用的是工作在 16MHz 的 Mega328,CTC 工作模式将允许您获得以下开关频率:

f_OCnA = f_clk_IO / (2 * N * [1 + OCRnA) = 16e6 / (2 * 1 * [1 + 1]) = 4MHz

快速 PWM 模式可让您在以下位置切换引脚:

f_OCnxPWM = f_clk_IO / (N * [1 + TOP]) = 16e6 / (1 * [1 + 1]) = 8MHz

所以我仍然认为你想要快速 PWM 模式。特别是模式 3,OCR0A = OCR0B = 0x80,占空比为 50%。并将 COM0A 位设置为 0x3,COM0B 位设置为 0x2,使 OC0A 和 OC0B 上的两个波形相互反转。

更新 #2 更多 Mega328 试试这个 Arduino 代码:

#define tick 9
#define tock 10

void setup(){

  pinMode(tick, OUTPUT);  
  pinMode(tock, OUTPUT); 

  // Setup Waveform Generation Mode 15
  // OC1A Compare Output Mode = inverting mode
  // OC1B Compare Output Mode = non-inverting mode
  // Timer Prescaler = 1
  // TOP = OCR1A = 1

  //COM1A[1:0] = 0b11, COM1B[1:0] = 0b10, WGM1[1:0] = 0b11
  TCCR1A = _BV(COM1A1) | _BV(COM1A0) | _BV(COM1B1) | _BV(WGM11) | _BV(WGM10);

  //WGM1[3:2] = 0b11, CS1[2:0] = 0b001
  TCCR1B = _BV(WGM13) | _BV(WGM12) | _BV(CS10);

  OCR1A = 0x0001;
  OCR1B = 0x0001;
}

void loop(){

}

ATtinyX5 家族内部有 PLL,使用它大男孩。

我也使用内部 PLL 来为 CPU 时钟供电,并且没有 XTAL 有 16Mhz。这很珍贵,因为您只有 5 个引脚。(我不计算复位引脚)。还有一个带 PLL 的 PWM (OCR1B) 在 XTAL 引脚上运行,具有可选的互补输出。您只需要调整 16Mhz Xtalless ATtiny 的保险丝......或者只是让 CPU 以 8Mhz 运行,但在不更换保险丝的情况下以 64Mhz 时钟运行 PWM..

您最多可以拥有 64 Mhz 时钟 PWM(但分辨率为 1 位)。或 125Khz @ 8 位分辨率。您可以通过减少 OCR1C 寄存器来降低 PWM 分辨率并提高速度。

对于 1 Mhz,您需要将 OCR1C 设置为 63。对于 2 Mhz,您需要将 OCR1C 设置为 31。对于 4 Mhz,您需要将 OCR1C 设置为 15。...

只需使用以下代码启用 PLL:

PLLCSR |= (1 << PLLE);           //Start PLL
while( !(PLLCSR & (1<<PLOCK)) ); //Wait for PLL lock
//PLLCSR |= (1<<LSM );           //Low Speed PLL that clocks 32Mhz, not 64Mhz
PLLCSR |= (1 << PCKE);           //Enable PLL

现在您在“OCR1B0/OCR1A0”PWM 上有 64 Mhz 时钟。

此外,您可以调整 OCR1[A/B]0 和 XOCR1[A/B]0 以进行镜像输出。

if(0){ //Synch mode
     //OCR1A & XOCR1A enable for Synch operation but not allow odd PWM values!
     TCCR1 |= (1 << PWM1A) | (0 << COM1A1) | (1 << COM1A0); 
     //Also ATtinyX5 has "Dead Time Generator", use it ;)
     DTPS1 = 3;   //8x Prescaler for dead time generator (maximum)
     DT1A = 0xff; //Clk dead on both channels (maximum)
     }
   else
     TCCR1 |= (1 << PWM1A) | (1 << COM1A1) | (0 << COM1A0);  //ONLY OCR1A enabled

您需要知道,如果您设置 OCR1A=1,死区时间发生器将消耗 PWM 输出。您需要比死区时间更高的值。

问候,

埃尔登