Necesita ayuda para comprender la salida duplicada del temporizador AVR ATMEGA / ATTINY

Estoy tratando de usar Timer1 del microcontrolador Atmel AVR, ya sea AtMega328 como se usa en Arduino, o ATTiny85, para generar dos señales de reloj que son imágenes especulares entre sí. La frecuencia que estoy tratando de generar es una variable de 1 MHz a 2 MHz o más que es demasiado alta para hacer esto usando un código para alternar los pines de salida a menos que no quiera hacer casi nada más en el controlador. Entonces quiero usar la salida del temporizador directamente en los pines asociados. Estoy usando la cadena de herramientas GCC, por lo que no estoy limitado por las bibliotecas o el idioma de arduino.

Timer1 en el Atmega328 tiene dos pines asociados y puedo obtener dos señales idénticas de 1MHz a 2MHz de ellos. Aunque la hoja de datos parece decir que puedo obtener una forma de onda invertida, me confunde. También puedo obtener dos señales que son ciclos de trabajo diferentes a 1 MHz, usando la configuración PWM con Timer1, pero ambas señales aumentan al mismo tiempo, la más corta disminuye antes. Esto no sirve para mi proyecto. Ni siquiera necesito la variación de ancho de pulso PWM, solo necesito dos señales idénticas de tipo "reloj" de fase opuesta, eso es todo.

No le pido a nadie que me escriba un código para hacer esto, solo necesito que alguien me diga qué modo/indicadores del temporizador me deben dar una forma de onda invertida simple en uno de los dos pines asociados con el temporizador. Si es posible, quiero evitar el uso de un circuito inversor externo para una de las salidas, a menos que esa sea la única opción.

Si esto es posible en ATTiny, será aún mejor. El ATTiny también tiene 2 pines asociados con un temporizador, pero no estoy seguro de que tenga las mismas opciones que el ATMega.

Ya tengo un cristal de 20 MHz y condensadores conectados en la PCB y el reloj de 20 MHz funciona de manera confiable en el ATMega328. En la PCB ATTiny85 tengo un cristal de 8 MHz y también funciona de manera confiable.

Por favor ayuda. Gracias.


ACTUALIZACIÓN : Hay algunas suposiciones no válidas en las respuestas y comentarios hasta el momento, así que tal vez debería aclarar: tenga en cuenta que en mi publicación original dije que estoy usando un reloj de 20 MHz, no 8 MHz , y también que no necesito PWM .

El único modo que da una frecuencia de salida lo suficientemente alta parece ser el modo CTC porque los modos PWM no funcionan para una salida de 2 MHz. ¿Hay alguna forma de invertir la salida A del temporizador 1 o la salida B en el modo CTC?

Ahora cambié a un Arduino Uno estándar (ATMega328, 16 MHz) en lugar de mi propia placa de 20 MHz para verificar mi código, y este es mi código para un buen reloj constante de 2 MHz en modo CTC desde los pines 9 y 10, el temporizador 1 pines de salida:

#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() {
}

Las trazas del osciloscopio para ambos pines son idénticas y están sincronizadas, ¿cómo puedo invertir cualquiera de las dos señales? El modo de inversión en la hoja de datos parece no hacer nada en el modo CTC. ¿Estoy leyendo mal la hoja de datos o me veré obligado a usar una frecuencia más baja y un modo PWM después de todo?

Para agregar una pregunta específica de "recompensa" a mi consulta original:
Entonces, ¿qué cambios debo hacer en mi código anterior para que proporcione señales perfectamente invertidas en los pines 9 y 11 a la frecuencia más alta posible para un reloj de 16 MHz , ya sea que es de 2 MHz o no?

Me quedaré con un Arduino Uno estándar por ahora, para que mi tablero casero no introduzca un modo de error, y para que cualquier persona con un arduino pueda probar mi código anterior y confirmar que funciona como he mencionado y no como yo ¡necesitar!

Mirando la página 97-98 de la hoja de datos de atmega8L , hay una tabla de los modos de operación. La página 108 establece "Los bits COM21:0 controlan si la salida PWM generada debe invertirse o no (PWM invertida o no invertida)". ¡Manténganos informados sobre su éxito!
¿Por qué no utilizar un inversor de transistor simple para las salidas de espejo?

Respuestas (2)

De la hoja de datos de ATtiny85:

El modo de operación, es decir, el comportamiento del temporizador/contador y los pines de comparación de salida, se define mediante la combinación del modo de generación de forma de onda (WGM0[2:0]) y el modo de salida de comparación (COM0x[1:0]) pedacitos Los bits del modo Comparar salida no afectan la secuencia de conteo, mientras que los bits del modo Generación de forma de onda sí lo hacen. Los bits COM0x[1:0] controlan si la salida PWM generada debe invertirse o no (PWM invertida o no invertida ).

La Tabla 11-5 muestra cómo configurar el modo.

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

Desea un modo Fast PWM (entonces el modo 3 o el modo 7). Si desea variar el ciclo de trabajo, y parece que sí, desea el modo 7 y variar el ciclo de trabajo configurando OCRA.

