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 に時刻を表示させてみよ.