ATMega I2C Slave - 16-Bit antwort?

Gast #3771429
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
}
#3771465
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.
Gast #3771518
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
#3772292
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.
Gast #3777977
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.

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren