/* ********************************************************************** * File K51PPO.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 30.05.05 * ********************************************************************** 05.06.2000 by Adriano Pedrielli (per la sola K51) (Versione originale in BASCOM 8051) 30/05/05: K51PPO.C - Rel. 1.1 - By Graziano Gaiba This demo allows to drive an I2C BUS peripheral on board of K51-AVR, PCF8574, through mini-BLOCK module GMB HR84 and grifo(r) mini module. PCF 8574 is an I/O expander that allows to drive 8 TTL digital lines, both as input and as output, all the operations of read and write are performed through synchronous serial interface I2C BUS. This demo activates in sequence one line at a time, creating the classic bit shift to the right and to the left, and writes 'out' on the 7 segments display of K51-AVR, near the characters 'out'. */ #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 PCF8574A o P *********************/ /* Esistono due versioni con slave address incompatibili di PCF8574, la A e la P. Selezionare di seguito quella desiderata. */ // Indirizzi per PCF8574A /* #define Pcf8574 0x3C // Slave address PCF8574A #define Wpcf8574 0x78 // Slave address PCF8574A in Write #define Rpcf8574 0x79 // Slave address PCF8574A in Read */ // Indirizzi per PCF8574P #define Pcf8574 0x24 // Slave address PCF8574P #define Wpcf8574 0x48 // Slave address PCF8574P in Write #define Rpcf8574 0x49 // Slave address PCF8574P 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 92, // writes 'o' 28, // writes 'u' 120, // writes 't' 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 $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_e rr=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 ****************************************************************************/ 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 i, d; 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 GMM932 ds300803 + GMBHR84 ds220503 + K51-AVR ds200500"); puts(""); puts("Esegue uno scorrimento sulle uscite TTL di CN1."); while(TRUE) // Infinite Loop { i=0x01; d=~i; // Per pilotaggio di LEDs in logica negata // Ad esempio con la DEB 01 o la TIO 16 while(i) { i2c_n_send(Wpcf8574, &d, 1); // Invia il dato al PCF8574 i<<=1; // Shift a sinistra d=~i; delay(500); // Per rendere visibile lo shift } i=0x80; d=~i; // Per pilotaggio di LEDs in logica negata // Ad esempio con la DEB 01 o la TIO 16 while(i) { i2c_n_send(Wpcf8574, &d, 1); // Invia il dato al PCF8574 i>>=1; // Shift a destra d=~i; delay(500); // Per rendere visibile lo shift } } }