Battery Gauge LTC2943

Gepostet von am 17. April 2017, 10:55

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.



Stichworte LTC2943


Kommentare

Kommentarfunktion für diesen Artikel geschlossen.