4 2

4.2 Temporizador/Contador Los temporizadores/contadores son probablemente los periféricos complejos de mayor uso en los

Views 41 Downloads 1 File size 560KB

Report DMCA / Copyright

DOWNLOAD FILE

Recommend stories

Citation preview

4.2 Temporizador/Contador Los temporizadores/contadores son probablemente los periféricos complejos de mayor uso en los microcontroladores. Los temporizadores/contadores son altamente versátiles, se pueden emplear para medir periodos de tiempo, para determinar el ancho de un pulso, para medir una velocidad, para medir frecuencia, o para proporcionar señales de salida. Ejemplos de aplicaciones pueden incluir la medición de las revoluciones por minuto de un motor de automóvil, el periodo exacto de un tiempo específico, tal como el tiempo de trayectoria de una bala, producir tonos para crear música o para producir la chispa de una bujía de un sistema de ignición, o proveer el ancho especifico de un pulso u obtener frecuencias específicas para el control de la velocidad de un motor. Un temporizador o timer, es un reloj especializado, que regularmente se ocupa para controlar la secuencia de un evento o proceso. Un contador es en términos generales, un dispositivo que almacena el número de veces que un evento se lleva a cabo.



Una labor habitual de los controladores suele ser la determinación de intervalos de tiempo concretos.



Esto se hace a través de un elemento denominado Temporizador (Timer).



Un temporizador básicamente es un registro de n-bits que se incrementa de manera automática en cada ciclo de instrucción o cuando ocurre un evento externo.



Si la fuente de temporización es externa, se le conoce como contador de eventos.



El registro puede ser pre-cargado para iniciar su conteo a partir de un valor determinado.



Cuando ocurre un desbordamiento en el registro (una transición de 1’s a 0’s) se genera alguna señalización.



En el caso de los microcontroladores AVR, pueden generarse

4.2.1 Configuración y programación como temporizador. La operación del Timer0 se controla a través del registro OPTION_REG . En el modo temporizador (T0CS=0), se produce un incremento del registro TMR0 cada ciclo de instrucción (prescaler asignado al perro guardián WDT). Si se escribe en el registro TMR0, no se produce el incremento durante los dos siguientes ciclos de instrucción; este hecho debe tenerse muy en cuenta por parte del usuario y, de ser necesario, ajustar el valor escrito en TMR0.

El TMR0 tiene las siguientes características: 

se puede leer y escribir



puede trabajar con reloj externo o interno



detecta el flanco de subida o bajada del reloj externo



tiene un prescaler de 8 bit´s



tiene una interrupción por desborde de la cuenta de 255 a 0

los registros asociados a el son el TMR0, INTCON y el OPTION_REG El bit T0CS (OPTION 2) a 1 trabaja como temporizador, 0 trabaja como contador El bit T0SE (OPTION 4) en modo contador a 1 flanco descendente, 0 flanco ascendente El bit PSA (OPTION 3) 0 prescaler al temporizador, 1 prescaler al perro guardian WDT Cuando se desborda el TMR0, se activa el bit 2 de INTCON (T0IF) y si el T0IE esta activado el T0IF generara interrupción y se debe borrar por software cuando se atienda la interrupción

Para calcular el tiempo de temporización

Configurators el OPTION_REG BANKSEL OPTION_REG MOVLW B'00000000' ANALICEMOS 

BIT 7 Pull up PORTB Disable



BTI 6 interrupcion por flaco de subida en el INT Pin



BIT 5 fuente del clock para el TMR0, internal 1 ciclo ed intruccion (1/Fosc)



BIT 4 Flanco de bajada para el incremento del TMR0



BIT 3 Prescaler asignado al TMR0



BIT 2-0 Division del prescaler

Configuramos el INTCON

Registro donde activamos las interrupciones globales y la del TMR0 MOVLW B'10100000' MOVWF OPTION_REG 

BIT 7 Activamos las interrupciones Globales



BIT 6 interrupciones de otros perifericos desactivadas



BIT 5 Habilitamos la interrupcion por desborde en el TMR0



BIT 4 desabilitamos interrupcion externa INT



BIT 3 desabilitamos interrupcion por cambio en el PORTB



BIT 2 bandera interrupcion en TMR0



BIT 1 bandera interrupcion en INT



BIT 0 bandera interrupcion por cambio en PORTB

4.2.2 Configuración y programación como contador. En el modo contador (T0CS=1), se produce un incremento por cada transición ascendente (T0SE=0) o descendente (T0SE=1) en el pin RA4 del Timer0. Para manejar la TMR0, el PIC utiliza 2 registros, estos son: • TMR0 (01h): Este es el contador propiamente dicho y puede ser leído y escrito en cualquier momento. • OPTION (81h): De este registro, utilizaremos los siguientes bits:



T0CS: Indica cual es el origen de los pulsos de clk para el timer; si es 1



el origen es la señal introducida por el pin RA4/T0CKI, si es 0 el origen



será el ciclo de instrucción (CLKOUT = FrecuenciaXtal/4).



T0SE: Indica el flanco por el que se incrementará la cuenta del contador



