Programació en C del PIC 18F45K20

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

Exemple IT - Interrupcions

A l'exemple VV hem fet servir un temporitzador per a comptar el temps però era el propi programa que estava pendent de si el temporitzador habia assolit el valor o no. El mateix passava amb el polsador. Ara farem que el temporitzador generi una interrupció quan arribi a zero i el polsador una altra quan sigui premut. Serà a les funcions d'interrupció on canviarem el sentit de gir i farem rodar els LED.

Com aquest microcontrolador admet dos nivells d'interrupció, farem que el polsador sigui una interrupció d'alt nivell i el temporitzador una de nivell baix.

#pragma config FOSC = INTIO67, FCMEN = OFF, IESO = OFF				// CONFIG1H
#pragma config PWRT = OFF, BOREN = SBORDIS, BORV = 30				// CONFIG2L
#pragma config WDTEN = OFF, WDTPS = 32768					// CONFIG2H
#pragma config MCLRE = OFF, LPT1OSC = OFF, PBADEN = ON, CCP2MX = PORTC		// CONFIG3H
#pragma config STVREN = ON, LVP = OFF, XINST = OFF				// CONFIG4L
#pragma config CP0 = OFF, CP1 = OFF, CP2 = OFF, CP3 = OFF			// CONFIG5L
#pragma config CPB = OFF, CPD = OFF						// CONFIG5H
#pragma config WRT0 = OFF, WRT1 = OFF, WRT2 = OFF, WRT3 = OFF			// CONFIG6L
#pragma config WRTB = OFF, WRTC = OFF, WRTD = OFF				// CONFIG6H
#pragma config EBTR0 = OFF, EBTR1 = OFF, EBTR2 = OFF, EBTR3 = OFF		// CONFIG7L
#pragma config EBTRB = OFF							// CONFIG7H
#include "p18f45k20.h"				// Carrega el fitxer d'adreces i paràmetres del PIC 18F45K20
#define FiTimer0 INTCONbits.TMR0IF		// Li assigna un nom al bit que indica el final del Timer 0
unsigned char Valor;  				// Variable de 8 bits sense signe (0 a 255)
bit Premut;					// Estat del polsador
bit Adreta;					// Sentit de gir
						// Definició de les funcions que farem servir 
