В автономных устройствах при работе от аккумуляторов прошивка должна иметь информацию о значениях таких параметров, как относительный заряд аккумулятора, время до полного разряда аккумулятора, других параметров. Наибольшую точность и надежность может обеспечить использование умных аккумуляторов.
Используемый нами умный аккумулятор включает в себя микросхему TI bq20z90, которая обеспечивает поддержку технологии SBS - Smart Battery System - система умного аккумулятора. SBS обеспечивает передачу информации о состоянии аккумулятора на другие устройства и изменение значений некоторых параметров. Обмен данными между умным аккумулятором и микроконтроллером устройства происходит по интерфейсу SMBUS v.1.1.
- RelativeStateOfCharge - относительный заряд (от 0 до 100 %);
- AverageCurrent - средний ток (усреднение за период 14.5 с);
- Voltage - напряжение;
- Temperature - температура аккумулятора;
- AverageTimeToEmpty - время до полного разряда аккумулятора (вычисляемое по значению среднего тока);
- BatteryStatus - флаги ошибкок и текущих режимов.
- RemainingCapacityAlarm - значение оставшегося заряда для выдачи тревожного сообщения о разряде;
- BatteryMode - изменение режима работы аккумулятора и формата выдачи информации (использовать для вычислений и выдачи информации мА или мВт, передавать в зарядное устройство ток и напряжение или нет, др.);
- CycleCount - количество циклов заряда/разряда;
- DesignCapacity - заявленная емкость аккумулятора;
- DesignVoltage - заявленное напряжение аккумулятора;
- SerialNumber - серийный номер;
- и другие параметры.
Изменяемые параметры доступны для редактирования разработчиком аккумулятора при переводе аккумулятора в режим "полного доступа" (Full Access) или "разблокирован" (Unsealed). В режиме "заблокирован" (Sealed) изменение параметров недоступно. После изменения значений параметров может потребоваться перекалибровка аккумулятора.
- TWI - название интерфейса микроконтроллера;
- I2C - двухпроводной интерфейс от Philips;
- SMBUS- двухпроводной интерфейс для связи с микросхемами управления питанием, основан на I2C.для передачи данных используются две линии - тактовых импульсов (SCL) и данных (SDA);
- допустимая частота тактовых импульсов: для I2C от 0 до 400 кГц, для SMBUSот 10 кГц до 100 кГц;
- каждое устройство на шине имеет уникальный адрес разрядностью 7 бит (за исключением зарезервированных адресов); в случае использования устройств с одинаковыми адресами необходимо использовать I2C-хаб;
- данные с ведущего на ведомый передаются в виде пакетов следующей структуры: старт импульс (1 бит, служебный - начало обмена данными), адрес (7 бит), тип операции - чтение или запись (1 бит), флаг подтверждения - принят или нет (1 бит), данные (8 бит), флаг подтверждения (1 бит) {при необходимости продолжение обмена данными - данные (8 бит), флаг подтверждения (1 бит), ...}, стоп импульс (1 бит, служебный - конец обмена данными);
- при изменении направления передачи данных (запись / чтение) надо установить повторный старт импульс (1 бит, служебный), адрес (7 бит), тип операции - чтение или запись (1 бит), далее аналогично предыдущему пункту;
- при появлении ошибок надо остановить передачу (установить стоп импульс);
- интерфейс I2C не регламентирует протокол обмена данными, интерфейс SMBUS v.1.1 имеет 8 протоколов обмена данными.
- Quick Command
- Send Byte
- Receive Byte
- Write Byte/Word
- Read Byte/Word
- Process Call
- Block Read
- Block Write
Для работы с двумя аккумуляторами их необходимо подключить к микроконтроллеру не напрямую, а через I2C-хаб (по причине совпадающих адресов). I2c-хаб позволяет выбрать один, второй, оба или ни одного аккумулятора для обмена данными. Нужно выбирать поочередно первый и второй аккумулятор.
Для задачи мониторинга состояния аккумулятора необходимо только вычитывание параметров. Из списка протоколов SMBUS необходимо реализовать Read Byte Protocol, Read Word Protocol.
- RelativeStateOfCharge - относительный заряд (от 0 до 100 %);
- AverageCurrent - средний ток (усреднение за период 14.5 с);
- Voltage - напряжение;
- Temperature - температура аккумулятора;
- AverageTimeToEmpty - время до полного разряда аккумулятора (вычисляемое по значению среднего тока);
- BatteryStatus - флаги ошибкок и текущих режимов.
- RemainingCapacityAlarm - значение оставшегося заряда для выдачи тревожного сообщения о разряде;
- BatteryMode - изменение режима работы аккумулятора и формата выдачи информации (использовать для вычислений и выдачи информации мА или мВт, передавать в зарядное устройство ток и напряжение или нет, др.);
- CycleCount - количество циклов заряда/разряда;
- DesignCapacity - заявленная емкость аккумулятора;
- DesignVoltage - заявленное напряжение аккумулятора;
- SerialNumber - серийный номер;
- и другие параметры.
Изменяемые параметры доступны для редактирования разработчиком аккумулятора при переводе аккумулятора в режим "полного доступа" (Full Access) или "разблокирован" (Unsealed). В режиме "заблокирован" (Sealed) изменение параметров недоступно. После изменения значений параметров может потребоваться перекалибровка аккумулятора.
- TWI - название интерфейса микроконтроллера;
- I2C - двухпроводной интерфейс от Philips;
- SMBUS- двухпроводной интерфейс для связи с микросхемами управления питанием, основан на I2C.для передачи данных используются две линии - тактовых импульсов (SCL) и данных (SDA);
- допустимая частота тактовых импульсов: для I2C от 0 до 400 кГц, для SMBUSот 10 кГц до 100 кГц;
- каждое устройство на шине имеет уникальный адрес разрядностью 7 бит (за исключением зарезервированных адресов); в случае использования устройств с одинаковыми адресами необходимо использовать I2C-хаб;
- данные с ведущего на ведомый передаются в виде пакетов следующей структуры: старт импульс (1 бит, служебный - начало обмена данными), адрес (7 бит), тип операции - чтение или запись (1 бит), флаг подтверждения - принят или нет (1 бит), данные (8 бит), флаг подтверждения (1 бит) {при необходимости продолжение обмена данными - данные (8 бит), флаг подтверждения (1 бит), ...}, стоп импульс (1 бит, служебный - конец обмена данными);
- при изменении направления передачи данных (запись / чтение) надо установить повторный старт импульс (1 бит, служебный), адрес (7 бит), тип операции - чтение или запись (1 бит), далее аналогично предыдущему пункту;
- при появлении ошибок надо остановить передачу (установить стоп импульс);
- интерфейс I2C не регламентирует протокол обмена данными, интерфейс SMBUS v.1.1 имеет 8 протоколов обмена данными.
- Quick Command
- Send Byte
- Receive Byte
- Write Byte/Word
- Read Byte/Word
- Process Call
- Block Read
- Block Write
Для работы с двумя аккумуляторами их необходимо подключить к микроконтроллеру не напрямую, а через I2C-хаб (по причине совпадающих адресов). I2c-хаб позволяет выбрать один, второй, оба или ни одного аккумулятора для обмена данными. Нужно выбирать поочередно первый и второй аккумулятор.
Для задачи мониторинга состояния аккумулятора необходимо только вычитывание параметров. Из списка протоколов SMBUS необходимо реализовать Read Byte Protocol, Read Word Protocol.
В общем виде, программирование умного аккумулятора на микроконтроллере заключается в реализации протоколов SMBUS v.1.1 Read Byte Protocol, Read Word Protocol через использование функций драйвера для интерфейса TWI / I2C / SMBUS микроконтроллера: I2cSend1Byte, I2cReceive1Byte, I2cReceive2Bytes.
Функция I2cSend1Byte(u8Address, u8Data) отправляет 1 байт данных в устройство по указанному адресу.
Функции I2cReceive1Byte(u8Address, &u8Data) и I2cReceive2Bytes(u8Address, &u8Data0, &u8Data1) принимают 1 и 2 байта, соответственно, от устройства по указанному адресу.
int main(void)
{
...
InitI2cMaster(C_I2cBitRateInKhz, ClockSystem.u16CpuClockInKhz);
...
// send command to get RelativeStateOfCharge - 1 byte
I2cSend1Byte(u8AddressBattery, u8CommandToGetRelativeStateOfCharge);
// read RelativeStateOfCharge - 1 byte
I2cReceive1Byte(u8AddressBattery, &u8RelativeStateOfCharge);
...
// send command to get AverageCurrent - 1 byte
I2cSend1Byte(u8AddressBattery, u8CommandToGetAverageCurrent);
// read AverageCurrent - 2 bytes
I2cReceive2Bytes(u8AddressBattery, &u8AverageCurrentLsb, &u8AverageCurrentMsb);
...
}
bool InitI2cMaster(U16 u16BitRateInKhz, U32 u32SystemClockInKhz)
{
// test for correct input frequency
if ((u32SystemClockInKhz / 2 / u16BitRateInKhz - 5) < 0)
{
return false;
}
TWIC.CTRL = 0; // clear common register
// enable TWI / I2C / SMBUS
TWIC.MASTER.CTRLA = TWI_MASTER_ENABLE_bm;
// set timeout for SMBUS compatibility
TWIC.MASTER.CTRLB = TWI_MASTER_TIMEOUT_50US_gc;
TWIC.MASTER.CTRLC = 0;
// force initial bus state to idle
TWIC.MASTER.STATUS = TWI_MASTER_BUSSTATE_IDLE_gc;
// set baud rate
TWIC.MASTER.BAUD = u32SystemClockInKhz / 2 / u16BitRateInKhz - 5;
return true;
}
bNextOpChangeDirection - необходимость смены направления передачи данных после этой команды.
bNotStandard - вместо бита повторного старта обмена данными используются биты стоп и старт передачи данных.
bool I2cSend1Byte(U8 u8Address, U8 u8Data, bool bNextOpChangeDirection, bool bNotStandard)
{
U16 u16TimeoutCounter;
if (GetI2cBusState() == TWI_MASTER_BUSSTATE_BUSY_gc)
{
// aribtration lost
if (IsI2cArbLost())
{
TWIC.MASTER.STATUS = TWI_MASTER_ARBLOST_bm;
}
TWIC.MASTER.CTRLC = TWI_MASTER_CMD_STOP_gc;
return false;
}
// send slave address to the I2C bus
TWIC.MASTER.ADDR = u8Address & ~0x01;
// check bus state after address sent
u16TimeoutCounter = 0;
while (u16TimeoutCounter++ <= C_MaxTimeout)
{
// aribtration lost
if (IsI2cWif() && IsI2cArbLost())
{
TWIC.MASTER.STATUS = TWI_MASTER_ARBLOST_bm;
TWIC.MASTER.CTRLC = TWI_MASTER_CMD_STOP_gc;
return false;
}
// slave not acknoledge
if (IsI2cWif() && IsI2cRxAck())
{
TWIC.MASTER.CTRLC = TWI_MASTER_CMD_STOP_gc;
return false;
}
// slave acknoledge
if (IsI2cWif() && !IsI2cRxAck())
{
break;
}
}
// no activity on I2C bus
if (u16TimeoutCounter == C_MaxTimeout)
{
TWIC.MASTER.CTRLC = TWI_MASTER_CMD_STOP_gc;
return false;
}
TWIC.MASTER.DATA = u8Data;
u16TimeoutCounter = 0;
while (u16TimeoutCounter++ <= C_MaxTimeout)
{
// aribtration lost
if (IsI2cArbLost())
{
TWIC.MASTER.CTRLC = TWI_MASTER_CMD_STOP_gc;
return false;
}
// slave not acknowledge
if (IsI2cWif() && IsI2cRxAck())
{
TWIC.MASTER.CTRLC = TWI_MASTER_CMD_STOP_gc;
return false;
}
// data is sent successfully
if (IsI2cWif() && IsI2cClkHold())
{
break;
}
}
// no activity on I2C bus
if (u16TimeoutCounter == C_MaxTimeout)
{
TWIC.MASTER.CTRLC = TWI_MASTER_CMD_STOP_gc;
return false;
}
if (bNextOpChangeDirection)
{
if (bNotStandard)
{
// for used smart battery chip
// no repeat start command, just stop and start again
TWIC.MASTER.CTRLC = TWI_MASTER_CMD_STOP_gc;
}
else
{
// for standard SMbus 1.1
// repeated start
TWIC.MASTER.CTRLC = TWI_MASTER_CMD_REPSTART_gc;
}
// delay between stop and start
for (u16TimeoutCounter = 0; u16TimeoutCounter < C_MaxTimeout; u16TimeoutCounter++);
}
else
{
// stop data transmition
TWIC.MASTER.CTRLC = TWI_MASTER_CMD_STOP_gc;
}
return true;
}
bool I2cReceive1Byte(U8 u8Address, U8 *u8Data)
{
U16 u16TimeoutCounter;
if (GetI2cBusState() == TWI_MASTER_BUSSTATE_BUSY_gc)
{
// aribtration lost
if (IsI2cArbLost())
{
TWIC.MASTER.STATUS = TWI_MASTER_ARBLOST_bm;
}
TWIC.MASTER.STATUS = TWI_MASTER_BUSSTATE_IDLE_gc;
return false;
}
// send slave address to the I2C bus
TWIC.MASTER.ADDR = u8Address | 0x01;
// check bus state after address sent
u16TimeoutCounter = 0;
while (u16TimeoutCounter++ <= C_MaxTimeout)
{
// aribtration lost
if (IsI2cWif() && IsI2cArbLost())
{
TWIC.MASTER.STATUS = TWI_MASTER_ARBLOST_bm;
TWIC.MASTER.CTRLC = TWI_MASTER_CMD_STOP_gc;
return false;
}
// slave not acknowledge
if (IsI2cRif() && IsI2cRxAck())
{
TWIC.MASTER.CTRLC = TWI_MASTER_CMD_STOP_gc;
return false;
}
// slave acknowledge
if (IsI2cRif() && !IsI2cRxAck())
{
break;
}
}
// no activity on I2C bus
if (u16TimeoutCounter == C_MaxTimeout)
{
TWIC.MASTER.CTRLC = TWI_MASTER_CMD_STOP_gc;
return false;
}
u16TimeoutCounter = 0;
while (u16TimeoutCounter++ <= C_MaxTimeout)
{
// data is ready to read
if (IsI2cRif() && IsI2cClkHold())
{
break;
}
}
// no activity on I2C bus
if (u16TimeoutCounter == C_MaxTimeout)
{
TWIC.MASTER.CTRLC = TWI_MASTER_CMD_STOP_gc;
return false;
}
// get data byte
*u8Data = TWIC.MASTER.DATA;
// send nack to indicate no further read allowed
// and send stop to finish transfer
TWIC.MASTER.CTRLC = TWI_MASTER_ACKACT_bm | TWI_MASTER_CMD_STOP_gc;
return true;
}
bool I2cReceive2Bytes(U8 u8Address, U8 *u8Data0, U8 *u8Data1)
{
...
// get the first data byte
*u8Data0 = TWIC.MASTER.DATA;
// send acknowledge
TWIC.MASTER.CTRLC = TWI_MASTER_CMD_RECVTRANS_gc;
u16TimeoutCounter = 0;
while (u16TimeoutCounter++ <= C_MaxTimeout)
{
// data is ready to read
if (IsI2cRif() && IsI2cClkHold())
{
break;
}
}
// no activity on I2C bus
if (u16TimeoutCounter == C_MaxTimeout)
{
TWIC.MASTER.CTRLC = TWI_MASTER_CMD_STOP_gc;
return false;
}
// get the second data byte
*u8Data1 = TWIC.MASTER.DATA;
// send nack to indicate no further read allowed
// and send stop to finish transfer
TWIC.MASTER.CTRLC = TWI_MASTER_ACKACT_bm | TWI_MASTER_CMD_STOP_gc;
return true;
}
bool ProcessSbsCommandS1bR1b(U8 u8Address, U8 u8Data2Send, U8 *u8Data0)
{
U16 u16TimeoutCounter;
bool bResult = false;
// send command - 1 byte
bResult = I2cSend1Byte(u8Address, u8Data2Send, true, true);
if (!bResult)
{
return bResult;
}
// read data - 1 byte
bResult = I2cReceive1Byte(u8Address, u8Data0);
return bResult;
}
bool ProcessSbsCommandS1bR2b(U8 u8Address, U8 u8Data2Send, U8 *u8Data0, U8 *u8Data1)
{
U16 u16TimeoutCounter;
bool bResult = false;
// send command - 1 byte
bResult = I2cSend1Byte(u8Address, u8Data2Send, true, true);
if (!bResult)
{
return bResult;
}
// read data - 2 byte
bResult = I2cReceive2Bytes(u8Address, u8Data0, u8Data1);
return bResult;
}
#define C_Pca9543ApwAddress 0xE0 // A1, A0 is set to zero
#define C_Pca9543ApwChannel1 1
#define C_Pca9543ApwChannel2 2
#define C_BatteryAddress 0x16
#define C_BatteryCmdGetTemperature 0x08
#define C_BatteryCmdGetVoltage 0x09
#define C_BatteryCmdGetAverageCurrent 0x0b
#define C_BatteryCmdGetRelativeStateOfCharge 0x0d
U8 GetPowerLevels(U8 *u8PowerLevelAcc1, U8 *u8PowerLevelAcc2)
{
// выбор 1-го канала
bResult = I2cSend1Byte( C_Pca9543ApwAddress,
C_Pca9543ApwChannel1,
false,
false);
// получение данных от аккумулятора - относительный заряд
bResult = ProcessSbsCommandS1bR1b(C_BatteryAddress,
C_BatteryCmdGetRelativeStateOfCharge,
u8PowerLevelAcc1);
if (!bResult)
{
// батарея не подключена, проверить по состоянию переключателя питания
*u8PowerLevelAcc1 = 0;
}
//============== other values ======================================
...
// получение температуры
bResult = ProcessSbsCommandS1bR2b(C_BatteryAddress,
C_BatteryCmdGetTemperature,
&g_u8DataReceived0,
&g_u8DataReceived1);
g_u16BatteryTemperature = (g_u8DataReceived1 << 8) + g_u8DataReceived0;
...
//============== other values end ==================================
// выбор 2-го канала
bResult = I2cSend1Byte( C_Pca9543ApwAddress,
C_Pca9543ApwChannel2,
false,
false);
// получение данных от аккумулятора - относительный заряд
bResult = ProcessSbsCommandS1bR1b(C_BatteryAddress,
C_BatteryCmdGetRelativeStateOfCharge,
u8PowerLevelAcc2);
if (!bResult)
{
// батарея не подключена, проверить по состоянию переключателя питания
*u8PowerLevelAcc2 = 0;
}
}
int main(void)
{
...
InitI2cMaster(C_I2cBitRateInKhz, ClockSystem.u32CpuClockInKhz);
...
u8Result =GetPowerLevels(& g_au8PowerLevelBat[0], & g_au8PowerLevelBat[1]);
...
}
Полученные результаты исследований используются при разработке портативных систем автономного электропитания для медицинских изделий "Комплекс универсальный для механической поддержки насосной функции левого и правого желудочков сердца "СТРИМ КАРДИО" и "Аппарат перфузионный для экстракорпоральной оксигенации"Ex-Stream".