/* ********************************************************************** ** Programma: demolcd.c - Versione : 1.2 - 25 Luglio 2003 ** ** Compilatore : uC/51 Ver. 1.10.7 ** ** Scheda : K51-AVR e microcontrollore ATMEL AT89c51 o superiore ** ** 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 ** ** sales@grifo.it tech@grifo.it grifo@grifo.it ** ** ** ** Realizzato da: Graziano Gaiba ** ********************************************************************** Questo programma e' un emulatore di terminale totalmente configuabile sia nei codici di emulazione sia nelle procedure che vengono eseguite in corrispondenza dei codici. La procedura che filtra i caratteri e decide se stamparli od eseguirli come comandi e' chamata elaboralcd. Esistono tre diversi tipi di comandi: 1) Caratteri di controllo 2) Comandi immeditati 3) Comandi con almeno un parametro I caratteri di controllo sono caratteri che non vengono visualizzati ma che, appena ricevuti, inducono l'esecuzione di un determinato pezzo di codice. Ad esempio il carattere ASCII detto BEL (codice 7) che normalmente produce un suono ma che non viene stampato sullo schermo. I comandi immeditai sono delle sequenze , ovvero un carattere prefisso che annuncia la richiesta di eseguire un comando (spesso si usa il carattere ASCII ESC (codice 27) e un codice che identifica il comando da eseguire. Non appena viene riconosciuto la porzione di istruzioni associata viene eseguita. Infine, i comandi con almeno un parametro sono sequenze di lunghezza variabile ... ogni comando e' preceduto dal prefisso e identificato da un codice, seguono uno o piu' caratteri che vengono memorizzati come parametri. Non appena l'ultimo parametro e' stato ricevuto vengono eseguite le istruzioni relative, ogni comando puo' avere un numero indipendente di parametri limitati solo dalla memoria disponibile. Le funzioni Conin e Conout (Console input e Console output) costituiscono una comoda interfaccia verso la funzione elaboralcd che tiene conto di quale dispositivo (seriale o scheda K51) si sta usando come input o come output. La decisione di quale dispositivo debba agire da input e quale da output viene presa impostando opportunamente il valore delle variabili indev e outdev mediante le funzioni setIndev() e setOutdev(). Ad esempio, se si vuole leggere la seriale ed inviare i caratteri verso lo schermo LCD sfruttando la elaboralcd,la sequenza corretta e': setIndev(SER); setOutdev(K51); if(conStatus()) { conOut(conIn()) } La funzione conStatus determina se il dispositivo scelto con setIndev() ha un carattere pronto per essere letto; se e' cosi' dopo averla chiamata la conStatus() restituia' valore 1, altrimenti avra' valore 0. Per leggere il carattere pendente si deve chiamare conIn(). Questa ritorna il valore 0 se viene chiamata quando nessun carattere e' in attesa, tuttavia potrebbe anche avere restituito il carattere 0, quindi prima di usarla bisogna SEMPRE chiamare la conStatus(). Per inviare un carattere al dispositivo di uscita selezionato con setOutdev() e' sufficiente chiamare conOut() passando come parametro il carattere da stampare. NOTE Per poter bufferizzare i caratteri dalle fonti di input e' necessario che un interrupt ne controlli periodicamente lo stato, inoltre bisogna effettuare il debouncing dei tasti della K51. Tale interrupt viene generato dall'azzeramento del Timer 0, pertanto se si vuole usare la K51-AVR come input si deve rinunciare ad impiegare il Timer 0. Per poter utilizzare il display LCD e' indispensabile togliere l'integrato in IC12 ed aprire il jumper J4, perdendo la possibilita' di utilizzare il tasto T3, allo scopo di evitare che questi componenti creino conflitti con i segnali del display LCD. Sulla stessa linea del tasto T4 e' collegata l'uscita periodica del Real Time Clock PCF8583, che genera pressioni spurie. Per poter utilizzare il tasto 4 bisogna rimuovere l'RTC (IC7) o programmarlo in modo da bloccare il segnale periodico. 09.01.2001 by Graziano Gaiba (per la sola K51) (Versione originale in BASCOM 8051) 25.07.2003 by Graziano Gaiba Traduzione in uC/51 e versione per ATMEL AT89c51 o superiore. */ #include #include #include #include #define FALSE 0x00 // Valori booleani #define TRUE 0xFF #define LF 0x0A #define CRET 0x0D // Gestione debouncing #define DEBITER 10 // Gestione del cursore #define BLINK 0x10 #define STEADY 0x00 // Codici per dispositivi di input ed output #define SER 0x01 #define K51 0x02 // Valore con cui ricaricare Timer 0 per scandire i tasti della K51 ogni 20 msec #define LoadtimerH 0xB7 #define LoadtimerL 0xFF // Iterazioni del debouncing prima che un tasto di consideri acqiuisito #define Debiter 10 // Massima dimensione del buffer di ricezione per caratteri dalla seriale #define Maxchar 5 // Massima dimensione del buffer di ricezione per i tasti della K51-AVR #define Maxtasti 2 // Massimo numero di parametri per un comando dell'emulazione terminale #define Maxpar 3 // Prefisso dei comandi di emulazione terminale (Carattere Esc) #define Prefix 0x1B // Varie // Durate (in cicli di interrupt) dei varii suoni #define BIP 5 #define BELL 25 /***************** 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 /************************************************************************/ // 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 }; // 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}; // Pattern per creare il nuovo carattere (r) const unsigned char pattern_r[]={253, 245, 249, 245, 245, 225, 226, 252}; // Variabili globali gestione I2C BUS con ATMEL AT89c51 o superiori bit unsigned char SDA @ 0xB6; // Pin SDA=P3.6 bit unsigned char SCL @ 0xB7; // Pin SCL=P3.7 // Variabili globali gestione tasti e buzzer bit unsigned char T1 @ 0x90; // P1.0 bit unsigned char T2 @ 0x91; // P1.1 bit unsigned char T3 @ 0x92; // P1.2 bit unsigned char T4 @ 0xB2; // P3.2 bit unsigned char BUZ @ 0xB5; // P3.5 // Variabili globali gestione display LCD bit unsigned char E @ 0x94; // Indirizzo pin di enable (P1.4) bit unsigned char RS @ 0x93; // Indirizzo pin di RS (P1.3) bit unsigned char db4 @ 0x95; // Indirizzo DB4 (P1.5) bit unsigned char db5 @ 0x96; // Indirizzo DB5 (P1.6) bit unsigned char db6 @ 0x97; // Indirizzo DB6 (P1.7) bit unsigned char db7 @ 0x92; // Indirizzo DB7 (P1.2) // Variabili globali che memorizzano i dispositivi da usare per input e output inear unsigned char indev, outdev; // Variabili globali per gestione comandi inear unsigned char codice, comando; inear unsigned char par[Maxpar]; // Parametri del comando // Variabili globali per memorizzare le coordinate del cursore inear unsigned char riga, colonna; // Variabili globali per gestire l'I/O bufferizzato inear unsigned char bufferK51[Maxtasti]; // Buffer tasti letti dalla K51 inear unsigned char bufferSER[Maxchar]; // Buffer per porta seriale near unsigned char idxSER, idxK51; // Indici per i buffer near unsigned char idxCarSER, idxCarK51; // Prossimo carattere da leggere near unsigned char debCnt; // Contatore debouncing near unsigned char debCod; // Codice tasto premuto near unsigned char debChar; // Tasto sotto debouncing // Variabili globali di uso vario inear unsigned char buzCnt; // Temporizzazione buzzer /********* 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*11059200)/(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 rit_45usec(unsigned int n) /* Effettua un ritardo software di n volte 46,40 microsecondi, calibrato su un Clock di CPU da 14.7456 MHz, a seconda della CPU montata. */ { unsigned int r; do { for (r=0 ; r<20 ; r++) ; n--; } while(n>0); } 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 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 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 } // 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 nei due display a destra. Es: 255= FFH, 32= 20H ecc. Parametri: Ingresso : numero da visualizzare in HEX Uscita : nulla **************************************************************************/ void vis_byte(unsigned char t) { unsigned char h, l; cifre(t, &h, &l); // Scomposizione in 2 nibble del valore di t // Nibble alto e nibble basso 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(0); // Spegne DY1 rdi2c_bit(); wri2c_byte(0); // Spegne DY2 rdi2c_bit(); wri2c_byte(digit(h)); // Trasforma in "7 segmenti" ed invia a DY3 rdi2c_bit(); wri2c_byte(digit(l)); // Trasforma in "7 segmenti" ed invia a DY4 rdi2c_bit(); stopi2c(); // Stop sequence } /************************ Visualizza un word in HEX *********************** Questa procedura permette di visualizzare una word in esadecimale. Nei primi 2 display (DY1 e DY2) viene visualizzato il byte alto, mentre nei 2 rimanenti il byte basso (DY3 e DY4). nei due display a destra. Es: 65535= FFFFH, 257= 101H ecc. Parametri: Ingresso : numero da visualizzare in HEX Uscita : nulla **************************************************************************/ void vis_word(unsigned int t) { unsigned char h, l; // Scomposizione in 2 nibble della parte alta di t cifre(t>>8, &h, &l); 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(h)); // Scrive in DY1 il nibble piu' significativo rdi2c_bit(); wri2c_byte(digit(l)); // Scrive in DY1 il nibble successivo rdi2c_bit(); // Scomposizione in 2 nibble della parte bassa di t cifre(t & 0xff, &h, &l); wri2c_byte(digit(h)); // Scrive in DY3 il nibble piu' significativ rdi2c_bit(); wri2c_byte(digit(l)); // Scrive in DY4 il nibble successivo rdi2c_bit(); stopi2c(); // Stop sequence } // Attiva il buzzer per 5 cicli di interrupt (circa un decimo di secondo) void bip(void) { buzCnt=BIP; BUZ=0; } // Attiva il buzzer per 25 cicli di interrupt (circa mezzo secondo) void bell(void) { buzCnt=BELL; BUZ=0; } // modo==0 -> Scrive un comando // modo!=0 -> Scrive un dato void wr_nibble(unsigned char d, unsigned char modo) { if(modo) // Comandi o dati { RS=1; } else { RS=0; } // Scrittura del dato // Il display viene gestito a nibbles, il nibble da inviare si trova nel // nibble alto del parametro d. // Quando si manda un byte, si deve inviare prima il nibble alto. // Invia il bit DB7 if(d & 0x80) { db7=1; } else { db7=0; } // Invia il bit DB6 if(d & 0x40) { db6=1; } else { db6=0; } // Invia il bit DB5 if(d & 0x20) { db5=1; } else { db5=0; } // Invia il bit DB4 if(d & 0x10) { db4=1; } else { db4=0; } E=1; // Abilitazione display modo=modo; // Ritardo E=0; // Disabilitazione display } void putc_lcd(unsigned char c) { wr_nibble(c & 0xF0, 1); // Il display viene gestito a nibbles, wr_nibble(c << 4, 1); // prima viene scritto il nibble alto rit_45usec(1); // del dato poi quello basso } void puts_lcd(unsigned char *s) { while(*s) putc_lcd(*s++); } //************************************************************************** // Per ulteriori informazioni, fare riferimento allo schema elettrico della // K51-AVR //************************************************************************** // Inizializzazione del display per due righe, caratteri da 5x7, cursore ON void init_lcd(void) { wr_nibble(0x30, 0); ritardo(5); // Ritardo 5 msec wr_nibble(0x30, 0); // Ripete lo stesso comando altre 3 volte con ritardi diversi rit_45usec(3); // Ritardo 100 usec wr_nibble(0x30, 0); rit_45usec(1); wr_nibble(0x20, 0); rit_45usec(1); wr_nibble(0x20, 0); wr_nibble(0x80, 0); rit_45usec(1); wr_nibble(0x00, 0); wr_nibble(0xd0, 0); rit_45usec(1); wr_nibble(0x00, 0); wr_nibble(0x60, 0); rit_45usec(1); wr_nibble(0x00, 0); wr_nibble(0x10, 0); ritardo(3); wr_nibble(0x80, 0); wr_nibble(0x00, 0); rit_45usec(1); } // Le coordinate vanno da x=0 e y=0 (angolo in alto sinistra) // fino a x=1 e y=19 (angolo in basso a destra) void locate_lcd(unsigned char x, unsigned char y) { unsigned char p; p=(x ? 0xc0 : 0x80) + y; p|=0x80; // Comando di settaggio posizione wr_nibble(p & 0xf0, 0); wr_nibble(p << 4, 0); rit_45usec(1); } void set_cursor_lcd(unsigned char status) { wr_nibble(0, 0); wr_nibble(status | 0xE0, 0); // Display e cursore ON comunque rit_45usec(1); } // Procedura che ridefinisce il carattere di codice specificate // dall'utente usando il pattern passato dall'utente. void set_usr_chr(unsigned char ch, unsigned char *pattern) { unsigned char i; wr_nibble(0x00, 0); // Display on, cursor off, blink off wr_nibble(0xc0, 0); i=0x40 | (ch<<3); // Determina comando Set CG RAM address per wr_nibble(i & 0xF0, 0); // carattere utente inserito e wr_nibble(i << 4, 0); // lo fornisce al display for(i=0; i<8; i++) // Invia il pattern: prima il nibble alto { wr_nibble(pattern[i] & 0xF0, 1); wr_nibble(pattern[i] << 4, 1); } wr_nibble(0x00, 0); // Display on, cursor off, blink on wr_nibble(0xd0, 0); wr_nibble(0x00, 0); // Cursor home wr_nibble(0x20, 0); ritardo(1); } // Sposta il display n colonne a destra void shift_display_right(unsigned char n) { while(n--) { wr_nibble(0x10, 0); wr_nibble(0xC0, 0); } } // Sposta il display n colonne a sinistra void shift_display_left(unsigned char n) { while(n--) { wr_nibble(0x10, 0); wr_nibble(0x80, 0); } } // Corregge il valore delle variabili riga e colonna per tenere conto della // posizione del cursore nel display LCD internamente al programma. void aggiustaXY(void) { if(colonna==20) // Se si va oltre l'ultima colonna { colonna=0; // Si ritorna alla prima riga++; // Della riga successiva if(riga==2) // Se si va oltre l'ultima riga { riga=0; // Si ritorna alla prima riga } } locate_lcd(riga, colonna); // Posiziona il cursore } // Interprete dei comandi per il display LCD void elaboraLCD(unsigned char c) { unsigned char val[4]; // Usati da uno dei comandi if(c==Prefix) // Se e' arrivato il prefisso di comando { if(! comando) // E nessun comando e' in esecuzione { comando=0x80; // Entra in modo comando } } else { switch(comando) { case 0: // non c'e' comando in corso // COMANDI IMMEDIATI switch(c) // Comandi immediati, noti anche come // caratteri di controllo { case 0x07: // Carattere BEL bell(); // Fa suonare il buzzer break; case 0x0A: // Carattere LF line feed riga++; // Riga successiva if(riga==2) // Ovvero la prima se si { riga = 0; // va oltre l'ultima } locate_lcd(riga, colonna); // Sposta il cursore break; default: // Caratteri stampabili putc_lcd(c); // Scrivi il carattere su LCD colonna++; aggiustaXY(); // posiziona il cursore break; } break; case 0x80: // Se deve acquisire un comando switch(c) // Attiva il riconoscitore { // COMANDO CON ZERO PARAMETRI case '1': // Comando senza parametri init_lcd(); // Esecuzione immediata riga = 0; // Aggiorna variabili di colonna = 0; // posizione cursore comando = 0; // Fine comando break; // COMANDI CON PARAMETRI case '2': // Comando con un parametro comando = 1; // Dichiara n. parametri codice = c; // Memorizza il codice break; case '3': // Comando con due parametri comando = 2; // Dichiara n. parametri codice = c; // Memorizza il codice break; default: // Comando non riconosciuto comando = 0; // Esce da modo comandi codice = 0; break; } break; // Altrimenti acquisisce i parametri di un comando, il numero di parametro e' // il valore stesso della variabile comando // RACCOLTA PARAMETRI default: par[--comando] = c; // Memorizza parametro,aggiorna n. parametri // ESECUZIONE COMANDI CON PARAMETRI if(! comando) // Ci sono tutti i parametri { switch(codice) // Esegue il comando { case '2': // Comando con un parametro if(par[0] == 'b') // Se il parametro e' "b" { set_cursor_lcd(BLINK); // Il cursore lampeggia } else // Altrimenti { if(par[0] == 'n') // Se il parametro e' "n" { set_cursor_lcd(STEADY); // Il cursore non lampeggia } } break; case '3': // Comando con due parametri // i parametri vengono memorizzati in ordine // inverso if(par[1]=='1') // Se il primo parametro e' "1" { putc_lcd(par[0]); // Stampa il secondo colonna++; } else // Altrimenti { sprintf(val, "%X", par[0]); // Scrivi il valore esadecimale putc_lcd(val[0]); colonna++; aggiustaXY(); putc_lcd(val[1]); colonna++; } aggiustaXY(); // Aggiorna le coordinate break; } //End switch(codice) codice=0; } //End if(!comando) } //End switch(comando) } //End if(prefix) } // Procedura di risposta all'interrupt del Timer 0. // Si occupa di temporizzare il suono del buzzer, aggiornare il buffer della // seriale se ci sono caratteri ricevuti e aggiornare il buffer dei tasti K51 // se un tasto e' premuto. void scan(void) interrupt { TR0=0; // Ferma il Timer 0 TH0=LoadtimerH; TL0=LoadtimerL; TR0=1; // Riattiva il Timer 0 if(buzCnt) { buzCnt--; } else { BUZ=1; // Spegne il buzzer } /* Lettura della porta seriale. Se il bit ri e' 1 c'e' un carattere in attesa. Viene memorizzato nell'array bufferser che viene gestito come un buffer circolare, ovvero una volta riempito si torna al suo inizio sovrascrivendo il contenuto. */ if(kbhit()) // Se c'e' un carattere { bufferSER[idxSER++] = getc(); // Leggilo, salvalo e incrementa indice if(idxSER==Maxchar) // Se indice oltre il massimo { idxSER = 0; // Riportalo all'inizio } } // Acquisizione dei tasti della K51. Se il corrispondente pin del micro e' a 0 // il tasto e' premuto debCod = 0; // Se tasto 1 e' premuto if(! T1) // memorizza il codice { debCod = '1'; } if(! T2) // Se tasto 2 e' premuto memorizza il codice { debCod = '2'; } // I tasti 3 e 4 non vengono acquisiti. Vedi note in cima al sorgente. /* if(! T3) // Se tasto 3 e' premuto memorizza il codice { debCod = '2'; } if(! T3) // Se tasto 3 e' premuto memorizza il codice { debCod = '2'; } */ /* Debouncing. Il codice eventualmente letto diventa il nuovo tasto sotto debouncing a meno che non fosse gia' sotto debouncing, in tal caso viene incrementato il contatore di debouncing. Quando il contatore raggiunge il valore limite, il tasto si considera acquisito e viene messo nel buffer K51. */ if(debCod) // Se c'e' un codice { if(debCod==debChar) // ed e' sotto debouncing { debCnt++; // incrementa contatore } else { // Altrimenti debChar = debCod; // nuovo tasto debCnt = 0; // azzera il contatore } } if(debCnt==Debiter) // Se il contatore eccede { debCnt = 0; // azzera il contatore bufferK51[idxK51++] = debChar; // memorizza il tasto e incrementa indice if(idxK51==Maxtasti) // Se indice eccede { idxK51 = 0; // riportalo all'inizio } buzCnt=BIP; // Emette un bip BUZ=0; } } IRQ_VECTOR(scan, TIMER0); // Imposta il dispositivo di ingresso void setIndev(unsigned char d) { indev=d; } // Imposta il dispositivo di uscita void setOutdev(unsigned char d) { outdev=d; } // Legge un byte dal dispositivo di input (non sospensiva) unsigned char conIn(void) { unsigned char in; in=0; // Nessun carattere letto switch(indev) { // Legge dalla linea seriale case SER: if(idxCarSER!=idxSER) // Se c'e' un carattere { in=bufferSER[idxCarSER]; // legge dal buffer if(++idxCarSER==Maxchar) // Se e' alla fine del buffer { idxCarSER=0; // torna all'inizio } } break; // Legge la tastiera della K51 case K51: if(idxCarK51!=idxK51) // Se c'e' un carattere { in=bufferK51[idxCarK51]; // legge dal buffer if(++idxCarK51==Maxchar) // Se e' alla fine del buffer { idxCarK51=0; // torna all'inizio } } break; } // End switch(indev) return in; } // Scrive un byte nel dispositivo di output void conOut(unsigned char out) { switch(outdev) { case SER: putc(out); // Invia alla linea seriale break; case K51: elaboraLCD(out); // Interpreta comando per LCD break; } // End switch(outdev) } // Determina la presenza di byte non letti nel buffer del dispositivo di input unsigned char conStatus(void) { unsigned char r; switch(indev) { case SER: if(idxCarSER==idxSER) // Se gli indici coincidono { r=0; // Riporta nessun carattere } else { r=1; // Viceversa } break; case K51: if(idxCarK51==idxK51) // Se gli indici coincidono { r=0; // Riporta nessun carattere } else { r=1; // Viceversa } break; } // End switch(indev) return r; // Restituisce la risposta } void demo_init(void) { unsigned char i; // Inizializzazione variabili usate globalmente comando = 0; // Cancella eventuali comandi codice = 0; // del terminale in corso riga=0; // Il cursore parte in alto a destra colonna=0; idxSER = 0; // Valore iniziale degli idxCarSER = 0; // indici dei buffer e idxK51 = 0; // di chi legge i buffer idxCarK51 = 0; debCod = 0; // Azzera codice tasto e debChar = 0; // tasto sotto debouncing debCnt = 0; // Azzera contatori di buzCnt = 0; // debouncing e buzzer // 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); TMOD=(TMOD & 0xF1) | 0x01; // Timer0 = timer, gate internal, mode 1 TR0=0; // Ferma il Timer 0 TH0=LoadtimerH; TL0=LoadtimerL; TR0=1; // Riattiva il Timer 0 ET0=1; // Abillita interrupt di Timer 0 } void main(void) { ritardo(2); iniser(19200); TI=0; RI=0; // Commentare se si usa la seriale in polling (SIOTYPE=p oppure k) ES=1; // Abilita interrupt porta seriale EA=1; // Abilitazione interrupt clrscr(); demo_init(); // Inizializzazione periferiche e variabili init_lcd(); // Pulisce il display locate_lcd(0, 0); set_usr_chr(0, pattern_r); // Associa al carattere 0 il pattern di (r) bip(); while(1) { setIndev(SER); // Selezione linea seriale per input setOutdev(K51); // Seleziona il display della K51 per output if(conStatus()) // Se c'e' un carattere... { conOut(conIn()); //...lo acquisisce e lo manda al display } setIndev(K51); // Selezione tastiera K51 per input setOutdev(SER); // Seleziona linea seriale per output if(conStatus()) // Se un tasto e' premuto... { conOut(conIn()); //...lo acquisisce e manda alla linea seriale } } // Loop infinito }