Programació en C del PIC 16F690

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

Bàscula electrònica

Programa del grup 5

En aquest cas van considerar que la bàscula tenia un límit màxim de 8 kg. L'usuari pot indicar el número de persones i la bàscula li indicarà el pes mesurat i també el pes per persona.

El polsador BTN0 serveix per activar i desactivar la bàscula. El polsador BTN1 guarda el pes actual com a tara. El polsador BTN2 serveix per canviar d'unitats entre g, kg, lb (lliura avoirdupois) i N. El polsador BTN3 permet emmagatzemar un valor de pes per a futura consulta i el BTN4 recupera el valor guardat. Finalment, el polsador BTN5 canvia el signe decimal entre punt i coma.

El programa és el següent:

/*
 * File:   Balança.c
 * Author: Iñaki
 *
 * Created on 16 de octubre de 2017, 22:26
 */
#pragma config FOSC = INTRCIO, WDTE = OFF, PWRTE = OFF, MCLRE = OFF, CP = OFF
#pragma config CPD = OFF, BOREN = OFF, IESO = OFF, FCMEN = OFF
#include "pic16f690.h"				// Carrega el fitxer d'adreces i paràmetres del PIC 16F690
#include <xc.h>					// Carrega el fitxer de funcions necessari per al compilador XC8
#include <string.h>
#define Polsador   RA3		// Li assigna un nom a l'adreça del polsador
#define _XTAL_FREQ  4000000			// La freqüència del rellotge és 4 MHz
// Declarem variables que utilitzarem
char Polsad;					// Polsador que s'ha premut
bit ON;						// Balança encesa
char uni=0;					// Unitats en que es mesura (0=kg;1=g;2=lb;3=N)
unsigned short long pes=0;			// Massa en g
unsigned short long Lectura;			// Valor llegit del sensor
unsigned short long L0;				// Valor de pes 0
char Port;					// Bits del port C
						// RC0 a RC3 són els LED
						// RC4 és DAT (DOUT del sensor)
						// RC6 és CLK (PD_SCK del sensor)
						// RC5 és on hi ha el brunzidor
unsigned short long marge=200000;		// Marge prou grans per evitar números
						// negatius en la lectura de pes
bit test=0;					// Variable per determinar si es troba en un cas
						// excepcional (0=normal;1=excepció)
int error=0;					// Tipus d'error (0=cap error; 1=sobrepes;
						// 2=cal realitzar una tara)
bit borra=0;					// Variable que indica si s'ha d'esborrar la
						// pantalla (0=False;1=True)
unsigned short long memoria=0;			// Variable que emmagatzema el valor a mostrar en
						// cas de premer el botó corresponent
bit coma=0;					// Quin tipus de signe de spearacó
						// decimal utilitzar (0='.'; 1=',')
unsigned short long pesm=0;			// Variable que emmagatzema la massa minima detectada
						// per tal de fer comparació per sobrepes
						// Declarem les funcions que utilitzarem
unsigned short long LlegirHX(void);		// Lectura del sensor
unsigned short long conver(unsigned short long Lectura);
						// Converteix el valor obtingut pel sensor a grams
						// i defineix els límits
char Premut(void);				// Funció de lectura dels polsadors
char decimal(void);				// Insereix '.' o ','
void encesa(void);				// Funció amb missatge a la pantalla al encendre
void apaga(void);				// Funció amb missatge a la pantalla al apagar
void E00(void);					// Funcióqueposa a 0 la pantalla
void Escriu(unsigned short long Valor);		// Escriu un valor de 24 bits a la pantalla
void EnviaL(char Caracter);			// Envia un caràcter ASCII
void Esborra(void);				// Esborra la pantalla i posa el cursor a l'inici
void Cursor(char Filera, char Columna);		// Posiciona el cursor (filera 1 a 2
						// i columna 1 a 32, segons pantalla)
void unis(void);				// Afegeix darrere del valor pesat les unitats
						// en que està mesurat
void Tara(unsigned short long Lectura);		// Actualitza el valor a partir el qual es mesura
						// la diferència de pes
void TocaNota(char ValPR2, char ValCCPR1L, char ValDC1B);
						// Fa sonar a una certa tonalitat depenent
						// dels valors proporcionats
