不揮発性メモリに書き込まずにMAX17205燃料計を設定する方法

はじめに

MAX17205燃料計ICは、MaximのModelGaugeTM m5アルゴリズムを利用して、複数個セル直列接続電池パックの充電状態を正確に追跡します。クーロンカウント、電圧監視、温度補償を組み合わせて精度を高めるとともに、セルバランシング、過電流コンパレータ、設定可能な警告信号、SHA-256認証、スマートバッテリ準拠の動作など、いくつかの便利な機能を備えています。もちろん、バッテリの仕様に合わせた初期設定や、必要な機能の有効化・無効化を行う必要があります。しかし、すべてのレジスタを正しく設定し、希望するデバイス動作を実現するには、数回の試行錯誤が必要であり、残念ながら、これらの不揮発性レジスタは、ユーザーが7回までしか書き込むことができません。この制限を回避してプロトタイピングを行うためには、ユーザーが設定を不揮発性メモリに保存せずにデバイスにロードできるようにすることが必要です。

この記事は、MAX17205燃料計に限定して説明しています。しかし、同じ原理はMAX17201、MAX17211、およびMAX17215燃料計にも適用されます。これらのデバイスの違いは、使用するセル数と通信インターフェースのみです。

品番 セル数 通信インターフェース
MAX17201 1 I2C
MAX17205 Multiple I2C
MAX17211 1 Maxim 1-Wire®
MAX17215 Multiple Maxim 1-Wire®

メモリ空間

MAX17205のメモリ空間は、いくつかの機能ブロックに編成されています。これらのブロックのうち、最も重要な2つのブロックについての基本的な理解は、デバイス上でアプリケーション固有の設定をテストするプロセスを完全に把握するために必要です。その1つが、表1にまとめたModelGauge m5レジスタの不連続ブロックです。ページ0x00~0x04、0x0B、0x0Dで示されているこれらのレジスタは、基本的にModelGauge m5アルゴリズムの入出力となります。入力には、セル、燃料計のアプリケーション、およびリアルタイムのシステム測定値に関する情報が含まれます。例えば、DesignCapレジスタ(0x018)はセルの予想容量を保持し、 VAlrtTh(0x001)は警告機能のセル電圧の上限と下限を設定し、Current(0x00A)は電流検出抵抗の最新の電圧測定値を保持します。これらのレジスタは、I2C通信インタフェースを介して直接読み書きすることができます。

表1:ModelGauge m5のレジスタメモリマップ[1]

2つ目の主要ブロックは、表2に示す不揮発性メモリです。0x18~0x1Dページには、前述のModelGaugeの設定レジスタ、特性テーブル、デバイス情報、汎用ユーザーメモリのバックアップが含まれています。例えば、nDesignCapレジスタ(0x1B3)に格納された値は、電源投入時またはハードウェアリセット時にDesignCapレジスタ(0x018)を復元するように設定できます。nXTableレジスタ(0x180 - 0x18B)はModelGague m5アルゴリズム用のカスタムセル特性情報を含み、0x1D8 - 0x1DFレジスタはデバイスのシリアル番号と名前を含み、いくつかのユーザーメモリワード(ラベルnUser)は不揮発性メモリブロックに散らばって配置されています。 ModelGauge m5のレジスタとは異なり、不揮発性メモリはI2Cを介して直接書き込むことができません。この場合は、図1に示すように、ユーザーは同じアドレス空間にあるシャドウRAMから読み出し/書き込みを行い、COPY NV BLOCKとNV RECALLコマンドを使用して不揮発性メモリとインターフェースする必要があります。NV RECALL動作は IC の電源投入時およびハードウェアリセット後に自動的に行われますが、レジスタ0x060に0xE001を書き込むことにより手動で行うことも可能です。COPY NV BLOCKの動作を行うには、多くのステップが必要なため、データシートの「Nonvolatile Block Programming」セクションを参照してください[1]。このコマンドの主な制限は、7回しか実行できないので、デバイスにカスタムコンフィギュレーションをロードする際は、もうエラーが許されない状態かもしれません。

