TODOPIC

Microcontroladores PIC => Almacén del Assembler => Mensaje iniciado por: n-utz en 11 de Octubre de 2016, 22:36:20

Título: TMR0 como contador por pulsos (P16F628A)
Publicado por: n-utz en 11 de Octubre de 2016, 22:36:20
Buenas gente, soy nuevo en el foro, y vengo con una duda, un poco fácil para ustedes. Verán estoy haciendo un trabajo para la facultad, sencillo, somos nuevos en assembler, y me piden por un DISPLAY de 7 segmentos contar automaticamente, en hexadecimal, hasta 15 (F), y despues decrementar también automáticamente, el problema surge que me piden entre cada cambio de display un retardo de 1 segundo hecho únicamente con el TMR0, y a la vez pausar y reanudar el programa mediante un pulsador en RB0.
Nunca hice retardos por el TMR0, solo los hacía mediante loops de contadores contando los ciclos de maquina etc...

Verán, lo que hice fue el programa beta que cuento hasta 15 y decremento, automaticamente. Me faltaría añadirle el retardo de 1 segundo entre cada cambio de display por el TMR0, y el pausar y reanudar por RB0. Este último, me faltaría saber el loop para continuar y reanudar, lo intente hacer btfss y btfsc pero no le encuentro la vuelta. Hasta ahora, si presiono RB0 se para el programa pero no lo puedo reanudar, les paso el .asm.

Código: [Seleccionar]
#include <p16f628a.inc>

Contador equ 0x20
AuxStatus equ 0x21
AuxW equ 0x22
Contador3 equ 0x24

org 0
goto Configuracion
org 0x04
Interrupcion:
btfsc PORTB,0
goto $
bcf INTCON,GIE ;INHABILITO INTERRUPCIONES GLOBALES
bcf INTCON,INTF ;BAJO EL FLAG DE INTERRUPCION DE RB0
bsf INTCON,GIE ;HABILITO INTERRUPCIONES GLOBALES
movwf AuxW ;GUARDO EL CONTENIDO DE W
movfw STATUS
movwf AuxStatus ;GUARDO EL CONTENIDO DEL STATUS
movfw AuxStatus
movwf STATUS
movfw AuxW
retfie

Configuracion:
bsf INTCON,GIE ;HABILITO INTERRUPCIONES GLOBALES
bsf INTCON,INTE ;HABILITO INTERRUPCIONES POR RB0
bsf STATUS,RP0 ;CAMBIO A BANCO 1
clrf TRISB ;SETEA TODOS LOS BITS DEL TRISB EN 0
bsf TRISB,0 ;RB0 COMO ENTRADA
bcf STATUS,RP0 ;CAMBIO A BANCO 0
movlw B'10000000' ;EL DISPLAY INICIA EN 0
movwf PORTB
clrf Contador ;INICIALIZO EL CONTADOR EN 0

Inicio:
movlw d'15
movwf Contador3
incf Contador,1 ;SI FUE PULSADO, SE INCREMENTA EL CONTADOR EN 1
movfw Contador ;SE PASA EL VALOR DE CONTADOR A W
call Display
movwf PORTB
decfsz Contador3,f
goto $-.6 ;HASTA CALL PRESION
movlw d'15
movwf Contador3
decf Contador,1
movfw Contador
call Display
movwf PORTB
decfsz Contador3,f
goto $-.6 ;HASTA CALL PRESION
goto Inicio


Display:
addwf PCL,1
retlw B'10000000' ;NUMERO 0
retlw B'11110010' ;NUMERO 1
retlw B'01001000' ;NUMERO 2
retlw B'01100000' ;NUMERO 3
retlw B'00110010' ;NUMERO 4
retlw B'00100100' ;NUMERO 5
retlw B'00000100' ;NUMERO 6
retlw B'11110000' ;NUMERO 7
retlw B'00000000' ;NUMERO 8
retlw B'00100000' ;NUMERO 9
retlw B'00010000' ;LETRA A
retlw B'00000110' ;LETRA B
retlw B'10001100' ;LETRA C
retlw B'01000010' ;LETRA D
retlw B'00001100' ;LETRA E
retlw B'00011100' ;LETRA F
clrf Contador ;REINICIA EL CONTADOR
retlw B'10000000' ;NUMERO 0
return

end

Cualquier ayuda es bienvenida, muchas gracias. Recuerden que soy muy nuevo en assembler, cualquier critica es bienvenida.
Título: Re:TMR0 como contador por pulsos (P16F628A)
Publicado por: KILLERJC en 11 de Octubre de 2016, 23:40:29
Varias formas de hacerlo, cada una tiene su gusto.

