7 sept 2022

MICROCONTROLADORES PROGRAMABLES (IV)

  

Cuarta publicación sobre Microcontroladores Programables PIC, en la veremos como trabajar con bits, tablas y su aplicación práctica.








OPERACIONES CON BITS.

Las operaciones con bits nos permiten manipular un solo bit dentro de una palabra “Word”. Con ellos podemos mover, activar o desactivar bits individualmente en registros o números que especifiquemos. Veamos las instrucciones para ello.

BCF 

La instrucción BCF pone a cero un bit que especifiquemos en el registro que especifiquemos. La sintaxis es la siguiente: 

BCF <registro>,<bit> 

Puede que resulte familiar, ya que la hemos utilizado previamente para cambiar del Banco 1 al Banco 0, cuando poníamos a 0 el bit 5 del registro STATUS. También podemos utilizarla para poner a 0 cualquier bit de cualquier otro registro o posición de memoria. 
Por ejemplo, si queremos poner a 0 el tercer bit de 11001101 que está almacenado en la posición de memoria 0Ch, introduciríamos: 


1
BCF        0Ch,2        


Recordar que en binario, los Bytes están formados por 8 bits que van de 0 a 7, donde 0 es el bit más a la derecha del número en binario. En la siguiente tabla, se muestra un ejemplo de número en binario, y el resultado tras aplicar la instrucción BCF anterior:




BSF 

La instrucción BSF pone a 1 el bit que especifiquemos en cualquier registro que especifiquemos. También nos tiene que resultar familiar, porque la hemos utilizado para cambiar del Banco 0 al Banco 1. Su sintaxis es: 

BSF <registro>,<bit> 

Y se utiliza exactamente de la misma forma que la BCF, vista anteriormente. 


BTFSC 

La instrucción BTFCS se utilizar para hacer la comprobación de un bit en un registro. Literalmente dice "Comprueba un bit en el registro o posición de memoria (F), y salta si es 0". Así que comprobará el bit que le especifiquemos en el registro. Si el bit es 0, la instrucción le dice al PIC que se salte la instrucción que viene a continuación.
Por ejemplo, si queremos comprobar si el flag de CARRY ha sido puesto a 1 cuando hayamos sumado dos números, haremos lo siguiente: 

1
2
3
4
;
BTFSC     803h,0        ; Dirección del Registro Puerto A
<Intrucción A>          ; Continua por aquí, si CARRY está a 1.
<Instrución B>          ; o continua aquí, si CARRY está a 0.



Si el valor del bit es 1, entonces BTFSC continuará por la instrucción inmediatamente siguiente. Si está a 0, entonces se salta esa instrucción. Este podría ser un ejemplo de aplicación:


1
2
3
4
Bucle
            :
            BTFSC    03,0
            Goto     Bucle


Aquí, el PIC solo saldrá de bucle del “goto”, si el bit 0 del registro STATUS (o el flag de CARRY) está puesto a 0. 


BTFSS 

La instrucción BTFSS, ”Comprueba el bit del registro o posición de memoria (F), y salta si está a 1". Como se puede comprobar, es similar a la instrucción BTFSC, a excepción que el PIC se saltará la siguiente instrucción si el bit que estamos comprobando está a 1, en lugar de a 0. 


CLRF 

La instrucción CLRF, pondrá todos los bits del contenido de un registro a 0. La sintaxis es: 

CLRF <registro> 

También tiene que sonarnos, ya que la hemos usado anteriormente para poner todos los pines de un puerto como salidas, haciendo "CLRF 85h" o para poner los pines de un puerto que estaban como salidas todos a 0, haciendo "CLRF 05h". 


CLRW 

La instrucción CLRW es similar a la instrucción CLRF, pero solo pone a 0 el registro W



RLF y RRF

