PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : I²C Usi mit ATTiny26



Bubi_00
21.03.2007, 08:44
Hallo Leute

Ich hab hier eine fertige Software von mikrocontroller.net für die USI vom ATTiny26 für einen I²C Slave.

http://www.mikrocontroller.net/topic/38917#287918

Danke an dieser Stelle, falls er es liest :)

Der Slave scheint einwandfrei zu funktionieren wenn ich nur 1nen Attiny am Bus habe.
Gebe ich aber einen zweiten Tiny hinzu Addressen (0xA0, 0xB0) hab ich das Problem, das mir auf eine Anfrage auf Addresse 0xA0, BEIDE antworten...
Genauso auch umgekehrt, er ignoriert also die Addresse...






#ifndef _I2C_SLAVE_C_
#define _I2C_SLAVE_C_


#include <avr/io.h>
#include <avr/interrupt.h>
#include "i2c_slave.h"


#define USI_DATA USIDR
#define USI_STATUS USISR
#define USI_CONTROL USICR

//Standart Device Address


#define NONE 0
#define ACK_PR_RX 1
#define BYTE_RX 2
#define ACK_PR_TX 3
#define PR_ACK_TX 4
#define BYTE_TX 5

// Device dependant defines

#define DDR_USI DDRB
#define PORT_USI PORTB
#define PIN_USI PINB
#define PORT_USI_SDA PORTB0
#define PORT_USI_SCL PORTB2

#define NONE 0


volatile unsigned char USI_address = NONE;
volatile uint8_t COMM_STATUS = NONE;
volatile unsigned char send_usi = NONE;


void USI_set(unsigned char data)
{
send_usi = data ;
}

void USI_init(unsigned char address) {
// Set the Device Address for the Slave Address+1 = Address with Reading
USI_address = address;
// 2-wire mode; Hold SCL on start and overflow; ext. clock
USI_CONTROL |= (1<<USIWM1) | (1<<USICS1);
USI_STATUS = 0xf0; // write 1 to clear flags, clear counter
DDR_USI &= ~(1<<PORT_USI_SDA);
PORT_USI &= ~(1<<PORT_USI_SDA);
DDR_USI |= (1<<PORT_USI_SCL);
PORT_USI |= (1<<PORT_USI_SCL);
// startcondition interrupt enable
USI_CONTROL |= (1<<USISIE);
}



ISR(USI_STRT_vect) {
uint8_t tmpUSI_STATUS;
tmpUSI_STATUS = USI_STATUS;
COMM_STATUS = NONE;
// Wait for SCL to go low to ensure the "Start Condition" has completed.
// otherwise the counter will count the transition
while ( (PIN_USI & (1<<PORT_USI_SCL)) );
USI_STATUS = 0xf0; // write 1 to clear flags; clear counter
// enable USI interrupt on overflow; SCL goes low on overflow
USI_CONTROL |= (1<<USIOIE) | (1<<USIWM0);
}


