{ **************************************************************** * File: k51termo.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 + 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 08.03.05 * **************************************************************** Questo demo permette di pilotare una periferica I2C BUS a bordo della K51-AVR, DS1621, attraverso il modulo mini-BLOCK GMB HR168 ed un mini modulo grifo(r). Il Ds1621 e' un termometro digitale programmabile, con risoluzione di mezzo grado Celsius, tutte le operazioni di programmazione e acquisizione della temperatura avvengono tramite l'interfaccia seriale sincrona I2C BUS. Il demo mostra sui display a 7 segmenti della K51-AVR la temperatura misurata. Rel 1.1 - by Graziano Gaiba Demo per l'utilizzo del DS1621 a bordo della K51-AVR usando una GMB HR168 pilotata da un Mini Modulo GMM 4620 } { ***************** Definizioni del compilatore ****************** } program k51termo; { *************** Dichiarazione delle contanti ******************* } const Saa1064:byte = $38; // Slave address SAA1064 const Wsaa1064:byte = $70; // Slave address SAA1064 in Write const Rsaa1064:byte = $71; // Slave address SAA1064 in Read //****************** Indirizzi registri Saa1064 ******************** const Ctb:byte = 0; // Registro di controllo const Dig1:byte = 1; // Cifra 1 const Dig2:byte = 2; // Cifra 2 const Dig3:byte = 3; // Cifra 3 const Dig4:byte = 4; // Cifra 4 // // ********************************************************************** // const Ds1621:byte = $4C; // Slave address DS1621 const Wds1621:byte = $98; // Slave address DS1621 in Write const Rds1621:byte = $99; // Slave address DS1621 in Read // ***************** Commands list of DS1621 **************************** const Cfg:byte = $AC; // Registro di configurazione DS1621 const Rtemp:byte = $AA; // Lettura temperatura in 2 bytes const Strct:byte = $EE; // Inizia conversione temperatura const Stpct:byte = $22; // Ferma conversione temperatura { ******************* Dichiarazione delle Variabili ******************** } // Variabili di uso generico var Tmp: byte; // Cifre della temperatura Units, Hundreds, Tenth: byte; // Usate per la gestione dell'I2C BUS hardware i2c_buffer: array[10] of byte; // Conversione letta da DS1621 Temp_h, Temp_l: byte; { ******************* 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; // Inizializza I2C BUS hardware procedure I2CBUSInit; begin // Inizializza I2C BUS // Configura RC3 e RC4 come inputs SetBit(trisc, 3); SetBit(trisc, 4); // Abilita modula SSP in master I2C BUS mode, clock 50 kHz ClearBit(sspstat, 6); sspadd := $32; sspcon1 := $28; end; // Genera I2C BUS start procedure I2CBUSStart; begin SetBit(sspcon2, 0); while TestBit(sspcon2, 0) = 1 do begin nop; end; ClearBit(pir1, 3); end; // Genera I2C BUS start ripetuta procedure I2CBUSRepStart; begin SetBit(sspcon2, 1); while TestBit(sspcon2, 1) = 1 do begin nop; end; ClearBit(pir1, 3); end; // Genera I2C BUS stop procedure I2CBUSStop; begin SetBit(sspcon2, 2); while TestBit(sspcon2, 2) = 1 do begin nop; end; ClearBit(pir1, 3); end; // Invia il contenuto della variabile i2c_data all'I2C BUS procedure I2CBUSSend(i2c_data: byte); begin sspbuf := i2c_data; // Attende completamento dell'ultima operaziane dell'interfaccia hardware I2C while TestBit(pir1, 3) = 0 do begin nop; end; ClearBit(pir1, 3); end; // Legge un dato da I2C BUS e lo restituisce function I2CBUSGet: byte; begin SetBit(sspcon2, 3); while TestBit(sspcon2, 3) = 1 do begin nop; end; Result := sspbuf; // Attende completamento dell'ultima operaziane dell'interfaccia hardware I2C while TestBit(pir1, 3) = 0 do begin nop; end; ClearBit(pir1, 3); end; // Invia NOT acknowledge procedure I2CBUSNACK; begin // Invia NOT acknowledge... SetBit(sspcon2, 5); SetBit(sspcon2, 4); // Attende completamento dell'ultima operaziane dell'interfaccia hardware I2C while TestBit(pir1, 3) = 0 do begin nop; end; ClearBit(pir1, 3); end; // Invia acknowledge procedure I2CBUSACK; begin // Invia acknowledge... ClearBit(sspcon2, 5); SetBit(sspcon2, 4); // Attende completamento dell'ultima operaziane dell'interfaccia hardware I2C while TestBit(pir1, 3) = 0 do begin nop; end; ClearBit(pir1, 3); end; // Indica che un acknowledge atteso non e' stato ricevuto. procedure I2CBUSNotAck; begin print_USART('Acknowledge non ricevuto.'); print_crlf; // Disabilita 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 messo nella variabile i2c_val. } function I2CBUSReadByte(i2c_slave: byte; i2c_addr: byte): byte; begin // Genera I2CBUS Start i2cbusstart; // Invia Slave Address i2cbussend(i2c_slave); // Se acknowledge non ricevuto, segnala un errore if TestBit(sspcon2, 6) = 1 then begin I2CBUSNotAck; end; // Invia indirizzo i2cbussend(i2c_addr); // Se acknowledge non ricevuto, segnala un errore if TestBit(sspcon2, 6) = 1 then begin I2CBUSNotAck; end; // Genera I2CBUS Start ripetuto i2cbusrepstart; // Invia Slave Address per la lettura i2cbussend(i2c_slave or $01); // Se acknowledge non ricevuto, segnala un errore if TestBit(sspcon2, 6) = 1 then begin I2CBUSNotAck; end; // Riceve il dato Result := i2cbusget; // Invia NOT acknowledge. I2CBUSNACK; // Genera stop i2cbusstop; 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 // Genera I2CBUS Start i2cbusstart; // Invia Slave Address i2cbussend(i2c_slave); // Se acknowledge non ricevuto, segnala un errore if TestBit(sspcon2, 6) = 1 then begin I2CBUSNotAck; end; // Invia indirizzo i2cbussend(i2c_addr); // Se acknowledge non ricevuto, segnala un errore if TestBit(sspcon2, 6) = 1 then begin I2CBUSNotAck; end; // Invia il dato i2cbussend(i2c_data); // Se acknowledge non ricevuto, segnala un errore if TestBit(sspcon2, 6) = 1 then begin I2CBUSNotAck; end; // Genera stop i2cbusstop; end; {Invia una sequenza di bytes ad un dispositivo I2C BUS. Lo slave address deve essere contentuo nella variabile i2c_slave. L'utente puo' cambiare la dimensione dell'array nella lista parametri. Il numero di bytes da inviare va nella variabile i2c_to_send. L'utente deve verifcare che non avvengano eventuali overflow. } procedure I2CBUSWrite(i2c_slave: byte; i2c_to_send:byte; var i2c_buffer: array[10] of byte); var i2c_cnt:byte; begin // Genera I2CBUS Start i2cbusstart; // Invia Slave Address i2cbussend(i2c_slave); // Se acknowledge non ricevuto, segnala un errore if TestBit(sspcon2, 6) = 1 then begin I2CBUSNotAck; end; for i2c_cnt := 0 to i2c_to_send - 1 do begin // Invia il dato i2cbussend(i2c_buffer[i2c_cnt]); // Se acknowledge non ricevuto, segnala un errore if TestBit(sspcon2, 6) = 1 then begin I2CBUSNotAck; end; end; // Genera stop i2cbusstop; end; {Legge una sequenza di bytes da un dispositivo I2C BUS. Lo slave address va nella variabile i2c_slave. L'utente puo' cambiare la dimensione dell'array nella lista parametri. I dati ricevuti vengono memorizzati nello stesso array. Il numero di bytes da inviare va nella variabile i2c_to_send. Il numero di bytes da ricevere va nella variabile i2c_to_receive. L'utente deve verificare che non avvengano eventuali overflow. } procedure I2CBUSRead(i2c_slave: byte; i2c_to_send: byte; i2c_to_receive: byte; var i2c_buffer: array[10] of byte); var i2c_cnt: byte; begin // Genera I2CBUS Start. i2cbusstart; // Invia Slave Address i2cbussend(i2c_slave); // Se acknowledge non ricevuto, segnala un errore if TestBit(sspcon2, 6) = 1 then begin I2CBUSNotAck; end; for i2c_cnt := 0 to i2c_to_send - 1 do begin // Invia il dato i2cbussend(i2c_buffer[i2c_cnt]); // Se acknowledge non ricevuto, segnala un errore if TestBit(sspcon2, 6) = 1 then begin I2CBUSNotAck; end; end; // Genera I2CBUS Start ripetuto i2cbusrepstart; // Invia Slave Address per la lettura i2cbussend(i2c_slave or $01); // Se acknowledge non ricevuto, segnala un errore if TestBit(sspcon2, 6) = 1 then begin I2CBUSNotAck; end; for i2c_cnt := 0 to i2c_to_receive - 1 do begin // Legge il dato i2c_buffer[i2c_cnt] := i2cbusget; if i2c_cnt = i2c_to_receive - 1 then begin // Invia NOT acknowledge. I2CBUSNACK; end else begin // Invia acknowledge. I2CBUSaCK; end; end; // Invia lo stop i2cbusstop; end; // Inizializzazione delle periferiche a bordo della K51-AVR procedure K51_init; var dummy: byte; begin // Configurazione del controller dei display a 7 segmenti SAA 1064 repeat begin I2CBUSstart; I2CBUSSend(rsaa1064); dummy := I2CBUSGet; I2CBUSNACK; I2CBUSStop; end; until dummy = 0; i2c_buffer[0] := ctb; // Punta al registro di controllo i2c_buffer[1] := %00100111; // bit0 =1 dynamic mode // bit1 =1 cifre 1+3 non spente // bit2 =1 cifre 2+4 non spente // bit3 =0 no test segmenti // bit4 =0 no corrente segmenti 3mA // bit5 =1 corrente segmenti 6mA // bit6 =0 no corrente segmenti 12mA // bit7 =0 indifferente i2c_buffer[2] := 0; i2c_buffer[3] := 0; i2c_buffer[4] := 0; i2c_buffer[5] := 0; i2cbuswrite(wsaa1064, 6, i2c_buffer); // Configurazione del termometro DS 1621 // Inizializza termometro I2CBUSStart; I2CBUSSend(Wds1621); I2CBUSSend(cfg); I2CBUSStop; // Legge registro di configurazione del DS 1621 dummy := i2cbusreadbyte(wds1621,cfg); // Se la conversione e' abilitata, falla partire if TestBit(dummy, 0) <> 0 then begin {Se la conversione non e' abilitata, abilitarla Scrive registro configurazione bit1= 1 polarita' "1" bit0= 0 conversione continua } i2cbuswritebyte(wds1621,cfg,%00001010); end; i2c_buffer[0] := strct; i2cbuswrite(wds1621, 1, i2c_buffer); end; // Converte una cifra decimalenella rappresentazione a 7-segmenti function DecTo7Seg(value: byte): byte; var temp: byte; begin case value of 0: temp := $3F; 1: temp := $06; 2: temp := $5B; 3: temp := $4F; 4: temp := $66; 5: temp := $6D; 6: temp := $7D; 7: temp := $07; 8: temp := $7F; 9: temp := $6F; 10: temp := $40; 11: temp := 00; end; Result := temp; end; {Questa procedura converte 1 byte da 0 a 255 in temperatura dove da 0 a 125 sono gradi centigradi positivi (+0..+125), mentre da 255 a 201 sono gradi centigradi negativi (-0...-55), in pratica il bit7 indica il segno, i gradi negativi si ottengono con il complemento. Es: Not 255=0, Not 201=54 } procedure Cifre; begin Label TempPositive; Label Temp1; Label Temp2; Label Temp3; Label Temp4; Label Temp_if1; Label Cifre_Units_vale_0; Label Cifre_esci; if Temp_h <= 127 then begin goto TempPositive; end; // Se la temperatura e' negativa, aggiustare il valore facendone // il complemento a 1 Temp_h := Temp_h xor $FF; // Attivo il segno meno Hundreds := 10; goto Temp_if1; TempPositive: if Temp_h <= 99 then begin goto Temp1; end; // Se e' uguale o superiore a 100, attivo le centinaia Hundreds := 1; // Sottraggo 100 Temp_h := Temp_h - 100; goto temp_if1; Temp1: // Non supera 99, spengo la cifra delle centinaia Hundreds := 11; Temp_if1: if Temp_h <= 9 then begin goto Temp2; end; // Supera 9, ricavo la cifra delle decine Tenth := Temp_h div 10; // Ricavo le unita' Tmp := Tenth * 10; Units := Temp_h - Tmp; goto Cifre_esci; Temp2: if Hundreds <> 11 then begin goto Temp3; end; // Less than 9 and Centinaia off, also Decine off Tenth := 11; goto Cifre_Units_vale_0; Temp3: if Hundreds <> 10 then begin goto temp4; end; // Minore di 9 e centinaia spente, cifra delle decine spenta Tenth := 11; goto Cifre_Units_vale_0; Temp4: // Minore di 9 e negativa, cifra delle decine spenta Tenth := 0; Cifre_Units_vale_0: // Unita' Units := Temp_h; Cifre_esci: end; // Visualizzazione della temperatura sui display a 7 segmenti procedure Vis_temp; begin // Separa il valore di temperatura in W0 in tre cifre: // Centinaia, Decine e Unita Cifre; // Converte il valore decimale in codifica a 7 segmenti Hundreds := DecTo7Seg(Hundreds); // Converte il valore decimale in codifica a 7 segmenti Tenth := DecTo7Seg(Tenth); // Converte il valore decimale in codifica a 7 segmenti Units := DecTo7Seg(Units); // Invia il valore di temperatura converitito in codifica a 7 // segmenti al driver dei display a 7 segmenti i2c_buffer[0] := dig1; i2c_buffer[1] := Hundreds; i2c_buffer[2] := Tenth; i2c_buffer[3] := Units or $80; if Temp_l = $80 then begin // Scrive 5 i2c_buffer[4] := $6D; end else begin // Scrive 0 i2c_buffer[4] := $3F; end; i2cbuswrite(Wsaa1064, 5, i2c_buffer); end; // Lettura della temperatura misurata dal DS1621 in Th e Tl procedure Read_temperature; begin i2c_buffer[0] := rtemp; i2cbusread(wds1621, 1, 2, i2c_buffer); Temp_h := i2c_buffer[0]; Temp_l := i2c_buffer[1]; end; { ********************** Programma principale *************************** } begin Init_cpu; // Spegne i relays set_relays(0); // Inizializza periferica I2C BUS hardware I2Cbusinit; // Inizializza periferiche I2C BUS a bordo della K51-AVR k51_init; // Pulisce lo schermo clrscr; print_USART('Legge la temperatura sul display a 7 segmenti...'); repeat begin Read_temperature; Vis_temp; end; until False; end.