八、IIC通信和操作四角OLED显示屏

一、IIC协议介绍

  (1)协议介绍

  IIC(又称I2C,Inter-Integrated Circuit),即集成电路总线,是一种两线式串行总线,由PHILIPS公司开发,用于连接微控制器及其外围设备。多用于主机和从机在数据量不大且传输距离短的场合下的主从通信。I2C总线由数据线SDA和时钟线SCL构成通信线路,既可用于发送数据,也可接收数据,是一种半双工通信协议。总线上的主设备与从设备之间以字节(8位)为单位进行双向的数据传输。

  (2)传输模式

  • 标准模式:100K bit/s
  • 快速模式:400K bit/s
  • 高速模式:3.4M bit/s

  (3)连接线路

  (4)时序分析

  • 空闲状态:IIC总线的SDA和SCL线同时处于高电平时。
  • 起始信号S:SCL和SDA都处于高电平状态,此时SDA线从高电平跃变到低电平产生起始信号。

  •  终止信号P:当SCL处于高电平,SDA处于低电平,SDA线从低电平跃变到高电平,产生终止信号。

  •  应答信号:8位一字节数据传送完毕后,第九位为应答信号,0表示接收到应答,1表示无应答。

   (5)代码编写模拟IIC,使用位带和SysTick滴答定时器延时函数

  1.起始信号

复制代码
 1 void IIC_Start(void)
 2 {
 3     sda_out();
 4     SCL=1;
 5     SDA=1;
 6     IIC_Delay_Us();//拉高SCL和SDA,维持5us
 7     SDA=0;
 8     IIC_Delay_Us();//拉低SDA,维持5us
 9     SCL=0;
10     IIC_Delay_Us();//拉低SCL,维持5us,等待数据发送或接收
11 }
复制代码

  2.终止信号

复制代码
1 void IIC_Stop(void)
2 {
3     sda_out();
4     SCL=1;
5     SDA=0;
6     IIC_Delay_Us();//SCL高电平,SDA低电平,维持5us
7     SDA=1;
8     IIC_Delay_Us();//SDA电平拉高,维持5us
9 }
复制代码

  3.主机接收标志位

复制代码
 1 u8 IIC_Check_ACK(void)
 2 {
 3     u8 ack;
 4     sda_in();
 5     SCL=0;
 6     IIC_Delay_Us();
 7     SCL=1;
 8     Delay_Us(2);//拉高后延时2us等待信号稳定
 9     if(SDA_STATE){//无应答
10         ack=NACK;
11     }else{//有应答
12         ack=ACK;
13     }
14     Delay_Us(2);
15     SCL=0;
16     return ack;
17 }
复制代码

  4.主机发送标志位

复制代码
 1 void IIC_ACK(u8 ack)
 2 {
 3     sda_out();
 4     SCL=0;
 5     IIC_Delay_Us();//发送数据,SCL拉低,SDA信号变换
 6     if(ack==ACK){
 7         SDA=0;//变换SDA电平
 8         IIC_Delay_Us();
 9     }else{
10         SDA=1;//变换SDA电平
11         IIC_Delay_Us();
12     }
13     SCL=1;
14     IIC_Delay_Us();//拉高SCL将SDA数据发送出去
15     SCL=0;
16 }
复制代码

  5.主机发送一字节数据给从机

复制代码
 1 void IIC_Send_Byte(u8 byte_data)
 2 {
 3     u8 i;
 4     sda_out();
 5     for(i=0;i<8;i++){
 6         SCL=0;
 7         IIC_Delay_Us();//拉低SCL等待SDA发送一位数据
 8         if(byte_data&0x80)//最高位为1
 9             SDA=1;
10         else
11             SDA=0;
12         IIC_Delay_Us();
13         
14         SCL=1;
15         IIC_Delay_Us();//拉高SCL将SDA的一位数据发送出去
16         
17         byte_data<<=1;  
18     }
19     SCL=0;
20     IIC_Delay_Us();//拉低SCL等待SDA发送一位数据
21 }
复制代码

  6.主机接收从机发送过来的一字节数据

复制代码
 1 u8 IIC_Read_Byte(void)
 2 {
 3     sda_in();
 4     u8 temp,i;
 5     for(i=0;i<8;i++){
 6         SCL=0;
 7         IIC_Delay_Us();//拉低SCL等待接收数据
 8         SCL=1;
 9         Delay_Us(2);
10         temp<<=1;
11         if(SDA_STATE)
12             temp|=0x01;
13         Delay_Us(2);
14     }
15     SCL=0;
16     IIC_Delay_Us();//拉低SCL
17     return temp;
18 }
复制代码

  7.其他函数以及头文件

