1
Contenido
Introducción El Timer0 y el Timer2 El Reloj del Timer0 y del Timer2 El Prescaler del Timer0 y del Timer2 Modos de Operación del Timer0 y Timer2 El Timer0 y el Timer2 en Modo Normal o Cálculo de la Temporización Normal El Timer0 y el Timer2 en Modo CTC o Cálculo de la Temporización CTC Interrupciones del Timer0 y el Timer2 El Timer0 y el Timer2 Como Contadores El Timer0 y el Timer2 en Modo PWM Práctica: Temporización con Sondeo del Timer0 Práctica: Interrupción del Timer0 Práctica: El Timer0 en Modo CTC Práctica: Control de Potencia Práctica: El Timer0/2 Como Contador Registros del Timer0 o TCCR0A o TCCR0B o TCNT0 o OCR0A o OCR0B o TIMSK0 o TIFR0 o GTCCR Registros del Timer2 o ASSR El Timer1 y el Timer3 El Timer1 y el Timer3 en Modos Normal y CTC El Timer1 y el Timer3 en Modo PWM o Fast PWM o PWM de Fase Correcta o PWM de Fase y Frecuencia Correctas Registros del Timer1 o TCCR1A o TCCR1B o TCCR1C o TCNT1H y TCNT1L o OCR1AH y OCR1AL o OCR1BH y OCR1BL o ICR1H y ICR1L o TIMSK1 o TIFR1 o GTCCR Registros del Timer3 Practica: Timer1 en PWM
2
Introducción Los Timers son módulos que trabajan en paralelo con el procesador, permitiendo que las operaciones de temporización y conteo se puedan llevar a cabo de manera eficiente, mientras el procesador se ocupa de otras tareas. Normalmente los megaAVR cuentan con tres Timers, llamados Timer0, Timer1 y Timer2. A veces desaparece el Timer2 y otras veces aparece adicionalmente el Timer3 o el Timer4. Todos los Timers pueden trabajar en modo de PWM pero esta funcionalidad está mejor implementada en unos Timers (1 y 3) que en otros. Por su relativa limitación, el Timer0 está más destinado a las temporizaciones y otro tanto a los conteos. El Timer2 por su parte fue diseñado con la característica adicional para trabajar con un XTAL de reloj externo, de 32kHz. A pesar de sus diferencias operativas, la configuración y control de los Timers son muy similares en todos los casos, así que por más que sean varios aprenderemos a controlarlos todos con el menor esfuerzo. Bueno, eso espero. Supongo que lo común debe ser conocer los Timer de a uno, empezando por el Timer0 hasta llegar al Timer3 o 4, si es que se llega. Pero después de revisar los diversos datasheets, creo que un mejor enfoque será abarcarlos de a dos, según su número de bits. Esto nos permitirá evitar las redundancias.
En primer lugar nos ocuparemos de los Timers de 8 bits, o sea, del Timer0 y del Timer2. Luego estudiaremos los Timers de 16 bits, que son el Timer1 y el Timer3.
De hecho, solo trataremos el Timer1 porque el Timer3 es su perfecto clon, si vale la expresión. Como habrán notado, había tratado hasta donde me posible de describir las características de los periféricos de los megaAVR pensando también en los que la
propia
ATmel
denomina
“viejos
AVR”,
dado
que
aún
son
muy
usados
en
la
actualidad y hay muchos proyectos de interés que se basan en esos microcontroladores. Pero creo que en esta ocasión dejaré de lado ese enfoque múltiple para no recargar el tema demasiado. Como había dicho hay mucha similitud entre todos los Timers y bastaría con conocer bien el funcionamiento de uno de ellos para poder comprender los demás. Con todo eso, el estar mencionando a cada rato que tal bit tiene otro nombre en el otro AVR o que está pequeña función no está presente en aquel otro,... puede hacer tediosa el estudio. Suficiente será con comparar los diversos Timers que mencionamos. No obstante, puesto que todo el control y configuración de un módulo queda reflejado en sus registros de E/S, creo que una buena idea poner al final la descripción respectiva de los registros de los Timers de los “viejos megaAVR”. Yo creo que eso será más que suficiente para programarlos
bien, sin necesidad de empezar a describirlos por separado.
2
Introducción Los Timers son módulos que trabajan en paralelo con el procesador, permitiendo que las operaciones de temporización y conteo se puedan llevar a cabo de manera eficiente, mientras el procesador se ocupa de otras tareas. Normalmente los megaAVR cuentan con tres Timers, llamados Timer0, Timer1 y Timer2. A veces desaparece el Timer2 y otras veces aparece adicionalmente el Timer3 o el Timer4. Todos los Timers pueden trabajar en modo de PWM pero esta funcionalidad está mejor implementada en unos Timers (1 y 3) que en otros. Por su relativa limitación, el Timer0 está más destinado a las temporizaciones y otro tanto a los conteos. El Timer2 por su parte fue diseñado con la característica adicional para trabajar con un XTAL de reloj externo, de 32kHz. A pesar de sus diferencias operativas, la configuración y control de los Timers son muy similares en todos los casos, así que por más que sean varios aprenderemos a controlarlos todos con el menor esfuerzo. Bueno, eso espero. Supongo que lo común debe ser conocer los Timer de a uno, empezando por el Timer0 hasta llegar al Timer3 o 4, si es que se llega. Pero después de revisar los diversos datasheets, creo que un mejor enfoque será abarcarlos de a dos, según su número de bits. Esto nos permitirá evitar las redundancias.
En primer lugar nos ocuparemos de los Timers de 8 bits, o sea, del Timer0 y del Timer2. Luego estudiaremos los Timers de 16 bits, que son el Timer1 y el Timer3.
De hecho, solo trataremos el Timer1 porque el Timer3 es su perfecto clon, si vale la expresión. Como habrán notado, había tratado hasta donde me posible de describir las características de los periféricos de los megaAVR pensando también en los que la
propia
ATmel
denomina
“viejos
AVR”,
dado
que
aún
son
muy
usados
en
la
actualidad y hay muchos proyectos de interés que se basan en esos microcontroladores. Pero creo que en esta ocasión dejaré de lado ese enfoque múltiple para no recargar el tema demasiado. Como había dicho hay mucha similitud entre todos los Timers y bastaría con conocer bien el funcionamiento de uno de ellos para poder comprender los demás. Con todo eso, el estar mencionando a cada rato que tal bit tiene otro nombre en el otro AVR o que está pequeña función no está presente en aquel otro,... puede hacer tediosa el estudio. Suficiente será con comparar los diversos Timers que mencionamos. No obstante, puesto que todo el control y configuración de un módulo queda reflejado en sus registros de E/S, creo que una buena idea poner al final la descripción respectiva de los registros de los Timers de los “viejos megaAVR”. Yo creo que eso será más que suficiente para programarlos
bien, sin necesidad de empezar a describirlos por separado.
3
El Timer0 y el Timer2 Dada la paridad de características entre el Timer0 y el Timer2, no las vamos a mencionar simultáneamente para no fatigarnos de términos. Simplemente vamos a referirnos al Timer0 y se dará por hecho que lo mismo es aplicable para el Timer2, salvo que se indique explícitamente lo contario. Las principales diferencias se dejarán notar solo al inicio y al final, luego el tratamiento será muy parejo. Naturalmente, el trato específico preocupación pues registro y bit de
control de cada Timer a nivel de programación requiere del de sus registros de configuración. Pero tampoco esto es de lo único que cambia es el número 0 por el número 2 en cada registro. Por ejemplo, los registros de E/S del Timer0 son:
TCNT0. TCCR0A, TCCR0B, OCR0A, OCR0B, TIMSK0 y TIFR0. En tanto que para el Timer2 son: TCNT2. TCCR2A, TCCR2B, OCR2A, OCR2B, TIMSK2 y TIFR2. Aparte de ellos, tenemos al registro GTCCR, el cual es de uso común para todos los Timers desde el Timer0 hasta el Timer3, y el registro ASSR, que es de uso exclusivo del Timer2 y que controla las características distintivas del funcionamiento asíncrono de este Timer. Del mismo modo, en los nombres de los bits de cada registro, solo cambian el número 0 por el 2. Por ejemplo, los mapas de bits de los registros TCCR0A y TCCR2A son, respectivamente:
Queda claro entonces que bastará con referirnos al Timer0, entendiendo que las mismas características, ventajas y limitaciones citadas serán igualmente aplicables al Timer2, salvo, repito, que se indique lo contrario. Empecemos, entonces. El nombre completo del Timer0 es Timer/Counter0 o Temporizador/Contador 0, pero por comodidad nos referimos a él simplemente como Timer0. Dicen que un diagrama vale más que mil palabras, así que el siguiente esquema nos ayudará para entender la operación común del Timer0. Las operaciones específicas de cada modo particular las describiremos en su momento.
4
Diagrama de bloques del Timer0.
El elemento central del Timer0 es su contador, que es mismo registro TCNT0. Como es un registro de 8 bits, decimos que el Timer0 es de 8 bits. El Timer0 puede avanzar hacia adelante o hacia atrás, según se programe, impulsado por la señal de su reloj, el cual puede ser interno o externo. Cuando nos referirnos al avance del Timer en realidad nos referimos al avance de su contador, el registro TCNT0. Con sus 8 bits, el Timer0 puede contar en todo su rango, o sea, entre 0 hasta 255. Cuando el Timer0 opera solo en modo ascendente y llega a su valor máximo de 255, continuará después contando desde 0 otra vez, cíclicamente. Esta transición de 255 a 0 es el famoso Desbordamiento y es un concepto clave en los Timers. El desbordamiento del Timer0 activa el bit de flag TOV0. También es posible hacer que el Timer0 cuente solo hasta un tope establecido por registro OCR0A. El Timer0 tiene dos comparadores que en todo momento están comparando el valor del registro TCNT0 con los registros OCR0A y OCR0B. Cada igualdad detectada entre los registros indicados se conoce como Coincidencia y es el segundo concepto clave de los Timers del AVR. La coincidencia entre TCNT0 y OCR0A activa el bit de flag OCF0A y la coincidencia entre TCNT0 y OCR0B activa el bit de flag OCF0B. El Desbordamiento del Timer0 y cada una de sus dos Coincidencias antes citadas pueden ser programadas para disparar interrupciones. Los detalles de las interrupciones serán vistos con paciencia en su sección respectiva. Desde el punto de vista de la programación, podemos controlar el Timer0 con tres tipos de bits:
5
Los bits CS (de Clock Select). Los bits CS02, CS01 y CS00 se encargan de configura todo los relacionado con el reloj y el prescaler del Timer. Los bits WGM (de Waveform Generator Mode). Los bits WGM02, WGM01 y WGM00 trabajan con los comparadores para producir ondas cuadradas de acuerdo con la configuración de los bits. En realidad, su función implica más que eso, pues establecen el modo en que operará el Timer0, ya sea modo Normal, CTC o PWM. Los bits COM (de Compare Output Mode). Son los bits COM0A1 y COM0A0 los que en última instancia deciden si las ondas generadas por los comparadores salen o no por los pines OC0A y OC0B del AVR. El tipo de onda más popular es PWM y es habitualmente el único caso en que se dejan salir las ondas. Cuando el Timer0 va a trabajar como simple contador o temporizador, los bits COM quedan con su valor por defecto de 0, con lo cual los pines OC0A y OC0B quedan desconectados del Timer y se pueden seguir usando como puertos de E/S generales.
El Reloj del Timer0 y del Timer2 La similitud entre el Timer0 y el Timer2 se comprueba fácilmente examinando sus correspondientes registros de control. Es en esta sección donde nos ocuparemos de las pocas diferencias entre ellos. El reloj del Timer0 es la señal digital, periódica o no, cuyos pulsos hacen avanzar el Timer. La fuente de reloj del Timer0 puede ser interna o externa.
Reloj Interno. Aquí el reloj del Timer0 deriva del mismo oscilador interno del sistema F_CPU. Como se ve en la figura, en este caso la señal pasa previamente por el prescaler, que puede dividir la frecuencia de F_CPU por un valor seleccionado por nosotros. Los prescalers del Timer0 y del Timer2 no son idénticos, aunque tengan los bits de control similares. Pero siendo este reloj el que se usa con regularidad, ya sea para las temporizaciones o para generar ondas PWM, sobrará espacio para familiarizarnos con estas ligeras diferencias. Reloj Externo. He aquí la brecha más grande que separa al Timer0 del Timer2. La forma como la señal externa se aplica al microcontrolador depende de cada Timer. En el Timer0 la señal externa se conecta al T0 del megaAVR. Con esto el programador decide si el Timer0 avanzará con cada flanco de subida o de bajada de dicha señal. Notemos en el diagrama que la señal externa no pasará por su prescaler. En el Timer2 su reloj externo puede ser de dos tipos: o es una señal aplicada al pin TOSC1 del megaAVR, en cuyo caso el Timer2 avanzará con cada flanco de bajada detectado en dicho pin; o es la señal de un oscilador de XTAL conectado entre los pines TOSC1 y TOSC2 del megaAVR. En ambos casos, la señal de reloj pasará por el prescaler del Timer2.
6
El modo donde el Timer0/2 trabaja con un reloj externo aplicado al pin T0 (para el Timer0) o TOSC1 (para el Timer2) se conoce como modo Contador porque de alguna forma el Timer contará los pulsos detectados en dicho pin. Sin embargo, el hecho de que el reloj provenga de una fuente externa no le quita sus otras funcionalidades, como por ejemplo, poder generar ondas PWM, interrupciones, etc., claro que sería conveniente que para tal caso la señal fuera periódica.
Contador del Timer2 con su fuente de reloj.
El Prescaler del Timer0 y del Timer2 El prescaler es un circuito contador por el que se puede hacer pasar el reloj del Timer para dividir su frecuencia. De ese modo el Timer avanzará más lento, según las necesidades del diseñador. El prescaler es parte del reloj del Timer, así que para configurarlo se usan los bits de Selección de Reloj o bits CS (por Clock Select).
7
Como puedes ver, los bits CS se encuentran en los registros TCCRxB de cada Timer, sin embargo, por más que sean casi iguales, tiene efectos diferentes debido a que los prescalers son diferentes. El prescaler del Timer2 es más sencillo y completo, pero empezaremos por explicar el prescaler del Timer0. El prescaler del Timer0 es compartido con el Timer1 (¿y qué tiene que ver en todo esto el Timer1?). De acuerdo con la figura, es posible que los dos Timers operen simultáneamente con el prescaler y utilizando diferentes factores de división puesto que cada Timer tiene sus propios bits CS (de Clock Select). El único reparo sería que se debe tener cuidado al resetear el prescaler porque para esto se dispone de una única señal PSRSYNC. Es un reset SYNCrono porque el Timers0 y el Timer1 trabajan siempre sincronizados con el reloj del sistema F_CPU, hasta cuando su reloj proviene de los pines T0 o T1, respectivamente. El bit PSRSYNC se encuentra en el registro GTCCR.
Prescaler y Relojes del Timer0 y del Timer1. Notemos que el prescaler divide el reloj del sistema por 8, 64, 256 ó 1024. Estos divisores se conocen como factores de prescaler. Observemos además que de usar un reloj proveniente del pin T0, entonces no será posible usar el prescaler.
8
Ahora revisemos el prescaler del Timer2. Este prescaler ofrece más factores de división, con lo que las temporizaciones podrán ser más flexibles. A diferencia del Timer0/1, si optamos por un reloj externo aplicado al pin TOSC1, dicha señal sí podrá pasar por el prescaler. Esta vez el prescaler se puede resetear con la señal PSRASY. Su nombre indica que se trata de naturaleza ASYncrona porque si el reloj del Timer2 viene del exterior, no habrá circuito que lo sincronice con el reloj del sistema F_CPU. Los bits AS2 y PSRASY se encuentran en el registro GTCCR.
Prescaler y Reloj del Timer2.
En la siguiente tabla la señal clkT2S puede ser F_CPU o el reloj proveniente del exterior.
9
Modos de Operación del Timer0 y Timer2 En general existen 3 modos en que pueden trabajar los Timers:
Modo Normal Modo CTC Modo PWM
Cada modo tendrá sus variantes dependiendo del Timer. Por ejemplo, en el Timer1 existen hasta 12 modos PWM, pero bueno, de eso nos ocuparemos en su momento.
10
Diagrama de bloques del Timer0. La figura nos resalta que esta vez vamos a trabajar con los bits WGM. Su nombre viene de Waveform Generation Mode porque estos bits pre-establecen el tipo de onda que podrá generar el Timer0 por los pines OC0A y OC0B. En la práctica es raro utilizar otras formas de onda que no sean de tipo PWM, así que el nombre no parece muy apropiado. En la figura también se aprecia que los GENERADORES DE ONDA también dependen de los bits COM (de Compare Output Mode). Estos bits establecen el modo en que finalmente saldrán las ondas por los pines OC0A y OC0B, es decir, pueden salir normales, invertidas, o pueden simplemente no salir y dejar los pines OC0x libres para otras tareas. A lo que quiero llegar es que al menos en cursomicros los bits COM solo se usan en modo PWM. En los modos Normal y CTC nos olvidamos de ellos.
Los bits WGM están distribuidos en los dos registros de control del Timer0, TCCR0A y TCCR0B. El hecho de que el bit WFG02 esté en TCCR0B y no junto con sus pares en TCCR0A habiendo espacios vacíos allí se debe a cuestiones de compatibilidad con otros AVR.
El Timer0 y el Timer2 en Modo Normal Este modo queda seleccionado cuando todos los bits WGM valen 0, es decir, es el modo por defecto del Timer0. De hecho, lo es en todos los Timers.
11
En modo Normal el Timer0, habilitado, avanza libre y cíclicamente en todo su rango, es decir, su registro TCNT0 cuenta desde 0x00 hasta 0xFF, luego se desborda para volver a iniciar desde 0x00. El desbordamiento del Timer activa el flag TOV0 del registro TIFR0 el cual puede programarse para disparar interrupciones. Como el registro TCNT0 es de lectura y escritura podemos en cualquier momento modificar su valor y así recortar los periodos de conteo para calibrar o ajustar las temporizaciones.
El Timer0 siempre inicia detenido, así que para que se cumpla todo lo descrito primero habrá echarlo a andar configurando los bits de reloj CS, según lo estudiado en fuentes de reloj y prescalers. Recordemos que los comparadores del Timer0 pueden sacar por los pines OC0A y OC0B unas señales que se pueden configurar con los bits COM. En los modos Normal o CTC esta señal se forma poniendo a cero, a uno, o conmutando el valor de OC0A/OC0B. Todas las opciones posibles se muestran en la siguiente tabla. Para temas de temporización, que es normalmente el propósito del modo Normal o CTC, debemos escoger la primera opción, que es la predeterminada y que nos dejará los pines OC0A/OC0B libres para seguir usándolos como puertos de E/S generales.
La tabla solo muestra la configuración de la onda generada para el pin OC0A pero es la misma que se obtiene para el pin OC0B con los bits COM0B1 y COM0B0.
Cálculo de la Temporización en Modo Normal Temporizar con el Timer0 implica cargar su registro TCNT0 con un valor adecuado y dejar que siga contando hasta que se desborde. Es el tiempo que demora en desbordarse lo que nos interesa conocer para aplicarlo a nuestras necesidades; y son el cálculo y la programación de ese tiempo el objetivo de esta sección.
12
Bueno, asumo que en este momento ya sabemos cómo configurar el reloj del Timer0 bits CS), que sabemos cómo programar el Timer0 en modo Normal (bits WGM) y que entendemos su operación en ese modo. Ahora aprenderemos a escoger la opción de reloj más adecuada para nuestras temporizaciones. Para empezar, debemos usar el reloj interno derivado de F_CPU (cuyo valor es teóricamente igual a la frecuencia del XTAL del megaAVR.), salvo que tengamos una señal externa periódica. Como sabemos, si la fuente de reloj es interna, el Timer0 y el Timer2 se programan igual. Lo único que cambiará serán los factores de prescaler.
En primer lugar veamos cómo avanza el Timer0. Por ejemplo, si tenemos un XTAL de 8 MHz y no usamos prescaler, entonces el reloj del Timer0 será de 8 MHz y el registro TCNT0 se incrementará cada 1/8MHz = 128ns, lo mismo que un ciclo de instrucción básica. Pero si usamos el factor de prescaler 8, TCNT0 avanzará cada 1us. Si usamos el factor de prescaler de 256, TCNT0 avanzará cada 32us. Y si cambiamos de XTAL, los tiempos serán otros. Ahora TCNT0 TCNT0 de 8,
entonces, suponiendo que seguimos con nuestro XTAL de 8MHz, el registro avanzará desde 0 hasta 255 en 32us (sin prescaler). Pero si cargamos con 200, llegará al desbordamiento después de 7us; y si usamos prescaler lo hará después de 7×8 = 56us.
Al inicio todos vemos en esto un enredo de números. Parece complejo pero solo es cuestión de encontrar el hilo de la madeja para suspirar diciendo ¡Ah…, era
así de fácil! Sin embargo, hay quienes se rinden y prefieren usar fórmulas y cálculos directos como los descritos a continuación. Bueno, vamos al grano. El Tiempo que pasará el Timer0 contando desde un valor inicial TCNT0 hasta 255 y se produzca el desbordamiento está dado por la fórmula:
13
Nota: los factores de prescaler N del Timer2 son 1, 8, 32, 64, 128, 256 y 1024. Eso podría dar otras soluciones para N y TCNT2. Como ves, ésta es una ecuación con dos incógnitas (N y TCNT0) y es posible encontrar más de una solución para ambas. Sin embargo, no todas serán igualmente adecuadas. Los valores más apropiados serán los que nos permitan realizar un mejor posterior ajuste de precisión. Si eres ducho resolviendo ecuaciones de Diofanto, puedes trabajar con esa fórmula. Pero si no quieres ir tanteando, puedes emplear las siguientes dos fórmulas: (He borrado todas sus deducciones para no alargar más esta sección.)
Lo más probable es que el valor obtenido con esta fórmula no esté disponible como factor de prescaler válido (1, 8, 64, 256 ó 1024 para el Timer0 o 1, 8, 32, 64, 128, 256 y 1024 para el Timer2). En tal caso deberemos tomar el factor superior más cercano (“redondear” para arriba). La otra fórmula es:
Como antes, si el resultado no fuera un número entero, habría que redondearlo para arriba. Si el factor de prescaler obtenido estuviera fuera del rango permitido (más alto que 1024), se puede optar por buscar otro camino, como fragmentar la temporización. Por otro lado, si la temporización es muy fina, puede que sea necesario subir un poquito el valor de inicio del TCNT0 para realizar una calibración añadiendo algunas instrucciones de relleno como nops. Estas dos situaciones las veremos en las prácticas; así que pierde cuidado si no las dejé muy claro. A modo de ejemplo, hallemos el factor de prescaler N y el valor de inicio de TCNT0 para generar una temporización de 5 ms si el megaAVR trabaja con un XTAL de 10 MHz.
14
Y el valor de inicio del registro TCNT0 será:
La secuencia de conteo resultaría así:
Otro ejemplo. ¿Cuáles son la razón de prescaler y el valor inicial de TCNT0 para conseguir una temporización de 200 µs si nuestro megaAVR tiene un XTAL de 4 MHz? El factor de prescaler N sería:
Y el valor inicial de TCNT0 será:
Luego, la secuencia de conteo quedaría así:
Finalmente, ¿cuáles son la razón de prescaler y el valor inicial de TCNT0 para conseguir una temporización de 50 ms si se tiene un megaAVR con un XTAL de 20 MHz? El factor de prescaler sería:
15
¿Y ahora de dónde vamos a sacar un factor de prescaler mayor que 3906.25 si el máximo es de 1024? ¿Buscamos otro Timer? Bueno, quizá podríamos temporizar 10 veces 5 ms.
El Timer0 y el Timer2 en Modo CTC
Su nombre es el acrónimo de Clear Timer on Compare Match y significa que el Timer se limpia cuando se produce una Coincidencia en la comparación de los registros TCNT0 y OCR0A. En este modo el Timer0 (su registro TCNT0) también empieza a contar desde 0x00 y se incrementa hasta que su valor sea igual al del registro OCR0A, en ese momento el registro TCNT0 se resetea y vuelve a contar desde 0x00. La Coincidencia también activa el flag OCF0A, el cual se puede usar para programar interrupciones. El registro OCR0A también es de lectura y escritura de modo que podemos establecer el tope hasta donde contará el Timer0. De cierta forma el auto-reseteo del Timer0 equivale a una auto-recarga. El diagrama indica la operación descrita.
Diagrama simplificado del Timer0 para temporizaciones en modo CTC. En términos generales todo lo que puede hacer el registro OCR0A también lo puede hacer su gemelo OCR0B, pero en este caso en especial la regla se rompe: Aunque los comparadores trabajen en todo momento y hay una Coincidencia por cada uno de ellos, el modo CTC está reservado para operar únicamente con el
16
registro OCR0A, es decir, aunque es posible que se activen los flags OCF0A y/o OCF0B, y se disparen inclusive sus interrupciones correspondientes, el Timer0 solo se resetea en su Coincidencia con OCR0A. Las aplicaciones del modo CTC son similares a las del modo Normal, o sea, las temporizaciones. Sin embargo, hay una diferencia que representa una ventaja o desventaja, según se vea. La auto-recarga del Timer0 en modo CTC es un suceso hardware que genera las más precisas temporizaciones independientemente de si el código está escrito en ensamblador, en un compilador o en otro. Pero la no necesidad de posteriores calibraciones software también implica que si la temporización obtenida no es la deseada, entonces no será posible ajustar su valor, a menos que volvamos a modificar el registro TCNT0, lo cual desnaturaliza este modo.
Cálculo de la Temporización en Modo CTC La fórmula que nos da el tiempo entre coincidencia y coincidencia es la siguiente. Recuerda que en modo CTC está fórmula solo vale para el registro OCR0A y no para OCR0B.
Nota: los factores de prescaler N del Timer2 son 1, 8, 32, 64, 128, 256 y 1024. Eso podría dar otras soluciones para N y TCNT2. De nuevo, esta ecuación tiene dos incógnitas (N y OCR0A). Como antes, es posible descomponerla en dos ecuaciones de una variable. La primera, para hallar N, es igual que en el modo Normal. De no resultar un valor exacto, también aquí debemos tomar el factor de prescaler superior más cercano.
La segunda fórmula se obtiene despejando OCR0A de la fórmula inicial. Se presupone que para esto ya debimos haber hallado el valor de N.
Si conseguimos para OCR0A una solución entera válida, será genial y no tendremos que recurrir a posteriores ajustes de precisión, sin importar en qué lenguaje o compilador se programe. De lo contrario, no habrá calibración en
17
OCR0A que valga, de modo que será de poco alivio para el diseñador redondearlo a su valor más cercano, superior o inferior. En ese caso y si la precisión fuera realmente importante, se puede optar por cambiar de XTAL. Ya no vamos a poner ejemplos porque estas fórmulas se resuelven exactamente igual que en el Cálculo de las Temporizaciones en Modo Normal.
Interrupciones del Timer0 y el Timer2 Esta vez el diagrama del Timer0 nos indica que debemos concentrarnos en los bits de flag que pueden disparar las interrupciones del Timer0.
Flags de Desbordamiento y de Coincidencias del Timer0. La real potencia del Timer0 se deja apreciar al emplear su característica más notable: las interrupciones. El Timer0 tiene dos tipos de interrupciones: una por el desbordamiento de su registro TCNT0 y dos en las coincidencias de su registro TCNT0 con los registros OCR0A y OCR0B. Estas interrupciones se controlan por los bits de los registros TIMSK0 y TIFR0:
TIMSK0 (Timer Interrupt Mask Register 0). Contiene los bits Enable de interrupciones. TIFR0 (Timer Interrupt Flags Register 0). Contiene los bits de Flag de interrupciones.
Para quienes aún trabajan con los viejos megaAVR, ellos no tienen interrupción en coincidencia B, los bits de la coincidencia A son simplemente OCIE0 y OCF0, y los siguientes registros se llaman TIMSK y TIFR. No llevan el 0 porque también controlan las interrupciones del Timer1 y del Timer2.
18
Interrupción por Desbordamiento del Timer0. El evento que puede disparar esta interrupción es el desbordamiento del registro TCNT0, o sea, la transición de 255 a 0. Esto implica la operación incremental del Timer0, sin importar si está contando en modo Normal, CTC o Fast PWM. En modo PWM de Fase Correcta el Timer0 cuenta en sube y baja sin pasar por la transición 255 a 0, así que en este modo no hay desbordamiento. El desbordamiento de Timer0 activará el flag TOV0 y si la interrupción está habilitada, se disparará. El bit TOV0 se limpia automáticamente al ejecutarse su función de interrupción ISR, pero también se puede limpiar por software, como de costumbre, escribiéndole un 1 y sin usar instrucciones de lecturamodificación-escritura como las generadas por las sentencias con el operador OR binario (|). Para habilitar la interrupción por Desbordamiento del Timer0 se setean los bits TOIE0 y obviamente, el bit enable general de interrupciones I, del registro SREG. La instrucción del ensamblador dedicada a esta operación es SEI y que en lenguaje C se puede incrustar mediante la función macro del mismo nombre sei(). (No sé por qué repito estas cosas.)
Interrupción en Coincidencia del Timer0. Como sabemos, los comparadores del Timer0 son circuitos que en todo momento están comparando los valores del registro TCNT0 con los registros OCR0A y OCR0B. Pues bien, el evento que puede disparar esta interrupción es la coincidencia entre los registros mencionados. Como puede haber interrupciones.
dos
coincidencias,
aquí
podemos
tener
hasta
dos
Especificando, cuando se detecte la igualdad entre TCNT0 y OCR0A se activará el flag OCF0A (Output Compare Flag 0 A), y cuando sean iguales TCNT0 y OCR0B se activará el flag OCF0B (Output Compare Flag 0 A). De nuevo, los flags se ponen a 1 independientemente de si sus interrupciones están habilitadas o no. Si lo están, se dispararán sus interrupciones, se ejecutarán las funciones ISR respectivas y los flags OCF0A y/o OCF0B se limpiarán por hardware. Ya sobra decir que también se pueden limpiar por software escribiéndoles un uno. Ambas interrupciones son gemelas pero no son siamesas, es decir, funcionan exactamente igual pero no necesariamente se tienen que habilitar las dos al mismo tiempo. Se habilitan por separado seteando el bit OCIE0A para una y OCIE0B para la otra. Una observación: el circuito comparador (llamado Output Compare) trabaja siempre sin importar en qué modo está operando el Timer0 (Normal, CTC o PWM), aunque las implicancias no serán las mismas. Explico: una coincidencia en modo CTC resetea el registro TCNT0, mientras que en los otros modos el registro TCNT0 sigue su marchar sin hacer caso. Si captaste mi cháchara, habrás descubierto que es posible temporizar con la Interrupción en Coincidencia incluso si el Timer trabaja en modo PWM.
El Timer0 y el Timer2 Como Contadores
El modo Contador de los Timers es una variante de su operación en modo Normal. Se dice contador porque cuenta los pulsos (o flancos) de la señal aplicada en un pin del megaAVR. Esto nos lleva de regreso a la configuración del reloj del
19
Timer y también a las diferencias entre el Timer0 y el Timer2 cuando sus relojes son externos. Antiguamente el Timer2 estaba más bien pensado para ser usado con un XTAL externo de reloj (de 32 kHz) conectado a los pines TOSC1 y TOSC2 del AVR. De ahí sus características para trabajar asíncronamente con el reloj del sistema. Esa era su única forma de soportar un reloj externo, es decir, no podía usarse para contar pulsos o flancos sueltos. En los viejos AVR el Timer2 es de ese tipo. Por fortuna, el Timer2 de los nuevos AVR viene mejor equipado y ofrece la opción adicional de trabajar con una señal externa (sin XTAL) aplicada al pin TOSC1. De ese modo el Timer2 se pone a nivel del Timer0/1. En realidad tiene una limitación y una mejora al respecto.
La limitación es que el Timer2 solo se incrementa con los flancos negativos de la señal externa, en tanto que el Timers0/1 puede programarse para que su avance sea con los flancos de bajada o de subida. La mejora es que el reloj externo del Timer2 sí pasa por el prescaler. En el Timer0/1 esto no es así por lo que el registro TCNT0 se incrementa o decrementa en uno por cada flanco de la señal externa. Se puede programar el factor de prescaler del Timer2 para que tenga un avance personalizado. Por ejemplo, si escogemos un factor de 8, entonces el registro TCNT0 podrá incrementarse cada 8 pulsos detectados en el pin TOSC1. Puedes ir a la sección El Prescaler del Timer0 y del Timer2 para revisar la estructura de los prescalers.
En el Timer0 debemos configurar los bits de CS02:CS00 a 110 ó 111.
En el Timer2 los bits CS nos dan más opciones.
20
Tengamos en cuenta que, siendo el modo Contador una interpretación particular del modo Normal, siguen latentes todas las funciones de temporización del Timer0, incluyendo las interrupciones en el Desbordamiento y en las Coincidencias. De hecho, también es posible temporizar con la señal externa, siempre que sea una onda cuadrada periódica, claro está. La única consideración es que la señal de T0 o T1 jamás debería superar, o igualar siquiera, la performance del reloj del sistema F_CPU, para permitir la sincronización entre ellas. Por otro lado, el reloj externo del timer2 no está sincronizado y en ese caso la manipulación de sus registros de datos puede requerir el uso del registro ASSR.
El Timer0 y el Timer2 en Modo PWM Inicialmente era el Timer1 el que fue plenamente equipado para generar ondas PWM. Si bien los otros Timers también ofrecen esa funcionalidad, aún tienen muchas limitaciones. El Timer0 de los AVR actuales puede producir hasta dos canales PWM, por los pines OC0A y OC0B. El Timer2 hace lo propio por los pines OC2A y OC2B. En los AVR clásicos cada uno de estos Timers genera un solo canal PWM. La principal desventaja de los Timers 0 y 2 respecto de los Timers 1 y 3 está en la resolución de sus ondas PWM. En los Timers 0 y 2 la resolución de cada onda es de 8 bits como máximo, por el mismo hecho de que trabajan con registros de 8 bits. De hecho, la resolución es siempre de 8 bits, a menos que se desee mutilar un canal PWM. En ese caso, la resolución puede disminuir. Por lo demás el mecanismo de generación de las ondas PWM es el mismo en todos los Timers, así que te sugiero que vayas a la sección del Timer1 para entender los detalles. Allí se describen y distinguen los modos Fast PWM, PWM de Fase correcta y PWM de Fase y frecuencia Correctas. Si después, de pasar por allí todavía te quedaran algunas dudas, puedes revisar las descripciones de los registros del Timer0 y de los registros del Timer2. Creo que con todo eso no hay pierde.
21
Práctica: Temporización con Sondeo del Timer0 El programa genera una onda cuadrada conmuta cada 2.5 ms, pero como ya estamos en temas serios, la temporización debe ser lo más precisa posible, ni 1 µs más ni 1 µs menos. Visto de otro modo, el programa genera una señal de onda cuadrada de 200 Hz.
El código fuente /****************************************************************************** * FileName: main.c * Purpose: Timer0 - Operación en modo Temporizador * Processor: ATmega164P * Compiler: IAR-C y AVR-GCC (WinAVR) * Author: Shawn Johnson. http://www.cursomicros.com. * * Copyright (C) 2008 - 2012 Shawn Johnson. All rights reserved. * * License: Se permiten el uso y la redistribución de este código con * modificaciones o sin ellas, siempre que se mantengan esta * licencia y la nota de autor de arriba. *****************************************************************************/ #include "avr_compiler.h" void Pause(void);
// Prototipo de función
//************************************************************************ // Función principal //************************************************************************ int main(void) {
22 DDRA = 0x01;
// PA0 salida de señal
/* Configuración del Timer0 * Modo de operación = Normal * Factor de prescaler = 256 */ TCCR0A = 0x00; TCCR0B = (1<
// Loop forever // Conmutar pin PA0 // Delay de 2499.55 us
} //************************************************************************ // Produce 2499.55 µs exactamente // Con el Prescaler de 256 y con con XTAL de 8 MHz el Timer0 se incrementa // cada 256/8 = 32 us. Por tanto, para alcanzar 2499.55 µs se requieren de // 2499.55/32 = 78.1 ticks. Ergo, TCNT0 se debe cargar con 256-78.1 = 178 //************************************************************************ void Pause(void) { GTCCR = (1<
// Limpiar flag de desbordamiento del Timer0
while((TIFR0&(1<
Descripción del programa El punto crucial del programa es el cálculo de la temporización. Según mi código, para que la señal cambie de nivel cada 2.5 ms, la función Pause debería tomar 2499.55µs, ya que el bucle llega a dicha llamada cada 0.45 µs (lo vi en el simulador del Studio 5). Por supuesto, este valor puede variar de un compilador a otro porque cada cual compila a su modo. Inclusive varía en un mismo compilador según el nivel de optimización establecido o según el microcontrolador usado. Esta exposición la hago habiendo compilado el código con AVR GCC con nivel de optimización –Os. while(1) { PINA = 0x01; Pause();
// Loop forever // Conmutar pin PA0 // Delay de 2499.55 us
23
} El factor de prescaler sería:
Y el valor inicial del TCNT0 es:
El ajuste de la temporización se ha conseguido añadiendo algunos nops en Pause. Esto es tiempo muerto pero son solo 2 us, nada comparado con los 2500 us del total. Para calibrar estas precisiones es aconsejable recurrir al Cronómetro de Proteus o al Stopwatch del Studio 5. Quizá te puedas preguntar ¿qué gracia tiene realizar temporizaciones de este modo, si bien se pueden usar los delays? En primer lugar, los conocidos delays nunca son precisos de por sí y, en segundo lugar, son muy susceptibles de sufrir dilataciones debido a las interrupciones. Además, significan tiempo muerto que impiden que el CPU ejecute otras tareas. Por ejemplo, ¿cómo harías si quisieras revisar el estado del puerto serie continuamente pero solo por 1 segundo?, ¿acaso le pondrías un delay de 1s? No, ¿verdad? Bueno, puede haber varias soluciones pero la más recomendada suele ser usando delays a base de Timers, en especial con sus interrupciones.
Práctica: Interrupción del Timer0 Se genera una onda cuadrada de frecuencia 500 Hz. Quizá creas que no es una práctica muy provechosa pero en realidad en la gran mayoría de aplicaciones el Timer0 funciona como se verá aquí. Dependerá del diseñador saber qué hacer con esta señal. Por ejemplo, aquí generaremos una onda PWM y al mismo tiempo una señal para bascular un LED en intervalos largos de tiempo, más allá de lo que permite el prescaler. Esta onda PWM se genera a nivel software y no tiene nada que ver con el modo PWM de los Timer0 (generado por hardware y con muchísima mejor performance). Esto es solo una demostración y hasta parece que el mismo datasheet recomienda no hacerlo. Pero como decía, se podrán encontrar otras aplicaciones donde la señal generada sea más útil, como las ondas PRM para controlar la velocidad de los motores de sus primeros robots que utilizaba Dale Heatherington. La onda PWM será de 500 Hz y con duty cycle variable mediante las teclas + y – del teclado. A la salida se puede conectar un pequeño motor DC o simplemente un LED, cuya intensidad de brillo variará.
24
Por otro lado, el programa también hará parpadear un LED cada 500 ms, un tiempo que a priori no se puede conseguir así nada más. Esto para demostrar que las temporizaciones no tienen que ser tareas exclusivas. En el circuito el IRF730 puede ser sustituido por un IRF720, un IRF540 o cualquier otro similar.
Circuito de la práctica. El código fuente /****************************************************************************** * FileName: main.c * Purpose: Interrupción del Timer0 en Modo Normal * Processor: ATmega164P * Compiler: IAR-C y AVR-GCC (WinAVR) * Author: Shawn Johnson. http://www.cursomicros.com. * * Copyright (C) 2008 - 2012 Shawn Johnson. All rights reserved.
25 * * License: Se permiten el uso y la redistribución de este código con * modificaciones o sin ellas, siempre que se mantengan esta * licencia y la nota de autor de arriba. *****************************************************************************/ #include "avr_compiler.h" #include "usart.h" #define MaxDuty 20 // Máximo duty cycle volatile unsigned char Duty; // Duty cycle actual /****************************************************************************** * Gestor de Interrupción por Desbordamiento del Timer0. * Esta interrupción se dispara cada 100 µs exactamente. * Genera una señal PWM con una frecuencia de 500 Hz *****************************************************************************/ ISR (TIMER0_OVF_vect) { static unsigned char Dgen = 1; // Duty generator. Rango = [1:MaxDuty] static unsigned int ticks = 0; TCNT0 += 157; if (Dgen <= Duty) PORTA |= (1<<0); else PORTA &= ~(1<<0);
// Reponer registro TCNT0 // 1-> Salida de señal PWM // 0-> Salida de señal PWM
if(++Dgen > MaxDuty) Dgen = 1;
// // Rango de Dgen es [1:MaxDuty]
if(++ticks == 5000) { PINA = (1<<1); ticks = 0; }
// Sig. bloque se ejecuta cada 5000×100u = 500ms // Conmutar pin PA0 (LED)
} /****************************************************************************** * Main Function *****************************************************************************/ int main(void) { DDRA = (1<<1)|(1<<0); // PA0 -> Salida de señal PWM // PA1 -> Salida LED Duty = 0;
// Rango = [0 : MaxDuty]
usart_init(); puts("\r\n Interrupción del Timer0 en Modo Normal \r"); puts("\r\n Control de Motor DC "); puts("\r\n (+) Aumentar velocidad"); puts("\r\n (-) Disminuir velocidad"); /* Configuración del Timer0 * - Modo de operación = Normal * - Factor de prescaler = 8 */ TCCR0A = 0x00;
26 TCCR0B = (1<
0)) { Duty--; } } } }
Descripción del programa El principio el Timer0 trabaja en modo Normal como en el programa anterior, solo que se le configura para que dispare interrupciones cada 100 µs. Ahora no interesa por qué 100 µs. El hecho es que para tal cometido los cálculos indicaban que el factor de prescaler N debía ser de 8 y el valor a recargar en TCNT0 debía ser de 156, tal como se ve abajo.
Y el valor inicial del TCNT0 será:
A pesar de que el cálculo para TCNT0 resultó exacto, en el programa tuve que utilizar 157 para conseguir los 100µs buscados. Como se predijo en la teoría, esto se debe a que la temporización es muy fina. Pero no solo se usa 157 en vez de 156 sino que en vez de una recarga directa como TCNT0 = 157 se utiliza una suma, TCNT0 += 157. Para las temporizaciones periódicas por interrupciones esta suma ayuda a disminuir las instrucciones de relleno que se deben colocar al ajustar la precisión. De hecho, como se ve, no tuve que añadir ni siquiera un nop como en la anterior práctica. Las interrupciones se disparan con total precisión cada 100us.
27
El hecho de tener que calibrar manualmente estás temporizaciones a pesar de que los cálculos eran precisos se debe a que la sentencia de recarga del registro TCNT0 no se produce en el preciso instante en que se desborda el Timer0, puesto que la ejecución de la función de interrupción ISR implica la ejecución de cierto código como el salto a dicha función y el almacenamiento temporal que se hace de algunos datos del programa. Todo esto es código oculto y se ejecuta muy rápido, pero no tanto como para pasar desapercibido ante temporizaciones pequeñas del orden de los microsegundos. Mientras se ejecuta ese pequeño código el Timer sigue avanzando de modo que el cálculo teórico no siempre se reflejará en la práctica. Pero no todo es mala noticia. Aún nos falta experimentar con el modo CTC. En las siguientes prácticas veremos que el Timer en modo CTC utiliza su “recarga automática” para temporizar mejor sin necesidad de este tipo de ajustes.
Ahora toca pensar en la temporización de 500 ms. Con nuestro XTAL de 8MHz y el máximo factor de prescaler para el Timer0 solo llegaríamos a 32.768 ms, que está bastante lejos de lo que buscamos. Aunque en las mismas condiciones el Timer1 puede temporizar hasta 8 388 608 ms, aquí vemos que no es necesario recurrir a él y así lo reservamos para su trabajo por excelencia que son las ondas PWM. Bueno, lo que hice en el programa es usar la anterior temporización de 100 µs para incrementar el contador ticks. Entonces deducimos que para alcanzar 500 ms, ticks deberá llegar a 5000. Y allí lo tenemos. if(++ticks == 5000) { PINA = (1<<1); ticks = 0; }
// Sig. bloque se ejecuta cada 5000×100u = 500ms // Conmutar pin PA0 (LED)
Lo hecho equivale a hacer una temporización grande a base de varias temporizaciones menores. Otro ejemplo podría ser temporizar 50 segundos partiéndolos en 50000. Habíamos hablado de esto en las secciones teóricas, cuando veíamos los ejemplos cuyos cálculos no daban soluciones. También se pueden añadir más contadores para multiplicar más temporizaciones, o emplear más variables como Dutyy Dgen para sacar otros canales PWM y controlar varios motores... Las ideas sobran. No voy a explicar el algoritmo de generación de la onda PWM porque creo que no viene al caso y porque es fácil de deducir. Posteriormente estudiaremos los Timers en modos PWM para generar ondas PWM de alta frecuencia y a nivel hardware. Una observación final: en la función principal el programa sondea el puerto serie para ver si llegaron datos. También se pudo utilizar la interrupción de recepción del USART para esta tarea y así poner al AVR en algún modo Sleep para
28
ahorrar energía. Quizá se deba hacer en una aplicación final, pero como estas prácticas son de ejemplo,…
Práctica: El Timer0 en Modo CTC Se utiliza el Timer0 para incrementar dos contadores, uno cada 1ms y el otro cada 100ms. Los contadores se pueden usar para establecer el tiempo de ejecución de otras rutinas. A manera de simple ejemplo, aunque no le encuentro mucha gracia, aquí se espera por 5 segundos a que el usuario ingrese una contraseña por el puerto serie para ahorrar en el circuito, pero que bien podría ser el prototipo de acceso a un sistema digitando la clave en un teclado matricial por un tiempo establecido. El tiempo se mide a partir del momento en que se presiona el primer dígito. Recuerdo haber hecho la pregunta ¿cómo haríamos para ejecutar una tarea durante cierto tiempo?, ya que, obviamente, para esto no se pueden recurrir a los típicos delays porque son tiempo muerto y poco precisos. Bueno, pues, en esta práctica veremos una solución.
El circuito. El código fuente /****************************************************************************** * FileName: main.c * Purpose: Temporización con el Timer0 en Modo CTC usando Interrupciones * Processor: ATmega164P
29 * Compiler: IAR-C y AVR-GCC (WinAVR) * Author: Shawn Johnson. http://www.cursomicros.com. * * Copyright (C) 2008 - 2012 Shawn Johnson. All rights reserved. * * License: Se permiten el uso y la redistribución de este código con * modificaciones o sin ellas, siempre que se mantengan esta * licencia y la nota de autor de arriba. *****************************************************************************/ #include "avr_compiler.h" #include "usart.h" char GetNumStr(char *, unsigned char); volatile unsigned char ticks_ms; volatile unsigned char ticks_100ms; /****************************************************************************** * Gestor de Interrupción en Coincidencia del Timer0. * Esta función se ejecuta cada 1 ms exactamente. *****************************************************************************/ ISR (TIMER0_COMPA_vect) { static unsigned char i; ticks_ms++; if(++i >= 100) { // Este bloque se ejecuta cada 100 ms ticks_100ms++; i = 0; } } /****************************************************************************** * Main Function *****************************************************************************/ int main(void) { const char psw[11] = "1234"; // password actual char buffer[11]; DDRA = (1<<0);
// PA0 -> Salida LED
usart_init(); puts("\r\n Temporización con el Timer0 en Modo CTC usando Interrupciones"); /* Configuración del Timer0 * - Modo de operación = CTC * - Factor de prescaler = 64 * - Periodo de auto-reset = 1 ms */ TCCR0A = (1<
30 for(;;) { start: GetNumStr(buffer, 0); // Reset internal counter puts("\r\r Ingrese su password \r "); while(kbhit() == 0); // Esperar a que lleguen datos al puerto serie GTCCR = (1<='0')&&(i0 i--; // putchar(c); // Eco } else if((c=='\r')&&(i)) { // Si c es ENTER y si i>0 buffer[i] = '\0'; // Poner un 0x00 (fin de cadena) putchar(c); // Eco i = 0; // Resetear contador return 1; // Retornar con 1 } } return 0; // Retornar con 0 }
31
Descripción del programa La Interrupción CTC o en Coincidencia del Timer0 incrementa los contadores ticks_ms yticks_100ms. Creo que es obvio cada cuánto tiempo se incrementan. Para generar el tiempo base de 1ms los valores de prescaler N del registro OCR0A los obtuve con las formulas presentadas en la secciónCálculo de la Temporización en Modo CTC.
Observa que el cálculo salió preciso y esos mismos valores son los que aparecen en el programa. La temporización es precisa y no hay que preocuparse por recargas ni ajustes, ni nada. Genial, ¿verdad? Para terminar esto quiero mencionar que el factor de prescaler usado es bastante grande como para que el prescaler avance unos varios ticks que pueden adelantar la temporización. En la simulación vi que en vez de la espera de 5 segundos solo se cronometraban 4.973 segundos, así que tuve que resetear el prescaler y con eso el problema se arregló. GTCCR = (1<
// Resetaer prescaler
Por supuesto que he dramatizado el tema, pero quise destacarlo para recordar que si bien puede haber otras aplicaciones donde sí resulte realmente serio, se debe pensar bien antes de hacerlo porque el prescaler es también compartido por el Timer1. Por ejemplo, si tuviera el Timer1 trabajando en modo PWM, yo no me atrevería a resetear el prescaler.
Práctica: Control de Potencia Un poco más adelante nos dedicaremos a los motores DC. Pero antes me parece interesante controlar la velocidad de un pequeño motor AC monofásico o de algún dispositivo de potencia que no genere mucho ruido como por ejemplo una típica bombilla de 100W. Bueno yo sé que en estos tiempos eso ya no es nada típico, pero la idea es variar la alimentación de 220V o 110V de la red doméstica para controlar un dispositivo de ese calibre. El circuito no está blindado para filtrar los ruidos que pueden generar los motores AC de cierta magnitud, así que es recomendable que no sea muy grande. Aunque el programe funcione bien, los ruidos grandes “sacuden” al microcontrolador y perturban su operación.
32
El código fuente /****************************************************************************** * FileName: main.c * Purpose: Timer0 en Modo CTC * Processor: ATmega164P * Compiler: IAR-C y AVR-GCC (WinAVR) * Author: Shawn Johnson. http://www.cursomicros.com. * * Copyright (C) 2008 - 2012 Shawn Johnson. All rights reserved. * * License: Se permiten el uso y la redistribución de este código con * modificaciones o sin ellas, siempre que se mantengan esta * licencia y la nota de autor de arriba. *****************************************************************************/ #include "avr_compiler.h" #include "usart.h" #define #define
MOC_OFF() (PORTA |= (1<<0)) MOC_ON() (PORTA &= ~(1<<0))
volatile unsigned char top = 0; volatile unsigned char duty = 0; volatile unsigned char ticks = 0; //***************************************************************************** // Gestor de Interrupción INT0. // Esta función se ejecuta cuando se detectan flancos de subida en el pin INT0. //***************************************************************************** ISR (INT0_vect) { static unsigned char i = 0; static unsigned int avr = 0;
33
if (duty) { MOC_ON(); } avr += ticks; if(++i >= 4) { i = 0; avr = avr/4; top = (unsigned char)avr + 1; avr = 0; } ticks = 0; } //***************************************************************************** // Gestor de Interrupción en Coincidencia del Timer0. // Esta función se ejecuta cada 200 us exactamente. //***************************************************************************** ISR (TIMER0_COMPA_vect) { if(++ticks >= duty) { if (duty < top) { MOC_OFF(); } } } /****************************************************************************** * Main Function *****************************************************************************/ int main(void) { DDRA = (1<<0); // PA0 -> Salida MOC usart_init(); printf("\r\n Control de Potencia "); printf("\r\n (+) Subir duty cycle "); printf("\r\n (-) Bajar duty cycle \r"); /* Configuración del Timer0 * - Modo de operación = CTC * - Factor de prescaler = 8 * - Periodo de auto-reset = 200 us */ TCCR0A = (1<
34
sei();
// Habilitación general de interrupciones
for(;;) { if(kbhit()) { char k = getchar(); if(top==0) { printf("\r No AC signal detected"); } else if((k == '+') && (duty < top)) { printf("\r duty = %d", ++duty); } else if ((k == '-') && (duty)) { printf("\r duty = %d", --duty); } } } }
Descripción del programa No hay mucho que explicar. Los cálculos de la temporización se realizaron exactamente como en la anterior práctica. Salieron precisos y no fue necesario realizar calibraciones ni nada. La Interrupción INT0 se estudió tan ampliamente como lo estamos haciendo con los Timers. Su función en este programa es detectar los cruces por cero de la señal alterna AC. En ese momento se activa el opto-acoplador y también se reinicia el contador ticks, el cual tiene la función de medir el tiempo que dura cada semiperiodo de la señal alterna. En el código se promedian cuatro de estos tiempos, aunque creo que hubiera sido mejor añadirle un filtro software para los picos. La Interrupción en Coincidencia del Timer0 pone fin al tiempo que el optoacoplador permanece activo. Este tiempo está determinado por el valor de la variable ticks, el cual a su vez se incrementa desde 0 hasta duty, que es el duty cycle establecido por el usuario mediante la consola del puerto serie. No sé si seguir explicando o sugerirte que mejor vieras el resultado en un osciloscopio, aunque sea en Proteus. Se ve impresionante. La siguiente figura es una captura de la onda alterna de 220V solo que recortada con un duty cycle de 50%. Como siempre la práctica lleva su archivo de simulación de Proteus y la puedes descargar haciendo clic en la imagen del circuito.
35
Práctica: El Timer0/2 Como Contador La práctica no puede ser más sencilla: el Timer0 contará los flancos de bajada generados por un pulsador conectado al pin T0 del AVR. El valor del registro TCNT0 será enviado al terminal serial. No se pondrá ningún mecanismo anti-rebote. Así que el Timer0 se incrementará con todo y los rebotes.
Circuito de la práctica. El código fuente /****************************************************************************** * FileName: main.c * Purpose: Timer0 en Modo Normal como Contador
36 * Processor: ATmega164P * Compiler: IAR-C y AVR-GCC (WinAVR) * Author: Shawn Johnson. http://www.cursomicros.com. * * Copyright (C) 2008 - 2012 Shawn Johnson. All rights reserved. * * License: Se permiten el uso y la redistribución de este código con * modificaciones o sin ellas, siempre que se mantengan esta * licencia y la nota de autor de arriba. *****************************************************************************/ #include "avr_compiler.h" #include "usart.h" /****************************************************************************** * Main Function *****************************************************************************/ int main(void) { unsigned char tcnt0; PORTB |= (1<<0);
// Activar pull-up de pin PB0/T0
usart_init(); printf("\r\n Timer0 como Contador de Pulsos \r"); /* Configuración del Timer0 * - Modo de operación = Normal * - Fuente de reloj = Pin T0 * El registro TCNT0 se incrementa con los flancos de bajada del pin T0 */ TCCR0A = 0X00; TCCR0B = (1<
37
Registros del Timer0 TCCR0A
COM0A1
COM0A0
COM0B1
COM0B0
---
---
WGM01
WGM00
TCCR0B
FOC0A
FOC0B
---
---
WGM02
CS02
CS01
CS00
TIMSK0
---
---
---
---
---
OCIE0B
OCIE0A
TOIE0
TIFR0
---
---
---
---
---
OCF0B
OCF0A
TOV0
GTCCR
TSM
---
---
---
---
---
PSRASY
PSRSYNC
TCNT0 OCR0A OCR0B
TCCR0A – Timer/Counter Control Register 0 A TCCR0A COM0A1:0
COM0A1
COM0A0
COM0B1
COM0B0
---
---
WGM01
WGM00
Compare Match Output A Mode Estos bits controlan el comportamiento del pin Output Compare (OC0A). Si uno o los dos bits COM0A1:0 están seteados, la salida OC0A tiene prioridad sobre la funcionalidad normal del pin al que está conectado. Sin embargo, note que el bit del registro DDR correspondiente al pin OC0A debe estar configurado como salida para habilitar el driver de salida. Cuando OC0A está conectado al pin la función de los bits COM0A1:0 depende de la configuración de los bits WGM02:0. La siguiente tabla muestra la funcionalidad de los bits COM0A1:0 cuando los bits WGM02:0 están configurados en COM0A1
COM0A0
Descripción
0
0
OC0A desconectado. puerto
0
1
OC0A OCR0A
1
0
OC0A se limpia en Coincidencia de TCNT0 y OCR0A
1
1
OC0A se setea en Coincidencia de TCNT0 y OCR0A
conmuta
modo Normal o CTC(no PWM).
en
Operación
Coincidencia
normal de
TCNT0
de y
38
TCCR0A
COM0A1
COM0A0
COM0B1
COM0B0
---
---
WGM01
La siguiente tabla muestra la funcionalidad de los bits COM0A1:0 cuando los bits WGM02:0 están configurados en modo Fast PWM. Nota: ocurre un caso especial cuando OCR0A es el tope del conteo y el bit COM0A1 vale uno. En este caso se ignora la Coincidencia, pero la puesta a cero o a uno de OC0A se produce al llegar a 0x00. COM0A1
COM0A0
Descripción
0
0
0
1
1
0
OC0A desconectado. Operación normal del pin WGM02 = 0: OC0A Desconectado, Operación normal del puerto. WGM02 = 1: OC0A conmuta en Coincidencia. OC0A se limpia en la Coincidencia, y se setea al llegar a 0x00 (modo no-invertido)
1
1
OC0A se setea en la Coincidencia, y se limpia al llegar a 0x00 (modo invertido).
La siguiente tabla muestra la funcionalidad de los bits COM0A1:0 cuando los bits WGM02:0 están configurados en modo PWM de Fase Correcta. Nota: ocurre un caso especial cuando OCR0A es el tope del conteo y el bit COM0A1 vale uno. En este caso se ignora la Coincidencia, pero la puesta a cero o a uno de OC0A se produce al llegar al tope.
COM0B1:0
COM0A1
COM0A0
Descripción
0
0
OC0A desconectado. Operación normal del pin
0
1
1
0
WGM02 = 0: OC0A Desconectado, Operación normal del puerto. WGM02 = 1: OC0A conmuta en Coincidencia. OC0A se limpia en la Coincidencia cuando se cuenta hacia arriba, y se setea en la Coincidencia cuando se cuenta hacia abajo.
1
1
OC0A se setea en la Coincidencia cuando se cuenta hacia arriba, y se limpia en la Coincidencia cuando se cuenta hacia abajo.
Compare Match Output B Mode Estos bits controlan el comportamiento del pin Output Compare (OC0B). si uno o los dos bits COM0B1:0 están seteados, la salida OC0B tiene prioridad sobre la funcionalidad normal del pin al que está conectado. Sin embargo, note que el bit del registro DDR correspondiente al pin OC0B debe estar configurado como salida para habilitar el driver de salida.
WGM00
39
TCCR0A
COM0A1
COM0A0
COM0B1
COM0B0
---
---
WGM01
WGM00
Cuando OC0B está conectado al pin la función de los bits COM0B1:0 depende de la configuración de los bits WGM02:0. La siguiente tabla muestra la funcionalidad de los bits COM0B1:0 cuando los bits WGM02:0 están configurados en COM0B1
COM0B0
Descripción
0
0
OC0B desconectado. puerto
0
1
OC0B OCR0B
1
0
OC0B se limpia en Coincidencia de TCNT0 y OCR0B
1
1
OC0B se setea en Coincidencia de TCNT0 y OCR0B
conmuta
en
Operación
Coincidencia
normal de
de
TCNT0
y
modo Normal o CTC(no PWM). La siguiente tabla muestra la funcionalidad de los bits COM0B1:0 cuando los bits WGM02:0 están configurados en modo Fast PWM. Nota: ocurre un caso especial cuando OCR0B es el tope del conteo y el bit COM0B1 vale uno. En este caso se ignora la Coincidencia, pero la puesta a cero o a uno de OC0B se COM0B1
COM0B0
Descripción
0
0
OC0B desconectado. Operación normal del pin
0
1
Reservado
1
0
OC0A se limpia en la Coincidencia, y se setea al llegar a 0x00 (modo no-invertido)
1
1
OC0A se setea en la Coincidencia, y limpia al llegar a 0x00 (modo invertido).
se
produce al llegar a 0x00. La siguiente tabla muestra la funcionalidad de los bits COM0B1:0 cuando los bits WGM02:0 están configurados en modo PWM de Fase Correcta. Nota: ocurre un caso especial cuando OCR0B es el tope del conteo y el bit COM0B1 vale uno. En este caso se ignora la Coincidencia, pero la puesta a cero o a uno de OC0B se produce al llegar al tope.
40
TCCR0A
Bits 3:2
COM0A1
COM0A0
COM0B1
COM0B0
---
---
WGM01
COM0B1
COM0B0
Descripción
0
0
OC0B desconectado. Operación normal del pin
0
1
Reservado
1
0
OC0B se limpia en la Coincidencia cuando se cuenta hacia arriba, y se setea en la Coincidencia cuando se cuenta hacia abajo.
1
1
OC0B se setea en la Coincidencia cuando se cuenta hacia arriba, y se limpia en la Coincidencia cuando se cuenta hacia abajo.
Reservados Estos bits están reservados en los ATmega164A/PA, ATmega324A/PA, ATmega644A/PA y ATmega1284/P, y siempre se leerán como cero.
GM01:0
aveform Generation Mode Estos bits se combinan con el bit WFGM02 del registro TCCR0B. Juntos controlan la secuencia de conteo del contador, el valor máximo del conteo (el tope) y el tipo de la forma de onda que se generará. Los modos operación que soporta la unidad del Timer/Counter son: modo Normal (contador), modo Clear Timer on Compare Match (CTC), y dos tipos de Modulación de Ancho de Pulso (PWM). WGM02
WGM01
WGM00
Modo de Operación del Timer0
Tope del Conteo
0
0
0
Normal
0xFF
0
0
1
PWM de Fase Correcta
0xFF
0
1
0
CTC
OCR0A
0
1
1
Fast PWM
0xFF
1
0
0
Reservado
---
1
0
1
PWM de Fase Correcta
1
1
0
Reservado
---
1
1
1
Fast PWM
OCR0A
OCR0A
WGM00
41
TCCR0B – Timer/Counter Control Register 0 B TCCR0B FOC0A FOC0A
FOC0B
---
---
WGM02
CS02
CS01
Force Output Compare A El bit FOC0A solo está activo cuando los bits WGM establecen un modo no PWM. Sin embargo, para asegurar la compatibilidad con futuros dispositivos, este bit se debe mantener en cero al escribir en TCCRB cuando el Timer está operando en uno de los modos PWM. Si se escribe un uno en el bit FOCA, se fuerza una Coincidencia inmediata en la Unidad Generadora de Forma de Onda. La salida de OC0A cambia de acuerdo con la configuración de los bits COM0A1:0. Note que el bit FOC0A se implementa como strobe. Así que es el valor presente en los COM0A1:0 los que determinan el efecto de la Coincidencia forzada. Un strobe de FOC0A no generará ninguna interrupción ni tampoco reseteará el Timer en modo CTC si se usa OCR0A como tope del conteo. El bit FOC0A siempre se lee como cero.
FOC0B
Force Output Compare B El bit FOC0B solo está activo cuando los bits WGM establecen un modo no PWM. Sin embargo, para asegurar la compatibilidad con futuros dispositivos, este bit se debe mantener en cero al escribir en TCCRB cuando está operando en modo PWM. Si se escribe un uno en el bit FOC0B, se fuerza una Coincidencia inmediata en la Unidad Generadora de Forma de Onda. La salida de FOC0B cambia de acuerdo con la configuración de los bits COM0B1:0. Note que el bit FOC0B se implementa como strobe. Así que es el valor presente en los COM0B1:0 los que determinan el efecto de la Coincidencia forzada. Un strobe de FOC0B no generará ninguna interrupción ni tampoco reseteará el Timer en modo CTC si se usa OCR0B como tope del conteo. El bit FOC0B siempre se lee como cero.
Bits 5:4 GM02
Reservados Estos bits están reservados y siempre se leerán como cero.
aveform Generation Mode Ver la descripción de los bits WGM01:0 del registro TCCR0A.
CS02:0
Clock Select Los tres bits de Clock Select seleccionan la fuente de reloj que usará el Timer/Counter. Si se usan los modos de pin externo para el Timer/Counter0, las transiciones en el pin T0 harán el contador incluso si el pin está configurado como salida. Esta característica permite el control software del contador.
CS00
42
TCCR0B FOC0A
FOC0B
---
---
WGM02
CS02
CS01
CS02
CS01
CS00
0
0
0
Sin fuente de reloj (el Timer0 está detenido)
0
0
1
F_CPU (Sin prescaler)
0
1
0
F_CPU/8 (con prescaler)
0
1
1
F_CPU/64 (con prescaler)
1
0
0
F_CPU/256 (con prescaler)
1
0
1
F_CPU/1024 (con prescaler)
1
1
0
1
1
1
Reloj externo en El Timer0 avanza con el flanco de Reloj externo en El Timer0 avanza con el flanco de
CS00
Fuente de reloj del Timer0
pin T0. bajada. pin T0. subida.
TCNT0 – Timer/Counter 0 Register
TCNT0 Bits 7:0
TCNT0[7:0] El Registro Timer/Counter da acceso directo al contador de 8 bits del Timer/Counter para las operaciones de lectura y escritura. La escritura en el registro TCNT0 bloquea (quita) la Coincidencia en el siguiente ciclo de reloj del Timer. La modificación de TCNT0 cuando el contador está corriendo conlleva un riesgo de perder una Coincidencia entre los registros TCNT0 y OCR0x.
OCR0A – Output Compare Register 0 A
OCR0A Bits 7:0
OCR0A[7:0] El registro Output Compare A contiene un valor de 8 bits que es continuamente comparado con el valor del contador (TCNT0). Se puede usar una Coincidencia para disparar una Interrupción en Coincidencia, o para generar una onda por el pin OC0A.
OCR0B – Output Compare Register 0 B OCR0B Bits 7:0
OCR0B[7:0] El registro Output Compare B contiene un valor de 8 bits que es continuamente comparado con el valor del contador (TCNT0). Se puede usar una Coincidencia para disparar una Interrupción en Coincidencia, o para generar una onda por el pin OC0B.
43
TIMSK0 – Timer/Counter Interrupt Mask 0 Register TIMSK0
---
---
---
---
---
OCIE0B
OCIE0A
Bits 7:3
Reserved
OCIE0B
Timer/Counter Output Compare Match B Interrupt Enable
TOIE0
Estos bits están reservados y siempre se leerán como cero.
Cuando se escribe uno en el bit OCIE0B y el bit I del registro SREG vale uno, se habilita la Interrupción en Coincidencia B del Timer/Counter0. Si ocurre una Coincidencia entre TCNT0 y OCR0B se ejecutará la función de interrupción correspondiente, esto es, cuando se active el flag OCF0B del registro TIFR0.
OCIE0A
Timer/Counter Output Compare Match A Interrupt Enable Cuando se escribe uno en el bit OCIE0A, y el bit I del registro SREG vale uno, se habilita la Interrupción en Coincidencia A del Timer/Counter0. Si ocurre una Coincidencia entre TCNT0 y OCR0A, se ejecutará la función de interrupción correspondiente, esto es, cuando se active el flag OCF0A del registro TIFR0.
TOIE0
Timer/Counter0 Overflow Interrupt Enable Cuando se escribe uno en el bit TOIE0, y el bit I del registro SREG vale uno, se habilita la Interrupción por Desbordamiento del Timer/Counter0. Si ocurre un Desbordamiento en el registro TCNT0, se ejecutará la función de Interrupción correspondiente, esto es, cuando se active al flag TOV0 del registro TIFR0.
TIFR0 – Timer/Counter Interrupt Flag 0 Register TIFR0
---
---
---
---
---
Bits 7:3
Reserved
OCF0B
Timer/Counter 0 Output Compare B Match Flag
OCF0B
OCF0A
Estos bits están reservados y siempre se leerán como cero. El bit OCF0B se setea cuando ocurre una Coincidencia entre los datos de los registros TCNT0 y OCR0B. El flag OCF0B se limpia por hardware al ejecutarse su correspondiente función de interrupción. Alternativamente se puede limpiar escribiendo en él un uno lógico. Cuando valgan uno el bit I de SREG, el bit de enable OCIE0B y el bit de flag OCF0B, entonces se ejecutará la Interrupción en Coincidencia B del Timer/Counter0.
TOV0
44
TIFR0
---
OCF0A
---
---
---
---
OCF0B
OCF0A
TOV0
Timer/Counter 0 Output Compare A Match Flag El bit OCF0A se setea cuando ocurre una Coincidencia entre los datos de los registros TCNT0 y OCR0A. El flag OCF0A se limpia por hardware al ejecutarse su correspondiente función de interrupción. Alternativamente se puede limpiar escribiendo en él un uno lógico. Cuando valgan uno el bit I de SREG, el bit de enable OCIE0A y el bit de flag OCF0A, entonces se ejecutará la Interrupción en Coincidencia A del Timer/Counter0.
TOV0
Timer/Counter0 Overflow Flag El bit TOV0 se setea cuando ocurre un Desbordamiento del registro TCNT0. El flag TOV0 se limpia por hardware al ejecutarse su correspondiente función de interrupción. Alternativamente se puede limpiar escribiendo en él un uno lógico. Cuando valgan uno el bit I de SREG, el bit de enable OCIE0A y el bit de flag OCF0A, entonces se ejecutará la Interrupción por Desbordamiento del Timer/Counter0. La activación de este flag depende de la configuración de los bits WGM02:0.
GTCCR – General Timer/Counter Control Register GTCCR TSM
TSM
---
---
---
---
---
PSRASY
PSRSYNC
Timer/Counter Synchronization Mode Al escribir uno en el bit TSM se activa el modo de Sincronización del Timer/Counter. En este modo, se mantendrán los valores que se escriban en los bits PSRASY y PSRSYNC, para mantener activadas las señales de reset del prescaler correspondiente. Esto asegura que los correspondientes Timers/Counters estén detenidos y se puedan configurar al mismo valor sin correr el riesgo de que uno de ellos avance durante la configuración. Si se escribe un cero en el bit TSM, los bits PSRASY y PSRSYNC se limpian por hardware y los Timers/Counters empiezan a contar simultáneamente.
PSRASY
Prescaler Reset Timer/Counter2 Cuando este bit vale uno, el prescaler del Timer/Counter2 se reseteará. Normalmente este bit se limpia de inmediato por hardware. Si se escribe en este bit cuando el Timer/Counter2 está trabajando en modo asíncrono, el bit permanecerá en uno hasta que se resetee el prescaler. El bit no se limpiará por hardware si el bit TSM vale uno.
45
GTCCR
TSM
---
PSRSYNC
---
---
---
---
PSRASY
PSRSYNC
Prescaler Reset Si este bit vale uno, el prescaler del Timer/Counter0 y el Timer/Counter1 se reseteará. Normalmente este bit se limpia de inmediato por hardware, excepto cuando el bit TSM valga uno. Note que el Timer/Counter0 y el Timer/Counter1 comparten el mismo prescaler y el reset de este prescaler afecta a ambos Timers.
Registros del Timer2 TCCR2A
COM2A1
COM2A0
COM2B1
COM2B0
---
---
WGM21
WGM20
TCCR2B
FOC2A
FOC2B
---
---
WGM22
CS22
CS21
CS20
TIMSK2
---
---
---
---
---
OCIE2B
OCIE2A
TOIE2
TIFR2
---
---
---
---
---
OCF2B
OCF2A
TOV2
TCNT2 OCR2A OCR2B
CS22:0
Clock Select Los tres bits de Clock Select seleccionan la fuente de reloj que usará el Timer/Counter2. CS22 CS21 CS20
Fuente de reloj del Timer2
0
0
0
Sin fuente de reloj (Timer/Counter2 detenido).
0
0
1
clkT2S
0
1
0
clkT2S/8
0
1
1
clkT2S/32
(Desde el prescaler)
1
0
0
clkT2S/64
(Desde el prescaler)
1
0
1
clkT2S/128
(Desde el prescaler)
1
1
0
clkT2S/256
(Desde el prescaler)
1
1
1
clkT2S/1024
ASSR – Asynchronous Status Register
(Sin prescaler) (Desde el prescaler)
(Desde el prescaler)
46
Si se efectúa una escritura en cualquiera de los cinco registros del Timer/Counter2 cuando su correspondiente flag de busy (ocupado) vale uno, el valor actualizado puede resultar corrompido y se podría disparar una interrupción inintencionada. El mecanismo para leer los registros TCNT2, OCR2A, OCR2B, TCCR2A y TCCR2B es diferente. Al leer TCNT2 se obtiene el valor actual del Timer. Al leer OCR2A, OCR2B, TCCR2A y TCCR2Bse obtiene el valor del registro de almacenamiento temporal.
ASSR --- EXCLK EXCLK
AS2
TCN2UB
OCR2AUB
OCR2BUB
TCR2AUB
Enable External Clock Input Al escribir uno en el bit EXCLK, y se tiene seleccionado el reloj asíncrono, se habilita el buffer de entrada de reloj externo y se puede poner un reloj externo al pin Timer Oscillator 1 (TOSC1) en vez del XTAL de 32kHz. La escritura en EXCLK se debería realizar antes de seleccionar la operación asíncrona. Note que el oscilador de XTAL solo trabajará cuando este bit vale cero.
AS2
Asynchronous Timer/Counter2 Al escribir cero en este bit, el reloj del Timer/Counter2 provendrá del reloj del sistema F_CPU. Al escribir uno en AS2, el reloj del Timer/Counter2 derivará de un XTAL externo conectado al pin Timer Oscillator 1 (TOSC1). Si se cambia el valor de AS2, se podrían corromper los contenidos de los registros TCNT2, OCR2A, OCR2B, TCCR2A y TCCR2B.
TCN2UB
Timer/Counter2 Update Busy Cuando el Timer/Counter2 opera en modo asíncrono y se escribe en el registro TCNT2, este bit se activa a uno. Este bit se limpiará por hardware cuando TCNT2 se actualice desde el registro de almacenamiento temporal. Un cero lógico en este bit indica que el registro TCNT2 está listo para ser actualizado con un nuevo valor.
OCR2AUB
Output Compare Register2 A Update Busy Cuando el Timer/Counter2 opera en modo asíncrono y se escribe en el registro OCR2A, este bit se activa a uno. Este bit se limpiará por hardware cuando OCR2A se actualice desde el registro de almacenamiento temporal. Un cero lógico en este bit indica que el registro OCR2A está listo para ser actualizado con un nuevo valor.
OCR2BUB
Output Compare Register2 B Update Busy Cuando el Timer/Counter2 opera en modo asíncrono y se escribe en el registro OCR2B, este bit se activa a uno. Este bit se limpiará por hardware cuando OCR2B se
TCR2BUB
47
ASSR --- EXCLK
AS2
TCN2UB
OCR2AUB
OCR2BUB
TCR2AUB
TCR2BUB
actualice desde el registro de almacenamiento temporal. Un cero lógico en este bit indica que el registro OCR2B está listo para ser actualizado con un nuevo valor.
TCR2AUB
Timer/Counter Control Register2 A Update Busy Cuando el Timer/Counter2 opera en modo asíncrono y se escribe en el registro TCCR2A, este bit se activa a uno. Este bit se limpiará por hardware cuando TCCR2A se actualice desde el registro de almacenamiento temporal. Un cero lógico en este bit indica que el registro TCCR2A está listo para ser actualizado con un nuevo valor.
TCR2BUB
Timer/Counter Control Register2 B Update Busy Cuando el Timer/Counter2 opera en modo asíncrono y se escribe en el registro TCCR2B, este bit se activa a uno. Este bit se limpiará por hardware cuando TCCR2B se actualice desde el registro de almacenamiento temporal. Un cero lógico en este bit indica que el registro TCCR2B está listo para ser actualizado con un nuevo valor.
El Timer1 y el Timer3 Si antes habíamos estudiado a la par el Timer0 y el Timer2 por ser muy simulares, esta vez la referencia al Timer3 se reduce solo a su mención en los títulos. Sucede que el Timer3 es completamente idéntico al Timer1. La mala noticia es que solo está disponible en los ATmega128xy :-( El Timer/Counter1, o simplemente Timer1, es de 16 bits. En principio, opera de la misma forma en que lo hace el Timer0, solo que utilizando sus registros de datos de 16 bits. Esto lo podremos comprobar al notar la gran similitud que tienen sus diagramas de bloques. Como en los registros de 8 bits. Por mismo sucede
megaAVR cada registro de E/S es de 8 bits, en realidad los datos del Timer1 están compuestos por la unión de dos registros de ejemplo, TCNT1 se forma uniendo los registros TCNT1H y TCNT1L. Lo con los siguientes registros
= unión de OCR1AH y OCR1AL OCR1A OCR1B = unión de OCR1BH y OCR1BL ICR1 = unión de ICR1H e ICR1L
Los registros de control del Timer1 bits: TCCR1A, TCCR1B, TCCR1C, TIMSK1,TIFR1 y GTCCR.
siguen
siendo
de
8
48
Diagrama de bloques del Timer1. Este diagrama del Timer2 es una adaptación que nos facilitará su descripción funcional. Es una descripción corta para no redundar demasiado sobre lo explicado para el Timer0. Bueno, empecemos: Los bits WGM configuran en gran medida el modo de operación del Timer2. Con ellos podemos escoger entre los modos Normal, CTC o PWM. Notemos que a diferencia del Timer0, ahora son 4 bits WGM. Esto es porque el Timer2 ofrece muchas variantes de los modos CTC y sobre todo PWM. Hay 16 modos en total, pero muchos de ellos son redundantes y de poca utilidad. Inicio de Conteo
Tope de Conteo
Normal
0x00
0xFFFF
1
PWM de Fase Correcta, 8 bits
0x00
0x00FF
1
0
PWM de Fase Correcta, 9 bits
0x00
0x01FF
0
1
1
PWM de Fase Correcta 10 bits
0x00
0x03FF
0
1
0
0
CTC
0x00
OCR1A
0
1
0
1
Fast PWM, 8 bits
0x00
0x00FF
0
1
1
0
Fast PWM, 9 bits
0x00
0x01FF
0
1
1
1
0x00
0x03FF
1
0
0
0
0x00
ICR1
1
0
0
1
Fast PWM, 10 bits PWM de Fase y Frecuencia Correctas PWM de Fase y Frecuencia Correctas
0x00
OCR1A
WGM13
WGM12
WGM11
WGM10
0
0
0
0
0
0
0
0
0
0
Modo de Operación del Timer1
49
Inicio de Conteo
Tope de Conteo
PWM de Fase Correcta
0x00
ICR1
1
PWM de Fase Correcta
0x00
OCR1A
0
0
CTC
0x00
ICR1
1
0
1
Reservado
0x00
–
1
1
1
0
Fast PWM
0x00
ICR1
1
1
1
1
Fast PWM
0x00
OCR1A
WGM13
WGM12
WGM11
WGM10
1
0
1
0
1
0
1
1
1
1
Modo de Operación del Timer1
Los bits CS (Clock Select), como indica su nombre, son para configurar la fuente de reloj del Timer2. El reloj del Timer2 es idéntico al del Timer0. Recordemos que comparten el mismo prescaler y aunque también trabajan con los mismos divisores o factores de prescaler, su configuración no tiene que ser la misma, es decir, podemos emplear nuestro Timer0 con un factor de 8 y el Timer2 con el factor 1024. Para mayor información puedes revisar la sección los prescalers del Timer0 y del Timer2. CS12
CS11
CS10
0
0
0
0
0
1
0
1
0
0
1
1
1
0
0
1
0
1
1
1
0
1
1
1
Fuente de reloj del Timer1 Sin fuente de reloj (el Timer1 está detenido) F_CPU (Sin prescaler)
F_CPU/8 (con prescaler) F_CPU/64 (con prescaler) F_CPU/256 (con prescaler) F_CPU/1024 (con prescaler) Reloj externo en pin T1. El Timer1 avanza en el flanco de bajada. Reloj externo en pin T1. de subida.
El Timer1 avanza en el flanco
Los bits COM . Hasta ahora los habíamos estado dejando de lado. Como su nombre lo indica, estos bits controlan en modo de salida de los comparadores (Compare Output Mode). Los habíamos ignorado porque no tiene mucho sentido usarlos en los modos Normal o CTC. En cambio ahora que entraremos de lleno en el modo PWM, serán más que necesarios. Desde el momento en que configuremos estos bits para sacar las ondas (PWM o no) por los pines OC1A y/o OC1B el Timer2 asumirá el control sobre ellos y ya no podremos usarlos como puertos de E/S generales (mediante el registro PORTx respectivo). Sin embargo, todavía será necesario configurarlos como pines de salida en sus correspondientes bits del registro DDRx. Los bits COM son detestables porque tienen diferente efecto dependiendo de si el Timer opera en modo Normal y CTC, en modo Fast PWM o en modo PWM de Fase correcta y PWM de Fase y Frecuencia correctas. Tan solo
50
mencionar estos términos marea un poco, ¿verdad? Por eso voy a reservar las tablas correspondientes para otro momento. El Registro ICR1. Como te habrás dado cuenta, este registro es nuevo; es propio del Timer1. Su nombre ICR1 es el acrónimo de Input Capture Register 1 y tiene dos funciones. En primer lugar sirve para capturar el valor del registro TCNT1 justo en el momento en que el bloque Fuente de Captura (ver la figura) le dé la señal. Esta función es útil en aplicaciones que necesitan medir la frecuencia de alguna señal externa, pero no tiene nada que ver con el modo PWM así que la ignoraremos por el momento. Sin embargo, el registro ICR1 también puede trabajar como el tope del conteo del Timer1 en los modos PWM. Ésta es su faceta en que más se le emplea. El bloque FUENTE DE CAPTURA . Se trata de un circuito que selecciona la señal de captura. Según su diagrama mostrado abajo, se disponen de dos opciones: o es el pin ICP del megaAVR o es la salida del Comparador Analógico. La elección se hace mediante el bit ACIC, del registro ACRS. El bit ICNC1 (del registro TCCR1B) puede activar el eliminador de ruido para evitar falsos disparos causados por los picos del ruido. En la etapa final, el bit ICES1 (también del registro TCCR1B) establece si el disparo se dará en el flanco de subida o de bajada de la fuente seleccionada. Una vez detectada la señal elegida, se activará el flag ICF1 y el registro ICR1 le tomará una fotografía al registro TCNT1. Este evento se puede aprovechar para programar y disparar su interrupción.
Diagrama de bloques de la Entrada de Captura. El Timer1 y el Timer3 en Modos Normal y CTC Con su registro TCNT1 de 16 bits, el Timer1 puede contar desde 0x0000 hasta 0xFFFF. En modo Normal y en modo CTC avanza siempre en modo ascendente. En modo Normal llega hasta 0xFFFF y en modo CTC avanza hasta que coincida con el valor del registro OCR1A o ICR1 que también son de 16 bits. El conteo es siempre cíclico, o sea que después de llegar a su valor tope, la cuenta se reinicia desde 0. Notamos que el modo CTC con tope de conteo establecido por registro ICR1 no era una opción disponible en los Timers de 8 bits. Este modo se selecciona con los bits WGM13:0 = 1100b.
51
Cualquiera que sea su modo de operación, el registro de conteo TCNT1 siempre es comparado con los registros OCR1A y OCR1B. Las Coincidencias detectadas activarán los flags OCF1A y OCF1B, respectivamente (ver el diagrama de bloques) y se pueden usar estos eventos para programar las interrupciones de Coincidencias setenado los bits del registro TIMSK1 OCIE1A para la coincidencia entreTCNT1 y OCR1A, y OCIE1B para la coincidencia entre TCNT1 y OCR1B. Si bien pueden usarse las Coincidencias como señales para setear, limpiar o conmutar el estado de los pines OC1A y/o OC1B, según la configuración de los bits COM , nunca he visto una aplicación que saque provecho de esa característica. El Timer1 se reserva de forma casi exclusiva para el modo PWM y es raro verlo trabajar incluso en los modos Normal o CTC. Con todo, si hubiera que hacerlo, no hay mucho que añadir sobre la operación de los Timers0 o 2 en ese modo. Obviamente, las fórmulas de temporización adoptarán nuevas formas, teniendo en cuenta los 16 bits del Timer1. El Tiempo que pasará entre la carga del Timer1 con un valor inicial TCNT1 hasta su desbordamiento está dado por:
Las siguientes formulas nos dan un camino para hallar por partes las dos incógnitas de la primera fórmula.
Si el Timer1 está programado en modo CTC, el tiempo entre coincidencia y coincidencia de TCNT1 conTOPE podemos calcular utilizando la fórmula.
52
En este caso la fórmula se puede descomponer en las siguientes dos fórmulas de una sola variable.
Si no tienes idea de cómo usarlas o de cuál modo (Normal o CTC) es mejor para temporizar, puedes revisar la teoría dedicada al Timer0 y al Timer2.
El Timer1 y el Timer3 en Modo PWM El mecanismos como los Timers generan ondas PWM es fácil de asimilar asumiendo que ya nos es familiar las funciones de sus registros de datos TCNT1, OCR1A y OCR1B. Ahora se les suma el registro ICR1, cuya función en este caso es establecer el tope de conteo opcional.
WGM13
WGM12
WGM11
WGM10
Modo de Operación del Timer1
Inicio de Conteo
Tope de Conteo
0
0
0
1
PWM de Fase Correcta, 8 bits
0x00
0x00FF
0
0
1
0
PWM de Fase Correcta, 9 bits
0x00
0x01FF
0
0
1
1
PWM de Fase Correcta, 10 bits
0x00
0x03FF
0
1
0
1
Fast PWM, 8 bits
0x00
0x00FF
0
1
1
0
Fast PWM, 9 bits
0x00
0x01FF
0
1
1
1
0x00
0x03FF
1
0
0
0
0x00
ICR1
1
0
0
1
0x00
OCR1A
1
0
1
0
Fast PWM, 10 bits PWM de Fase y Frecuencia Correctas PWM de Fase y Frecuencia Correctas PWM de Fase Correcta
0x00
ICR1
1
0
1
1
PWM de Fase Correcta
0x00
OCR1A
1
1
1
0
Fast PWM
0x00
ICR1
1
1
1
1
Fast PWM
0x00
OCR1A
La resolución para las ondas PWM se puede fijar a 8, 9 ó 10 bits, o definir por el registro OCR1A oICR1. La mínima resolución permitida es de 2 bits (OCR1A o ICR1 = 0x0003) y la máxima frecuencia posible es de 16 bits
53
(OCR1A o ICR1 = 0xFFFF). La resolución PWM en bits se puede calcular usando la siguiente fórmula.
Cuando se cambia el valor del tope, el programa debe asegurarse de que el nuevo valor del tope sea mayor o igual que el valor de todos los registros de comparación. Si el valor tope es menor que cualquier registro de comparación, nunca ocurrirá una coincidencia entre TCNT1 y OCR1A/OCR1B. Note que al usar los valores tope fijos, los bits no usados serán enmascarados a cero cuando se escriba cualquiera de los registros OCR1A/OCR1B. El procedimiento para actualizar ICR1 difiere de la actualización de OCR1A cuando se usan para definir el valor tope. El registro ICR1 no usa doble buffer. Esto significa que al cambiar ICR1 a un valor inferior cuando un contador está corriendo hay un riesgo de que el nuevo valor escrito en ICR1 sea inferior a TCNT1. El resultado será que el contador no encontrará una coincidencia en la pendiente actual. Sin embargo, el registro OCR1A sí usa doble buffer. Con esta característica podemos escribir enOCR1A en cualquier momento porque el valor escrito será puesto en su buffer de respaldo. El registroOCR1A se actualizará con el valor de su buffer en el siguiente ciclo de reloj del Timer en que TCNT1coincida con el tope actual. El uso del registro ICR1 para definir el valor tope funciona bien cuando se usan topes fijos. El uso deICR1 deja libre el registro OCR1A para ser usado para generar una salida PWM en OC1A . Sin embargo, si la frecuencia PWM base cambia activamente (cambiando el valor tope), el uso de OCR1A como tope es claramente la mejor opción debido a su característica de doble buffer. En modo Fast PWM los comparadores permiten la generación de ondas PWM por los pines OC1A y/oOC1B. Estos pines deberán ser configurados como salida. El Timer1 presenta 3 modos PWM.
Fast PWM En este modo el registro TCNT1 avanza siempre en forma ascendente hasta llegar a un valor tope que puede ser el del registro OCR1A o ICR1, o bien un valor fijo 0x00FF, 0x01FF o 0x03FF. El modo Fast PWM o Fast Pulse Width Modulation difiere de las otras opciones de PWM por su operación de pendiente única. El contador TCNT1 avanza desde 0x0000
54
hasta el valor tope establecido y luego se reinicia desde 0x0000. Los bits COM respectivos establecen si la onda saldrá invertida o no. En modo no-invertido el estado del pin OC1A /OC1B se limpia en la Coincidencia y se setea al llegar a 0x0000. En modo invertido la salida OC1A /OC1B se setea en la Coincidencia y se limpia al llegar a 0x0000. Debido a su operación de pendiente única, la frecuencia de la señal Fast PWM puede ser hasta dos veces mayor que la frecuencia de las ondas en modo PWM de Fase Correcta y PWM de Fase y Frecuencia Correctas, que usan la operación de doble pendiente. Esta alta frecuencia hace del modo Fast PWM muy adecuado para aplicaciones de regulación de potencia, rectificaciones y DAC. La siguiente figura muestra el diagrama de tiempos del modo Fast PWM cuando se usa el registroICR1 como tope. En el diagrama el valor del registro TCNT1 se muestra como un histograma para ilustrar la operación en modo de pendiente única. El diagrama incluye las salidas PWM invertida y no invertida. Las pequeñas marcas de líneas horizontales en la pendiente de TCNT1 representan las Coincidencias entre los registros TCNT1 y OCR1A. El flag de interrupción OCF1A /OCF1B se setea cuando ocurra la Coincidencia.
El flag de desbordamiento TOV1 se activa cada vez que el contador alcanza el tope. Adicionalmente, cuando se usa OCR1A o ICR1 para definir el tope del conteo, el flag OCF1A o ICF1 se activa en el mismo ciclo de reloj del Timer en que se setea TOV1. Si está habilitada una de las interrupciones, se puede usar la función ISR para actualizar el valor tope.
55
La frecuencia de la onda Fast PWM se puede calcular con la siguiente ecuación.
La variable N representa el factor de prescaler (1, 8, 64, 256 ó 1024) Los valores extremos del registro OCR1A representan casos especiales. Si OCR1A es igual a 0x0000 la salida será un pico angosto para cada ciclo de reloj del Timer de tope+1. Si OCR1A es igual al tope, la salida será un estado constante de alto o bajo (dependiendo de la polaridad de la salida establecida por los bits COM ).
PWM de Fase Correcta Al igual que el modo PWM de Fase y Frecuencia Correctas, el modo PWM de fase correcta está basado en una operación de doble pendiente, esto es, el Timer cuenta repetidamente desde 0x0000 hasta el tope y luego desde el tope hasta 0x0000. En el modo no-invertido, la salida se limpia en la coincidencia entre TCNT1 y OCR1A si la cuenta es ascendente y se setea en la coincidencia si el conteo es descendente. En modo invertido la operación es inversa. Debido a la operación de doble pendiente la frecuencia de la señal PWM será menor que la que se puede obtener en modo Fast PWM. Sin embargo, la simetría de su onda la hace preferible para las aplicaciones de control de motores.’
Los registros OCR1A y OCR1B se actualizan cuando el contador llegue al tope.
con el
valor de
su buffer
doble
56
El flag TOV1 se activa cada vez que el contador alcanza 0x0000. Si se usa el registro ICR1 como tope del conteo, su flag ICF1 se activará en el mismo ciclo de reloj del Timer en que el contador alcance el tope. Se pueden usar los flags de interrupción para generar las interrupciones correspondientes. Como se ve en el tercer periodo del diagrama, el hecho de cambiar el tope activamente mientras el contador está corriendo puede resultar en una salida asimétrica, debido al momento en que se actualizan los registros OCR1A/OCR1B. Puesto que la actualización de OCR1A/OCR1B ocurre en el tope, el periodo PWM empieza y termina en el tope. Esto implica que la pendiente de bajada está determinada por el tope previo y la pendiente de subida está determinada por el nuevo tope. Se recomienda usar el modo PWM de Fase y Frecuencia Correctas en lugar del modo de Fase Correcta cuando se va a cambiar el tope mientras el Timer está corriendo. Cuando se usa un valor estático de tope prácticamente no hay diferencias entre ambos modos de operación. La frecuencia PWM para la salida se puede calcular por la siguiente ecuación.
Cuando el valor de OCR1A/OCR1B es igual a cero, la salida en modo no-invertido es un estado bajo constante, a diferencia del modo Fast PWM donde aparecen unos delgados picos.
PWM de Fase y Frecuencia Correctas La principal diferencia entre el modo PWM de Fase y Frecuencia Correctas y el modo de Fase Correcta es el tiempo en que se actualizan los registros OCR1A/OCR1B desde su buffer doble.
57
En contraste con el modo de Fase Correcta, la figura muestra que la salida generada es, simétrica en todos sus periodos. Puesto que los registros OCR1A/OCR1B se actualizan al llegar a 0x0000, las pendientes de subida y de bajada serán siempre iguales. Esto brinda pulsos de salida simétricos y por tanto de frecuencia correcta. El flag de Desbordamiento TOV1 se activa cuando el contador llega a 0x0000.’ Cuando se usaOCR1A como tope del contador, su flag OCF1A se activará cuando TCNT1 alcance el tope. Se pueden usar estos flags para generar las interrupciones correspondientes. Los registros OCR1A/OCR1B se actualizan desde su buffer doble en el mismo ciclo de reloj del Timer en que el contador llega a 0x0000.
Registros del Timer1 TCCR1A
COM1A1
COM1A0
COM1B1
COM1B0
---
---
WGM11
WGM10
TCCR1B
ICNC1
ICES1
---
WGM13
WGM12
CS12
CS11
CS10
TCCR1C
FOC1A
FOC1B
---
---
---
---
---
---
TCNT1H TCNT1L OCR1AH OCR1AL
58 OCR1BH OCR1BL ICR1H ICR1L TIMSK1
--
---
ICIE1
---
---
OCIE1B
OCIE1A
TOIE1
TIFR1
--
---
ICF1
---
---
OCF1B
OCF1A
TOV1
GTCCR
TSM
---
---
---
---
---
PSRASY
PSRSYNC
TCCR1A – Timer/Counter Control Register 1 A TCCR1A COM1A1
COM1A0
COM1B1
COM1B0
---
COM1A1:0
Compare Output Mode for Channel A
COM1B1:0
Compare Output Mode for Channel B
---
WGM11
Los bits COM1A1:0 y COM1A1:0 controlan el comportamiento de los pines Output Compare (OC1A y OC1B, respectivamente). Si uno o los dos bits COM1A1:0 están seteados, la salida OC0A tiene prioridad sobre la funcionalidad normal del pin al que está conectado. Si uno o los dos bits COM1B1:0 están seteados, la salida OC0B tiene prioridad sobre la funcionalidad normal del pin al que está conectado. Sin embargo, note que el bit del registro DDR correspondiente al pin OC0A o OC0B debe estar configurado como salida para habilitar el driver de salida. Cuando OC0A o OC0B está conectado al pin la función de los bits COM1x1:0 depende de la configuración de los bits WGM13:0. La siguiente tabla muestra la funcionalidad de los bits COM1x1:0 cuando los bits WGM13:0 están configurados en modo Normal o CTC (no PWM). COM1A1/ COM1B1
COM1A0/ COM1B0
0
0
0
1
OC0A/OC0B conmuta en Coincidencia
1
0
OC0A/OC0B se limpia en Coincidencia
1
1
OC0A/OC0B se setea en Coincidencia
Descripción OC0A/OC0B de pin
desconectado.
Operación
normal
La siguiente tabla muestra la funcionalidad de los bits COM1A1:0 cuando los bits WGM13:0 están configurados en modo Fast PWM.
Nota: ocurre un caso especial cuando OCR1A/OCR1B es el tope del
WGM10
59
TCCR1A COM1A1
COM1A0
COM1B1
COM1B0
---
---
WGM11
conteo y el bit COM1A1/COM1B1 vale uno. En este caso se ignora la Coincidencia, pero la puesta a cero o a uno de OC0A/OC0B se produce al llegar a 0x0000. COM1A1/ COM1A0/ COM1B1 COM1B0
Descripción
0
0
OC1A/OC1B desconectado. Operación normal del pin
0
1
WGM13:0 = 14 o 15: OC1A conmuta en Coincidencia, OC1B desconectado (operación normal de pin). Para las demás configuraciones de WGM1, OC1A/OC1B desconectado (operación normal de pin).
1
0
OC1A/OC1B se limpia en la Coincidencia, y se setea al llegar a 0x0000 (modo no-invertido)
1
1
OC1A/OC1B se setea en la Coincidencia, y se limpia al llegar a 0x00 (modo invertido).
La siguiente tabla muestra la funcionalidad de los bits COM1A1:0 cuando los bits WGM13:0 están configurados en modo PWM de Fase Correcta o PWM de Fase y Frecuencia Correctas. Nota: ocurre un caso especial cuando OCR1A es el tope del conteo y el bit COM1A1/COM1B1 vale uno... COM1A1/ COM1A0/ COM1B0 COM1B1
Bits 3:2
Descripción
0
0
OC1A/OC1B desconectado. Operación normal del pin
0
1
WGM13:0 = 9 u 11: OC1A conmuta en Coincidencia, OC1B desconectado (operación normal de pin). Para las demás configuraciones de WGM1, OC1A/OC1B desconectado (operación normal de pin).
1
0
OC1A/OC1B se limpia en la Coincidencia cuando se cuenta hacia arriba, y se setea en la Coincidencia cuando se cuenta hacia abajo.
1
1
OC1A/OC1B se setea en la Coincidencia cuando se cuenta hacia arriba, y se limpia en la Coincidencia cuando se cuenta hacia abajo.
Reservados Estos bits están reservados en los ATmega164A/PA, ATmega324A/PA, ATmega644A/PA y ATmega1284/P, y siempre se
WGM10
60
TCCR1A COM1A1
COM1A0
COM1B1
COM1B0
---
---
WGM11
leerán como cero.
GM11:0
aveform Generation Mode Estos bits se combinan con los bits WFGM13:2 del registro TCCR1B. Juntos controlan la secuencia de conteo del contador, el valor máximo del conteo (el tope) y el tipo de la forma de onda que se generará. Los modos operación que soporta la unidad del Timer/Counter son: modo Normal (contador), modo Clear Timer on Compare Match (CTC), y tres tipos de Modulación de Ancho de Pulso (PWM). WGM13 WGM12 WGM11 WGM10
Modo de Operación del Timer1
Tope del Conteo
0
0
0
0
Normal
0xFFFF
0
0
0
1
PWM de Fase Correcta, 8 bits
0x00FF
0
0
1
0
PWM de Fase Correcta, 9 bits
0x01FF
0
0
1
1
PWM de bits
0x03FF
0
1
0
0
CTC
OCR1A
0
1
0
1
Fast PWM, 8 bits
0x00FF
0
1
1
0
Fast PWM, 9 bits
0x01FF
0
1
1
1
0x03FF
1
0
0
0
1
0
0
1
1
0
1
0
Fast PWM, 10 bits PWM de Fase y Frecuencia Correctas PWM de Fase y Frecuencia Correctas PWM de Fase Correcta
1
0
1
1
PWM de Fase Correcta
OCR1A
1
1
0
0
CTC
ICR1
1
1
0
1
Reservado
1
1
1
0
Fast PWM
ICR1
1
1
1
1
Fast PWM
OCR1A
Fase
Correcta,
10
ICR1 OCR1A ICR1
–
WGM10
61 TCCR1B – Timer/Counter Control Register 1 B
TCCR1B
ICNC1
ICNC1
ICES1
---
WGM13
WGM12
CS12
CS11
Input Capture Noise Canceler La escritura de uno en este bit activa el circuito eliminador de ruido Input Capture Noise Canceler del Timer1. Cuando el eliminador de ruido está activado, la entrada del pin Input Capture (ICP1) será filtrada. La función del filtro requiere de la igualdad de cuatro muestreos del pin ICP1 para cambiar su estado. Debido a esto, cuando el eliminador de ruido está habilitado, la unidad de Input Capture se retrasa por cuatro ciclos del oscilador.
ICES1
Input Capture Edge Select Este bit selecciona el flanco en el pin Input Capture (ICP1) que se usará para disparar el evento de captura. Cuando el bit ICES1 vale uno, el disparo se dará en el flanco de bajada, y si ICES1 vale cero, el disparo se dará en el flanco de subida. Cuando se dispara una captura de acuerdo con la configuración del bit ICES1, el valor del contador se copiará al registro Input Capture Register (ICR1). El evento también seteará el flag Input Capture (ICF1), y se puede usar para generar una Interrupción de Input Capture, si esta interrupción está habilitada. Cuando se usa como el tope de conteo el registro ICR1 (ver la descripción de los bits WGM13:0 ubicados en los registros TCCR1A y TCCR1B), ICP1 queda desconectado y por tanto se deshabilita la función de Input Capture.
Bits 5
Reservado Este bit está reservado para usos futuros. Al escribir en TCCR1B, este bit se debe mantener en cero para asegurar la compatibilidad con futuros dispositivos.
GM13:2
aveform Generation Mode Ver la descripción de los bits WGM11:0 del registro TCCR1A.
CS12:0
Clock Select Los tres bits de Clock Select seleccionan la fuente de reloj que usará el Timer/Counter.
CS10
62
TCCR1B
ICNC1
ICES1
---
WGM13
WGM12
CS12
CS11
CS10
Si se usan los modos de pin externo para el Timer/Counter1, las transiciones en el pin T1 harán el contador incluso si el pin está configurado como salida. Esta característica permite el control software del contador. CS12
CS11
CS10
Fuente de reloj del Timer1
0
0
0
0
0
1
F_CPU (Sin prescaler)
0
1
0
F_CPU/8 (con prescaler)
0
1
1
F_CPU/64 (con prescaler)
1
0
0
F_CPU/256 (con prescaler)
1
0
1
F_CPU/1024 (con prescaler)
1
1
0
1
1
1
Reloj externo en pin T1. El Timer1 avanza en el flanco de bajada. Reloj externo en pin T1. El Timer1 avanza en el flanco de subida.
Sin detenido)
fuente
de
reloj
(el
Timer1
está
TCCR1C – Timer/Counter Control Register 1 C TCCR1C FOC1A
FOC1B
---
FOC1A
Force Output Compare A
FOC1B
Force Output Compare B
---
Los bits FOC1A/FOC1B solo están WGM13:0 establecen un modo no PWM.
---
activos
---
cuando
---
los
bits
Sin embargo, para asegurar la compatibilidad con futuros dispositivos, estos bits se deben mantener en cero al escribir en TCCR1C cuando el Timer está operando en uno de los modos PWM. Si se escribe un uno en el bit FOC1A/FOC1B, se fuerza una Coincidencia inmediata en la Unidad Generadora de Forma de Onda. La salida de OC1A/OC1B cambia de acuerdo con la configuración de los bits COM1A1:0/COM1B1:0. Note que los bits FOC1A/FOC1B se implementan como strobes. Así que es el valor presente en los bits COM1A1:0/COM1B1:0 los que determinan el efecto de la Coincidencia forzada. Un strobe de FOC1A/FOC1B no generará ninguna interrupción ni tampoco reseteará el Timer en modo CTC si se usa OCR1A como
---
63
TCCR1C FOC1A
FOC1B
---
---
---
---
---
---
tope del conteo. Los bits FOC1A/FOC1B siempre se leen como cero.
Bits 5:0
Reservados Estos bits están reservados y siempre se leerán como cero.
TCNT1H y TCNT1L – Timer/Counter 1 Register TCNT1H TCNT1L Bits 15:0
TCNT1H[7:0] y TCNT1L[7:0] Los dos registros TCNT1H y TCNT1L combinados, dan acceso directo al contador de 16 bits del Timer/Counter para las operaciones de lectura y escritura. Para asegurar que los dos registros se lean y escriban al mismo tiempo, el acceso se realiza utilizando un registro temporal de 8 bits para almacenar el byte alto (TCNT1H). Este registro temporal se comparte con todos los otros registros de 16 bits. La modificación de TCNT1 cuando el contador está corriendo conlleva un riesgo de perder una Coincidencia entre los registros TCNT1 y OCR1A/ OCR1B. La escritura en el registro TCNT0 bloquea (quita) la Coincidencia en el siguiente ciclo de reloj del Timer para todas las unidades de comparación.
OCR1AH y OCR1AL – Output Compare Register 1 A
64
OCR1AH OCR1AL Bits 15:0
OCR1AH[7:0] y OCR1AL[7:0] Los dos registros Output Compare A contienen un valor de 16 bits que es continuamente comparado con el valor del contador (TCNT1). Se puede usar una Coincidencia para disparar una Interrupción en Coincidencia, o para generar una onda por el pin OC1A. Para asegurar que los dos registros Output Compare A se escriban simultáneamente cuando el CPU escribe en estos registros, el acceso se realiza utilizando un registro temporal de 8 bits para almacenar el byte alto (OCR1AH). Este registro temporal es compartido por todos los demás registros de 16 bits.
OCR1BH y OCR1BL – Output Compare Register 1 B OCR1BH OCR1BL Bits 15:0
OCR1BH[7:0] y OCR1BL[7:0] Los dos registros Output Compare B contienen un valor de 16 bits que es continuamente comparado con el valor del contador (TCNT1). Se puede usar una Coincidencia para disparar una Interrupción en Coincidencia, o para generar una onda por el pin OC1B. Para asegurar que los dos registros Output Compare B se escriban simultáneamente cuando el CPU escribe en estos registros, el acceso se realiza utilizando un registro temporal de 8 bits para almacenar el byte alto (OCR1BH). Este registro temporal es compartido por todos los demás registros de 16 bits.
ICR1H y ICR1L – Input Capture Register 1 ICR1H ICR1L Bits 15:0
ICR1H [7:0] y ICR1L [7:0] El registro Input Capture se actualiza con el valor del contador (TCNT1) cada vez que ocurre un evento en el pin ICP1 (u opcionalmente en la salida del Comparador Analógico que va al Timer/Counter1). Se puede usar el registro Input Capture para definir el valor tope del conteo. El registro Input Capture es de 16 bits. Para asegurar que sus dos bytes se escriban simultáneamente cuando el CPU escribe en estos registros, el acceso se realiza utilizando un registro temporal de 8 bits para almacenar el byte alto (ICR1H). Este registro temporal es compartido por todos los demás registros de 16 bits.
65
TIMSK1 – Timer/Counter Interrupt Mask 1 Register TIMSK1
--
---
ICIE1
---
---
Bits 7:6
Reserved
Bits 4:3
Reserved
ICIE1
Timer/Counter1, Input Capture Interrupt Enable
OCIE1B
OCIE1A
TOIE1
Estos bits están reservados y siempre se leerán como cero. Al escribir uno en este bit, y el bit enable general I del registro SREG vale uno, se habilita la Interrupción Input Capture del Timer/Counter1. Al activarse el flag ICF1, del registro TIFR1, se ejecutará su respectiva función de interrupción ISR.
OCIE1B
Timer/Counter1, Output Compare B Match Interrupt Enable Al escribir uno en este bit, y el bit enable general I del registro SREG vale uno, se habilita la Interrupción en Coincidencia B del Timer/Counter1. Al activarse el flag OCF1B, del registro TIFR1, se ejecutará su respectiva función de interrupción ISR.
OCIE1A
Timer/Counter1, Output Compare A Match Interrupt Enable Al escribir uno en este bit, y el bit enable general I del registro SREG vale uno, se habilita la Interrupción en Coincidencia A del Timer/Counter1. Al activarse el flag OCF1A, del registro TIFR1, se ejecutará su respectiva función de interrupción ISR.
TOIE1
Timer/Counter1 Overflow Interrupt Enable Cuando se escribe uno en el bit TOIE1, y el bit I del registro SREG vale uno, se habilita la Interrupción por Desbordamiento del Timer/Counter1. Si ocurre un Desbordamiento en el registro TCNT1, se ejecutará la función de Interrupción correspondiente, esto es, cuando se active al flag TOV1 del registro TIFR1.
TIFR1 – Timer/Counter Interrupt Flag 1 Register TIFR1
--
---
ICF1
---
Bits 7:6
Reserved
Bits 4:3
Reserved
ICF1
Timer/Counter1, Input Capture Flag
---
OCF1B
OCF1A
Estos bits están reservados y siempre se leerán como cero. Este flag se activa cuando ocurre un evento en el pin ICP1. Si la configuración de los bits WGM13:0 establecen el registro Input Capture como tope del conteo, el flag ICF1 se activará cuando el contador alcance el valor tope. El bit ICF1 se limpia automáticamente al ejecutarse la función de Interrupción respectiva. Alternativamente, ICF1 se puede limpiar por software escribiendo sobre él un uno lógico.
OCF1B
Timer/Counter1, Output Compare B Match Flag Este flag se activa en el ciclo de reloj del Timer después de que el valor del contador (TCNT1) coincida con el registro
TOV1
66
TIFR1
--
---
ICF1
---
---
OCF1B
OCF1A
TOV1
Output Compare B (OCR1B). Note que una Coincidencia forzada con el bit FOC1B no activará el flag OCF1B. El flag OCF1B se limpia por hardware al ejecutarse su correspondiente función de interrupción. Alternativamente se puede limpiar escribiendo en él un uno lógico. OCF1A
Timer/Counter1, Output Compare A Match Flag Este flag se activa en el ciclo de reloj del Timer después de que el valor del contador (TCNT1) coincida con el registro Output Compare A (OCR1A). Note que una Coincidencia forzada con el bit FOC1A no activará el flag OCF1A. El flag OCF1A se limpia por hardware al ejecutarse su correspondiente función de interrupción. Alternativamente se puede limpiar escribiendo en él un uno lógico.
TOV1
Timer/Counter1, Overflow Flag La activación de este flag depende de la configuración de los bits WGM13:0. En los modos Normal y CTC el flag TOV1 se activa cuando se desborda el Timer1. El flag TOV1 se limpia por hardware al ejecutarse su correspondiente función de interrupción. Alternativamente se puede limpiar escribiendo en él un uno lógico.
GTCCR – General Timer/Counter Control Register GTCCR TSM
TSM
---
---
---
---
---
PSRASY
Timer/Counter Synchronization Mode Al escribir uno en el bit TSM se activa el modo de Sincronización del Timer/Counter. En este modo, se mantendrán los valores que se escriban en los bits PSRASY y PSRSYNC, para mantener activadas las señales de reset del prescaler correspondiente. Esto asegura que los correspondientes Timers estén detenidos y se puedan configurar al mismo valor sin correr el riesgo de que uno de ellos avance durante la configuración. Si se escribe un cero en el bit TSM, los bits PSRASY y PSRSYNC se limpian por hardware y los Timers/Counters empiezan a contar simultáneamente.
PSRASY
Prescaler Reset Timer/Counter2 Cuando este bit vale uno, el prescaler del Timer/Counter2 se reseteará. Normalmente este bit se limpia de inmediato por hardware. Si se escribe en este bit cuando el Timer/Counter2 está trabajando en modo asíncrono, el bit
PSRSYNC
67
GTCCR
TSM
---
---
---
---
---
PSRASY
PSRSYNC
permanecerá en uno hasta que se resetee el prescaler. El bit no se limpiará por hardware si el bit TSM vale uno.
PSRSYNC
Prescaler Reset Si este bit vale uno, el prescaler del Timer/Counter0 y el Timer/Counter1 se reseteará. Normalmente este bit se limpia de inmediato por hardware, excepto cuando el bit TSM valga uno. Note que el Timer/Counter0 y el Timer/Counter1 comparten el mismo prescaler y el reset de este prescaler afecta a ambos Timers.
Registros del Timer3 TCCR3A
COM3A1
COM3A0
COM3B1
COM3B0
---
---
WGM31
WGM30
TCCR3B
ICNC3
ICES3
---
WGM33
WGM32
CS32
CS31
CS30
TCCR3C
FOC3A
FOC3B
---
---
---
---
---
---
TIMSK3
--
---
ICIE3
---
---
OCIE3B
OCIE3A
TOIE3
TIFR3
--
---
ICF3
---
---
OCF3B
OCF3A
TOV3
GTCCR
TSM
---
---
---
---
---
PSRASY
PSRSYNC
TCNT3H TCNT3L OCR3H OCR3AL OCR3BH OCR3BL ICR3H ICR3L
68
PRÁCTICA: EL TIMER1 EN MODO PWM Se generan ondas PWM de Fase y Frecuencia correctas por los dos canales, A y B. Se pueden configurar la Frecuencia y el Duty cycle de la dos señales desde la consola serial.
El código fuente
Circuito de la práctica.
/****************************************************************************** * FileName: main.c * Purpose: Timer1 - Operación en modo PWM * Processor: ATmega164P * Compiler: IAR-C y AVR-GCC (WinAVR) * Author: Shawn Johnson. http://www.cursomicros.com. * * Copyright (C) 2008 - 2012 Shawn Johnson. All rights reserved. * * License: Se permiten el uso y la redistribución de este código con * modificaciones o sin ellas, siempre que se mantengan esta
69 * licencia y la nota de autor de arriba. *****************************************************************************/ #include "avr_compiler.h" #include "usart.h" char GetNumStr(char * buffer, unsigned char len); int main(void) { unsigned int reg; char k, buffer[10]; usart_init(); puts("\r\n Timer1 en Modo PWM \r"); /* Configuración del Timer1 * - Bits WGM: PWM de Fase y Frecuencia Correctas con tope de conteo = ICR1 * - Bits COM: PWM no-invertida en los dos canales A y B * - Bits CS: Fuente de reloj = F_CPU/8 */ TCCR1A = (1<
valores iniciales */ // frecuencia PWM = 1/(2*200 ) = 2.5 kHz // Duty cycle canal A = 50% // Duty cycle canal B = 25%
puts("\r Escoja el registro a modificar "); puts("\r [1] ICR1 -> Frecuencia"); puts("\r [2] OCR1A -> Duty cycle A"); puts("\r [3] OCR1B -> Duty cycle B\r"); while(1) { do { k = getchar(); }while (!((1<='k') && (k<='3'))); switch (k) { case '1': puts("\r ICR1 = "); break; case '2': puts("\r OCR1A = "); break; case '3': puts("\r OCR1B = "); break; } while( GetNumStr(buffer, 9)==0); reg = atoi(buffer); switch (k) { case '1': ICR1 = reg; break; case '2': OCR1A = reg; break; case '3': OCR1B = reg; break; } } } //**************************************************************************** // Lee una cadena de texto de 'len' números // El tamaño de 'buffer' debe ser mayor que 'len'.