Programació en C del PIC 16F690 amb PICkit 2

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

Sensor de temperatura i humitat

Hem triat el sensor de temperatura i humitat RHT03 de l'empresa Maxdetect.

Sensor de temperatura i humitat

El connexionat del sensor és molt senzill. Té quatre potes, dues són per a l'alimentació, una és la que es connecta amb el microcontrolador i la restant no es connecta enlloc. L'esquema és el següent:

Sensor de temperatura i humitat

Com la mateixa pota serveix d'entrada i de sortida, hi ha moments que és el sensor qui envia senyal i en altres moments és el microcontrolador qui ho fa. Però en algun moment la pota queda sense connexió efectiva i, per això, cal posar-hi una resistència que manté la pota a 5 V si no està treballant.

El sensor pot llegir correctament valors de temperatura entre -40 °C i +80 °C així com humitats relatives entre 0 i 100 % amb una xifra decimal en tots dos casos. El sensor envia una cadena de 40 bits com la següent:

		0000 0010 1000 1100 0000 0001 0101 1111 1110 1110

Els valors s'envien en l'ordre següent:

Ordre Número de bits Contingut Exemple
Bits Decimal Lectura
1 16 Humitat 0000 0010 1000 1100 652 65,2 %
2 16 Temperatura 0000 0001 0101 1111 351 35,1 °C
1000 0000 0110 0101 101 -10,1 °C
3 8 Suma de comprovació 1110 1110

El primer bit de la temperatura no forma part del valor sinó que indica el signe. Quan la temperatura és positiva aquest bit és zero i si és negativa el bit és 1. El valor es calcula amb els altres 15 bits.

La suma de comprovació es calcula sumant els quatre bytes de la temperatura i la humitat. El resultat d'aquesta suma (prescindim de si hi ha un novè bit) és el valor que s'envia. Per exemple en el cas següent

		0000 0010 1000 1100 0000 0001 0101 1111 1110 1110

seria:

                0000 0010                
1000 1100
0000 0001
0101 1111
1110 1110

La seqüència d'un enviament seria la de la figura seüent:

Sensor de temperatura i humitat

Abans i després de l'enviament, el microcontrolador i el sensor estan funcionant com a entrades i és la resistència qui manté l'estat a 5 V (tram en color blau).

Quan el microcontrolador vol llegir el sensor (color taronja) ha de posar-se en mode sortida i enviar un zero durant 1 ms o més. Tot seguit es posa a 1 i torna a passar a mode entrada per esperar que el sensor respongui.

El sensor envia primer un senyal de que inicia la transmissió de dades (color rosa) consistent en un 0 i un 1 de 80 μs cada un. Tots els temps que aquí s'indiquen són aproximats.

Després el sensor envia els 40 bits (color verd). Un bit a 0 està format per un valor 0 d'uns 50 μs seguit d'un valor 1 d'uns 26 a 28 μs. En canvi, un bit a 1 està format per un valor 0 d'uns 50 μs seguit d'un valor 1 d'uns 70 μs.

Un cop ha acabat la transmissió, el sensor es posa a 0 un moment i després torna a passar a mode recepció.

A continuació hi ha un programa de prova del sensor. El programa llegeix el valor del sensor (connectat a RA5) i mostra a una pantalla sèrie la humitat i la temperatura. També mostra la suma de comprovació. Per assegurar que la lectura es fa correctament s'ha optat per fer anar el rellotge del microcontrolador a una velocitat més alta durant el temps de fer la lectura del sensor i després es torna a la velocitat normal.

#pragma config FOSC = INTRCIO, WDTE = OFF, PWRTE = OFF, MCLRE = OFF, CP = OFF, 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
#define _XTAL_FREQ  4000000			// La freqüència del rellotge és 4 MHz
#define Sens   PORTAbits.RA5			// Li assigna un nom a l'adreça del sensor
unsigned int Valor;				// Variable de treball
unsigned int Error;				// Error de lectura
unsigned int Lectura[3];			// Valor llegit
						// Lectura[0] és la suma de comprovació
						// Lectura[1] és la temperatura
						// Lectura[2] és la humitat
