I2C (ESP-IDF 環境 + C 言語)
はじめに
教育ボードに搭載されているリアルタイムクロック (RTC) を使う.
プログラムの書き方.
I2C のプログラムの書き方は, ESP-IDF Programming Guide の API Reference の I2C Driver を参照して欲しい.
I2C で RTC と通信する際は, i2c_master_write_byte などの引数を通信相手先の都合に合わせる必要がある. そのためには各機器のデータシートを確認せねばならない.
I2C のテスト
全部のセンサーについてアドレスの取得を確認.
$ cp -r esp-idf/examples/peripherals/i2c/i2c_tools ./ $ cd i2c_tools $ make menuconfig $ make $ make flash monitor (Ctrl-] で終了) esp32> i2cconfig --port=0 --sda=21 --scl=22 --freq=100000 esp32> i2cdetect 0 1 2 3 4 5 6 7 8 9 a b c d e f 00: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 30: -- -- 32 -- -- -- -- -- -- -- -- -- -- -- 3e -- 40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 70: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
出力結果より, 0x3e (LCD) と 0x30 (RTC) が接続されていることがわかる.
プログラムの作成
標準出力に 1 秒間隔で時刻を表示するプログラムを作成する.
準備
まずは適当な資源一式のディレクトリをコピーして名前を変えておく. ここでは hello_world のディクトリをコピーすることにした.
$ cp -r hello_world i2c $ cd i2c $ mv main/hello_world_main.c main/i2c_main.c
プログラム (i2c_main.c) の作成
RTC に時刻を設定し,時間を標準出力に出力するようにしてみる.以下のサンプルは <URL:i2c-rtc.c> でダウンロードできる.
1 #include <stdio.h> 2 #include <time.h> 3 #include "driver/i2c.h" 4 #include "esp_err.h" 5 #include "esp_log.h" 6 #include "freertos/task.h" 7 8 #define SDA_PIN GPIO_NUM_21 9 #define SCL_PIN GPIO_NUM_22 10 #define rtc_address 0x32 //I2Cアドレスの指定 11 12 //I2C初期化 13 void i2c_init(){ 14 i2c_port_t port = 0; 15 uint32_t speed = 400 * 1000; //スピードはデフォルト値 16 17 i2c_config_t config = { 18 .mode = I2C_MODE_MASTER, //マスターの設定 19 .scl_io_num = SCL_PIN, //SCLのGPIO番号の設定 20 .sda_io_num = SDA_PIN, //SDAのGPIO番号の設定 21 .scl_pullup_en = true, //プルアップする 22 .sda_pullup_en = true, //プルアップする 23 .master.clk_speed = speed, //スピードの設定 24 }; 25 26 i2c_param_config(port, &config); //configの読み込み 27 i2c_driver_install(port, I2C_MODE_MASTER, 0, 0, 0); 28 } 29 30 //RTC初期化 31 void rtc2_init(){ 32 i2c_port_t port = 0; 33 i2c_cmd_handle_t cmd = i2c_cmd_link_create(); 34 35 i2c_master_start(cmd); 36 i2c_master_write_byte(cmd, rtc_address << 1 | I2C_MASTER_WRITE, I2C_MASTER_ACK); //上位 7 ビットが I2C アドレス, 下位 1 ビットが書き込み (I2C_MASTER_WRITE) か読み込み (I2C_MASTER_READ) を示す. 37 i2c_master_write_byte(cmd, 0xE0, I2C_MASTER_ACK); //アドレス Eh の指定. 前半4bitがE、後半4bitが0なので、0xE0 38 i2c_master_write_byte(cmd, 0x00, I2C_MASTER_ACK); //アドレス Eh への書き込み 39 i2c_master_write_byte(cmd, 0x00, I2C_MASTER_ACK); //自動インクリメントなので、Fh への書き込み 40 i2c_master_stop(cmd); 41 i2c_master_cmd_begin(port, cmd, 1 / portTICK_RATE_MS); 42 i2c_cmd_link_delete(cmd); 43 44 vTaskDelay(100/ portTICK_PERIOD_MS); //0.1秒待つ 45 } 46 47 //時刻セット. 48 void rtc2_set(){ 49 i2c_port_t port = 0; 50 i2c_cmd_handle_t cmd = i2c_cmd_link_create(); 51 52 //時刻を設定する。アドレス 0h から順に秒・分・時・week・日・月・年(下2桁)を入れる 53 //ここで,2020-11-30 23:59:30 にセットせよ.但し,week は 1 にセットすれば良い. 54 // 55 // ............ 56 // 57 58 vTaskDelay(100/ portTICK_PERIOD_MS); 59 60 //書き込み後に、アドレスFhをゼロクリアする 61 // 62 // ............ 63 // 64 65 vTaskDelay(100/ portTICK_PERIOD_MS); 66 } 67 68 69 //時刻の呼び出し. "...." の部分は自分で直すこと. 70 void rtc2_get(struct tm *tt){ 71 uint8_t data_rd[8]; 72 i2c_port_t port = 0; 73 74 i2c_cmd_handle_t cmd = i2c_cmd_link_create(); 75 i2c_master_start(cmd); 76 i2c_master_write_byte(cmd, ..............., I2C_MASTER_ACK); //読み込みは? rtc2_init を参照. 77 i2c_master_read_byte(cmd, &data_rd[0], I2C_MASTER_ACK); //1byte目はackあり 78 i2c_master_read_byte(cmd, &data_rd[1], I2C_MASTER_ACK); //2byte目はackあり 79 i2c_master_read_byte(cmd, &data_rd[2], I2C_MASTER_ACK); //3byte目はackあり 80 i2c_master_read_byte(cmd, &data_rd[3], I2C_MASTER_ACK); //4byte目はackあり 81 i2c_master_read_byte(cmd, &data_rd[4], I2C_MASTER_ACK); //5byte目はackあり 82 i2c_master_read_byte(cmd, &data_rd[5], I2C_MASTER_ACK); //6byte目はackあり 83 i2c_master_read_byte(cmd, &data_rd[6], I2C_MASTER_ACK); //7byte目はackあり 84 i2c_master_read_byte(cmd, &data_rd[7], I2C_MASTER_NACK); //読み取りの最後は NACK 85 i2c_master_stop(cmd); 86 i2c_master_cmd_begin(port, cmd, 1 / portTICK_RATE_MS); 87 i2c_cmd_link_delete(cmd); 88 89 tt->tm_year = data_rd[7]; 90 tt->tm_mon = data_rd[6]; 91 tt->tm_mday = data_rd[5]; 92 tt->tm_hour = ......... ; //24時間モードの場合は? 93 tt->tm_min = data_rd[2]; 94 tt->tm_sec = data_rd[1]; 95 } 96 97 //メインプログラム 98 void app_main() { 99 100 struct tm tt; //時刻を入れる構造体 101 102 //I2C初期化 103 i2c_init(); 104 105 //RTC初期化 106 rtc2_init(); 107 108 //時刻の設定. この関数を自分で作ること. 109 rtc2_set(); 110 111 while(1){ 112 rtc2_get( &tt ); //時刻取得. 要修正. 113 114 printf("20%02x-%02x-%02x ", tt.tm_year, tt.tm_mon, tt.tm_mday); 115 printf("%02x:%02x:%02x \n", tt.tm_hour, tt.tm_min, tt.tm_sec); 116 117 vTaskDelay(1000 / portTICK_PERIOD_MS); 118 } 119 120 }
コンパイルと実行
$ make $ make flash monitor
課題
上記プログラムを作成せよ.余裕があれば,時刻に連動して LED を点灯させたり,LCD に時刻を表示させてみよ.