Bei mobilen Systemen, die mit Batterien oder Akkus versorgt werden, ist es immer wichtig zu wissen, wie viel Kapazität noch in der Batterie ist. In einfachen Fällen lässt sich dies mit einer Spannungsmessung feststellen. Dies ist aber relativ ungenau und wird stark vom aktuellen Belastungsfall und vom Innenwiderstand der Batterie beeinflusst. Gerade bei Systemen, bei denen die Batterie nicht immer komplett entladen und geladen wird, wie zum Beispiel bei Inselsystemen mit Photovoltaikmodulen ist es schwierig herauszufinden, wie der Ladestand der Batterie gerade ist.
Coulomb-Zähler
Eine bessere Möglichkeit ist es, die Ladung zu messen, die aus bzw. in die Batterie fließt. Dazu muss dauernd der Strom gemessen werden und diese Werte aufintegriert werden. Dadurch kann man zu jeder Zeit sagen, wie viel Ladung noch in der Batterie ist. Eventuelle Verluste innerhalb der Batterie (zum Beispiel Selbstentladung oder Gasung) müssen aber herausgerechnet werden.
LTC2943
Solche Messschaltungen kann man selber aufbauen oder man verwendet einen fertigen Baustein, wie den LTC2943 von Linear Technologies . Dieser Baustein hat intern einen Coulomb-Counter der per I2C ausgelesen werden kann. Zusätzlich wird noch Strom, Spannung und Temperatur gemessen, die ebenfalls per I2C ausgelesen werden können.
Anschluss an einen Raspberry Pi
Der LTC2943 kann einfach mit dem I2C-Bus vom Raspberry Pi verbunden werden. Allerdings benötigt der Baustein unbedingt einen Repeated Start um die Register auszulesen. Dies unterstützt der Rapsberry Pi von Haus aus nicht. Man kann dies aber durch folgenden Befehl aktivieren:
sudo sh -c '/bin/echo Y > /sys/module/i2c_bcm2708/parameters/combined'
Der Status dieser Einstellung kann mit folgendem Befehl überprüft werden:
cat /sys/module/i2c_bcm2708/parameters/combined
Wenn dann ein N ausgegeben wird, ist der Repeated Start deaktiviert. Wenn ein Y ausgegeben wird, ist der Repeated Start aktiviert.
Auslesen mit Repeated Start
Wenn der Repeated Start aktiviert wurde, kann der LTC2943 mit folgendem Programm ausgelesen werden:
#include <stdio.h> #include <stdlib.h> #include <sys/stat.h> #include <linux/i2c-dev.h> #include <fcntl.h> #include <unistd.h>
unsigned short int swap_bytes(unsigned short int input) { return (((input & 0xff) << 8) | ((input >> 8) & 0xff)); }
int main(void) { int file; char x; unsigned short int charge,voltage,current,temperature;
printf("LTC2943\n"); if((file=open("/dev/i2c-1", O_RDWR))<0) // open i2c-bus { perror("cannot open i2c-1"); exit(1); } if(ioctl(file, I2C_SLAVE, 0x64)<0) // open slave { perror("cannot open slave address"); exit(1); }
i2c_smbus_write_byte_data(file,0x01,0x7c); // automatic mode usleep(45000); x=i2c_smbus_read_byte_data(file, 0x00); // status printf("Status %02x\n",x); x=i2c_smbus_read_byte_data(file, 0x01); // control printf("Control %02x\n",x); charge=swap_bytes(i2c_smbus_read_word_data(file, 0x02)); // charge printf("Charge %f mAh (%04x)\n",(float)charge*3.4,charge); // R = 5 mOhm voltage=swap_bytes(i2c_smbus_read_word_data(file, 0x08)); // voltage printf("Voltage %f V (%04x)\n",(float)voltage*0.0003601,voltage); current=swap_bytes(i2c_smbus_read_word_data(file, 0x0e)); // current printf("Current %f A (%04x)\n",(float)(current-0x7fff)*0.000366,current); // R = 5 mOhm temperature=swap_bytes(i2c_smbus_read_word_data(file, 0x14)); // temperature printf("Temperature %f K = %f C (%04x)\n",(float)temperature*0.00778,(float)temperature*0.00778-274.0,temperature);
close(file);
return 0; }
Leider läuft dieses Programm bei mir nicht zuverlässig und liefert nur ab und zu vernünftige Werte.
Auslesen in einen Buffer
Eine weitere Möglichkeit zum Auslesen des LTC2943 besteht darin, den Baustein komplett in einem Rutsch auszulesen. Nach jedem STOP-Befehl auf dem I2C-Bus wird der interne Adresszeiger des LTC2943 wieder auf 0 zurückgesetzt und somit weiß man genau, was in welchem Byte steht.
Das Programm schaut dann so aus:
#include <stdio.h> #include <stdlib.h> #include <sys/stat.h> #include <linux/i2c-dev.h> #include <fcntl.h> #include <unistd.h>
unsigned short int swap_bytes(unsigned short int input) { return (((input & 0xff) << 8) | ((input >> 8) & 0xff)); }
int main(void) { int file; int x; __u8 buf[32]; unsigned short int charge,voltage,current,temperature;
printf("LTC2943\n"); if((file=open("/dev/i2c-1", O_RDWR))<0) // open i2c-bus { perror("cannot open i2c-1"); exit(1); } if(ioctl(file, I2C_SLAVE, 0x64)<0) // open slave { perror("cannot open slave address"); exit(1); }
i2c_smbus_write_byte_data(file,0x01,0x7c); // automatic mode usleep(45000); x=i2c_smbus_read_i2c_block_data(file, 0x00,22,buf); // status printf("read %i bytes from device\n",x); charge=((buf[2])<<8)+buf[3]; voltage=((buf[8])<<8)+buf[9]; current=((buf[14])<<8)+buf[15]; temperature=((buf[20])<<8)+buf[21]; printf("Status %02x\n",buf[0]); printf("Control %02x\n",buf[1]); printf("Charge %f mAh (%04x)\n",(float)charge*3.4,charge); // R = 5 mOhm printf("Voltage %f V (%04x)\n",(float)voltage*0.0003601,voltage); printf("Current %f A (%04x)\n",(float)(current-0x7fff)*0.000366,current); // R = 5 mOhm printf("Temperature %f K = %f C (%04x)\n",(float)temperature*0.00778,(float)temperature*0.00778-274.0,temperature);
close(file);
return 0; }
Diese Programm funktioniert bei mir ohne Probleme und liefert zuverlässig alle Werte.
Kommentare
Kommentarfunktion für diesen Artikel geschlossen.