esp32开发快速入门 5 : I2C(驱动SSD1306)

对于学嵌入式的来说,i2c在熟悉不过了,两根线数据线(SDA)和时钟线(SCL)在加上一根地线就可以完成通信。

i2c 使用中涉及到的一些概念:起始位start、 从机地址slave_addr、 数据 data ,校验ACK NOACK、 停止位stop。具体硬件上怎么实现的我们暂且不管,我们只需记住这些概念就行了。

I2C通信规则

我现在简要说一下他的通信规则:i2c分成主机master 和从机slave 两种设备。每次都是主机发起通信请求的。

  1. master 发送一个start 信号,在总线上所有的设备都可以接收到这个start信号。

  2. 紧接着master 发送slave的地址addr+w/r(w表示下一次发送的数据是主机向设备写数据,r表示下一次发送的是主机从从机中读取数据),这时只有对应地址adrr的设备才能回复信号,这个slave回复一个ack,表示通信成功。

  3. 这回主机就可以发送数据了。

  4. 当通信完成后,从机返回一个noack,表示通信结束,主机发送一个stop信号,释放总线。

以上就是i2c最简单的通信过程。

esp32中I2C的使用

“工欲善其事,必先利其器”

对于i2c接口来说乐鑫为开发者提供了i2c调试工具i2c-tools,它的功能和linux上的i2c-tools一样,对于基于esp32来调试i2c设备很方便。 这个工具是以例程方式提供给我们的,我们暂时不用管他是怎么实现,只需要知道它的是使用方法即可。

cd ~/esp/esp-idf/examples/peripherals/i2c

i2c文件夹中有两个工程:

i2c_self_test

i2c_tools

进入i2c_tools

$ make menuconfig //配置串口 $make -j4 // 编译 $make flash moitor// 调试

成功运行之后我们看到会

==============================================================
 |       Steps to Use i2c-tools on ESP32                      |
 |                                                            |
 |  1. Try 'help', check all supported commands               |
 |  2. Try 'i2cconfig' to configure your I2C bus              |
 |  3. Try 'i2cdetect' to scan devices on the bus             |
 |  4. Try 'i2cget' to get the content of specific register   |
 |  5. Try 'i2cset' to set the value of specific register     |
 |  6. Try 'i2cdump' to dump all the register (Experiment)    |
 |                                                            |
 ==============================================================

用过linux的朋友都很熟悉这几个命令,这个linux的i2c-tools 一样,这个i2c-tools默认使用gpio18 和gpio19.可以使用i2cconfig 修改i2c配置。

我在gpio18 和gpio19 上接了一个oled ssd1603,ssd1603默认地址是0x3c,执行i2cdetcet命令

esp32> i2cdetect
     0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f
00: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
30: -- -- -- -- -- -- -- -- -- -- -- -- 3c -- -- -- 
40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
70: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --

在i2c总线成功读到设备,如果想知道其他命令的用法可以执行help 命令查看。

i2c 的配置过程

  1. 首先配置i2c_config_t 结构体,这个结构体包含了i2c的一些参数,以下内容可以在i2c.h 中找到。

/**
 * @brief I2C initialization parameters
 */
typedef struct{
    i2c_mode_t mode;       /*!< I2C mode */
    gpio_num_t sda_io_num;        /*!< GPIO number for I2C sda signal */
    gpio_pullup_t sda_pullup_en;  /*!< Internal GPIO pull mode for I2C sda signal*/
    gpio_num_t scl_io_num;        /*!< GPIO number for I2C scl signal */
    gpio_pullup_t scl_pullup_en;  /*!< Internal GPIO pull mode for I2C scl signal*/

    union {
        struct {
            uint32_t clk_speed;     /*!< I2C clock frequency for master mode, (no higher than 1MHz for now) */
        } master;
        struct {
            uint8_t addr_10bit_en;  /*!< I2C 10bit address mode enable for slave mode */
            uint16_t slave_addr;    /*!< I2C address for slave mode */
        } slave;

    };
}i2c_config_t;

具体的配置如下:

#define SDA_PIN  GPIO_NUM_18
#define SCL_PIN  GPIO_NUM_19

i2c_config_t  i2c_config = {
            .mode = I2C_MODE_MASTER,//主机模式
            .sda_io_num = SDA_PIN,//sda i引脚编号
            .scl_io_num = SCL_PIN,//scl 引脚编号
            .sda_pullup_en = GPIO_PULLUP_ENABLE,//上拉使能
            .scl_pullup_en = GPIO_PULLUP_ENABLE,//上拉使能
            .master.clk_speed = 1000000 // 100k
    };

    i2c_param_config(I2C_NUM_0, &i2c_config);//配置参数初始化,此函数内部就是将i2c_config 中的相关参数 填入到i2c[i2c_num_0] 结构体中。
    i2c_driver_install(I2C_NUM_0, I2C_MODE_MASTER, 0, 0, 0);//初始化配置以外的所有相关参数,将配置写入寄存器
  1. 初始化完成之后,就开始使用esp-idf提供的函数进行i2c的读写操作了。

