Forum: Mikrocontroller und Digitale Elektronik AtTiny2313 <-> FoxBoard G20 mit I2C


von Alex B. (alde_oma)


Lesenswert?

Hallo,

ich bin gerade am Aufbau einer I2C-Verbindung zwischen einem AtTiny2313 
und dem FoxBoard G20. Ich habe nun das Problem, dass ich den AtTiny 
nicht "finden" kann.

Mit dem Befehl "i2cdetect -y 0" werden die I2C-Adressen von 00h bis 77h 
durchsucht, ob sich Geräte melden. Beim AtTiny wird aber nichts erkannt. 
Andere Bausteine funktionieren.

Muss man beim AtTiny zuvor noch irgendwelche Register setzen 
(Umschaltung SPI/I2C) oder ähnliches?

Die Pinbelgung:

AtTiny - FoxBoard

10 GND - J6.40 GND
17 SDA - J6.32 SDA
19 SCL - J6.31 SCL
20 VCC - +5V

Der Bus hat 2 Pullup-R mit 4,7kOhm auf 5V.

Um Hilfe wäre ich sehr Dankbar.

von Sven K. (Gast)


Lesenswert?

Ähm,

Im tiny muss ein I2C Slave Programm laufen?

Gruß Sven

von c-hater (Gast)


Lesenswert?

Alex B. schrieb:

> Muss man beim AtTiny zuvor noch irgendwelche Register setzen
> (Umschaltung SPI/I2C) oder ähnliches?

Du mußt vor allem erstmal ein Programm drauf flashen, was den Tiny zum 
I2C-Slave macht. Das Ding ist ein µC und wie jeder µC macht der ohne ein 
Programm erstmal garnix (jedenfalls nix sinnvolles).

von katastrophenheinz (Gast)


Lesenswert?

Und außerdem: Bist du sicher, daß die I2C-Leitungen vom FoxBoard 
TTL-Level haben? Meistens hat dieser ARM-basierende Kram nur noch 3.3 V

Gruss, Heinz

von Alex B. (alde_oma)


Lesenswert?

Danke.
Ich dachte die Adresse sei von Anfang an ansprechbar.
Die 5V Busspannung sind ein Fehler von mir. Müssen natürlich 3,3V sein.

von spess53 (Gast)


Lesenswert?

Hi

>Ich dachte die Adresse sei von Anfang an ansprechbar.

Nö. Aber es gibt eine AppNote bei ATMEL:

http://www.atmel.com/Images/doc2560.pdf

Software: http://www.atmel.com/Images/AVR312.zip

MfG Spess

von Alex B. (alde_oma)


Lesenswert?

Danke.
Ich habs mittlerweile hinbekommen. Jedoch hab ich grad Verständnis 
Probleme.

Ich schreibe vom FoxBoard zum Tiny mit dem Befehl:
1
I2C_WriteToDevice(i2c_fd, 0x02, 0x00);

wobei die Routine so aussieht:
1
void I2C_WriteToDevice(int fd, int reg, int val) {
2
3
    char buf[2];
4
    buf[0] = reg; //Register
5
    buf[1] = val; //Wert
6
    if (write(fd, buf, 2) != 2) {
7
        printf("Schreiben fehlgeschlagen...\n");
8
        return(0);
9
    }
10
}
Ich schreibe also den Wert 0x00 an das Register 0x02.

Die Auswertung auf dem Tiny hat ergeben, dass wenn ich an Register 2 
schreibe, ich auf "Wert == 2" abfragen muss, bei Register 1 auf "Wert == 
1" usw., damit ich ein True erhalte.

Ich kann jetzt aber nicht einfach auf irgendwelche Register schreiben. 
Daher die Frage auf welche kann ich? Die freien, auf dem Datenblatt mit 
Reserved gekenzeichneten? Passt das ganze so überhaupt wie ich das hier 
erkläre??? Kommt mir etwas komisch vor.

Mfg
Alex

von Alex B. (alde_oma)


Lesenswert?

Hallo,

das Thema oben hat sich mittlerweile erledigt. Nun habe ich aber ein 
weiteres "Problem" bzw ein Verhalten, dass ich nicht verstehe.