ISR(USI_OVF_vect) {

uint8_t BUF_USI_DATA = USI_DATA;
switch(COMM_STATUS) {
case NONE:
if (((BUF_USI_DATA & 0xfe)) != USI_address & 0xfe) { // if not receiving my address
// disable USI interrupt on overflow; disable SCL low on overflow
USI_CONTROL &= ~((1<<USIOIE) | (1<<USIWM0));
}
else { // else address is mine
DDR_USI |= (1<<PORT_USI_SDA);
USI_STATUS = 0x0e; // reload counter for ACK, (SCL) high and back low
if (BUF_USI_DATA & 0x01) COMM_STATUS = ACK_PR_TX; else COMM_STATUS = ACK_PR_RX;
}
break;
case ACK_PR_RX:
DDR_USI &= ~(1<<PORT_USI_SDA);
COMM_STATUS = BYTE_RX;
break;
case BYTE_RX:
/* Save received byte here! ... = USI_DATA*/
DDR_USI |= (1<<PORT_USI_SDA);
USI_STATUS = 0x0e; // reload counter for ACK, (SCL) high and back low
COMM_STATUS = ACK_PR_RX;
break;
case ACK_PR_TX:
/* Put first byte to transmit in buffer here! USI_DATA = ... */
USI_DATA = send_usi;
PORT_USI |= (1<<PORT_USI_SDA); // transparent for shifting data out
COMM_STATUS = BYTE_TX;
break;
case PR_ACK_TX:
if(BUF_USI_DATA & 0x01) {
COMM_STATUS = NONE; // no ACK from master --> no more bytes to send
}
else {
/* Put next byte to transmit in buffer here! USI_DATA = ... */
PORT_USI |= (1<<PORT_USI_SDA); // transparent for shifting data out
DDR_USI |= (1<<PORT_USI_SDA);
COMM_STATUS = BYTE_TX;
}
break;
case BYTE_TX:
DDR_USI &= ~(1<<PORT_USI_SDA);
PORT_USI &= ~(1<<PORT_USI_SDA);
USI_STATUS = 0x0e; // reload counter for ACK, (SCL) high and back low
COMM_STATUS = PR_ACK_TX;
break;
}
USI_STATUS |= (1<<USIOIF); // clear overflowinterruptflag, this also releases SCL
}

#endif



Bis auf ein paar Kleinigkeiten wurde am Code eigentlich nichts geändert. Sieht wer den Fehler?

mfg

linux_80
21.03.2007, 12:34
Hallo,

gröbere Schnitzer sind mir jetzt nicht aufgefallen,
ausser das #define NONE zweimal vorkommt :-k

Evtl. machst du mal das & 0xfe beim vergleich von USI_address weg,
in der Zeile nach case NONE:

Bubi_00
21.03.2007, 13:28
Kann ich nicht, das ist die Bitmaske um das letzte Bit wegzubekommen, das bleibt ja frei um 0(schreiben) und 1(lesen) zur Verfügung zu haben

linux_80
21.03.2007, 13:35
Wenn du das bit vorher nicht gesetzt hast, muss Du es hier nicht wieder wegmaskieren,
probier es mal aus.

Das ist die einzige Stelle an der die Slaveadresse überprüft wird, und das ist auch die Stelle, die Du u.a. umgebaut hast !

Bubi_00
21.03.2007, 13:54
naja aber wenn ich die addresse 1111 0000 habe und ich aber 1111 0001
reinbekomme, muss ich es abfangen...
Und jo das is ein Teil den ich ändern musste, wegen Verständnissproblemen und weils auf die originale Weise nicht funktioniert hat.. :(
Testen kann ichs aber leider erst morgen
mfg

linux_80
21.03.2007, 14:40
Aber Du bist doch der, der das programmiert, und wenn Du da keine solche Adresse im Quellcode reinschreibst, musst du das dort nicht wieder rausfiltern.

Das erste & 0xfe muss natürlich stehen bleiben, weil das die Adresse ist, die der Master gesendet hat.

SprinterSB
21.03.2007, 15:13
Mal ganz vorsichtig gefragt... bist du sicher, daß nicht vielleicht die gleiche Binärdatei auf beiden Slaves landet? 8-[

Bubi_00
22.03.2007, 12:30
Jo, die Idee kam mir auch schon, nur hab ichs mittlerweile schon beschriftet und mehrfach neu beschrieben..
Das Problem ist ja auch wenn ich zB ox20 als Addresse in den Slave schreibe, reagiert er ja auch auf 0xA0, auch wenn nur ein Slave vorhanden ist.

Bubi_00
22.03.2007, 13:26
So Problem gelöst weis aber ned wieso :-b
Ich vergebe die Addresse jetzt DIREKT in der Interruptroutine und nicht wie vorher über eine Funktion mit eine Buffervariable..ich weiß nicht wieso.. aber es geht :)
Danke für die Tips