void TN(char nota[]);				// Fa sonar tonalitats properes a les notes especificades
void main(void) {
	ON=1;					// La balança començarà apagada
	ANSEL = 0b00000101;			// Configura AN0 i AN2 com entrada analògica
	ANSELH = 0;				// Desactiva les altres entrades analògiques
	TRISC = 0b00110000;			// Definim com volem les E/S del port C
	TRISB = 0b00000000;			// Tot el port B és de sortida
	TRISA = 0xFF;				// Posa RA3 com a entrada
	TXSTAbits.BRGH = 1;			// Configuració de velocitat
	BAUDCTLbits.BRG16 = 0;			// Paràmetre de velocitat de 8 bits
	SPBRG = 25;				// Velocitat de 9600 baud
	TXSTAbits.SYNC = 0;			// Comunicació asíncrona
	TXSTAbits.TX9 = 0;			// Comunicació de 8 bits
	RCSTAbits.SPEN = 1;			// Activa comunicació sèrie
	TXSTAbits.TXEN = 1;			// Activa comunicació
	ADCON1 = 0b00010000;			// Posa el conversor a 1/8 de la freqüència
	ADCON0 = 0b00001001;			// Activa el conversor A/D connectat a AN2
	PORTB = 0;				// Desactiva tots els bits del port B
	Port = 0;				// Desactiva tots els bits de Port
						// RC5 (sortida del PWM), de moment, com a entrada
	PORTC = 0;				// Desactiva les sortides del port C
	CCP1CON = 0b00001100;			// Configura el PWM, bits P1M (bits 7-6) a 00 mode senzill
						// DC1B = 11 (bits 5-4) els dos bits de menys pes són 0
						// CCP1M = 11xx en mode senzill els bit 0 i 1 no afecten
						// Ho posa com a configuració del PWM
	CCPR1L = 49;				// Valor que correspon a un cicle del 35 % a 440 Hz
						// Registre que ens dona l'amplada de tON
	PIR1bits.TMR2IF = 0;			// Desactiva el bit d'interrupció del Timer 2
	T2CON = 0b00000011;			// Configura el Timer 2
						// bits T2KCPS (bits 1-0) a 11 prescalat de 16
						// bit 2 (TMR2ON) a 0, Timer aturat
						// Postscaler TOUTPS (bits 6-3) no afecten al PWM
	while (1){
		unsigned char Compta = 0;	// Variable local per comptar
		if (Polsador==0){		// Si el polsador s'activa
			int mante=1;		// Fins que no canvii,
			while (mante){		// es mantindra dins del bucle
				if (Polsador){	// Si es deixa anar abans de temps
					mante=0;		// surt del bucle
				} else{
					Compta++;		// Si no contunua comptant
					_delay(1000);		// Espera 1000 cicles
				}
				if (Compta>4){	// Si el comptador arriba a 5
					if (ON){		// Si està encès,
						ON=0;		// apaga
						apaga();
					} else{			// Si està apagat,
						ON=1;		// encen
						encesa();	// executa la funcio
						Lectura = LlegirHX();
						// Emmagatzema el valor actual en Lectura
						Tara(Lectura);	// actualitza
						pesm=L0;	// Fixa la massa minima amb el pes actual
					}
					while(Polsador==0){
						;		// Espaera fins que es deixi anar el posador
					}
					mante=0;		// Després d'haver acabat surt del bucle
				}
			}
		}
		if (ON){			// Si està encès
			RC0=1;	// Encen el LED 1
			Polsad=Premut();	// Actualitza quin polsador està premut
			Lectura = LlegirHX();	// Llegeix el valor del sensor HX711
			if (Lectura<pesm){	// Si la lectura actual és menor
						// que el pes minim detectat fins ara,
				pesm=Lectura;	// actualitza
			}
			pes=conver(Lectura);	// Converteix la lectura feta a grams
			if (Polsad==2){		// Si el polsador 2 està activat,
				uni++;		// passsa a la seguent unitat.
				if (uni==4){	// Si ja no hi ha mes,
					uni=0;	// torna a la primera
				}
			} else{
				if (Polsad==1){			// Si el polsador 1 està activat,
					Tara(Lectura);		// actualitza la tara
				} else{
					if(Polsad==3){		// Si el polsador 3 està activat,
						memoria=pes;	// actualitza la memoria
						Cursor(2,1);	// Ubica el cursor al començament
								// de la segona linia
						EnviaL('S');	// Escriu
						EnviaL('a');	// Escriu
						EnviaL('v');	// Escriu
						EnviaL('i');	// Escriu
						EnviaL('n');	// Escriu
						EnviaL('g');	// Escriu
						EnviaL('.');	// Escriu
						EnviaL('.');	// Escriu
						EnviaL('.');	// Escriu
						RC2=1;		// encen el LED 3
						__delay_ms(500);		// epsera 0,5s
						Esborra();	// deixa la pantalla en blanc
						RC2=0;		// apaga el LED 3
					} else{
						if (Polsad==4){	// Si el polsador 4 està activat,
							Escriu(memoria);	// mostra en pantalla el
										// valor emmagatzemat 
							unis();			// escriu les unitats
										// corresponents 
							RC1=1;	// encen el LED 2
							__delay_ms(2000);	// espera 2s
							RC1=0;	// apaga el LED 2
							Esborra();		// deixa la pantalla en blanc
						} else{
							if (Polsad==5){		// Si el polsador 2
										// està activat,
								coma=!coma;	// canvia tipus
							}
						}
					}
				}
			}
			RC0=1;	// torna a encendre el LED 1
			Escriu(pes);		// escriu el pes o l'error
			if (!(test)){		// si no hi ha hagut error,
				unis();		// afegeix les unitats
			}
			if (borra){		// si s'ha d'esborrar:
				Esborra();	// esborra
				borra=0;	// i descativa l'ordre d'esborrar
			}
			Cursor(1,1);		// ubica el cursor al començament de la pantalla
			RC0=1;	// torna a encendre el LED 1
			__delay_ms(100);	// Retard de 0,1 s
			RC0=1;	// torna a encendre el LED 1
		} else{				// Si està apagat
			RC0=0;	// apaga el LED 1
		}
	}
}
char Premut(void) {
	char Pols = 0;
	ADCON0bits.GO = 1;			// Posa en marxa el conversor
	while (ADCON0bits.GO == 1)		// Mentre no acabi
		;				// ens esperem
	if (ADRESH < 220 && ADRESH > 200) {
		Pols = 1;			// Comprova polsador 1
	}
	if (ADRESH < 194 && ADRESH > 174) {
		Pols = 2;			// Comprova polsador 2
	}
	if (ADRESH < 163 && ADRESH > 143) {
		Pols = 3;			// Comprova polsador 3
	}
	if (ADRESH < 90 && ADRESH > 70) {
		Pols = 4;			// Comprova polsador 4
	}
	if (ADRESH < 55 && ADRESH > 35) {
		Pols = 5;			// Comprova polsador 5
	}
	return Pols;
}
void encesa(void){
	Esborra();				// Esborra la pantalla i posa el cursor a l'inici
	EnviaL('O');				// Escriu
	EnviaL('N');				// Escriu
	TN("do");				// Toca do
	TN("re#");				// Toca re#
	TN("fa");				// Toca fa
	__delay_ms(300);			// Espera 0,3s 
	TN("do");				// Toca do
	TN("re#");				// Toca re#
	TN("fa#");				// Toca fa#
	TN("fa");				// Toca fa
	__delay_ms(200);			// Espera 0,2s
	TN("do");				// Toca do
	TN("re#");				// Toca re#
	TN("fa");				// Toca fa
	__delay_ms(200);			// Espera 0,2s 
	TN("re#");				// Toca re#
	__delay_ms(20);				// Espera 0,02s
	TN("do");				// Toca do
	//_delay(1000000);			// Espera
	Esborra();				// Esborra la pantalla i posa el cursor a l'inici
}
void apaga(void){
	Esborra();				// Esborra la pantalla i posa el cursor a l'inici
	EnviaL('O');				// Escriu
	EnviaL('F');				// Escriu
	EnviaL('F');				// Escriu
	TN("do#");				// Toca do
	_delay(1000000);			// Espera
	Esborra();				// Esborra la pantalla i posa el cursor a l'inici
}
unsigned short long LlegirHX(void) {
	unsigned short long Buffer;		// Valor rebut
	Port = Port & 0b10111111;		// Desactiva PD_SCK
	PORTC = Port;
	for (int j = 1; j <= 24; j++){		// 24 bits
		Port = Port | 0b01000000;	// Activa PD_SCK
		PORTC = Port;
		Buffer = Buffer << 1;		// Rodem els bits per situar el següent
						// a la dreta hi quedarà un zero
		if (RC4 == 1) {	// Si DOUT està activat, posem un 1
			Buffer = Buffer | 0b00000001;
		}
		Port = Port & 0b10111111;	// Desactiva PD_SCK
		PORTC = Port;
		__delay_us(3);			// Espera 3 us per fer els polsos de la mateixa amplada
	}
	Port = Port | 0b01000000;		// Activa PD_SCK per dir-li el guany
	PORTC = Port;
	__delay_us(6);				// Espera 6 us per fer els polsos de la mateixa amplada
	Port = Port & 0b10111111;		// Desactiva PD_SCK
	PORTC = Port;
	return Buffer;				// Retorna el valor
}
void EnviaL(char Caracter) {
	TXREG = Caracter;			// Agafa el caràcter i l'envia
	_delay(5);				// Donem temps
	while (PIR1bits.TXIF == 0)		// Esperem que s'acabi d'enviar
		;				// No fem res
}
void Esborra(void) {
	EnviaL(254);				// Caràcter de control
	EnviaL(1);				// Esborra la pantalla i posa el cursor a l'inici
}
void Cursor(char Filera, char Columna) {
	char Posicio = 0;			// Variable per a calcular la posició
	if (Filera == 2) {
		Posicio = 64;			// La primera columna de la segona fila és 64;
	}
	if (Columna > 0 && Columna < 33) {	// Comprovem que sigui un valor raonable
		Posicio = Posicio + Columna;	// Sumem les adreces
		Posicio = Posicio - 1;		// Restem 1 perquè numera des de 0
	}
	Posicio = Posicio + 128;		// Posa el bit de posicionat a 1
	EnviaL(254);				// Control de la posició del cursor
	EnviaL(Posicio);			// Canvia el cursor de lloc
}
void unis(void){
	if (uni==0){				// Si són kg
		Cursor(1,15);			// ubica el cursor al començament
		EnviaL('k');			// Escriu
		EnviaL('g');			// Escriu
	} else{
		if (uni==1){			// Si son g
		Cursor(1,15);			// ubica el cursor al començament
		EnviaL(' ');			// Escriu
		EnviaL('g');			// Escriu
		} else{
			if (uni==2){		// Si son lb
				Cursor(1,15);	// ubica el cursor al començament
				EnviaL('l');	// Escriu
				EnviaL('b');	// Escriu
			} else{
				if (uni==3){	// Si son N
					Cursor(1,15);			// ubica el cursor al començament
					EnviaL(' ');			// Escriu
					EnviaL('N');			// Escriu
				}
			}
		}
	}
}
unsigned short long conver(unsigned short long Lectura){
	unsigned short long a=Lectura+marge;	// Assegurar que sigui un numero positiu
	unsigned short long r;			// Lectura en g
	unsigned short long h;			// auxiliar
	if (a>L0){				// Si la lectura està per sobre de 0
		r=(a-L0)/214;			// massa
		h=(a-pesm)/214;			// massa sense Tara
		if (h>8000){			// si h passa dels 8 kg
			test=1;			// error
			error=1;		// sobrepes
			RC0=1;	// Encen el LED 1
			RC1=1;	// Encen el LED 2
			RC2=1;	// Encen el LED 3
			RC3=1;	// Encen el LED 4
			TN("sol");		// Toca do
			return 9000;
		} else{				// si h es menor de 8 kg
			if (test){		// Si hi havia error
				test=0;		// desactiva error
				borra=1;	// i esborra el missatge
				if (error){	// Si hi havia sobrepes
					RC1=0;		// Apaga el LED 2
					RC2=0;		// Apaga el LED 3
					RC3=0;		// Apaga el LED 4
				}
			}
			return r;		// envia massa
		}
	} else{					// Si la lectura està per sota de 0
		r=(L0-a)/214;			// calcula la massa "negativa"
		if (r<1){			// si es menor d'1 g
			if (test){		// (i hi havia error
				test=0;		// desactiva error
				borra=1;	// i esborra el missatge)
				if (error){	// Si hi havia sobrepes
					RC1=0;		// Apaga el LED 2
					RC2=0;		// Apaga el LED 3
					RC3=0;		// Apaga el LED 4
				}
			}
			return 0;		// envia 0
		} else{				// si es "molt negativa"
			error=2;		// massa insuficient
			test=1;			// error
			TN("do");		// Toca sol3
			return 9000;
		}
	}
}
void Tara(unsigned short long Lectura){
	L0=Lectura+marge;			// Assegurar que sigui un numero positiu
}
void Escriu(unsigned short long Valor){
	char Digits[5];				// Aquí guardarem els 5 dígits
	unsigned short long Result;		// Variable de treball
	if (uni==2 && !(test)){			// Si està en lb i no hi ha algun error
		Valor=Valor*1000/453.59;	// converteix a lb
	} else{
		if (uni==3 && !(test)){		// Si esta en N i no hi ha algun error
			Valor=Valor*9.81;	// converteix a N
		}
	}
	Result = Valor % 10;
	Digits[0] = (char) Result;		// Unitats
	Valor = Valor / 10;
	Result = Valor % 10;
	Digits[1] = (char) Result;		// Desenes
	Valor = Valor / 10;
	Result = Valor % 10;
	Digits[2] = (char) Result;		// Centenes
	Valor = Valor / 10;
	Result = Valor % 10;
	Digits[3] = (char) Result;		// Milers
	Valor = Valor / 10;
	Result = Valor % 10;
	Digits[4] = (char) Result;		// Desenes de miler
	if (test){				// Si hi ha algun error
		Esborra();			// Esborra
		EnviaL('E');			// Escriu
		EnviaL('r');			// Escriu
		EnviaL('r');			// Escriu
		EnviaL('o');			// Escriu
		EnviaL('r');			// Escriu
		EnviaL(',');			// Escriu
		Cursor(2,1);			// Ubica el cursor al començament de la segona linia
		if (error==1){			// Si hi ha sobrepes
			EnviaL('E');		// Escriu
			EnviaL('n');		// Escriu
			EnviaL('o');		// Escriu
			EnviaL('u');		// Escriu
			EnviaL('g');		// Escriu
			EnviaL('h');		// Escriu
			EnviaL(' ');		// Escriu
			EnviaL('m');		// Escriu
			EnviaL('a');		// Escriu
			EnviaL('s');		// Escriu
			EnviaL('s');		// Escriu
			EnviaL('.');		// Escriu
		} else{				// Si no hi ha prou pes
			EnviaL('N');		// Escriu
			EnviaL('e');		// Escriu
			EnviaL('e');		// Escriu
			EnviaL('d');		// Escriu
			EnviaL(' ');		// Escriu
			EnviaL('m');		// Escriu
			EnviaL('o');		// Escriu
			EnviaL('r');		// Escriu
			EnviaL('e');		// Escriu
			EnviaL(' ');		// Escriu
			EnviaL('m');		// Escriu
			EnviaL('a');		// Escriu
			EnviaL('s');		// Escriu
			EnviaL('s');		// Escriu
			EnviaL('.');		// Escriu
		}
	} else{								// Si no hi ha error
		if (uni==0){						// Si esta en kg
			for (int j = 4; j >= 0; j--){			// 8 dígits
				Digits[j] = Digits[j] + '0';		// Li sumem el codi ASCII de 0
				if (j==2) {				// Posem espais per separar
					EnviaL(decimal());		// Espai
				}
				EnviaL(Digits[j]);			// Número
			}
		} else{
			if (uni==1){					// Si esta en g
				for (int j = 4; j >= 0; j--){		// 8 dígits
					Digits[j] = Digits[j] + '0';	// Li sumem el codi ASCII de 0
					if (j==2) {			// Posem espais per separar
						EnviaL(' ');		// Espai
					}
					EnviaL(Digits[j]);		// Número
				}
			} else{
				if (uni==2){				// Si esta en lb
					for (int j = 4; j >= 0; j--){	// 5 dígits
						Digits[j] = Digits[j] + '0';
									// Li sumem el codi ASCII de 0
						if (j==2) {		// Posem espais per separar
							EnviaL(decimal());		// Espai
						}
						EnviaL(Digits[j]);	// Número
					}
				} else{
					if (uni==3){			// Si esta en N
						for (int j = 4; j >= 0; j--){		// 5 dígits
							Digits[j] = Digits[j] + '0';
									// Li sumem el codi ASCII de 0
							if (j==2) {	// Posem espais per separar
								EnviaL(decimal());	// Espai
							}
							EnviaL(Digits[j]);		// Número
						} 
					}
				}
			}
		}	
	}
}
char decimal(void){
	if (coma){				// Si es ','
		return ',';			// Escriu ','
	}
	else{					// Si es '.'
		return '.';			// Escriu '.'
	}
}
void TocaNota(char ValPR2, char ValCCPR1L, char ValDC1B) {
	TRISC = 0b00100000;			// Definim com volem les E/S del port C
						// RC5 (sortida del PWM), de moment, com a entrada
	PR2 = ValPR2;				// Carrega PR2
	CCP1CON = CCP1CON & 0b11001111;		// Posa a zero els bits que corresponen a DC1B
	ValDC1B = ValDC1B % 4;			// DC1B va de 0 a 3
	ValDC1B = ValDC1B * 16;			// Desplaça els bits a la posició que els correspon a CCP1CON
	CCP1CON = CCP1CON + ValDC1B;		// Coloca DC1B al seu lloc
	CCPR1L = ValCCPR1L;			// Carrega CCPR1L, registre que ens dona l'amplada de tON
	PIR1bits.TMR2IF = 0;			// Desactiva el bit d'interrupció del Timer 2
	T2CON = 0b00000111;			// Configura el Timer 2
						// bits T2KCPS (bits 1-0) a 11 prescalat de 16
						// bit 2 (TMR2ON) a 1, Timer activat
						// Postscaler TOUTPS (bits 6-3) no afecten al PWM
	while (PIR1bits.TMR2IF == 0)		// Espera l'activació del bit d'interrupció del Timer 2
		;				// Esperem
	TRISC = 0b00000000;			// Posem RC5 (sortida del PWM) com a sortida
	__delay_ms(200);			// Retard de 0,2 s
	TRISC = 0b00110000;			// Posem RC5 (sortida del PWM) com a entrada
						// O sigui, silenci
	__delay_ms(100);			// Retard de 0,2 s
}

