/* ********************************************************************** * File K51AD4CH.C - Rel. 1.1 con uC/51 V. 1.20.04 * * Schede: GMB HR 84 + GMM 936 + 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 06.06.05 * ********************************************************************** 05.06.2000 by Adriano Pedrielli (per la sola K51) (Versione originale in BASCOM 8051) 06/06/05: K51AD4CH.C - Rel. 1.1 - By Graziano Gaiba Questo demo permette di pilotare una periferica I2C BUS a bordo della K51-AVR, PCF8591, attraverso il modulo mini-BLOCK GMB HR84 ed un mini modulo grifo(r). Il PCF891 e' dotato di quattro ingressi analogici per conversione A/D con risoluzione ad 8 bit, tutte le operazioni di conversione e la lettura della combinazione acquisita avvengono tramite interfaccia seriale sincrona I2C BUS. Il demo permette di impostare quale canale convertire tramite la tastiera del PC, e di leggere la combinazione sui display a 7 segmenti della K51-AVR. */ #include "89LPC936.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 PCF8591 ***********************/ #define Pcf8591 0x48 // Slave address PCF8591 #define Wpcf8591 0x90 // Slave address PCF8591 in Write #define Rpcf8591 0x91 // Slave address PCF8591 in Read // 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 119, // scrive 'a' 94, // scrive 'd' 0, // scrive DY3 off 0 // scrive DY4 off }; // Codici per convertire una cifra da 0 a F nella codifica a 7 segmenti const unsigned char cifra_7seg[]={0x3F,0x06,0x5B,0x4F,0x66, 0x6D,0x7D,0x07,0x7F,0x6F, 0x77,0x7C,0x39,0x5E,0x79,0x71}; /********* 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 $0FF03 ; 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 ****************************************************************************/ /***************** Gestione dell'A/D converter ************************* Questa procedura permette di leggere un canale dell'A/D converter, disponibile su IC2 (PCF8591). Parametri: Ingresso : canale da leggere (da 0 a 3) Uscita : combinazione ad 8 bit ************************************************************************/ unsigned char adc(unsigned char v) // Gestione dell'A/D converter { unsigned char d[2]; d[0]=v & 0x03; // Canale da convertire i2c_n_receive(Wpcf8591, d, 1, 2); // Legge conversione precedente ed attuale return d[1]; // Restituisce conversione attuale } // Converte un valore da 0 a 255 in due cifre esadecimali a 4 bit void cifre(unsigned char v, unsigned char *c1, unsigned char *c2) { *c1=(v & 0xf0) >> 4; *c2=v & 0x0f; } /***************** Converte un numero da 0 a 15 in 7 segmenti *********** Questa procedura converte una cifra da 0 a 15 nel formato 7 segmenti. Parametri: Ingresso : valore da 0 a 15 Uscita : valore in formato 7 segmenti. ************************************************************************/ unsigned char digit(unsigned char n) { if(n < 16) { return cifra_7seg[n]; // legge in tabella il valore } else { return 0; // Se la cifra supera 15 spegne il display } } /************************ Visualizza un byte in HEX *********************** Questa procedura permette di visualizzare un byte nel formato esadecimale. Es: 255= FFH, 32= 20H ecc. Parametri: Ingresso : numero da visualizzare in HEX Uscita : nulla **************************************************************************/ void vis_num(unsigned char t) { unsigned char h, l; cifre(t, &h, &l); // Scomposizione in 2 nibble del valore di t // Nibble alto e nibble basso i2c_start(); // Start sequence i2c_sla(Wsaa1064); // Slave address i2c_wr(Dig1); // Primo digit i2c_wr(digit(i)); // Trasforma in "7 segmenti" ed invia i2c_wr(0); // Spento i2c_wr(digit(h)); // Trasforma in "7 segmenti" ed invia i2c_wr(digit(l)); // Trasforma in "7 segmenti" ed invia i2c_stop(); // Stop sequence } /* 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(); } } /**************************************************************************** Programma principale ****************************************************************************/ void main(void) { unsigned char c; 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 clrscr(); // Pulsce lo schermo puts("Demo 1.1 per GMM936 ds300803 + GMBHR84 ds220503 + K51-AVR ds200500"); puts(""); puts("Mostra il valore letto dall'A/D converter su CN1."); i=0; puts("Premere + per incrementare il numero del canale da convertire"); puts("Premere - per decrementare il numero del canale da convertire"); while(TRUE) // Infinite Loop { vis_num(adc(i)); // Visualizza il canale scelto // Se ci sono errori, vengono mostrati a console if(i2c_err) { i2c_showres(); } if(kbhit()) // Se viene premuto un tasto { c=getc(); switch(c) { case '+': // Il + incrementa il numero del canale if(i==3) // Da 0 a 3 { i=0; } else { i++; }; break; case '-': // Il + incrementa il numero del canale if(i==0) // Da 0 a 3 { i=3; } else { i--; }; break; } // end switch(c) } // end if(kbhit()) } // end while(TRUE) }