{ **************************************************************** * File: gmbrtc.ppas - Ver. 1.1 * * Compilatore: mikroPascal for PIC by mikroElektronica * * IDE: mikroPascal for PIC by mikroElektronica * * Versione Compilatore: 2.1.6.0 * * Schede: GMM 4620 + GMB HR168 * * 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 09.03.05 * **************************************************************** GMM 4620 e' dotata di un Orologio in Tempo Reale, RTC, di bordo, che e' tamponato mediante la batteria al Litio di bordo, scollegabile. Tale componente puo' essere utilizzato per la schedulazione di sequenze di operazioni o per creare etichette temporali da associare a determinati eventi. L'interfaccia tra RTC e CPU e' in I2C BUS. Il programma Demo GMBRTC permette di utilizzare immediatamente la funzioinalita' dell'orologio, sia in lettura che in scrittura, mediante una serie di funzioni largamente commentate. Dette funzioni si preoccupano sia di comunicare con il componente, sia di visualizzare i dati. NOTA: L'ultima locazione della SRAM dell'RTC ($FF) e' riservata. Rel 1.1 - by Graziano Gaiba Demo di utilizzo dell'RTC I2C BUS usando una GMB HR168 pilotata da un Mini Modulo GMM 4620 } { ***************** Definizioni del compilatore ****************** } program gmbrtc; { *************** Dichiarazione delle contanti ******************* } const RTCSLA:byte = $A0; const ASC_s:byte = $73; const ASC_sU:byte = $53; { ******************* Dichiarazione delle Variabili ******************** } // Variabili di uso generico var chr_in: byte; txt: string[3]; // Usate per la gestione dell'RTC rtc_anno, rtc_mese, rtc_settimana, rtc_giorno: byte; rtc_ora, rtc_minuto, rtc_secondo: byte; Label for_ever; { ******************* Definizione delle procedure ********************* } // Inizializzazione direzione segnali della CPU procedure Init_cpu; begin ADCON1 :=$0f; // Imposta come I/O digitale i pin AN0..12 CMCON :=$07; // Imposta come I/O digitale RA0..4 { Gli ingressi optoisolati di CN1 sono: IN1-1 <-> RA0 IN2-1 <-> RA1 IN3-1 <-> RB0 IN4-1 <-> RB1 IN5-1 <-> RA4 IN6-1 <-> RC0 IN7-1 <-> RC1 IN8-1 <-> RC5 } trisa := trisa or $13; trisb := trisb or $03; trisc := trisc or $23; { Gli ingressi optoisolati di CN2 sono: IN1-2 <-> RD0 IN2-2 <-> RD1 IN3-2 <-> RD2 IN4-2 <-> RD3 IN5-2 <-> RD4 IN6-2 <-> RD5 IN7-2 <-> RD6 IN8-2 <-> RD7 } trisd := $ff; { Le uscite relays di CN3 sono: OUT A1 <-> RB4 OUT A2 <-> RB5 OUT B1 <-> RB6 OUT B2 <-> RB7 OUT C1 <-> RB3 OUT C2 <-> RB2 } trisb := trisb and $03; { Le uscite relays di CN4 sono: OUT D1 <-> RA3 OUT D2 <-> RC2 (J10 in posizione 3-4) } trisa := trisa and $f7; trisc := trisc and $fb; // Inizialzza modulo USART (8 bit, 19200 baud rate, // no parity) Usart_init(19200); end; // Invia una stringa di caratteri alla porta seriale procedure print_USART(var txt: string[100]); var temp1, temp2: byte; begin temp1 := txt[0]; for temp2 := 1 to temp1 do begin USART_Write(txt[temp2]); end; end; // Invia CR + LF procedure print_CRLF; begin USART_Write(10); USART_Write(13); end; // Pulisce lo schermo, inviando 25 volte CR + LF. procedure clrscr; var temp: byte; begin for temp := 0 to 24 do begin print_CRLF; end; end; // Chiede la pressione di un tasto procedure wait_key; var temp: byte; begin print_usart('Premere un tasto...'); repeat begin nop; end; until USART_Data_Ready = 1; temp := USART_Read; end; { Procedura per impostare lo stato dei relays sui connettori CN3 e CN4. A seconda del valore dei bits di port_val, ogni relay viene attivato (contatto chiuso) o disattivato (contatto aperto). I bit di port_val hanno il seguente significato: -- CN3 port_val.0 pilota il relay OUT A1 port_val.1 pilota il relay OUT A2 port_val.2 pilota il relay OUT B1 port_val.3 pilota il relay OUT B2 port_val.4 pilota il relay OUT C1 port_val.5 pilota il relay OUT C2 -- CN4 port_val.6 pilota il relay OUT D1 port_val.7 pilota il relay OUT D2 Ogni bit ha il seguente significato: bit Significato 0 Relay disattivato (contatto aperto) 1 Relay attivato (contatto chiuso) } procedure set_relays(port_val: byte); begin // I relays sono pilotati in logica complementata , quindi anche port_val // deve essere complementato port_val:=port_val xor $ff; if TestBit(port_val, 0) = 0 then begin ClearBit(portb, 4); end else begin SetBit(portb, 4); end; if TestBit(port_val, 1) = 0 then begin ClearBit(portb, 5); end else begin SetBit(portb, 5); end; if TestBit(port_val, 2) = 0 then begin ClearBit(portb, 6); end else begin SetBit(portb, 6); end; if TestBit(port_val, 3) = 0 then begin ClearBit(portb, 7); end else begin SetBit(portb, 7); end; if TestBit(port_val, 4) = 0 then begin ClearBit(portb, 3); end else begin SetBit(portb, 3); end; if TestBit(port_val, 5) = 0 then begin ClearBit(portb, 2); end else begin SetBit(portb, 2); end; if TestBit(port_val, 6) = 0 then begin ClearBit(porta, 3); end else begin SetBit(porta, 3); end; if TestBit(port_val, 7) = 0 then begin ClearBit(portc, 2); end else begin SetBit(portc, 2); end; end; // Indica che un acknowledge atteso non e' stato ricevuto. procedure I2CBUSNotAck; begin print_USART('Acknowledge non ricevuto.'); print_crlf; // Disables I2CBUS ClearBit(sspcon1, 5); end; {Legge un byte dal dispositivo il cui slave address e' contenuto nella variable i2c_slave e il cui indirizzo e' contenuto nella variabile i2c_addr. Il dato letto viene restituito. } function I2CBUSReadByte(i2c_slave: byte; i2c_addr: byte): byte; var value: byte; begin ClearBit(sspcon1, 5); SetBit(sspcon1, 5); // Genera I2CBUS Start. I2C_start; // Invia Slave Address if I2C_wr(i2c_slave) <> 0 then begin // Se acknowledge non ricevuto, segnala un errore I2CBUSNotAck; end; // Invia indirizzo if I2C_wr(i2c_addr) <> 0 then begin // Se acknowledge non ricevuto, segnala un errore I2CBUSNotAck; end; // Genera I2CBUS Start ripetuto. I2C_Repeated_Start; // Invia Slave Address per la lettura if I2C_wr(i2c_slave or $01) <> 0 then begin // Se acknowledge non ricevuto, segnala un errore I2CBUSNotAck; end; // Riceve il dato e invia NOT acknowledge value := I2C_Rd(0); // Genera stop I2C_Stop; Result := value; end; {Scrive un byte al dispositivo il cui slave address e' contenuto nella variablie i2c_slave e il cui indirizzo e' contenuto nella variabile i2c_addr. Il dato da scrivere viene messo nella variabile i2c_val. } procedure I2CBUSWriteByte(i2c_slave: byte; i2c_addr: byte; i2c_data: byte); begin ClearBit(sspcon1, 5); SetBit(sspcon1, 5); // Genera I2CBUS Start. I2C_start; // Invia Slave Address if I2C_wr(i2c_slave) <> 0 then begin // Se acknowledge non ricevuto, segnala un errore I2CBUSNotAck; end; // Invia indirizzo if I2C_wr(i2c_addr) <> 0 then begin // Se acknowledge non ricevuto, segnala un errore I2CBUSNotAck; end; // Invia il dato if I2C_wr(i2c_data) <> 0 then begin // Se acknowledge non ricevuto, segnala un errore I2CBUSNotAck; end; // Genera stop I2C_Stop; end; {Inserimento di un valore decimale fino a cinque cifre. Il parametro in ingresso e' il numero di cifre, restituisce una word contenente il valore immesso. Gestisce il backspace (ASCII 08) ed accetta cifre in input da '0' a '9'. Iniva un carattere di bell (ASCII 07) quando riceve un dato non accettabile per l'input, o si tenta un inserimento o una cancellazione fuori dal range ammesso. } function inputDEC(n_char: byte): word; var str_input:array[6] of byte; idx, chr_input: byte; pow_of_10:array[6] of word; value: word; begin pow_of_10[1] := 1; pow_of_10[2] := 10; pow_of_10[3] := 100; pow_of_10[4] := 1000; pow_of_10[5] := 10000; // Indice deve partire da 1 idx := 1; Inc(n_char); // Nessun carattere in input chr_input := 0; // Valore iniziale value := 0; // Continua finche' non riceve un carattere CR while chr_input <> 13 do begin // Se un dato e' pronto if usart_data_ready = 1 then begin // Leggilo chr_input := USART_Read; // Se e' backspace (ASCII BS) if chr_input = 8 then begin // e il buffer non e' vuoto if idx > 1 then begin // Invia dato letto USART_Write(chr_input); // Cancella carattere USART_Write(32); USART_Write(chr_input); // Elimina ultimo dato letto Dec(idx); end else begin // Carattere bell per indicare un errore USART_Write(7); end; end; // Se CR ricevuto, elabora il valore e restituiscilo if chr_input = 13 then begin n_char := 0; while idx > 1 do begin Dec(idx); Inc(n_char); value := value + word((str_input[n_char] - 48)) * pow_of_10[idx]; end; end else begin // Se il carattere puo' essere inserito if idx < n_char then begin // Se il dato ricevuto e' valido: // 0..9 if (chr_input >= 48) or(chr_input <= 57) then begin // Invia il dato letto USART_Write(chr_input); // Memorizzalo nel buffer di ingresso str_input[idx] := chr_input; // Punta alla posizione successiva Inc(idx); end; end else begin // Carattere bell per indicare un errore USART_Write(7); end; end; end; end; print_CRLF; Result := value; end; {Imposta data e ora sull'RTC I2C BUS PCF8583. Salva offset anno sull'ultima locazione SRAM, l'ultima locazione della SRAM sono riservate } procedure set_rtc(rtc_settimana: byte; rtc_giorno: byte; rtc_mese: byte; rtc_anno: byte; rtc_ora: byte; rtc_minute: byte; rtc_secondo: byte); var dummy: byte; begin // Ferma RTC I2CBUSWriteByte(RTCSLA,$0,$84); // Setta centinaia di secondi i2cbuswritebyte(RTCSLA,$1,$0); // Setta secondi dummy := Dec2Bcd(rtc_secondo); i2cbuswritebyte(RTCSLA,$2,dummy); // Setta minuti dummy := Dec2Bcd(rtc_minuto); i2cbuswritebyte(RTCSLA,$3,dummy); // Setta ORE,formato 24h dummy := Dec2Bcd(rtc_ora); i2cbuswritebyte(RTCSLA,$4,dummy); // Setta giorno ed anno = 0 dummy := Dec2Bcd(rtc_giorno); i2cbuswritebyte(RTCSLA,$5,dummy); // Setta mese,settimana dummy := Dec2Bcd(rtc_mese); dummy := dummy or (rtc_settimana shl 5); i2cbuswritebyte(RTCSLA,$6,dummy); // Setta timer mode i2cbuswritebyte(RTCSLA,$7,0); // Start RTC i2cbuswritebyte(RTCSLA,$0,0); // Salva offset anno su ultima locazione SRAM i2cbuswritebyte(RTCSLA,$ff,rtc_anno); end; {Legge ora e data dall'RTC I2C BUS PCF8583. Legge offset anno dall'ultima locazione SRAM, le otto ultime locazioni della SRAM sono riservate } procedure get_rtc(var rtc_settimana: byte; var rtc_giorno: byte; var rtc_mese: byte; var rtc_anno: byte; var rtc_ora: byte; var rtc_minuto: byte; var rtc_secondo: byte); var dummy: byte; begin // Legge e determina secondi rtc_secondo := Bcd2Dec(i2cbusreadbyte(RTCSLA, $2)); // Legge e determina minuti rtc_minuto := Bcd2Dec(i2cbusreadbyte(RTCSLA, $3)); // Legge e determina ore rtc_ora := Bcd2Dec(i2cbusreadbyte(RTCSLA, $4)); // Legge e determina giorno ed offset ann dummy := i2cbusreadbyte(RTCSLA, $5); rtc_anno := (dummy and $c0) shr 6; dummy := dummy and $3f; rtc_giorno := Bcd2Dec(dummy); // Legge e determina giorno settimana e mese dummy := i2cbusreadbyte(RTCSLA, $6); rtc_settimana := (dummy and $e0) shr 5; dummy := dummy and $1f; rtc_mese := Bcd2Dec(dummy); // Calcola anno nel range 00..99 // Offset dell'anno e' nella locazione $FF della SRAM interna del PCF. // Le otto ultime locazioni della SRAM sono riservate. dummy := i2cbusreadbyte(RTCSLA, $ff); rtc_anno := (dummy + rtc_anno) mod 100; end; { ********************** Programma principale *************************** } begin Init_cpu; // Spegne i relays set_relays(0); // Inizializza la periferica hardware I2C BUS I2C_init(100000); for_ever: clrscr; print_USART('Demo RTC I2C BUS Rel 1.1 per GMM 4620 rel 120304 e GMB HR168 rel 110104'); print_CRLF; print_CRLF; print_USART('Ora e data memorizzate nel modulo RTC in I2C BUS vengono visualizzate'); print_CRLF; print_USART('continuamente.'); print_CRLF; print_USART('Si desidera impostare ora e data di RTC (s/n)? '); repeat begin nop; end; until USART_Data_Ready = 1; chr_in := USART_Read; USART_Write(chr_in); if (chr_in = ASC_s) or (chr_in = ASC_sU) then begin print_CRLF; print_CRLF; print_USART('Giorno della settimana (0 Domenica, 1 Lunedi, .. , 6 Sabato): '); rtc_settimana := inputDEC(1); print_USART('Giorno: '); rtc_giorno := inputDEC(2); print_USART('Mese: '); rtc_mese := inputDEC(2); print_USART('Anno: 20'); rtc_anno := inputDEC(2); print_USART('Ora: '); rtc_ora := inputDEC(2); print_USART('Minuto: '); rtc_minuto := inputDEC(2); print_USART('Secondo: '); rtc_secondo := inputDEC(2); print_CRLF; set_rtc(rtc_settimana,rtc_giorno,rtc_mese,rtc_anno,rtc_ora, rtc_minuto,rtc_secondo); end; print_CRLF; repeat begin get_rtc(rtc_settimana,rtc_giorno,rtc_mese,rtc_anno,rtc_ora, rtc_minuto,rtc_secondo); case rtc_settimana of 0: print_USART('Domenica '); 1: print_USART('Lunedi '); 2: print_USART('Martedi '); 3: print_USART('Mercoledi '); 4: print_USART('Giovedi '); 5: print_USART('Venerdi '); 6: print_USART('Sabato '); end; // Stampa giorno ByteToStr(rtc_giorno, txt); if length(txt) = 1 then begin USART_Write('0'); end; print_USART(txt); USART_Write('.'); // Stampa mese ByteToStr(rtc_mese,txt); if length(txt) = 1 then begin USART_Write('0'); end; print_USART(txt); print_USART('.20'); // Stampa anno ByteToStr(rtc_anno, txt); if length(txt) = 1 then begin USART_Write('0'); end; print_USART(txt); print_USART(' '); // Stampa ora ByteToStr(rtc_ora, txt); if length(txt) = 1 then begin USART_Write('0'); end; print_USART(txt); USART_Write(':'); // Stampa minuto ByteToStr(rtc_minuto, txt); if length(txt) = 1 then begin USART_Write('0'); end; print_USART(txt); USART_Write('.'); // Stampa secondo ByteToStr(rtc_secondo, txt); if length(txt) = 1 then begin USART_Write('0'); end; print_USART(txt); USART_Write(13); end; until False; goto for_ever; end.