一、什么是I2C?
I2C总线,全称Inter-Integrated Circuit(互连集成电路),是一种由Philips(现NXP半导体)公司在1980年代初开发的同步 串行 半双工通信总线。
二、有了串口通信为什么要使用I2C?
串口通信对比I2C有一些缺点
-
串口通信通常需要至少三条线(TX、RX和GND),而 I2C 总线仅需要两条信号线(SDA和SCL);
-
串口通信仅支持一对一通信,而 I2C 总线支持多机通信,允许单个主机与多个从机设备 进行通信;
-
串口通信通常无应答机制,而 I2C 必须有应答机制;
-
串口通讯一般是异步通信,而 I2C 使用同步传输方式,数据在时钟信号(SCL)的控制下传输。
三、I2C的工作原理以及特点
四、IIC总线时序
4.1 起始信号
代码书写方式如下:
//开始信号
void oled_i2c_start(void)
{
OLED_SCL_SET();
OLED_SDA_SET();
OLED_SDA_RESET();
OLED_SCL_RESET();
}
4.2 停止信号
//停止信号
void oled_i2c_stop(void)
{
OLED_SCL_SET();
OLED_SDA_RESET();
OLED_SDA_SET();
}
4.3 应答信号
//应答信号
void oled_i2c_ack(void)
{
OLED_SCL_SET();
OLED_SCL_RESET();
}
4.4 读写时序
一般都是发送,很少接收,这里只展现发送数据
//写一个字节信号
void oled_i2c_write_byte(uint8_t data)
{
uint8_t i,tmp;
tmp = data;
for(i=0;i<8;i++)
{
if((tmp & 0x80) == 0x80)//取出最高位,判断最高位是不是1
OLED_SDA_SET();//如果最高位是1,则sda给一个高电平
else
OLED_SDA_RESET();//如果最高位是0,则sda给一个低电平
tmp = tmp << 1;
OLED_SCL_SET();
OLED_SCL_RESET();
}
}
//写一个字节信号
void oled_i2c_write_byte(uint8_t data)
{
uint8_t i,tmp;
tmp = data;
for(i=0;i<8;i++)
{
if((tmp & 0x80) == 0x80)//取出最高位,判断最高位是不是1
OLED_SDA_SET();//如果最高位是1,则sda给一个高电平
else
OLED_SDA_RESET();//如果最高位是0,则sda给一个低电平
tmp = tmp << 1;
OLED_SCL_SET();
OLED_SCL_RESET();
}
}
4.5 一次完整的I2C时序
五、OLED实验目的
驱动 OLED 屏幕,显示点、线、字符、字符串、汉字、图片等内容。
六、项目实现-OLED通讯协议
复制项目文件19-串口打印功能
重命名为47-OLED实验
打开项目文件
加载文件
代码书写顺序:
oled.c
cpp
#include "oled.h"
//初始化oled的gpio
void oled_gpio_init(void)
{
GPIO_InitTypeDef gpio_initstruct;
OLED_I2C_SCL_CLK();
OLED_I2C_SDA_CLK();
gpio_initstruct.Mode = GPIO_MODE_OUTPUT_PP;
gpio_initstruct.Pin = OLED_I2C_SCL_PIN;
gpio_initstruct.Pull = GPIO_PULLUP;
gpio_initstruct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(OLED_I2C_SCL_PORT,&gpio_initstruct);
gpio_initstruct.Pin = OLED_I2C_SDA_PIN;
HAL_GPIO_Init(OLED_I2C_SDA_PORT,&gpio_initstruct);
}
/*IIC协议的分装*/
//开始信号
void oled_i2c_start(void)
{
OLED_SCL_SET();
OLED_SDA_SET();
OLED_SDA_RESET();
OLED_SCL_RESET();
}
//停止信号
void oled_i2c_stop(void)
{
OLED_SCL_SET();
OLED_SDA_RESET();
OLED_SDA_SET();
}
//应答信号
void oled_i2c_ack(void)
{
OLED_SCL_SET();
OLED_SCL_RESET();
}
//写一个字节信号
void oled_i2c_write_byte(uint8_t data)
{
uint8_t i,tmp;
tmp = data;
for(i=0;i<8;i++)
{
if((tmp & 0x80) == 0x80)//取出最高位,判断最高位是不是1
OLED_SDA_SET();//如果最高位是1,则sda给一个高电平
else
OLED_SDA_RESET();//如果最高位是0,则sda给一个低电平
tmp = tmp << 1;
OLED_SCL_SET();
OLED_SCL_RESET();
}
}
//发送一个命令信号
void oled_write_cmd(uint8_t cmd)
{
oled_i2c_start();
oled_i2c_write_byte(0x78);
oled_i2c_ack();
oled_i2c_write_byte(0x00);
oled_i2c_ack();
oled_i2c_write_byte(cmd);
oled_i2c_ack();
oled_i2c_stop();
}
//发送一个数据信号
void oled_write_data(uint8_t data)
{
oled_i2c_start();
oled_i2c_write_byte(0x78);
oled_i2c_ack();
oled_i2c_write_byte(0x40);
oled_i2c_ack();
oled_i2c_write_byte(data);
oled_i2c_ack();
oled_i2c_stop();
}
//初始化OLED函数
void oled_init(void)
{
oled_gpio_init();
}
oled.h
cpp
#ifndef __OLED_H__
#define __OLED_H__
#include "sys.h"
/*SCL引脚*/
#define OLED_I2C_SCL_CLK() __HAL_RCC_GPIOB_CLK_ENABLE()
#define OLED_I2C_SCL_PORT GPIOB
#define OLED_I2C_SCL_PIN GPIO_PIN_6
/*SDA引脚*/
#define OLED_I2C_SDA_CLK() __HAL_RCC_GPIOB_CLK_ENABLE()
#define OLED_I2C_SDA_PORT GPIOB
#define OLED_I2C_SDA_PIN GPIO_PIN_7
/*SCL引脚拉高拉低*/
#define OLED_SCL_SET() HAL_GPIO_WritePin(OLED_I2C_SCL_PORT,OLED_I2C_SCL_PIN,GPIO_PIN_SET)
#define OLED_SCL_RESET() HAL_GPIO_WritePin(OLED_I2C_SCL_PORT,OLED_I2C_SCL_PIN,GPIO_PIN_RESET)
/*SDA引脚拉高拉低*/
#define OLED_SDA_SET() HAL_GPIO_WritePin(OLED_I2C_SCL_PORT,OLED_I2C_SDA_PIN,GPIO_PIN_SET)
#define OLED_SDA_RESET() HAL_GPIO_WritePin(OLED_I2C_SCL_PORT,OLED_I2C_SDA_PIN,GPIO_PIN_RESET)
#endif
七、项目实现-OLED实现点、线、字符
mian.c
cpp
#include "sys.h"
#include "delay.h"
#include "led.h"
#include "uart1.h"
#include "oled.h"
extern const unsigned char shuai_data[];
int main(void)
{
HAL_Init(); /* 初始化HAL库 */
stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */
led_init();//初始化led灯
uart1_init(115200);
oled_init();
printf("hello world!\r\n");
oled_fill(0);//清屏函数
//字母A-page0
oled_set_cursor(0, 0);
oled_write_data(0x00);
oled_write_data(0x00);
oled_write_data(0xC0);
oled_write_data(0x38);
oled_write_data(0xE0);
oled_write_data(0x00);
oled_write_data(0x00);
oled_write_data(0x00);
//page1
oled_set_cursor(0, 1);
oled_write_data(0x20);
oled_write_data(0x3C);
oled_write_data(0x23);
oled_write_data(0x02);
oled_write_data(0x02);
oled_write_data(0x27);
oled_write_data(0x38);
oled_write_data(0x20);
while(1)
{
}
}
oled.c
cpp
#include "oled.h"
#include "delay.h"
//初始化oled的gpio
void oled_gpio_init(void)
{
GPIO_InitTypeDef gpio_initstruct;
OLED_I2C_SCL_CLK();
OLED_I2C_SDA_CLK();
gpio_initstruct.Mode = GPIO_MODE_OUTPUT_PP;
gpio_initstruct.Pin = OLED_I2C_SCL_PIN;
gpio_initstruct.Pull = GPIO_PULLUP;
gpio_initstruct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(OLED_I2C_SCL_PORT,&gpio_initstruct);
gpio_initstruct.Pin = OLED_I2C_SDA_PIN;
HAL_GPIO_Init(OLED_I2C_SDA_PORT,&gpio_initstruct);
}
/*IIC协议的分装*/
//开始信号
void oled_i2c_start(void)
{
OLED_SCL_SET();
OLED_SDA_SET();
OLED_SDA_RESET();
OLED_SCL_RESET();
}
//结束信号
void oled_i2c_stop(void)
{
OLED_SCL_SET();
OLED_SDA_RESET();
OLED_SDA_SET();
}
//应答信号
void oled_i2c_ack(void)
{
OLED_SCL_SET();
OLED_SCL_RESET();
}
//写一个字节信号
void oled_i2c_write_byte(uint8_t data)
{
uint8_t i,tmp;
tmp = data;
for(i=0;i<8;i++)
{
if((tmp & 0x80) == 0x80)//取出最高位,判断最高位是不是1
OLED_SDA_SET();//如果最高位是1,则sda给一个高电平
else
OLED_SDA_RESET();//如果最高位是0,则sda给一个低电平
tmp = tmp << 1;
OLED_SCL_SET();
OLED_SCL_RESET();
}
}
//发送一个命令信号
void oled_write_cmd(uint8_t cmd)
{
oled_i2c_start();
oled_i2c_write_byte(0x78);
oled_i2c_ack();
oled_i2c_write_byte(0x00);
oled_i2c_ack();
oled_i2c_write_byte(cmd);
oled_i2c_ack();
oled_i2c_stop();
}
//发送一个数据信号
void oled_write_data(uint8_t data)
{
oled_i2c_start();
oled_i2c_write_byte(0x78);
oled_i2c_ack();
oled_i2c_write_byte(0x40);
oled_i2c_ack();
oled_i2c_write_byte(data);
oled_i2c_ack();
oled_i2c_stop();
}
//初始化OLED函数
void oled_init(void)
{
oled_gpio_init();
delay_ms(100);
oled_write_cmd(0xAE); //设置显示开启/关闭,0xAE关闭,0xAF开启
oled_write_cmd(0xD5); //设置显示时钟分频比/振荡器频率
oled_write_cmd(0x80); //0x00~0xFF
oled_write_cmd(0xA8); //设置多路复用率
oled_write_cmd(0x3F); //0x0E~0x3F
oled_write_cmd(0xD3); //设置显示偏移
oled_write_cmd(0x00); //0x00~0x7F
oled_write_cmd(0x40); //设置显示开始行,0x40~0x7F
oled_write_cmd(0xA1); //设置左右方向,0xA1正常,0xA0左右反置
oled_write_cmd(0xC8); //设置上下方向,0xC8正常,0xC0上下反置
oled_write_cmd(0xDA); //设置COM引脚硬件配置
oled_write_cmd(0x12);
oled_write_cmd(0x81); //设置对比度
oled_write_cmd(0xCF); //0x00~0xFF
oled_write_cmd(0xD9); //设置预充电周期
oled_write_cmd(0xF1);
oled_write_cmd(0xDB); //设置VCOMH取消选择级别
oled_write_cmd(0x30);
oled_write_cmd(0xA4); //设置整个显示打开/关闭
oled_write_cmd(0xA6); //设置正常/反色显示,0xA6正常,0xA7反色
oled_write_cmd(0x8D); //设置充电泵
oled_write_cmd(0x14);
oled_write_cmd(0xAF); //开启显示
}
//分装的坐标函数
void oled_set_cursor(uint8_t x, uint8_t y)//x是列,y是page
{
oled_write_cmd(0xB0 + y);
oled_write_cmd((x & 0x0F) | 0x00);
oled_write_cmd(((x & 0xF0) >> 4) | 0x10);
}
//清屏函数
void oled_fill(uint8_t data)
{
uint8_t i, j;
//外层循环循环page
for(i = 0; i < 8; i++)
{
oled_set_cursor(0, i);
for(j = 0; j < 128; j++)
oled_write_data(data);
}
}
oled.h
cpp
#ifndef __OLED_H__
#define __OLED_H__
#include "sys.h"
/*SCL引脚*/
#define OLED_I2C_SCL_CLK() __HAL_RCC_GPIOB_CLK_ENABLE()
#define OLED_I2C_SCL_PORT GPIOB
#define OLED_I2C_SCL_PIN GPIO_PIN_6
/*SDA引脚*/
#define OLED_I2C_SDA_CLK() __HAL_RCC_GPIOB_CLK_ENABLE()
#define OLED_I2C_SDA_PORT GPIOB
#define OLED_I2C_SDA_PIN GPIO_PIN_7
/*SCL引脚拉高拉低*/
#define OLED_SCL_SET() HAL_GPIO_WritePin(OLED_I2C_SCL_PORT,OLED_I2C_SCL_PIN,GPIO_PIN_SET)
#define OLED_SCL_RESET() HAL_GPIO_WritePin(OLED_I2C_SCL_PORT,OLED_I2C_SCL_PIN,GPIO_PIN_RESET)
/*SDA引脚拉高拉低*/
#define OLED_SDA_SET() HAL_GPIO_WritePin(OLED_I2C_SCL_PORT,OLED_I2C_SDA_PIN,GPIO_PIN_SET)
#define OLED_SDA_RESET() HAL_GPIO_WritePin(OLED_I2C_SCL_PORT,OLED_I2C_SDA_PIN,GPIO_PIN_RESET)
void oled_init(void);
void oled_write_cmd(uint8_t cmd);
void oled_write_data(uint8_t data);
void oled_set_cursor(uint8_t x, uint8_t y);
void oled_fill(uint8_t data);
#endif
项目实现-显示任意字符汉字图片
mian.c
cpp
#include "sys.h"
#include "delay.h"
#include "led.h"
#include "uart1.h"
#include "oled.h"
extern const unsigned char shuai_data[];
int main(void)
{
HAL_Init(); /* 初始化HAL库 */
stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */
led_init();//初始化led灯
uart1_init(115200);
oled_init();
printf("hello world!\r\n");
oled_fill(0);//清屏函数
// //字母A page0
// oled_set_cursor(0, 0);
// oled_write_data(0x00);
// oled_write_data(0x00);
// oled_write_data(0xC0);
// oled_write_data(0x38);
// oled_write_data(0xE0);
// oled_write_data(0x00);
// oled_write_data(0x00);
// oled_write_data(0x00);
// //page1
// oled_set_cursor(0, 1);
// oled_write_data(0x20);
// oled_write_data(0x3C);
// oled_write_data(0x23);
// oled_write_data(0x02);
// oled_write_data(0x02);
// oled_write_data(0x27);
// oled_write_data(0x38);
// oled_write_data(0x20);
oled_show_char(0, 0, 'L', 16);
oled_show_char(8, 0, 'X', 16);
oled_show_char(16, 0, '?', 16);
oled_show_char(24, 0, '6', 16);
// oled_show_string(0, 2, "hello LX", 24);
// uint8_t i;
// for(i = 0; i < 5; i++)
// oled_show_chinese(i*24, 0, i, 24);
// oled_show_image(0, 0, 128, 8, (unsigned char *)shuai_data);
while(1)
{
}
}
oled.c
cpp
#include "oled.h"
#include "delay.h"
#include "font.h"
//初始化oled的gpio
void oled_gpio_init(void)
{
GPIO_InitTypeDef gpio_initstruct;
OLED_I2C_SCL_CLK();
OLED_I2C_SDA_CLK();
gpio_initstruct.Mode = GPIO_MODE_OUTPUT_PP;
gpio_initstruct.Pin = OLED_I2C_SCL_PIN;
gpio_initstruct.Pull = GPIO_PULLUP;
gpio_initstruct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(OLED_I2C_SCL_PORT,&gpio_initstruct);
gpio_initstruct.Pin = OLED_I2C_SDA_PIN;
HAL_GPIO_Init(OLED_I2C_SDA_PORT,&gpio_initstruct);
}
/*IIC协议的分装*/
//开始信号
void oled_i2c_start(void)
{
OLED_SCL_SET();
OLED_SDA_SET();
OLED_SDA_RESET();
OLED_SCL_RESET();
}
//结束信号
void oled_i2c_stop(void)
{
OLED_SCL_SET();
OLED_SDA_RESET();
OLED_SDA_SET();
}
//应答信号
void oled_i2c_ack(void)
{
OLED_SCL_SET();
OLED_SCL_RESET();
}
//写一个字节信号
void oled_i2c_write_byte(uint8_t data)
{
uint8_t i,tmp;
tmp = data;
for(i=0;i<8;i++)
{
if((tmp & 0x80) == 0x80)//取出最高位,判断最高位是不是1
OLED_SDA_SET();//如果最高位是1,则sda给一个高电平
else
OLED_SDA_RESET();//如果最高位是0,则sda给一个低电平
tmp = tmp << 1;
OLED_SCL_SET();
OLED_SCL_RESET();
}
}
//发送一个命令信号
void oled_write_cmd(uint8_t cmd)
{
oled_i2c_start();
oled_i2c_write_byte(0x78);
oled_i2c_ack();
oled_i2c_write_byte(0x00);
oled_i2c_ack();
oled_i2c_write_byte(cmd);
oled_i2c_ack();
oled_i2c_stop();
}
//发送一个数据信号
void oled_write_data(uint8_t data)
{
oled_i2c_start();
oled_i2c_write_byte(0x78);
oled_i2c_ack();
oled_i2c_write_byte(0x40);
oled_i2c_ack();
oled_i2c_write_byte(data);
oled_i2c_ack();
oled_i2c_stop();
}
//初始化OLED函数
void oled_init(void)
{
oled_gpio_init();
delay_ms(100);
oled_write_cmd(0xAE); //设置显示开启/关闭,0xAE关闭,0xAF开启
oled_write_cmd(0xD5); //设置显示时钟分频比/振荡器频率
oled_write_cmd(0x80); //0x00~0xFF
oled_write_cmd(0xA8); //设置多路复用率
oled_write_cmd(0x3F); //0x0E~0x3F
oled_write_cmd(0xD3); //设置显示偏移
oled_write_cmd(0x00); //0x00~0x7F
oled_write_cmd(0x40); //设置显示开始行,0x40~0x7F
oled_write_cmd(0xA1); //设置左右方向,0xA1正常,0xA0左右反置
oled_write_cmd(0xC8); //设置上下方向,0xC8正常,0xC0上下反置
oled_write_cmd(0xDA); //设置COM引脚硬件配置
oled_write_cmd(0x12);
oled_write_cmd(0x81); //设置对比度
oled_write_cmd(0xCF); //0x00~0xFF
oled_write_cmd(0xD9); //设置预充电周期
oled_write_cmd(0xF1);
oled_write_cmd(0xDB); //设置VCOMH取消选择级别
oled_write_cmd(0x30);
oled_write_cmd(0xA4); //设置整个显示打开/关闭
oled_write_cmd(0xA6); //设置正常/反色显示,0xA6正常,0xA7反色
oled_write_cmd(0x8D); //设置充电泵
oled_write_cmd(0x14);
oled_write_cmd(0xAF); //开启显示
}
//分装的坐标函数
void oled_set_cursor(uint8_t x, uint8_t y)//x是列,y是page
{
oled_write_cmd(0xB0 + y);
oled_write_cmd((x & 0x0F) | 0x00);
oled_write_cmd(((x & 0xF0) >> 4) | 0x10);
}
//清屏函数
void oled_fill(uint8_t data)
{
uint8_t i, j;
//外层循环循环page
for(i = 0; i < 8; i++)
{
oled_set_cursor(0, i);
for(j = 0; j < 128; j++)
oled_write_data(data);
}
}
//指定显示字符 - 列坐标-page -哪个字符-字符大小
void oled_show_char(uint8_t x, uint8_t y, uint8_t num, uint8_t size)
{
uint8_t i, j, page;
num = num - ' ';
page = size / 8;
if(size % 8)//判断字符大小
page++;
for(j = 0; j < page; j++)//page循环
{
oled_set_cursor(x, y + j);
for(i = size / 2 * j; i < size /2 * (j + 1); i++)
{
if(size == 12)
oled_write_data(ascii_6X12[num][i]);
else if(size == 16)
oled_write_data(ascii_8X16[num][i]);
else if(size == 24)
oled_write_data(ascii_12X24[num][i]);
}
}
}
//显示字符串
void oled_show_string(uint8_t x, uint8_t y, char *p, uint8_t size)
{
while(*p != '\0')
{
oled_show_char(x, y, *p, size);
x += size/2;//偏移字符
p++;
}
}
//显示汉字
void oled_show_chinese(uint8_t x, uint8_t y, uint8_t N, uint8_t size)
{
uint16_t i, j;
for(j = 0; j < size/8; j++)
{
oled_set_cursor(x, y + j);
for(i = size *j; i < size * (j + 1); i++)
{
if(size == 16)
oled_write_data(chinese_16x16[N][i]);
else if(size == 24)
oled_write_data(chinese_24x24[N][i]);
}
}
}
//显示图片
void oled_show_image(uint8_t x, uint8_t y, uint8_t width, uint8_t height, uint8_t *bmp)
{
uint8_t i, j;
for(j = 0; j < height; j++)
{
oled_set_cursor(x, y + j);
for(i = 0; i < width; i++)
oled_write_data(bmp[width * j + i]);
}
}
oled.h
cpp
#ifndef __OLED_H__
#define __OLED_H__
#include "sys.h"
/*SCL引脚*/
#define OLED_I2C_SCL_CLK() __HAL_RCC_GPIOB_CLK_ENABLE()
#define OLED_I2C_SCL_PORT GPIOB
#define OLED_I2C_SCL_PIN GPIO_PIN_6
/*SDA引脚*/
#define OLED_I2C_SDA_CLK() __HAL_RCC_GPIOB_CLK_ENABLE()
#define OLED_I2C_SDA_PORT GPIOB
#define OLED_I2C_SDA_PIN GPIO_PIN_7
/*SCL引脚拉高拉低*/
#define OLED_SCL_SET() HAL_GPIO_WritePin(OLED_I2C_SCL_PORT,OLED_I2C_SCL_PIN,GPIO_PIN_SET)
#define OLED_SCL_RESET() HAL_GPIO_WritePin(OLED_I2C_SCL_PORT,OLED_I2C_SCL_PIN,GPIO_PIN_RESET)
/*SDA引脚拉高拉低*/
#define OLED_SDA_SET() HAL_GPIO_WritePin(OLED_I2C_SCL_PORT,OLED_I2C_SDA_PIN,GPIO_PIN_SET)
#define OLED_SDA_RESET() HAL_GPIO_WritePin(OLED_I2C_SCL_PORT,OLED_I2C_SDA_PIN,GPIO_PIN_RESET)
void oled_init(void);
void oled_write_cmd(uint8_t cmd);
void oled_write_data(uint8_t data);
void oled_set_cursor(uint8_t x, uint8_t y);
void oled_fill(uint8_t data);
void oled_show_char(uint8_t x, uint8_t y, uint8_t num, uint8_t size);
void oled_show_string(uint8_t x, uint8_t y, char *p, uint8_t size);
void oled_show_chinese(uint8_t x, uint8_t y, uint8_t N, uint8_t size);
void oled_show_image(uint8_t x, uint8_t y, uint8_t width, uint8_t height, uint8_t *bmp);
#endif
font.h
cpp
#ifndef __FONT_H__
#define __FONT_H__
//16号汉字
const unsigned char chinese_16x16[][32] =
{
/*汉字的字模*/
};
//24号汉字
const unsigned char chinese_24x24[][72] =
{
/*汉字的字模*/
};
const unsigned char ascii_6X12[][12]={
/*可见的ASCII字符集字模*/
};
//16*16 ASCII字符集点阵
const unsigned char ascii_8X16[][16]=
{
/*可见的ASCII字符集字模*/
};
//16*16 ASCII字符集点阵
const unsigned char ascii_12X24[][36]=
{
/*可见的ASCII字符集字模*/
};
//汉字帅
const unsigned char shuai_data[] = {
/*汉字帅的字模*/
};
#endif