unsigned char Analogica (void);			// Funció de lectura del conversor
void main (void)
{
						// Inicialització de variables
	Adreta = 0;				// Iniciem girant a l'esquerra
	Premut = 0;				// Suposem el polsador no premut
	Valor = 1;          			// Inicialment posem 1 (activem LED 0)
						// Configuració d'entrades i sortides
	TRISD = 0b00000000;    			// El port D és de sortida
	INTCON2bits.RBPU = 0;			// Habilita el control de resistències de pull-up al port B
	WPUBbits.WPUB0 = 1;			// Activa la resistència de pull-up a RB0
	TRISBbits.TRISB0 = 1;      		// RB0 és entrada
						// Configuració d'interrupció a INT0 (RB0)
	INTCON2bits.INTEDG0 = 0;		// Interrupció quan es desactiva (quan es prem el polsador)
						// INT0 sempre és de prioritat alta, no cal definir-ho
	INTCONbits.INT0IF = 0;			// Inicialitzem amb el bit de control desactivat
	INTCONbits.INT0IE = 1;			// Habilitem la interrupció INT0
						// Inicialització del conversor
	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 = 0;				// Les tensions de referència són les d'alimentació
	ADCON2 = 0b00111000;			// Configuració típica amb justificat per l'esquerra
	ADCON0 = 0b00000001;			// Activa el conversor connectat a AN0
						// Configuració del Timer 0
	FiTimer0 = 0;       			// Aquest bit es posarà a 1 quan el temporitzador acabi
						// cal desactivar-lo des del programa
	INTCON2bits.TMR0IP = 0;			// Definim Timer 0 com una interrupció de baixa prioritat
	INTCONbits.TMR0IE = 1;			// Habilitem la interrupció del Timer 0
	T0CON = 0b00000001;        		// El timer 0 és de 16 bits i compta temps amb pre-escalat de 1/4
	TMR0H = 0;                     		// Fem la precàrrega a zero per començar, primer el registre H
	TMR0L = 0;				// i després el registre L
	T0CONbits.TMR0ON = 1;           	// Fem que Timer 0 comenci a comptar
						// Control de les interrupcions
	RCONbits.IPEN = 1;			// Activa el funcionament de dos nivells d'interrupció
	INTCONbits.GIEL = 1;			// Permet les interrupcions de baix nivell
	INTCONbits.GIEH = 1;			// Permet les interrupcions d'alt nivell
	while (1)				// Inici del bucle de programa
	{
						// El programa només activa els LED,
						// la resta ho fan les interrupcions
		LATD = Valor;			// Copiem el valor al port (als LED)
	}
}
unsigned char Analogica (void)			// Funció de lectura del conversor
{
	ADCON0bits.GO_DONE = 1;           	// Posa en marxa el conversor
	while (ADCON0bits.GO_DONE == 1)		// Mentre no acabi
		;    				// ens esperem
	return ADRESH;                    	// Retornem el resultat llegit
}
void interrupt alt_nivell(void)			// funció d'interrupcions d'alt nivell
{
	if (INTCONbits.INT0IF)			// Comprovem que hi ha interrupció per INT0
	{
		INTCONbits.INT0IF = 0;		// Desactivem el bit que indica la interrupció
		Adreta = ~Adreta;		// Invertim el sentit de gir
	}
}
void interrupt low_priority baix_nivell(void)	// funció d'interrupcions de baix nivell
{
	if  (FiTimer0)				// Comprovem que hi ha interrupció per Timer 0
	{
		FiTimer0 = 0;          		// Tornem a posar el bit a zero
		TMR0H = Analogica();		// Cridem a la funció de lectura del conversor i
						// copiem el resultat llegit com a precàrrega del Timer
		TMR0L = 0;			// El TMR0L segueix a 0 però cal escriure-ho per recarregar TMR0H
		if (Adreta == 0)
			Valor = (Valor << 1)|(Valor >> 7);		// Desplacem els bits a l'esquerra
		if (Adreta == 1)
			Valor = (Valor >> 1)|(Valor << 7);		// Desplacem els bits a la dreta
	}
}

Podem observar que en alguns casos quan premem el polsador sembla que vagi a canviar el sentit de gir però no ho fa. Això pot succeir si quan ja ha canviat el sentit de gir hi ha un rebot del polsador que provoca una nova interrupció. Per evitar el problema, al següent programa comprovem que la situació del polsador és estable. Això ho fem posant un retard petit (100 ms) a l'inici de la funció d'interrupció i després comprovem si el polsador està premut. Si ho està, canviem el sentit.

#pragma config FOSC = INTIO67, FCMEN = OFF, IESO = OFF				// CONFIG1H
#pragma config PWRT = OFF, BOREN = SBORDIS, BORV = 30				// CONFIG2L
#pragma config WDTEN = OFF, WDTPS = 32768					// CONFIG2H
#pragma config MCLRE = OFF, LPT1OSC = OFF, PBADEN = ON, CCP2MX = PORTC		// CONFIG3H
#pragma config STVREN = ON, LVP = OFF, XINST = OFF				// CONFIG4L
#pragma config CP0 = OFF, CP1 = OFF, CP2 = OFF, CP3 = OFF			// CONFIG5L
#pragma config CPB = OFF, CPD = OFF						// CONFIG5H
#pragma config WRT0 = OFF, WRT1 = OFF, WRT2 = OFF, WRT3 = OFF			// CONFIG6L
#pragma config WRTB = OFF, WRTC = OFF, WRTD = OFF				// CONFIG6H
#pragma config EBTR0 = OFF, EBTR1 = OFF, EBTR2 = OFF, EBTR3 = OFF		// CONFIG7L
#pragma config EBTRB = OFF							// CONFIG7H
#include "p18f45k20.h"				// Carrega el fitxer d'adreces i paràmetres del PIC 18F45K20
#define FiTimer0 INTCONbits.TMR0IF		// Li assigna un nom al bit que indica el final del Timer 0
#include <xc.h>					// Carrega el fitxer de funcions
unsigned char Valor;  				// Variable de 8 bits sense signe (0 a 255)
bit Premut;					// Estat del polsador
bit Adreta;					// Sentit de gir
						// Definició de les funcions que farem servir 
