Un programa senzill tindrà normalment una estructura en tres parts.
#include <p16F690.inc> __config (_INTRC_OSC_NOCLKOUT&_WDT_OFF&_PWRTE_OFF&_MCLRE_OFF&_CP_OFF&_BOR_OFF&_IESO_OFF&_FCMEN_OFF) #define Nivell .21 ; Valor de comparació per al nivell
cblock 0x20 Valor ; Variable on guardem el que mostraran el valor Comptador:2 ; Variable de 16 bits Llista:.16 ; Bloc de 16 valors endc cblock 0xA0 ; Zona de memòria de dades del banc 1 Taula:40 ; Vector de 64 posicions (40h = 64) endc
org 0 Inici bsf STATUS,RP0 ; Tria el banc 1 ... ; Inicialitzacions de registres del banc 1 bcf STATUS,RP0 bsf STATUS,RP1 ; Tria el banc 2 ... ; Inicialitzacions de registres del banc 2 bcf STATUS,RP1 ; Tria el banc 0 ... ; Inicialitzacions de registres del banc 0 movlw b'00001000' ; 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
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 dues 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, això s'indica amb un valor després del símbol dels dos punts. 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 tenir diverses definicions de variables perquè fem 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.
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.
#include <p16F690.inc> __config (_INTRC_OSC_NOCLKOUT&_WDT_OFF&_PWRTE_OFF&_MCLRE_OFF&_CP_OFF&_BOR_OFF&_IESO_OFF&_FCMEN_OFF)
cblock 0x20 ... endc
org 0 Inici ... 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
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.
#include <p16F690.inc> __config (_INTRC_OSC_NOCLKOUT&_WDT_OFF&_PWRTE_OFF&_MCLRE_OFF&_CP_OFF&_BOR_OFF&_IESO_OFF&_FCMEN_OFF)
cblock 0x20 ... endc
org 0 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
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.
#include <p16F690.inc> __config (_INTRC_OSC_NOCLKOUT&_WDT_OFF&_PWRTE_OFF&_MCLRE_OFF&_CP_OFF&_BOR_OFF&_IESO_OFF&_FCMEN_OFF)
cblock 0x20 ... endc
org 0 goto Inici ; Saltem al lloc on hi ha el programa principal nop ; Zona de memòria de programa que no utilitzem nop nop
Interrup 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 ... 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
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).
#include <p16F690.inc> __config (_INTRC_OSC_NOCLKOUT&_WDT_OFF&_PWRTE_OFF&_MCLRE_OFF&_CP_OFF&_BOR_OFF&_IESO_OFF&_FCMEN_OFF)
cblock 0x20 ... endc
org 0 goto Inici ; Saltem al lloc on hi ha el programa principal nop ; Zona de memòria de programa que no utilitzem nop nop
Interrup ... retfie ; Torna al programa principal, on s'havia quedat
Inici ... Bucle ... goto Bucle ; Repetim-ho...
... return
org 0x700 ; Lloc de la MEMPROG on guardem els valors DadesMP db 0,b'00000000',0,b'00000001',0,b'00000011',0,b'00000010',0,b'00000110',0,b'00000111' org 0x2100 ; Inici de la memòria EEPROM DadesEE de b'00000000',b'00000001',b'00000011',b'00000010',b'00000110',b'00000111',b'00000101' end
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.
#include <p16F690.inc> __config (_INTRC_OSC_NOCLKOUT&_WDT_OFF&_PWRTE_OFF&_MCLRE_OFF&_CP_OFF&_BOR_OFF&_IESO_OFF&_FCMEN_OFF)
cblock 0x20 ... endc
org 0 goto Inici ; Saltem al lloc on hi ha el programa principal nop ; Zona de memòria de programa que no utilitzem nop nop
Interrup ... 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

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