Las instrucciones RLF y RRF desplazan los bits del contenido de un registro un lugar hacia la izquierda (RLF), o un lugar hacia la derecha (RRF). 
Por ejemplo, si tenemos 00000001 y utilizamos la instrucción RLF, el resultado sería 00000010. Pero si tenemos 10000000 y empleamos RLF, el bit 1 será desplazado al flag CARRY. Y si empleamos RLF una vez más, el 1 reaparecerá justo al principio, o dicho de otro modo en el bit0. 
Lo mismo ocurre con la instrucción RRF pero de manera inversa. En la siguiente tabla, vemos el resultado de aplicar la instrucción RLF, donde mostramos los 8 bits del contenido de un registro, y el flag de CARRY



La sintaxis para estas dos instrucciones es la siguiente:

RLF <registro>,d 

RRF <registro>,d 

En ambos casos, d le dice al PIC donde almacenar el resultado. Si d=0, el resultado se almacena en el registro W, y si d=1 el resultado se almacena en ese registro especificado.

Con todo lo visto hasta ahora, vamos a realizar un programa de ejemplo. Este código hace que una luz se desplace, comenzando en el bit 0 del puerto B hasta el bit 8 del mismo, y después siguiendo al bit 0 del puerto A hasta el bit 5. Al terminar hace el mismo camino a la inversa, hasta llegar de nuevo al principio. Este sería el circuito:




1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
;**********************************************************;   
; PROGRAMA LEDS.asm                    FECHA: XX XXXXX 2022  ; 
; Programa para hacer desplazar LEDS por los puertos A y B ;
; Usa el puerto RA 1 (Pin 18)                                 ;
; Revisión: 1.0                   Programa para PIC16F48A      ;
; Velocidad de Reloj: XTAL externo 4Mhz (1Mhz=1us)          ;
; WatchDog = OFF                     Tipo Reloj:  Cristal      ;
; Autor: XMG                      Por: XANUR2001/ ACME 2022    ;
; Compilado en : MPLAB X IDE v2.05 (MICROCHIP)              ;
; *********************************************************;
;
;*****Establecimiento de constantes *******
TIEMPO      equ     9Fh       	; Variable para el bucle de retardo
PORTB       equ     06h       	; Dirección del Port B
TRISB       equ     86h       	; Dirección registro Port B
PORTA       equ     05h       	; Dirección del Port A
TRISA       equ     85h       	; Dirección registro Port A
STATUS      equ     03h       	; Registro para seleccionar el banco
CONTADOR1   equ     0Ch       	; Registro para el bucle1
CONTADOR2   equ     0Dh       	; Registro para el bucle2
;
            bsf     STATUS,5    ; vamos al Banco 0.
             movlw   00h       	; y configura
            movwf   TRISB     	; los puertos A y B
            movlw   00h       	; como salidas,
            movwf   TRISA     	; después vuelve
            bcf     STATUS,5  	; al Banco 0.  
            movlw   00h       	; Ponemos a 0 el Puerto A.
            movwf   PORTA     	;
;
; Inicio del Programa
CorreLuz
            movlw   01h       	; Ponemos a 1 el primer bit
            movwf   PORTB     	; del Puerto B. PORTB = 00000001
            call    Retardo   	; y hacemos un retardo.
            call    Retardo   	;   
;
; Desplaza el bit a la izquierda, y después pausa.
            rlf     PORTB,1   	; PORTB = 00000010, C = 0
            call    Retardo 	; y hacemos un retardo.
            call    Retardo 
            rlf     PORTB,1   	; PORTB = 00000100, C = 0
            call    Retardo 	; y hacemos un retardo 
            call    Retardo
            rlf     PORTB,1   	; PORTB = 00001000, C = 0
            call    Retardo 
            call    Retardo 
            rlf     PORTB,      ; PORTB = 00010000, C = 0
            call    Retardo
            call    Retardo
            rlf     PORTB,1    ; PORTB = 00100000, C = 0
            call    Retardo
            call    Retardo
            rlf     PORTB,1    ; PORTB = 01000000, C = 0
            call    Retardo 
            call    Retardo 
            rlf     PORTB,1    ; PORTB = 10000000, C = 0
            call    Retardo 
            call    Retardo 
            rlf     PORTB,1    ; desplaza el bit al "flag" CARRY.
                               ; PORTB = 00000000, C = 1
