一、iic总线概述
-
iic总线只有两根双向信号线,一根是数据线SDA,一根是时钟线SCL -
iic总线是通过上拉电阻接正电源。当总线空闲时,两根线均为高电平 -
当连接到总线的任一器件输出低电平,都将使总线的信号拉低,即各器件的SDA和SCL到是线“与”关系
二、iic通信流程
- 主设备发送起始信号
- 主设备接着发送8bit数据,其中高7位是从设备的地址,低一位 置一代表 读,置零 代表写
- 主设备读取从设备响应信号
- 数据传输
- 主设备读取从设备响应信号
- 循环N个4和5步骤完成通信
- 主设备发送停止信号
三、起始信号
SCL为高电平期间,SDA线由高电平向低电平变化
void iic_start()
{
SCL = 1;
SDA = 1;
iic_delay();
SDA = 0;
iic_delay();
}
四、终止信号
SCL为高电平期间,SDA线由低电平向高电平变化
void iic_stop()
{
SCL = 1;
SDA = 0;
iic_delay();
SDA = 1;
iic_delay();
}
五、应答信号
应答: SDA=0,可继续通信 非应答: SDA=1,结束通信 应答: ack等于1,应答; ack等于0,非应答
void iic_ack(bit ack)
{
SCL = 0;
_nop_();
if (ack == ACK)
{
SDA = 0;
_nop_();
}
else
{
SDA = 1;
_nop_();
}
SCL = 1;
iic_delay();
SCL = 0;
}
等待应答信号:
unsigned char iic_wait_ack()
{
SDA = 1;
SCL = 1;
_nop_();
while (!SCL);
if (SDA)
{
SCL = 0;
return 1;
}
SCL = 0;
return 0;
}
六、数据收发
- IIC总线进行数据传输时,时钟信号SCL高电平器件,数据线SDA上的数据必须保持稳定
- 只有在时钟线SCL位低电平期间,数据线SDA上的高、低电平状态才允许数据变化
- 输出到SDA线上的每个字节必须是8位,按位传输,从高位到低位,每一个被传送的字节后面都必须跟随一位应答位
数据收发过程:先拉低SCL,在改变SDA,最后拉高SCL 4. SCL = 0; 5. SDA = 0; 或 SDA = 1; 6. SCL = 1;
void iic_send_byte(unsigned char da)
{
char i;
for(i = 0; i < 8; i++)
{
SCL = 0;
_nop_();
if (da & 0x80)
{
SDA = 1;
}
else
{
SDA = 0;
}
_nop_();
SCL = 1;
_nop_();
SCL = 0;
da <<= 1;
}
}
七、OLED发送数据
高七位为从机地址;低一位为1,读模式,为0,写模式
SA0: 通过将SA0更改为低或高的SA0,从机地址为“b0111100”或“b0111101”,(D/C引脚充当SA0)。可实现两个设备,显示不同内容 Co: 如果Co位被设置为逻辑“0”,则以下信息的传输将只包含数据字节 D/C:如果D/C#位设置为逻辑“0”,则会将以下数据字节定义为命令。如果D/C#位被设置为逻辑“1”,那么它会将以下数据字节定义为将存储在GDDRAM中的数据 (显示数据) 数据发送流程: 1.发送 起始信号 2. 发送 从机地址(0x78 : 0111 1000)(0:写模式) 3. 等待应答信号 4. 发送数据内容标志 (命令:0x00:0(Co)0(D/C)00 0000,显示数据:0x40:0(Co)1(D/C)00 0000) 5. 发送数据 6. 发送 终止信号
void oled_send_byte(unsigned int addr, unsigned char da, char mode)
{
iic_start();
send_byte(addr);
iic_wait_ack();
if (mode) {send_byte(0x40);}
else {send_byte(0x00);}
iic_wait_ack();
send_byte(da);
iic_wait_ack();
iic_stop();
}
八、OLED选址模式
OLED分辨率为128*64,每8行作为一个PAGE
页选址模式 在页面寻址模式下,读写显示RAM后,列地址指针自动递增1。如果列地址指针到达列结束地址,则列地址指针被重置为列起始地址,并且页面地址指针不被更改。用户需要设置新的页面和列地址,以访问下一页的RAM内容。(页地址重置后,列地址不会自动重置,列保持递增) 水平选址模式 在水平寻址模式下,读写显示RAM后,列地址指针自动递增1。如果列地址指针到达列结束地址,则列地址指针重置为列起始地址,页地址指针增加1。 垂直选址模式 在垂直寻址模式下,读写显示RAM后,页面地址指针自动递增1。如果页面地址指针到达页面结束地址,则页面地址指针将重置页面开始地址,列地址指针将增加1。
发送两个命令指定选址模式: 页选址: 发送命令0x20和0x02 (默认)
oled_send_byte(0x78, 0x20, 0);
oled_send_byte(0x78, 0x02, 0);
水平选址: 发送命令 0x20和0x00
oled_send_byte(0x78, 0x20, 0);
oled_send_byte(0x78, 0x00, 0);
垂直选址: 发送命令 0x20和0x01
oled_send_byte(0x78, 0x20, 0);
oled_send_byte(0x78, 0x01, 0);
九、OLED位置选择
1. 页选址模式 设置 页起始地址
PAGE0~PAGE7 页地址使用 X[2:0] 指定
例:指定第一页,发送指令 0xB0
oled_send_byte(0x78, 0xB0, 0);
2. 页选址模式 设置 列起始地址
设置的列数 = Lower + Higher * 16 例:设置列数为100 = 4 + 6*16 即发送指令 0x04 和 0x16
3. 位置选择
void oled_set_pos(unsigned char x, unsigned char y)
{
oled_send_byte(0x78, 0xb0+y, 0);
oled_send_byte(0x78, ((x&0xf0)>>4)|0x10, 0);
oled_send_byte(0x78, (x&0x0f), 0);
}
十、OLED清屏(页选址模式)
void oled_clear()
{
unsigned char i,n;
for(i = 0; i < 8; i++)
{
oled_send_byte(0x78, 0xb0+i, 0);
oled_send_byte(0x78, 0x00, 0);
oled_send_byte(0x78, 0x10, 0);
for(n = 0; n < 128; n++)oled_send_byte(0x78, 0, 0);
}
}
十一、OLED显示函数参考
void OLED_ShowChar(u8 x,u8 y,u8 chr,u8 sizey)
{
u8 c=0,sizex=sizey/2;
u16 i=0,size1;
if(sizey==8)size1=6;
else size1=(sizey/8+((sizey%8)?1:0))*(sizey/2);
c=chr-' ';
OLED_Set_Pos(x,y);
for(i=0;i<size1;i++)
{
if(i%sizex==0&&sizey!=8) OLED_Set_Pos(x,y++);
if(sizey==8) OLED_WR_Byte(asc2_0806[c][i],OLED_DATA);
else if(sizey==16) OLED_WR_Byte(asc2_1608[c][i],OLED_DATA);
else return;
}
}
void OLED_ShowNum(u8 x,u8 y,u32 num,u8 len,u8 sizey)
{
u8 t,temp,m=0;
u8 enshow=0;
if(sizey==8)m=2;
for(t=0;t<len;t++)
{
temp=(num/oled_pow(10,len-t-1))%10;
if(enshow==0&&t<(len-1))
{
if(temp==0)
{
OLED_ShowChar(x+(sizey/2+m)*t,y,' ',sizey);
continue;
}else enshow=1;
}
OLED_ShowChar(x+(sizey/2+m)*t,y,temp+'0',sizey);
}
}
void OLED_ShowString(u8 x,u8 y,u8 *chr,u8 sizey)
{
u8 j=0;
while (chr[j]!='\0')
{
OLED_ShowChar(x,y,chr[j++],sizey);
if(sizey==8)x+=6;
else x+=sizey/2;
}
}
void OLED_ShowChinese(u8 x,u8 y,u8 no,u8 sizey)
{
u16 i,size1=(sizey/8+((sizey%8)?1:0))*sizey;
for(i=0;i<size1;i++)
{
if(i%sizey==0) OLED_Set_Pos(x,y++);
if(sizey==16) OLED_WR_Byte(Hzk[no][i],OLED_DATA);
else return;
}
}
void OLED_DrawBMP(u8 x,u8 y,u8 sizex, u8 sizey,u8 BMP[])
{
u16 j=0;
u8 i,m;
sizey=sizey/8+((sizey%8)?1:0);
for(i=0;i<sizey;i++)
{
OLED_Set_Pos(x,i+y);
for(m=0;m<sizex;m++)
{
OLED_WR_Byte(BMP[j++],OLED_DATA);
}
}
}
取模软件及参考资料
|