char Digits[5];					// Vector amb el número dígit a dígit
						// Digits[0] és el decimal
char Negatiu;					// Guarda si el valor de la temperatura és negatiu o positiu
						// Definició de les funcions que farem servir 
char Sensor(void);				// Lectura del sensor. Dóna 0 si la suma de control és correcta
void EnviaL(char Caracter);			// Envia un caràcter
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 main (void) {
	ANSEL = 0b00000001;			// Configura AN0 com entrada analògica
	ANSELH = 0;				// Desactiva les altres entrades analògiques
	TRISC = 0;				// Tot el port C és de sortida
	TRISB = 0;				// Tot el port B és de sortida
	TRISA = 0b11011111;			// Tot el port A és d'entrada excepte, de moment, RA5
	PORTA = 0b00100000;			// RA5 a 1
	T1CON = 0b00100001;			// Configuració de Timer1
						// Com a temporitzador basat en rellotge
						// 10 - Factor d'escala de 4
						// TMR1L s'incrementarà cada 2 us
	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ó
	__delay_ms(1000);			// Retard d'1 s
	Esborra();				// Esborra la pantalla i posa el cursor a l'inici
	while (1) {				// Inici del bucle de programa
		OSCCON = 0b01111000;			// IRCF = 111, rellotge a 8 MHz
		Error = Sensor();
		OSCCON = 0b01101000;			// IRCF = 110, rellotge a 4 MHz
		if (Error == 0) {		// Llegeix el sensor i si és correcte segueix
			Valor = Lectura[2];	// Humitat
			Digits[0] = Valor % 10;	// Unitats
			Valor = Valor / 10;
			Digits[1] = Valor % 10;	// Desenes
			Valor = Valor / 10;
			Digits[2] = Valor % 10;	// Centenes
			Valor = Valor / 10;
			Digits[3] = Valor % 10;	// Milers
			Digits[4] = Valor / 10;	// Desenes de milers
			for (int j = 0; j < 5; j++){		// 5 dígits
				Digits[j] = Digits[j] + '0';	// Li sumem el codi ASCII de 0
			}			// Això és la humitat multiplicada per 10
			if (Digits[4] == '0') {			// Mirem si el primer dígit és 0
				Digits[4] = ' ';		// Si ho és, hi posem un espai
				if (Digits[3] == '0') {		// I mirem si ho és el segon
					Digits[3] = ' ';	// Si ho és, hi posem un espai
					if (Digits[2] == '0') {			// I mirem si ho és el tercer
						Digits[2] = ' ';			// Si ho és, hi posem un espai
					}	// Els dos darrers zeros els mostrarem sempre
				}
			}
			Cursor(1, 1);		// Posició
			for (int j = 4; j > 0; j--){		// 4 dígits (el 5è és el decimal
				EnviaL(Digits[j]);		// Número
			}
			EnviaL(',');		// Coma
			EnviaL(Digits[0]);	// Número
			EnviaL(' ');		// Espai
			EnviaL('%');		// Tant per cent
			Valor = Lectura[1];	// Temperatura
			if (Valor >= 32768) {	// Si el bit més significatiu està activat és negatiu
				Negatiu = 1;			// És negatiu
				Valor = Valor -32768;		// Agafem el valor absolut
			} else {
				Negatiu = 0;			// És positiu
			}			
			Digits[0] = Valor % 10;	// Unitats
			Valor = Valor / 10;
			Digits[1] = Valor % 10;	// Desenes
			Valor = Valor / 10;
			Digits[2] = Valor % 10;	// Centenes
			Valor = Valor / 10;
			Digits[3] = Valor % 10;	// Milers
			Digits[4] = Valor / 10;	// Desenes de milers
			for (int j = 0; j < 5; j++){		// 5 dígits
				Digits[j] = Digits[j] + '0';	// Li sumem el codi ASCII de 0
			}			// Això és la temperatura multiplicada per 10
			if (Digits[4] == '0') {			// Mirem si el primer dígit és 0
				Digits[4] = ' ';		// Si ho és, hi posem un espai
				if (Digits[3] == '0') {		// I mirem si ho és el segon
					Digits[3] = ' ';	// Si ho és, hi posem un espai
					if (Digits[2] == '0') {			// I mirem si ho és el tercer
						Digits[2] = ' ';			// Si ho és, hi posem un espai
					}	// Els dos darrers zeros els mostrarem sempre
				}
			}
			if (Negatiu) {
				Digits[4] = '-';		// Si és negatiu, posem el -
			}
			Cursor(2, 1);		// Posició
			for (int j = 4; j > 0; j--){		// 4 dígits (el 5è és el decimal)
				EnviaL(Digits[j]);		// Número
			}
			EnviaL(',');		// Coma
			EnviaL(Digits[0]);	// Número
			EnviaL(' ');		// Espai
			EnviaL(0b11011111);	// Graus
			EnviaL('C');		// Lletra
			Valor = Lectura[0];	// Suma de comprovació
			Digits[0] = Valor % 10;	// Unitats
			Valor = Valor / 10;
			Digits[1] = Valor % 10;	// Desenes
			Valor = Valor / 10;
			Digits[2] = Valor % 10;	// Centenes
						// No cal seguir perquè només és un byte
			for (int j = 0; j < 3; j++){		// 3 dígits
				Digits[j] = Digits[j] + '0';	// Li sumem el codi ASCII de 0
			}
			if (Digits[3] == '0') {			// Mirem si el primer dígit és 0
				Digits[3] = ' ';		// Si ho és, hi posem un espai
				if (Digits[2] == '0') {		// I mirem si ho és el segon
					Digits[2] = ' ';	// Si ho és, hi posem un espai
				}		// Els darrer zero el mostrarem sempre
			}
			Cursor(1, 11);		// Posició
			for (int j = 2; j >= 0; j--){		// 3 dígits
				EnviaL(Digits[j]);		// Número
			}
		} else {
			Cursor(1, 1);		// Posició
			EnviaL('E');
			EnviaL('r');
			EnviaL('r');
			EnviaL('o');
			EnviaL('r');
			EnviaL(' ');
			Error = Error + '0';	// Passa el valor a ASCII
			EnviaL(Error);		// Número
		}
		__delay_ms(1000);		// Retard d'1 s
	}
}
char Sensor(void) {
	char Bytes[5];				// Vector per a guardar els cinc bytes que envia el sensor
	char Temps;				// Guarda el valor de TMR1L
	for (int j = 0; j < 5; j++){		// 5 bytes
		Bytes[j] = 0;			// Posem el vector a zero
	}
	TRISA = 0b11011111;			// Tot el port A és d'entrada excepte, de moment, RA5
	PORTA = 0b00000000;			// RA5 a 0
	__delay_ms(2);				// Retard de 2 ms
	PORTA = 0b00100000;			// RA5 a 1
	__delay_us(15);				// Retard de 15 us
						// Un cop activat, esperem la resposta
	TRISA = 0b11111111;			// Tot el port A és d'entrada, inclosa RA5
						// Esperem a rebre el pols d'inici
						// Primer un L d'uns 80 us
						// Per començar, s'ha de desactivar l'entrada
						// i s'ha de reactivar al cap d'entre 70 i 90 us
	while (Sens)				// S'ha desactivat l'entrada?
		;				// No, doncs esperem
	TMR1H = 0;
	TMR1L = 0;				// Sí, doncs comencem a comptar el temps
	while ((!Sens) && (TMR1L < 45))		// Esperem que s'activi l'entrada
		;				// O passin més de 90 us
	Temps = TMR1L;				// Agafa el valor de TMR1L
	TMR1H = 0;
	TMR1L = 0;				// Torna a començar a comptar el temps
	if (Temps > 45) {			// És més gran
		return 1;			// Error 1 - L inicial massa llarg
	}
	if (Temps < 35) {			// Mirem que no sigui menor que 70 us
		return 2;			// Error 2 - L inicial massa curt
	}
						// L inicial ja està
	while (Sens && (TMR1L < 45))		// Esperem a que es desactivi l'entrada
		;				// O passin més de 90 us
	Temps = TMR1L;				// Agafa el valor de TMR1L
	TMR1H = 0;
	TMR1L = 0;				// Torna a començar a comptar el temps
	if (Temps > 45) {			// És més gran
		return 3;			// Error 3 - H inicial massa llarg
	}
	if (Temps < 35) {			// Mirem que no sigui menor que 70 us
		return 4;			// Error 4 - H inicial massa curt
	}
						// H inicial ja està
						// Ara hem de rebre els bits
	TMR1H = 0;
	TMR1L = 0;				// Torna a començar a comptar el temps
	for (int j = 0; j < 5; j++){		// 5 bytes
		for (int k = 0; k < 8; k++){	// 8 bits a cada byte
			Bytes[j] = 2 * Bytes[j];		// Rodem a l'esquerra el bit anterior
								// per deixar lloc al nou
								// Si no n'hi havia cap no canvia res
								// ja que estava a zero
						// Esperem a rebre un bit
						// Primer un L d'uns 80 us
						// Ara l'entrada està a zero
						// S'ha d'activar al cap d'entre 10 i 90 us
			while ((!Sens) && (TMR1L < 45))		// Esperem que s'activi l'entrada
				;		// O passin més de 90 us
			Temps = TMR1L;		// Agafa el valor de TMR1L
			TMR1H = 0;
			TMR1L = 0;		// Torna a començar a comptar el temps
			if (Temps > 45) {	// És més gran
				return 5;	// Error 5 - Valor L del bit massa llarg
			}
			if (Temps < 5) {	// Mirem que no sigui menor que 10 us
				return 6;	// Error 6 - Valor L del bit massa curt
			}
						// Ja tenim el valor L del bit. Ara esperem un H
			while (Sens && (TMR1L < 40))		// Esperem a que es desactivi l'entrada
				;		// O passin més de 80 us
			Temps = TMR1L;		// Agafa el valor de TMR1L
			TMR1H = 0;
			TMR1L = 0;		// Torna a començar a comptar el temps
			if (Temps > 40) {	// És més gran
				return 7;	// Error 7 - Valor H del bit massa llarg
			}
			if (Temps < 6) {	// Mirem que no sigui menor que 12 us
				return 8;	// Error 8 - Valor H del bit massa curt
			}
						// Ja hem comprovat que no sigui massa curt ni massa llarg
						// Si és més petit que 38 us és un pols curt, o sigui un 0
						// Si és curt no cal fer res, el 0 ja hi és
						// Si és més gran que 60 us és un pols llarg, o sigui un 1
			if (Temps > 19) {	// És més gran que 38 us
						// Sí, doncs no és un pols curt
				if (Temps > 30) {		// És més gran que 60 us
						// És un pols llarg
					Bytes[j] = Bytes[j] + 1;			// Entrem un 1
				} else {
					return 9;		// Error 9 - Valor H del bit incorrecte
				}
			}
		}
	}
						// Ja tenim els 5 bytes. Fem la suma de comprovació
						// Cal sumar els quatre primers bytea
						// Però sense portar-ne
	Valor = 0;				// Primer la posem a zero
	for (int j = 0; j < 4; j++){		// 4 bytes
		Valor = Valor + Bytes[j];	// Sumem un dels quatre elements
		if (Valor > 255) {		// Mirem si és més gran del que cap a un byte
						// Si és més gran, només pot haver activat un bit del segon byte
						// el que correspon a 256
			Valor = Valor -256;	// Restem les que en portàvem
		}
	}
	if (Valor != Bytes[4]) {		// No coincideixen
		return 10;			// Error 10 - Falla la suma de comprovació
	}
	Lectura[0] = Bytes[4];			// Suma de comprovació
	Lectura[1] = 256 * Bytes[2] + Bytes[3];			// Temperatura
	Lectura[2] = 256 * Bytes[0] + Bytes[1];			// Humitat
	return 0;				// Recepció correcta
}
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
}

 

 

Licencia de Creative Commons
Esta obra de Oriol Boix está licenciada bajo una licencia no importada Creative Commons Reconocimiento-NoComercial-SinObraDerivada 3.0.