Zunächst mal der Code vom Master:
1
i2c_buffer[0] = 3;
2
i2c_buffer[1] = 6;
3
i2c_buffer[2] = 40;
4
i2c_buffer[3] = 50;
5
i2c_buffer[4] = 152;
6
I2C_WriteToDevice();
7
8
//------------
9
10
void I2C_WriteToDevice(void) {
11
12
    if (write(i2c_fd, i2c_buffer, sizeof(i2c_buffer)) != sizeof(i2c_buffer)) {
13
        printf("Schreiben fehlgeschlagen...\n");
14
        return(0);
15
    }
16
}
ich möchte also die oben geschriebenen Werte dem Slave übergeben 
(ATTiny2313).

Der hat folgenden Code:
1
/****************************************************/
2
/* The USI as Slave                                 */
3
/* Author: Axel Gartner                             */
4
/****************************************************/
5
#define  F_CPU 1000000UL
6
7
#include <util/delay.h>
8
#include <avr/io.h>
9
#include <avr/interrupt.h>
10
11
#define USI_DATA         USIDR
12
#define USI_STATUS      USISR
13
#define USI_CONTROL     USICR
14
#define USI_ADDRESS      0x20
15
16
#define NONE        0
17
#define ACK_PR_RX      1
18
#define BYTE_RX        2
19
#define ACK_PR_TX      3
20
#define PR_ACK_TX      4
21
#define BYTE_TX        5
22
23
// Device dependant defines
24
/*#if defined(__at90tiny26__) | defined(__attiny26__)
25
#define DDR_USI             DDRB
26
#define PORT_USI            PORTB
27
#define PIN_USI             PINB
28
#define PORT_USI_SDA        PORTB0
29
#define PORT_USI_SCL        PORTB2
30
#endif*/
31
32
#if defined(__AVR_ATtiny2313A__)
33
#define DDR_USI             DDRB
34
#define PORT_USI            PORTB
35
#define PIN_USI             PINB
36
#define PORT_USI_SDA        PORTB5
37
#define PORT_USI_SCL        PORTB7
38
#endif
39
40
volatile uint8_t COMM_STATUS = NONE;
41
42
43
//uint8_t tmp1[2];
44
45
46
void USI_init(void) {
47
  // 2-wire mode; Hold SCL on start and overflow; ext. clock
48
  USI_CONTROL |= (1<<USIWM1) | (1<<USICS1);
49
  USI_STATUS = 0xf0;  // write 1 to clear flags, clear counter
50
  DDR_USI  &= ~(1<<PORT_USI_SDA);
51
  PORT_USI &= ~(1<<PORT_USI_SDA);
52
  DDR_USI  |=  (1<<PORT_USI_SCL);
53
  PORT_USI |=  (1<<PORT_USI_SCL);
54
  // startcondition interrupt enable
55
  USI_CONTROL |= (1<<USISIE);
56
}
57
58
int main(void) {
59
60
  USI_init();
61
  sei();
62
63
  for(;;)  {
64
  }
65
66
}
67
68
SIGNAL(USI_START_vect ) {
69
  uint8_t tmpUSI_STATUS;
70
  tmpUSI_STATUS = USI_STATUS;
71
  COMM_STATUS = NONE;
72
  // Wait for SCL to go low to ensure the "Start Condition" has completed.
73
  // otherwise the counter will count the transition
74
  while ( (PIN_USI & (1<<PORT_USI_SCL)) );
75
  USI_STATUS = 0xf0; // write 1 to clear flags; clear counter
76
  // enable USI interrupt on overflow; SCL goes low on overflow
77
  USI_CONTROL |= (1<<USIOIE) | (1<<USIWM0);
78
}
79
80
SIGNAL(USI_OVERFLOW_vect ) {
81
  uint8_t BUF_USI_DATA = USI_DATA;
82
  
83
  switch(COMM_STATUS) {
84
    case NONE:
85
      if (((BUF_USI_DATA & 0xfe) >> 1) != USI_ADDRESS) {  // if not receiving my address
86
        // disable USI interrupt on overflow; disable SCL low on overflow
87
        USI_CONTROL &= ~((1<<USIOIE) | (1<<USIWM0));
88
      }
89
      else { // else address is mine
90
        DDR_USI  |=  (1<<PORT_USI_SDA);
91
        USI_STATUS = 0x0e;  // reload counter for ACK, (SCL) high and back low
92
        if (BUF_USI_DATA & 0x01) COMM_STATUS = ACK_PR_TX; else COMM_STATUS = ACK_PR_RX;
93
      }
94
    break;
95
    
96
    case ACK_PR_RX:
97
      DDR_USI  &= ~(1<<PORT_USI_SDA);
98
      COMM_STATUS = BYTE_RX;
99
    break;
100
    
101
    case BYTE_RX:
102
      /* Save received byte here! ... = USI_DATA */
103
          
104
      if (USI_DATA == 6) {
105
        PORTD |= (1<<PD3);
106
        _delay_ms(1000);
107
        PORTD &= ~(1<<PD3);
108
      }
109
          
110
      DDR_USI  |=  (1<<PORT_USI_SDA);
111
      USI_STATUS = 0x0e;  // reload counter for ACK, (SCL) high and back low
112
      COMM_STATUS = ACK_PR_RX;
113
    break;
114
    
115
    case ACK_PR_TX:
116
      /* Put first byte to transmit in buffer here! USI_DATA = ... */
117
          
118
      PORT_USI |=  (1<<PORT_USI_SDA); // transparent for shifting data out
119
      COMM_STATUS = BYTE_TX;
120
    break;
121
    
122
    case PR_ACK_TX:
123
    if(BUF_USI_DATA & 0x01) {
124
      COMM_STATUS = NONE; // no ACK from master --> no more bytes to send
125
    }
126
    else {
127
      /* Put next byte to transmit in buffer here! USI_DATA = ... */
128
      PORT_USI |=  (1<<PORT_USI_SDA); // transparent for shifting data out
129
      DDR_USI  |=  (1<<PORT_USI_SDA);
130
      COMM_STATUS = BYTE_TX;
131
    }
132
    break;
133
    
134
    case BYTE_TX:
135
      DDR_USI  &= ~(1<<PORT_USI_SDA);
136
      PORT_USI &= ~(1<<PORT_USI_SDA);
137
      USI_STATUS = 0x0e;  // reload counter for ACK, (SCL) high and back low
138
      COMM_STATUS = PR_ACK_TX;
139
    break;
140
  }
141
  USI_STATUS |= (1<<USIOIF); // clear overflowinterruptflag, this also releases SCL
142
}

