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
をしている??)。