Mi propuesta es:

Código: ASM
  1. #include        <p16f628a.inc>
  2.  
  3. Contador        equ     0x20
  4. AuxStatus       equ     0x21
  5. AuxW            equ     0x22
  6. Contador3       equ     0x24
  7. NumInter        equ     0x25
  8. Banderas        equ     0x26
  9.  
  10. ;Bits de bandera
  11. Direccion       equ     0x1
  12. Habilitado      equ     0x2
  13. Otromas         equ     0x4
  14.  
  15.         org             0x00
  16.         goto    Configuracion
  17.         org             0x04
  18.  
  19. Interrupcion:
  20.         ; Aca poner el codigo de guardado de contexto ( W, STATUS), Buscar codigo en datasheet, buscar por W_TEMP
  21.  
  22.         MOVLW   0x00                    ;Cargo el timer de nuevo
  23.         MOVWF   TMR0
  24.         BCF     INTCON, TMR0IF          ;Borro flag del timer
  25.         BTFSS   Banderas, Habilitado    ; Habilitado el contador? Sino salgo
  26.         GOTO    Salir_Int
  27.         INCF    NumInter, F             ;Incremento la cantidad de interrupciones, para saber cuando cambiar el valor del display
  28.         MOVF    NumInter, W             ; Comparo con el valor correcto, si es igual muestro el valor.
  29.         XORLW   0x00                    ; ACA PONER LA CANTIDAD DE INTERRUPCIONES
  30.         BTFSS   STATUS, Z
  31.         GOTO    Salir_Int               ; En caso de que todavia no paso el segundo salgo.
  32.         CLRF    NumInter                ; Pongo a 0 para el proximo segundo.
  33.         MOVF    Contador, W             ; Y muestro el numero
  34.         ANDLW   0x0F                    ; Proteccion, limito cualquier cosa a un maximo de 0xF
  35.         CALL    Display
  36.         MOVWF   PORTB
  37.         BTFSS   Banderas, Direccion     ; Ascendente = 1, descendente = 0
  38.         GOTO    Cuenta_Descendente
  39.  
  40. Cuenta_Ascendente:
  41.         INCF    Contador, F             ;Incremento, reviso si se paso de 15, si es asi entonces cambio de direccion
  42.         MOVF    Contador, W
  43.         XORLW   0x10
  44.         BTFSS   STATUS, Z
  45.         GOTO    Salir_Int
  46.         BCF     Banderas, Direccion     ; Al pasarse de 0xF quedaria con 0x10, el mismo DECF que sigue lo deja en 0xF
  47. Cuenta_Descendente:
  48.         DECF    Contador, F             ; Decremento, cuando pasa de 0x00 a 0xFF lo detecto y lo pongo a 0x00 de vuelta, tambien cambio direccion
  49.         MOVF    Contador, W
  50.         XORLW   0xFF
  51.         BTFSS   STATUS, Z
  52.         GOTO    Salir_Int
  53.         CLRF    Contador
  54.         BSF     Banderas, Direccion
  55.  
  56. Salir_Int:
  57.         ;Aca poner el codigo de  restauracion de contexto ( W y STATUS )
  58.         retfie 
  59.  
  60. Configuracion:
  61.  
  62.         ;Banco 1
  63.         BANKSEL TRISB
  64.         MOVLW   0x01
  65.         MOVWF   TRISB
  66.  
  67.         ;Banco 0
  68.         BANKSEL PORTB
  69.        
  70.  
  71.         ;Configurar el Timer aca y activar el TMR0IE de INTCON
  72.  
  73.  
  74.         ; Preparacion de todos los registros antes de comenzar
  75.         CLRF    Contador                ; INICIALIZO EL CONTADOR EN 0
  76.         BSF     Banderas, Direccion     ; Direccion Ascendente
  77.         BCF     Banderas, Habilitado    ; No habilitado
  78.  
  79.  
  80.         ; Como paso final activo las interrupciones
  81.         bsf     INTCON,GIE              ; HABILITO INTERRUPCIONES GLOBALES
  82.  
  83.  
  84.  
  85. Inicio:
  86.         BTFSS   PORTB, 0        ; Presiona
  87.         GOTO    $-1
  88.         CALL    Delay_10ms      ; Un delay por un antirebote
  89.         BTFSC   PORTB, 0        ; Espero que suelte
  90.         GOTO    $-1
  91.         MOVLW   Habilitado      ; Cargo el bit
  92.         XORWF   Banderas, F     ; Invierto ese bit nomas
  93.         GOTO    Inicio
  94.        
  95.  
  96. Display:
  97.         addwf   PCL, F
  98.         retlw   B'10000000'             ;NUMERO 0
  99.         retlw   B'11110010'             ;NUMERO 1
  100.         retlw   B'01001000'             ;NUMERO 2
  101.         retlw   B'01100000'             ;NUMERO 3
  102.         retlw   B'00110010'             ;NUMERO 4
  103.         retlw   B'00100100'             ;NUMERO 5
  104.         retlw   B'00000100'             ;NUMERO 6
  105.         retlw   B'11110000'             ;NUMERO 7
  106.         retlw   B'00000000'             ;NUMERO 8
  107.         retlw   B'00100000'             ;NUMERO 9
  108.         retlw   B'00010000'             ;LETRA A
  109.         retlw   B'00000110'             ;LETRA B
  110.         retlw   B'10001100'             ;LETRA C
  111.         retlw   B'01000010'             ;LETRA D
  112.         retlw   B'00001100'             ;LETRA E
  113.         retlw   B'00011100'             ;LETRA F
  114.  
  115. end