复制代码
 1 void IIC_Init(void)
 2 {
 3     RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
 4     
 5     GPIO_InitTypeDef GPIO_InitStruct;
 6     GPIO_InitStruct.GPIO_Pin = GPIO_Pin_14|GPIO_Pin_15;
 7     GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_OD;//开漏输出
 8     GPIO_InitStruct.GPIO_Speed = GPIO_Speed_10MHz;
 9     GPIO_Init(GPIOB,&GPIO_InitStruct);
10     
11     Delay_Init();
12 }
13 
14 void IIC_Delay_Us(void)
15 {
16     Delay_Us(5);
17 }
18 
19 static void sda_out(void)
20 {
21     GPIO_InitTypeDef GPIO_InitStruct;
22     GPIO_InitStruct.GPIO_Pin = GPIO_Pin_15;
23     GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_OD;//开漏输出
24     GPIO_InitStruct.GPIO_Speed = GPIO_Speed_10MHz;
25     GPIO_Init(GPIOB,&GPIO_InitStruct);
26 }
27 
28 static void sda_in(void)
29 {
30     GPIO_InitTypeDef GPIO_InitStruct;
31     GPIO_InitStruct.GPIO_Pin = GPIO_Pin_15;
32     GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPU;//上拉输入
33     GPIO_InitStruct.GPIO_Speed = GPIO_Speed_10MHz;
34     GPIO_Init(GPIOB,&GPIO_InitStruct);
35 }
36 
37 //头文件
38 #ifndef __MY_IIC_H__
39 #define __MY_IIC_H__
40 #include "stm32f10x.h"
41 #include "delay.h"
42 #include "sys.h"
43 
44 #define SCL PBout(14)
45 #define SDA PBout(15)
46 
47 #define SDA_STATE PBin(15)
48 
49 #define ACK 0   //应答信号
50 #define NACK 1  //不应答信号
51 
52 //IIC初始化端口
53 void IIC_Init(void);
54 //IIC延时5us
55 void IIC_Delay_Us(void);
56 
57 static void sda_out(void);
58 static void sda_in(void);
59 
60 //IIC开始信号
61 void IIC_Start(void);
62 //结束信号
63 void IIC_Stop(void);
64 //主机应答从机
65 void IIC_ACK(u8 ack);
66 //从机应答主机
67 u8 IIC_Check_ACK(void);
68 //发送一个字节数据
69 void IIC_Send_Byte(u8 byte_data);
70 //接收一字节数据
71 u8 IIC_Read_Byte(void);
72 
73 #endif
复制代码

二、OLED相关信息

  • 显示分辨率128*64 点阵
  • IIC从地址0x78

   发送完从机地址后发送一字节的控制字节,告诉IIC设备接下来是指令字节还是数据字节。Co=0,接下来的只包含数据字节。D/C=0,接下来写入的是命令字节。D/C=1,接下来写入的是数据字节。

  写命令和写数据的程序如下:

  1.写命令

复制代码
 1 static void Write_Cmd(u8 cmd)
 2 {
 3     IIC_Start();
 4     IIC_Send_Byte(0x78);
 5     IIC_Check_ACK();//可能有误
 6     IIC_Send_Byte(0x00);
 7     IIC_Check_ACK();
 8     IIC_Send_Byte(cmd);
 9     IIC_Check_ACK();
10     IIC_Stop();
11 }
复制代码

  2.写数据

复制代码
 1 static void Write_Data(u8 data)
 2 {
 3     IIC_Start();
 4     IIC_Send_Byte(0x78);
 5     IIC_Check_ACK();
 6     IIC_Send_Byte(0x40);
 7     IIC_Check_ACK();
 8     IIC_Send_Byte(data);
 9     IIC_Check_ACK();
10     IIC_Stop();
11 }
复制代码
  •  显存GDDRAM大小为128*64
  • 每8COM组成一个Page,共有8Page,即64行
  • 每一个COM中有128个Segment,即128列
  • 通过设置COM与Page的映射关系,可以改变OLED的行刷新方向
  • 通过设置SEG与Column的映射关系,可以改变OLED的列刷新方向
  • 在一个Page中,数据刷新总是从低SEG刷新到高SEG,在一个SEG中,数据的低位在上,高位在下,即在一个SEG中数据总是从低COM刷新到高COM

  3.命令解释Command

  3.1 设置对比度

命令 数据
0x81

对比度(0~255) 0x00~0xFF

默认值为0x7F(127)

  先发送命令字节0x81,然后设置对比度数据,实际是设置OLED的驱动电流,对比度设置越大,驱动电流越大,显示亮度就越高。

  3.2 OLED显示跟随GDDRAM

命令 数据

0xA4:OLED显示跟随GDDRAM

0xA5:OLED显示固定,不跟随GDDRAM

默认值为0xA4

  3.3 设置反色

命令 数据

0xA6:正常显示,即GDDRAM中1表示显示,0表示不显示

0xA7:反转显示,即GDDRAM中0表示显示,1表示不显示

默认值0xA6

  3.4 开启/关闭显示

命令 数据

0xAE:关闭显示,进入睡眠模式

0xAF:开启显示

默认值0xAE

  3.5 设置显示列地址

命令 数据 说明

0x00~0x0F:设置列地址的低四位,默认0x00

0x10~0x1F:设置列地址的高四位,默认0x10

此设置仅在页寻址模式下有效

两个设置共同决定显示列的起始地址

  3.6 设置寻址模式

命令 数据 说明
0x20

0x00:行寻址模式

