Longan Nano で INA219 を扱ってみる


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/533274/e63968dc-736e-1c1a-af1e-0e9f4ef8b009.png)

皆さんの記事をたどってみただけなのですが，情報が少ないこのボード，自分のまとめとして記しておきます。

# 参考 URL 皆さんに感謝です

- 開発環境
- https://tomono.tokyo/2019/11/04/8005/
- フォントを使う
- http://kyoro205.blog.fc2.com/blog-entry-669.html?sp
- 公式資料置き場
- https://dl.sipeed.com/shareURL/LONGAN/Nano

# まずは開発環境

macOSで開発することは諦めました。どうもGDのtoolchainがインストール失敗します。解消する腕がありません...

上記にしたがって，VMware上のubuntu 20.04 LTSにVS Codeを導入，PIOで開発できるようにしました。以下も追加しました。

```bash
sudo usermod -a -G dialout $USER 
sudo usermod -a -G plugdev $USER 
```

platform.ini は以下のような感じです。

```platform.ini
[env:sipeed-longan-nano]
platform = gd32v
board = sipeed-longan-nano
framework = gd32vf103-sdk
upload_protocol = dfu
```

以下の手順でDFUモードにして，PIOからUploadすれば書き込まれます。

- Longan Nanoを接続しておく。
- BOOTキーを押しながらRESETを押し，先にRESETを離してBOOTを離す。
- Longan NanoをVMwareのubuntuに認識させる。
- PIO上からUpload操作を行う。

最後DFUがエラー表示となりますが，きちんと書き込めているようです。

# ボードを固定する

せっかくのケースですので，それを使います。いったん外し，ピンをはんだ付けして，ケースに戻します。

ピンがケースからかなり出ますので，私はブレッドボードに刺しました。ケース下部の厚みの分，少し浮きますが，しっかり導通しているようです。

ただし，もとから反っているようなブレッドボードではうまく固定されません。宣伝めいていますが，サンハヤトのブレッドボードではしっかり固定できました（笑）

回路図，ピンアウト図を参考に，3.3V/GND/SCL/SDAをINA219に接続します。

# LCDを使う

どうせならしっかり，くっきり見える文字を表示させたいです。以下のページを参考にさせてもらいました。

- http://kyoro205.blog.fc2.com/blog-entry-669.html?sp

フォントファイルもここからたどっていけます。microSDに書き込み，Longan Nanoに接続しておきます。

# I2Cを使う

公式ページからSDKに入るとFirmware Libraryがダウンロードでき，その中にExampleがいっぱい入っています。

Example - I2C - Master_receiver_two_bytes とか Master_transmitter あたりから，ソースをコピペして編集しました。

私はINA219をPB6/PB7に接続しました。ペリフェラルクロックを有効にし，GPIOを設定，I2Cペリフェラルを初期化します。STM32に似てますね。

```main.c
    rcu_periph_clock_enable(RCU_GPIOB);
    rcu_periph_clock_enable(RCU_I2C0);
    gpio_init(GPIOB, GPIO_MODE_AF_OD, GPIO_OSPEED_50MHZ, GPIO_PIN_6 | GPIO_PIN_7); // I2C0

    // init I2C
    i2c_clock_config(I2C0, 400000, I2C_DTCY_2);
    i2c_mode_addr_config(I2C0, I2C_I2CMODE_ENABLE, I2C_ADDFORMAT_7BITS, 0);
    i2c_enable(I2C0);
    i2c_ack_config(I2C0, I2C_ACK_ENABLE);
    // end of init I2C
```

INA219のアドレスは，2進で `1000 000x` です。Arduino等では`x`を取り除いて右詰めにして `0b1000000 == 64` で指定しますが，gd32vでは `0b1000000 == 128` で指定するようです。

自分の記事ですみませんが，
- https://qiita.com/numeru55/items/ba6fce4bbb9260351992

まずはINA219にデータを書き込む場合です。ポインターアドレス 1バイトを書き，続いてデータ2バイトを書く関数を用意しました。

```main.c
const INA219_address = 0b10000000;

void write_INA219(
    int m,  // mode 0: Pointer Address only 1:address + data  
    uint8_t p,  //  pointer address
    uint16_t d) { // data

    while(i2c_flag_get(I2C0, I2C_FLAG_I2CBSY));
    i2c_start_on_bus(I2C0);
    while(!i2c_flag_get(I2C0, I2C_FLAG_SBSEND));
    i2c_master_addressing(I2C0, INA219_address, I2C_TRANSMITTER);
    while(!i2c_flag_get(I2C0, I2C_FLAG_ADDSEND));
    i2c_flag_clear(I2C0, I2C_FLAG_ADDSEND);
    while(!i2c_flag_get(I2C0, I2C_FLAG_TBE));

    // send pointer address

    i2c_data_transmit(I2C0, p);
    while(!i2c_flag_get(I2C0, I2C_FLAG_TBE));

    if (m==1) {
        i2c_data_transmit(I2C0, (uint8_t)(d>>8));
    	while(!i2c_flag_get(I2C0, I2C_FLAG_TBE));
        i2c_data_transmit(I2C0, (uint8_t)(d & 0xff));
    	while(!i2c_flag_get(I2C0, I2C_FLAG_TBE));
    }

    i2c_stop_on_bus(I2C0);
    while(I2C_CTL0(I2C0)&0x0200);
}
```