Masomenos las cosas que le faltan son:

- Poner el codigo de entrada y salida de interrupcion, como bien lo dice el comentario, busca en el datasheet del PIC por W_TEMP y lo vas a encontrar. (Imagino que eso es AuxW y AuxStatus)
- Configurar el Timer, seleccionar el preescaler correcto, de donde viene la señal, y cargarle el valor correcto.
- Cambiar el valor de la primer XOR dentro de la interrupcion, esto es porque normalmente si usas el Timer0 con el cristal de 4Mhz, vas a tener que entrar varias veces a la interrupcion ANTES de que pase 1 segundo. La cantidad de veces esta dada ahi, esta explicado en los comentarios.
- Cambiar los valores a cargar dentro de la interrupcion del TMR0.  Suponete que logras una interrupcion de 20ms cargandole 28 al TMR0, entonces cada ves que entras a la interrupcion tenes que cargarle esos 28. Y como dice en el punto anterior 1s / 20ms = 50 veces, por lo tanto la XOR deberia ser con .50
- Crear un codigo de delay de unos 10ms para el antirebote del boton de RB0 (si es que no posee un antirebote por hardware, sino quitar la linea)

Podes observar que con el uso de interrupciones puedo casi que entrar en un loop infinito dentro del main que no vas a tener problemas en tu interrupcion.
Título: Re:TMR0 como contador por pulsos (P16F628A)
Publicado por: n-utz en 12 de Octubre de 2016, 12:46:16
Buenisimo, aunque me quedan un par de dudas... ¿para qué usas los bits de banderas, y por qué en esas posiciones? ¿las puedo poner por ejemplo en posicion 0x27, en adelante? Pregunto esto ya que me dijeron que debo usar a partir de la posicion 0x20. Junto con esa duda "btfss Banderas, Direccion" qué querrías lograr con esa línea?, sé la funcion del btfss, pero no el por qué Banderas y Dirección.

Y por ultimo el XORLW y ANDLW. Y con cantidad de interrupciones a qué te referís?, cuántos tipos tengo habilitado?

Creo que con esas dudas entendería todo el código, aunque también tengo dudas con el Salir_int, es necesario? o puedo poner esas lineas dentro del flag de Interrupcion tmb?

Muchas gracias!!
Título: Re:TMR0 como contador por pulsos (P16F628A)
Publicado por: KILLERJC en 12 de Octubre de 2016, 13:41:41
Citar
¿para qué usas los bits de banderas, y por qué en esas posiciones?

Paraque entiendas eso , el EQU en realidad no es para asignar un nombre a un registro, sino es una igualdad, Si vos haces:

Código: ASM
  1. Registro1 EQU 0x20

Lo que estas haciendo es simplemente decir que Registro1 es una constante, cuando vos luego escribis

Código: ASM
  1. MOVWF Registro1, F

Se traduce en

Código: ASM
  1. MOVWF 0x20, F

---------

Mi objetivo fue darle "nombre" a los bits del registro Bandera,  Para que se entienda esto seria equivalente:

Código: ASM
  1. BTFSS   Banderas, Habilitado
  2.         BTFSS   Banderas, Direccion
  3.  
  4.         BTFSS   Banderas, 0
  5.         BTFSS   Banderas, 1

(Aca cometi un error en el codigo, pero creo que lo vas a poder solucionar solo, ya que no funcionaria como te lo pase)

Citar
sé la funcion del btfss, pero no el por qué Banderas y Dirección.
Banderas es un registro en el cual uso 2 bits, para determinar el flujo del programa, la direccion del contador si incrementa o decrementa y si esta habilitado el contador para que incremente o decremente..

