SNSへはこちら

ESP32で遊んでみる(7) - I2C

I2C で AQM1602 を動かそうという感じです。比較的素直ですね。

実装が素直なおかげで、特にハマらず、簡単にできました
なので、説明は至極簡素にさせていただきますね。

ソースコード

こちらに示しておきます。前半は AQM1602 用のソースコードです。lcd_ で始まるものはこの LCD モジュール特有の関数として設計しました。

GPIO の設定はしなくても、I2C 側の関数でいい感じにやってくれます。

#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/i2c.h"
#include "sdkconfig.h"

// LCD I2C Funcs {{{
const static uint8_t lcd_init_cmds[] = {
        0x38, // function set
        0x39, // function set
        0x14, // internal OSC frequency
        0x73, // contrast set
        0x56, // Power/IOCON/contrast control
        0x6c, // follower control
        0x38, // function set
        0x01, // clear display
        0x0c, // display ON/OFF control
};
const static uint8_t address = 0x7c;

void lcd_cmd(uint8_t dat) {
    uint8_t arr[] = {0x00, dat};
    i2c_cmd_handle_t handle = i2c_cmd_link_create();
    i2c_master_start(handle);
    i2c_master_write_byte(handle, address, true);
    i2c_master_write(handle, arr, 2, true);
    i2c_master_stop(handle);
    i2c_master_cmd_begin(0, handle, 0xFF);
    i2c_cmd_link_delete(handle);
}
void lcd_data(uint8_t dat) {
    uint8_t arr[] = {0x40, dat};
    i2c_cmd_handle_t handle = i2c_cmd_link_create();
    i2c_master_start(handle);
    i2c_master_write_byte(handle, address, true);
    i2c_master_write(handle, arr, 2, true);
    i2c_master_stop(handle);
    i2c_master_cmd_begin(0, handle, 0xFF);
    i2c_cmd_link_delete(handle);
}
void lcd_str(char *str) {
    while( *str != '\0' ) {
        lcd_data(*str);
        str++;
    }
}

void lcd_init(void) {
    for(int i=0; i< 9; i++){
        lcd_cmd(lcd_init_cmds[i]);
        printf("LCD Init #%d\r\n", i);
        vTaskDelay(1 / portTICK_PERIOD_MS);
        if( i == 5 ) vTaskDelay(200 / portTICK_PERIOD_MS);
    }
}
// }}}

void vI2cAqm1602(void *pvParameters) {
    i2c_config_t config = {
        .mode = I2C_MODE_MASTER,
        .sda_io_num = 25,
        .scl_io_num = 26,
        .sda_pullup_en = true,
        .scl_pullup_en = true,
        .master.clk_speed = 10e3, // 10kHz
    };
    i2c_param_config(I2C_NUM_0, &config); // I2C0
    i2c_driver_install(I2C_NUM_0, I2C_MODE_MASTER, 0, 0, 0);

    lcd_init();
    lcd_str("Hello");

    while(1) {
        vTaskDelay(1);
    }
}

void app_main(void) {
    xTaskCreatePinnedToCore(vI2cAqm1602, "I2C Transferring to AQM1602", 1024 * 2, NULL, 1, NULL, 1);
}

データ送信

比較的素直です。単純に送りたいデータバイトを送出するだけ。i2c_master_cmd_begin はブロッキング処理をする(送信が完了するまで返ってこない)ので、ポーリング処理をするには簡単です。
本来ですと ACK が返っているかをチェックする必要があるので、その辺はやるべきです。

void lcd_cmd(uint8_t dat) {
    uint8_t arr[] = {0x00, dat};
    i2c_cmd_handle_t handle = i2c_cmd_link_create();
    i2c_master_start(handle);
    i2c_master_write_byte(handle, address, true);
    i2c_master_write(handle, arr, 2, true);
    i2c_master_stop(handle);
    i2c_master_cmd_begin(0, handle, 0xFF); // execute
    i2c_cmd_link_delete(handle);
}

このように i2c_cmd_handle_t 型のハンドラーを用意して、処理を行います。その後の関数(start から stop まで)は handler にキューを追加します。つまりどのようなデータを送ってどのような動作をさせるのか、を handle に追加してあげるイメージです(おそらく)。
一連の動作を関数によって追加した上で、i2c_master_cmd_begin によってその動作を実行します。

最後に i2c_cmd_link_delete を呼ぶことで、handle を解放します(恐らく内部で malloc とか free をしている??)。