Zur Ansteuerung des I2C-Busses benötigt man 2 open-drain- oder open-kollektor-Ausgänge, die auch als Eingänge funktionieren. Das Pin RA4 ist das einzige Pin, das diese Voraussetzung erfüllt. Allerdings ist es aufgrund der RA4-Falle, nicht ganz einfach zu nutzen. Ich bastle mir lieber aus jeweils 2 Pins des Ports A einen bidirektionalen open-drain-Leitungstreiber. Folglich sind die Pins RA0..RA3 für den I2C-Bus reserviert.
|
Ich lege per #define-Befehl eingängige Namen für diese 4 Pins fest:
list p=16f628 ;************************************************************** ;* Pinbelegung ;* ---------------------------------- ;* PORTA: 0 SDA out ;* 1 CLK in ;* 2 SDA in ;* 3 CLK out ;* 4 - ;************************************************************** ; ;sprut (zero) Bredendiek 12/2002 ; I2C-Bus am 16F628 ; Prozessor 16F628 ; Prozessor-Takt 10 MHz ; ; I2C am PortA ; ;********************************************************** ; Includedatei für den 16F628 einbinden #include ; Configuration festlegen: ; für I2C |
Initialisierung
Um die Pins RA0..RA3 wie oben beschrieben nutzen zu können, müssen RA0 und RA3 als Output definiert werden. Außerdem müssen die Komparatoreingänge deaktiviert werden, damit die Pins überhaupt als digitale I/O-Pins nutzbar sind.
; Das Programm beginnt mit der Initialisierung Init bsf STATUS, RP0 ; Bank 1 movlw B'11100110' ; PortA alle input außer RA0,3,4 movwf TRISA bcf STATUS, RP0 ; Bank 0 clrf PORTA ; 16F628 alle Comparatoreingänge auf Digital umschalten call i2c_reset |
Einstellung der Busgeschwindigkeit
Da das gesamte Busprotokoll per Software erzeugt wird, hängt die Busgeschwindigkeit von der Kompaktheit der Softwareroutinen und dem Takt des PICs ab. Die hier vorgestellten Routinen erreichen bei 10 MHz-PIC-Takt einen I2C-Bus-Takt von 210 kHz. Sie können für low-speed-I2C-Slaves verwendet werden, wenn der PIC mit 4 MHz getaktet wird:
(100 kHz) | (400 kHz) | ||
Natürlich kann der I2C-Takt auch durch das Einfügen einiger NOP-Befehle in die Routinen verringert werden. Eine Beschleunigung ist aber nur möglich, wenn man nicht alle Feinheiten des I2C-Busses nutzen will. Meine Routinen berücksichtigen die Möglichkeit, dass ein Slave den I2C-Takt aktiv verlängert. Das macht aber in Wirklichkeit kaum ein I2C-Schaltkreis. Wer sich sicher ist, dass er dieses Feature nicht braucht, kann die I2C-Routinen verschlanken, und einen schnelleren I2C-Takt erreichen.
Daten auf den I2C-Bus schreiben
Um Daten auf den I2C-Bus zu schreiben, wird der Bus mit i2c_on übernommen. Danach wird das zu schreibende Byte nach 'w' geladen, und danach i2c_txt aufgerufen.
Ist der Datentransfer beendet, gibt man mit i2c_off den Bus wieder frei.
Nachfolgendes Beispiel steuert einenTDA8444 an. Dieser Chip enthält acht 6-Bit-Digital-Analog-Wandler (DAC). Die ersten 6 DACs des TDA8444 werden auf 6 unterschiedliche Spannungen eingestellt:
; Achtung PIC-Takt maximal 4 MHz call i2c_on ; Bus übernehmen movlw H'40' ; Adresse des TDA8444 (A0..A2=0) movlw H'00' ; kanal 0 increment adress movlw H'00' ; DAC0: 0V movlw H'01' ; DAC1: 1/64 Vcc movlw H'02' ; DAC2: 1/32 Vcc movlw H'04' ; DAC3: 1/16 Vcc movlw H'08' ; DAC4: 1/8 Vcc movlw H'10' ; DAC5: 1/4 Vcc movlw H'20' ; DAC6: 1/2 Vcc call i2c_off ; Bus freigeben |
Da der TDA8444 ein Standard-Mode-Chip (max. 100 kHz) ist, darf der PIC dabei mit nur 4 MHz getaktet werden. Der TDA benötigt offiziell eine Betriebsspannung (Vcc) von mindestens 4,5V. Meiner Erfahrung nach läuft er aber erst ab 6V. Diese Betriebsspannung darf natürlich nicht mit dem Vcc-Pin des PIC verbunden werden. Der braucht seine eigenen 5 V.
Daten vom I2C-Bus lesen
Um Daten vom den I2C-Bus zu lesen, wird der Bus mit i2c_on übernommen. Danach wird die Adresse des auszulesenden I2C-Bausteins mit gesetztem Bit0 in 'w' geladen, und dieser Baustein dann mit i2c_tx adressiert. Danach wird i2c_rx oder i2c_rxack aufgerufen. Diese Routine liest ein Byte vom I2C-Bus, und schreibt es nach 'w'. i2c_rxack erzeugt zusätzlich ein ACK-Signal für den gelesenen I2C-Baustein. Das ist nötig, wenn noch weitere Bytes gelsesen werden sollen..
Ist der Datentransfer beendet, gibt man mit i2c_off den Bus wieder frei.
Nachfolgendes Beispiel steuert einen LM75-Temperatursensor an, und liest die von ihm gemessene Temperatur aus (2 Byte) :
call i2c_on ; Bus aktiv movlw H'91' ; 1001 0001 , LM75 (A0..A2=0) call i2c_rxack ; lesen mit ACK call i2c_rx ; lesen ohne ACK - letztes Byte call i2c_off ; Bus freigeben |
Hilfsroutinen
Folgende Routinen werden benötigt:
- i2c_reset
bringt den I2C-Bus in einen definierten Ausgangszustand - i2c_on
übernimmt den I2C-Bus, sobald er frei ist - i2c_off
gibt den I2C-Bus wieder frei - i2c_tx
sendet das Byte aus 'w' über den I2C-Bus - i2c_rx
- empfängt ein Byte aus dem I2C-Bus, und legt es in 'w', buf ab
- i2c_rxack
empfängt ein Byte aus dem I2C-Bus, und legt es in 'w', buf ab
anschließend wird ein ACK-Signal erzeugt
;***************************************************************** ; Routinen für I2C ; Bus zurücksetzen i2c_reset ; Bus übernehmen i2c_on ; W senden i2c_tx ; Byte empfangen i2c_rx (nach w und buf) ; Byte empfangen & ACK i2c_rxack (nach w und buf) ; Bus freigeben i2c_off ;***************************************************************** i2c_reset bsf SDAo bsf SCLo nop movlw 9 movwf buf i2c_reset1 nop bcf SCLo nop nop nop nop nop bsf SCLo nop decfsz buf, f goto i2c_reset1 nop call i2c_on nop bsf SCLo nop nop bcf SCLo nop call i2c_off return i2c_on i2c_tx i2c_rxack i2c_rx i2c_off ;***************************************************** ;schiebt das Byte aus W in den I2C ;liest das Byte aus I2C nach W |