; Ahora se moverá al puerto A, desplazando el bit hacia la izquierda.
            rlf     PORTA,1    ; movemos bit CARRY al puerto A.
                               ; PORTA = 00001, C = 0            
            call    Retardo 
            call    Retardo 
            rlf     PORTA,1    ; PORTA = 00010, C = 0
            call    Retardo 
            call    Retardo 
            rlf     PORTA,1    ; PORTA = 01000, C = 0
            call    Retardo 
            call    Retardo 
            rlf     PORTA,1    ; PORTA = 10000, C = 0
            call    Retardo 
            call    Retardo 
; Desplaza el bit de vuelta por el puerto A 
            rrf     PORTA,1    ; PORTA = 01000, C = 0
            call    Retardo
            call    Retardo
            rrf     PORTA,1    ; PORTA = 00100, C = 0
            call    Retardo
            call    Retardo
            rrf     PORTA,1    ; PORTA = 00010, C = 0
            call    Retardo
            call    Retardo
            rrf     PORTA,1    ; PORTA = 00001, C = 0
            call    Retardo
            call    Retardo
            rrf     PORTA,1    ; desplaza el bit al "flag" CARRY
                               ; PORTA = 00000, C = 1;  
; Ahora desplaza el bit de vuela al Puerto B
            rrf     PORTB,1    ; PORTB = 10000000, C = 0
            call    Retardo
            call    Retardo
            rrf     PORTB,1    ; PORTB = 01000000, C = 0
            call    Retardo
            call    Retardo
            rrf     PORTB,1    ; PORTB = 00100000, C = 0
            call    Retardo
            call    Retardo
            rrf     PORTB,1    ; PORTB = 00010000, C = 0
            call    Retardo
            call    Retardo
            rrf     PORTB,1    ; PORTB = 00001000, C = 0
            call    Retardo
            call    Retardo
            rrf     PORTB,1    ; PORTB = 00000100, C = 0
            call    Retardo
            call    Retardo
            rrf     PORTB,1    ; PORTB = 00000010, C = 0
            call    Retardo
            call    Retardo
            rrf     PORTB,1    ; PORTB = 00000001, C = 0
            call    Retardo
            call    Retardo    ; hemos llegado donde empezamos
;
            goto    CorreLuz   ; y volvemos al inicio. 
;
; Subrutina para hacer el retardo entre los movimientos de bits. 
;
Retardo 
            movlw   TIEMPO     ; Cogemos el tiempo de retardo,
            movwf   CONTADOR1  ; y lo ponemos en una variable. 
Bucle1                         ;
            decfsz  CONTADOR1  ; Decremento del tiempo de retardo 
            goto    Bucle1     ; hasta que llegue a cero.
            movwf   CONTADOR2  ; Cogemos el tiempo de retardo, 
Bucle2                         ; y repetimos la cuenta atrás.
            decfsz  CONTADOR2  ; 
            goto    Bucle2     ; 
            return             ; Fin de la subrutina.
;**** Fin del Programa****            
            end                ;
; Algunos compiladores necesitan esta instrucción,
; O por si nos olvidamos poner la instrucción 'goto'.




TABLAS DE DATOS.

En el conjunto de instrucciones del PIC, dispone de una propiedad interesante, que nos va a permitir realizar tablas de datos.
Para el quien no este habituado, una tabla de datos es una lista simple de valores de datos, donde cada uno de ellos puede ser leído, dependiendo de algún criterio.

Un ejemplo práctico, podría ser realizar un circuito que cuente de 0 a 9 y se pueda visualizar el número en un display de 7 segmentos. El display de 7 segmentos se conecta al puerto B, de manera que cada pin encienda un LED del display. Este sería el esquema de conexiones:




Es decir, el pin RB7 no lo conectaríamos al display. De este modo tendremos la siguiente tabla de correspondencias entre los números a representar y el valor binario que debe de tomar el puerto B para que se enciendan los diodos LED correspondientes:



Con estas premisas, la tabla se podría crear utilizando el siguiente código:



