RL78/G10 微少電流計の自作1|CS+でCとアセンブラ使用

当サイトにはアフィリエイト広告が表示されます。

微少電流の変化を目で見る必要があるので、微少電流計と言うか微少電流の変化を表示するものを作った。
こちらを参考にさせて頂いて、CPUをPICからG10(R5F10Y47)に変更し、表示装置をLCDからPC(パソコン)に変更した。

興味があるところを読む

動作の概要

微少電流計2(mbed+INA226版)
2019/08/15

24bitのADC(MCP3901)の生値をSPIで取得してUARTでPCに送信し、生値の処理や表示は全部PC側で行う・・・G10にはSPI-UARTゲートウエイのような働きをさせる。

電源は、ADCが5V、CPUもG10は5Vで使えるので、電源は5V一本でOK。

  • CPUのメインクロックは内蔵OSCを20MHzで使用。
  • UARTは2,000,000=2Mbps・・・こんな高速度でPCと通信できるのか分からなかったけど、やってみたら普通に通信できた。
  • SPIはポートをソフトでパタパタして実現した。速度は、6byte取得に56uS=9.3uS/byte → 107,143byte/秒 → 856Kbps。
  • 200μS毎のインターバル割込みでサンプリングしてUARTで送信(5Ksps)。

左がCPU、右がADC。
CPU:R5F10Y47(秋月)
ADC:MCP3901A0-I/SS(ミスミ)+変換基板(秋月)
PCとの接続はFT232ケーブルの5Vタイプ(秋月)で、5V電源もこのケーブルからもらってる。
※ADCは3.3Vタイプの3911も出てる。

スポンサーリンク

SPI

このADCは24bitでSPIインターフェースだ。
CH0とCH1の2CH有り、各CHが24bitなので合計で48bit=6byteがSPIで出力される。
LAP-Cで見るとこんな感じ。

読み出すときは_CSをLOWに固定したままで、_DR=0を確認しながら単純に連続で6バイト読み込めばOK。なのでADC初期化終了時に_CS=0にしてしまえば、後は_CSを動かす必要は無い=6byte毎に_CSを上下させる必要は無い(元記事では6byte毎に_CSを動かしてるけど、そうしなくても取得できます)。

CとアセンブラでSPI動作を書く

PCとの通信にはUARTが必要なのだが、G10はSPIかUARTのどっちかしか使えないんで、SPIはポートを自分でパタパタすることにした。

G13だとSPIとUART両方を同時に使えるんで、G13で動作チェックして、大体OKになってからG10に移植した(G13はちょい大きいし勿体ないんで)。
SPI操作はアセンブラにした(先にCで書いておいて、動作確認後にアセンブラで書き直した)。
コードはこれ。アセンブラで書き直すとCの約二倍の速度になる。

// Cでの連続6バイト入力(検証済み)
uint8_t* SPIRcv6(uint8_t rd[])
{
    int i, lp;
    uint8_t BitPos, data;
    for (lp=0; lp<6; ++lp) {
        data = 0;
        BitPos =0x80;               // ビット位置リセット
        for(i=0; i<8; i++){         // 8ビット繰り返し
            SCK = 0;
            SCK = 0;
            SCK = 1;
            if (SDI) data |= BitPos;// ビット入力
            BitPos = BitPos >> 1;   // ビットシフト
        }
        rd[lp] = data;
    }
    SCK = 1;
    return(rd);     // 受信データ*を戻す
}

=======================================================================================
; 自分用のアセンブラ関数用ファイル 2018/8/9
;=======================================================================================
; PUBLIC宣言はここ(TEXT内で宣言すると引数のアドレスが変になる)
.PUBLIC _asmSPIRcv6

; TEXT
    .SECTION .text,TEXT
