ESP32学习6:I2C总线
一、概述I2C 通讯协议(Inter-Integrated Circuit)是由 Phiilps 公司开发的,由于它引脚少,硬件实现简单,可扩展性强,不需要 USART、CAN 等通讯协议的外部收发设备,被广泛地使用在系统内多个集成电路(IC)间的通讯。I2C总线由数据线SDA和时钟线SCL两条线构成通讯线路,即可发送数据,也可接收数据。在CPU与被控IC之间、IC与IC之间都可以进行双向传递,最
一、概述
I2C 通讯协议(Inter-Integrated Circuit)是由 Phiilps 公司开发的,由于它引脚少,硬件实现简单,可扩展性强,不需要 USART、CAN 等通讯协议的外部收发设备,被广泛地使用在系统内多个集成电路(IC)间的通讯。
I2C总线由数据线SDA和时钟线SCL两条线构成通讯线路,即可发送数据,也可接收数据。在CPU与被控IC之间、IC与IC之间都可以进行双向传递,最高传送速率为400kbps,各种被控器件均并联在总线上,但每个器件都有唯一的地址。
1. I2C总线特点
上图为I2C总线系统的硬件结构图,他的物理层有以下几个主要特点:
- 支持多设备的总线。“总线”指多个设备共用的信号线,在一个 I2C 通讯总线中,可连
接多个 I2C 通讯设备,支持多个通讯主机及多个通讯从机。 - 一个 I2C 总线只使用两条总线线路,一条双向串行数据线(SDA) ,一条串行时钟线
(SCL)。数据线即用来表示数据,时钟线用于数据收发同步。 - 每个连接到总线的设备都有一个唯一的地址,主机利用这个地址在不同设备之间的访问。
- 总线通过上拉电阻接到电源。当 I2C 设备空闲时,会输出高阻态,而当所有设备都空闲,
都输出高阻态时,由上拉电阻把总线拉成高电平。多个主机同时使用总线时,为了防止
数据冲突,会利用仲裁方式决定由哪个设备占用总线。 - 常用的速率:标准模式传输速率为 100kbit/s ,快速模式为 400kbit/s。
2. I2C总线通讯格式
I2C 的协议定义了通讯的起始和停止信号、数据有效性、响应、仲裁、时钟同步和地址广
播等环节。
I2C 通讯过程的基本结构如下:
主机写数据到从机
图例说明:
图中,阴影表示数据由主机传输至从机 S: 传输开始信号;
SLAVE_ADDRESS: 从机地址;
空白表示数据由从机传输至主机 :传输方向选择,1为读,0为写;
: 应答(ACK)或非应答(NACK)信号;
P:停止传输信号。
写数据的基本过程:
- 首先,主机的I2C接口产生传输起始信号S,并广播出去。这时,连接到I2C总线上的所有从机都会接收到这个信号。
- 主机发出起始信号之后,所有从机设备开始等待主机紧接着广播的从机地址信号SLAVE_ADDRESS。在I2C总线上,每个设备的地址都是唯一的,当主机广播的地址与某个设备地址相同时,这个设备就被选中了,没被选中的设备将会忽略之后的数据信号。
- 在地址位之后,是传输方向的选择位(R/W'),当该位为0时,表示后面的数据传输方向是由主机传输至从机,即主机向从机写数据。该位为1时,则相反,即主机由从机读数据。
- 从机在接收到匹配的地址之后,主机或从机会返回一个应答(ACK)或非应答(NACK)信号,只有接收到应答信号之后,主机才能继续发送或接收数据。
- 在写数据时,广播完地址,接收到应答信号后,主机就开始向从机传输数据(DATA),数据报大小为8位,主机每发送完一个字节数据,都要等待从机的应答信号(ACK),重复这个过程,就可以向从机传输数据。
- 当数据传输结束时,主机向从机传输一个停止传输信号P,表示不再传输数据。
主机由从机中读数据
主机由从机中读数据与上述写数据过程类似,广播完地址,接收到应答信号后,从机开始向主机放回数据(DATA),数据包大小为8位,从机每发完一个数据,都会等待主机的应答信号(ACK),重复这个过程,主机就由从机读到数据。但由于是主机读数据,从机传输数据,因此当主机不希望接收数据时,会返回非应答信号(NACK),这时从机就会停止发送数据。(主动权都在主机手中)
通讯复合格式
除了基本的读写数据,I2C协议更长用到的是复合格式。该传输过程有两次起始信号(S)。一般在第一次传输中,主机通过SLAVE_ADDRESS寻找到从机设备后,发送一段“数据”,这段数据通常用于表示从机设备内部的寄存器或存储地址(SLAVE_ADDRESS表示的是该从机区别于其他从机的地址,此处注意区分),在第二次的传输中,对该地址的内容进行读或写。即,第一次通讯是告诉从机要读写的内存地址,第二次则是实际要读写的内容。
3. 数据有效性:
I2C 使用SDA 信号线来传输数据,使用 SCL 信号线进行数据同步。SDA数据线在SCL 的每个时钟周期传输一位数据。数据传送时,SLC时钟信号为高电平时SDA表示的数据有效,即此时的SDA 为高电平时表示数据“1”,为低电平时表示数据“0”。当SCL为低电平时,SDA 的数据无效,数据线上的高电平或低电平状态才允许变化,一般在这个时候 SDA 进行电平切换,为下一次表示数据做好准备。
4. 通讯的起始和停止信号
上文中提到的起始(S)和停止(P)信号是两种特殊的状态, 如下图:
- 当SCL线是高电平时SDA线从高电平向低电平切换,表示启动信号
- 当SCL线是高电平时SDA线从低电平向高电平切换,表示停止信号
- 起始信号和停止信号一般由主机产生
5. 地址及数据方向
I2C 总线上的每个设备都有自己的独立地址,主机发起通讯时,先发送启动信号,再发出寻址信号,来寻找从机。
I2C 协议规定设备地址可以是 7 位或 10 位,实际中7 位的地址应用比较广泛。
紧跟设备地址的一个数据位用来表示数据传输方向(R/W')。数据方向位为“1”时表示主机由从机读数据,该位为“0”时表示主机向从机写数据。
设备地址(7位)及数据传输方向
读数据方向时,主机会释放对 SDA 信号线的控制,由从机控制 SDA 信号线,主机接
收信号,写数据方向时,SDA 由主机控制,从机接收信号。
6. 响应信号
I2C协议规定,每传输一个字节数据(包含地址和命令字)后,都要有一个应答信号,以确定数据传输是否被对方接受到。应答信号由接收设备产生,包括“应答(ACK)”和“非应答(NACK)”两种信号。
作为数据接收端时,当设备(无论主从机)接收到 I2C 传输的一个字节数据或地址后,若希望对方继续发送数据,则需要向对方发送“应答(ACK)”信号(SCL信号为高电平期间,接收设备将SDA拉为低电平),发送方会继续发送下一个数据;若接收端希望结束数据传输,则向对方发送“非应答(NACK)”信号(SCL信号为高电平期间,接收设备将SDA拉为高电平),发送方接收到该信号后会产生一个停止信号,结束信号传输。如下图:
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_conf:I2C参数配置 typedef struct{ }; |
返回值 | 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:发送的数据 |
返回值 | 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发送数据的整个流程
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 : 参数错误 |
4.OLED介绍
OLED,即有机发光二极管(Organic Light-Emitting Diode),又称为有机电激光显示(Organic Electroluminesence Display)。OLED 由于同时具备自发光,不需背光源、对比度高、厚度薄、视角广、反应速度快、可用于挠曲性面板、使用温度范围广、构造及制程较简单等优异之特性,被认为是下一代的平面显示器新兴应用技术。
5.软件程序编写
#ifndef COMPONENTS_I2C_I2COLED_H_
#define COMPONENTS_I2C_I2COLED_H_
/*
=============
头文件包含
=============
*/
#include <stdio.h>
#include "esp_system.h"
#include <esp_log.h>
#include <freertos/FreeRTOS.h>
#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函数。
开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!
更多推荐
所有评论(0)