EspressifのソースコードにあるI2CのサンプルをベースにしてPCA9624PWを動かします。
sampleコードは照度センサーBH1750を動かすようになっています。
LEDコントローラのPCA9624PWの動かし方などは、以前紹介したPCA9624PW I2C LEDモジュールのエントリをご覧ください。
参考にしたのは以下のコードです。
- /esp-idf/examples/peripherals/i2c
#Step1 デバイスの接続
ソースコードを参考にすると以下がわかります
マスターはGPIO18をdata signal(SDA), GPIO19をclock signal(SCL)として使用
スレーブはGPIO25をdata signal(SDA), GPIO26をclock signal(SCL)として使用
/**
* TEST CODE BRIEF
*
* This example will show you how to use I2C module by running two tasks on i2c bus:
*
* - read external i2c sensor, here we use a BH1750 light sensor(GY-30 module) for instance.
* - Use one I2C port(master mode) to read or write the other I2C port(slave mode) on one ESP32 chip.
*
* Pin assignment:
*
* - slave :
* GPIO25 is assigned as the data signal of i2c slave port
* GPIO26 is assigned as the clock signal of i2c slave port
* - master:
* GPIO18 is assigned as the data signal of i2c master port
* GPIO19 is assigned as the clock signal of i2c master port
*
* Connection:
*
* - connect GPIO18 with GPIO25
* - connect GPIO19 with GPIO26
* - connect sda/scl of sensor with GPIO18/GPIO19
* - no need to add external pull-up resistors, driver will enable internal pull-up resistors.
*
* Test items:
*
* - read the sensor data, if connected.
* - i2c master(ESP32) will write data to i2c slave(ESP32).
* - i2c master(ESP32) will read data from i2c slave(ESP32).
*/
今回はMasterのみ確認するので、ピンは以下を接続します
WROOM32 --- PCA9624PW
MASTER SDA D18 -> SDA
MASTER SCL D19 -> SCL
3V3 --> VDD
GND --> GND
GND --> OE
#Step2 I2Cの操作プログラム
I2Cのslave への書き込みを行います。
<I2Cの初期化 i2c_example_master_init()>
このコードは変更なしで、このまま使用します。
設定しているのは主に以下の内容です。
- マスターモード
- SDAポートの指定/プルアップ設定
- SCLポートの指定/プルアップ設定
- I2Cのクロック周波数の指定
/**
* @brief i2c master initialization
*/
static void i2c_example_master_init()
{
int i2c_master_port = I2C_EXAMPLE_MASTER_NUM;
i2c_config_t conf;
conf.mode = I2C_MODE_MASTER;
conf.sda_io_num = I2C_EXAMPLE_MASTER_SDA_IO;
conf.sda_pullup_en = GPIO_PULLUP_ENABLE;
conf.scl_io_num = I2C_EXAMPLE_MASTER_SCL_IO;
conf.scl_pullup_en = GPIO_PULLUP_ENABLE;
conf.master.clk_speed = I2C_EXAMPLE_MASTER_FREQ_HZ;
i2c_param_config(i2c_master_port, &conf);
i2c_driver_install(i2c_master_port, conf.mode,
I2C_EXAMPLE_MASTER_RX_BUF_DISABLE,
I2C_EXAMPLE_MASTER_TX_BUF_DISABLE, 0);
}
<slaveへのコマンド送信>
この辺りを参考にします。
/**
* @brief test code to write esp-i2c-slave
*
* 1. set mode
* _________________________________________________________________
* | start | slave_addr + wr_bit + ack | write 1 byte + ack | stop |
* --------|---------------------------|---------------------|------|
* 2. wait more than 24 ms
* 3. read data
* ______________________________________________________________________________________
* | start | slave_addr + rd_bit + ack | read 1 byte + ack | read 1 byte + nack | stop |
* --------|---------------------------|--------------------|--------------------|------|
*/
static esp_err_t i2c_example_master_sensor_test(i2c_port_t i2c_num, uint8_t* data_h, uint8_t* data_l)
{
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
i2c_master_start(cmd);
i2c_master_write_byte(cmd, BH1750_SENSOR_ADDR << 1 | WRITE_BIT, ACK_CHECK_EN);
i2c_master_write_byte(cmd, BH1750_CMD_START, ACK_CHECK_EN);
i2c_master_stop(cmd);
int ret = i2c_master_cmd_begin(i2c_num, cmd, 1000 / portTICK_RATE_MS);
i2c_cmd_link_delete(cmd);
if (ret == ESP_FAIL) {
return ret;
}
vTaskDelay(30 / portTICK_RATE_MS);
cmd = i2c_cmd_link_create();
i2c_master_start(cmd);
i2c_master_write_byte(cmd, BH1750_SENSOR_ADDR << 1 | READ_BIT, ACK_CHECK_EN);
i2c_master_read_byte(cmd, data_h, ACK_VAL);
i2c_master_read_byte(cmd, data_l, NACK_VAL);
i2c_master_stop(cmd);
ret = i2c_master_cmd_begin(i2c_num, cmd, 1000 / portTICK_RATE_MS);
i2c_cmd_link_delete(cmd);
if (ret == ESP_FAIL) {
return ESP_FAIL;
}
return ESP_OK;
}
処理の流れは以下です。
- コマンドリンクの作成
- Start信号のセット
- slaveデバイスのアドレスの指定
- コマンドの入力
- End信号のセット
- コマンドの実行
- コマンドリンクの破棄
上記を参考にして、PCA9624PW(アドレス0xC0)へのコマンドを発行する関数を書くとこんな感じです。
static esp_err_t i2c_write_to_PCA9624PW(i2c_port_t i2c_num, uint8_t* data_h, uint8_t* data_l)
{
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
i2c_master_start(cmd);
i2c_master_write_byte(cmd, PCA9624PW_SENSOR_ADDR | WRITE_BIT, ACK_CHECK_EN);
i2c_master_write(cmd,data_h,*data_l,ACK_CHECK_EN);
i2c_master_stop(cmd);
int ret = i2c_master_cmd_begin(i2c_num, cmd, 1000 / portTICK_RATE_MS);
i2c_cmd_link_delete(cmd);
if (ret == ESP_FAIL) {
return ret;
}
vTaskDelay(30 / portTICK_RATE_MS);
return ESP_OK;
}
sampleでは、i2c_master_write_byte()を使用していますが、これは1byte用なので、byte数を指定して複数バイト分を入力できるi2c_master_write()を使用しました。
#Step3 動作確認
Step2で作成した関数を使用してslaveとして接続したPCA9624PWの8個のLEDを1秒置きに全点灯、全消灯させるプログラムです。
/* i2c - Example
For other examples please check:
https://github.com/espressif/esp-idf/tree/master/examples
This example code is in the Public Domain (or CC0 licensed, at your option.)
Unless required by applicable law or agreed to in writing, this
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied.
*/
#include <stdio.h>
#include "driver/i2c.h"
/**
* TEST CODE BRIEF
*
* This example will show you how to use I2C module by running two tasks on i2c bus:
*
* - read external i2c sensor, here we use a BH1750 light sensor(GY-30 module) for instance.
* - Use one I2C port(master mode) to read or write the other I2C port(slave mode) on one ESP32 chip.
*
* Pin assignment:
*
* - slave :
* GPIO25 is assigned as the data signal of i2c slave port
* GPIO26 is assigned as the clock signal of i2c slave port
* - master:
* GPIO18 is assigned as the data signal of i2c master port
* GPIO19 is assigned as the clock signal of i2c master port
*
* Connection:
*
* - connect GPIO18 with GPIO25
* - connect GPIO19 with GPIO26
* - connect sda/scl of sensor with GPIO18/GPIO19
* - no need to add external pull-up resistors, driver will enable internal pull-up resistors.
*
* Test items:
*
* - read the sensor data, if connected.
* - i2c master(ESP32) will write data to i2c slave(ESP32).
* - i2c master(ESP32) will read data from i2c slave(ESP32).
*/
#define DATA_LENGTH 512 /*!<Data buffer length for test buffer*/
#define RW_TEST_LENGTH 129 /*!<Data length for r/w test, any value from 0-DATA_LENGTH*/
#define DELAY_TIME_BETWEEN_ITEMS_MS 1000 /*!< delay time between different test items */
#define I2C_EXAMPLE_SLAVE_SCL_IO 26 /*!<gpio number for i2c slave clock */
#define I2C_EXAMPLE_SLAVE_SDA_IO 25 /*!<gpio number for i2c slave data */
#define I2C_EXAMPLE_SLAVE_NUM I2C_NUM_0 /*!<I2C port number for slave dev */
#define I2C_EXAMPLE_SLAVE_TX_BUF_LEN (2*DATA_LENGTH) /*!<I2C slave tx buffer size */
#define I2C_EXAMPLE_SLAVE_RX_BUF_LEN (2*DATA_LENGTH) /*!<I2C slave rx buffer size */
#define I2C_EXAMPLE_MASTER_SCL_IO 19 /*!< gpio number for I2C master clock */
#define I2C_EXAMPLE_MASTER_SDA_IO 18 /*!< gpio number for I2C master data */
#define I2C_EXAMPLE_MASTER_NUM I2C_NUM_1 /*!< I2C port number for master dev */
#define I2C_EXAMPLE_MASTER_TX_BUF_DISABLE 0 /*!< I2C master do not need buffer */
#define I2C_EXAMPLE_MASTER_RX_BUF_DISABLE 0 /*!< I2C master do not need buffer */
#define I2C_EXAMPLE_MASTER_FREQ_HZ 100000 /*!< I2C master clock frequency */
#define PCA9624PW_SENSOR_ADDR 0xC0 /*!< slave address for PCA9624PW sensor */
#define PCA9624PW_REDISTER_MODE1 0x00
#define PCA9624PW_REDISTER_LEDOUT0 0x0C
#define PCA9624PW_REDISTER_LEDOUT1 0x0D
#define PCA9624PW_REDISTER_LED_CONTINUOUS 0x80
#define BH1750_SENSOR_ADDR 0x23 /*!< slave address for BH1750 sensor */
#define BH1750_CMD_START 0x23 /*!< Command to set measure mode */
#define ESP_SLAVE_ADDR 0x28 /*!< ESP32 slave address, you can set any 7bit value */
#define WRITE_BIT I2C_MASTER_WRITE /*!< I2C master write */
#define READ_BIT I2C_MASTER_READ /*!< I2C master read */
#define ACK_CHECK_EN 0x1 /*!< I2C master will check ack from slave*/
#define ACK_CHECK_DIS 0x0 /*!< I2C master will not check ack from slave */
#define ACK_VAL 0x0 /*!< I2C ack value */
#define NACK_VAL 0x1 /*!< I2C nack value */
xSemaphoreHandle print_mux;
/**
* @brief i2c master initialization
*/
static void i2c_example_master_init()
{
int i2c_master_port = I2C_EXAMPLE_MASTER_NUM;
i2c_config_t conf;
conf.mode = I2C_MODE_MASTER;
conf.sda_io_num = I2C_EXAMPLE_MASTER_SDA_IO;
conf.sda_pullup_en = GPIO_PULLUP_ENABLE;
conf.scl_io_num = I2C_EXAMPLE_MASTER_SCL_IO;
conf.scl_pullup_en = GPIO_PULLUP_ENABLE;
conf.master.clk_speed = I2C_EXAMPLE_MASTER_FREQ_HZ;
i2c_param_config(i2c_master_port, &conf);
i2c_driver_install(i2c_master_port, conf.mode,
I2C_EXAMPLE_MASTER_RX_BUF_DISABLE,
I2C_EXAMPLE_MASTER_TX_BUF_DISABLE, 0);
}
/**
* @brief test function to show buffer
*/
void disp_buf(uint8_t* buf, int len)
{
int i;
for (i = 0; i < len; i++) {
printf("%02x ", buf[i]);
if (( i + 1 ) % 16 == 0) {
printf("\n");
}
}
printf("\n");
}
/**
* @brief test code to write esp-i2c-slave
*
* 1. set mode
* _________________________________________________________________
* | start | slave_addr + wr_bit + ack | write 1 byte + ack | stop |
* --------|---------------------------|---------------------|------|
* 2. wait more than 24 ms
* 3. read data
* ______________________________________________________________________________________
* | start | slave_addr + rd_bit + ack | read 1 byte + ack | read 1 byte + nack | stop |
* --------|---------------------------|--------------------|--------------------|------|
*/
static esp_err_t i2c_write_to_PCA9624PW(i2c_port_t i2c_num, uint8_t* data_h, uint8_t* data_l)
{
disp_buf(data_h, *data_l);
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
i2c_master_start(cmd);
i2c_master_write_byte(cmd, PCA9624PW_SENSOR_ADDR | WRITE_BIT, ACK_CHECK_EN);
i2c_master_write(cmd,data_h,*data_l,ACK_CHECK_EN);
i2c_master_stop(cmd);
int ret = i2c_master_cmd_begin(i2c_num, cmd, 1000 / portTICK_RATE_MS);
i2c_cmd_link_delete(cmd);
if (ret == ESP_FAIL) {
return ret;
}
vTaskDelay(30 / portTICK_RATE_MS);
return ESP_OK;
}
static void i2c_test_task_PCA9624PW(void* arg)
{
int ret;
uint32_t task_idx = (uint32_t) arg;
uint8_t data_init_PCA9624PW[] = {PCA9624PW_REDISTER_MODE1,0x00};
uint8_t data_led_on_PCA9624PW[] = {PCA9624PW_REDISTER_LEDOUT0|PCA9624PW_REDISTER_LED_CONTINUOUS,0x55,0x55};
uint8_t data_led_off_PCA9624PW[] = {PCA9624PW_REDISTER_LEDOUT0|PCA9624PW_REDISTER_LED_CONTINUOUS,0x00,0x00};
uint8_t data_init_PCA9624PW_l = 0x02;
uint8_t data_led_PCA9624PW_l = 0x03;
ret = i2c_write_to_PCA9624PW( I2C_EXAMPLE_MASTER_NUM, data_init_PCA9624PW, &data_init_PCA9624PW_l);
if (ret == ESP_OK) {
xSemaphoreTake(print_mux, portMAX_DELAY);
printf("*******************\n");
printf("TASK[%d] MASTER WRITE MODE SETTING TO SLAVE \n", task_idx);
printf("*******************\n");
printf("----TASK[%d] Master write ----\n", task_idx);
xSemaphoreGive(print_mux);
}
vTaskDelay(( DELAY_TIME_BETWEEN_ITEMS_MS * ( task_idx + 1 ) ) / portTICK_RATE_MS);
while (1) {
ret = i2c_write_to_PCA9624PW( I2C_EXAMPLE_MASTER_NUM, data_led_on_PCA9624PW, &data_led_PCA9624PW_l);
if (ret == ESP_OK) {
xSemaphoreTake(print_mux, portMAX_DELAY);
printf("*******************\n");
printf("TASK[%d] MASTER WRITE LED ON TO SLAVE\n", task_idx);
printf("*******************\n");
printf("----TASK[%d] Master write ----\n", task_idx);
xSemaphoreGive(print_mux);
}
vTaskDelay(( DELAY_TIME_BETWEEN_ITEMS_MS * ( task_idx + 1 ) ) / portTICK_RATE_MS);
ret = i2c_write_to_PCA9624PW( I2C_EXAMPLE_MASTER_NUM, data_led_off_PCA9624PW, &data_led_PCA9624PW_l);
if (ret == ESP_OK) {
xSemaphoreTake(print_mux, portMAX_DELAY);
printf("*******************\n");
printf("TASK[%d] MASTER WRITE LED OFF TO SLAVE\n", task_idx);
printf("*******************\n");
printf("----TASK[%d] Master write ----\n", task_idx);
xSemaphoreGive(print_mux);
}
vTaskDelay(( DELAY_TIME_BETWEEN_ITEMS_MS * ( task_idx + 1 ) ) / portTICK_RATE_MS);
//we need to fill the slave buffer so that master can read later
}
}
void app_main()
{
print_mux = xSemaphoreCreateMutex();
i2c_example_master_init();
xTaskCreate(i2c_test_task_PCA9624PW, "i2c_test_task_0_for_PCA9624PW", 1024 * 2, (void* ) 0, 10, NULL);
}