表2:不揮発性レジスタのメモリマップ[1]


図1:シャドーRAMと不揮発性メモリの関係[1]

最後に、MAX1705のI2Cスレーブアドレスについて簡単にメモしておきます。このデバイスとの通信に使用されるアドレスは2つあり、どちらを使用するかはアクセスするメモリ範囲に依存します。データシートの表27では、0x000~0x0FFの範囲のメモリにアクセスする場合のスレーブアドレスは0x6C、0x180~0x1FFの範囲のメモリにアクセスする場合のスレーブアドレスは0x16と規定されています[1]。しかし、データシートには、これらが一般的な7ビットアドレスではなく、8ビットアドレスであることは記載されていません。これは基本的に、図2に示すように、リード/ライトビットのためのスペースを確保するために、左に1ビット「プリシフト」されていることを意味します。したがって、7ビットのアドレスはそれぞれ0x36と0x0Bとなります。


図2:MAX17205 I2Cスレーブアドレスの明確化

不揮発性メモリの書き込み制限を回避する方法

MAX17205を設定する場合、把握すべき設定項目はたくさんあります。ユーザーは、アプリケーションに必要なデバイスの機能をすべて適切に有効にする方法を見つけ出すだけでなく、そうでないものを無効にする方法も見つけ出さなければなりません。しかも、データシートの説明が散在しており、矛盾していることもあります(78ページ[1]参照)。開発当初は、1回目(あるいは7回目であっても)の設定に失敗することはまずないでしょう。また、将来、アプリケーションの変更や前回の設定の誤りが明らかになった場合、設定を変更しなければならない可能性があることを念頭に置き、 不揮発性メモリの更新可能な回数の予備を確保しておく必要があります。 明らかに、必要なのは、不揮発性メモリにコミットする前に設定をプロトタイプ化し、不必要に1回の更新を無駄にしない方法です。

たまたま、このデバイスには、まさにこの目的に使えるコマンドが含まれているのです。Fuel Gauge Resetコマンド(Hardware Resetコマンドと混同しないでください)は、不揮発性メモリブロックをシャドーRAMに復元することなく、燃料計の動作をリセットします。この方法では、ユーザーがデバイスを設定するために通常行うようにシャドーRAMの値を変更することができますが、その後、COPY NV BLOCKコマンドではなく、燃料計リセットを実行します。シャドーRAMは不揮発性メモリに保存されている値で上書きされないので、アルゴリズムの実行を開始する前に、ModelGauge m5のレジスタは目的の設定値で初期化されます。このシナリオにおける明らかな欠点は、MAX17205が起動時に時間のかかる初期化手順を必要とし、もはやスタンドアロンデバイスとして機能しないことです。

リスト1は、いくつかのシャドーRAMレジスタを変更し、最後にConfig2レジスタ(0x0BB)に0x0001を書き込んで燃料計をリセットするシンプルな初期化関数の擬似コードを示しています。一番最後に0.5秒の遅れがあることに注意してください。これは、リセット動作が完了するまでの時間(tPOR)と、ModelGauge m5の出力レジスタが有効になるまでに必要な480msを確保するために使用されます[1]。

リスト1:MAX17205の初期化疑似コードの例