void TN(char nota[]){
	if (nota=="do"){
		TocaNota(238,119,2);		// Valor que correspon aproximadament a do3
	}
	if (nota=="re#"){
		TocaNota(200,100,2);		// Valor que correspon aproximadament a re#3
	}
	if (nota=="fa"){
		TocaNota(178,89,2);	 	// Valor que correspon aproximadament a fa3
	}
	if (nota=="fa#"){
		TocaNota(168,84,2); 		// Valor que correspon aproximadament a fa#3
	}
	if (nota=="sol"){
		TocaNota(158,79,2); 		// Valor que correspon aproximadament a sol3
	}
	if (nota=="do#"){
		TocaNota(224,112,2);		// Valor que correspon aproximadament a do#3
	}
	if(nota=="re"){
		TocaNota(212,106,2);		// Valor que correspon aproximadament a re3
	}
	if(nota=="mi"){
		TocaNota(189,95,0); 		// Valor que correspon aproximadament a mi3
	}
	if(nota=="sol#"){
		TocaNota(149,75,0);	 	// Valor que correspon aproximadament a sol#3
	}
	if(nota=="la"){
		TocaNota(141,71,0); 		// Valor que correspon aproximadament a la3
	}
	if (nota=="la#"){
		TocaNota(133,67,0);		// Valor que correspon aproximadament a la#3
	}
	if(nota=="si"){
		TocaNota(126,63,2); 		// Valor que correspon aproximadament a si3
	}
	if(nota=="do4"){
		TocaNota(118,59,2); 		// Valor que correspon aproximadament a do4
	}
}

 

 

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