« 定電流回路の電流検出抵抗 | トップページ | 異形のハンダ付け補助ツール・・・試作で »

2020年2月21日 (金)

JIS C8708:2019充放電試験回路試運転

JIS C8708:2019充放電試験回路製作中、ざっと
制御ソフトが出来たので試運転中です。

今回はJIS C8708:2019の充電条件へ対応。
「0.5Cで132分 又は -ΔV(5mV~10mV)」
これに対応するため、電池電圧を読むA/D入力に
アンプを入れました。

Arduino(ATmega328P)のA/Dコンバータは10bit。
基準電圧を2.5Vにして0~2.5Vをそのまま入力
すると1ビットが約2.44mV。
5~10mVの-ΔVを拾うとすると、その変化量は
2~4ビットと微少。
もうちょい分解能が欲しいか、ということでこんな
アンプをA/Dの前段に入れました。 (U6 1,2,3のところ)

C1

考え方
・電池の寿命試験で電池電圧を測るのに0V付近は不要。
・0.9Vくらいから2.0Vの範囲を測れたらよいんじゃないか。
・A/Dの分解能を1ビットを1mVくらいにしたい。
・とりあえず、入力範囲を0.85V~2.1Vにして、測定スパン
 を1.25Vに。
・これを2倍に増幅。
・1ビットはきっちり1mVにはならないで約1.22mV。

こんな回路になっています。

また、A/D入力処理も、速度は不要なんで
・1mSごとのA/D変換で、256回の加算平均処理。
 256回分10ビットのA/D値を加算合計して、256回目に
 1/256して平均値を算出。256mSごとにA/Dデータが確定。
・この平均値を16回移動平均。
 リングバッファの古いのを捨てて新しいのを加える。
・さらに、バッファのデータをソートして、上下それぞれ
 4つのデータを捨て(maxに近い4つとminに近い4つ)、
 8つの中央値で平均計算。
・256mSごとにこのスムージング処理したデータが確定。
 16回なんで、A/D入力値が安定するまで約4秒。
・これで、一瞬の短絡や開放はデータに出てこない。

こんな処理で-ΔVを見つけるようにしました。

これでうまく処理できるか、試運転中です。

※OP-AMPについて
今回使ったMCP6072、電源電圧5V以下で使うならおすすめです。
特徴
・安価  100円くらい  ただしDIPは無い
・レール to レール入力/出力
・ゲインバンド幅 1.2MHz
・低オフセット電圧 0.15mV(max) ★
・入力バイアス電流 1pA (CMOSなんで)
・低消費電流 0.11mA
・動作電圧範囲 1.8V~6V

オフセット電圧の小さなCMOSアンプって、なかなか
ないんですよね。
ただ、DIP品が無いので試作にはピッチ変換基板が必須です。


※追記

・1msタイマー処理とその中で起動されるA/D変換。
・A/D割り込みで行う256回のA/D値平均処理。
  ↑
この2つの処理は割り込みで行うので、時間待ちは無し。
勝手にA/D平均データが上がってきます。

この中でスイッチ入力処理とブザー報知処理も実行。
液晶画面表示を行っていると、スイッチのチャタリング除去
処理やブザー報知の処理が遅れることがあるんで、タイマー
割り込みの中で勝手に処理するようにします。

・メインルーチン内でf_adokフラグをチェックして
 行う256msごとのA/D値スムージング処理。
 ソートを行うので、割り込み内からは出してます。

これを示しておきます。 (スペースは全角で)

/********************************/
/*   1ms タイマー割り込み  */
/********************************/
// 関数プロトタイプ宣言 (タイマー割込み内で処理)
void swscan(void);      // SW入力スキャン
void bzzexc(void);      // ブザー報知実行
/***** タイマーデータ  *****/
volatile byte tm_1ms;   // 1msダウンカウントタイマー
volatile byte tm_10ms;   // 10msダウンカウントタイマー
volatile byte f_1sec;   // 1秒経過フラグ
volatile byte f_1min;   // 1分経過フラグ
// 測定用タイマー(カウントアップ)
byte f_tmion;        // 計時指令
              // onで計測タイマーをカウントアップ
volatile word tmi_1min;  // 1分 カウントアップタイマー
              // 24時間で1440分 999分で16時間39分
volatile byte tmi_1sec;  // 1秒 カウントアップタイマー
              // 00~59
volatile byte tmi_10ms;  // 10ms カウントアップタイマー
// tmmreadで読む測定タイマー
word tmm_1min;       // 1分 max5999分=99時間59分
byte tmm_1sec;       // 1秒 0~59
/***** タイマー0コンペアマッチA割込み    *****/
// 割り込みでパルス出力(1kHz周期)
ISR(TIMER0_COMPA_vect)
{
static byte cnt10  = 0;
  PB0_H;           // (!!!) 14pin
  if(tm_1ms)   tm_1ms--;  // 1msダウンカウント
// 10msタイマー
  cnt10++;
  if(cnt10 >= 10){
    cnt10 = 0;
    if(tm_10ms)   tm_10ms--;   // 10mS ダウンカウント
    if(f_tmion){          // 計時する?
     tmi_10ms++;
     if(tmi_10ms >= t_spdup){   // 100カウントで1秒
      tmi_10ms = 0;
      f_1sec = 1;         // 1秒フラグをオン
      tmi_1sec++;         // +1秒
      if(tmi_1sec >= 60){     // 60秒
       tmi_1sec = 0;
       f_1min = 1;        // 1分フラグをオン
       tmi_1min++;        // +1分
       if(tmi_1min >= 6000){   // 6000分越え?
        tmi_1min = 5999;
        tmi_1sec = 59;     // 99時間59分59秒に
       }
      }
     }
    }
  }
// 1ms処理関数
  swscan();      // SW入力スキャン
  bzzexc();      // ブザー報知実行
// 内蔵A/D開始
  ADCSRA |= (1 << ADSC);   // A/D変換開始
  PB0_L;           // (!!!) 14pin
}