void max17205_init( void )
{   
    // max17205_write_reg( addr, value )
    max17205_write_reg( 0x18E, 0x0E0B ); // nODSCTh
    max17205_write_reg( 0x18F, 0x4040 ); // nODSCCfg
    max17205_write_reg( 0x19E, 0x9658 ); // nVEmpty
    max17205_write_reg( 0x1B0, 0x0215 ); // nConfig
    max17205_write_reg( 0x1B3, 0x27D8 ); // nDesignCap
    max17205_write_reg( 0x1A5, 0x3454 ); // nFullCapNom
    max17205_write_reg( 0x1B5, 0x0C02 ); // nPackCfg
    max17205_write_reg( 0x1B8, 0x0930 ); // nNVCfg0
    max17205_write_reg( 0x1B9, 0x080E ); // nNVCfg1
    max17205_write_reg( 0x1C0, 0xD996 ); // nVAltTh
    max17205_write_reg( 0x1C1, 0x2305 ); // nTAlrtTh
    max17205_write_reg( 0x1C2, 0x6401 ); // nSAlrtTh
    max17205_write_reg( 0x1C3, 0x00BC ); // nIAlrtTh
 
    max17205_write_reg( 0x0BB, 0x0001 ); // fuel gauge reset
    wait( 0.5 );
}

おわりに

その多くの構成オプションにより、MAX17205は幅広いバッテリ管理アプリケーションの良い候補となります。ただし、これらすべての設定で、ユーザーは設定をテストして、限られた不揮発性メモリの更新の1つを無駄にしないことを確認できる必要があります。幸いなことに、この解決策は非常にシンプルであり、設計者が設定の正しさを検証するあらゆる機会を与えてくれます。

参考文献

[1] Maxim Integrated、「Stand-Alone ModelGauge m5 Fuel Gauge with SHA-256 Authentication(SHA-256認証のスタンドアロン型ModelGauge m5燃料計)」、MAX17201/MAX17205/MAX17211/MAX17215データシート、2016年2月[2016年8月改定]





luyyy

こんにちは、チップの初期設定は、あなたの例に書かれているこれらのレジスタを設定する必要があるのでしょうか?



Matt_Mielke DigiKey Employee

チップは、例が示すものとは異なる方法で設定できます。 設定で変更されるレジスタと値は、選択したバッテリと最終的なアプリケーションによって異なります。



luyyy

わあ、お返事ありがとうございます!設定しなければならない基本的なレジスタはあるのか、お聞きしてもよろしいでしょうか?私は学生で、このチップで簡単な電池測定装置を作っているのですが、今、単電池の電圧を読み取ることができません。





Matt_Mielke DigiKey Employee

その場合、私が提供した例から始めます。私のアプリケーションでは、電圧、電流、容量を読み取ることができました。しかし、私のアプリケーションでは 2 つのセルを使用し、その公称容量があなたのセルと同じでない可能性が高いので、いくつか変更する必要があります。私のレジスタの設定をデータシートの記述と比較して、私がデフォルトのコンフィギュレーションから行った変更を確認してください。

基本レジスタについては、nConfig、nNVCfgx、nPackCfgから始めます。

以下は私のアプリケーションの完全なコードですが、参考になれば幸いです。コメント不足で申し訳ありません。


/******************************************************************************/
/*                               Includes                                     */
/******************************************************************************/
#include "mbed.h"


/******************************************************************************/
/*                                Globals                                     */
/******************************************************************************/
I2C max17205( D7, D10 ); // PF_0 and PA11
Serial data_out( PB_6, PB_7, 9600 );
Ticker tick;
DigitalOut led( LED1 );

const int slave_addr = 0x6C;
const int nslave_addr = 0x16;


/******************************************************************************/
/*                           Function Prototypes                              */
/******************************************************************************/
int read_reg( int addr );
void write_reg( int addr, int value );


/******************************************************************************/
/*                       Interrupt Service Routines                           */
/******************************************************************************/
void wake( void )
{
    SCB->SCR &= ~( SCB_SCR_SLEEPONEXIT_Msk );
}


