PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : USI (als TWI slave) mit ATTINY x61



robin
09.04.2009, 18: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

linux_80
09.04.2009, 18:43
Hallo,

USI macht nix automatisch, wenn man ein ACK (0) senden will, muss man eben ein Bit mit dem entsprechenden Wert auf den Bus schieben.

Für Bascom hatte ich mal was gemacht:
https://www.roboternetz.de/phpBB2/viewtopic.php?t=25058

Im Wiki ist auch etwas zu USI.
https://www.roboternetz.de/wissen/index.php/USI_%28Avr%29

robin
10.04.2009, 15:02
Hi,

die Links kenn ich beide schon, trotzdem danke.

Im Wiki artikel werden größtenteils nur die Register beschrieben, was mir eigentlich nicht weiterhilft.


Ich hab den Code oben Aktualisiert.

Ich konnte den Fehler auch schon ausfindig machen. Die USIoverflow Routine wird nicht aufgerufen.

Ich hab aber meiner Meinung nach alles richtig eingestellt.

Der USI counter zählt bei steigender und fallender Flanke, also sollte nach 8 übertragenen Bits der Interrupt ausgelöst werden.

uffi
12.04.2009, 20:23
Hallo,

ich hab einen funktionierenden Code für einen ATTiny26 als I2C Slave. Der ist zum ATTiny861 kompatible. Das ganze läuft bis 100 kHz SCL. Als Master nehme ich einen ATMega16.

Siehe hier:

http://freenet-homepage.de/uffmann/Ultrasonic2.html

gruß, uffi

robin
13.04.2009, 15:30
Hi,

danke für deinen Code, er hat mir einiges gebracht.

leider funktioniert das ganze immernoch nicht.

Ich habe mitlerweile den Bustakt auf 40khz (fast minimum) runtergedreht. und mehrere wait schleifen eingebaut, damit alles schön langsam ist.

Mein erfolg ist immerhin, dass meine Slave seine adresse erkennt, aber kein ACK zurücksendet.

Meine routine um ein ACK zu senden habe ich von dir übernommen:


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




Timer_1Bit:
ldi temp1,0xee
out USISR,temp1 ;timer für ACK zurückstellen und flags löschen
ret


Als master verwende ich eine Mega 168 mit hadware TWI, je 10k pullups an SDA und SCL.