/******************************/
/*   内蔵A/D処理    */
/******************************/
/**** A/D データ    *****/
#define ADAVR_ADD   256   // A/D平均回数
volatile word ad_avr;     // A/D変換データ 0~1023
                // 平均処理された結果
volatile ulong ad_add;     // A/D平均処理用加算データ
volatile byte f_adok;     // A/D変換完了フラグ
                // 割り込みでセット
// 平均処理確定でad_avrを転送 電池電圧に変換
byte f_advolt;         // A/Dデータ転送変換完了フラグ
word ad_data;          // 電圧A/D値 (平均値)
word bat_ad;          // スムージング処理結果A/D値 (0~1023)
word bat_volt;         // 電池電圧 bat_adをmV値に変換
word bat_peak;         // 電池電圧ピーク値(mV)
word bat_deltav;        // ΔV値 ピーク-現在値 (mV)
byte f_peakon;         // ピーク検出開始フラグ
/***** A/D割り込み処理  *****/
//  1msタイマー割り込みでA/D変換開始
//  256回で平均値算出
ISR(ADC_vect)
{
word d;
static word cnt = 0;  // A/D変換平均回数
  PB4_H;           // (!!!) 18pin
  d = (word)ADCL;       //
  d |= (ADCH & 0x03) << 8;  // 符号なしで 0~3FF
// 測定値
  ad_add += (ulong)d;     // 平均用に加算
// 平均処理
  cnt++;           // 平均加算回数
  if(cnt >= ADAVR_ADD){    // 256回?
    cnt = 0;
    ad_avr = (word)(ad_add / ADAVR_ADD); // 平均
    ad_add = 0L;      // 次加算データクリア
    f_adok = 1;       // 変換完了
  }
  PB4_L;           // (!!!) 18pin
}
/***********************************/
/* A/Dデータスムージング処理   */
/***********************************/
// 処理バッファ
word ad_smzbff[16];     // A/Dデータ保存バッファ
word ad_sort[16];      // ソート用バッファ
/***** qsort用データ比較    *****/
// wordで比較
int wordcmp( const void *p, const void *q ) {
  if( *(word *)p > *(word *)q )   return 1;
  if( *(word *)p < *(word *)q )   return -1;
  return 0;
}
/***** スムージング処理  *****/
// 16コのA/Dデータを順にサンプル
// ソートして上下4コ(8コ)を捨て、中央の8コを平均
// 256ms X 16 = 4.096秒後に安定
word adsmz(word d)
{
static byte f1 = 0;   // はじめてフラグ
static byte wp;     // 書き込みポインタ
byte i;
long a;
word b;
  if(f1 == 0){        // 初めて
    f1 = 1;
    for(i = 0; i < DIMSIZ(ad_smzbff); i++){
      ad_smzbff[i] = d;    // バッファを初期データで埋める
    }
    wp = 0;           // データ書き込みポインタ
  }
  else{            // 2回目以降
    ad_smzbff[wp] = d;       // A/Dデータをストア
    wp++;              // 書き込みポインタを進める
    if(wp >= DIMSIZ(ad_smzbff))  wp = 0;  // 一周回って先頭に
  }
  memcpy(ad_sort, ad_smzbff, sizeof(ad_smzbff)); // バッファにコピー
  qsort(ad_sort, DIMSIZ(ad_sort), sizeof(word), wordcmp); // qsort実行
// 中央の8つで平均処理 上下4コは捨てる
  a = 0;             // 合計
  for(i = 0; i < 8; i++){     // 中央の8コを加算
    a += ad_sort[4 + i];    // 4コ目から8コを加算
  }
  b = word(a / 8);        // 1/8して平均値
  if((a % 8) >= 4)    b += 1; // 四捨五入
  return b;
}

全体のスケッチはまた今度に。

|

« 定電流回路の電流検出抵抗 | トップページ | 異形のハンダ付け補助ツール・・・試作で »

電池」カテゴリの記事

電子工作」カテゴリの記事

Arduino」カテゴリの記事

コメント

2020年04月10日:JIS C8708:2019対応ニッケル水素電池充放電実験回路(回路図とプログラム)
http://igarage.cocolog-nifty.com/blog/2020/04/post-614212.html

投稿: 居酒屋ガレージ店主(JH3DBO) | 2020年4月11日 (土) 09時36分

コメントを書く



(ウェブ上には掲載しません)




« 定電流回路の電流検出抵抗 | トップページ | 異形のハンダ付け補助ツール・・・試作で »