Programació en pic-as del PIC 16F690

Referència Trucs Perifèrics   Recursos CITCEA
Tutorial Exemples Projectes   Inici

Estructura d'un programa

Un programa senzill tindrà normalment una estructura en tres parts.

PROCESSOR 16F690
#include <xc.inc>
config FOSC = INTRCIO, WDTE = OFF, PWRTE = OFF, MCLRE = OFF, CP = OFF
config CPD = OFF, BOREN = OFF, IESO = OFF, FCMEN = OFF
#define  Nivell  21  ; Valor de comparació per al nivell
Comptador EQU 0x20  ; Variable de 16 bits, 2 bytes (0x20 i 0x21)
Llista EQU 0x22  ; Bloc de 16 valors (0x22 a 0x31)
Valor EQU 0x32  ; Variable on guardem el que mostrarem
; Zona de memòria de dades del banc 1
Taula EQU 0xA0
PSECT code, class=CODE, delta=2, abs
main:
  bsf RP0  ; Tria el banc 1
  ...      ; Inicialitzacions de registres del banc 1
  bcf RP0	
  bsf RP1  ; Tria el banc 2
  ...      ; Inicialitzacions de registres del banc 2
  bcf RP1  ; Tria el banc 0
  ...      ; Inicialitzacions de registres del banc 0
  movlw 00001000B  ; Posa el valor al registre W
  movwf Valor  ; Copia el valor de W a la variable Valor
  ...	
Bucle:
  ...         ; Tros de programa que es repeteix cíclicament
  goto Bucle  ; Repetim-ho...
END main

