AVR TWI Master ausschließlich mit Interrupt in C

OP #7747907
Lesenswert?

Bei der Programmierung eines Atmega328P als Modbus-Slave (yaMBSiavr) und der Abfrage eines I2C-Sensors alle 200ms mit der I2C-Software-Assembler-Bibliothek von Peter Fleury bekam ich immer wieder Timeouts auf dem Modbus.

Also probierte ich die TWI-Bibliothek von Peter Fleury, aber auch die "glänzt" mit Waits ;(

TWI-Bibliotheken findet man ja an jeder Ecke, nur eine die 100% und ausschließlich mit Interrupts funktioniert (ähnlich wie die uartlibrary von Peter Fleury) ist mir nicht untergekommen.

Als Basis habe ich https://github.com/scttnlsn/avr-twi genommen, eine als "nonblocking" beworbene Interrupt-Version die gleichmal solche lustige Sachen enthielt:

1
uint8_t *twi_wait() {
2
  while (busy);
3
  return &transmission.buffer[1];
4
}
5

6
void twi_write(uint8_t address, uint8_t* data, uint8_t length, void (*callback)(uint8_t, uint8_t *)) {
7
  twi_wait();
8
  [...]
9
}

Soviel zu "nonblocking" ;)

Hab' den ganzen Delay- und Wait-Mist rausgeschmissen, herausgekommen sind nun die angehängten Dateien die es auch unter https://github.com/joed74/avr-twi gibt.

Was nicht implementiert ist, ist repeated Start - bislang nutze ich also nur Write / Read jeweils mit Start/Stop.

Verwendet werden kann die Bibliothek z.B. so:

1
if (readtemp==1) {
2
    twi_write(0x44,0xFD);
3
    uint8_t length=6;
4
    uint8_t *buffer=NULL;
5
    if (twi_read(0x44,&length,&buffer)==TWI_OK) {
6
       if (length==6) {
7
            uint16_t t=buffer[0]*256+buffer[1];
8
            uint16_t h=buffer[3]*256+buffer[4];
9
            reg.temperature=-45.0f+175.0f*((float) t/65535.0f);
10
            reg.humidity=-6.0f+125.0f*((float) h/65535.0f);
11
            if (reg.humidity<0) reg.humidity=0;
12
            if (reg.humidity>100) reg.humidity=100;
13
       }
14
       readtemp++;
15
    }
16
}

readtemp wird von einem Timer alle 200ms auf 1 gesetzt. An den I2C-Sensor an Adresse 0x44 wird dann das Kommando 0xFD (Messung starten) gesendet. Der twi_write wird intern solange wiederholt bis ein ACK empfangen wird.

Der Sensor benötigt 10ms bis das Ergebnis abrufbar ist. In dieser Zeit wird auf eine Leseanfrage mit NACK geantwortet. Der twi_read wird dementsprechend solange wiederholt bis ein ACK empfangen wird. Wenn ein ACK empfangen wurde wird readtemp um 1 erhöht, somit wird dieser Programmteil erst wieder beim nächsten Setzen von 1 vom Timer ausgeführt.

Die Bibliothek ist so aufgebaut das nach einem twi_write intern ein twi_read "erwartet" wird. Wenn dieses Verhalten nicht gewünscht ist muss nach einem erfolgreichen twi_write ein twi_resetstate aufgerufen werden:

1
switch (operation) {
2
   case 1:
3
     if (twi_write(address, &data, 2)==TWI_OK) {
4
        twi_resetstate();
5
        operation++;
6
     }
7
     break;
8

9
   case 2:
10
     if (twi_write(address, &data, 2)==TWI_OK) {
11
        operation++;
12
     }
13
     break;
14

15
   case 3:
16
     if (twi_read(address, &length, &data)==TWI_OK) {
17
        operation++;
18
     }
19
     break;
20
}

Hier bietet sich eine "State-Machine" an, wenn der Rückgabewert des ersten twi_write TWI_OK (=ACK) ist kann mit twi_resetstate() der interne Status zurückgesetzt werden und die nächste Operation (ein weiteres twi_write) aufgerufen werden.

Insgesamt gibt es bis jetzt fünf Funktionen:

1
void twi_init()

Initialisiert das TWI(0) Interface

1
void twi_resetstate()

Setzt den internen Status zurück (siehe oben)

1
TWI_STATUS twi_write(uint8_t address, uint8_t* data, uint8_t length)

Schreibt den Buffer data mit der Länge length an die Adresse address, Rückgabe als TWI_STATUS: TWI_OK, TWI_BUSY oder TWI_NOK

1
TWI_STATUS twi_write1(uint8_t address, uint8_t data)

Schreibt 1 Byte an die angegebene Adresse, shortcut damit man keinen Buffer braucht

1
TWI_STATUS twi_read(uint8_t address, uint8_t *length, uint8_t **data)

Empfängt Daten von Adresse address. In data wird ein Pointer auf die empfangenen Daten zurückgegeben. Der Längenpointer length muss vor dem Aufruf mit der erwarteten Anzahl Bytes belegt werden, bei TWI_OK steht dort dann die Länge der empfangenen Daten drin.

Getestet wurde die Bibliothek mit Atmega324PB (twi0) und einem Atmega328P (twi). Ein Test mit Atmega328PB (twi0) steht noch aus..

Angehängte Dateien:

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