/* ********************************************************************** * File: k51termo.c - Rel. 1.1 with uC/51 V. 1.20.04 * * Boards: GMB HR168 + GMM AC Zero + 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 date 07.06.05 * ********************************************************************** 07/06/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 HR168 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. N.B. Per evitare problemi non eseguire operazioni complesse su una singola riga, specialmente all'interno di procedure sui relativi parametri e/o variabili locali. */ #include "canary.h" #include #include #define FALSE 0x00 // Valori booleani #define TRUE 0xFF #define LF 0x0A #define CRET 0x0D /***************** 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 gestione I2C BUS bit unsigned char SDA @ 0xA1; // Pin SDA=P2.1 bit unsigned char SCL @ 0xA0; // Pin SCL=P2.0 /********* Funzioni di utility generale e di gestione sezioni hw ***********/ void iniser(unsigned long baud) /* Inizializza la linea seriale con: Bit x chr = 8 Stop bit = 1 Parity = None Baud rate = baud usando come baud rate generator il timer 1. */ { SCON=0x052; // Modo 1, abilita ricevitore TMOD&=0x00F; // Timer 1 in modo auto-reload TMOD|=0x020; TR1=0; // Stop al TIMER 1 TH1=256-((2*14745600)/(384*baud)); // baud a 14.7456 MHz PCON=PCON|0x080; // Setta SMOD=1 per baud rate alti TR1=1; // Start al TIMER 1 } 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 ritardo(unsigned int rit) /* Effettua un ritardo software di rit millisecondi, calibrato su un Clock di CPU da 14.7456 MHz, a seconda della CPU montata. */ { unsigned int r,rit1ms; rit1ms=100; // Valore sperimentale per ritardo di 1 msec. con 80c32 do { for (r=0 ; r0); } void init_cpu(void) /* Verifica la CPU montata sulla scheda e lo salva in apposita variabile. Effettua inoltre le apposite inizializzazioni: seriale, wait, ecc. */ { CKCON=0x00; // Setta X1 clock mode = standard mode AUXR=0x0C; // Seleziona ERAM su area dati esterna EECON=0x00; // Disabilita EEPROM del micro } void riti2c(void) /* Genera ritardo per comunicazione sincrona in I2CBUS. */ { #asm nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop #endasm } void starti2c(void) /* Genera sequenza di start per I2C BUS */ { SCL = 0; // Genera sequenza di start SDA = 1; riti2c(); SCL = 1; SDA = 0; riti2c(); SCL = 0; } void stopi2c(void) /* Genera sequenza di stop per I2C BUS */ { SCL = 0; // Genera sequenza di stop SDA = 0; riti2c(); SCL = 1; SDA = 1; riti2c(); SCL = 0; } void wri2c_bit(unsigned char i2cbit) /* Serializza il bit D0 di i2cbit su I2C BUS */ { SCL = 0; // Setta SDA e genera impulso positivo su SCL SDA = i2cbit; riti2c(); SCL = 1; riti2c(); SCL = 0; } unsigned char rdi2c_bit(void) /* Deserializza un bit da I2C BUS e lo salva in D0 del risultato */ { unsigned char biti2c; SDA = 1; // Evita conflitti su acquisizione SDA SCL = 0; // Assicura stato SCL riti2c(); SCL = 1; // Genera impulso positivo su SCL e su questo legge SDA biti2c = SDA; riti2c(); SCL = 0; return biti2c; } void wri2c_byte(unsigned char i2cbyte) /* Serializza il byte i2cbyte su I2C BUS */ { unsigned char b; for (b = 1; b <= 8; b++) { if ((i2cbyte & 0x80) == 0) // Determina e setta bit b wri2c_bit(0); else wri2c_bit(1); i2cbyte = i2cbyte << 1; } } unsigned char rdi2c_byte(void) /* Deserializza un byte da I2C BUS e lo salva nel risultato */ { unsigned char b,tmp; tmp = 0; for (b = 1; b <= 8; b++) { tmp = tmp << 1; tmp = tmp | rdi2c_bit(); // Preleva e salva bit b } return tmp; } unsigned char wr_i2c(unsigned char i2csla,unsigned char i2cadd,unsigned char i2cdat) /* Scrive il byte i2cdat all'indirizzo i2caddr del dispositivo I2C BUS che ha uno slave address i2csla. Restituisce flag booleano che indica il risul tato dell'operazione: 0=corretta,1=errata. */ { unsigned char i2cris; i2cris = 0; // Setta risultato corretto starti2c(); // Fornisce sequenza di start wri2c_byte(i2csla); // Fornisce slave address+W i2cris = i2cris | rdi2c_bit(); // Controlla ACK su slave address+W wri2c_byte(i2cadd); // Fornisce address i2cris = i2cris | rdi2c_bit(); // Controlla ACK su address wri2c_byte(i2cdat); // Fornisce dato i2cris = i2cris | rdi2c_bit(); // Controlla ACK su dato stopi2c(); // Fornisce sequenza di stop return i2cris; // Restituisce risultato operazione } unsigned char rd_i2c(unsigned char i2csla,unsigned char i2cadd,unsigned char *i2cdat) /* Legge il byte i2cdat dall'indirizzo i2caddr del dispositivo I2C BUS che ha uno slave address i2csla. Restituisce flag booleano che indica il risultato dell'operazione: 0=corretta,1=errata. */ { unsigned char i2cris; i2cris = 0; // Setta risultato corretto starti2c(); // Fornisce sequenza di start wri2c_byte(i2csla); // Fornisce slave address+W i2cris = i2cris | rdi2c_bit(); // Controlla ACK su slave address+W wri2c_byte(i2cadd); // Fornisce address i2cris = i2cris | rdi2c_bit(); // Controlla ACK su address starti2c(); // Fornisce sequenza di start wri2c_byte(i2csla | 0x01); // Fornisce slave address+R i2cris = i2cris | rdi2c_bit(); // Controlla ACK su slave address+R *i2cdat = rdi2c_byte(); // Preleva dato stopi2c(); // Fornisce sequenza di stop return i2cris; // Restituisce risultato operazione } // Invia allo slave address indicato (primo parametro) il numero di bytes indicato // (terzo parametro) memorizzati a partire dall'indirizzo indicato (secondo // parametro). // Non fa nulla se indicati 0 dati. // Restituisce flag booleano che indica il risultato dell'operazione: // 0=corretta,1=errata. unsigned char i2c_invia(unsigned char slave, unsigned char *dati, unsigned char ndati) { unsigned char i2cris, i; if(! ndati) // Se ndati e' zero, esce return 0; i2cris=0; starti2c(); // Sequenza di start wri2c_byte(slave); // Invia lo slave address i2cris|=rdi2c_bit(); // Controlla ACK i=0; while(ndati--) // Invia ndati bytes { wri2c_byte(dati[i++]); // Invia lo slave address i2cris|=rdi2c_bit(); // Controlla ACK } stopi2c(); // Sequenza di stop return i2cris; // Restituisce il risultato } // Invia allo slave address indicato (primo parametro) il numero di bytes indicato // (terzo parametro) memorizzati a partire dall'indirizzo indicato (secondo // parametro), poi si aspetta di ricevere in risposta il numero di bytes indicati // nel quarto parametro e li memorizza a partire dall'indirizzo prima specificato. // Non fa nulla se indicati 0 dati in arrivo e in invio. // Restituisce flag booleano che indica il risultato dell'operazione: // 0=corretta,1=errata. unsigned char i2c_ricevi(unsigned char slave, unsigned char *dati, unsigned char ndati_out, unsigned char ndati_in) { unsigned char i2cris, i; if((! ndati_in) && (! ndati_out)) // Se ndati_in e ndati_out sono zero,esce return 0; i2cris=0; // Invia i dati starti2c(); // Sequenza di start wri2c_byte(slave); // Invia lo slave address i2cris|=rdi2c_bit(); // Controlla ACK i=0; while(ndati_out--) // Invia ndati_out bytes { wri2c_byte(dati[i++]); // Invia lo slave address i2cris|=rdi2c_bit(); // Controlla ACK } // Riceve i dati starti2c(); wri2c_byte(slave | 0x01); // Fornisce slave address+R i2cris|=rdi2c_bit(); // Controlla ACK su slave address+R i=0; while(ndati_in--) // Riceve ndati_in bytes { dati[i++]=rdi2c_byte(); // Legge il dato if(ndati_in) { wri2c_bit(0); // Invia ACK } else { wri2c_bit(1); // Ultimo dato da leggere, invia NACK } } stopi2c(); // Sequenza di stop return i2cris; // Restituisce il risultato } void demo_init(void) { unsigned char i; // Attende accensione SAA1064 do { starti2c(); wri2c_byte(Rsaa1064); rdi2c_bit(); i = rdi2c_byte(); wri2c_byte(1); // Not Acknowledge stopi2c(); } while(i); // Invia la sequenza di inizializzazione dell'SAA1064 i2c_invia(Wsaa1064, saa1064_init, saa1064_init_length); // Inizializza il termometro DS1621 // Chiede accesso a registro configurazione starti2c(); wri2c_byte(Wds1621); rdi2c_bit(); wri2c_byte(Cfg); rdi2c_bit(); stopi2c(); // Legge il registro configurazione starti2c(); wri2c_byte(Rds1621 | 0x01); rdi2c_bit(); i = rdi2c_byte(); wri2c_byte(1); // Not Acknowledge stopi2c(); if(i & 0x01) // Se il valore in AND con 1 e' ancora 1 { // Conversione disattiva, attivala i2c_invia(Wsaa1064, ds1621_init, ds1621_init_length); ritardo(50); } else { starti2c(); wri2c_byte(Wds1621); rdi2c_bit(); wri2c_byte(Strct); rdi2c_bit(); stopi2c(); } } /***************** 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 starti2c(); // Start sequence wri2c_byte(Wsaa1064); // Slave address rdi2c_bit(); // Controlla ACK su slave address+R wri2c_byte(Dig1); // Primo digit rdi2c_bit(); wri2c_byte(digit(c)); // Trasforma in "7 segmenti" ed invia rdi2c_bit(); wri2c_byte(digit(d)); // Trasforma in "7 segmenti" ed invia rdi2c_bit(); wri2c_byte(digit(u) | 0x80); // Trasforma in "7 segmenti" ed invia rdi2c_bit(); // Attivando anche il punto decimale if(l==0x80) // Scrive il mezzo grado { wri2c_byte(digit(5)); } else { wri2c_byte(digit(0)); } rdi2c_bit(); stopi2c(); // Stop sequence } // Legge la temperatura unsigned int temperatura(void) { unsigned char d[2]; d[0]=Rtemp; i2c_ricevi(Wds1621, d, 1, 2); return (d[0] << 8) | d[1]; } void main(void) { ritardo(2); init_cpu(); iniser(19200); demo_init(); // Inizializzazione periferiche while(TRUE) // Infinite Loop { vis_temp(temperatura()); // Legge e visualizza la temperatura } }