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 |
|
2 |
|
3 |
|
4 |
|
5 |
|
6 |
|
7 |
|
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 |
|
2 |
|
3 |
|
4 |
|
5 |
|
6 |
|
7 |
|
8 |
|
9 |
|
10 |
|
11 |
|
12 |
|
13 |
|
14 |
|
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 |
|
2 |
|
3 |
|
4 |
|
5 |
|
6 |
|
7 |
|
8 |
|
9 |
|
10 |
|
11 |
|
12 |
|
13 |
|
14 |
|
15 |
|
16 |
|
17 |
|
18 |
|
19 |
|
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 |
|
Initialisiert das TWI(0) Interface
1 |
|
Setzt den internen Status zurück (siehe oben)
1 |
|
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 |
|
Schreibt 1 Byte an die angegebene Adresse, shortcut damit man keinen Buffer braucht
1 |
|
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..