;---------------------------------------------------------------------
; void _asmSPIRcv6(uint8_t* data)
;   AX=data pointer
;   6byte取得に56uS=9.3uS/byte → 107,143byte/秒
;---------------------------------------------------------------------
_asmSPIRcv6:
    push    hl          ; HLは要保存
    movw    hl,ax       ; 引数はAXで届くんで HL ← data
    ; for(X=0; X<6; ++X) 
    clrb    x           ; X は6byteカウント用
  .lp0: 
    clrb    b           ; B は8bitカウント用
    clrb    c           ; C はMISOを1byteに纏めたもの(それを[HL]に入れる)
    mov     d,#0x80     ; D は bit7-6-5...0 とシフトしてる
  .lp1: 
    ; 立ち上がりエッジをCLKに出力
    mov     a,p0
    and     a,#0xf7     ; CLK=0 P0.3=0
    mov     p0,a
    or      a,#0x8      ; CLK=1 P0.3=1
    mov     p0,a
    ; MISO(P0.4)を読む
    mov     a,p0        ; A=MISO
    and     a,#0x10     ; check MISO bit
    bz      $.lp2       ; if 0 skip
    ; MISO=HならCにDのbitをセット
    mov     a,c
    or      a,d         ; D goes to 0x80-0x40-0x20...0x0
    mov     c,a         ; set to C
  .lp2: 
    mov     a,d         ; shr D
    shr     a,1
    mov     d,a
    ; chek 8 bits loop
    inc     b           ; ++B
    mov     a,b
    cmp     a,#8
    bnz     $.lp1
    ; set 8 bits to data[]
    mov     a,c
    mov     [hl],a
    incw    hl
    ; chk 6 loop count
    inc     x           ; ++X
    mov     a,x
    cmp     a,#6
    bnz     $.lp0
    ; end
    pop     hl
    ret

RL78の変態アセンブラ

RL78アセンブラ言語仕様のリンク
RL78ユーザーズ・マニュアル(S1コアの命令セットはP32~)

RL78のアセンブラを良く知らないんで適当に書いてるけど、今のところは動いてる。
Cからの引数の受け渡しや戻り値などの決まりは、Cで書いてコンパイル・オプションでアセンブラソースを出してみたり、逆アセンブラでステップ実行すれば分かる。

このRL78アセンブラ、雰囲気的にはZ80なんだけど、妙に書きにくい部分が多々ありで、μVisionのように自由自在ではない。
特にインライン・アセンブラがメチャクチャ不便で、殆どインラインでは書けないんで、別ファイルにしてASMソースとしてゼロから書いてる。つまりCで書いた関数内の一部だけを高速化するとかが出来ない・・・多分。

その他「20bitのCPUである」ってことがアセンブラレベルでは非情に使い難い。
20bitだから昔の8086みたいなセグメント・レジスタやら64k範囲内アクセス用のnearやらfarやらミラー領域やら、手間のかかることが大量に出てくる。んま、Cで書いてる際には気にならないから良いか。

それにしても「#0x80」って何?なんなのこの書き方は?「$.lp0」だって凄い変態だよね。なんで「mov d,80h」や「JPNZ LP0」じゃダメなの?・・・んま文句を垂れたらキリが無いんで(^^;
取り敢えずレジスタだけを使って書いたけど、変数を使おうとすると更に変態になる必要があるんでパスしたい。

CS+内でデバッグ時、ポート値はSFRウインドウに、レジスタ値はCPUレジスタ・ウインドウに表示される。

スポンサーリンク

PC側のアプリ

アプリはG10側に「サンプリングを開始せよ」を送信し、G10は200μS毎に6byteをPCに送信する。

アプリ側では受け取った6byteの半分(前半がCH0で後半がCH1)の3byeをInt32に変換してからADCのオフセットを加え、更にdoubleに変換してからてチャートに表示する。
オフセット量はADC入力をショート(電流ゼロ状態だから本当なら3byteのゼロが来るはず)したときの値から読み出した。
また1bitの大きさ(重み)は、適当な抵抗に決まった電圧をかけ、本来流れる電流を計算で出しておき、ADCから来た値と比べて出した。
もっとまともな計算方法があるはずだけど、ざっと合ってるから良しって事で。

<抵抗に3.0Vを通電してCH0を実測した>
ショート時、ADCから 740C0 が来た。740C0・・・これをオフセットとして使う
116.6Kの抵抗に3Vをかければ26uA=260uV(CH0のシャント抵抗は10Ω)流れるはず。この時ADCからは 87FC0 が来た。