TMR0. Si vale 0 se hará por el flanco de bajada, si es 1 se hará por el



flanco de subida.



PSA: El chip tienen internamente un divisor, este se puede asignar



mediante este bit al WDT con un 1 o al TMR0 con un 0.



PS2:PS0: Este es el valor de la preescala del divisor, según la siguiente



tabla de valores.

El TMR0 puede funcionar de dos maneras; como contador de eventos o como temporizador. Para seleccionar de que manera queremos que funciones utilizamos el bit T0CS del registro OPTION_REG. Cuando seleccionamos TMR0 como contador de eventos, mediante el pin T0SE indicamos porque flanco, de subida o bajada, en la señal aplicada en el pin RA4/T0CKI, queremos que se incremente el contador. La señal que lleva al TMR0 la podemos preescalar mediante un divisor, este divisor lo podemos asignar tanto al WDT como al TMR0 mediante el bit PSA del

OPTION_REG. El valor del rango del divisor viene determinado por los bit PS2:PS0 según la tabla anterior. El TMR0 es capaz de realizar una interrupción cada vez que pasa de 255 a 0, para permitirla hay que poner a 1 el bit T0IE del registro INTCON y el flag de la interrupción es el T0IF del registro INTCON.

El valor de TMR0 se inicializa con ValIni (250), con lo que cada 6 pulsaciones en la tecla conectada al pin RA4/T0CKI, el TMR0 pasara de 255 a 0, generando una interrupción que incrementara el valor del contador formado por los leds conectados al PORTA.

Lo único a destacar es la programación del OPTION_REG para configurar el TMRO. Veamos dos ejemplos: •

OPTION_REG = b’11111000 : Con este valor hacemos T0CS=1 ( pone el timer en modo contador de eventos), T0SE=1 ( el contador se incrementará con el flanco de bajada de la señal el RA4/T0CKI), PSA=1 ( el divisor esta asignado al WDT por lo que el ratio de la señal será 1:1). Con esta configuración se necesitarán 6 pulsos para incrementar el valor de los leds.



OPTION_REG = b’11110000 : Con este valor hacemos T0CS=1 ( pone el timer en modo contador de eventos), T0SE=1 ( el contador se incrementará con el flanco de bajada de la señal el RA4/T0CKI), PSA=0 ( el divisor esta asignado al TMR0 por lo que el ratio de la señal vendrá determinado por el valor de PS2:PS0, como es 000 el rango será 1:2). Con esta configuración se necesitarán 12 pulsos para incrementar el valor de los leds. Puedes probar diferentes valores de OPTION_REG, para estudiar más en profundidad el funcionamiento del TMR0.

Si utilizamos el TMR0, junto con el divisor, como temporizador podremos medir periodos de tiempo muy precisos, hacer periodos de espera o generar señales de tipo PWM.

4.2.3 Desarrollo de aplicaciones Timer0_1.c: Cada vez que se actúe sobre el pulsador conectado en el pin RA4 se incrementa un contador que se visualiza en el LCD.

Cuando este módulo trabaja como temporizador cuenta los ciclos de instrucción (sin prescaler) o los ciclos que recibe del prescaler. Como es un contador ascendente el TMR0 debe ser cargado con el valor de los ciclos que se desean contar restados de 256 que es el valor de desbordamiento. Por ejemplo, para contar 28 ciclos (de instrucción/prescaler), se carga el TMR0 con 228 (256-28). El ciclo de instrucción tiene una duración de 4us para una frecuencia de oscilador de 1MHz (PIC16F88). Sin prescaler mediría un tiempo de 28x4x1us = 112us. Con un prescaler 1:8, el tiempo medido sería 28x4x8us = 896us. De manera general, el intervalo de temporización T se puede calcular con la siguiente fórmula: T=N x TCI x n

Donde: N = número de ciclos de instrucción/prescaler TCI = período del ciclo de instrucción n = valor del prescaler

Mientras que el valor de carga Q del TMR0 se calcula así: Q=256-N Para medir 500us, con un prescaler 1:1 (prescaler asignado al perro guardián WDT) y un TCI = 4us se necesitan 500/4 = 125 ciclos de instrucción. El valor inicial del Ejemplo 2 TMR0 debe ser 131 (256-125). T0CON: el más importante de cara a la configuración del timer: T0CON.TMR0ON (bit 7) T0CON.T08bit T0CON.T0CS T0CON.T0SE

(bit 6) (bit 5)

-> arranca(1) o para (0) el TIMER -> selecciona modo 8 bits (1) o 16 bits (0) -> selecciona modo TIMER (0) o contador externo (1)

(bit 4)

-> en caso de contador externo decide si cuenta en

flanco subida (0) o bajada (1). T0CON.PSA

(bit 3)

-> uso (0) o no (1) de un divisor (prescaler) previo.

T0CON.PS0-2

(bits 0-2) -> bits que definen el valor del divisor previo, desde 1:2

(000) hasta 1:256 (111) TMR0L y TMR0H: permiten acceder (lectura/escritura) al valor del contador (TMR0L para el byte menos significativo y TMR0H para el más significativo).

