Hallo Stefan,
ich weiß zwar nicht, ob Du hier noch mitliest, aber egal.
Ich habe mich heute mit USI Master auseinandergesetzt und, es ist doch nicht ganz so wild, ich glaube die nackte Routine ergibt etwa 160 Bytes.
Testumgebung :
Tiny 25 als Master.
Mein I2C Display als Slave Nr.1 zur Ausgabe
DS1621 I2C Temperatursensor als Slave Nr.2
Eine Real Time Clock als Slave Nr.3
Der Aufbau läuft seit einer Stunde und zeigt mir schön in Sekundentakt die Temeratur und Uhrzeit abweschseln an \/
Der Master läuft mit 1MHz und 100 kHz Bustakt
ich poste einfach mal das Program:
Code:
; USI_TWI_master Routinen
;**************************************************
.macro TRANSFER_8_BIT
ldi param1,(1<<USISIF)|(1<<USIOIF)|(1<<USIPF)|(1<<USIDC)
rcall USI_TWI_master_transfer
.endmacro
.macro TRANSFER_1_BIT
ldi param1,(1<<USISIF)|(1<<USIOIF)|(1<<USIPF)|(1<<USIDC)|(0xE<<USICNT0)
rcall USI_TWI_master_transfer
.endmacro
.equ WRITE = 0
.equ READ = 1
.equ ACK = 1
.equ NACK = 0
; I2C Master init
;keine Parameter, keine Rueckgabewerte
;************************************************************************
USI_TWI_master_init:
ldi tmp1,(1<<PORT_USI_SDA)|(1<<PORT_USI_SCL);Pullups an SDA u. SCL an
out PORT_USI,tmp1
out DDR_USI,tmp1;SDA u. SCL als Ausgaenge
ser tmp1 ;Lade Dataregister mit High Bytes
out USIDR,tmp1
;USI Interrupts inaktiv, USI im 2 Wire Modus Software Clock
ldi tmp1,(1<<USIWM1)|(1<<USICS1)|(1<<USICLK)
out USICR,tmp1
ldi tmp1,(1<<USISIF)|(1<<USIOIF)|(1<<USIPF)|(1<<USIDC);Lösche USI Flags
out USISR,tmp1 ;und resete den Counter
ret
;************************************************************************
; USI_TWI_master_start
; Sendet Startbedingung und die Slaveadresse
; Parameter, Slaveadresse im Register param1
; Rueckgabe 1 beim Erfolg 0 beim Fehler ueber Register param1
;************************************************************************
USI_TWI_master_start:
sbi PORT_USI,PIN_USI_SCL;zieht SCL High
USI_TWI_master_start_1:
sbis PIN_USI,PIN_USI_SCL;warte bis SCL High ist
rjmp USI_TWI_master_start_1
rcall delay
cbi PORT_USI,PIN_USI_SDA ; zieht SDA LOW
rcall delay
cbi PORT_USI,PIN_USI_SCL ;SCL wieder LOW
sbi PORT_USI,PIN_USI_SDA ;SDA High
out USIDR,param1; Slaveadresse/Data in den USI Dataregister schreiben
;mastertransfer aufrufen
TRANSFER_8_BIT
cbi DDR_USI,PIN_USI_SDA ;SDA auf Eingang
TRANSFER_1_BIT
ret
;***********************************************************************
; USI_TWI_master_write
; Sendet ein Byte am den Master
; Parameter: Data im Register param1
; Rueckgabe 1 beim Erfolg 0 beim Fehler ueber Register param1
;***********************************************************************
USI_TWI_master_write:
out USIDR,param1; Data in den USI Dataregister schreiben
;mastertransfer aufrufen für die
TRANSFER_8_BIT ;Byte Uebergabe
cbi DDR_USI,PIN_USI_SDA ;SDA auf Eingang
TRANSFER_1_BIT;um ACK/Nack Abzuholen
ret
;***********************************************************************
; USI_TWI_master_read
; Liest ein Byte vom Slave aus
; Parameter: Sende ACK/Nack im param1 1 == ACK senden; 0 == kein ACK senden(Nack)
; Rueckgabe gelesene Daten in param1
USI_TWI_master_read:
tst param1 ;Schaue ob zum Schluss NACK oder ACK soll
breq USI_TWI_master_read_prepare_NACK
set ;T Flag setzen
rjmp USI_TWI_master_read_1
USI_TWI_master_read_prepare_NACK:
clt ;T Flag loeschen
USI_TWI_master_read_1:
cbi DDR_USI,PIN_USI_SDA ;SDA auf Eingang setzen
TRANSFER_8_BIT
push param1 ;gelesene Daten sichern
brtc USI_TWI_master_read_send_NACK ;wenn T Flag geloescht ist erfolgt ein NACK
;sonst muss ein ACK erfolgen
clr param1
rjmp USI_TWI_master_read_end
USI_TWI_master_read_send_NACK:
ser param1
USI_TWI_master_read_end:
out USIDR,param1
TRANSFER_1_BIT
pop param1
ret
;***********************************************************************
; USI_TWI_master_transfer
; sorgt für die Komunikation mit dem slave
;mit param1 wird übergeben ob ein Byte empfange/gesendet wird, oder nur 1 Bit für ACK/NACK
USI_TWI_master_transfer:
out USISR,param1;der Counter wird vorgeladen
USI_TWI_master_transfer_1:
rcall delay
sbi USICR,USITC ;Positive Flanke an SCL erzeugen
USI_TWI_master_transfer_2:
sbis PIN_USI,PIN_USI_SCL ; warten bis SCL High ist
rjmp USI_TWI_master_transfer_2
rcall delay
sbi USICR,USITC;negative Flanke an SCL erzeugen
sbis USISR,USIOIF;testen ob Counter ubergelaufen ist
rjmp USI_TWI_master_transfer_1 ;wenn nicht mit dem nächstem Bit
rcall delay ;wenn gelesen wird
in param1,USIDR;Daten holen
ldi tmp1,0xFF
out USIDR,tmp1
sbi DDR_USI,PIN_USI_SDA; SDA auf Ausgang stellen
ret
;***********************************************************************
; USI_TWI_master_stop:
;Sendet die Stop Bedingung
;***********************************************************************
USI_TWI_master_stop:
cbi PORT_USI,PIN_USI_SDA
sbi PORT_USI,PIN_USI_SCL
USI_TWI_master_stop_1:
sbis PIN_USI,PIN_USI_SCL
rjmp USI_TWI_master_stop_1
rcall delay
sbi PORT_USI,PIN_USI_SDA
rcall delay
ret
;***********************************************************************
; delay
;Eine kurze Pause von insgesammt 10 Takten
;Bei 1 MHz ergibt das 100 kHz Mastertakt
;;keine Parameter, keine Rueckgabewerte
;***********************************************************************
delay:
;Rcall hat 3 Takte gedauert
nop ;1 Takt
nop ;1 Takt
nop ;1 Takt
ret ; 4 Takte
;***********************************************************************
Du mußt Dir 2 Register definieren tmp1, und param1, die nicht gesichert werden.
Ich habe es anhand der o.g. appnote geschrieben, aber so ziemlich umgekrämpelt, weil es mir nicht so gefallen hat.
Die ist etwa genauso zu benutzen, wie die Routine von Peter Fleury, da komm ich besser mit klar, als diese komische Pufferlösung von Atmel.
Ein Miniprogramm um den DS1621 Wandeln zu lassen und auszulesen könnte damit etwa so aussehen:
Code:
.include "tn25def.inc"
.equ PORT_USI = PORTB
.equ DDR_USI = DDRB
.equ PIN_USI = PINB
.equ PORT_USI_SDA = PORTB0
.equ PORT_USI_SCL = PORTB2
.equ PIN_USI_SDA = PINB0
.equ PIN_USI_SCL = PINB2
.equ I2C_DISPLAY = 0x14
.equ THERMO = 0x90
.def nachkomma = r1
.def tmp1 = r16
.def tmp2 = r17
.def param1 = r18
.def param2 = r19
.def temperatur = r21
.cseg
.org 0x0000
rjmp reset
reset:
clr ausgabe
ldi tmp1,RAMEND
out SPL,tmp1
rcall USI_TWI_master_init
;Thermometer einschalten
ldi param1,THERMO+WRITE
rcall USI_TWI_master_start
ldi param1,0xEE
rcall USI_TWI_master_write
rcall USI_TWI_master_stop
;Thermometer eingeschaltet
loop:
;Temperatur auslesen
ldi param1,THERMO+WRITE
rcall USI_TWI_master_start
ldi param1,0xAA
rcall USI_TWI_master_write
ldi param1,THERMO+READ
rcall USI_TWI_master_start
ldi param1,ACK
rcall USI_TWI_master_read
mov temperatur,param1
ldi param1,NACK
rcall USI_TWI_master_read
mov nachkomma,param1
rcall USI_TWI_master_stop
;Temperatur ausgelesen
loop1:
loop1
Noch etwas unaufgeräumt, ich denke bis zum Wochenende werde ich Zeit finden und alles besser dokumentieren.
Ich hoffe, das Du was mit tun kannst
Gruß Sebastian
Lesezeichen