I2C是由两根线组成的一种串行通信线路,如何用两根线约定传输信号呢?
I2C两根线分别为SCL时钟同步线,SDA数据传输线,两根线通过上拉电阻拉高电势,这个拉高有个好处就是可以释放总线。这些先不说,先讲如何标记不同的信号:
(资料图)
规定SCL为高的时候切换SDA跳变记作开始信号与结束信号;
规定SCL为低的时候切换SDA跳变记作数据传输信号。
记SDA释放后拉低信号为响应信号(应答ACK),不拉低信号为非响应信号(非应答NACK)。
于是乎我们就可以用两根线在SCL不同状态下的跳变传输信号了。
开始信号:SCL为高电平时候SDA产生一个下降沿
SCL_H
SDA_H
SDA_L
结束信号:SCL为高电平时候产生一个上升沿
SCL_H
SDA_L
SDA_H
数据传输信号:SCL低电平时候切换SDA到要发送的电平,然后拉高准备下一位发送。
传输一个0
SCL_L
SDA_L
SCL_H
传输一个1
SCL_L
SDA_H
SCL_H
应答与非应答:SCL拉低释放SDA,SCL再拉高后读取SDA的状态,如果为高则非应答,如果为低则为应答。最后拉低SCL确保在停止信号前保证为时钟低电平。
SCL_L
SDA_H
SCL_H
读取SDA状态判断是否有应答
SCL_L
以上所有的信号都解读完成了,你看懂了吗?
接下来就是利用上述的I2C基础信号组合成能发送字节的信号。
#include<REG52.h>
sbit OLED_SCL=P2^0;//时钟 D0(SCLK)时钟
sbit OLED_SDIN=P2^1;//D1(MOSI) 数据
#define IO_SCL_SET_L() OLED_SCL=0
#define IO_SCL_SET_H() OLED_SCL=1
#define IO_SDA_SET_H() OLED_SDIN=1
#define IO_SDA_SET_L() OLED_SDIN=0
#define Get_IO_SDA() OLED_SDIN
#define OLED_CMD 0//写命令
#define OLED_DATA 1//写数据
/*
I2C Start
起始信号:时钟信号在高电平状态时候,数据线产生一个下降沿表示开始信号
*/
void I2C_Start()
{
IO_SCL_SET_H();
IO_SDA_SET_H();
IO_SDA_SET_L();
}
/*
I2C Stop
停止信号:时钟信号在高电平状态时候,数据线产生一个上升沿表示停止
*/
void I2C_Stop()
{
IO_SCL_SET_H();
IO_SDA_SET_L();
IO_SDA_SET_H();
}
/*
I2C ACK/NACK
应答信号:第九个时钟周期的时钟高电平时候读取SDA数据,如果SDA此时为0,则表示应答,如果为1则表示非应答。
*/
void I2C_ACK()
{
IO_SDA_SET_H();
IO_SCL_SET_H();
if(Get_IO_SDA()==0)
IO_SCL_SET_L();
else
while(Get_IO_SDA()==1);
IO_SCL_SET_L();
}
/*
I2C 上发送一个字节数据
发送字节:发送字节是时钟信号的低电平时候修改数据,发送完要让时钟总线保持在低电平。
*/
void Write_I2C_Byte(unsigned char I2C_Byte)
{
unsigned char i;
for(i=0;i<8;i++)
{
IO_SCL_SET_L();
if((I2C_Byte<<i)&0x80)
IO_SDA_SET_H();
else
IO_SDA_SET_L();
IO_SCL_SET_H();
}
IO_SCL_SET_L();
}
/*
add:7位地址;
rwbit:读写位,1表示读,0表示写
dat:数据字节
命令或数据:1表示发送的字节是数据,0表示发送的字节是命令
*/
void OLED_WR_Byte(unsigned char dat,unsigned char cmd)
{
I2C_Start();
Write_I2C_Byte(0x78);
I2C_ACK();
if(cmd)
Write_I2C_Byte(0x40);
else
Write_I2C_Byte(0x00);
I2C_ACK();
Write_I2C_Byte(dat);
I2C_ACK();
I2C_Stop();
}
//坐标设置
void OLED_Set_Pos(unsigned char x, unsigned char y)
{
OLED_WR_Byte(0xb0+y,OLED_CMD);
OLED_WR_Byte(((x&0xf0)>>4)|0x10,OLED_CMD);
OLED_WR_Byte((x&0x0f),OLED_CMD);
}
//开启OLED显示
void OLED_Display_On(void)
{
OLED_WR_Byte(0X8D,OLED_CMD); //SET DCDC命令
OLED_WR_Byte(0X14,OLED_CMD); //DCDC ON
OLED_WR_Byte(0XAF,OLED_CMD); //DISPLAY ON
}
//关闭OLED显示
void OLED_Display_Off(void)
{
OLED_WR_Byte(0X8D,OLED_CMD); //SET DCDC命令
OLED_WR_Byte(0X10,OLED_CMD); //DCDC OFF
OLED_WR_Byte(0XAE,OLED_CMD); //DISPLAY OFF
}
//清屏函数,清完屏,整个屏幕是黑色的!和没点亮一样!!!
void OLED_Clear(void)
{
unsigned char i,n;
for(i=0;i<4;i++)
{
OLED_WR_Byte (0xb0+i,OLED_CMD); //设置页地址(0~7)
OLED_WR_Byte (0x00,OLED_CMD); //设置显示位置—列低地址
OLED_WR_Byte (0x10,OLED_CMD); //设置显示位置—列高地址
for(n=0;n<128;n++)OLED_WR_Byte(0,OLED_DATA);
} //更新显示
}
//反显函数
void OLED_ColorTurn(unsigned char i)
{
if(i==0)
{
OLED_WR_Byte(0xA6,OLED_CMD);//正常显示
}
if(i==1)
{
OLED_WR_Byte(0xA7,OLED_CMD);//反色显示
}
}
//屏幕旋转180度
void OLED_DisplayTurn(unsigned char i)
{
if(i==0)
{
OLED_WR_Byte(0xC8,OLED_CMD);//正常显示
OLED_WR_Byte(0xA1,OLED_CMD);
}
if(i==1)
{
OLED_WR_Byte(0xC0,OLED_CMD);//反转显示
OLED_WR_Byte(0xA0,OLED_CMD);
}
}
//初始化
void OLED_Init(void)
{
OLED_WR_Byte(0xAE,OLED_CMD); /*display off*/
OLED_WR_Byte(0x00,OLED_CMD); /*set lower column address*/
OLED_WR_Byte(0x10,OLED_CMD); /*set higher column address*/
OLED_WR_Byte(0x00,OLED_CMD); /*set display start line*/
OLED_WR_Byte(0xB0,OLED_CMD); /*set page address*/
OLED_WR_Byte(0x81,OLED_CMD); /*contract control*/
OLED_WR_Byte(0xff,OLED_CMD); /*128*/
OLED_WR_Byte(0xA1,OLED_CMD); /*set segment remap*/
OLED_WR_Byte(0xA6,OLED_CMD); /*normal / reverse*/
OLED_WR_Byte(0xA8,OLED_CMD); /*multiplex ratio*/
OLED_WR_Byte(0x1F,OLED_CMD); /*duty = 1/32*/
OLED_WR_Byte(0xC8,OLED_CMD); /*Com scan direction*/
OLED_WR_Byte(0xD3,OLED_CMD); /*set display offset*/
OLED_WR_Byte(0x00,OLED_CMD);
OLED_WR_Byte(0xD5,OLED_CMD); /*set osc division*/
OLED_WR_Byte(0x80,OLED_CMD);
OLED_WR_Byte(0xD9,OLED_CMD); /*set pre-charge period*/
OLED_WR_Byte(0x1f,OLED_CMD);
OLED_WR_Byte(0xDA,OLED_CMD); /*set COM pins*/
OLED_WR_Byte(0x00,OLED_CMD);
OLED_WR_Byte(0xdb,OLED_CMD); /*set vcomh*/
OLED_WR_Byte(0x40,OLED_CMD);
OLED_WR_Byte(0x8d,OLED_CMD); /*set charge pump enable*/
OLED_WR_Byte(0x14,OLED_CMD);
OLED_Clear();
OLED_WR_Byte(0xAF,OLED_CMD); /*display ON*/
}
unsigned char code num[][16]={
{0x00,0xE0,0x10,0x08,0x08,0x10,0xE0,0x00,0x00,0x0F,0x10,0x20,0x20,0x10,0x0F,0x00},/*"0",0*/
{0x00,0x00,0x10,0x10,0xF8,0x00,0x00,0x00,0x00,0x00,0x20,0x20,0x3F,0x20,0x20,0x00},/*"1",1*/
{0x00,0x70,0x08,0x08,0x08,0x08,0xF0,0x00,0x00,0x30,0x28,0x24,0x22,0x21,0x30,0x00},/*"2",2*/
{0x00,0x30,0x08,0x08,0x08,0x88,0x70,0x00,0x00,0x18,0x20,0x21,0x21,0x22,0x1C,0x00},/*"3",3*/
{0x00,0x00,0x80,0x40,0x30,0xF8,0x00,0x00,0x00,0x06,0x05,0x24,0x24,0x3F,0x24,0x24},/*"4",4*/
{0x00,0xF8,0x88,0x88,0x88,0x08,0x08,0x00,0x00,0x19,0x20,0x20,0x20,0x11,0x0E,0x00},/*"5",5*/
{0x00,0xE0,0x10,0x88,0x88,0x90,0x00,0x00,0x00,0x0F,0x11,0x20,0x20,0x20,0x1F,0x00},/*"6",6*/
{0x00,0x18,0x08,0x08,0x88,0x68,0x18,0x00,0x00,0x00,0x00,0x3E,0x01,0x00,0x00,0x00},/*"7",7*/
{0x00,0x70,0x88,0x08,0x08,0x88,0x70,0x00,0x00,0x1C,0x22,0x21,0x21,0x22,0x1C,0x00},/*"8",8*/
{0x00,0xF0,0x08,0x08,0x08,0x10,0xE0,0x00,0x00,0x01,0x12,0x22,0x22,0x11,0x0F,0x00},/*"9",9*/
};
/*
numb:显示的数字,0-9
x:坐标:0-127
y:坐标:0-3
flag:0为正显示,1为反显示
*/
void numberx(unsigned char numb, unsigned char x, unsigned char y, unsigned char flag) {
unsigned char i;
for (i = 0; i < 16; i++) {
if (i == 0 || i == 15) {
OLED_Set_Pos(x + i, y + 0);
OLED_WR_Byte(flag ? ~0xFF : 0xFF, OLED_DATA);
OLED_Set_Pos(x + i, y + 1);
OLED_WR_Byte(flag ? ~0xFF : 0xFF, OLED_DATA);
} else if ((i > 0 && i < 4) || (i > 12 && i < 16)) {
OLED_Set_Pos(x + i, y + 0);
OLED_WR_Byte(flag ? ~0x01 : 0x01, OLED_DATA);
OLED_Set_Pos(x + i, y + 1);
OLED_WR_Byte(flag ? ~0x80 : 0x80, OLED_DATA);
} else {
OLED_Set_Pos(x + i, y + 0);
OLED_WR_Byte(flag ? ~(0x01 | num[numb][i - 4]) : (0x01 | num[numb][i - 4]), OLED_DATA);
OLED_Set_Pos(x + i, y + 1);
OLED_WR_Byte(flag ? ~(0x80 | num[numb][i + 4]) : (0x80 | num[numb][i + 4]), OLED_DATA);
}
}
}
关键词:
上一篇:帮宝适积分扫码不成功_帮宝适积分
下一篇:最后一页
-
I2C协议的超简单解读,小学生都能学得会|全球关注2023-06-19
-
帮宝适积分扫码不成功_帮宝适积分2023-06-19
-
303潜艇恐怖照片_303潜艇2023-06-19
-
未来交通什么样?这些黑科技抢先看_环球速读2023-06-19