office@biosoft-m.ru 8 (495) 729-43-14

Программирование умных аккумуляторов (Smart Battery) на микроконтроллерах Atmel семейства Xmega

В автономных устройствах при работе от аккумуляторов прошивка должна иметь информацию о значениях таких параметров, как относительный заряд аккумулятора, время до полного разряда аккумулятора, других параметров. Наибольшую точность и надежность может обеспечить использование умных аккумуляторов.

Используемый нами умный аккумулятор включает в себя микросхему 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 / SMBUS

  • 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 протоколов обмена данными.

Протоколы обмена данными SMBUS v.1.1

  • 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 / SMBUS

  • 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 протоколов обмена данными.

Протоколы обмена данными SMBUS v.1.1

  • 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);
    ...
}

Реализация программы для программирования умного аккумулятора на микроконтроллере Atmel семейства Xmega
Драйвер I2C для микроконтроллера Atmel семейства Xmega
Инициализация

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;
}

Реализация протоколов SMBUS для чтения данных
Read Byte Protocol - чтение одного байта

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;
}

Read Word Protocol - чтение двух байтов

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".



Продукты