Citar
Y por ultimo el XORLW y ANDLW. Y con cantidad de interrupciones a qué te referís?, cuántos tipos tengo habilitado?

Las interrupciones estan realizadas para el TMR0, como explique vos necesitas 1 segundo entre cuentas (incremento o decremento del display), el mayor problema es que el TMR0 es de 8 bits y usando incluso el preescaler de 1:256 nunca lograrias llegar al segundo.Por ejemplo si usas un cristal de 4Mhz y cargando al TMR0 el valor 60, tendrias una interrupcion cada 0.050176 s, es decir deberias contar 20 interrupciones y recien ahi es que paso 1 segundo. Es recien ahi cuando vas a incrementar o decrementartu display.

Con el XORLW lo que hago es ver si ya pasaron la cantidad de interrupciones, podes pensarlo como una "igualdad".

El unico ANDLW es cuando llamas la tabla y es una proteccion, suponete que existe un problema y en ves de existir un numero de 0 a F en W al momento de llamar la tabla, queda un numero asi: 0x42 , esto significaria que saltaria a otro lugar donde no esta previsto.
Para protegerme de eso, antes de llamar lo que hago es "recortar" el numero que tengo en W, siguiendo el ejemplo

0x42 = 0100 0010
0x0F = 0000 1111
------------------------------
AND = 0000 0010

Es decir lo que tiene el 1 queda como esta y lo que tiene el 0 queda en 0.

Citar
aunque también tengo dudas con el Salir_int, es necesario?
El mayor problema de una interrupcion cuando tenes codigo en el loop principal es que W y STATUS se comparten, Si estas en el loop principal y tenes:

MOVLW 0x10
ADDWF Registro,F

Pero si la interrupcion ocurre luego del MOVLW (ya que no tenes control de CUANDO va a ocurrir la interrupcion) puede ocurrir problemas si es que no guardas W y STATUS, Suponete esto, tenes en Registro elvalor 0x20, por lo tanto el resultado de esas instrucciones te deberia dar 0x30, Si ocurre lo anterior entra a la interrupcion vos NO guardas W, modificas el valor del mismo y cuando salis (retfie) queda con el valor W = 0x04, ocurre que el resultado del ADDWF seria 0x24. cambiando el funcionamiento del programa. STATUS ocurre lo mismo, solo pensa que en ves de tener un ADDWF tenes un BTFSS y si seguis la logica tenes el mismo problema.

Ahora el Salir_int, no es un RETFIE solo, sino que tenes que agregarle el codigo (que se encuentra en el datasheet) que reestablece el valor guardado al entrar en la interrupcion de W y STATUS. Como siempre al entrar a la interrupcion GUARDAS W y STATUS, al salir SIEMPRE deberias reestablecerlo, por eso no encontras RETFIE solos, sino que para salir de la interrupcion van a un GOTO Salir_Int, donde estaria ese codigo de reestablecer.

En resumen el programa que propongo es:

Loop principal: revisa el boton y si se detecta, activa/desactiva un bit (bandera, el habilitado) nada mas
Interrupcion:
Revisa que esta habilitado el contador, sino no cuenta (interrupciones) y se queda mostrando siempre lo mismo.
Al momento de estar habilitado, deja pasar el flujo de programa y empieza a contar, cuando se cuenta y se llega al segundo pasa a la otra parte del programa, muestra el digito, y pregunta por si estaba incrementando o decrementando, si incrementa , lo hace y revisa que el resultado este dentro del 0 a 15 ( 0x0 a 0xF) si se paso, es decir queda con 0x10, deberia dejar el valor a mostrar en la proxma entrada como 0xF y ademas cambia el estado del bit de la direccion, asi ahora en ves de incrementar, decrementa.Lo mismo ocurre cuando se decrementa, cuando llega a 0x00 si restas 1 pasa a 0xFF, si detecta eso, pone el numero a 0x00 y pasa la direccion a incrementar.

Lo que verias es:

Display : Va de 0 a F luego de F a 0, y se repite.
En caso de presionar el boton y soltarlo, deja de contar interrupciones, con lo cual jamas va a pasar el segundo, ya que nunca va a llegar al numero de interrupcion ( por que no se cuenta mas_), si presionas de vuelta, dejas que pase y cuente de nuevo, volviendo a funcionar como debe.

Espero que lo entiendas ahora, hay errores seguro, por que lo hice en un bloc de notas mientras comia, y esto lo escribo desde el trabajo :P
Título: Re:TMR0 como contador por pulsos (P16F628A)
Publicado por: n-utz en 12 de Octubre de 2016, 20:50:43
Gracias genio!!