1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
PC        equ       02h
:
:
tabla       addwf    PC
            retlw    41h
            retlw    3Bh  
            retlw    6Bh
            retlw    4Dh
            retlw    6Eh
            retlw    7Eh
            retlw    43h
            retlw    7Fh
            retlw    6Fh
            retlw    77h
            return



Vamos a Usando parte del código que hemos empleado anteriormente para crear otro nuevo. Por eso, hay partes que serán familiares. Este es el código completo para hacer que se muestre en el display una cuenta ascendente de 0 a 9 y luego volver a empezar:



1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
;**********************************************************;   
; PROGRAMA DISPLAY.asm        	   	FECHA: XX XXXXX 2022   ;
; Programa para contar de 0 a 9 y mostrar en un dispay 7seg;
; Usa el puerto RB (Pines 6 al 12)    			           ;
; Revisión: 1.0       			Programa para PIC16F48A    ;
; Velocidad de Reloj: XTAL externo 4Mhz (1Mhz=1us)          ;
; WatchDog = OFF                     Tipo Reloj:  Cristal      ;
; Autor: XMG                      Por: XANUR2001/ ACME 2022    ;
; Compilado en : MPLAB X IDE v2.05 (MICROCHIP)              ;
; *********************************************************;
;
;*****Establecimiento de constantes *******
PC          equ     02h            ; Dirección Contador de Programa(PC)
TIEMPO      equ     9Fh            ; Dirección del Bucle de Retardo.
PORTB       equ     06h            ; Dirección del Puerto B.
TRISB       equ     86h            ; Dirección del registro Puerto B.
.
STATUS      equ     03h            ; Registro para selecciona el Banco.
CONTADOR1   equ     0Ch            ; Registro para el Bucle1 de Retardo.
CONTADOR2   equ     0Dh            ; Registro para el Bucle2 de Retardo.
;
            bsf       STATUS,5     ; Vamos al Banco 1.
            movlw     00h          ; Y configura
             movwf     TRISB        ; los puertos A y B
            movlw     00h          ; como salidas,
            movwf     TRISA        ; después vuelve
            bcf       STATUS,5     ; al Banco 0.
            movlw     00h          ; Ponemos a 0 el Puerto A 
            movwf     PORTA        ; 
; 
; Configuración del Puerto B.
            bsf       STATUS,5     ; Vamos al Banco 1.
            movlw     00h          ; Configura todos los pines del Puerto B.
            movwf     TRISB        ; como salidas.
            bcf       STATUS,5     ; Vuelta al Banco 0.
            movlw     00h          ; pone al 0 el Puerto B.
            movwf     PORTB        ; Es decir, Display apagado. 
;**** Comienzo del Programa ****
Inicio      movlw     0Ah          ; Carga el valor 10 en W.
            call      tabla        ; Llamada a la tabla para encender el valor 0.
            movwf     PORTB        ; w=77h, y representa 0 en el display.
;
            call      Retardo      ; Hacemos un retardo...
            call      Retardo      ; ...para que se pueda ver el Nº0.
;
            movlw     01h          ; Carga el valor 1 en W.
            call      tabla        ; Llamada a la tabla para encender el valor 1.
            movwf     PORTB        ; y representar 1 en el display.
;
            call      Retardo
            call      Retardo
;
            movlw     02h          ; Carga el valor 2 en W
            call      tabla        ; Llamada a la tabla para encender el valor 2.
            movwf     PORTB        ; y representa 2 en el display.
;
            call      Retardo
            call      Retardo
;
            movlw     03h          ; Cargar el valor 3 en W
            call      tabla        ; Llamada a la tabla para encender el valor 3.
            movwf     PORTB 	   ; y representa 3 en el display.
;
            call      Retardo 
            call      Retardo 
;
            movlw     04h          ; Carga el valor 4 en W
            call      tabla        ; Llamada a la tabla para encender el valor 4.
            movwf     PORTB        ; y representa 4 en el display.
;
            call      Retardo    
            call      Retardo
; 
            movlw     05h   	   ; Carga el valor 5 en W
            call      tabla 	   ; Llamada a la tabla para encender el valor 5.
            movwf     PORTB        ; y representa 5 en el display. 
;
            call      Retardo
            call      Retardo