87FC0 – 740C0 = 13F00 (81664D) で 、1uA辺りの係数は 26uA ÷ 81664 ≒ 0.00032・・・これを1bitの値とする。

    for (int i = 0; i < rcvbytes; i += 6) {
        int idx = (ch == 0) ? 0 : 3; // CH0は受信データの[0]から、CH1は[3]から
        Int32 temp = (rxbuf[i + idx] << 16) + (rxbuf[i + idx + 1] << 8) + rxbuf[i + idx + 2];
        double ad = 0;
        if (ch == 0) {
            temp = (temp - 0x740C0);    // CH0はPLUS側に多めに出るのでマイナスする
            if (temp < 0) temp = 0;
            ad = (double)temp * ((double)(26.0 / 81664.0));     // CH0 0.00032
        } else {
            temp = (temp + 0x4300) & 0xffffff;  // CH1はMINUS側に少な目に出るのでプラスする
            if (temp > 0x7fffff) temp = 0;
            ad = (double)temp * ((double)(30.430 / 1197446.0)); // CH1 0.000025
        }
        // トリガチェック
        if (trgpos == 0 && checkBox_Trig.Checked == true) {
            double trigval = double.Parse(textBox_Trig.Text);
            if (ad >= trigval) trgpos = dcnt;
        }
        // チャート用に保存
        df[dcnt++] = ad;
        total += ad;
        // for debug
        _totalH += temp;
        ++_lpH;
    }
スポンサーリンク

反省

24bitのADCを使っても素人工作なのでノイズ成分が多い。
と言うことなら16bitのADCで構わないわけで、それならここのINA226のシャント抵抗を取り替えて使う方が良さそう。
余計な工作も工夫もいらないし簡単だよね。

次回はINA226を使いmbedで作る

写真で見ると6432(2512inch)な薄膜チップ抵抗な感じなんで、これの1Ωと取り替えれば行けそうな予感だけど、サイズの合うのが見付からないかも。
ミスミの検索結果 (温度係数が±15ppm/℃で2500円だ!)。ちなみに巻き線抵抗で構わなければ1500円・・・やっぱ高いわ。

INA226PRCで試してみた
2019/12/7 INA226マニュアル Configレジスタ設定など 簡単な計算式
購入時点では25mΩ1W(MAX 3.2A)の薄膜シャント抵抗(誤差1%)が付いてるんだけど、これを外して手元にあった秋月の1Ω3W(誤差1%)を無理矢理くっつけて実験。本当はこの2500円のが良いんだろうけど高くて買えませんわ。チップ抵抗の型番とサイズ表

INA226の動作電圧範囲は0~36V、シャント抵抗は1Ω、8000~0x7FFFだから1bit辺り2.5μAかな?だとすれば電流の計測レンジはMAX±81.917mAなはず。

↓菊水の電圧発生器で10Vを出し、手元にあった誤差5%の抵抗を繋いで実測した。「」は計算値。
<10Vでシャント抵抗1Ω>
1M:27~30μA(0.03mA) ← 異常 「10μA」
470K:40~42μA(0.04mA) ← 異常「21.2μA」
100K:117~120μA(0.12mA) ← 怪しい「100μA」
47K:227~230μA(0.23mA)
20K:520~522μA(0.52mA)
10K:1,025~1,027μA(1mA)
2K:5,060~5,062μA(5mA)
1K:10,125~10,127μA(10mA)
470:21,515~21,517μA(21mA)
240:42,925~42,927μA(43mA)
ショートすると81,917でOverFlow

抵抗値が大きいと誤差が大きく出るようなので、電圧を1Vにして計測してみた。

<1Vでシャント抵抗1Ω>
100K:10μA
47K:22.5μA
良いみたい。
1bitの誤差は仕方ないんで結構正確。

ファームは元々INA260用に作ってあった「LPC1114+mbed」のを改造した。
INA260とINA226はソケットで取り替え可能にし、電源投入時にConfigRegを読み出して、0x6127ならINA260、0x4127ならINA226と判定してる。
I2Cで読み出してUARTでバラバラ連続送信してTeraTermで表示。

シャント抵抗10Ωで実験

手元にあった秋月の10Ω3W(誤差1%)をくっつけて実験した。
良い感じで10倍の精度で電流を測れた。
MAXは±8.1mAになるけど、0.25μA/bitで出てる感じで、これで行きますわ。
微少電流計2(mbed+INA226版)

スポンサーリンク
スポンサーリンク
  • URLをコピーしました!

コメント

コメントする

CAPTCHA


興味があるところを読む