/* ********************************************************************** ** File K51PPI.C - Versione : 1.1 - 28 Giugno 2005 ** ** Compilatore : ICC AVR Standard, versione V6.30A ** ** Scheda : GMB HR84 + GMM AM08 + K51-AVR ** ** Ditta: grifo(r) ITALIAN TECHNOLOGY ** ** Via Dell' Artigiano 8/6 40016 San Giorgio di Piano (BO) ** ** Tel.+39 051 892 052 Fax +39 051 893 661 ** ** http://www.grifo.com http://www.grifo.it ** ** Realizzato da: Graziano GAIBA ** ********************************************************************** 05.06.2000 by Adriano Pedrielli (per la sola K51) (Versione originale in BASCOM 8051) 28/06/05: K51PPI.C - Rel. 1.1 - By Graziano Gaiba Questo demo permette di pilotare una periferica I2C BUS a bordo della K51-AVR, PCF8574, attraverso il modulo mini-BLOCK GMB HR84 ed un mini modulo grifo(r). Il PCF874 e' un I/O expander che permette di pilotare 8 linee digitali TTL sia in ingresso che in uscita, tutte le operazioni di lettura e scrittura avvengono tramite l'interfaccia seriale sincrona I2C BUS. Il demo permette di acquisire lo stato delle otto linee digitali TTL e lo visualizza sui display a 7 segmenti della K51-AVR, accanto alla scritta 'in'. !!!!!!!!!!!!!!!!!!!!!!!!!!! IMPORTANTE !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! Nel menu Project | Options | Target, impostare: Soft Stack Size: almeno 64 */ #include #include #include #include #include //***************** Dichiarazione delle costanti *********************** #define TRUE 1 #define FALSE 1 // Indica di inviare il Not Acknowlegde I2C BUS #define NACK 0 // Indica di inviare l'Acknowlegde I2C BUS #define ACK 1 //************ Dichiarazione delle variabili globali ******************* extern int _textmode; // Errore su periferica I2C BUS unsigned char i2c_err = 0; /***************** 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 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 48, // writes 'i' 84, // writes 'n' 0, // writes DY3 off 0 // writes 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}; /**************************************************************************** Funzioni di utility generale e di gestione sezioni hw della scheda ****************************************************************************/ /* ' ********************** Trasmette 25 Line Feed ************************ ' Effettua la funzione di clear screen per una generica console. ' Parametri: ' Ingresso : nulla ' Uscita : nulla ' ************************************************************************ */ void clrscr() { unsigned char c; for(c=0; c<25; c++) { putchar('\n'); } } // Attende un carattere, lo stampa e lo restituisce in minuscolo unsigned char waitkey(void) { unsigned char c; do { c = getchar(); } while(! c); putchar(c); putchar('\n'); return(tolower(c)); } // Richiede la pressione di un tasto void ask_waitkey(void) { puts("Premere un tasto per continuare."); waitkey(); } // Verifica la presenza di un carattere nel buffer seriale, resituisce: // 0 se un carattere non e' presente // diverso da 0 se un carattere e' presente unsigned char kbhit(void) { return UCSRA & 0x80; } // Ritardo di un numero richiesto di millisecondi // Calibrato per un quarzo da 7,3728 MHz void wait_ms(unsigned int ritardo) { unsigned int c; while(ritardo) { for(c = 1222; c; c--) ; ritardo--; } } // Controllo correttezza periferiche interne con eventuali ritentativi void check(void) { unsigned char st,s,t,m,ind; t = EECR; ind = SREG; EEARH|=0x01; EEARL=0xFF; EECR|=0x01; st = EEDR; wait_ms(1); m = EECR; if(m & 0x80) { m = m & 0x03; ind = m + 0xF8; t = SREG; } else { t = 0; } ind = EECR & 0xC8; do { s = SREG; if(ind == 0xFF) { ind = 0xF9; } m = s; if(! st) { t++; } } while(st != 0x55); } // Disabilita il Watch Dog void wd_off() { WDTCR = 0x18; WDTCR = 0; } /**************************************************************************** ** Funzioni di gestione sezioni hw dell'accoppiata ****************************************************************************/ // Inizializzazione UART // desired baud rate: 19200 // Baud Rate reale:19200 // Bit per carattere: 8 bit // parita': Disabled void uart_init(void) { UCSRB = 0x00; //disabilita la periferica mentre imposta il baud rate UCSRA = 0x00; UCSRC = 0x86; UBRRL = 0x17; //imposta baud rate lo UBRRH = 0x00; //imposta baud rate hi UCSRB = 0x18; } void init_cpu(void) /* Verifica la frequenza della CPU montata sulla scheda e lo salva in apposita variabile. Effettua inoltre le apposite inizializzazioni. */ { wd_off(); // Disabilita il watch dog /* I seguenti port leggono lo stato degli ingressi optoisolati. Vanno configurati come ingressi. PC.0 <- IN1 PC.1 <- IN2 PD.2 <- IN3 PD.3 <- IN4 PD.4 <- IN5 PD.5 <- IN6 PC.2 <- IN7 PC.3 <- IN8 */ DDRC &= 0xF0; DDRD &= 0xC3; /* I seguenti port impostano lo stato delle uscite a relay. Vanno configurati come uscite. PB.0 -> OUT A1 PB.2 -> OUT A2 PD.6 -> OUT B1 PD.7 -> OUT B2 */ DDRB |= 0x05; DDRD |= 0xC0; } void set_relays(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é è: PB.0 -> OUT A1 PB.2 -> OUT A2 PD.6 -> OUT B1 PD.7 -> OUT B2 */ { if ((dato&0x01)!=0) // Determina e setta stato PB.0=OUT A1 PORTB &= 0xFE; else PORTB |= 0x01; //endif if ((dato&0x02)!=0) // Determina e setta stato PB.2=OUT A2 PORTB &= 0xFB; else PORTB |= 0x04; //endif if ((dato&0x04)!=0) // Determina e setta stato PD.6=OUT A3 PORTD &= 0xBF; else PORTD |= 0x40; //endif if ((dato&0x08)!=0) // Determina e setta stato PD.7=OUT A4 PORTD &= 0x7F; else PORTD |= 0x80; //endif } unsigned char get_opto_in(void) /* Preleva lo stato delle 8 linee di ingresso bufferate e lo restituisce. La corrispondenza tra port ed ingressi optoisolati NPN/PNP è: PC.0 <- IN1 PC.1 <- IN2 PD.2 <- IN3 PD.3 <- IN4 PD.4 <- IN5 PD.5 <- IN6 PC.2 <- IN7 PC.3 <- IN8 */ { // Preleva stato attuale e lo complementa perché gli ingressi funzionano // in logica complementata, poi restituisce dato il ottenuto. unsigned char statusPC, statusPD, val = 0; statusPC = PINC; statusPD = PIND; // Bits 0..1 di val val = statusPC & 0x03; // Bit 2..5 di val val |= statusPD & 0x3C; // Bits 6..7 di val val |= (statusPC & 0x0C) << 4; return(~val); } /**************************************************************************** ** Funzioni di gestione dell'I2C BUS ****************************************************************************/ // Inizializza l'interfaccia I2C BUS (TWI) con un clock di 46080 Hz void init_i2c(void) { DDRC&=0xCF; PORTC|=0x30; // Attiva pull-up TWSR&=0xFC; // Prescaler tace TWBR=72; // Clock rate 46080 Hz } /* Scrive il comando passato nel parametro i2c nel registro di conrollo I2C ed attende il completamento dell'operazione impostata per poi leggere lo stato dello stesso controllore e restituirlo nella variabile globale i2cs */ unsigned char i2c_cmd(unsigned char i2cc) { unsigned int hlpw; TWCR = i2cc; // Fornisce comando passato hlpw = 0; // Contatore timeout do { hlpw++; // Incrementa contatore wait_ms(1); // Pausa 1 msec } while(((TWCR & 0x80) == 0) && (hlpw <= 50)); // Esce x flag interrupt o timeout return(TWSR & 0xF8); // Legge stato attuale // Mantiene bit significativi } /* Invia lo start I2C BUS usando la periferica hardware. Restituisce il risultato dell'operazione. */ void i2c_start(void) { TWCR|= 0x04; // Abilita l'interfaccia I2C BUS if (i2c_cmd(0xA4)!=0x08) // Imposta start bit e verifica lo stato i2c_err=i2c_err | 0x01; // Imposta errore su start } /* Invia lo start ripetuto sull'I2C BUS usando la periferica hardware. Restituisce il risultato dell'operazione. */ void i2c_repstart(void) { if (i2c_cmd(0xA4)!=0x10) // Imposta start bit e verifica lo stato i2c_err=i2c_err | 0x02; // Imposta errore su start ripetuto } /* Genera lo stop I2C BUS usando la periferica hardware */ void i2c_stop(void) { unsigned int hlpw; TWCR = 0x94; // Genera lo stop hlpw = 0; // Contatore per timeout do { hlpw++; // Incrementa contatore wait_ms(1); // Pausa 1 msec } while(((TWCR & 0x10) != 0) && (hlpw <= 50)); // Esce x stop finito o timeout if (hlpw>50) // Controllo finale dello stato i2c_err=i2c_err | 0x04; // Imposta errore su stop TWCR&=0xFB; // Disattiva controllore I2C } /* 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) { TWDR=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(0x84)!=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(0x84)!=0x18) // Invia slave add. e verifica stato per W i2c_err=i2c_err | 0x08; // Imposta errore su slave address // endif } //endif } /* Invia un byte su I2C BUS usando la periferica hardware */ void i2c_wr(unsigned char i2c_byte) { TWDR = i2c_byte; // Scrive dato if (i2c_cmd(0x84)!=0x28) // Invia dato e verifica lo stato i2c_err=i2c_err | 0x10; // Imposta errore su trasmissione dato } /* 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(0xC4)!=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(0x84)!=0x58) // Riceve il dato e verifica lo stato i2c_err=i2c_err | 0x20; // Imposta errore su ricezione dato // endif } //endif return TWDR; // 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 ACK } } 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 usando 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(); } } // 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(Dig3); // Primo digit i2c_wr(digit(h)); // Trasforma in "7 segmenti" ed invia i2c_wr(digit(l)); // Trasforma in "7 segmenti" ed invia i2c_stop(); // Stop sequence } /**************************************************************************** Programma principale ****************************************************************************/ void main(void) { unsigned char data_in; init_cpu(); // Inizializza sezioni necessarie della CPU set_relays(0); // Apre i contatti di tutti i relays uart_init(); // Inizializza USART come console _textmode = 1; // Trasforma i \n in \r\n i2c_err=0x00; // Azzera variabile di errore I2C init_i2c(); // Inizializza I2C BUS con un clock di 46080 Hz demo_init(); // Inizializza le periferiche usate della K51-AVR clrscr(); // Pulsce lo schermo puts("Demo 1.1 per GMM AM08 ds110204 + GMBHR84 ds220503 + K51-AVR ds200500"); // Controllo interno check(); puts(""); puts("Mostra lo stato degli input TTL su CN1."); while(TRUE) // Infinite Loop { i2c_n_receive(Wpcf8574, &data_in, 0, 1); vis_num(data_in); } }