La Tabla 11-3 muestra cómo configurar el modo de salida de comparación para el modo Fast 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)

Es decir, puede configurar la salida OC0A para que sea baja cuando el valor del temporizador == OCR0A y alta cuando el valor del temporizador == 0x00 configurando COM0A1:COM0A0 = 0b10. O viceversa, configurando COM0A1:COM0A0 = 0b11. Y lo mismo para OC0B, OCR0B, COM0B0, COM0B1.

La frecuencia PWM está determinada por el reloj de E/S (suena como 8 MHz para usted) y la configuración del preescalador del temporizador. Y la ecuación se da como f_clk_IO / (N * 256) para el modo Fast PWM.

Por lo tanto, puede usar OC0A para polaridad "normal" y OC0B para polaridad "invertida" configurando OCR0A y OCR0B en el mismo valor y configurando COM0A1:COM0A0 = 0b10 y COM0B1:COM0B0 en 0b11.

ACTUALIZAR

Dado que desea alternar la salida lo más rápido posible y está utilizando el Mega328 operando a 16 MHz, el modo de operación CTC le permitirá obtener una frecuencia de conmutación de:

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

El modo Fast PWM le permitirá alternar el pin en:

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

Así que sigo pensando que quieres el modo Fast PWM. Específicamente Modo 3 con OCR0A = OCR0B = 0x80 para ciclo de trabajo del 50%. Y establezca los bits COM0A en 0x3 y los bits COM0B en 0x2 para hacer que las dos formas de onda se inviertan entre sí en OC0A y OC0B.

Actualización #2 Más el Mega328 Pruebe este código de 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(){

}
Déjame masticar esto un poco y ver si funciona. Gracias.
Después de volver a leer su respuesta para probarlo hoy, veo un par de suposiciones inválidas: especifiqué un reloj de 20 MHz (y ahora cambié a 16 MHz), no "(8MHz suena como para usted)" . También especifiqué que no necesito una variación de ancho de pulso PWM, por lo que no estoy seguro de dónde supuso "Si desea variar el ciclo de trabajo, y parece que lo hace" .
@ExcitingProjects Estaba escribiendo su declaración "En la PCB ATTiny85 tengo un cristal de 8 MHz y también funciona de manera confiable". y mi respuesta es en referencia al ATtiny85. Intentaré actualizar mi respuesta en respuesta a su pregunta actualizada.
@vicateu Gracias. He actualizado la pregunta, ya que el modo de inversión parece no tener efecto en el modo CTC a menos que me falte algún paso.
@ExcitingProjects de la hoja de datos de ATmega328: "Para los modos que no son PWM, los bits COM0x1: 0 controlan si la salida debe configurarse, borrarse o alternarse en una coincidencia de comparación"
@vicatcu Pregunta: ¿Has probado esto? Acabo de hacerlo, cambié las cosas a Timer1 según el requisito de OP, y no funciona en mi alcance: ambas señales aumentan simultáneamente sin importar qué. ¿Qué podría estar mal?
@AnindoGhosh no, no lo he probado personalmente, pero acabo de publicar un código de Arduino para probar que estoy bastante seguro de que debería funcionar
@vicatcu Odd... No funciona, y por mi vida no puedo entender qué pasa con eso: ahora el visor solo tiene un pin alto y el otro bajo, ni siquiera los relojes idénticos que teníamos antes . Extraño.
@AnindoGhosh intenta hacer OCR1A = OCR1B = 0x8000 por diversión.
@vicatcu Lo mismo. Uno alto, uno bajo, sin reloj.
hm... ¿por qué no estaría haciendo tictac...? Estoy desconcertado
@vicatcu Lo siento, no funciona.

La familia ATtinyX5 tiene PLL adentro, úsalo chico grande.

También uso PLL interno para alimentar el reloj de la CPU y tengo 16Mhz sin XTAL. Esto es precioso ya que solo tienes 5 pines. (No cuento el pin de reinicio). También un PWM con PLL (OCR1B) se ejecuta en pines XTAL con su salida complementaria opcional. Solo necesita ajustar los fusibles para 16Mhz Xtalless ATtiny... O simplemente deje que la CPU funcione en 8Mhz pero ejecute PWM con un reloj de 64Mhz sin cambiar los fusibles.

Puede tener hasta 64 Mhz de reloj PWM (pero con una resolución de 1 bit). O 125Khz a una resolución de 8 bits. Puede reducir la resolución PWM y aumentar la velocidad disminuyendo el registro OCR1C.

Para 1 Mhz, debe configurar OCR1C en 63. Para 2 Mhz, debe configurar OCR1C en 31. Para 4 Mhz, debe configurar OCR1C en 15. ...

Simplemente habilite PLL con este código:

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

Ahora tiene un reloj de 64 Mhz en PWM "OCR1B0/OCR1A0".

Además, puede ajustar OCR1[A/B]0 y XOCR1[A/B]0 para una salida reflejada.

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

Debe saber que Dead Time Generator comerá la salida de PWM si configura OCR1A = 1. Necesita valores más altos que el tiempo muerto.

Saludos,

Erdem