鳥なき里のマイコン屋(89) Longan nano、I2CでAQM1602

JosephHalfmoon

RISC-V搭載の超お手頃価格マイコン開発ボードLongan nanoを触ってみております。LCD、LED、DAC、USART(UART)と来たので、本日はI2Cであります。同じシリアル通信といっても、お互い取り決めだけで勝手に投げつける感のある、RX/TXのみの非同期シリアル(UART)にくらべると、I2Cはいろいろ確認しながら通信する分安心でもあり、面倒でもあり。テスト通信のお相手は毎度おなじみの秋月通商AQM1602であります。

※「鳥なき里のマイコン屋」投稿順Indexはこちら

Longan nanoに搭載されているマイコンチップ GD32VF103のI2Cインタフェースは、I2Cといって思い浮かびそうな大抵の仕事をこなすことができる「期待に応えられる」ペリフェラルじゃないかと思います。

    • I2Cのマスタにもスレーブにもなれる(勿論、どちらでも送信、受信できる)
    • 7ビットアドレスでも10ビットアドレスでも設定可能
    • マルチマスタにも対応可能
    • SMBUSモード、PMBUSモードもあり

しかし、今回、「テスト」ということで接続してみましたのは、モノクロ小型キャラクタLCD、AQM1602です。Longan nanoの場合、自分の背中にカラーLCDを搭載しているので、わざわざ別なLCDを取り付ける必然性がないですが、まあ「テスト」ですから。それに「電子工作」業界?ではAQM1602は多くの皆さんがお使いの人気の一品ではないかと思いますので。折角、いろいろな機能があるGD32VF103のI2Cですが、AQM1602に接続するときは、

    • I2Cマスタ、それも送信のみでよい。
    • 7ビットアドレス

という、もうミニマム設定でOK。まあ、とりあえず「楽」にI2Cインタフェースを動かしてみれる、ということであります。なお、

    • 使用するI2Cインタフェースは I2C0 (先に述べた諸般の事情でこちら)
    • IO電圧は3.3V

であります。なお、アイキャッチ画像でAQM1602を動かして(上下反転ですみません)いるところを掲げていますが、プルアップ抵抗はAQM1602基板の半田オプションのものを使っています。AQM1602は5Vでも3.3Vでも動作するので、この手のテストはやりやすいです。

さて、何度もLongan nanoをプログラムしていると、大分、手順が飲み込めてきました。まずは、ペリフェラル毎のクロック・イネーブル。I2C0を使用するために、GPIOBとI2C0をイネーブルにする行を追加。

    // Enable Peripheral clock
    rcu_periph_clock_enable(RCU_GPIOA);
    rcu_periph_clock_enable(RCU_GPIOB);
    rcu_periph_clock_enable(RCU_GPIOC);
    rcu_periph_clock_enable(RCU_DAC);
    rcu_periph_clock_enable(RCU_USART0);
    rcu_periph_clock_enable(RCU_I2C0);

次は、IO端子の動作モードの設定、GPIOBの6番、7番がI2C0に割り当てられるので、定数指定のモード

GPIO_MODE_AF_OD

に設定。多分、AF=オルタネーティブ・ファンクション、OD=アウトプット・デジタルってな感じですかね?

    gpio_init(GPIOC, GPIO_MODE_OUT_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_13); // LED_RED
    gpio_init(GPIOA, GPIO_MODE_OUT_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_1 | GPIO_PIN_2); // LED_GREEN, LED_BLUE
    gpio_init(GPIOA, GPIO_MODE_AIN, GPIO_OSPEED_50MHZ, GPIO_PIN_4); //DAC0
    gpio_init(GPIOA, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_9); // USART0 TX
    gpio_init(GPIOA, GPIO_MODE_IN_FLOATING, GPIO_OSPEED_50MHZ, GPIO_PIN_10); // USART0 RX
    gpio_init(GPIOB, GPIO_MODE_AF_OD, GPIO_OSPEED_50MHZ, GPIO_PIN_6 | GPIO_PIN_7); //I2C0

そして、I2C0自体の動作設定。とりあえず速度は100K。

void i2c0_config(void)
{
    i2c_clock_config(I2C0, 100000, 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);
}

例によって、SDKのExamplesのコードを「参考」にしながら、AQM1602へのデータおよびコマンド書き込み関数を準備。AQM1602の場合、まずデータなのかコマンドなのか決めるバイトを送った後、実際のデータバイトなり、コマンドバイトを送るというスタイルです。スレーブアドレスをまず送った後、この2バイトを送信するのが1単位。

void writeI2C(uint8_t dc, uint8_t dat) {
    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, AQM1602_SLAVE_ADDRESS7, 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));
    i2c_data_transmit(I2C0, dc);
    while(!i2c_flag_get(I2C0, I2C_FLAG_TBE));
    i2c_data_transmit(I2C0, dat);
    while(!i2c_flag_get(I2C0, I2C_FLAG_TBE));
    i2c_stop_on_bus(I2C0);
    while(I2C_CTL0(I2C0)&0x0200);
    delay_1ms(10);
}

さて、任意のコマンドでも、データでも送信できるようになったので、初期化関数を作成。この辺は、秋月電子通商殿のペラ紙資料に乗っていたArduino用のコードが参考(もろパクリ)になりました。

void initAQM1602() {
    delay_1ms(100);
    writeI2C(AQM1602_COM, 0x38);
    delay_1ms(20);
    writeI2C(AQM1602_COM, 0x39);
    delay_1ms(20);
    writeI2C(AQM1602_COM, 0x14);
    delay_1ms(20);
    writeI2C(AQM1602_COM, 0x73);
    delay_1ms(20);
    writeI2C(AQM1602_COM, 0x56);
    delay_1ms(20);
    writeI2C(AQM1602_COM, 0x6C);
    delay_1ms(20);
    writeI2C(AQM1602_COM, 0x38);
    delay_1ms(20);
    writeI2C(AQM1602_COM, 0x01);
    delay_1ms(20);
    writeI2C(AQM1602_COM, 0x0C);
    delay_1ms(20);
}

初期化がすめば、後は実際に文字を書くのみ。1行16文字で2行あるので、最初の行(0x80をコマンドに送ると最初の行の頭に書き込み位置が設定される)、には、0x30から0x3Fまでの文字コード(主に数字)を列挙、2行目には0x40から0x4Fまでの文字コード(@とアルファベット大文字の前半)を列挙。ま、テストなので。。。

        writeI2C(AQM1602_COM, 0x80);
        for (int i=0; i<16; i++) {
            writeI2C(AQM1602_DAT, 0x30+i);
        }

        writeI2C(AQM1602_COM, 0x80+0x40);
        for (int i=0; i<16; i++) {
            writeI2C(AQM1602_DAT, 0x40+i);
        }

アイキャッチ画像のように出力されました。

鳥なき里のマイコン屋(88) Longan nano、USARTでprintf に戻る

鳥なき里のマイコン屋(90) Longan nano、microSDへ進む