/* ********************************************************************** * File K51TERMO.C - Rel. 1.1 con uC/51 V. 1.20.04 * * Schede: GMB HR 84 + GMM 932 + K51-AVR * * GRIFO(R) via Dell'Artigiano 8/6 40016 S. Giorgio di Piano (BO) * * Tel. +39 051 892052 Fax. +39 051 893661 * * http://www.grifo.com http://www.grifo.it * * by Graziano Gaiba del 27.05.05 * ********************************************************************** 05.06.2000 by Adriano Pedrielli (per la sola K51) (Versione originale in BASCOM 8051) 27/05/05: K51TERMO.C - Rel. 1.1 - By Graziano Gaiba Questo demo permette di pilotare una periferica I2C BUS a bordo della K51-AVR, il DS1621,attraverso il modulo mini-BLOCK GMB HR84 ed un mini modulo grifo(r). Il DS1621 e' un termometro digitale programmabile, con risoluzione di mezzo grado Celsius, tutte le operazioni di programmazione e acquisizione della temperatura avvengono tramite l'interfaccia seriale sincrona I2C BUS. Il demo mostra sui display a 7 segmenti della K51-AVR la temperatura misurata. */ #include "lpc932.h" #include #include #include #define FALSE 0x00 // Valori booleani #define TRUE 0xFF #define BEL 0x07 // Codici ascii #define LF 0x0A #define CRET 0x0D #define RCINT 7372800 // Frequenza clock da RC interno #define QZ11M 11059200 // Frequenza clock da quarzo esterno #define TOUTEEP 5000 // Costante di time out gestione EEPROM #define NACK 0 // Indica di inviare il Not Acknowlegde I2C BUS #define ACK 1 // Indica di inviare l'Acknowlegde I2C BUS #define PINSCL P1_2 // Pins usati come segnali I2C BUS lines #define PINSDA P1_3 /***************** Elenco indirizzi per Saa1064 ***********************/ #define Saa1064 0x38 // Slave address SAA1064 #define Wsaa1064 0x70 // Slave address SAA1064 in Write #define Rsaa1064 0x71 // Slave address SAA1064 in Read #define Ctb 0x00 // Ind. Control byte #define Dig1 0x01 // Ind. Digit 1 #define Dig2 0x02 // Ind. Digit 2 #define Dig3 0x03 // Ind. Digit 3 #define Dig4 0x04 // Ind. Digit 4 /***************** Elenco indirizzi per DS1621 ***********************/ #define Ds1621 0x4C // Slave address DS1621 #define Wds1621 0x98 // Slave address DS1621 in Write #define Rds1621 0x99 // Slave address DS1621 in Read /***************** Elenco comandi per DS1621 ***********************/ #define Cfg 0xAC // R/W Reg. Config per DS1621 #define Rtemp 0xAA // Leggi temperatura 2 bytes #define Strct 0xEE // Start conversione temperatura #define Stpct 0x22 // Stop conversione temperatura // Valori di inizializzazione dell'SAA1064 #define saa1064_init_length 0x06 // Lunghezza sequenza inizializzazione const unsigned char saa1064_init[]={ Ctb, // Punto al registro di controllo 0x27, // = 0b00100111 // bit0 =1 dynamic mode // bit1 =1 digit 1+3 not blanked // bit2 =1 digit 2+4 not blanked // bit3 =0 no test segment // bit4 =0 no 3mA segment current // bit5 =1 6mA segment current // bit6 =0 no 12mA segment current // bit7 =0 indifferente 0, // scrive DY1 off 0, // scrive DY2 off 0, // scrive DY3 off 0 // scrive DY4 off }; #define ds1621_init_length 0x03 // Lunghezza sequenza inizializzazione const unsigned char ds1621_init[]={ Wds1621, // comunico lo Slave address Cfg, // chiedo, il Reg. di Config. 0x0A // Scrivo il Reg. di Config. }; // Codici per convertire una cifra da 0 a 9 nella codifica a 7 segmenti const unsigned char cifra_7seg[]={0x3F,0x06,0x5B,0x4F,0x66, 0x6D,0x7D,0x07,0x7F,0x6F}; /********* Variabili globali usate dal main e dalle procedure **************/ near unsigned long clk; // Clock della CPU near unsigned char i,dr; // Errore su periferica I2C BUS near unsigned char i2c_err = 0; /**************************************************************************** Funzioni di utility generale e di gestione sezioni hw della scheda ****************************************************************************/ unsigned char rd_ucfg1(void) /* Legge il byte di configurazione UCFG1 in FLASH grazie alle procedure IAP del Boot Rom del P89LPC932. Restituisce byte letto nel nome funzione, senza controllare gli eventuali errori. */ { #asm mov A,#$03 ; Usa comando Misc. Read mov R7,#$00 ; Legge registro UCFG1 lcall $0FF00 ; Chiama procedura IAP ad indirizzo PGM_MTP #endasm } unsigned char peekb(unsigned int addr) /* Legge il byte all'indirizzo addr dell'area dati esterna e lo restituisce nel nome funzione */ { return *(xdata unsigned char *)addr; // Preleva byte dalla locazione } // Controllo correttezza periferiche interne con eventuali ritentativi // Usa le variabili generali st,t,m,ind,hlpw void check(void) { unsigned char st,s,t,m,ind; DEECON = 0x01; t = peekb(0xff); DEEADR = 0xFF; t = 1; ind = 0xF7; do { m = peekb(0x07); if(m & 0x80) { m = m & 0x03; ind = m + 0xF8; t = peekb(ind); } else { t = 0; } st = DEECON & 0x80; } while(st == 0); st = DEEDAT; do { s = peekb(ind++); if(ind == 0xFF) { ind = 0xF9; } do { m = s; if(! st) { t++; } } while(t <= m); } while(st != 0); } void init_cpu(void) /* Verifica la frequenza della CPU montata sulla scheda e lo salva in apposita variabile. Effettua inoltre le apposite inizializzazioni. */ { unsigned char M1, M2; EA=0; // Assicura disabilitazione interrupt P1M1 = 0; // Necessario per la seriale RS232 /* IN1 <- P0.0 IN2 <- P0.1 IN3 <- P0.2 IN4 <- P1.4 IN5 <- P0.3 IN6 <- P0.4 IN7 <- P0.5 IN8 <- P0.7 OUT A1 -> P1.6 OUT A2 -> P1.7 OUT B1 -> P2.1 OUT B2 -> P2.7 */ PT0AD = 0; // P0.1-5 come I/O non c. analogico M1 = P0M1; // Imposta P0.0-5 e P0.7 come solo input M2 = P0M2; M1 = M1 | 0xDF; M2 = M2 & 0x40; P0M1 = M1; P0M2 = M2; M1 = P1M1; // Imposta P1.4 come solo input e M2 = P1M2; // P1.6 e P1.7 come open drain output M1 = M1 | 0xD0; M2 = M2 | 0xC0; M2 = M2 & 0xEF; P1M1 = M1; P1M2 = M2; M1 = P2M1; // Imposta P2.1 e P2.7 come M2 = P2M2; // open drain output M1 = M1 | 0x82; M2 = M2 | 0x82; P2M1 = M1; P2M2 = M2; } void iniser(unsigned long baud, unsigned char stop) /* Inizializza la linea seriale con: Bit x chr = 8 Stop bit = stop Parity = None Baud rate = baud usando l'apposito baud rate generator interno al micro e la frequenza di clock salvata nella variabile globale clk. */ { unsigned long divbr; PCON = PCON & 0xBF; // Azzera bit SMOD0 x settare modo if (stop==1) SCON=0x52; // Modo 1 (1 stop),No multiproc,Attiva rx else SCON=0xDA; // Modo 3 (2 stop),No multiproc,Attiva rx //endif BRGCON=0x02; // Imposta baud rate passato divbr=baud << 4; // Calcola divisore per baud divbr=clk-divbr; divbr=divbr/baud; BRGR0=(unsigned char)(divbr & 0x00FF); divbr=divbr >> 8; BRGR1= (unsigned char)(divbr & 0x00FF); BRGCON=0x03; TI=1; // Setta bit fine trasmissione per console ottimizzata (SIOTYPE=k) } void clrscr(void) /* Effettua la funzione di clear screen per una generica console */ { unsigned char r; putc(CRET); for (r = 0 ; r < 25 ; r++) { putc(LF); // Trasmette 25 Line Feed } //endfor } void waitkey(void) /* Rappresenta messaggio ed attende pressione tasto */ { printf("Premere tasto per continuare..."); getc(); puts(""); } void delay(unsigned int rit) /* Effettua un ritardo software di rit millisecondi, con clock presente sul minimodulo, precedentemente determinato e salvato nella variabile globale clk. */ { unsigned int r,rit1ms; if (clk==RCINT) { rit1ms=335; // Valore sperimentale per ritardo di 1 msec a 7 MHz } else { rit1ms=500; // Valore sperimentale per ritardo di 1 msec a 11 MHz }; //endif do { for (r=0 ; r0); } void setoutbuf(unsigned char dato) /* Setta stato 4 linee di uscita bufferate con lo stato salvato nel nibble basso del parametro dato; il settaggio e` sui singoli bit del port per evitare conflitti sulle altre linee usate in altre sezioni ed e` in logica PNP (ovvero un bit a 1 in dato setta a 0 la linea del micro e causa la chiusura del contatto d'uscita. La corrispondenza tra port ed uscite a relé è: OUT A1 -> P1.6 OUT A2 -> P1.7 OUT B1 -> P2.1 OUT B2 -> P2.7 */ { if ((dato&0x01)!=0) // Determina e setta stato P1.6=OUT A1 P1_6=0; else P1_6=1; //endif if ((dato&0x02)!=0) // Determina e setta stato P1.7=OUT A2 P1_7=0; else P1_7=1; //endif if ((dato&0x04)!=0) // Determina e setta stato P2.1=OUT A3 P2_1=0; else P2_1=1; //endif if ((dato&0x08)!=0) // Determina e setta stato P2.7=OUT A4 P2_7=0; else P2_7=1; //endif } unsigned char getinpbuf(void) /* Preleva lo stato delle 8 linee di ingresso bufferate e lo restituisce. La corrispondenza tra port ed ingressi optoisolati NPN/PNP è: IN1 <- P0.0 IN2 <- P0.1 IN3 <- P0.2 IN4 <- P1.4 IN5 <- P0.3 IN6 <- P0.4 IN7 <- P0.5 IN8 <- P0.7 */ { // Preleva stato attuale e lo complementa perché gli ingressi funzionano // in logica complementata, poi restituisce dato il ottenuto. return(~P0); } /**************************************************************************** Procedure gestione I2C BUS ****************************************************************************/ void i2c_init(void) { P1M1=P1M1 | 0x0C; // Configura P1.2 (SCL) e P1.3 (SDA) P1M2=P1M2 | 0x0C; // in modo 3=Open Drain PINSCL=1; // Stato iniziale corretto dei segnali I2C BUS PINSDA=1; if (clk==RCINT) { I2SCLL=153; // Imposta bit rate= 2*(153+153) cicli di clock I2SCLH=153; // pari a circa 12 KHz a 7.3728 MHz } else { I2SCLL=230; // Imposta bit rate= 2*(230+230) cicli di clock I2SCLH=230; // pari a circa 12 KHz a 11.0592 MHz } //Endif i2c_err=0x00; // Azzera variabile errori I2C } /* Scrive il comando passato nel parametro i2cc parameter nel controller I2C BUS, attende l'esecuzione del comando, legge lo stato dello stesso controller e lo restituisce. */ unsigned char i2c_cmd(unsigned char i2cc) { unsigned int w; I2CON=i2cc; // Scrive il parametro nell'I2C control register w=0; // Azzera contatore di tempo trascorso while (((I2CON & 0x08)==0) && (w<60000)) // Attende I2C interrupt o timeout w++; // Incrementa tempo trascorso //endwhile w=I2STAT; // Legge I2C status register w=w & 0xF8; // Mantiene solo i bit significativi return (unsigned char) w; // Restituisce il risultato } /* Genera la sequenza di start dell'I2C BUS hardware e verifica eventuali errori, che vengono memorizzati nella variabile globale i2c_err. Questa procedura selezione il modo Master xxxxxxx del controller. */ void i2c_start(void) { I2CON=0x40; // Abilita I2C controller if (i2c_cmd(0x60)!=0x08) // Imposta start bit e verifica lo stato i2c_err=i2c_err | 0x01; // Imposta errore su start //endif } /* Genera la sequenza di start ripetuto dell'I2C BUS hardware e verifica eventuali errori, che vengono memorizzati nella variabile globale i2c_err. Questa procedura selezione il modo Master xxxxxxx del controller. */ void i2c_repstart(void) { I2CON=0x40; // Abilita I2C controller if (i2c_cmd(0x60)!=0x10) // Imposta start bit e verifica lo stato i2c_err=i2c_err | 0x02; // Imposta errore su start ripetuto //endif } /* Genera la sequenza di stopdell'I2C BUS hardware e verifica eventuali errori, che vengono memorizzati nella variabile globale i2c_err. */ void i2c_stop(void) { unsigned int w; I2CON=0x50; // Imposta stop bit nell'I2C control register w=0; // Azzera contatore di tempo trascorso do { w++; // Incrementa tempo trascorso delay(1); // Genera una pausa di 1 msec } while ((I2CON & 0x10) && (w<200)); // Attende I2C interrupt o timeout if (I2CON & 0x10) // Controllo finale dello stato i2c_err=i2c_err | 0x04; // Imposta errore su stop //endif I2CON=0x00; // Disabilita I2C controller } /* Invia lo slave address passato nel parametro sla sull'I2C BUS hardware e verifica eventuali errori, che vengono memorizzati nella variabile globale i2c_err. Il bit sla.0 definisce la direzione dei dati e decide se il controller lavora un modo trasmettitore Master o ricevitore Master. */ void i2c_sla(unsigned char sla) { I2DAT=sla; // Scrive lo slave address+R/W bit nel controller if (sla & 0x01) // Verifica la direzione poiche' influenza il codice di ritorno { // Direzione=R -> modo ricevitore Master if (i2c_cmd(0x40)!=0x40) // Invia slave add. e verifica stato per R i2c_err=i2c_err | 0x08; // Imposta errore su slave address // endif } else { // Direzione=W -> modo trasmettitore Master if (i2c_cmd(0x40)!=0x18) // Invia slave add. e verifica stato per W i2c_err=i2c_err | 0x08; // Imposta errore su slave address // endif } //endif } /* Invia il dato passato nel parametro dat sull'I2C BUS hardware e verifica eventuali errori, che vengono memorizzati nella variabile globale i2c_err. Il controller deve essere impostato in modo trasmettitore Master. */ void i2c_wr(unsigned char dat) { I2DAT=dat; // Scrive dato sul controller if (i2c_cmd(0x40)!=0x28) // Invia dato e verifica lo stato i2c_err=i2c_err | 0x10; // Imposta errore su trasmissione dato //endif } /* Riceve un dato dall'I2C BUShardware e verifica eventuali errori, che vengono memorizzati nella variabile globale i2c_err. Il parametro ack decide se un bit di Acknowledge deve essere inviato dopo la ricezione. Il dato ricevuto viene restituito. Il controller deve essere in modo ricevitore Master. */ unsigned char i2c_rd(unsigned char ack) { if (ack) // Verifica Acknowledge bit { // Riceve il dato e invia ACK bit if (i2c_cmd(0x44)!=0x50) // Riceve il dato e verifica lo stato i2c_err=i2c_err | 0x20; // Imposta errore su ricezione dato // endif } else { // Riceve il dato e invia NACK bit if (i2c_cmd(0x40)!=0x58) // Riceve il dato e verifica lo stato i2c_err=i2c_err | 0x20; // Imposta errore su ricezione dato // endif } //endif return I2DAT; // Restituisce il dato ricevuto } /* Mostra lo statu attuale degli errori I2C BUS, memorizzato nella variabile globale i2c_err, e lo azzera. */ void i2c_showres(void) { printf("-Errori I2C BUS avvenuti:"); if (i2c_err) { // Mostra errori if (i2c_err & 0x01) // Verifica e mostra errore su start printf(" Start"); // endif if (i2c_err & 0x02) // Verifica e mostra errore su start ripetuto printf(" Start-Ripetuto"); // endif if (i2c_err & 0x04) // Verifica e mostra errore su stop printf(" Stop"); // endif if (i2c_err & 0x08) // Verifica e mostra errore su slave address printf(" Slave-address"); // endif if (i2c_err & 0x10) // Verifica e mostra errore su trasmissione dato printf(" Transmissione-dato"); // endif if (i2c_err & 0x20) // Verifica e mostra errore su ricezione dato printf(" Recezione-dato"); // endif puts(""); // Va a capo } else puts(" nessuno"); // Nessun errore //endif i2c_err=0x00; // Azzera variabile di errore I2C } /* Invia n_out e legge n_in byte dallo slave address I2C BUS indicato usando la periferica hardware. I dati letti vanno nell'array chiamato getData di tipo unsigned char *. */ void i2c_n_receive(unsigned char i2c_slave, unsigned char *getData, unsigned char n_out, unsigned char n_in) { unsigned char dummy; i2c_start(); // Invia lo start i2c_sla(i2c_slave); // Scrive slave address for(dummy = 0; dummy < n_out; dummy++) { i2c_wr(getData[dummy]); // Scrive dato } i2c_repstart(); // Invia il secondo start i2c_sla(i2c_slave | 0x01); // Scrive slave address + R for(dummy = 0; dummy < n_in; dummy++) { if(dummy == n_in - 1) // Ultimo byte da leggere { getData[dummy] = i2c_rd(NACK); // Legge un byte e invia il NACK } else { getData[dummy] = i2c_rd(ACK); // Legge un byte e invia il NACK } } i2c_stop(); // Invia lo stop } /* Invia n_out byte allo slave address I2C BUS indicato usando la periferica hardware. I dati da inviare devono essere passati usndo l'array chiamato sendData di tipo unsigned char *. */ void i2c_n_send(unsigned char i2c_slave, unsigned char *sendData, unsigned char n_out) { unsigned char dummy; i2c_start(); // Invia lo start i2c_sla(i2c_slave); // Scrive slave address for(dummy = 0; dummy < n_out; dummy++) { i2c_wr(sendData[dummy]); // Scrive dato } i2c_stop(); } /**************************************************************************** Procedure specifiche per la gestione delle periferiche della K51-AVR ****************************************************************************/ /* Inizializzazione delle periferiche di bordo della K51-AVR */ void demo_init(void) { unsigned char i; // Attende accensione SAA1064 do { i2c_start(); i2c_sla(Rsaa1064); i = i2c_rd(NACK); i2c_stop(); // Se ci sono errori, vengono mostrati a console if(i2c_err) { i2c_showres(); } } while(i); // Invia la sequenza di inizializzazione dell'SAA1064 i2c_n_send(Wsaa1064, saa1064_init, saa1064_init_length); // Se ci sono errori, vengono mostrati a console if(i2c_err) { i2c_showres(); } // Inizializza il termometro DS1621 // Chiede accesso a registro configurazione i2c_start(); i2c_sla(Wds1621); i2c_wr(Cfg); i2c_stop(); // Se ci sono errori, vengono mostrati a console if(i2c_err) { i2c_showres(); } // Legge il registro configurazione i2c_start(); i2c_sla(Rds1621); i = i2c_rd(NACK); i2c_stop(); // Se ci sono errori, vengono mostrati a console if(i2c_err) { i2c_showres(); } if(i & 0x01) // Se il valore in AND con 1 e' ancora 1 { // Conversione disattiva, attivala i2c_n_send(Wsaa1064, ds1621_init, ds1621_init_length); delay(50); } else { i2c_start(); i2c_sla(Wds1621); i2c_wr(Strct); i2c_stop(); } // Se ci sono errori, vengono mostrati a console if(i2c_err) { i2c_showres(); } } /***************** Converte 1 byte in due cifre temperatura ************* Questa procedura converte 1 byte da 0 a 255 in temperatura dove da 0 a 125 sono gradi centigradi positivi (+0..+125), mentre da 255 a 201 sono gradi centigradi negativi (-0...-55), in pratica il bit7 indica il segno, i gradi negativi si ottengono con il complemento. Es: Not 255=0, Not 201=54 **************************************************************************/ void cifre(unsigned char v, unsigned char *c1, unsigned char *c2, unsigned char *c3) { unsigned char c, d, u; if(v > 127) // la temperatura risulta negativa { c = 10; // attivo il segno meno v=~v; // complemento il valore } else { if(v > 99) // se positiva, supera 99 { c = 1; // attivo il numero 1 delle centinaia v-=100; // sottraggo 100 } else { c = 11; // non supera 99, spengo la cifra delle centinaia } } if(v > 9) // supera 9 { d = v / 10; // ricavo la cifra delle decine u = v - (d * 10); // ricavo la cifra delle unita } else { if(c==11) // minore di 9 e cifra centinaia spenta { d = 11; // spengo la cifra delle decine } else { if(c==10) // minore di nove, e temp. negativa { d = 11; // spengo la cifra delle decine } else { d = 0; // minore di 9 e cifra centinaia attiva } // visualizzo uno zero } u = v; // salvo il valore delle unita } *c1=c; *c2=d; *c3=u; } /***************** Converte un numero da 0 a 9 in 7 segmenti *********** Questa procedura converte una cifra da 0 a 9 nel formato 7 segmenti, se il valore e maggiore = 11 il display risulta spento, se = 10 viene rappresentato il segno negativo. Parametri: Ingresso : dig as byte, valore da 0 a 9 Uscita : dig as byte, valore in formato 7 segmenti. ************************************************************************/ unsigned char digit(unsigned char n) { if(n < 10) { return cifra_7seg[n]; // legge in tabella il valore } else { if(n > 10) { return 0; // Se la cifra supera 10 spegne il display } else { return 0x40; // Se la cifra e' 10 mostra il segno "-" } } } void vis_temp(unsigned int t) { unsigned char h, l; unsigned char c, d, u; h=t>>8; // Parte che contiene gradi e segno l=t & 0x00ff; // Parte che contiene il mezzo grado cifre(h, &c, &d, &u); // Scomposizione in 3 cifre del valore di h // Centinaia, decine ed unita' con segno i2c_start(); // Start sequence i2c_sla(Wsaa1064); // Slave address i2c_wr(Dig1); // Primo digit i2c_wr(digit(c)); // Trasforma in "7 segmenti" ed invia i2c_wr(digit(d)); // Trasforma in "7 segmenti" ed invia i2c_wr(digit(u) | 0x80); // Trasforma in "7 segmenti" ed invia // Attivando anche il punto decimale if(l==0x80) // Scrive il mezzo grado { i2c_wr(digit(5)); } else { i2c_wr(digit(0)); } i2c_stop(); // Stop sequence } // Legge la temperatura unsigned int temperature(void) { unsigned char d[2]; d[0]=Rtemp; i2c_n_receive(Wds1621, d, 1, 2); return((d[0] << 8) | d[1]); } /**************************************************************************** Programma principale ****************************************************************************/ void main(void) { init_cpu(); // Inizializza sezioni necessarie della CPU clk=RCINT; // Setta clock per RC interno (puo` essere acquisito solo dopo) P0M1=P0M1 | 0x40; // Setta configurazione P0.6 in modo 3=Open Drain P0M2=P0M2 | 0x40; for (i=0 ; i<16 ; i++) // Ritardo partenza Hyperterminal con lampeggio DL1 { P0_6=!P0_6; // Complementa stato DL1 delay(125); } //enfor dr=rd_ucfg1(); // Preleva configurazione da FLASH dr=dr & 0x07; // Mantiene configurazione della sorgente di CLK switch (dr) { case 0x00: clk=QZ11M; // Frequenza clock da quarzo esterno break; case 0x03: clk=RCINT; // Frequenza clock da RC interno break; } iniser(19200, 1); // Inizializza seriale A di console i2c_init(); // Inizializza micro e periferica I2C BUS a circa 50 kHz demo_init(); // Inizializzazione periferiche K51-AVR for(;;) { clrscr(); // Pulsce lo schermo puts("Demo 1.1 per GMM932 ds300803 + GMBHR84 ds220503 + K51-AVR ds200500"); puts(""); puts("Mostra la temperatura letta dal DS1621."); while(TRUE) // Infinite Loop { vis_temp(temperature()); // Legge e visualizza la temperatura // Se ci sono errori, vengono mostrati a console if(i2c_err) { i2c_showres(); } } } }