ESP32学习6:I2C总线

一、概述

        I2C 通讯协议(Inter-Integrated Circuit)是由 Phiilps 公司开发的,由于它引脚少,硬件实现简单,可扩展性强,不需要 USART、CAN 等通讯协议的外部收发设备,被广泛地使用在系统内多个集成电路(IC)间的通讯。

        I2C总线由数据线SDA时钟线SCL两条线构成通讯线路,即可发送数据,也可接收数据。在CPU与被控IC之间、IC与IC之间都可以进行双向传递,最高传送速率为400kbps,各种被控器件均并联在总线上,但每个器件都有唯一的地址。

ESP32学习6:I2C总线_第1张图片

1. I2C总线特点 

上图为I2C总线系统的硬件结构图,他的物理层有以下几个主要特点:

  • 支持多设备的总线。“总线”指多个设备共用的信号线,在一个 I2C 通讯总线中,可连
    接多个 I2C 通讯设备,支持多个通讯主机及多个通讯从机。
  • 一个 I2C 总线只使用两条总线线路,一条双向串行数据线(SDA) ,一条串行时钟线
    (SCL)
    。数据线即用来表示数据,时钟线用于数据收发同步。
  • 每个连接到总线的设备都有一个唯一的地址,主机利用这个地址在不同设备之间的访问。
  • 总线通过上拉电阻接到电源。当 I2C 设备空闲时,会输出高阻态,而当所有设备都空闲,
    都输出高阻态时,由上拉电阻把总线拉成高电平。多个主机同时使用总线时,为了防止
    数据冲突,会利用仲裁方式决定由哪个设备占用总线。
  • 常用的速率:标准模式传输速率为 100kbit/s ,快速模式为 400kbit/s。

 2. I2C总线通讯格式

        I2C 的协议定义了通讯的起始和停止信号、数据有效性、响应、仲裁、时钟同步和地址广
播等环节。
        I2C 通讯过程的基本结构如下:

 主机写数据到从机

