1 | /*
|
2 | * Beispielprogramm eines TWI (I2C) Slave. Getestet mit ATMega8
|
3 | *
|
4 | * Die gesamte Behandlung der TWI-Zustände erfolgt in der Interrupt-Routine. Das Hauptprogramm tut nichts.
|
5 | * Wenn die Daten nicht direkt in der Interruptroutine bearbeitet werden können, muss man am Ende den Interrupt ausschalten
|
6 | * und nach der Verarbeitung im Haptprogramm wieder einschalten. Aber Achtung: wenn der Prozessor nicht auf TWI-Adressanfragen reagiert,
|
7 | * gibt es einen SLA_NACK beim Sender.
|
8 | *
|
9 | */
|
10 |
|
11 | #include "util/delay.h"
|
12 | #include "avr/interrupt.h"
|
13 | #include "util/twi.h"
|
14 |
|
15 | static uint8_t data[64]; // Datenpuffer
|
16 | volatile uint8_t ind; // Index in den Datenpuffer
|
17 | volatile uint8_t max; // Anzahl der zu sendenden Bytes bei Slave-Transmitter-Betrieb
|
18 |
|
19 | /*
|
20 | * Die einzelnen Zustände der TWI-State-machine findet man in der Atmel Dokumentation.
|
21 | * Der Kommentar gibt die Ursache des Interrupts an.
|
22 | */
|
23 | ISR(TWI_vect) {
|
24 | switch (TWSR & TW_STATUS_MASK) {
|
25 | // TW_SR.... Slave Receiver
|
26 | // Erkennung der eigene Adresse nach verschiedenen Startbedingungen
|
27 | case TW_SR_ARB_LOST_SLA_ACK:
|
28 | case TW_SR_ARB_LOST_GCALL_ACK:
|
29 | case TW_SR_GCALL_ACK:
|
30 | case TW_SR_SLA_ACK:
|
31 | ind = 0;
|
32 | // Adresse quittieren
|
33 | TWCR = (1 << TWINT) | (1 << TWEA) | (0 << TWSTA) | (0 << TWSTO) | (1 << TWEN) | (1 << TWIE);
|
34 | break;
|
35 | // Datenbyte erkannt und mit ACK quittiert
|
36 | case TW_SR_GCALL_DATA_ACK:
|
37 | case TW_SR_DATA_ACK:
|
38 | // Noch Platz im Puffer?
|
39 | if (ind < sizeof data) {
|
40 | data[ind++] = TWDR;
|
41 | }
|
42 | // Quittieren und nächstes Byte anfordern
|
43 | TWCR = (1 << TWINT) | (1 << TWEA) | (0 << TWSTA) | (0 << TWSTO) | (1 << TWEN) | (1 << TWIE);
|
44 | break;
|
45 | // NACK im Receiver-Mode führt zum Rücksetzen des TWI-Interfaces
|
46 | case TW_SR_GCALL_DATA_NACK:
|
47 | case TW_SR_DATA_NACK:
|
48 | TWCR = (1 << TWINT) | (1 << TWEA) | (0 << TWSTA) | (0 << TWSTO) | (1 << TWEN) | (1 << TWIE);
|
49 | ind = 0;
|
50 | break;
|
51 | // STOP erkannt: TWI-Interface in Grundstellung bringen.
|
52 | case TW_SR_STOP:
|
53 | TWCR = (1 << TWINT) | (1 << TWEA) | (0 << TWSTA) | (0 << TWSTO) | (1 << TWEN) | (1 << TWIE);
|
54 | ind = 0;
|
55 | break;
|
56 | // TW_ST.... Slave Transmitter
|
57 | // Adresse erkannt, erstes Byte (Länge) senden
|
58 | case TW_ST_ARB_LOST_SLA_ACK:
|
59 | case TW_ST_SLA_ACK:
|
60 | ind = 0;
|
61 | max = 8; // Länge des Sendetelegramms
|
62 | TWDR = max;
|
63 | TWCR = (1 << TWINT) | (1 << TWEA) | (0 << TWSTA) | (0 << TWSTO) | (1 << TWEN) | (1 << TWIE);
|
64 | break;
|
65 | // Byte wurde quittiert, nächstes Byte ist angefordert
|
66 | case TW_ST_DATA_ACK:
|
67 | if (ind < max) {
|
68 | // Es sind noch Daten da, also ACK
|
69 | TWDR = data[32 + ind++];
|
70 | TWCR = (1 << TWINT) | (1 << TWEA) | (0 << TWSTA) | (0 << TWSTO) | (1 << TWEN) | (1 << TWIE);
|
71 | } else {
|
72 | // Zu viele Daten angefordert, also NACK
|
73 | TWCR = (1 << TWINT) | (0 << TWEA) | (0 << TWSTA) | (0 << TWSTO) | (1 << TWEN) | (1 << TWIE);
|
74 | }
|
75 | break;
|
76 | // NACK bzw. ACK auf letztes Datenbyte erhalten: TWI-Interface in Grundstellung
|
77 | case TW_ST_DATA_NACK:
|
78 | case TW_ST_LAST_DATA:
|
79 | // hier die Daten verarbeiten. Z.B. umkopieren o.ä. Wenn es längere Aktionen sind, in das Hauptprogramm auslagern
|
80 | TWCR = (1 << TWINT) | (1 << TWEA) | (0 << TWSTA) | (0 << TWSTO) | (1 << TWEN) | (1 << TWIE);
|
81 | ind = 0;
|
82 | break;
|
83 | }
|
84 | }
|
85 |
|
86 | int main(int argc, char** argv) {
|
87 | uint8_t i;
|
88 |
|
89 | DDRB = 0x00;
|
90 | PORTB = 0xff;
|
91 | DDRC = ((0 << DDC0) | (0 << DDC1) | (0 << DDC2) | (0 << DDC3) | (1 << DDC4) | (1 << DDC5));
|
92 | PORTC = ((1 << DDC0) | (1 << DDC1) | (1 << DDC2) | (1 << DDC3) | (0 << DDC4) | (0 << DDC5));
|
93 | DDRD = 0x00;
|
94 | PORTD = 0xff;
|
95 |
|
96 | for (i = 0; i < sizeof data; i++) {
|
97 | data[i] = i;
|
98 | }
|
99 |
|
100 | // Adressregister (TWAR) auf Adresse setzen. Achtung: 1x links schieben, da Bit 0 für Global call verwendet wird
|
101 | TWAR = (1 << 1);
|
102 | // Interrupt erlauben und ACK auf Adresse aktivieren
|
103 | TWCR = (0 << TWINT) | (1 << TWEA) | (0 << TWSTA) | (0 << TWSTO) | (1 << TWEN) | (1 << TWIE);
|
104 | sei();
|
105 |
|
106 | // alles läuft jetzt im Interrupt
|
107 | for (;;) {
|
108 | }
|
109 | }
|