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:
Initialisiert das TWI(0) Interface
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..