根据官方例程讲解esp-idf 读写流程 ,下面的函数是我从官方例成中提取出的发送函数。

/**
 * @brief test code to operate on BH1750 sensor
 *
 * 1. set operation mode(e.g One time L-resolution 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_master_sensor_test(i2c_port_t i2c_num, uint8_t *data_h, uint8_t *data_l)
{
    int ret;
    i2c_cmd_handle_t cmd = i2c_cmd_link_create(); // 在执行i2c之前,必须执行此函数 创建一个i2c 命令 链接,为之后的i2c操作执行,在执行完成之后需要销毁

    i2c_master_start(cmd);//i2c运行开始函数。注意这不是真的开始,只是是一个开始标记,初始化cmd 
    //以下是i2c真正的读写操作
    i2c_master_write_byte(cmd, BH1750_SENSOR_ADDR << 1 | WRITE_BIT, 0x01);// 向BH1750_SENSOR_ADDR 写入操作,等待从机返回数据
    i2c_master_write_byte(cmd, BH1750_CMD_START, 0x01); // 继续写入数据
    i2c_master_stop(cmd);//i2c停止运行。并不是真正的停止,因为此时i2c还没有真正的运行,我认为这是一个标识,当时i2c运行的时候读取到此标志就停止运行。
    ret = i2c_master_cmd_begin(i2c_num, cmd, 1000 / portTICK_RATE_MS); //按照cmd中的记录的操作顺序开始运行i2c (start-> write BH1750 地址 -> write BH1750_CMD  -> stop)
    i2c_cmd_link_delete(cmd); // 操作完成 删除cmd
    if (ret != ESP_OK) {
        return ret;
    }
    vTaskDelay(30 / portTICK_RATE_MS);//freeRTOS的系统延时函数
    cmd = i2c_cmd_link_create(); //重新创建cmd 链接
    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);
    return ret;
}

esp32 i2c 驱动 ssd1603

关于ssd1306的工作原理就不做介绍了,只需要记住ssd1306的默认地址是0x3c。

跟据上面介绍的esp-idf i2c 的操作方式实现对ssd1306初始化 1. ssd1306 初始化函数

void ssd1306_init() {
            esp_err_t esp_err;

            i2c_cmd_handle_t cmd = i2c_cmd_link_create();//必须创建

            i2c_master_start(cmd);
            i2c_master_write_byte(cmd, (OLED_I2C_ADDRESS << 1) | I2C_MASTER_WRITE, true);
            i2c_master_write_byte(cmd, 0x00, true);

            i2c_master_write_byte(cmd, 0x8d, true);
            i2c_master_write_byte(cmd, 0x14, true);

            i2c_master_write_byte(cmd, 0xa1, true); // reverse left-right mapping
            i2c_master_write_byte(cmd, 0xc8, true); // reverse up-bottom mapping

            i2c_master_write_byte(cmd, 0xaf, true);
            i2c_master_stop(cmd);

            esp_err = i2c_master_cmd_begin(I2C_NUM_0, cmd, 10/portTICK_PERIOD_MS);
            if (esp_err == ESP_OK) {
                ESP_LOGI(tag, "configured successfully");
            } else {
                ESP_LOGE(tag, " configuration failed. code: 0x%.2X", espRc);
            }
            i2c_cmd_link_delete(cmd);
        }
  1. ssd1306 清屏函数

void ssd1306_clear() {
    i2c_cmd_handle_t cmd;
    uint8_t zero[128];
    for (uint8_t i = 0; i < 8; i++) {
        cmd = i2c_cmd_link_create();
        i2c_master_start(cmd);
        i2c_master_write_byte(cmd, (OLED_I2C_ADDRESS << 1) | I2C_MASTER_WRITE, true);
        i2c_master_write_byte(cmd, OLED_CONTROL_BYTE_CMD_SINGLE, true);
        i2c_master_write_byte(cmd, 0xB0 | i, true);

        i2c_master_write_byte(cmd, OLED_CONTROL_BYTE_DATA_STREAM, true);
        i2c_master_write(cmd, zero, 128, true);
        i2c_master_stop(cmd);
        i2c_master_cmd_begin(I2C_NUM_0, cmd, 10/portTICK_PERIOD_MS);
        i2c_cmd_link_delete(cmd);
    }
}

其他函数就不多写了,按照这个写法很容易将其他平台的ssd1306代码移植到esp32上。

欢迎关注我的个人网站zwww.zcxbb.com

知乎专栏:物联网开发入门 - 知乎 (zhihu.com)

  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值