INTCON: bits para activar la interrupción asociada al Timer0 (ver la entrada sobre interrupciones).

Las decisiones importantes de cara a la configuración del timer son:

·

Uso de contador de 8 o 16 bits. Si se escoge 8 bits solo se usa TMR0L como

contador y obviamente se reseteará cada 256 incrementos. Esto es importante también de cara a las posibles interrupciones asociadas, ya que la interrupción del TIMER0 se produce al pasar por 0 el contador. ·

Si queremos hacer más lento el contador podemos usar el prescaler, que no

es más que un divisor previo que hace que sólo se cuenten 1 de cada N ciclos. N puede ser 1 (sin prescaler), 2, 4, 8, etc. En ese caso hay que poner a 0 el bit PSA (usar PRESCALER) y poner el valor correspondiente en los bits asociados. La fórmula es simplemente que divisor N deseado será = 2^(bits_PS+1). Una vez en marcha el timer podemos consultar su valor accediendo a los registros TMR0L y TMR0H. El byte alto TMR0H no es el verdadero byte alto del contador sino un buffer de dicho valor. La razón de esto es asegurar la consistencia al hacer lecturas/escrituras de un contador de 16 bits en un procesador de 8 bits. Siempre habría la posibilidad de que entre la lectura de TMR0L y la de TRMR0H el contador se actualizase invalidando la lectura. La solución es hacer que una lectura de TMR0L cause una copia simultánea del byte alto del contador a TMR0H. De la misma forma una escritura de TMR0L causa la escritura simultánea de TMR0H al byte alto del contador.

¿Consecuencias para nosotros? Si queremos leer el valor del

timer (16 bits)

debemos siempre leer primero TMR0L y luego TMR0H. En la escritura es lo

contrario, primero escribimos TMR0H (lo que no tiene ningún efecto sobre el contador, ya que TMR0H es un buffer) y luego TMR0L (momento en el que TMR0L y lo que tuviéramos en TMR0H se vuelcan al contador. Podríamos definir una macro o función para no tener que estar pendientes. Por ejemplo, para escribir el valor de TMR0 (16 bits) haríamos:

Como vemos primero fijamos TMR0H (con la parte alta de x) y luego TMR0L. Igualmente podríamos definir algunas macros para arrancar/parar el timer sin tener que acordarnos de los bits involucrados como hicimos con las interrupciones:

En el main(), configuraremos el timer en modo 16 bits con un prescaler de 256, lo que supone que se incrementa cada 256 x 0.2 usec (@20 Mhz), esto es, cada 51.2 usec más o menos. Con la macro set_TMR0 inicializamos el contador y con start_TMR0 arrancamos el reloj. Durante el bucle (cada 10,000 ciclos = 2 msec) mostramos el contenido de TMR0L y TMR0H en PORTB y PORTC respectivamente.

Al ejecutarse PORTB se ve estático, ya que cambia demasiado rápido (50 usec) para poder ser apreciado. En cambio en PORTC (el byte alto del timer) si se aprecia el incremento. PORTC se incrementa cada 256 x 51.2 usec, es decir, unos 13 msec. Cada vez que se actualiza los datos (2 msec) PORTC habrá cambiado en unas 6/7 unidades.

Trás un rato (65536 x 2 msec = 128 sec) las lucecitas se detienen, ya que el contador cont da la vuelta completa y la condición establecida (cont==0) detiene el timer.

Como otros compiladores, C18 tiene algunas rutinas para facilitar el manejo de los timers:

OpenTimer0 -> da valores a T0CON a través de mascaras predefinidas. También pone a 0 el contador y lo pone en marcha.

CloseTimer0 -> desactiva el contador TMR0 y su interrupción asociada.

WriteTimer0 -> equivalente a la macro set_TMR0()

Abajo se lista el mismo programa usando estas rutinas. Algunos comentarios sobre los cambios: Es preciso incluir para tener acceso a las declaraciones de las funciones usadas No es necesario resetear el contador ni arrancarlo explícitamente (lo hace OpenTimer0)

Las máscaras de configuración se combinan con AND. Es posible combinarlas con OR definiendo previamente #define USE_OR_MASKS en el programa. Esto afecta a las opciones que se establecen por defecto. En el modo AND el valor por defecto de los bits es 1 (modo 8 bits, contador de pulsos en un pin, etc.). En modo OR el valor por defecto es 0 (16 bits, contador de reloj).

Obviamente es más cómodo y legible usar OpenTimer que dar valores directamente a T0CON. Sin embargo conocer los detalles siempre es interesante por si lo que queremos hacer no se puede hacer exactamente con las rutinas suministradas. Por ejemplo, closeTimer0() no sólo para el contador sino que desactiva la interrupción asociada, por lo que no es exactamente equivalente a nuestra macro stop_TMR0. Las llamadas a funciones son más costosas que una macro, por lo que en ciertas aplicaciones críticas son preferibles, etc. Como siempre es una cuestión de usar lo más conveniente en cada caso (pero para ello hay que conocer las posibilidades).