手抜きをしてTIMEOUTの処理とかしていませんので，デバイスが見つからないと永遠に待ち続けます。自分用ではOKとしました。

これを使って，

```main.c
    write_INA219(1,0,0x39FF); // set pointer address=0 to default
    write_INA219(1,5,410); // set pointer address=5 to 410  near 40.96/0.1
```

で設定とシャント抵抗値を書き込みます。

INA219から読み込む場合は，ポインターアドレス 1バイトを書き，続けて2バイト読むという処理を行います。

```main.c
    write_INA219(0,2,0); // set pointer address=2 Bus Voltage
    uint16_t v0,v1;
    v0=((read_INA219_2byte())>>3)*4; // v0 = Bus Voltage [mV]
    v1=v0/1000;
    v0=v0-v1*1000;

    sprintf(str,"  %d.%03d [V]   ", v1, v0);
    LCD_ShowString( 0, ( FONT_HEIGHT + 1 ) * 0, str, WHITE );
```

手抜きで，OverFlowとかは無視しています。

読み込む関数も用意しました。

```main.c
uint16_t read_INA219_2byte() {

    // I2C read 2byte
    // from GD32VF103_Firmware_Library_V1.0.1/Examples/I2C/Master_receiver_two_bytes

    i2c_ackpos_config(I2C0, I2C_ACKPOS_NEXT);
    while(i2c_flag_get(I2C0, I2C_FLAG_I2CBSY));
    i2c_start_on_bus(I2C0);
    while(!i2c_flag_get(I2C0, I2C_FLAG_SBSEND));
    i2c_master_addressing(I2C0, INA219_address, I2C_RECEIVER);
    i2c_ack_config(I2C0, I2C_ACK_DISABLE);
    while(!i2c_flag_get(I2C0, I2C_FLAG_ADDSEND));
    i2c_flag_clear(I2C0, I2C_FLAG_ADDSEND);
    while(!i2c_flag_get(I2C0, I2C_FLAG_BTC));
    while(!i2c_flag_get(I2C0, I2C_FLAG_RBNE));

    uint8_t d0 = i2c_data_receive(I2C0);
    while(!i2c_flag_get(I2C0, I2C_FLAG_RBNE));
    uint8_t d1 = i2c_data_receive(I2C0);
    i2c_stop_on_bus(I2C0);
    while(I2C_CTL0(I2C0)&0x0200);
    i2c_ackpos_config(I2C0, I2C_ACKPOS_CURRENT);
    i2c_ack_config(I2C0, I2C_ACK_ENABLE);

    return (d0<<8)|d1;
}
```

# タイマー割り込みを使う

私は手抜きをしましたが（しつこい），
ガチの測定では，タイマー割り込みが使えると便利ですよね。

```GD32VF103_Firmware_Library_V1.0.1/Examples/TIMER/TIMER1_timebase```

からソースをひろってテストしてみましたが，すっと動いて嬉しかったです。

Examplesでは，割り込み時に呼ばれる関数 `void TIMER1_IRQHandler(void){}` が別ファイルに記載されていましたが，ベタに `main.c` ファイルに書いてもOKでした。


# Userボタンを使う

Boot0ボタンはUserボタンとして使えます。回路図によれば，PA8に接続され，10k外付けプルダウンありです。そのため，

押したとき:H
押していないとき:L

で読めます。

```main.c
    rcu_periph_clock_enable(RCU_GPIOA);
    gpio_init(GPIOA, GPIO_MODE_IN_FLOATING, GPIO_OSPEED_50MHZ, GPIO_PIN_8);

    // read BOOT0 button

    if (gpio_input_bit_get(GPIOA, GPIO_PIN_8)) {
            // 押されたときの処理
    }
```

# おしまい

あやうく積みボードにしてしまうところでした。最近 MicroPython なボードばかり使っていて，Longan Nanoを使えていませんでした。

ケースに入っていて，きれいなLCD付き，ユーザーボタンも付いていて，小物を作るのには便利なはずです。ということで，作るための小ネタを調べてまとめてみました。参考になれば幸いです。