A la primera part (que en aquest web s'indica amb fons de color daurat) hi ha la definició del microcontrolador que estem programant i la configuració base del microcontrolador (bits de configuració general). Aquesta configuració es defineix en el moment d'enviar un programa i no pot canviar-se mentre el programa està en execució. Aquestes quatre primeres línies són sempre idèntiques per a la majoria dels nostres programes. En aquesta part també s'hi poden definir símbols per definir valors. Els símbols no són variables i, per tant, no ocupen memòria (ni de programa ni de dades). En el moment d'enviar el programa, tots els símbols seran substituïts pels valors que els corresponen. L'ús de símbols permet tenir en un lloc fàcilment localitzable aquells valors que pot ser necessari modificar durant el desenvolupament i proves d'un programa.

A la segona part (que en aquest web s'indica amb fons de color blau fosc) hi ha la definició de les variables. La primera línia indica l'adreça on guardarem les variables i cada variable ocuparà una o més posicions a partir d'aquesta adreça. En principi, cada variable ocupa només una posició de memòria. Si una variable ha d'ocupar més d'una posició de memòria, cal deixar lliures les posicions que ocuparà. Les variables de més d'una posició de memòria poden guardar números de 16 o més bits o bé llistes o vectors. Les llistes i els vectors normalment es gestionen amb adreçament indirecte. En alguns programes podem fer servir més d'un banc de memòria.

A la tercera part (que en aquest web s'indica amb fons de color vermell fosc) correspon al programa pròpiament dit. Quasi tots els programes consten de dues parts, una part d'inicialització (que només s'executa un cop) i un bucle que es repeteix indefinidament. A la part d'inicialització es configura el microcontrolador, s'inicialitzen les variables i es fan aquelles altres tasques que només cal fer un cop. Sempre que sigui possible, es procura posar juntes totes les accions que corresponen a un banc per tal de no haver de canviar de banc més vegades que les mínimes necessàries. Normalment es fa abans la configuració que les inicialitzacions ja que algunes inicialitzacions poden requerir que el microcontrolador ja estigui convenientment configurat.

Podem posar, si és necessari, part del programa en una part diferent de la memòria. Per exemple, en el programa següent el bucle està a partir de l'adreça 60 h.

PROCESSOR 16F690
#include <xc.inc>
config FOSC = INTRCIO, WDTE = OFF, PWRTE = OFF, MCLRE = OFF, CP = OFF
config CPD = OFF, BOREN = OFF, IESO = OFF, FCMEN = OFF
#define  Nivell  21  ; Valor de comparació per al nivell
Comptador EQU 0x20  ; Variable de 16 bits, 2 bytes (0x20 i 0x21)
Llista EQU 0x22  ; Bloc de 16 valors (0x22 a 0x31)
Valor EQU 0x32  ; Variable on guardem el que mostrarem
; Zona de memòria de dades del banc 1
Taula EQU 0xA0
PSECT code, class=CODE, delta=2, abs
; Les instruccions següents es posaran a partir de l'adreça 0h
main:
  bsf RP0  ; Tria el banc 1
  ...      ; Inicialitzacions de registres del banc 1
  bcf RP0	
  bsf RP1  ; Tria el banc 2
  ...      ; Inicialitzacions de registres del banc 2
  bcf RP1  ; Tria el banc 0
  ...      ; Inicialitzacions de registres del banc 0
  movlw 00001000B  ; Posa el valor al registre W
  movwf Valor  ; Copia el valor de W a la variable Valor
  ...	
; Les instruccions següents es posaran a partir de l'adreça 60h
ORG 0x60
Bucle:
  ...         ; Tros de programa que es repeteix cíclicament
  goto Bucle  ; Repetim-ho...
END main

Un programa pot tenir funcions (també anomenades subfuncions). Les funcions (que en aquest web s'indica amb fons de color verd fosc) són trossos de programa separats als que s'accedeix amb la instrucció call i se'n retorna amb la instrucció return. Les funcions tenen l'avantatge que, quan acaben, retornen a la instrucció següent a aquella des d'on s'hi ha accedit. Això permet que es pugui accedir a la mateixa funció des de llocs diferents del programa ja que després es retorna al lloc correcte. També es fan servir les funcions quan tenim trossos de programa ja provats que ens interessa que no estiguin dins del programa principal per poder entendre millor el funcionament d'aquest. El cas més habitual és quan aquestes funcions s'aprofiten de programes anteriors.

PROCESSOR 16F690
#include <xc.inc>
config FOSC = INTRCIO, WDTE = OFF, PWRTE = OFF, MCLRE = OFF, CP = OFF
config CPD = OFF, BOREN = OFF, IESO = OFF, FCMEN = OFF
Retard1 EQU 0x20
Retard2 EQU 0x21
...
PSECT code, class=CODE, delta=2, abs
main:
  ...	
Bucle:
  ...
  call Retard
  ...
  goto Bucle  ; Repetim-ho...
;
; Bucle de retard
;
Retard:
  movwf Retard2  ; Ho copia a la variable Retard2
RetVar:
  decfsz Retard1,f  ; Decrementa la variable 1
                    ; si dona zero, no es fa la instrucció següent
  goto RetVar  ; Salta, excepte si el resultat ha estat zero
  decfsz Retard2,f  ; Decrementa la variable 2
  goto RetVar  ; Salta, excepte si el resultat ha estat zero
  return
END main

Atès que normalment cal modificar-les menys, és habitual posar les funcions al final, com en el cas de l'exemple anterior. Això, però, no és imprescindible. L'estructura de programa següent també funcionaria.

PROCESSOR 16F690
#include <xc.inc>
config FOSC = INTRCIO, WDTE = OFF, PWRTE = OFF, MCLRE = OFF, CP = OFF
config CPD = OFF, BOREN = OFF, IESO = OFF, FCMEN = OFF
Retard1 EQU 0x20
Retard2 EQU 0x21
...
PSECT code, class=CODE, delta=2, abs
main:
  goto Inici
;
; Bucle de retard
;
Retard:
  movwf Retard2  ; Ho copia a la variable Retard2
RetVar:
  decfsz Retard1,f  ; Decrementa la variable 1
                    ; si dona zero, no es fa la instrucció següent
  goto RetVar  ; Salta, excepte si el resultat ha estat zero
  decfsz Retard2,f  ; Decrementa la variable 2
  goto RetVar  ; Salta, excepte si el resultat ha estat zero
  return
Inici:
  ...	
Bucle:
  ...
  call Retard
  ...
  goto Bucle  ; Repetim-ho...
END main

Convé posar totes les funcions juntes (a l'inici o al final) i no barrejar-les amb el programa per evitar confusions.

Quan un programa fa servir interrupcions l'estructura se'ns complica una mica. En aquest web les instruccions corresponents a les interrupcions s'indiquen amb fons de color lila. Resulta que el microcontrolador comença a executar el programa a partir de la instrucció que hi ha a la posició 0 i en cas d'interrupció executa a partir de la posició 4. Atès que entre les posicions 0 i 3 no hi cap pràcticament res, quan es treballa amb interrupcions se sol fer l'estructura següent.

PROCESSOR 16F690
#include <xc.inc>
config FOSC = INTRCIO, WDTE = OFF, PWRTE = OFF, MCLRE = OFF, CP = OFF
config CPD = OFF, BOREN = OFF, IESO = OFF, FCMEN = OFF
; Zona de memòria de dades habitual
Visualit EQU 0x20  ; Una variable on guardem el que mostraran els LED
;
; Zona de memòria de dades que no depèn del banc triat
W_Copia EQU 0x70  ; Guardarà el contingut de W durant la interrupció
ST_Copia EQU 0x71  ; Guardarà STATUS durant la interrupció
PSECT code, class=CODE, delta=2, abs
main:  ; Adreça 0 h
  goto	Inici  ; Saltem al lloc on hi ha el programa principal
  nop  ; Zona de memòria de programa que no utilitzem
  nop
  nop
Interrup:  ; Adreça 4 h
  movwf W_Copia  ; Copiem l'acumulador a W_Copia
  swapf STATUS,w  ; Copiem STATUS a l'acumulador permutant els nibbles
  clrf STATUS  ; Posa a 0 i així segur que el banc és el 0
  movwf ST_Copia  ; Guarda STATUS permutat a ST_Copia
  ...	
FiInt:
  swapf ST_Copia,w  ; Copia permutant ST_Copia a l'acumulador
  movwf STATUS  ; I ho passa a STATUS recuperant el valor d'abans
                ; de la interrupció
  swapf W_Copia,f  ; Permuta els bits de W_Copia
  swapf W_Copia,w  ; Torna a permutar els bits de W_Copia
                   ; i els guarda a l'acumulador sense variar STATUS
  retfie  ; Torna al programa principal, on s'havia quedat
Inici:
  ...	
Bucle:
  ...
  goto Bucle  ; Repetim-ho...
  ...
  return
END main

El nostre programa pot contenir dades guardades a la memòria de programa o a la memòria EEPROM. En aquest web les dades que es defineixen a la memòria de programa o a la memòria EEPROM estan marcades amb un fons de color cian (blau verd).

PROCESSOR 16F690
#include <xc.inc>
config FOSC = INTRCIO, WDTE = OFF, PWRTE = OFF, MCLRE = OFF, CP = OFF
config CPD = OFF, BOREN = OFF, IESO = OFF, FCMEN = OFF
; Zona de memòria de dades habitual
Visualit EQU 0x20  ; Una variable on guardem el que mostraran els LED
;
; Zona de memòria de dades que no depèn del banc triat
W_Copia EQU 0x70  ; Guardarà el contingut de W durant la interrupció
ST_Copia EQU 0x71  ; Guardarà STATUS durant la interrupció
PSECT code, class=CODE, delta=2, abs
main:  ; Adreça 0 h
  goto	Inici  ; Saltem al lloc on hi ha el programa principal
  nop  ; Zona de memòria de programa que no utilitzem
  nop
  nop
Interrup:  ; Adreça 4 h
  ...	
  retfie  ; Torna al programa principal, on s'havia quedat
Inici:
  ...	
Bucle:
  ...
  goto Bucle  ; Repetim-ho...
  ...
  return
Gray:  ; Dades a la memòria de programa
  DB  00000000B, 00000001B, 00000011B, 00000010B, 00000110B, 00000111B
  DB  00000101B, 00000100B, 00001100B, 00001101B, 00001111B, 00001110B
  DB  00001010B, 00001011B, 00001001B, 00001000B
PSECT edata  ; Inici de la memòria EEPROM
Gray:
  DW 00000000B, 00000001B, 00000011B, 00000010B, 00000110B, 00000111B, 00000101B, 00000100B
  DW 00001100B, 00001101B, 00001111B, 00001110B, 00001010B, 00001011B, 00001001B, 00001000B
END main

Quan tenim programes molt llargs, podem necessitar la segona pàgina de la memòria de programa. En aquest web el programa situat a la segona pàgina té el fons de color marró i les funcions que es troben en aquesta zona tenen el fons de color verd clar.

PROCESSOR 16F690
#include <xc.inc>
config FOSC = INTRCIO, WDTE = OFF, PWRTE = OFF, MCLRE = OFF, CP = OFF
config CPD = OFF, BOREN = OFF, IESO = OFF, FCMEN = OFF
; Zona de memòria de dades habitual
Visualit EQU 0x20  ; Una variable on guardem el que mostraran els LED
	...	
PSECT code, class=CODE, delta=2, abs
main:  ; Adreça 0 h
  goto	Inici  ; Saltem al lloc on hi ha el programa principal
  nop  ; Zona de memòria de programa que no utilitzem
  nop
  nop
Interrup:  ; Adreça 4 h
  ...	
  retfie  ; Torna al programa principal, on s'havia quedat
Inici:
  ...	
Bucle:
  ...
  goto Bucle  ; Repetim-ho...
  ...
  return
ORG 0x800
IniciP2:
  ...  ; Part del programa situada a la pàgina 2
  ...  ; Funcions situades a la pàgina 2
  return
END main

 

 

Llicència de Creative Commons
Aquesta obra d'Oriol Boix està llicenciada sota una llicència no importada Reconeixement-NoComercial-SenseObraDerivada 3.0.