Timers Atmega328p
Timers Atmega328p
Timers Atmega328p
Índice
1. Timers 2
3. Interrupción 3
4. Timers atmega328p 4
1
1. Timers
2
pinMode(pin,mode): pin es el número de puerto, y mode es la congu-
ración del puerto (INPUT, OUTPUT o INPUT_PULLUP). Dene si
el puerto o pin se congura como entrada o salida
digitalWrite(pin,estado): Si el pin esta congurado como salida, enton-
ces estado es el valor que adquiere ese puerto a la salida.
3. Interrupción
3
1. Deshabilitar todas las interrupciones.
2. Conguración del periférico y la interrupción.
3. Volver a habilitar todas las interrupciones.
Cabe destacar que estos tres puntos pueden utilizarse en cualquier parte
del código, ya que depende de la aplicación, y en que casos se utiliza. Por
ejemplo, si se sabe que el microcontrolador siempre lee el puerto 4, entonces
conviene congurarse la ISR en el inicio del programa. Si por ejemplo el
puerto 4 se lee una sola vez y la lecutura se hara una sola vez, entonces
puede realizarse la una sola vez, y luego deshabilitarla.
Las interrupciones en general estan asociados a periféricos del microcon-
trolador, y en general son de dos tipos: hardware y software. Las de software
se asocian a eventos como timers, pwm, watchdog, etc. Las de hardware se
asocian en general a buses de comunicación como UART, I2C,SPI, etc y
se comunican con periféricos. Otra ISR común es la de detectar ancos de
subida o bajada en los puertos del microcontrolador.
En el caso del microcontrolador utilizado, microchip (fabricante) provee
una macro denominada ISR, y para ejecutar la rutina de interrupción debe
escribirse el nombre del periférico asociado a la interrupción. La forma de
realizarlo es:
ISR(NOMBREINTERRUPCIÓN{
//AQUI VA LA RUTINA DE INTERRUPCION
}
4. Timers atmega328p
4
FAST PWM MODE: Modo de PWM, genera una salida pwm con fre-
cuencia y ancho de pulso prejado.
Phase Correct PWM: idéntico al anterior, pero la frecuencia se realiza
al doble del modo anterior.
El modo normal será tratado en este texto. Los modos PWM son utili-
zados para generar una señal PWM en el microcontrolador, donde se puede
congurar la frecuencia del PWM y el ancho de pulso. El modo CTC se verá
en proximas clases.
EL modo normal tiene el siguiente funcionamiento:
1. Ingreso de la frecuencia de clk del sistema (punto 1 en 2).
2. Esta frecuencia se divide por 2(punto 2 en gura 2).
3. Luego, esta se pasa por un preescaler, que elije la frecuencia el progra-
mador (registro TCCR2B, punto 3 en gura 2).
4. Una vez elejida esta frecuencia, empieza a contar un contador de 8
bits. Una vez que este contador desborda (llego a 255) genera una
interrupción (registro TCCR2B, punto 4 en gura 2).
5. Este contador queda siempre contando a la frecuencia seleccionada y
genera una interrupción cada determinada cantidad de tiempo.
5
void setup(){
...
SREG = (SREG & 0b01111111); ///deshabilitar interrupciones
TCNT2 = 0 ;
TIMSK2 =TIMSK2|0b00000001;
TCCR2B = 0b00000011 ; //FCLK = 16M/32 = 500k -> sistyck = (1/500k)*255
SREG = (SREG & 0b01111111) | 0b10000000; //Habilitar interrupci
...
}
El registro SREG, es un registro global que habilita/deshabilita interrup-
ciones. La operacion SREG = (SREG & 0b01111111) se encarga de deshabi-
litar todas las interrupciones, mientras que SREG = (SREG & 0b01111111)
| 0b10000000 las vuelve a habilitar. Todo el código que esta entre estas dos
sentencias son para generar la conguración del timer2 en modo normal. Las
sentencias que hay en el medio realizan las siguientes acciones:
1. TCNT2 =0: pone el contador a cero.
2. TIMSK2 =TIMSK2|0b00000001:habilita la interrupción por desborda-
miento del contador.
3. TCCR2B = 0b00000011: congura el timer a la frecuencia que desea
el programador. Para conocer que frecuencias tiene disponible el pro-
gramador debe remitirse a la hoja de datos del microcontrolador. Los
valores para congurar el preescaler son los últimos tres bits.
Una vez congurado el timer2, en modo normal, se realiza la rutina de
interrupción, que tiene el siguiente formato de código
ISR(TIMER2_OVF_vect)
{
sistyck_timer_2++ ;
}
En este caso, lo único que realiza es el incremento de una variable. Esta
variable se toma desde la función loop principal, y al incrementarse una
determinada cantidad de veces, se sabe con exactitud cada cuanto tiempo se
ejecuta determinada rutina.
6
volatile uint16_t sistyck_timer_2 = 0 ;
uint8_t state_led = LOW ;
void setup() {
Serial.begin(9600) ;
pinMode(13,OUTPUT) ;
digitalWrite(13,state_led) ;
Serial.print("config timer") ;
//// configuración del timer en modo normal
SREG = (SREG & 0b01111111); ///deshabilitar interrupciones
TCNT2 = 0 ;
TIMSK2 =TIMSK2|0b00000001;
TCCR2B = 0b00000011 ; //FCLK = 16M/32 = 500k -> sistyck = (1/500k)*255
SREG = (SREG & 0b01111111) | 0b10000000; //Habilitar interrupciones
void loop() {
if (sistyck_timer_2 >= 2000){
Serial.println("han pasado x seg") ;
sistyck_timer_2 = 0 ;
state_led = state_led == LOW?HIGH:LOW ;
digitalWrite(13,state_led) ;
ISR(TIMER2_OVF_vect)
{
sistyck_timer_2++ ;
}
7
32. Resumiendo los pasos(remitase a la gura 2):
8
Se va a utilizar el caso de N=8 (segunda la de 3), pero esto no implica
que sea la mejor. Sino será un ejemplo de cálculo para que vea como es el
desarrollo.
Si se elije N=8, la frecuencia de entrada es 16 Mhz, entonces, la salida
del segundo preescaler es 1 MHz. Para verlo puede remitirse a la gura 2. Si
fclk = 16M hz (punto 1 gura 2), la salida es fclk /2 = 8M hz (punto 2 gura
2), al elejir el preescaler de 8, la salida es de fclk /8 = 1M hz (punto 3 gura
2). Luego la interrupción se va a ejecutar cada Tisr = 1µs × 255 = 0,25ms.
Por lo expuesto, la forma de congurar el timer2 dentro de la funcion setup
es:
void setup(){
SREG = (SREG & 0b01111111); ///deshabilitar interrupciones
TCNT2 = 0 ;
TIMSK2 =TIMSK2|0b00000001;
TCCR2B = 0b00000010 ; // <- ver que los ultimos 3 bits coinciden
SREG = (SREG & 0b01111111) | 0b10000000; //Habilitar interrupcion
}
Ahora, dado que una interrupción de timer se ejecuta cada 0.25ms, de-
bemos saber cuantas interrupciones se deben contar para tener 10 ms. Esto
se realiza realizando una regla de tres simple: si una interrupción se ejecuta
cada 0.25 ms, entonces en 10 ms se tiene que: 10ms/0.25ms = 40 isr. Por lo
tanto, dentro de la interrupción hay una variable que se incrementa y cuan-
do llega a 40, se debe ejecutar la acción. El código dentro de la función loop
queda:
uint16_t timer_inc = 0 ; //VARIABLE 16 BITS SIN SIGNO
void loop(){
if (timer_inc >=40){
// codigo que se ejecuta cada 10 ms
timer_inc = 0 ; // <- EVITA EL DESBORDE DE LA VARIABLE
}
}
ISR(TIMER2_OVF_vect)
{
timer_inc++ ; // EQUIVALE A timer_inc = timer_inc + 1 ;
}
Si por ejemplo quisieramos mas de una función para ejecutarse, por ejem-
plo una por segundo, y otra a los tres segundos, se debe repetir un proceso
9
similar. Siguiendo con el caso, si se quiere una función cada segundo, se debe
realizar 1000ms/0.25ms = 4000, y la otra a los tres segundos 3000ms/0.25ms
= 12000. Entonces se deben agregar dos variables mas, una que llegue a 4000,
y otra a 12000. El código queda como sigue dentro de la función loop.
uint16_t timer_inc = 0 ; //VARIABLE 16 BITS SIN SIGNO para tarea de 10 ms
uint16_t timer_inc1 = 0 ; //VARIABLE 16 BITS SIN SIGNO para tarea de 1 se
uint16_t timer_inc2 = 0 ; //VARIABLE 16 BITS SIN SIGNO para tarea de 3 se
void loop(){
if (timer_inc >=40){
// codigo que se ejecuta cada 10 ms
timer_inc = 0 ; // <- EVITA EL DESBORDE DE LA VARIABLE
}
if (timer_inc1 >=4000){
// codigo que se ejecuta cada 1 segundo
timer_inc1 = 0 ; // <- EVITA EL DESBORDE DE LA VARIABLE
}
if (timer_inc >=12000){
// codigo que se ejecuta cada 3 segundos
timer_inc2 = 0 ; // <- EVITA EL DESBORDE DE LA VARIABLE
}
}
ISR(TIMER2_OVF_vect)
{
timer_inc++ ; // EQUIVALE A timer_inc = timer_inc + 1 ;
timer_inc1++ ;
timer_inc2++ ;
}
El código nalmente ordenado queda:
void setup(){
....
SREG = (SREG & 0b01111111); ///deshabilitar interrupciones
TCNT2 = 0 ;
TIMSK2 =TIMSK2|0b00000001;
TCCR2B = 0b00000010 ; // <- ver que los ultimos 3 bits se sacan de la tabla
SREG = (SREG & 0b01111111) | 0b10000000; //Habilitar interrupciones
...
10
}
void loop(){
if (timer_inc >=40){
// codigo que se ejecuta cada 10 ms
timer_inc = 0 ; // <- EVITA EL DESBORDE DE LA VARIABLE
}
if (timer_inc1 >=4000){
// codigo que se ejecuta cada 1 segundo
timer_inc1 = 0 ; // <- EVITA EL DESBORDE DE LA VARIABLE
}
if (timer_inc >=12000){
// codigo que se ejecuta cada 3 segundos
timer_inc2 = 0 ; // <- EVITA EL DESBORDE DE LA VARIABLE
}
}
ISR(TIMER2_OVF_vect)
{
timer_inc++ ; // EQUIVALE A timer_inc = timer_inc + 1 ;
timer_inc1++ ;
timer_inc2++ ;
}
11