robin
09.04.2009, 19:18
Hi,
ich habe einen ATTiny 861, der als TWI slave arbeiten soll.
Ich habe aus dem sehr bescheidenen APNotes von Atmel zum thema USI als TWI slave (AVR:312) und dem Datenblatt des Tinys einen Code erstellt.
Jedoch wird mir aus keinem der beiden pdfs klar, was ich machen muss, damit die USI ein ACK sendet.
Codes zur ansteuerung finde ich meistens nur in C und für andere controller (C is nicht so meine stärke).
Hier mal mein Code:
Hauptcode:
.include "Tn861def.inc"
.include "Bezeichnung.asm"
rjmp Init
.org 0x0007 rjmp USI_START ; USI Start Handler
.org 0x0008 rjmp USI_OVF ; USI Overflow Handler
.include "init.asm"
main:
out porta,USI_Status
rjmp main
rjmp main
USI_START:
push temp1 ;temp1 sichern
ldi USI_Status, 0x00 ;Status auf 0 setzen (adresse empfangen)
wait:
sbic pinb,2 ;warten bis scl low ist,da sonst der counter erhöht wird
rjmp wait
SBI USICR,6 ;USI Overflow interrupt aktivieren
rcall timer_8bit ;counter reseten, flags löschen
pop temp1 ;temp1 zurückholen
reti
USI_OVF:
push temp1
push temp2
;Status auslesen
cpi USI_Status,0
breq Status0 ;adresse
cpi USI_Status,1
breq Status1 ;Slave soll senden
cpi USI_Status,2
breq Status2 ;
cpi USI_Status,3
breq Status3 ;
cpi USI_Status,4
breq Status4 ;Slave soll empfangen
cpi USI_Status,5
breq Status5 ;
rjmp reset_USI
Status0: ;Adresse empfangen
in temp2,USIDR ;kann auch USIBR/USIDR sein, sollte egal sein
mov temp1,temp2 ;Empfangene Adresse sichern (wegen R/W)
andi temp1,0xfe ;Adresse Maskieren
cpi temp1, ((Slave_adr*2));Eigene Adresse?
breq Status01 ;Wenn JA, Springe zu Status01
rjmp reset_USI ;Wenn nein, USI reseten (interrupt deaktivieren)
Status01: ;Eigene Adresse erkannt
rcall sende_ACK ;ACK senden
Andi temp2,0x01 ;R/W auslesen und entsprechend Status wählen
SBRC temp2,0 ;Wenn 1 = Read
LDI USI_Status,1 ;Wenn 0 = Write
SBRS temp2,0
LDI USI_Status,4
rjmp ende
Status1: ;Master will Byte empfangen (Slave soll senden)
out USIDR,data ;Daten in Datenregister laden ,kann auch durch laden von daten aus einer Tabelle ersetzt werden
sbi PORTB,0 ;Port transparent machen
LDI USI_Status,2
rjmp ende
Status2: ;Slave hat gesendet, empfangen von ACK vorbereiten
cbi DDRB,0 ;Empfangen von ACK vorbereiten
cbi PORTB,0 ;
LDI USI_Status,3 ;Status ändern
rcall Timer_1Bit ;Timer auf das erhalten von 1 Bit einstellen
rjmp ende
Status3: ;ACK oder NACK erhalten?
in temp1,USIDR ;Empfangene daten einlesen
Andi temp1,0x01 ;ACK/NACK maskieren
sbi DDRB,0 ;SDA auf low ziehen
SBRS temp1,0 ;Wenn ACK, siehe Status1
rjmp Status1
rjmp reset_USI ;Wenn NACK, reset
Status4: ;Slave soll Empfangen
rcall Zuruckstellen_ACK
LDI USI_Status,5 ;Status ändern
rjmp ende
Status5: ;Byte empfangen, ACK senden
in data,USIDR ;Daten einlesen
rcall sende_ACK
rcall Timer_1Bit
ldi USI_Status,4
rjmp ende
sende_ACK:
cbi PORTB,0 ;prepare to send "0" on SDA
sbi DDRB,0 ;setup the SDA line as output
rcall Timer_1Bit ;timer für ACK zurückstellen
ret
Zuruckstellen_ACK:
cbi DDRB,0 ;SDA wieder als eingang
ret
TImer_8Bit:
ldi temp1,0xe0 ;flags löschen, counter resetten könnte auch 0xf8 sein
out USISR,temp1
ret
Timer_1Bit:
ldi temp1,0xee
out USISR,temp1 ;timer für ACK zurückstellen und flags löschen
ret
reset_USI:
cbi USICR,(USIOIE) ;interrupt deaktivieren
ldi USI_STATUS,6 ;TESTZWECK
rjmp ende
Ende:
sbi USISR,(USIOIF) ;Interrupt flag löschen
pop temp2 ;Stack zurückholen
pop temp1 ;
reti
und die Init:
init:
init:
;Stack
ldi temp1, LOW(RAMEND) ; LOW-Byte der obersten RAM-Adresse
out SPL, temp1
ldi temp1, HIGH(RAMEND) ; HIGH-Byte der obersten RAM-Adresse
out SPH, temp1
;I/O einstellen
ldi temp1, 0xFF ; Port A = Ausgang
out DDRA, temp1
Ldi temp1, 0x00 ;PortB = Eingang
out DDRB, temp1
ldi temp1, 0x00 ;Pullups bei TWI pins deaktivieren
out PORTB,temp1
;TWI Slave mit interrupt
cbi DDRB,0 ;SDA als Input
ldi temp1,0x00 ;Pinbelegung von TWI bei 0x01 PA, bei 0x00 PB
out USIPP,temp1 ;
;TWI modus aktivieren (ohne Clockstreching),
Ldi temp1,(1<<USISIE)+(0<<USIOIE)+(1<<USIWM0)+(1<<USIWM1)+(1<<USICS1)+(0<<USICS0)+(0<<USICLK)
out USICR,temp1 ;
ldi temp1,0xf0 ;flags löschen, counter resetten
out USISR,temp1
ser temp1 ;TESTZWECK
mov data,temp1 ;
LDI USI_Status,0xFF ;TESTZWECK
sei ;interrupts aktivieren
rjmp main
Als master verwende ich einen Mega8, mit hardware TWI. Dieser bekommt nach dem senden der Adresse kein ACK zurück, die Adresse stimmt aber, hab sie schon mehrfach gedreht, hat nie geklappt.
Das Problem dass ich habe ist, dass ich nicht weis, wie ich mit der Slave ein ACK Signal erzeugen kann, oder falls USI das automatisch macht, warum keins beim Master ankommt.
Könnte vlt. jemand drüberschaun, ich such schon den ganzen tag nach fehlern und find keine mehr, auser die sache mit dem ACK.
mfg robin
ich habe einen ATTiny 861, der als TWI slave arbeiten soll.
Ich habe aus dem sehr bescheidenen APNotes von Atmel zum thema USI als TWI slave (AVR:312) und dem Datenblatt des Tinys einen Code erstellt.
Jedoch wird mir aus keinem der beiden pdfs klar, was ich machen muss, damit die USI ein ACK sendet.
Codes zur ansteuerung finde ich meistens nur in C und für andere controller (C is nicht so meine stärke).
Hier mal mein Code:
Hauptcode:
.include "Tn861def.inc"
.include "Bezeichnung.asm"
rjmp Init
.org 0x0007 rjmp USI_START ; USI Start Handler
.org 0x0008 rjmp USI_OVF ; USI Overflow Handler
.include "init.asm"
main:
out porta,USI_Status
rjmp main
rjmp main
USI_START:
push temp1 ;temp1 sichern
ldi USI_Status, 0x00 ;Status auf 0 setzen (adresse empfangen)
wait:
sbic pinb,2 ;warten bis scl low ist,da sonst der counter erhöht wird
rjmp wait
SBI USICR,6 ;USI Overflow interrupt aktivieren
rcall timer_8bit ;counter reseten, flags löschen
pop temp1 ;temp1 zurückholen
reti
USI_OVF:
push temp1
push temp2
;Status auslesen
cpi USI_Status,0
breq Status0 ;adresse
cpi USI_Status,1
breq Status1 ;Slave soll senden
cpi USI_Status,2
breq Status2 ;
cpi USI_Status,3
breq Status3 ;
cpi USI_Status,4
breq Status4 ;Slave soll empfangen
cpi USI_Status,5
breq Status5 ;
rjmp reset_USI
Status0: ;Adresse empfangen
in temp2,USIDR ;kann auch USIBR/USIDR sein, sollte egal sein
mov temp1,temp2 ;Empfangene Adresse sichern (wegen R/W)
andi temp1,0xfe ;Adresse Maskieren
cpi temp1, ((Slave_adr*2));Eigene Adresse?
breq Status01 ;Wenn JA, Springe zu Status01
rjmp reset_USI ;Wenn nein, USI reseten (interrupt deaktivieren)
Status01: ;Eigene Adresse erkannt
rcall sende_ACK ;ACK senden
Andi temp2,0x01 ;R/W auslesen und entsprechend Status wählen
SBRC temp2,0 ;Wenn 1 = Read
LDI USI_Status,1 ;Wenn 0 = Write
SBRS temp2,0
LDI USI_Status,4
rjmp ende
Status1: ;Master will Byte empfangen (Slave soll senden)
out USIDR,data ;Daten in Datenregister laden ,kann auch durch laden von daten aus einer Tabelle ersetzt werden
sbi PORTB,0 ;Port transparent machen
LDI USI_Status,2
rjmp ende
Status2: ;Slave hat gesendet, empfangen von ACK vorbereiten
cbi DDRB,0 ;Empfangen von ACK vorbereiten
cbi PORTB,0 ;
LDI USI_Status,3 ;Status ändern
rcall Timer_1Bit ;Timer auf das erhalten von 1 Bit einstellen
rjmp ende
Status3: ;ACK oder NACK erhalten?
in temp1,USIDR ;Empfangene daten einlesen
Andi temp1,0x01 ;ACK/NACK maskieren
sbi DDRB,0 ;SDA auf low ziehen
SBRS temp1,0 ;Wenn ACK, siehe Status1
rjmp Status1
rjmp reset_USI ;Wenn NACK, reset
Status4: ;Slave soll Empfangen
rcall Zuruckstellen_ACK
LDI USI_Status,5 ;Status ändern
rjmp ende
Status5: ;Byte empfangen, ACK senden
in data,USIDR ;Daten einlesen
rcall sende_ACK
rcall Timer_1Bit
ldi USI_Status,4
rjmp ende
sende_ACK:
cbi PORTB,0 ;prepare to send "0" on SDA
sbi DDRB,0 ;setup the SDA line as output
rcall Timer_1Bit ;timer für ACK zurückstellen
ret
Zuruckstellen_ACK:
cbi DDRB,0 ;SDA wieder als eingang
ret
TImer_8Bit:
ldi temp1,0xe0 ;flags löschen, counter resetten könnte auch 0xf8 sein
out USISR,temp1
ret
Timer_1Bit:
ldi temp1,0xee
out USISR,temp1 ;timer für ACK zurückstellen und flags löschen
ret
reset_USI:
cbi USICR,(USIOIE) ;interrupt deaktivieren
ldi USI_STATUS,6 ;TESTZWECK
rjmp ende
Ende:
sbi USISR,(USIOIF) ;Interrupt flag löschen
pop temp2 ;Stack zurückholen
pop temp1 ;
reti
und die Init:
init:
init:
;Stack
ldi temp1, LOW(RAMEND) ; LOW-Byte der obersten RAM-Adresse
out SPL, temp1
ldi temp1, HIGH(RAMEND) ; HIGH-Byte der obersten RAM-Adresse
out SPH, temp1
;I/O einstellen
ldi temp1, 0xFF ; Port A = Ausgang
out DDRA, temp1
Ldi temp1, 0x00 ;PortB = Eingang
out DDRB, temp1
ldi temp1, 0x00 ;Pullups bei TWI pins deaktivieren
out PORTB,temp1
;TWI Slave mit interrupt
cbi DDRB,0 ;SDA als Input
ldi temp1,0x00 ;Pinbelegung von TWI bei 0x01 PA, bei 0x00 PB
out USIPP,temp1 ;
;TWI modus aktivieren (ohne Clockstreching),
Ldi temp1,(1<<USISIE)+(0<<USIOIE)+(1<<USIWM0)+(1<<USIWM1)+(1<<USICS1)+(0<<USICS0)+(0<<USICLK)
out USICR,temp1 ;
ldi temp1,0xf0 ;flags löschen, counter resetten
out USISR,temp1
ser temp1 ;TESTZWECK
mov data,temp1 ;
LDI USI_Status,0xFF ;TESTZWECK
sei ;interrupts aktivieren
rjmp main
Als master verwende ich einen Mega8, mit hardware TWI. Dieser bekommt nach dem senden der Adresse kein ACK zurück, die Adresse stimmt aber, hab sie schon mehrfach gedreht, hat nie geklappt.
Das Problem dass ich habe ist, dass ich nicht weis, wie ich mit der Slave ein ACK Signal erzeugen kann, oder falls USI das automatisch macht, warum keins beim Master ankommt.
Könnte vlt. jemand drüberschaun, ich such schon den ganzen tag nach fehlern und find keine mehr, auser die sache mit dem ACK.
mfg robin