;
            movlw     06h          ; Carga el valor 6 en W
            call      tabla        ; Llamada a la tabla para encender el valor 4.
            movwf     PORTB        ; y representa 6 en el display.
;
            call      Retardo    
            call      Retardo
; 
            movlw     07h   	   ; Carga el valor 7 en W
            call      tabla 	   ; Llamada a la tabla para encender el valor 5.
            movwf     PORTB        ; y representa 7 en el display. 
;
            movlw     08h   	   ; Carga el valor 8 en W
            call      tabla 	   ; Llamada a la tabla para encender el valor 5.
            movwf     PORTB        ; y representa 8 en el display. 
;
            call      Retardo
            call      Retardo
;
            movlw     06h          ; Carga el valor 9 en W
            call      tabla        ; Llamada a la tabla para encender el valor 4.
            movwf     PORTB        ; y representa 9 en el display.
;
            call      Retardo    
            call      Retardo
            goto      Inicio       ; Y regresamos al Inicio
; Aquí empieza la subrutina para hacer el retardo.
Retardo
            movlw     TIEMPO
            movwf     CONTADOR1    ; Tomamos el Tiempo de retardo y lo ponemos en una variable.
Bucle1
            decfsz    CONTADOR1
            goto      Bucle1       ; Decremeto del tiempo hasta llegar a 0
            movwf     CONTADOR2
Bucle2
            decfsz    CONTADOR2    ; 
            goto      Bucle2
            return                 ; Fin de la Subrutina
; *****TABLA ***
; Aqui situamos la configuracio para representar los nuemros
; en el display de 7 segmentos, segun el esquema de conexiones.
tabla       addwf     PCPORTA
            retlw     41h          ; Carga 41h en W (Nº1) y retorna.
            retlw     3Bh          ; Carga 3Bh en W (Nº2) y retorna. 
            retlw     6Bh          ; Carga 6Bh en W (Nº3) y retorna.
            retlw     4Dh          ; Carga 4Dh en W (Nº4) y retorna.
            retlw     6Eh          ; Carga 6Eh en W (Nº5) y retorna.
            retlw     7Eh          ; Carga 7Eh en W (Nº6) y retorna.
            retlw     43h          ; Carga 43h en W (Nº7) y retorna.
            retlw     7Fh          ; Carga 7Fh en W (Nº8) y retorna.
            retlw     6Fh          ; Carga 6Fh en W (Nº9) y retorna.
            retlw     77h          ; Carga 77h en W (Nº0) y retorna.
            return
; 
; **** Fin del Programa **** 
            end
; Algunos compiladores necesitan esta instrucción, o por si olvidamos poner la instrucción "goto".      



Este código, es un simple ejemplo que puede ser optimizado con los conceptos explicados anteriormente. Por ejemplo, se pueden aplicar subrutinas para procesos repetidos, como este:



1
2
3
4
5
6
7
8
            call     DISPLAY    ;Llamada a la rutina 
:
:
DISPLAY     call     Tabla      ; Llamada a la Tabla
            movwf    PORTB      ; y representamos en el display
            call     Retardo
            call     Retardo
            return



En cualquier caso, como aprendizaje, es una forma muy clara de ver el proceso.




En la siguiente y última publicación, continuaremos con las interrupciones y el "Watchdog": 
Microcontroladores Programables PIC (V)



ENLACES.

Y a continuación, una serie de enlaces útiles sobre lo expuesto:

Hoja de características (Datasheet) del PIC 16F84A®:

Web del Fabricante Microchip®:

MPLAB de Microchip®:

Software PICKit2 Programmer (y otros recursos en Microchip):

NotePad++:

WinPic800:

IC-PROG:

Programador JDM, en Blog Xanur:

Programador PicKit, en Blog Xanur:

Electrónica Digital, en Blog Xanur:




Esperamos que os haya gustado esta publicación. Si es así, no dudes en compartirla.

© Se permite reproducción total o parcial de este contenido, siempre y cuando se reconozca la fuente de información utilizada y se incluya el enlace a este artículo.

Equipo Xanur©2022.