Forum: Mikrocontroller und Digitale Elektronik ATMega I2C Slave - 16-Bit antwort?


von Johannes M. (Gast)


Lesenswert?

Hallo,

Ich habe mit 5x 74HC4051 und 5 x ADC Eingängen des ATMega328P einen 
Analogmultiplexer für ein 40-Potentiometer-Array gebaut.

Nun möchte ich, dass der ATMega als I2C-Slave läuft. Die 40 Werte für 
die 40 Potis werden in einem uint16_t array mit 40 Elementen 
gespeichert. Ich habe nun zwei befehle: OP_RESET, zum resetten des array 
counters (von vorn beginnen) und OP_PROBE zum auslesen eines Potis, 
welcher nun 40 mal aufgerufen wird um 40 Potis auszulesen. Das TWDR 
(Two-Wire/I2C Data register ist aber 8-Bit weit).. Was ist die gängige 
Praxis um 2 x 8-Bit als Antwort auf einen Request über I2C zu schicken?

Hier der Code:
1
#include <avr/io.h>
2
#include <util/delay.h>
3
#include <compat/twi.h>
4
#include <avr/interrupt.h>
5
#include "l74hc4051.h"
6
#include "adc.h"
7
8
#define I2C_ADDR 0x4E
9
10
#deinfe OP_RESET 0x00
11
#define OP_PROBE 0x01
12
#define OP_SINGLE 0x02
13
14
#define IODIR 0x00
15
#define GPIO 0x09
16
#define OLAT 0x0A
17
unsigned char regaddr;    // Store the MCP23008 Requested Register Address
18
unsigned char regdata;    // Store the MCP23008 Register Address Data
19
unsigned char index;
20
21
uint16_t current_values[40] = {0};
22
uint16_t old_values[40] = {0};
23
24
void i2c_slave_action(unsigned char rw_status)
25
{
26
   // rw_status: 0-Read, 1-Write
27
   switch(regaddr) {
28
29
     case OP_RESET:
30
  if(rw_status)++
31
  {
32
    index = 0;
33
  }
34
  else
35
  {
36
    index = 0;
37
    regdata = 0xFF;
38
  }
39
  break;
40
41
42
     case OP_PROBE:
43
       if (rw_status) {
44
       } else {
45
  if(index < 80)
46
  {
47
     regdata=current_values[index++];
48
  }
49
   else
50
  {
51
    index = 0;
52
    regdata=0x00;
53
  }
54
       }    
55
       break;
56
   }
57
}
58
ISR(TWI_vect)
59
{
60
   static unsigned char i2c_state;
61
   unsigned char twi_status;
62
   // Disable Global Interrupt
63
   cli();
64
   // Get TWI Status Register, mask the prescaler bits (TWPS1,TWPS0)
65
   twi_status=TWSR & 0xF8;     
66
67
   switch(twi_status) {
68
     case TW_SR_SLA_ACK:      // 0x60: SLA+W received, ACK returned
69
       i2c_state=0;           // Start I2C State for Register Address required   
70
71
       TWCR |= (1<<TWINT);    // Clear TWINT Flag
72
       break;
73
     case TW_SR_DATA_ACK:     // 0x80: data received, ACK returned
74
       if (i2c_state == 0) {
75
         regaddr = TWDR;      // Save data to the register address
76
   i2c_state = 1;
77
       } else {
78
   regdata = TWDR;      // Save to the register data
79
   i2c_state = 2;
80
       }
81
82
       TWCR |= (1<<TWINT);    // Clear TWINT Flag
83
       break;
84
     case TW_SR_STOP:         // 0xA0: stop or repeated start condition received while selected
85
       if (i2c_state == 2) {
86
   i2c_slave_action(1); // Call Write I2C Action (rw_status = 1)
87
   i2c_state = 0;        // Reset I2C State
88
       }     
89
90
       TWCR |= (1<<TWINT);    // Clear TWINT Flag
91
       break;
92
93
     case TW_ST_SLA_ACK:      // 0xA8: SLA+R received, ACK returned
94
     case TW_ST_DATA_ACK:     // 0xB8: data transmitted, ACK received
95
       if (i2c_state == 1) {
96
   i2c_slave_action(0); // Call Read I2C Action (rw_status = 0)
97
   TWDR = regdata;      // Store data in TWDR register
98
   i2c_state = 0;        // Reset I2C State
99
       }         
100
101
       TWCR |= (1<<TWINT);    // Clear TWINT Flag
102
       break;
103
     case TW_ST_DATA_NACK:    // 0xC0: data transmitted, NACK received
104
     case TW_ST_LAST_DATA:    // 0xC8: last data byte transmitted, ACK received
105
     case TW_BUS_ERROR:       // 0x00: illegal start or stop condition
106
     default:
107
       TWCR |= (1<<TWINT);    // Clear TWINT Flag
108
       i2c_state = 0;         // Back to the Begining State
109
   }
110
   // Enable Global Interrupt
111
   sei();
112
}
113
int main(void)
114
{
115
  DDRB = 0xFE;      // Set PORTB: PB0=Input, Others as Output
116
  PORTB = 0x00;
117
  DDRD = 0xFF;      // Set PORTD to Output
118
  PORTD = 0x00;     // Set All PORTD to Low
119
120
121
  // XXX 
122
  l74hc4051_init();
123
  adc_setchannel(1);
124
  adc_init();
125
126
  // Initial I2C Slave
127
  TWAR = I2C_ADDR & 0xFE; // Set I2C Address, Ignore I2C General Address 0x00
128
  TWDR = 0x00;                 // Default Initial Value
129
  // Start Slave Listening: Clear TWINT Flag, Enable ACK, Enable TWI, TWI Interrupt Enable
130
  TWCR = (1<<TWINT) | (1<<TWEA) | (1<<TWEN) | (1<<TWIE);
131
  // Enable Global Interrupt
132
  sei();
133
134
  // Initial Variable Used
135
  regaddr=0;
136
  regdata=0;
137
138
  for(;;) {
139
  uint8_t i,j = 0;
140
        for(j=0; j < 5; j++)
141
  {
142
    for(i=0; i<L74HC4051_MAXCH; i++) {
143
      l74hc4051_setchannel(i);
144
      current_values[i*j] = adc_read(j);
145
    }
146
    _delay_us(100);
147
  }
148
149
  }
150
  return 0;
151
}

