clase teclado e interrupcion
TRANSCRIPT
INTERRUPCIONES Y MANEJO DE UN TECLADO Una interrupción es un evento especial, ya que "interrumpe" al microcontrolador de su ejecución en curso, podríamos mencionar como ejemplo el hecho de que el microcontrolador este ocupado ejecutando una secuencia de código para sumar multiplicar y hacer algunos procesos más de unos datos, una interrupción se genera y el microcontrolador es obligado a detener la ejecución de ese proceso que estaba haciendo y desviar su atención por un momento para ejecutar un código (subrutina) que se debe hacer por la presencia de la interrupción, posteriormente cuando se termine de ejecutar la rutina que se hace por la interrupción, el procesador del microcontrolador vuelve a ejecutar el proceso que antes estaba haciendo desde el lugar desde donde fue interrumpido.
Las interrupciones tienen muchas ventajas, ya que se puede de cierta manera obtener una especie de "paralelismo" porque el micro puede ejecutar el código principal, y en momentos especiales por las interrupciones, ejecutar una parte de código como si todo sucediera a la misma vez y volver al programa principal. Pero a la vez se debe tener cuidado en su manejo, pues son un arma de doble filo si no son bien administradas, en caso de tener que usar muchas interrupciones, o al tener mucho código que ejecutar en la rutina de interrupción.
El microcontrolador tiene varias fuentes de interrupción, algunas son: por conversión análoga a digital terminada, por escritura en la eeprom completada, por desborde de temporizadores, por recepción de datos en la uart, por transmisión de datos completada en la uart, interrupciones externas por pines del micro al detectar un flanco ya sea de bajada o subida, entre otras.
En los micros AVR cada interrupción posee su propio vector de interrupción lo que hace más cómoda su ejecución y programación de la rutina correspondiente (ver tabla 11-1 del datasheet, adjunto en la siguiente página de este documento).
Ahora se describen las configuraciones para utilizar la interrupción externa INT0, cabe mencionar que el micro ATMEGA16 posee dos fuentes de interrupción externa, estas son INT0 ya mencionada además de INT1, alojadas en los pines D2 y D3 del micro respectivamente. Usaremos la Interrupción INT0 en el pin D2, los registros involucrados para su configuración son:
• MCUCR – MCU Control Register • GICR – General Interrupt Control Register • GIFR – General Interrupt Flag Register
Se muestra ahora el significado de los bits que intervienen en el manejo de la interrupción INTO (externa por el pin D2).
MCUCR – MCU Control Register
Bit 3, 2 – ISC11, ISC10: Interrupt Sense Control 1 Bit 1 and Bit 0: Se usan para la INT1. Bit 1, 0 – ISC01, ISC00: Interrupt Sense Control 0 Bit 1 and Bit 0: Se usan para la INT0, y responde a la siguiente tabla:
Que nos dice como se generará la interrupción, puede ser con un estado bajo en el pin (00), por cualquier cambio de estado (01), por flanco de bajada (10), o por flanco de subida (11).
GICR – General Interrupt Control Register
Bit 6 – INT0: External Interrupt Request 0 Enable: Mediante este bit puesto en 1 se activa la máscara de Interrupción Externa 0, esto quiere decir que activando el bit de interrupciones globales se podrá esperar por la interrupción configurada.
GIFR – General Interrupt Flag Register
Bit 6 – INTF0: External Interrupt Flag 0: Es la bandera de interrupción que se pone automáticamente en 1 cuando se ha producido la Interrupción Externa 0 y además las interrupciones globales están habilitadas, entonces el procesador saltará al vector de interrupción correspondiente. Se pondrá a 0 por hardware cuando la rutina de interrupción se ejecute. Ahora como ejemplo se muestra una forma de controlar la interrupción externa del micro por el pin D2 (INT0). Mediante 2 displays de 7 segmentos se visualiza un conteo ascendente de 0 a 99, conteo que será incrementado por la INT0. El manejo de los displays se hace por tabla de datos, o datos constantes en la memoria de programa, a la vez que accedo a la memoria RAM para almacenar los datos del conteo.
Código ;--------------------------------------------------------------------------------
; Programa: Contador 0-99 por interrupción
; Version: 0.0
;
; Dispositivo: ATmega16 Compilador: AVRASM2
; Entorno IDE: AVR Studio4.15 Simulador: Proteus 7.5sp3
;
; Notas: Este programa realiza un conteo ascendente de 0 a 99 mediante
; 2 Displays de 7 segmentos, y un pulsador que incrementa la cuenta
;
; Registros: r5 a r14 para la tabla que maneja los displays de 7 segmentos
; r16 para configuraciones, y despues con r15 manipula los datos de
; unidades y decenas de la tabla para el display.
; r30 y r21 para llevar la cuenta de las unidades y decenas.
; r22 es el registro bandera para saber si ocurre la interrupcion externa
; r30 y r31 (Z) para direccionamiento indirecto de las constantes
; en memoria de programa que se almacenan de los datos del display
; de 7 segmentos.
; r28 y r29 (Y), para copia de la dirección original de las constantes.
; r17 y r18 son para el retardo de 1ms.
;
; Conexiones: C0 -> Anodo Display 1
; C1 -> Anodo Display 2
; B0 -> Segmento a del Display
; B1 -> Segmento b del Display
; B3 -> Segmento c del Display
; B4 -> Segmento d del Display
; B5 -> Segmento e del Display
; B6 -> Segmento f del Display
; B7 -> Segmento g del Display
; D2 -> Pulsador
;--------------------------------------------------------------------------------
;DIRECTIVAS EN ENSAMBLADOR
.include "m16def.inc" ;ATmega16
.device ATMEGA16
;Constantes utilizadas
.def flag=r22
;reset-vector address $0000
.org $0000
jmp inicio ;va al inicio
;vector de interrupcion con la direccion de INT0
.org $02
jmp int_ext ;va a la interrupción externa
;Subrutina con instrucciones a realizar con INT0
int_ext:
ser flag ;ponemos flag (r22) a 255
reti ;retorno de rutina de interrupción, habilitando I del SREG
;PROGRAMA PRINCIPAL
inicio:
;Cargar el puntero de pila
ldi r16,high(ramend) ;Configuracion de...
out sph,r16
ldi r16,low(ramend)
out spl,r16 ;...la pila: Stack: $045F=RAMEND
;Confirugracion de los registros a usar
ser r16 ;r16 <- $FF
out ddrc,r16 ;portC todo de salida
out ddrb,r16 ;portB todo de salida
ldi r16,$fb ;r16 <- $fb
out ddrd,r16 ;portD salidas, excepto D2
;configuración de la Interrupción.
ldi r16,3 ;r16 <- 3
out mcucr,r16 ;configuramos nivel bajo para INT1, y flanco de subida para
INT0
ldi r16,$40 ;r16 <- $40
out gicr,r16 ;activamos la INT0 solamente
clr r16 ;r16 <- 0
out gifr,r16 ;limpiarmos flags de interrupcion INTF0 e INTF1 e INTF2
;Cargar los valores de datos para mostrar los datos en el display de ánodo común
;Puerto: B7 B6 B5 B4 B3 B2 B1
B0
;Segmentos: g f e d c X b a
ldi r16, $80 ; 1 0 0 0 0 0 0 0
mov r5,r16 ;Cero en R5
ldi r16, $F1 ; 1 1 1 1 0 0 0 1
mov r6,r16 ;Uno en R6
ldi r16, $48 ; 0 1 0 0 1 0 0 0
mov r7,r16 ;Dos en R7
ldi r16, $60 ; 0 1 1 0 0 0 0 0
mov r8,r16 ;Tres en R8
ldi r16, $31 ; 0 0 1 1 0 0 0 1
mov r9,r16 ;Cuatro en R9
ldi r16, $22 ; 0 0 1 0 0 0 1 0
mov r10,r16 ;Cinco en R10
ldi r16, $02 ; 0 0 0 0 0 0 1 0
mov r11,r16 ;Seis en R11
ldi r16, $B0 ; 1 0 1 1 0 0 0 0
mov r12,r16 ;Siete en R12
ldi r16, $00 ; 0 0 0 0 0 0 0 0
mov r13,r16 ;Ocho en R13
ldi r16, $20 ; 0 0 1 0 0 0 0 0
mov r14,r16 ;Nueve en R14
;inicializacion de registros importantes
clr flag ;bandera para identificar interrupción puesta a 0
ldi r20,$00 ;registro para unidades
ldi r21,$00 ;registro para decenas
ldi r30,$05 ;apuntador Z a R5 que contiene el cero
sei ;se habilitan las interrupciones globales
;ciclo de mostrar en el display
ciclo:
cpi flag,255 ;preguntamos si flag es 255
breq incremento ;si es 255, la interrupción ocurrió, saltamos a incrementar
multi:
call multiplexar ;vamos a visualizar los datos en los displays multiplexados
jmp ciclo ;ciclo infinito
;rutina de incremento hasta 99
incremento:
call multiplexar ;mostramos datos, hasta que se suelte el pulsador
sbic pind,2 ;pin D2 esta todavía siendo pulsado?
jmp incremento ;si, está pulsado, volvemos a mostrar el valor actual del conteo, sino se
salta instrucción
inc r20 ;incrementamos el valor de r20 que contiene el valor de unidades
cpi r20,10 ;r20<10?
brmi salir ;si es menor, salimos, sino...
clr r20 ;r20 <- 0, unidades a cero
inc r21 ;incrementamos r21 que contiene el valor de decenas
cpi r21,10 ;r21<10?
brmi salir ;si es menor, salimos, sino...
clr r21 ;r21 <- 0,decenas a cero
salir:
clr flag ;ponemos flag a 0
jmp multi ;volvemos a multiplexar, con los nuevos datos
multiplexar:
cbi portc,0 ;apagamos ambos
cbi portc,1 ;displays
mov r28,r30 ;copiamos la dirección donde estan
mov r29,r31 ;los datos del display
add r30,r20 ;se suma el valor de unidades, para desplazarse en la tabla
ld r16,Z ;r16 <- (Z)
mov r30,r28 ;r30 <- r28
add r30,r21 ;se suma el valor de decenas, para desplazarse en la tabla
ld r15,Z ;r17 <- (Z)
mov r30,r28 ;r30 <- r28
out portb,r16 ;se muestra en el display de unidades, el valor correspondiente
sbi portc,0 ;enciende el display correspondiente
;call unseg
call delay1m ;tiempo de retardo para que sea visible al ojo
cbi portc,0 ;se apaga el display encendido anteriormente
out portb,r15 ;se muestra en el display de decenas, el valor correspondiente
sbi portc,1 ;enciende el display correspondiente
;call unseg
call delay1m ;tiempo de retardo para que sea visible al ojo
cbi portc,1 ;se apaga el display encendido anteriormente
ret
; =============================
; delay loop generator
; 4000 cycles:
; -----------------------------
; delaying 3999 cycles:
delay1m:
ldi R17, $1F
WGLOOP00: ldi R18, $2A
WGLOOP11: dec R18
brne WGLOOP11
dec R17
brne WGLOOP00
; -----------------------------
; delaying 1 cycle:
nop
ret
; =============================
; =============================
;Subrutina de 1seg
; 4000000 cycles:
; -----------------------------
; delaying 3999996 cycles:
unseg: ldi R17, $24
WGLOOP03: ldi R18, $BC
WGLOOP04: ldi R19, $C4
WGLOOP05: dec R19
brne WGLOOP05
dec R18
brne WGLOOP04
dec R17
brne WGLOOP03
; -----------------------------
; delaying 3 cycles:
ldi R17, $01
WGLOOP06: dec R17
brne WGLOOP06
; -----------------------------
; delaying 1 cycle:
ret
; =============================
Esquema de conexiones:
Programa para uso de teclado de matriz de 4x4
Ahora, un programa que lee una tecla presionada en un teclado 4x4. Una buena manera de ahorrar líneas de entrada al micro es, en lugar de dedicar una línea exclusiva por cada tecla utilizada, emplear un teclado matricial. El teclado matricial se caracteriza por estar cada una de las teclas conectada a dos líneas (una columna y una fila) que la identifican. De este modo el número de teclas que pueden conectarse es el producto de filas por el de columnas.
La técnica de programación requiere tanto de entradas como de salidas. Las filas están conectadas a las patillas de salida y las columnas a las de entrada.
Se comienza el testeo colocando a ‘0’ la primera columna, y a ‘1’ las restantes. Si la tecla pulsada estuviese en la fila ‘1’, ésta colocaría en su línea un ‘0’ lógico. Bastará con hacer un muestreo de las filas, buscando el 0, para saber la tecla exacta que fue pulsada en la matriz de teclas.
Si no es pulsada ninguna tecla en una fila, las entradas se encuentran en estado flotante, razón por la que son necesarias las resistencias de polarización internas, que mantienen las entradas a nivel alto.
También pueden usarse resistencias externas, e incluso llevarlas a tierra. En tal caso, lo que se busca es un UNO en las líneas de entrada.
Las instrucciones que son útiles para saber cuando se presiona la tecla son:
sbic pinb,5
Brinca la siguiente instrucción si el bit 5 del puerto B está en CERO.
Si fuera sbis, brinca la siguiente instrucción si el bit 5 del puerto B está en UNO.
El programa es:
;----------------------------------------
;
; Autor: MigSantiago
; Adaptación ATMEGA16 por Lewin Lopez
;
; Notas: Este programa realiza un barrido por filas
; en el puerto B donde se encuentra conectado un teclado
; matricial 4x4. En el puerto D se muestra el valor de la
; tecla de acuerdo a la tabla siguiente:
;
; pb4 pb5 pb6 pb7
; | | | |
; pb0 -> 0 1 2 3
; pb1 -> 4 5 6 7
; pb2 -> 8 9 a b
; pb3 -> c d e f
;
; Registros: r16 -> registro temporal para pasar datos.
; r17 -> registro de mostrar tecla presionada
; r18 -> registro para contar el barrido
; r19 -> registro para contar la fila actual
;
; Conexiones: Puerto B -> Teclado
; pb0, pb1, pb2, pb3 Salidas matriz
; pb4, pb5, pb6, pb7 Entradas matriz
; Puerto D -> Salida Hexadecimal de tecla presionada
; pd4 -> salida estado tecla presionada (E)
;----------------------------------------
.include "m16def.inc"
.device ATMEGA16
.org 0000
jmp inicio
inicio:
;Cargar el puntero de pila
LDI R16, HIGH(RAMEND)
OUT SPH, R16
LDI R16, LOW(RAMEND)
OUT SPL, R16
;Configuracion de puertos
SER R16 ;FF
OUT DDRD,R16 ;PtoD Salida
LDI R16,$0F ;00001111
OUT DDRB,R16 ;PtoB 4sal 4ent
;Inicializacion de puertos y activacion de pull-up
CLR R16
OUT PORTD,R16 ;borra PtoD
OUT PORTB,r16 ;borra PtoB y desactiva pull-up internas
reinicio:
;Programa Principal
clr r16
clr r17 ;Registro mandado a ptoD
clr r18 ;registro del contador de barrido
clr r19 ;indicador de línea actual de barrido
linea1:
ldi r16,$01
out portb,r16 ;rb0=1 línea 1 activa
rjmp barrido ;va a checar presión de tecla
linea2:
inc r19
inc r18 ;en subrutina barrido, la primer línea
;no hace incremento, por eso se
;incrementa aquí
ldi r16,$02
out portb,r16 ;rb1=1 línea 2 activa
rjmp barrido
linea3:
inc r19 ;incrementa indicador de línea
inc r18
ldi r16,$04
out portb,r16 ;rb2=1 línea 3 activa
rjmp barrido
linea4:
inc r19 ;incrementa indicador de línea
inc r18
ldi r16,$08
out portb,r16 ;rb3=1 línea 4 activa
rjmp barrido
nopresion:
clr r16 ;pon E y LEDS a cero
out portd,r16 ;no hubo presión
rjmp reinicio
barrido:
sbic pinb,4 ;si rb4=0 no hay presión
rjmp presion
inc r18 ;siguiente tecla
sbic pinb,5
rjmp presion
inc r18 ;siguiente tecla
sbic pinb,6
rjmp presion
inc r18 ;siguiente tecla
sbic pinb,7
rjmp presion
rjmp quelinea ;no hubo presión en X línea
presion:
mov r17,r18 ;r17=r18
out portd,r17 ;saca tecla y E por portd
sbi portd,4 ;pd4=1 es decir E=1
rjmp reinicio ;que ya no haga barrido
quelinea:
cpi r19,$00 ;determina en que línea de barrido va
breq linea2 ;va a línea 2
cpi r19,$01
breq linea3
cpi r19,$02
breq linea4
cpi r19,$03
breq nopresion ;se va a limpiar ptod
;-----------------------------
Al final lo que hace el programa es determinar si se presionó una tecla. Si se presionó, enciende la salida E (pd4) y saca la tecla por pd0, pd1, pd2, pd3. Si se presionara el CERO sólo encendería E y pd0, pd1, pd2, pd3 estarían apagadas. Si se presionara la tecla F encendería E y todas las demás: 1111 = 15 = F. El esquema de conexiones es:
Declaración de variables
No uso la declaración de variables para los registros del AVR, pero les explico unas directivas para usarlas:
Si quieres definir pines:
.equ RxD =0; En este caso el pin 0 se llama RxD
.equ TxD =1; En este caso el pin 1 se llama TxD
Si quieres renombrar registros:
.def contbit =R16
.def temporal =R17
Y se usan normalmente:
cbi PORTD,TxD; el pin 1 se pone a cero
ldi contbit,$aa; Carga el R16 con AA
EJERCICIO EN CLASE 9.
a) Realizar un programa que permita identificar la tecla presionada en el teclado, pero muestre su salida por el puerto C, conecte el teclado al puerto D y utilice resistencias internas de pull-up.
b) Realizar un programa que utilice la interrupción externa INT1 para detectar un pulsador que cuando se presiona incrementa la letra que se visualiza en un LCD. El programa debe funcionar hasta mostrar la letra ‘z’.