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

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