/******************************************************************************/
/*                           Function Definitions                             */
/******************************************************************************/
/*******************************************************************************
* Function:     main 
* Author:       Matt Mielke
* Description:      
* Date:         04/03/17
*******************************************************************************/
int main( void )
{
    float RepSOC; //, VCell;
    //int Current;
    char data_str[100];

    wait( 0.5 );
    write_reg( 0x18E, 0x0A05 ); // nODSCTh
    write_reg( 0x18F, 0x4040 ); // nODSCCfg
    write_reg( 0x19E, 0x9658 ); // nVEmpty
    write_reg( 0x1B0, 0x0215 ); // nConfig
    write_reg( 0x1B3, 0x3454 ); // nDesignCap
    write_reg( 0x1A5, 0x3454 ); // nFullCapNom
    write_reg( 0x1B5, 0x0C02 ); // nPackCfg
    write_reg( 0x1B8, 0x0930 ); // nNVCfg0
    write_reg( 0x1B9, 0x080E ); // nNVCfg1
    write_reg( 0x1C0, 0xD996 ); // nVAltTh
    write_reg( 0x1C1, 0x2305 ); // nTAlrtTh
    write_reg( 0x1C2, 0x6401 ); // nSAlrtTh
    write_reg( 0x1C3, 0x00BC ); // nIAlrtTh
    write_reg( 0x0BB, 0x0001 ); // fuel gauge reset
                
    wait( 0.5 );
    tick.attach( &wake, 5.0 );
    
    while ( 1 )
    {
        led = 1;
    
        RepSOC = read_reg( 0x006 ) / 256.0;
        //VCell = read_reg( 0x009 ) * 0.078125e-3;  
    
        //Current = read_reg( 0x00A );
        //if ( Current & 0x00008000 ) Current |= 0xFFFF0000;
        
        //sprintf( data_str, "%f, %f, %f\n\r", RepSOC, VCell, Current * 1.5625e-6 / 0.0197 );

        data_out.printf( "%03.1f\0", RepSOC );
        
        led = 0;
    
        SCB->SCR &= ~( SCB_SCR_SLEEPDEEP_Msk );
        SCB->SCR |= SCB_SCR_SLEEPONEXIT_Msk;
        __WFI();        
    }
}


/*******************************************************************************
* Function:     read_reg 
* Author:       Matt Mielke
* Description:    This function utilizes the mbed I2C library to read the value
*               contained in a register on the MAX17205. It distinguishes 
*               between the volitile and non volitile address spaces. If an 
*               error occurs when writing the desired address to the device, the
*               write function will be repeaded until it succeeds. If the 
*               subsequent read operation fails, a value of -1 is returned.  
* Date:         04/03/17
*******************************************************************************/
int read_reg( int addr )
{
    char buffer[2] = {0};
    
    if ( addr & 0x100 )
    {
        addr &= 0xFF;
        while ( max17205.write( nslave_addr, (char*)&addr, 1 ) );
        if ( max17205.read( nslave_addr, buffer, 2 ) )
        {
            return -1;
        }
    }
    else
    {
        addr &= 0xFF;
        while ( max17205.write( slave_addr, (char*)&addr, 1 ) );
        if ( max17205.read( slave_addr, buffer, 2 ) )
        {
            return -1;
        }
    }
    
    return ( buffer[1] << 8 ) | buffer[0];
}


/*******************************************************************************
* Function:     write_reg 
* Author:       Matt Mielke
* Description:    This function utilizes the mbed I2C library to write to a 
*               register on the MAX17205. If the register address is in the non
*               volitile space, the non volitile address is used. The write 
*               function will repeat in a loop if it fails. 
* Date:         04/03/17
*******************************************************************************/
void write_reg( int addr, int value )
{
    char buffer[3] = {0};
    
    buffer[0] = addr & 0xFF;
    buffer[1] = value & 0xFF;
    buffer[2] = ( value >> 8 ) & 0xFF;
    
    if ( addr & 0x100 )
    {
        while ( max17205.write( nslave_addr, buffer, 3 ) );
    }
    else
    {
        while ( max17205.write( slave_addr, buffer, 3 ) );
    }
}





luyyy

分かりました。ご回答ありがとうございました。 max17205.read関数とmax17205.write関数では、一般レジスタのアドレスと不揮発性レジスタのアドレスが区別されています。




オリジナル・ソース(English)