von Johannes M. (Gast)


Lesenswert?

PS: Dieser Code beinhaltet noch einige "tote" Elemente aus einem anderen 
code aus dem AVR-Forum. Daher bitte den Rest ignorieren.

von Peter D. (peda)


Lesenswert?

Johannes M. schrieb:
> Was ist die gängige
> Praxis um 2 x 8-Bit als Antwort auf einen Request über I2C zu schicken?

Einfach soviele Bytes senden, wie Du lustig bist.
Du kannst auch alle 80 Bytes übertragen.
Der Master sendet die Nummer des zu lesenden Potis und liest dann die 2 
Bytes aus. Liest er mehr Bytes aus, wird einfach das nächste Poti 
übertragen usw. Hat er keine Lust mehr, zu lesen, setzt er vor dem 
letzten Byte einfach das NACK.

von Johannes M. (Gast)


Lesenswert?

Das heißt in der Praxis?
Kann TWDR mehrere male hintereinander gesetzt werden?
Oder muss ein bestimmtes register dazwischen gesetzt werden?

von Peter D. (peda)


Lesenswert?

Steht alles im Datenblatt:

21.7.4 Slave Transmitter Mode

Table 21-5. Status Codes for Slave Transmitter Mode

Figure 21-18. Formats and States in the Slave Transmitter Mode

von jonas biensack (Gast)


Lesenswert?

>Oder muss ein bestimmtes register dazwischen gesetzt werden?

Meistens muss man ein Register auslesen, welches einen Status über das 
Versenden beinhaltet (noch im Puffer, schon gesendet...).

Gruß Jonas

von Johannes M. (Gast)


Lesenswert?

Hmm, anders als 2 Kommandos für MSB-Übertragen und LSB-Übertragen zu 
haben kriege ich es aktuell nicht ohne Probleme hin.

von Peter D. (peda)


Lesenswert?

Woran hapert es denn?
Du mußt bei jedem Daten-Interrupt (0xA8, 0xB8) einfach das nächste Byte 
senden.

Funktioniert denn der Master überhaupt?
Nimm doch erstmal einen EEPROM (z.B. 24C02) und schreibe / lese den im 
Page-Mode (z.B. 8 Byte).
Ehe das nicht geht, brauchst Du mit dem Slave garnicht erst anfangen.

von Johannes M. (Gast)


Lesenswert?

Nene, das funktioniert schon alles.
Nur bisher habe ich Byte-Weise ausgelesen und hatte ein Kommando
für LSB lesen und eins für MSB lesen.

von Peter D. (peda)


Lesenswert?

Johannes M. schrieb:
> Nene, das funktioniert schon alles.

D.h. bei einem standard Slave kann dein Master erfolgreich mehrere Bytes 
am Stück lesen/schreiben?

Johannes M. schrieb:
> Nur bisher habe ich Byte-Weise ausgelesen

Wenn der Master nur ein Byte liest, kann doch der Slave nichts für.

von Johannes M. (Gast)


Lesenswert?

Der Slave ist ein bare-bones ATMega328P.
Der Master ist ein mega328p mit Arduino-Firmare und macht folgendes:

Wire.beginTransmission(I2C_deviceAddress);
Wire.send(registerLocation);
Wire.endTransmission();
Wire.requestFrom(I2C_deviceAddress, howManyBytes);
... Wire.read() (howManyBites mal)

Ich würde jetzt erwarten, dass ich 20 x uint8_t ankommen,
wenn ich 20 Bytes anfrage.

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.