/* ********************************************************************** * File K51PPO.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: 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'. !!!!!!!!!!!!!!!!!!!!!!!!!!! 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 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}; /**************************************************************************** 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(); } } /**************************************************************************** Programma principale ****************************************************************************/ void main(void) { unsigned char led_mask, val_out; 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("Esegue uno scorrimento sulle uscite TTL di CN1."); while(TRUE) // Infinite Loop { led_mask=0x01; val_out=~led_mask; // Per pilotaggio di LEDs in logica negata // Ad esempio con la DEB 01 o la TIO 16 while(led_mask) { i2c_n_send(Wpcf8574, &val_out, 1); // Invia il dato al PCF8574 led_mask<<=1; // Shift a sinistra val_out=~led_mask; wait_ms(500); // Per rendere visibile lo shift } led_mask=0x80; val_out=~led_mask; // Per pilotaggio di LEDs in logica negata // Ad esempio con la DEB 01 o la TIO 16 while(led_mask) { i2c_n_send(Wpcf8574, &val_out, 1); // Invia il dato al PCF8574 led_mask>>=1; // Shift a destra val_out=~led_mask; wait_ms(500); // Per rendere visibile lo shift } } }