Programació en C del PIC 16F690

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

Combinant C i assemblador

A vegades pot ser interessant fer parts d'un programa en assemblador. En llenguatge assemblador podem generar codi més compacte i que s'executa de manera més ràpida. Abans de fer servir instruccions en assemblador dins el programa, caldrà que coneguem com es programa en llenguatge assemblador.

Una de les qüestions a destacar quan posem trossos en assemblador dins d'un programa en C és que no coneixem l'adreça exacta de les variables. Però tenim una manera de fer-les servir. La següent taula ens mostra un parell d'exemples de variables:

Tipus Nom en C Bytes Nom en ASM Comentari
Char Var1 1 _Var1
Unsigned short Var2 2 _Var2 Byte menys significatiu
_Var2+1 Byte més significatiu
Char Var3[2] 2 · 1 _Var3 Var3[0]
_Var3+1 Var3[1]

Atès que no sabem on s'ha guardat la variable, cal que ens assegurem que estem en el banc correcte. Com que no sabem quin és, fem servir la instrucció banksel. Només podem accedir a variables globals, les locals no són accessibles.

Cada instrucció en assemblador les posarem, com a cadena de caràcters (és a dir entre cometes), com a paràmetre de la funció asm. En instruccions en les que hi posem números, aquests s'interpretaran en decimal:

	asm("movlw 80");			// Valor 80 decimal
	asm("movwf _Var1");

Quan el programa té més variables que els que caben al banc 0, n'hi haurà que estaran al banc 1 o, potser, al 2. En aquests casos podem obtenir un error perquè l'adreça de la variable serà de vuit (banc 1) o nou (banc 2) bits i, en canvi, l'espai que té la instrucció per guardar l'adreça és de set bits. Atès que els bits vuitè i novè ja estan implícits en el banc, el problema no és a l'hora de trobar la variable sinó només a l'hora de guardar l'adreça a la instrucció. Per evitar l'error, tenim la possibilitat de forçar al compilador a posar al banc 0 les variables a les que volem accedir des de l'assemblador, com en aquest cas:

char Var0;					// Variable que no fem servir des de l'assemblador
__bank(0) char Var1;				// Variable que forcem perquè estigui al banc 0

Però si el compilador creu que és millor que aquella variable estigui al banc 1 (o al 2) serà per algun motiu. Una alternativa més flexible és fer un emmascarament per tal que les adreces que posem en les instruccions d'assemblador siguin sempre de set bits, com en l'exemple següent:

	asm("movlw 80");			// Valor 80 decimal
	asm("movwf (_Var1&7fh)");		// Equival a 0b01111111

Fent això amb totes les variables (no ho cal fer amb les adreces dels registres) no tindrem errors per culpa d'adreces de vuit o nou bits. Per tant, podem escriure la taula anterior d'aquesta forma:

Tipus Nom en C Bytes Nom en ASM Comentari
Char Var1 1 (_Var1&7fh)
Unsigned short Var2 2 (_Var2&7fh) Byte menys significatiu
((_Var2+1)&7fh) Byte més significatiu
Char Var3[2] 2 · 1 (_Var3&7fh) Var3[0]
((_Var3+1)&7fh) Var3[1]

Tenim dues maneres de posar les etiquetes per a les instruccions goto.

	asm("Bucle:");				// Etiqueta sola
	asm("movf ADRESH,w");
...
	asm("goto Bucle");

	asm("Bucle: movf ADRESH,w");		// Etiqueta abans de la instrucció
...
	asm("goto Bucle");

En programes llargs, pot passar que el nostre tros de programa en assemblador caigui a la segona pàgina de la memòria de programa. Si és així, el compilador ens pot donar un error si intenta posar una adreça de la segona pàgina com a paràmetre de les instruccions goto o call. Per això, és convenient emmascarar l'adreça de l'etiqueta.

Si la instrucció on cal emmascarar forma part de la mateixa funció, és quasi segur que les dues instruccions estaran a la mateixa pàgina i, per tant, només cal emmascarar:

	asm("Bucle:");
	asm("movf ADRESH,w");
...
	asm("goto (Bucle&7ffh)");

Si, en canvi, les instruccions estan en funcions diferents es farà imprescindible seleccionar el banc abans de fer el salt:

	asm("pagesel Func");
	asm("call (Func&7ffh)");

A continuació hem posat, a tall d'exemple, el programa de l'exemple EA però amb un petit tros en assemblador.

#pragma config FOSC = INTRCIO, WDTE = OFF, PWRTE = OFF, MCLRE = OFF, CP = OFF
#pragma config CPD = OFF, BOREN = OFF, IESO = OFF, FCMEN = OFF
#include <xc.h>					// Carrega el fitxer de funcions necessari per al compilador XC8
char Valor;					// Valor llegit
void main (void){
	TRISC = 0b00000000; 			// Posa el port C com a sortida
	TRISAbits.TRISA0 = 1;			// La pota RA0 és d'entrada
	ANSEL = 0;				// Desactiva totes les entrades analògiques
	ANSELH = 0;	
	ANSELbits.ANS0 = 1;			// I ara activa la del potenciòmetre (AN0)
	ADCON1 = 0b00010000;			// Posa el conversor a 1/8 de la freqüència
	ADCON0 = 0b00000001;			// Activa el conversor connectat a AN0
						// amb el resultat justificat per l'esquerra
	while (1){				// Bucle infinit
		ADCON0bits.GO = 1;		// Posa en marxa el conversor
		while (ADCON0bits.GO == 1)	// Mentre no acabi
			;    			// ens esperem
		asm("banksel ADRESH");		// Banc on està ADRESH
		asm("movf ADRESH,w");		// Agafem la lectura del conversor
		asm("banksel _Valor");		// Banc on està la variable
		asm("movwf (_Valor&7fh)");	// Ho guarda a la variable
		PORTC = Valor/16;		// Copiem el resultat, dividit per 16, als LED
        _delay(200000);	    			// Retard per permetre la visualització
	}
}

 

 

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