了解 I2C
飞利浦(NXP)于 1980 提出I2C
串行总线,使用多主从架构,为同步半双工通信方式 .
常见速率:100kbps、300kbps、3.4Mbps .
端口设置
采用外部上拉(强上拉)时,端口配置为开漏输出,配合上拉电阻(1K 左右)以输出高电平 .
I2C 的数据有效性
在传输期间,SCL
为高电平时数据有效,数据的切换只能在SCL
为低电平时进行 .
I2C 的 START 和 STOP 信号
void IIC_Start(void)
{
SDA_HIGH;
SCL_HIGH;
SDA_LOW;
SCL_LOW;
}
void IIC_Stop(void)
{
SDA_LOW;
SCL_HIGH;
SDA_HIGH;
}
I2C 的 ACK 应答信号
START
之后,在接下来的 8 个 CLK 中,SDA
进行一次 8bit 的数据传输,若接收到数据,则在第 9 个 CLK 对SDA
拉低,将其视作一次ACK
应答信号 .
发送 ACK 的方式参考
void IIC_SendAck(uint8_t ACK_Byte)
{
if(ACK_Byte == 1)
SDA_HIGH;
else
SDA_LOW;
SCL_HIGH;
SCL_LOW;
}
接受 ACK 方式参考
uint8_t IIC_ReceiveAck(void)
{
uint8_t ACK_Byte;
SDA_HIGH;//主机释放,从机接管
SCL_HIGH;
ACK_Byte = READ_SDA;
SCL_LOW;
return ACK_Byte;
}
I2C 读写 1Byte 数据
写数据的方式参考
void IIC_WriteByte(uint8_t IIC_Byte)
{
uint8_t i;
uint8_t data = IIC_Byte;
SCL_LOW;
for(i=0;i<8;i++)
{
if((data&0x80) == 0x80)
SDA_HIGH;
else
SDA_LOW;
data = data<<1;
SCL_HIGH;
SCL_LOW;
}
}
读数据的方式参考
uint8_t IIC_ReadByte(void)
{
uint8_t i,IIC_Byte = 0x00;
SDA_HIGH;//主机释放,从机接管 --这里的SDA,SCL都是推挽输出
for(i=0;i<8;i++)
{
SCL_HIGH;
if(READ_SDA == 1)
IIC_Byte |= (0x80>>i);
SCL_LOW;
}
return IIC_Byte;
}
了解 SSD1306
SSD1306 使用 I2C 的数据格式
首字节
由从机地址0x3C
和R/W#
组成 .
IIC_WriteByte(0x78);//W
IIC_WriteByte(0x79);//R
控制字节
由Co
和D/C#
及000000
组成,Co
低电平意指后续的 Byte 都是数据,可用于连续输出 CMD 和 DATA .
IIC_WriteByte(0x00);//后面的字节是命令
IIC_WriteByte(0x40);//后面的字节是数据,并存入GDDRAM
IIC_WriteByte(0x80);//后一位字节是命令
IIC_WriteByte(0xC0);//后一位字节是数据,并存入GDDRAM
写一次命令
void OLED_CMD(uint8_t CMD)
{
IIC_Start();
IIC_WriteByte(0x78);
IIC_ReceiveAck();
IIC_WriteByte(0x80);
IIC_ReceiveAck();
IIC_WriteByte(CMD);
IIC_ReceiveAck();
IIC_Stop();
}
验证
尝试发送 0xAF
开启屏幕的显示
OLED_CMD(0xAF);
接上逻辑分析仪查看时序
观察屏幕,情况如下:
屏幕被成功点亮,并将 GDDRAM 中的初始数据输出 .
SSD1306 的 GDDRAM
GDDRAM 结构
从上至下,将 GDDRAM 分为 8 个 PAGE,8 * 128 Byte
从左至右,将 PAGE 分为 128 个 SEG,128 * 1 Byte
写入 GDDRAM 的 Byte,最低位 D0 在顶行,最高位 D7 在底行 .
GDDRAM 的寻址模式
20h+A[1:0]
设置内存寻址模式
A[1:0]=10b
页面寻址 用于局部刷新
仅在 PAGE 中寻址 .向 GDDRAM 写入 1Byte 后,列指针自增 1,直至寻址结束,页指针不变 .A[1:0]=00b
水平寻址A[1:0]=01b
竖直寻址A[1:0]=11b
无效寻址
填充全屏
OLED_FILL()
{
OLED_CMD(0x20); // 设置寻址模式
OLED_CMD(0x00); // 水平寻址模式
OLED_CMD(0X21); // 设置列起始和结束地址
OLED_CMD(0X00); // 列起始地址 0
OLED_CMD(0X7F); // 列终止地址 127
OLED_CMD(0X22); // 设置页起始和结束地址
OLED_CMD(0X00); // 页起始地址 0
OLED_CMD(0X07); // 页终止地址 7
uint8_t r,c;
for(r=0;r<8;r++)
for(c=0;c<128;c++)
OLED_DATA(0xFF);
}