0x01:列寻址模式

0x02:页寻址模式

默认0x02

先发送0x20,然后发送寻址模式选择字节

页寻址模式

水平/行寻址模式

 垂直/列寻址模式

   3.7 设置列地址

命令 数据 说明
0x21

0~127,默认为0

0~127,默认为127

先发送0x21,然后发送两字节的数据用于设置列的起始地址和结束地址

仅在行/列寻址模式下有效

  3.8 设置页地址

命令 数据 说明
0x22

0~7:默认0

0~7:默认7

先发送0x22,然后发送两字节的数据用于设置页的起始地址和结束地址

仅在行/列寻址模式下有效

  3.9 设置页的起始地址

命令 说明

0xB0~00xB7

默认0xB0

用于设置页寻址模式下的页起始地址

  3.10 设置显示开始行

命令 数据

0x40~0x7F对应0-63行

默认0x40

  3.11 设置列对于Segment的映射

命令 说明

0xA0:Column0映射到Seg0

0xA1:Column127映射到Seg0

默认为0xA0

从左刷新数据还是从右刷新数据

  3.12 设置通道数

命令 数据
0xA8

0x10~0x3F 对应15+1~63+1

默认为63

  3.13 设置COM Driver的扫描方向

命令 说明

0xC0:从COM0扫描到COMN

0xC8:从COMN扫描到COM0

默认值0xC0

也就是从上往下扫描还是从下往上扫描

  3.14 设置COM的偏移值,一般设置为0,保证屏幕的完整显示

命令 数据
0xD3

0~63

默认0

  3.15 设置时钟分频比和时钟频率

命令 数据
0xDA

[3:0]:设置时钟分频比

[7:4]:设置时钟频率

0x12:128*64OLED

0x02:128*32OLED

  3.16 预充电时间,保持0x22默认值即可

  4.代码设置OLED初始化

复制代码
 1 void Oled_Init(void)
 2 {
 3     IIC_Init();
 4     Delay_Ms(200);
 5     
 6     Write_Cmd(0xA8);//设置分辨率
 7     Write_Cmd(0x3F);//0x3f : 128*64  0x1f  128*32
 8     
 9     Write_Cmd(0xDA);//设置COM硬件引脚配置,适应分辨率
10     Write_Cmd(0x12);//0x12 : 0.96->128*64  0x02 : 0.91->128*32
11     
12     Write_Cmd(0xD3);//设置显示偏移
13     Write_Cmd(0x00);//默认无偏移
14     Write_Cmd(0x40);//设置显示开始0-63
15     Write_Cmd(0xA0);//段SEGMENT重映射对于IIC四角OLED要设置为0xA1
16     Write_Cmd(0x81);//对比度设置
17     Write_Cmd(0x7F);//亮度设置0x00-0xFF,数值越大亮度越大
18     Write_Cmd(0xA4);//输出遵循RAM内容,0x05输出忽略RAM内容
19     Write_Cmd(0xA6);//显示方式正常显示,0xA7反向显示,逆码,0点亮还是1点亮
20     Write_Cmd(0xD5);//设置显示时钟分频/振荡器频率
21     Write_Cmd(0xF0);//设置分辨率值
22     Write_Cmd(0x8D);//充电泵设置
23     Write_Cmd(0x14);//允许在显示开启的时候使用,0x10:不允许在开启前使用
24     Write_Cmd(0xAE);//显示关闭,0xAF显示开启
25     Write_Cmd(0x20);//设置内存地址模式 水平/垂直/页寻址(默认)
26     Write_Cmd(0x02);//水平0x00   垂直0x01    页寻址0x02
27     Write_Cmd(0xC0);//设置COM扫描方式0xC0上下反置左到右  0xC8正常右到左
28     Write_Cmd(0xB0);//为页寻址模式设置开启地址0-7
29     Write_Cmd(0x00);//设置低列地址
30     Write_Cmd(0x10);//设置高列地址
31     Write_Cmd(0x40);//设置显示开始行 0-63
32     Write_Cmd(0xD9);//设置预充电时期
33     Write_Cmd(0x22);//充电时间
34     Write_Cmd(0xDB);//设置取消选择级别
35     Write_Cmd(0x20);//默认0x20 0.77xvcc
36     Write_Cmd(0xAF);//显示开启
37     
38     Oled_Off();
39     Oled_Clear();
40     Oled_On();
41 }
复制代码

  5.开关显示屏代码

复制代码
 1 void Oled_On(void)
 2 {
 3     Write_Cmd(0x8D);//操作充电泵
 4     Write_Cmd(0x14);//打开
 5     Write_Cmd(0xAF);
 6 }
 7 
 8 void Oled_Off(void)
 9 {
10     Write_Cmd(0x8D);
11     Write_Cmd(0x10);//关闭
12     Write_Cmd(0xAE);
13 }
复制代码

 

posted @   稚终KINO  阅读(878)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· Ollama——大语言模型本地部署的极速利器
· 使用C#创建一个MCP客户端
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· Windows编程----内核对象竟然如此简单?
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
点击右上角即可分享
微信分享提示