Wenn ich nun das Register "USI_DATA" abfrage, sind darin alle gesendeten 
Werte vom Master enthalten. Die If-Abfrage trifft also bei 3, 6 ... zu. 
USI_DATA ist ja aber nur ein 1 Byte Register. Wie können darin dann 5 
Bytes enthalten sein? Und wie bekomme ich es hin, die Bytes einzel 
auszulesen?

von Marcel (Gast)


Lesenswert?

Alex B. schrieb:
> /* Save received byte here! ... = USI_DATA */

Indem du an der Stelle, genau wie es im Kommentar steht, die empfangenen 
Bytes speicherst. Dazu machst du dir ein Array und merkst dir die 
aktuelle Schreibeposition. Dahin kommt dann dein Byte.

[c]
empfangsbuffer[akutelle_position] = USI_DATA;
[c]

Noch ein bisschen Verwaltungskram drumrum und eine Erkennung dafür, wann 
ein Datenpaket komplett ist, und du bist fertig.

von Alex B. (alde_oma)


Lesenswert?

Hi,

das ist klar.
1
      for (uint8_t i=0; i<5; i++) {
2
        tmp1[i] = USI_DATA;
3
      }

Aber so wird in jedes tmp1-Byte der gesamt Inhalt geschrieben.
Wie es dann aber weiter geht, kapier ich nicht. Hab mir das DB auch 
schon durchgelesen, blicks aber nicht. Wird nach jedem Byte ein 
Interrupt ausgelöst, oder wenn alle Bytes angekommen sind?
Wann muss ich in tmp das nächste Byte angeben?

: Bearbeitet durch User
von Marcel (Gast)


Lesenswert?

Ich habe mit dem I²C des ATTiny2313 noch nicht gearbeitet, aber 
normalerweise wird für jedes Byte ein Interrupt erzeugt. Daher ist eine 
Schleife an der Stelle vollkommen ungeeignet.
Du schreibst jeden Interrupt-Aufruf ein Byte in dein Array und wertest 
es aus, wenn du deine 5 Bytes hast.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.