unsigned char Analogica (void);			// Funció de lectura del conversor
void main (void)
{
						// Inicialització de variables
	Adreta = 0;				// Iniciem girant a l'esquerra
	Premut = 0;				// Suposem el polsador no premut
	Valor = 1;          			// Inicialment posem 1 (activem LED 0)
						// Configuració d'entrades i sortides
	TRISD = 0b00000000;    			// El port D és de sortida
	INTCON2bits.RBPU = 0;			// Habilita el control de resistències de pull-up al port B
	WPUBbits.WPUB0 = 1;			// Activa la resistència de pull-up a RB0
	TRISBbits.TRISB0 = 1;      		// RB0 és entrada
						// Configuració d'interrupció a INT0 (RB0)
	INTCON2bits.INTEDG0 = 0;		// Interrupció quan es desactiva (quan es prem el polsador)
						// INT0 sempre és de prioritat alta, no cal definir-ho
	INTCONbits.INT0IF = 0;			// Inicialitzem amb el bit de control desactivat
	INTCONbits.INT0IE = 1;			// Habilitem la interrupció INT0
						// Inicialització del conversor
	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 = 0;				// Les tensions de referència són les d'alimentació
	ADCON2 = 0b00111000;			// Configuració típica amb justificat per l'esquerra
	ADCON0 = 0b00000001;			// Activa el conversor connectat a AN0
						// Configuració del Timer 0
	FiTimer0 = 0;       			// Aquest bit es posarà a 1 quan el temporitzador acabi
						// cal desactivar-lo des del programa
	INTCON2bits.TMR0IP = 0;			// Definim Timer 0 com una interrupció de baixa prioritat
	INTCONbits.TMR0IE = 1;			// Habilitem la interrupció del Timer 0
	T0CON = 0b00000001;        		// El timer 0 és de 16 bits i compta temps amb pre-escalat de 1/4
	TMR0H = 0;                     		// Fem la precàrrega a zero per començar, primer el registre H
	TMR0L = 0;				// i després el registre L
	T0CONbits.TMR0ON = 1;          		// Fem que Timer 0 comenci a comptar
						// Control de les interrupcions
	RCONbits.IPEN = 1;			// Activa el funcionament de dos nivells d'interrupció
	INTCONbits.GIEL = 1;			// Permet les interrupcions de baix nivell
	INTCONbits.GIEH = 1;			// Permet les interrupcions d'alt nivell
	while (1)				// Inici del bucle de programa
	{
						// El programa només activa els LED,
						// la resta ho fan les interrupcions
		LATD = Valor;			// Copiem el valor al port (als LED)
	}
}
unsigned char Analogica (void)			// Funció de lectura del conversor
{
	ADCON0bits.GO_DONE = 1;           	// Posa en marxa el conversor
	while (ADCON0bits.GO_DONE == 1)		// Mentre no acabi
		;    				// ens esperem
	return ADRESH;         	          	// Retornem el resultat llegit
}
void interrupt alt_nivell(void)			// funció d'interrupcions d'alt nivell
{
	if (INTCONbits.INT0IF)			// Comprovem que hi ha interrupció per INT0
	{
		_delay(25000);			// Retard de 25000 cicles (aprox. 100 ms)
		INTCONbits.INT0IF = 0;		// Desactivem el bit que indica la interrupció
		if (PORTBbits.RB0 == 0)		// Si el polsador encara està premut
			Adreta = ~Adreta;	// Invertim el sentit de gir
	}
}
void interrupt low_priority baix_nivell(void)	// funció d'interrupcions de baix nivell
{
	if  (FiTimer0)				// Comprovem que hi ha interrupció per Timer 0
	{
		FiTimer0 = 0;         	 	// Tornem a posar el bit a zero
		TMR0H = Analogica();		// Cridem a la funció de lectura del conversor i
						// copiem el resultat llegit com a precàrrega del Timer
		TMR0L = 0;			// El TMR0L segueix a 0 però cal escriure-ho per recarregar TMR0H
		if (Adreta == 0)
			Valor = (Valor << 1)|(Valor >> 7);		// Desplacem els bits a l'esquerra
		if (Adreta == 1)
			Valor = (Valor >> 1)|(Valor << 7);		// Desplacem els bits a la dreta
	}
}

 

 

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