ESP32学习6:I2C总线_第2张图片

 图例说明:

        图中阴影表示数据由主机传输至从机    S: 传输开始信号;

                                                                               SLAVE_ADDRESS: 从机地址;

                   空白表示数据由从机传输至主机   ​ ​​​​​​:传输方向选择,1为读,0为写;

                                                                               : 应答(ACK)或非应答(NACK)信号;

                                                                               P:停止传输信号。

 写数据的基本过程:

  • 首先,主机的I2C接口产生传输起始信号S,并广播出去。这时,连接到I2C总线上的所有从机都会接收到这个信号。
  • 主机发出起始信号之后,所有从机设备开始等待主机紧接着广播的从机地址信号SLAVE_ADDRESS。在I2C总线上,每个设备的地址都是唯一的,当主机广播的地址与某个设备地址相同时,这个设备就被选中了,没被选中的设备将会忽略之后的数据信号
  • 在地址位之后,是传输方向的选择位(R/W'),当该位为0时,表示后面的数据传输方向是由主机传输至从机,即主机向从机写数据。该位为1时,则相反,即主机由从机读数据
  • 从机在接收到匹配的地址之后,主机或从机会返回一个应答(ACK)或非应答(NACK)信号,只有接收到应答信号之后,主机才能继续发送或接收数据。
  • 在写数据时,广播完地址,接收到应答信号后,主机就开始向从机传输数据(DATA),数据报大小为8位,主机每发送完一个字节数据,都要等待从机的应答信号(ACK),重复这个过程,就可以向从机传输数据。
  • 当数据传输结束时,主机向从机传输一个停止传输信号P,表示不再传输数据

 主机由从机中读数据

ESP32学习6:I2C总线_第3张图片

       主机由从机中读数据与上述写数据过程类似,广播完地址,接收到应答信号后,从机开始向主机放回数据(DATA),数据包大小为8位,从机每发完一个数据,都会等待主机的应答信号(ACK),重复这个过程,主机就由从机读到数据。但由于是主机读数据,从机传输数据,因此当主机不希望接收数据时,会返回非应答信号(NACK),这时从机就会停止发送数据。(主动权都在主机手中)

 通讯复合格式

ESP32学习6:I2C总线_第4张图片

     除了基本的读写数据,I2C协议更长用到的是复合格式。该传输过程有两次起始信号(S)。一般在第一次传输中,主机通过SLAVE_ADDRESS寻找到从机设备后送一段“数据”,这段数据通常用于表示从机设备内部的寄存器或存储地址(SLAVE_ADDRESS表示的是该从机区别于其他从机的地址,此处注意区分),在第二次的传输中,对该地址的内容进行读或写。即,第一次通讯是告诉从机要读写的内存地址,第二次则是实际要读写的内容。

3. 数据有效性:

      I2C 使用SDA 信号线来传输数据,使用 SCL 信号线进行数据同步。SDA数据线在SCL 的每个时钟周期传输一位数据。数据传送时,SLC时钟信号为高电平时SDA表示的数据有效,即此时的SDA 为高电平时表示数据“1”,为低电平时表示数据“0”。当SCL为低电平时,SDA 的数据无效,数据线上的高电平或低电平状态才允许变化,一般在这个时候 SDA 进行电平切换,为下一次表示数据做好准备。 

ESP32学习6:I2C总线_第5张图片

 4. 通讯的起始和停止信号

上文中提到的起始(S)和停止(P)信号是两种特殊的状态, 如下图:

  • 当SCL线是高电平时SDA线从高电平向低电平切换,表示启动信号
  • 当SCL线是高电平时SDA线从低电平向高电平切换,表示停止信号
  • 起始信号和停止信号一般由主机产生

ESP32学习6:I2C总线_第6张图片

5. 地址及数据方向 

         I2C 总线上的每个设备都有自己的独立地址,主机发起通讯时,先发送启动信号,再发出寻址信号,来寻找从机。

         I2C 协议规定设备地址可以是 7 位或 10 位,实际中7 位的地址应用比较广泛。

        紧跟设备地址的一个数据位用来表示数据传输方向(R/W')。数据方向位为“1”时表示主机由从机读数据,该位为“0”时表示主机向从机写数据。

设备地址(7位)及数据传输方向

ESP32学习6:I2C总线_第7张图片

         读数据方向时,主机会释放对 SDA 信号线的控制,由从机控制 SDA 信号线,主机接
收信号,写数据方向时,SDA 由主机控制,从机接收信号。
 

6. 响应信号

        I2C协议规定,每传输一个字节数据(包含地址和命令字)后,都要有一个应答信号,以确定数据传输是否被对方接受到。应答信号由接收设备产生,包括“应答(ACK)”和“非应答(NACK)”两种信号。

        作为数据接收端时,当设备(无论主从机)接收到 I2C 传输的一个字节数据或地址后,若希望对方继续发送数据,则需要向对方发送“应答(ACK)”信号(SCL信号为高电平期间,接收设备将SDA拉为低电平),发送方会继续发送下一个数据;若接收端希望结束数据传输,则向对方发送“非应答(NACK)”信号(SCL信号为高电平期间,接收设备将SDA拉为高电平),发送方接收到该信号后会产生一个停止信号,结束信号传输。如下图:

ESP32学习6:I2C总线_第8张图片

 7. I2C通讯方式

I2C通讯一般具有两种方式,分别为软件模拟的方式或硬件 I2C 的方式。

  • 软件模拟,即直接使用 CPU 内核按照 I2C 协议的要求控制 GPIO 输出高低电平。如控制产生 I2C 的起始信号时,先控制作为 SCL 线的 GPIO 引脚输出高电平,然后控制作为 SDA 线的 GPIO 引脚在此期间完成由高电平至低电平的切换,最后再控制SCL 线切换为低电平,这样就输出了一个标准的 I2C 起始信号。后续类似。
  • 硬件,即直接利用 芯片中的硬件 I2C 外设,只要配置好对应的寄存器,外设就会产生标准串口协议的时序。

二、ESP32实现I2C通讯

        本文以OLED显示为例,介绍ESP32中,通讯协议的使用方法。

1. ESP32-I2C介绍

        上文中,介绍了两种不同的实现I2C通讯的方式,一种是通过软件模拟的方式,控制GPIO电平实现,另外一种是硬件实现方式。在ESP32中,已经带了硬件的I2C,因此只需要调用相关的API接口,就可以实现I2C通讯

I2C API接口函数如下:

  • I2C 配置函数:i2c_param_config();
函数原型 esp_err_t i2c_param_config
(
    i2c_port_t   i2c_num,
    const i2c_config_t*   i2c_conf
)
函数功能 硬件I2C配置
参数

i2c_num:I2C编号,取值:

  •     I2C_NUM_0 = 0,  /*I2C_0 */
  •     I2C_NUM_1 , /*I2C_1*/

i2c_conf:I2C参数配置

    typedef struct{
        i2c_mode_t   mode;  /*I2C 模式 */
        gpio_num_t   sda_io_num; /*SDA 引脚*/
        gpio_pullup_t   sda_pullup_en; /*SDA 上拉使能*/
        gpio_num_t   scl_io_num; /*SCL 引脚*/
        gpio_pullup_t   scl_pullup_en; /*SCL 上拉使能*/
        union {
            struct {
                uint32_t clk_speed; /*时钟速度*/
            } master;
            struct {
                uint8_t   addr_10bit_en; /*10 位地址使能*/
                uint16_t   slave_addr;  /*作为从机时地址*/
            } slave;

        };
    }i2c_config_t;

返回值 ESP_OK:成功
ESP_ERR_INVALID_ARG : 参数错误
  •  I2C 功能安装使能函数:i2c_driver_install();
函数原型 esp_err_t i2c_driver_install
(
    i2c_port_t i2c_num,
    i2c_mode_t mode,
    size_t slv_rx_buf_len,
    size_t slv_tx_buf_len,
    int intr_alloc_flags
)
函数功能 I2C功能安装使能函数
参数 [in] i2c_num:I2C 编号
[in] mode:I2C 模式
[in] slv_rx_buf_len:接收缓存大小
[in] slv_tx_buf_len:发送缓存大小
[in] intr_alloc_flags:分配中断标记
返回值 ESP_OK:成功
ESP_ERR_INVALID_ARG : 参数错误
  • 创建 I2C 连接函数:i2c_cmd_link_create();
函数原型 int  i2c_cmd_link_create()
函数功能 创建 I2C 连接函数
参数
返回值 i2c_cmd_handle_t:I2C 连接的句柄
  • 写启动信号到缓存函数:i2c_master_start(); 
函数原型 esp_err_t i2c_master_start
(
i2c_cmd_handle_t cmd_handle
)
函数功能 I2C写启动信号到缓存函数
参数  [in] cmd_handle:I2C 连接的句柄,i2c_cmd_link_create()函数的返回值
返回值 ESP_OK:成功
ESP_ERR_INVALID_ARG : 参数错误
  • 写一个字节的命令放到到缓存函数:i2c_master_write_byte();
函数原型 esp_err_t i2c_master_write_byte
(
    i2c_cmd_handle_t  cmd_handle,
    uint8_t  data,
    bool  ack_en
)
函数功能 I2C 写一个字节的命令放到缓存函数
参数

[in] cmd_handle:I2C 连接的句柄,i2c_cmd_link_create()函数的返回值

[in] data:发送的数据
[in] ack_en:是否需要等待 ack 使能

返回值 ESP_OK:成功
ESP_ERR_INVALID_ARG : 参数错误
  •  写停止信号到缓存函数:i2c_master_stop();
函数原型 esp_err_t i2c_master_stop
(
i2c_cmd_handle_t cmd_handle
)
函数功能 I2C 写停止信号到缓存函数
参数 [in] cmd_handle:I2C 连接的句柄,i2c_cmd_link_create()函数的返回值
返回值 ESP_OK:成功
ESP_ERR_INVALID_ARG : 参数错误
  •  I2C 发送函数:i2c_master_cmd_begin();
函数原型 esp_err_t i2c_master_cmd_begin
(
i2c_port_t i2c_num,
i2c_cmd_handle_t cmd_handle,
TickType_t ticks_to_wait
)
函数功能 I2C 发送函数
参数 [in] i2c_num:I2C 编号
[in] cmd_handle:I2C 连接的句柄,i2c_cmd_link_create()函数的返回值
[in] ticks_to_wait:等待时间
返回值 ESP_OK:成功
ESP_ERR_INVALID_ARG : 参数错误
ESP_FAIL:发送错误
ESP_ERR_INVALID_STATE:I2C 设备未初始化
ESP_ERR_TIMEOUT:超时
  •  删除 I2C 连接函数:i2c_cmd_link_delete();
函数原型 void i2c_cmd_link_delete
(
i2c_cmd_handle_t cmd_handle
)
函数功能 删除I2C连接函数
参数  [in] cmd_handle:I2C 连接的句柄,i2c_cmd_link_create()函数的返回值
返回值

 2.ESP32 I2C发送数据的整个流程

ESP32学习6:I2C总线_第9张图片

 3.ESP32 I2C读数据的流程

  •  读一个字节的命令放到缓存函数:i2c_master_read_byte();
函数原型  esp_err_t i2c_master_read_byte
(
i2c_cmd_handle_t cmd_handle,
uint8_t* data,
i2c_ack_type_t ack
)
函数功能 I2C 读一个字节的命令放到缓存函数
参数 [in] cmd_handle:I2C 连接的句柄,i2c_cmd_link_create()函数的返回值
[in] data:数据
[in] ack:应答的值
返回值 ESP_OK:成功
ESP_ERR_INVALID_ARG : 参数错误

ESP32学习6:I2C总线_第10张图片

 4.OLED介绍

        OLED,即有机发光二极管(Organic Light-Emitting Diode),又称为有机电激光显示(Organic Electroluminesence Display)。OLED 由于同时具备自发光,不需背光源、对比度高、厚度薄、视角广、反应速度快、可用于挠曲性面板、使用温度范围广、构造及制程较简单等优异之特性,被认为是下一代的平面显示器新兴应用技术。 

ESP32学习6:I2C总线_第11张图片

 5.软件程序编写

#ifndef COMPONENTS_I2C_I2COLED_H_
#define COMPONENTS_I2C_I2COLED_H_
/*
=============
头文件包含
=============
*/
#include 
#include "esp_system.h"
#include 
#include 
#include "freertos/task.h"
#include "driver/i2c.h"
#include "fonts.h"
/*
===========================
宏定义
===========================
*/
//I2C
#define I2C_OLED_MASTER_SCL_IO          33               /*!< gpio number for I2C master clock (SCL)*/
#define I2C_OLED_MASTER_SDA_IO          32               /*!< gpio number for I2C master data  (SDA)*/
#define I2C_OLED_MASTER_NUM             I2C_NUM_1        /*!< I2C port number for master dev */
#define WRITE_BIT                       I2C_MASTER_WRITE /*!< I2C master write 0*/
#define READ_BIT                        I2C_MASTER_READ  /*!< I2C master read 1*/
#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 */

//OLED
#define OLED_WRITE_ADDR    0x78
#define SSD1306_WIDTH  128
#define SSD1306_HEIGHT 64
#define WRITE_CMD      			 0X00
#define WRITE_DATA     			 0X40

#define TURN_OFF_CMD             0xAE                     //--turn off oled panel
#define SET1_LOW_COL_ADDR_CMD    0x00                     //---set low column address
#define SET2_HI_COL_ADDR_CMD     0x10                     //---set high column address
#define SET3_START_LINE_CMD      0x40                     //--set start line address  Set Mapping RAM Display Start Line (0x00~0x3F)
#define SET4_CONTR_REG           0x81                     //--set contrast control register
#define SET5_OUT_CURR            0xff                      // Set SEG Output Current Brightness
#define SET6_SEG_MAPPING         0xA1                     //--Set SEG/Column Mapping     0xa0���ҷ��� 0xa1����
#define SET7_SCAN_DIR            0xC8                     //Set COM/Row Scan Direction   0xc0���·��� 0xc8����
#define SET8_NORMAL_DIS          0xA6                     //--set normal display
#define SET9_RATIO               0xA8                     //--set multiplex ratio(1 to 64)
#define SET10_DUTY               0x3f                     //--1/64 duty
#define SET11_DIS_OFFSET         0xD3                     //-set display offset	Shift Mapping RAM Counter (0x00~0x3F)
#define SET12_NO_OFFSET          0x00                     //-not offset
#define SET13_CLOCK_DIV          0xd5                     //--set display clock divide ratio/oscillator frequency
#define SET14_CLOCK_FC           0x80                     //--set divide ratio, Set Clock as 100 Frames/Sec
#define SET15_PRE_CHARGE         0xD9                     //--set pre-charge period
#define SET16_PER_CHARGE_15      0xF1                     //Set Pre-Charge as 15 Clocks & Discharge as 1 Clock
#define SET17_COM_PIN_CONF       0xDA                     //--set com pins hardware configuration
#define SET18_STG1               0x12
#define SET19_vCOMH              0xDB                     //--set vcomh
#define  SET20_vCOM_D_LEVVEL     0x40                     //Set VCOM Deselect Level
#define  SET21_PAGE_ADDR_MODE    0x20                     //-Set Page Addressing Mode (0x00/0x01/0x02)
#define  SET22_STG2              0x02                     //
#define  SET23_CHARGE_PUMP       0x8D                     //--set Charge Pump enable/disable
#define  SET24_DIS_              0x14                     //--set(0x10) disable
#define  SET25_ENTIRE_DIS        0xA4                     // Disable Entire Display On (0xa4/0xa5)
#define  SET26_INV_DIS           0xA6                     // Disable Inverse Display On (0xa6/a7)
#define  TURN_ON_CMD             0xAF                     //--turn on oled panel

//显示1,擦除0
typedef enum {
	SSD1306_COLOR_BLACK = 0x00, /*!< Black color, no pixel */
	SSD1306_COLOR_WHITE = 0x01  /*!< Pixel is set. Color depends on LCD */
} SSD1306_COLOR_t;

typedef struct {
	uint16_t CurrentX;
	uint16_t CurrentY;
	uint8_t Inverted;
	uint8_t Initialized;
} SSD1306_t;



void i2c_inti(void);
void oled_init(void);
void oled_claer(void);
void oled_all_on(void);
void oled_set_pos(uint8_t x,uint8_t y);
int oled_write_cmd(uint8_t command);
int oled_write_data(uint8_t data);
void clean_oled_buff(void);
void oled_update_screen(void);
int oled_write_lang_data(uint8_t *data,uint16_t len);
void oled_drawpixel(uint16_t x, uint16_t y, SSD1306_COLOR_t color);
void oled_gotoXY(uint16_t x, uint16_t y) ;
char oled_show_char(uint16_t x, uint16_t y,char ch, FontDef_t* Font, SSD1306_COLOR_t color)  ;
char oled_show_str(uint16_t x, uint16_t y,char* str, FontDef_t* Font, SSD1306_COLOR_t color) ;

#endif /* COMPONENTS_I2C_I2COLED_H_ */
#include "I2COled.h"
//#include "oledfont.h"
#include "string.h"
#include "stdlib.h"
/*
===========================
全局变量定义
===========================
*/
//OLED缓存128*64bit
static uint8_t g_oled_buffer[SSD1306_WIDTH * SSD1306_HEIGHT / 8];
//OLED实时信息
static SSD1306_t oled;
//OLED是否正在显示,1显示,0等待
static bool is_show_str =0;
/*
===========================
函数定义
===========================
*/

/**
 * oled_i2c 初始化
 * @param[in]   NULL
 * @retval
 *              NULL
 */
void i2c_init(void)
{
    i2c_config_t conf;
    conf.mode = I2C_MODE_MASTER;
    conf.sda_io_num = I2C_OLED_MASTER_SDA_IO;  //SDA IO映射
    conf.sda_pullup_en = GPIO_PULLUP_ENABLE;
    conf.scl_io_num = I2C_OLED_MASTER_SCL_IO;  //SCL IO映射
    conf.scl_pullup_en = GPIO_PULLUP_ENABLE;
    conf.master.clk_speed = 400000;            //I2C CLK 频率
    i2c_param_config(I2C_OLED_MASTER_NUM, &conf);  //配置I2C
    i2c_driver_install(I2C_OLED_MASTER_NUM, conf.mode,0, 0, 0); //I2C功能安装使能函数
}

/**
 * 向oled写命令
 * @param[in]   command
 * @retval
 */

int oled_write_cmd(uint8_t command)
{
    int ret;
    i2c_cmd_handle_t cmd = i2c_cmd_link_create(); //创建I2C连接函数

    ret = i2c_master_start(cmd); //写启动信号到缓存函数
    ret = i2c_master_write_byte(cmd, OLED_WRITE_ADDR |WRITE_BIT , ACK_CHECK_EN); //写数据
    ret = i2c_master_write_byte(cmd, WRITE_CMD, ACK_CHECK_EN);
    ret = i2c_master_write_byte(cmd,command, ACK_CHECK_EN);
    ret = i2c_master_stop(cmd); //写停止信号
    ret = i2c_master_cmd_begin(I2C_OLED_MASTER_NUM, cmd, 100 / portTICK_RATE_MS); //I2C发送函数
    i2c_cmd_link_delete(cmd); //删除I2C链接
    if (ret != ESP_OK)
    {
        return ret;
    }
    return ret;
}

/**
 * 向oled写数据
 * @param[in]   data
 * @retval
 *              - ESP_OK
 * @par         修改日志
 *               Ver0.0.1:
                     XinC_Guo, 2018/07/18, 初始化版本\n
 */
int oled_write_data(uint8_t data)
{
    int ret;
    i2c_cmd_handle_t cmd = i2c_cmd_link_create();
    ret = i2c_master_start(cmd);
    ret = i2c_master_write_byte(cmd, OLED_WRITE_ADDR | WRITE_BIT, ACK_CHECK_EN);
    ret = i2c_master_write_byte(cmd, WRITE_DATA, ACK_CHECK_EN);
    ret = i2c_master_write_byte(cmd, data, ACK_CHECK_EN);
    ret = i2c_master_stop(cmd);
    ret = i2c_master_cmd_begin(I2C_OLED_MASTER_NUM, cmd, 100 / portTICK_RATE_MS);
    i2c_cmd_link_delete(cmd);
    if (ret != ESP_OK)
    {
        return ret;
    }
    return ret;
}
/**
 * 向oled写长数据
 * @param[in]   data   要写入的数据
 * @param[in]   len     数据长度
 * @retval
 *              - ESP_OK
 * @par         修改日志
 *               Ver0.0.1:
                     XinC_Guo, 2018/07/18, 初始化版本\n
 */
int oled_write_long_data(uint8_t *data,uint16_t len)
{
    //注释参考sht30之i2c教程
    int ret;
    i2c_cmd_handle_t cmd = i2c_cmd_link_create();
    ret = i2c_master_start(cmd);
    ret = i2c_master_write_byte(cmd, OLED_WRITE_ADDR | WRITE_BIT, ACK_CHECK_EN);
    ret = i2c_master_write_byte(cmd, WRITE_DATA, ACK_CHECK_EN);
    ret = i2c_master_write(cmd, data, len,ACK_CHECK_EN);
    ret = i2c_master_stop(cmd);
    ret = i2c_master_cmd_begin(I2C_OLED_MASTER_NUM, cmd, 10000 / portTICK_RATE_MS);
    i2c_cmd_link_delete(cmd);
    if (ret != ESP_OK)
    {
        return ret;
    }
    return ret;
}

/**
 * 初始化 oled
 * @param[in]   NULL
 * @retval
 *              NULL
 * @par         修改日志
 *               Ver0.0.1:
                     XinC_Guo, 2018/07/18, 初始化版本\n
 */
void oled_init(void)
{
    //i2c初始化
    i2c_init();
    //oled配置
    oled_write_cmd(TURN_OFF_CMD);
    oled_write_cmd(0xAE);//关显示
    oled_write_cmd(0X20);//低列地址
    oled_write_cmd(0X10);//高列地址
    oled_write_cmd(0XB0);//
    oled_write_cmd(0XC8);
    oled_write_cmd(0X00);
    oled_write_cmd(0X10);
     //设置行显示的开始地址(0-63)
    //40-47: (01xxxxx)
    oled_write_cmd(0X40);
     //设置对比度
    oled_write_cmd(0X81);
    oled_write_cmd(0XFF);//这个值越大,屏幕越亮(和上条指令一起使用)(0x00-0xff)

    oled_write_cmd(0XA1);//0xA1: 左右反置,  0xA0: 正常显示(默认0xA0)
   //0xA6: 表示正常显示(在面板上1表示点亮,0表示不亮)
    //0xA7: 表示逆显示(在面板上0表示点亮,1表示不亮)
    oled_write_cmd(0XA6);

    oled_write_cmd(0XA8);//设置多路复用率(1-64)
    oled_write_cmd(0X3F);//(0x01-0x3f)(默认为3f)
    oled_write_cmd(0XA4);
    //设置显示抵消移位映射内存计数器
    oled_write_cmd(0XD3);
    oled_write_cmd(0X00);
    //设置显示时钟分频因子/振荡器频率
    oled_write_cmd(0XD5);
    //低4位定义显示时钟(屏幕的刷新时间)(默认:0000)分频因子= [3:0]+1
    //高4位定义振荡器频率(默认:1000)
    oled_write_cmd(0XF0);
    //时钟预充电周期
    oled_write_cmd(0XD9);
    oled_write_cmd(0X22);
    //设置COM硬件应脚配置
    oled_write_cmd(0XDA);
    oled_write_cmd(0X12);
    oled_write_cmd(0XDB);
    oled_write_cmd(0X20);
    //电荷泵设置(初始化时必须打开,否则看不到显示)
    oled_write_cmd(0X8D);
    oled_write_cmd(0X14);
    //开显示
    oled_write_cmd(0XAF);
    //清屏
    oled_claer();
}

/**
 * 将显存内容刷新到oled显示区
 * @param[in]   NULL
 * @retval
 *              NULL
 * @par         修改日志
 *               Ver0.0.1:
                     XinC_Guo, 2018/07/18, 初始化版本\n
 */
void oled_update_screen(void)
{
    uint8_t line_index;
    for(line_index=0    ;   line_index<8   ;  line_index++)
    {
        oled_write_cmd(0xb0+line_index);
        oled_write_cmd(0x00);
        oled_write_cmd(0x10);

        oled_write_long_data(&g_oled_buffer[SSD1306_WIDTH * line_index],SSD1306_WIDTH);
    }
}

/**
 * 清屏
 * @param[in]   NULL
 * @retval
 *              NULL
 * @par         修改日志
 *               Ver0.0.1:
                     XinC_Guo, 2018/07/18, 初始化版本\n
 */
void oled_claer(void)
{
    //清0缓存
    memset(g_oled_buffer,SSD1306_COLOR_BLACK,sizeof(g_oled_buffer));
    oled_update_screen();
}
/**
 * 填屏
 * @param[in]   NULL
 * @retval
 *              NULL
 * @par         修改日志
 *               Ver0.0.1:
                     XinC_Guo, 2018/07/18, 初始化版本\n
 */
void oled_all_on(void)
{
    //置ff缓存
    memset(g_oled_buffer,0xff,sizeof(g_oled_buffer));
    oled_update_screen();
}
/**
 * 移动坐标
 * @param[in]   x   显示区坐标 x
 * @param[in]   y   显示去坐标 y
 * @retval
 *              其它
 * @par         修改日志
 *               Ver0.0.1:
                     XinC_Guo, 2018/07/18, 初始化版本\n
 */
void oled_gotoXY(uint16_t x, uint16_t y)
{
	oled.CurrentX = x;
	oled.CurrentY = y;
}
/**
 * 向显存写入
 * @param[in]   x   坐标
 * @param[in]   y   坐标
 * @param[in]   color   色值0/1
 * @retval
 */
void oled_drawpixel(uint16_t x, uint16_t y, SSD1306_COLOR_t color)
{
	if (
		x >= SSD1306_WIDTH ||
		y >= SSD1306_HEIGHT
	)
    {
		return;
	}
	if (color == SSD1306_COLOR_WHITE)
	{
		g_oled_buffer[x + (y / 8) * SSD1306_WIDTH] |= 1 << (y % 8);
	}
    else
    {
		g_oled_buffer[x + (y / 8) * SSD1306_WIDTH] &= ~(1 << (y % 8));
	}
}
/**
 * 在x,y位置显示字符
 * @param[in]   x    显示坐标x
 * @param[in]   y    显示坐标y
 * @param[in]   ch   要显示的字符
 * @param[in]   font 显示的字形
 * @param[in]   color 颜色  1显示 0不显示
 * @retval
 */
char oled_show_char(uint16_t x,uint16_t y,char ch, FontDef_t* Font, SSD1306_COLOR_t color)
{
	uint32_t i, b, j;
	if ( SSD1306_WIDTH <= (oled.CurrentX + Font->FontWidth) || SSD1306_HEIGHT <= (oled.CurrentY + Font->FontHeight) )
    {
		return 0;
	}
	if(0 == is_show_str)
    {
        oled_gotoXY(x,y);
    }

	for (i = 0; i < Font->FontHeight; i++)
    {
		b = Font->data[(ch - 32) * Font->FontHeight + i];
		for (j = 0; j < Font->FontWidth; j++)
        {
			if ((b << j) & 0x8000)
            {
				oled_drawpixel(oled.CurrentX + j, (oled.CurrentY + i), (SSD1306_COLOR_t) color);
			}
            else
            {
				oled_drawpixel(oled.CurrentX + j, (oled.CurrentY + i), (SSD1306_COLOR_t)!color);
			}
		}
	}
	oled.CurrentX += Font->FontWidth;
	if(0 == is_show_str)
    {
       oled_update_screen();
    }
	return ch;
}
/**
 * 在x,y位置显示字符串
 * @param[in]   x    显示坐标x
 * @param[in]   y    显示坐标y
 * @param[in]   str   要显示的字符串
 * @param[in]   font 显示的字形
 * @param[in]   color 颜色  1显示 0不显示
 * @retval
 */
char oled_show_str(uint16_t x,uint16_t y, char* str, FontDef_t* Font, SSD1306_COLOR_t color)
{
    is_show_str=1;
    oled_gotoXY(x,y);
	while (*str)
    {
		if (oled_show_char(x,y,*str, Font, color) != *str)
        {
            is_show_str=0;
			return *str;
		}
		str++;
	}
    is_show_str=0;
    oled_update_screen();
	return *str;
}

三、结束

        本文主要介绍了I2C通讯协议的实现原理,以及ESP32硬件I2C的API函数。

你可能感兴趣的:(ESP32,嵌入式硬件,单片机,网络协议)