IIC协议与OLED

news2024/11/24 23:02:28

1.认识OLED

1、OLED概述:

  1. OLED(Organic Light-Emitting Diode,有机发光二极管)是一种显示技术,利用有机材料的发光特性来产生光。
  2. OLED显示器由一系列有机材料层组成,当电流通过时,这些材料会发光,每一个像素都是一个独立的发光元素,可以精准控制亮度和颜色。
  3. OLED相较于LCD有很多优势:①OLED显示器由于不需要背光源,所以可以实现自发光,从而可以实现非常高的对比度。②OLED显示器具有更广的可视角度、更快的相应时间和更薄的设计。
  4. OLED技术被应用于各种电子设备,比如智能手机、电视机、电子手表、虚拟现实设备等。它提供了更鲜亮、细腻和动态的图像。
  5. OLED是我们学习的第一个IIC协议外设,它仍然遵循IIC协议的起始、终止、应答、数据传输的标准时序。但是对OLED这个特定的设备,需要我们去编写和设备相关的代码。手册ssd1306.pdf是全英文、除了我们提到的IIC协议以外还可以支持多种协议(SPI、古老的多线协议等),我们之前的IIC时序图来源于叁议电子为我们从该手册中提取出来的有关IIC协议的那部分内容。

 

2、OLED的特点:

  1. 分辨率:128 segments × 64 commons 的点阵。SSD1306是一款具有控制器的单芯片CMOS OLED/PLED驱动,用于 有机/聚合物 发光二极管点阵图形显示系统。它由128个段和64个公共引脚组成。该集成电路是为共阴极型OLED面板设计的。
  2. SSD1306嵌入了对比度控制、显示RAM和振荡器,减少了外部元件的数量和功耗。它有256级亮度控制。数据/命令通过硬件可选的6800/8000系列兼容并行接口从通用MCU发送;I2C接口或串行外设接口。它适用于许多小型便携式应用,如手机子显示器、MP3播放器、计算器等。

2.开发逻辑1:OLED写模式

1、和单片机的接线方法:

 

  1. 电源线
    1. GND——GND
    2. VCC——5V
  2. IIC总线
    1. SCL——P0.1
    2. SDA——P0.3

2、写操作时序分析:如图所示是IIC协议数据总线上一帧数据的数据格式。

 

  1. 发送起始信号:调用IIC协议一节中封装的函数:IIC_Start();
  2. 确认从机地址,准备写入:发送的数据格式为:0111 10 [SA0] [R/W#]
    1. SA0 是从机地址位(slaver address),比如说我们的单片机接着两个显示屏,那么就可以通过SA0这个地址位来区分这两个显示屏
    2. R/W# 是读写选择位,该位为高电平代表读模式(类似于LCD的R/W位,在读取忙信号的时候始终将R/W“置1”),该位为低电平代表写模式(类似于LCD的R/W位,不论是写地址还是写数据,始终将R/W“置0”)。
    • 手册上告知我们SA0应该设置成0,另外我们告诉OLED准备开始写入,所以根据8421码,二进制的0111 1000就是十六进制的0x78。
  3. 应答信号ACK读取:调用IIC协议一节中封装的函数:IIC_ACK();
  4. 发送控制字节:[Co] [D/C#] 00 0000
    1. Co 是继续位(continuation),如果该位“置0”,那么接下来将要传输的信息会包含数据字节
    2. D/C# 是数据指令选择位(data/command selection),该位为高电平代表写入的是数据(类似于LCD的RS位,在写数据的时候将RS“置1”),该位为低电平代表写入的是指令(类似于LCD的RS位,在写位置指令的时候将RS“置0”)。注意当该位“置1”时,写入的数据会被存放在GDDRAM(图形数据存储器)中,GDDRAM的列地址指针在每次数据写入后自动加1。
    • 如果我们打算写入指令,那么根据8421码,二进制的0000 0000就是十六进制的0x00;
    • 如果我们打算写入数据,那么根据8421码,二进制的0100 0000就是十六进制的0x40;
  5. 应答信号ACK读取:调用IIC协议一节中封装的函数:IIC_ACK();
  6. 发送指令或者数据
  7. 应答信号ACK读取:调用IIC协议一节中封装的函数:IIC_ACK();
  8. 发送停止信号:调用IIC协议一节中封装的函数:IIC_Stop();

3、写指令函数封装:

void Oled_Write_Cmd(char dataCmd)
{
    IIC_Start();            //1.发送起始信号
    IIC_Send_Byte(0x78);    //2.确认从机地址,准备写入
    IIC_ACK();              //3.应答信号ACK读取
    IIC_Send_Byte(0x00);    //4.发送控制字节,确认发送指令
    IIC_ACK();              //5.应答信号ACK读取
    IIC_Send_Byte(dataCmd); //6.发送指令
    IIC_ACK();              //7.应答信号ACK读取
    IIC_Stop();             //8.发送停止信号
}

4、写数据函数封装:

void Oled_Write_Data(char dataData)
{
    IIC_Start();            //1.发送起始信号
    IIC_Send_Byte(0x78);    //2.确认从机地址,准备写入
    IIC_ACK();              //3.应答信号ACK读取
    IIC_Send_Byte(0x40);    //4.发送控制字节,确认发送数据
    IIC_ACK();              //5.应答信号ACK读取
    IIC_Send_Byte(dataData);//6.发送数据
    IIC_ACK();              //7.应答信号ACK读取
    IIC_Stop();             //8.发送停止信号
}

3.开发逻辑2:OLED寻址模式

1、OLED点阵的管理:搞清在哪显示

  • OLED的分辨率是128×64,那么用显示器的术语来说就是水平方向上有128段(segments),竖直方向上有64个公共引脚(commons)。这与电脑的分辨率类似,比如电脑的分辨率是1920×1080,那么水平方向上(宽度)就分布着1920个像素点,竖直方向上(高度)就分布着1080个像素点。
  • 在OLED竖直方向上将64个点阵分为8组page,从page0到page7,每页由8位像素点组成。这就像我们控制单片机开发板上面的LED需要配置寄存器一样,控制OLED上像素的亮灭也需要配置PAGE。但是要注意的是page是个常用的用于描述显示器分区的术语,并不是特殊的寄存器。
  • 备注:GDDRAMGraphic Display Data RAM,图形数据显示内存)是一个位映射静态内存,用与保存要显示的位模式,内存的大小就是128×64 bit,同时内存被细分成如上所述的8个page。
  • 如下是GDDRAM的局部放大图片,以PAGE2为例:
  • 当一个字节数据被写入进GDDRAM时,当前列中所有处于同一page的行图像数据都会被填充。OLED的每一页可以管理8行,我们以PAGE2为例,如图,在水平方向上就有128列,每列都有8个像素点,最上方的像素点是bit0,最下方的像素点是bit7。假设我们写入的数据是0x08,那么其二进制就是0000 1000,对应的就会点亮位于SEG0且处于PAGE2上bit4这一个像素点,同时熄灭其他像素点。
  • 这里可以画个大表格,可以帮助我们分析要显示的像素点:
    OLED:128×640列1列2列3列..............124列125列126列127列
    PAGE0bit00行
    bit11行
    bit22行
    bit33行
    bit44行
    bit55行
    bit66行
    bit77行
    PAGE18到15行
    PAGE216到23行
    PAGE324到31行
    PAGE432到39行
    PAGE540到47行
    PAGE648到55行
    PAGE756到63行

 

 

2、三种内存寻址模式:搞清显示地址指针移动方式

  • 目的:不论OLED上显示的是什么,我们首要的工作是做到如何点亮OLED上的一个点,步骤是先选一个page,然后向其发送一个字节数据,点亮page的对应位置。而在此之前,我们需要先了解地址指针在内存GDDRAM中的移动方式。OLED对page有三种寻址办法,如下:
  1. 页寻址模式:图中的箭头是页寻址模式下地址指针的移动方向,以在PAGE0上写入数据0x80为例,如果写入1次,那么仅能点亮COL0上处于PAGE0的bit4这一个像素;在这种寻址模式下,GDDRAM的列地址指针在每次数据写入后会自动加1,如果写入128次,那么就能显示水平的一条线,并且PAGE0中的COL127写完后又会回到PAGE0的COL0。
  2. 水平寻址模式:图中的箭头是水平寻址模式下地址指针的移动方向,在这种寻址模式下,GDDRAM的列地址指针在每次数据写入后会自动加1,但是在当前PAGE的最后一列写完之后,也就是COL127写完之后,地址指针会移动到下一页的首列,比如PAGE0的COL127写完之后,地址指针会移动到PAGE1的COL0。
  3. 垂直寻址模式:图中的箭头是垂直寻址模式下地址指针的移动方向,这种模式用得很少。

 

 

 

3、发送指令确认寻址模式:搞清如何显示,查阅地址设置控制表。

  • 确认寻址模式需要发送长度为两个字节的指令
  1. 指令0x20用于设置存储器。
  2. 指令00b(0x00)用于设置水平寻址模式、指令01b(0x01)用于设置垂直寻址模式、指令10b(0x02)用于设置页寻址模式(默认)。

 

4、发送指令选择特定页:搞清如何显示,查阅地址设置控制表。

  1. 指令的格式为:1011 0 [X2] [X1] [X0] ,写入的指令是0xB0~0xB7。
  • 因为指令的低三位是可编程,的并且23有8种选择,所以通过发送该指令可以指定GDDRAM中的PAGE的开始地址,比如说1011 0000代表的就是PAGE0
  • 注意:这种指令只适合用于页寻址模式。

 

5、发送指令选择特定段:搞清如何显示,查阅地址设置控制表。

  • 目的:当我们在某一个PAGE中写入了几个数据之后,重新再往新的PAGE里面写入数据,此时列地址仍然往后面偏移1,所以不是从新的PAGE的开始位置显示。因此如果想从新的PAGE的特定列(比如COL0)开始输入数据,那么就需要发送指令来选择特定列。
  • 查表可见,确认特定列需要发送长度为两个字节的指令
  1. 确认低地址:0000 [X3] [X2] [X1] [X0],写入的指令是0x00~0xF。
  2. 确认高地址:0001 [X3] [X2] [X1] [X0],写入的指令是0x10~0x7。
  • 128列地址需要通过7位二进制数(27=128)来表示。由于只有128列,也就是说只需要7位二进制数就能表示全部的列,所以高地址的[X3]不能被使用,需要设置为0。
  • 这种指令只适用于页寻址模式。

 

4.开发逻辑3:OLED初始化

1、OLED初始化函数封装:叁议电子为我们整理了如下初始化过程

(01)display off (0xae)

(02)set low column address (0x00)

(03)set high column address (0x10)

(04)set start line address (0x40)

(05)set page address (0xb0)

(06)contract control (0x81)

(07)send 0xff(多字节指令)

(08)set segment remap (0xa1)

(09)set normal/reverse (0xa6)

(10)set multiplex ratio (1 to 64) (0xa8)

(11)set duty 1/32 (0x3f)

(12)com scan direction (0xc8)

(13)set display offset (0xd3)

(14)send 0x00 

(15)set osc division(0xd5)

(16)send 0x80

(17)set area color mode off (0xd8)

(18)send 0x05

(19)set pre-charge period (0xd9)

(20)send 0xf1

(21)set com pin configuration (0xda)

(22)send 0x12

(23)set Vcomh(0xdb)

(24)send0x30

(25)set charge pump enable(0x8d)

(26)send 0x14

(27)turn on oled panel(0xaf)

void Oled_Init()
{
    Oled_Write_Cmd(0xAE);   //(01)display off (0xae)
    Oled_Write_Cmd(0x00);   //(02)set low column address (0x00)
    Oled_Write_Cmd(0x10);   //(03)set high column address (0x10)
    Oled_Write_Cmd(0x40);   //(04)set start line address (0x40)
    Oled_Write_Cmd(0xB0);   //(05)set page address (0xb0)
    Oled_Write_Cmd(0x81);   //(06)contract control (0x81)
    Oled_Write_Cmd(0xFF);   //(07)send 0xff(多字节指令)
    Oled_Write_Cmd(0xA1);   //(08)set segment remap (0xa1)
    Oled_Write_Cmd(0xA6);   //(09)set normal/reverse (0xa6)
    Oled_Write_Cmd(0xA8);   //(10)set multiplex ratio (1 to 64) (0xa8)
    Oled_Write_Cmd(0x3F);   //(11)set duty 1/32 (0x3f)
    Oled_Write_Cmd(0xC8);   //(12)com scan direction (0xc8)
    Oled_Write_Cmd(0xD3);   //(13)set display offset (0xd3)
    Oled_Write_Cmd(0x00);   //(14)send 0x00 
    Oled_Write_Cmd(0xD5);   //(15)set osc division(0xd5)
    Oled_Write_Cmd(0x80);   //(16)send 0x80
    Oled_Write_Cmd(0xD8);   //(17)set area color mode off (0xd8)
    Oled_Write_Cmd(0x05);   //(18)send 0x05
    Oled_Write_Cmd(0xD9);   //(19)set pre-charge period (0xd9)
    Oled_Write_Cmd(0xF1);   //(20)send 0xf1
    Oled_Write_Cmd(0xDA);   //(21)set com pin configuration (0xda)
    Oled_Write_Cmd(0x12);   //(22)send 0x12
    Oled_Write_Cmd(0xDB);   //(23)set Vcomh(0xdb)
    Oled_Write_Cmd(0x30);   //(24)send0x30
    Oled_Write_Cmd(0x8D);   //(25)set charge pump enable(0x8d)
    Oled_Write_Cmd(0x14);   //(26)send 0x14
    Oled_Write_Cmd(0xAF);   //(27)turn on oled panel(0xaf)
}

5.开发逻辑4:OLED清屏

1、OLED添加清屏函数:

  • 原因:为了在OLED屏幕上显示别的图案,我们去修改程序然后重新烧录,这时重新给OLED上电会发现:如果没有对OLED的清屏操作,那么上一次的显示结果会再次显示在屏幕上。这是因为:我们给OLED写数据的时候,实际上是给OLED的GDDRAM这个内存中写数据,OLED的这个内存中的数据实际上断电后是会保留的。
  • 代码:在我们实现了对OLED的每一个PAGE、每一个列的访问之后,接下来就可以通过循环嵌套熄灭128×64中所有的像素点,来实现我们的清屏函数。
    void Oled_Clear()
    {
        int i;
        int j;
        for(i=0; i<8; i++){
            Oled_Write_Cmd(0xB0 + i); // 依次指定PAGE0~PAGE7
            // 指定SEG0
            Oled_Write_Cmd(0x00);
            Oled_Write_Cmd(0x10);
            // 0到127列,每次写入0,每次写入数据列地址自动偏移
            for(j=0; j<128; j++){
                Oled_Write_Data(0);
            }
        }
    }

6.OLED显示简单的点和线

1、OLED雪花BUG:

 

  1. 现象:给OLED上电后直接显示雪花,或者有时候一行横线能够显示但是其他地方出现雪花。
  2. 原因
    1. 原因1:上一次烧录程序后出现了雪花,这一次烧录程序时忘记调用清屏函数进行屏幕清除。
    2. 原因2:在清屏函数中,如果我们为了省空间去将循环变量 i 和 j 的类型从int改变为char,那么也会导致雪花的出现。这是因为字符型变量的取值范围是﹣128 ~ +127,那么无疑的是在清屏函数的内层循环中,循环变量 j 会发生一次越界,j 最终的值会在+128, 导致出问题。
    3. 原因2:我们一开始在写IIC协议的起始和终止信号函数时,起始信号函数一开始没有将SCL“置0”,终止信号函数的结尾没有将SCL“置1”。——>  回顾我们本节封装的写指令和写数据函数,在写入的一帧数据中都要在数据发送前发送起始信号,在数据发送后发送终止信号。——>  那么我们在第一条指令或数据发送完毕后,IIC上的SCL总线的电平仍然是1。——>  同时我们知道“在SCL为高电平时出现了SDA上数据的翻转,那么IIC接收器会误以为这是起始信号或终止信号”,这就出现了矛盾。——>  导致OLED无法正确识别被写入的内容,最终导致OLED屏幕上的雪花出现。
  3. 解决方法1:清屏函数中,在定义循环变量 i 和 j 的时候,不要用char来定义,用int 或者 unsigned char
  4. 解决方法2:修改IIC协议的起始信号
    void IIC_Start()
    {
        scl = 0;    // 防止OLED雪花
        // o点:
        scl = 1;    // SCL置高电平
        sda = 0;    // SDA置低电平
        // a点:
                    // SCL维持高电平
        sda = 1;    // SDA从低电平转折为高电平,时间未知
        // a—>b:
                    // SCL维持高电平
        _nop_();    // SDA延时4.7微秒
        // b—>c:
                    // SCL维持高电平
        sda = 0;    // SDA从高电平转折为低电平,时间未知
        // c—>d:
        _nop_();    // SCL延时4微秒
                    // SDA维持低电平
        // d点:
        scl = 0;    // SCL从高电平转折为低电平,时间未知
                    // SDA维持低电平
    }

2、OLED显示一个点及一行水平线:

 

  • 代码:其中API 1~API 8 是通用代码7.OLED显示一个字符
    #include "reg52.h"
    #include "intrins.h"
    
    sbit scl = P0^1;
    sbit sda = P0^3;
    
    /* API1. 启动I2C */
    void IIC_Start();
    /* API2. 终止I2C */
    void IIC_Stop();
    /* API3. 获取IIC的应答位 */
    char IIC_ACK();
    /* API4. I2C发送1字节数据 */
    void IIC_Send_Byte(char cdata);
    /* API5. OLED写指令(1Byte) */
    void Oled_Write_Cmd(char dataCmd);
    /* API6. OLED写数据(1Byte) */
    void Oled_Write_Data(char dataData);
    /* API7. OLED初始化 */ 
    void Oled_Init();
    /* API8. OLED清屏 */
    void Oled_Clear();
    
    void main(void)
    {
        Oled_Init();
        // 设置页寻址模式
        Oled_Write_Cmd(0x20);
        Oled_Write_Cmd(0x02);
        // 清屏
        Oled_Clear();
    
        // 指定PAGE0
        Oled_Write_Cmd(0xB0);
        // 指定SEG0
        Oled_Write_Cmd(0x00);
        Oled_Write_Cmd(0x10);
        // 写数据,显示一个点
        Oled_Write_Data(0x08);
    
        // 指定PAGE1
        Oled_Write_Cmd(0xB1);
        // 指定SEG0
        Oled_Write_Cmd(0x00);
        Oled_Write_Cmd(0x10);
        // 写数据,显示一条线
        Oled_Write_Data(0x08);
        Oled_Write_Data(0x08);
        Oled_Write_Data(0x08);
        Oled_Write_Data(0x08);
        Oled_Write_Data(0x08);
    
        while(1);   //防止程序退出
    }
    
    void IIC_Start()
    {
        scl = 0;    // 防止OLED雪花
        // o点:
        scl = 1;    // SCL置高电平
        sda = 0;    // SDA置低电平
        // a点:
                    // SCL维持高电平
        sda = 1;    // SDA从低电平转折为高电平,时间未知
        // a—>b:
                    // SCL维持高电平
        _nop_();    // SDA延时4.7微秒
        // b—>c:
                    // SCL维持高电平
        sda = 0;    // SDA从高电平转折为低电平,时间未知
        // c—>d:
        _nop_();    // SCL延时4微秒
                    // SDA维持低电平
        // d点:
        scl = 0;    // SCL从高电平转折为低电平,时间未知
                    // SDA维持低电平
    }
    
    void IIC_Stop()
    {
        // o点:
        scl = 0;    // SCL置低电平
        sda = 0;    // SDA置低电平
        // a点:
        scl = 1;    // SCL从低电平转折为高电平,时间未知
                    // SDA维持低电平
        // a—>b:
                    // SCL维持高电平
        _nop_();    // SDA延时4微秒
        // b—>c:
                    // SCL维持高电平
        sda = 1;    // SDA从低电平折为高电平,时间未知
        // c—>d:
                    // SCL维持低高电平
        _nop_();    // SDA延时4.7微秒
        // d点:
                    // SCL维持高电平
        sda = 0;    // SDA从高电平转折为低电平,时间未知
    }
    
    char IIC_ACK()
    {
        char flag;
        // o点:
                    // SCL置低电平
        sda = 1;    // SDA:在主控设备发送完成一个字节数据后,就立马发送一个高电平,代表释放SDA数据线
        // o—>a:
        _nop_();    // SCL延时
        // a点:
        scl = 1;    // SCL从低电平转折为高电平,代表释放SCL数据线
        // a—>b:
        _nop_();    // SCL延时4微秒,因为读取SDA的时候一定要保证SCL处于高电平的状态
        flag = sda; // SDA被读
        _nop_();    // SCL延时4微秒,因为读取SDA的时候一定要保证SCL处于高电平的状态
        // b点:
        scl = 0;    // SCL从高电平转折为低电平
        // b—>c:
        _nop_();    // SCL延时
        return flag;
    }
    
    void IIC_Send_Byte(char cdata)
    {
        int i;
        for(i=0; i<8; i++){
            // o点:
            scl = 0;            // SCL置低电平:允许数据翻转
            sda = cdata & 0x80; // SDA发送1bit数据
            // o—>a:
            _nop_();            // SCL延时5微秒
                                // SDA正在发送数据
            // a点:
            scl = 1;            // SCL从低电平转折为高电平
            // a—>b:
            _nop_();            // SCL延时5微秒
                                // SDA正在发送数据
            // b点:
            scl = 0;            // SCL从高电平转折为低电平:为下一次数据翻转做准备
            // b—>c:
            _nop_();            // SCL延时5微秒
            cdata = cdata << 1;
        }
    }
    
    void Oled_Write_Cmd(char dataCmd)
    {
        IIC_Start();            //发送起始信号
        IIC_Send_Byte(0x78);    //确认从机地址,准备写入
        IIC_ACK();              //应答信号ACK读取
        IIC_Send_Byte(0x00);    //发送控制字节,确认发送指令
        IIC_ACK();              //应答信号ACK读取
        IIC_Send_Byte(dataCmd); //发送指令
        IIC_ACK();              //应答信号ACK读取
        IIC_Stop();             //发送停止信号
    }
    
    void Oled_Write_Data(char dataData)
    {
        IIC_Start();            //发送起始信号
        IIC_Send_Byte(0x78);    //确认从机地址,准备写入
        IIC_ACK();              //应答信号ACK读取
        IIC_Send_Byte(0x40);    //发送控制字节,确认发送数据
        IIC_ACK();              //应答信号ACK读取
        IIC_Send_Byte(dataData);//发送数据
        IIC_ACK();              //应答信号ACK读取
        IIC_Stop();             //发送停止信号
    }
    
    void Oled_Init()
    {
        Oled_Write_Cmd(0xAE);   //(01)display off (0xae)
        Oled_Write_Cmd(0x00);   //(02)set low column address (0x00)
        Oled_Write_Cmd(0x10);   //(03)set high column address (0x10)
        Oled_Write_Cmd(0x40);   //(04)set start line address (0x40)
        Oled_Write_Cmd(0xB0);   //(05)set page address (0xb0)
        Oled_Write_Cmd(0x81);   //(06)contract control (0x81)
        Oled_Write_Cmd(0xFF);   //(07)send 0xff(多字节指令)
        Oled_Write_Cmd(0xA1);   //(08)set segment remap (0xa1)
        Oled_Write_Cmd(0xA6);   //(09)set normal/reverse (0xa6)
        Oled_Write_Cmd(0xA8);   //(10)set multiplex ratio (1 to 64) (0xa8)
        Oled_Write_Cmd(0x3F);   //(11)set duty 1/32 (0x3f)
        Oled_Write_Cmd(0xC8);   //(12)com scan direction (0xc8)
        Oled_Write_Cmd(0xD3);   //(13)set display offset (0xd3)
        Oled_Write_Cmd(0x00);   //(14)send 0x00 
        Oled_Write_Cmd(0xD5);   //(15)set osc division(0xd5)
        Oled_Write_Cmd(0x80);   //(16)send 0x80
        Oled_Write_Cmd(0xD8);   //(17)set area color mode off (0xd8)
        Oled_Write_Cmd(0x05);   //(18)send 0x05
        Oled_Write_Cmd(0xD9);   //(19)set pre-charge period (0xd9)
        Oled_Write_Cmd(0xF1);   //(20)send 0xf1
        Oled_Write_Cmd(0xDA);   //(21)set com pin configuration (0xda)
        Oled_Write_Cmd(0x12);   //(22)send 0x12
        Oled_Write_Cmd(0xDB);   //(23)set Vcomh(0xdb)
        Oled_Write_Cmd(0x30);   //(24)send0x30
        Oled_Write_Cmd(0x8D);   //(25)set charge pump enable(0x8d)
        Oled_Write_Cmd(0x14);   //(26)send 0x14
        Oled_Write_Cmd(0xAF);   //(27)turn on oled panel(0xaf)
    }
    
    void Oled_Clear()
    {
        int i;
        int j;
        for(i=0; i<8; i++){
            Oled_Write_Cmd(0xB0 + i); // 依次指定PAGE0~PAGE7
            // 指定SEG0
            Oled_Write_Cmd(0x00);
            Oled_Write_Cmd(0x10);
            // 0到127列,每次写入0,每次写入数据列地址自动偏移
            for(j=0; j<128; j++){
                Oled_Write_Data(0);
            }
        }
    }

7.OLED显示一个字符

1、字模软件的使用:

 

 

 

  • 目的:在点阵屏幕上显示字符比较复杂,我们需要借助字模软件来帮助我们实现点阵的规划。
  • 字符取模步骤:
  • 拷贝结果:

    /*--  文字:  A  --*/

    /*--  宋体12;  此字体下对应的点阵为:宽x高=8x16   --*/

    0x00,0x00,0xC0,0x38,0xE0,0x00,0x00,0x00,0x20,0x3C,0x23,0x02,0x02,0x27,0x38,0x20,

  • 对于我们输入的文字A(字体样式为:宋体12),生成的点阵宽为8、高为16。这就意味着需要用到2个PAGE,那么我们就要将这个数据拆分成两个字符数组:

  • 数组1:0x00,0x00,0xC0,0x38,0xE0,0x00,0x00,0x00
  • 数组2:0x20,0x3C,0x23,0x02,0x02,0x27,0x38,0x20

2、字符'A'的点阵规划:

PAGE000000000
00000000
00000000
00010000
00010000
00011000
00101000
00101000
PAGE100100100
00111100
01000100
01000010
01000010
11100111
00000000
00000000

3、OLED显示一个字符‘A’: 

 

代码:其中API 1~API 8 是通用代码

#include "reg52.h"
#include "intrins.h"

sbit scl = P0^1;
sbit sda = P0^3;

/* API1. 启动I2C */
void IIC_Start();
/* API2. 终止I2C */
void IIC_Stop();
/* API3. 获取IIC的应答位 */
char IIC_ACK();
/* API4. I2C发送1字节数据 */
void IIC_Send_Byte(char cdata);
/* API5. OLED写指令(1Byte) */
void Oled_Write_Cmd(char dataCmd);
/* API6. OLED写数据(1Byte) */
void Oled_Write_Data(char dataData);
/* API7. OLED初始化 */ 
void Oled_Init();
/* API8. OLED清屏 */
void Oled_Clear();
/* API9. OLED显示字符'A' */
void Oled_Show_Char();

/*--  文字:  A  --*/
/*--  宋体12;  此字体下对应的点阵为:宽x高=8x16,用到两个PAGE,所以拆分成两个字符数组   --*/
char A1[8] = {0x00,0x00,0xC0,0x38,0xE0,0x00,0x00,0x00};
char A2[8] = {0x20,0x3C,0x23,0x02,0x02,0x27,0x38,0x20};

void main(void)
{
    int i;
    Oled_Init();
    // 设置页寻址模式
    Oled_Write_Cmd(0x20);
    Oled_Write_Cmd(0x02);
    // 清屏
    Oled_Clear();
    
    Oled_Show_Char();

    while(1);   //防止程序退出
}

void IIC_Start()
{
    scl = 0;    // 防止OLED雪花
    // o点:
    scl = 1;    // SCL置高电平
    sda = 0;    // SDA置低电平
    // a点:
                // SCL维持高电平
    sda = 1;    // SDA从低电平转折为高电平,时间未知
    // a—>b:
                // SCL维持高电平
    _nop_();    // SDA延时4.7微秒
    // b—>c:
                // SCL维持高电平
    sda = 0;    // SDA从高电平转折为低电平,时间未知
    // c—>d:
    _nop_();    // SCL延时4微秒
                // SDA维持低电平
    // d点:
    scl = 0;    // SCL从高电平转折为低电平,时间未知
                // SDA维持低电平
}

void IIC_Stop()
{
    // o点:
    scl = 0;    // SCL置低电平
    sda = 0;    // SDA置低电平
    // a点:
    scl = 1;    // SCL从低电平转折为高电平,时间未知
                // SDA维持低电平
    // a—>b:
                // SCL维持高电平
    _nop_();    // SDA延时4微秒
    // b—>c:
                // SCL维持高电平
    sda = 1;    // SDA从低电平折为高电平,时间未知
    // c—>d:
                // SCL维持低高电平
    _nop_();    // SDA延时4.7微秒
    // d点:
                // SCL维持高电平
    sda = 0;    // SDA从高电平转折为低电平,时间未知
}

char IIC_ACK()
{
    char flag;
    // o点:
                // SCL置低电平
    sda = 1;    // SDA:在主控设备发送完成一个字节数据后,就立马发送一个高电平,代表释放SDA数据线
    // o—>a:
    _nop_();    // SCL延时
    // a点:
    scl = 1;    // SCL从低电平转折为高电平,代表释放SCL数据线
    // a—>b:
    _nop_();    // SCL延时4微秒,因为读取SDA的时候一定要保证SCL处于高电平的状态
    flag = sda; // SDA被读
    _nop_();    // SCL延时4微秒,因为读取SDA的时候一定要保证SCL处于高电平的状态
    // b点:
    scl = 0;    // SCL从高电平转折为低电平
    // b—>c:
    _nop_();    // SCL延时
    return flag;
}

void IIC_Send_Byte(char cdata)
{
    int i;
    for(i=0; i<8; i++){
        // o点:
        scl = 0;            // SCL置低电平:允许数据翻转
        sda = cdata & 0x80; // SDA发送1bit数据
        // o—>a:
        _nop_();            // SCL延时5微秒
                            // SDA正在发送数据
        // a点:
        scl = 1;            // SCL从低电平转折为高电平
        // a—>b:
        _nop_();            // SCL延时5微秒
                            // SDA正在发送数据
        // b点:
        scl = 0;            // SCL从高电平转折为低电平:为下一次数据翻转做准备
        // b—>c:
        _nop_();            // SCL延时5微秒
        cdata = cdata << 1;
    }
}

void Oled_Write_Cmd(char dataCmd)
{
    IIC_Start();            //发送起始信号
    IIC_Send_Byte(0x78);    //确认从机地址,准备写入
    IIC_ACK();              //应答信号ACK读取
    IIC_Send_Byte(0x00);    //发送控制字节,确认发送指令
    IIC_ACK();              //应答信号ACK读取
    IIC_Send_Byte(dataCmd); //发送指令
    IIC_ACK();              //应答信号ACK读取
    IIC_Stop();             //发送停止信号
}

void Oled_Write_Data(char dataData)
{
    IIC_Start();            //发送起始信号
    IIC_Send_Byte(0x78);    //确认从机地址,准备写入
    IIC_ACK();              //应答信号ACK读取
    IIC_Send_Byte(0x40);    //发送控制字节,确认发送数据
    IIC_ACK();              //应答信号ACK读取
    IIC_Send_Byte(dataData);//发送数据
    IIC_ACK();              //应答信号ACK读取
    IIC_Stop();             //发送停止信号
}

void Oled_Init()
{
    Oled_Write_Cmd(0xAE);   //(01)display off (0xae)
    Oled_Write_Cmd(0x00);   //(02)set low column address (0x00)
    Oled_Write_Cmd(0x10);   //(03)set high column address (0x10)
    Oled_Write_Cmd(0x40);   //(04)set start line address (0x40)
    Oled_Write_Cmd(0xB0);   //(05)set page address (0xb0)
    Oled_Write_Cmd(0x81);   //(06)contract control (0x81)
    Oled_Write_Cmd(0xFF);   //(07)send 0xff(多字节指令)
    Oled_Write_Cmd(0xA1);   //(08)set segment remap (0xa1)
    Oled_Write_Cmd(0xA6);   //(09)set normal/reverse (0xa6)
    Oled_Write_Cmd(0xA8);   //(10)set multiplex ratio (1 to 64) (0xa8)
    Oled_Write_Cmd(0x3F);   //(11)set duty 1/32 (0x3f)
    Oled_Write_Cmd(0xC8);   //(12)com scan direction (0xc8)
    Oled_Write_Cmd(0xD3);   //(13)set display offset (0xd3)
    Oled_Write_Cmd(0x00);   //(14)send 0x00 
    Oled_Write_Cmd(0xD5);   //(15)set osc division(0xd5)
    Oled_Write_Cmd(0x80);   //(16)send 0x80
    Oled_Write_Cmd(0xD8);   //(17)set area color mode off (0xd8)
    Oled_Write_Cmd(0x05);   //(18)send 0x05
    Oled_Write_Cmd(0xD9);   //(19)set pre-charge period (0xd9)
    Oled_Write_Cmd(0xF1);   //(20)send 0xf1
    Oled_Write_Cmd(0xDA);   //(21)set com pin configuration (0xda)
    Oled_Write_Cmd(0x12);   //(22)send 0x12
    Oled_Write_Cmd(0xDB);   //(23)set Vcomh(0xdb)
    Oled_Write_Cmd(0x30);   //(24)send0x30
    Oled_Write_Cmd(0x8D);   //(25)set charge pump enable(0x8d)
    Oled_Write_Cmd(0x14);   //(26)send 0x14
    Oled_Write_Cmd(0xAF);   //(27)turn on oled panel(0xaf)
}

void Oled_Clear()
{
    int i;
    int j;
    for(i=0; i<8; i++){
        Oled_Write_Cmd(0xB0 + i); // 依次指定PAGE0~PAGE7
        // 指定SEG0
        Oled_Write_Cmd(0x00);
        Oled_Write_Cmd(0x10);
        // 0到127列,每次写入0,每次写入数据列地址自动偏移
        for(j=0; j<128; j++){
            Oled_Write_Data(0);
        }
    }
}

void Oled_Show_Char()
{
    /* 显示字符'A' */
    // 指定PAGE0
    Oled_Write_Cmd(0xB0);
    // 指定SEG0
    Oled_Write_Cmd(0x00);
    Oled_Write_Cmd(0x10);
    // 写数据,显示字符'A'的上半部分
    for(i=0; i<8; i++){
        Oled_Write_Data(A1[i]);
    }
    // 指定PAGE1
    Oled_Write_Cmd(0xB1);
    // 指定SEG0
    Oled_Write_Cmd(0x00);
    Oled_Write_Cmd(0x10);
    // 写数据,显示字符'A'的下半部分
    for(i=0; i<8; i++){
        Oled_Write_Data(A2[i]);
    }
}

8.OLED显示一串汉字

1、汉字字模提取:

  • 一个汉字的宽度是16,高度是16,所以也需要用到2个PAGE,那么我们就要将这个数据拆分成两个字符数组。

2、OLED显示“嵌入式软件”:

  1. 思路:先在PAGE 0中统一显示一串汉字的上半部分,再在PAGE 1中统一显示一串汉字的下半部分。
  2. 代码:其中API 1~API 8 是通用代码
    #include "reg52.h"
    #include "intrins.h"
    
    sbit scl = P0^1;
    sbit sda = P0^3;
    
    /* API1. 启动I2C */
    void IIC_Start();
    /* API2. 终止I2C */
    void IIC_Stop();
    /* API3. 获取IIC的应答位 */
    char IIC_ACK();
    /* API4. I2C发送1字节数据 */
    void IIC_Send_Byte(char cdata);
    /* API5. OLED写指令(1Byte) */
    void Oled_Write_Cmd(char dataCmd);
    /* API6. OLED写数据(1Byte) */
    void Oled_Write_Data(char dataData);
    /* API7. OLED初始化 */ 
    void Oled_Init();
    /* API8. OLED清屏 */
    void Oled_Clear();
    /* API9. OLED显示汉字 */
    void Oled_Show_Hanzi();
    
    /*--  文字:  嵌  --*/
    /*--  宋体12;  此字体下对应的点阵为:宽x高=16x16   --*/
    code char qian1[16] = {0x80,0x80,0xEE,0x88,0x88,0x88,0xE8,0x8F,0x08,0x88,0x78,0x48,0x4E,0x40,0xC0,0x00};
    code char qian2[16] = {0x00,0x00,0x7F,0x24,0x24,0x24,0x7F,0x00,0x81,0x40,0x30,0x0F,0x30,0x41,0x80,0x00};
    
    /*--  文字:  入  --*/
    /*--  宋体12;  此字体下对应的点阵为:宽x高=16x16   --*/
    code char ru1[16] = {0x00,0x00,0x00,0x00,0x00,0x01,0xE2,0x1C,0xE0,0x00,0x00,0x00,0x00,0x00,0x00,0x00};
    code char ru2[16] = {0x80,0x40,0x20,0x10,0x0C,0x03,0x00,0x00,0x00,0x03,0x0C,0x30,0x40,0x80,0x80,0x00};
    
    /*--  文字:  式  --*/
    /*--  宋体12;  此字体下对应的点阵为:宽x高=16x16   --*/
    code char shi1[16] = {0x10,0x10,0x90,0x90,0x90,0x90,0x90,0x10,0x10,0xFF,0x10,0x10,0x11,0x16,0x10,0x00};
    code char shi2[16] = {0x00,0x20,0x60,0x20,0x3F,0x10,0x10,0x10,0x00,0x03,0x0C,0x10,0x20,0x40,0xF8,0x00};
    
    /*--  文字:  软  --*/
    /*--  宋体12;  此字体下对应的点阵为:宽x高=16x16   --*/
    code char ruan1[16] = {0x08,0xC8,0xB8,0x8F,0xE8,0x88,0x88,0x40,0x30,0x0F,0xC8,0x08,0x28,0x18,0x00,0x00};
    code char ruan2[16] = {0x08,0x18,0x08,0x08,0xFF,0x04,0x84,0x40,0x30,0x0E,0x01,0x0E,0x30,0x40,0x80,0x00};
    
    /*--  文字:  件  --*/
    /*--  宋体12;  此字体下对应的点阵为:宽x高=16x16   --*/
    code char jian1[16] = {0x00,0x80,0x60,0xF8,0x07,0x80,0x60,0x1C,0x10,0x10,0xFF,0x10,0x10,0x10,0x00,0x00};
    code char jian2[16] = {0x01,0x00,0x00,0xFF,0x00,0x02,0x02,0x02,0x02,0x02,0xFF,0x02,0x02,0x02,0x02,0x00};
    
    void main(void)
    {
        unsigned char i;
        Oled_Init();
        // 设置页寻址模式
        Oled_Write_Cmd(0x20);
        Oled_Write_Cmd(0x02);
        // 清屏
        Oled_Clear();
    
        Oled_Show_Hanzi();
    
        while(1);   //防止程序退出
    }
    
    void IIC_Start()
    {
        scl = 0;    // 防止OLED雪花
        // o点:
        scl = 1;    // SCL置高电平
        sda = 0;    // SDA置低电平
        // a点:
                    // SCL维持高电平
        sda = 1;    // SDA从低电平转折为高电平,时间未知
        // a—>b:
                    // SCL维持高电平
        _nop_();    // SDA延时4.7微秒
        // b—>c:
                    // SCL维持高电平
        sda = 0;    // SDA从高电平转折为低电平,时间未知
        // c—>d:
        _nop_();    // SCL延时4微秒
                    // SDA维持低电平
        // d点:
        scl = 0;    // SCL从高电平转折为低电平,时间未知
                    // SDA维持低电平
    }
    
    void IIC_Stop()
    {
        // o点:
        scl = 0;    // SCL置低电平
        sda = 0;    // SDA置低电平
        // a点:
        scl = 1;    // SCL从低电平转折为高电平,时间未知
                    // SDA维持低电平
        // a—>b:
                    // SCL维持高电平
        _nop_();    // SDA延时4微秒
        // b—>c:
                    // SCL维持高电平
        sda = 1;    // SDA从低电平折为高电平,时间未知
        // c—>d:
                    // SCL维持低高电平
        _nop_();    // SDA延时4.7微秒
        // d点:
                    // SCL维持高电平
        sda = 0;    // SDA从高电平转折为低电平,时间未知
    }
    
    char IIC_ACK()
    {
        char flag;
        // o点:
                    // SCL置低电平
        sda = 1;    // SDA:在主控设备发送完成一个字节数据后,就立马发送一个高电平,代表释放SDA数据线
        // o—>a:
        _nop_();    // SCL延时
        // a点:
        scl = 1;    // SCL从低电平转折为高电平,代表释放SCL数据线
        // a—>b:
        _nop_();    // SCL延时4微秒,因为读取SDA的时候一定要保证SCL处于高电平的状态
        flag = sda; // SDA被读
        _nop_();    // SCL延时4微秒,因为读取SDA的时候一定要保证SCL处于高电平的状态
        // b点:
        scl = 0;    // SCL从高电平转折为低电平
        // b—>c:
        _nop_();    // SCL延时
        return flag;
    }
    
    void IIC_Send_Byte(char cdata)
    {
        int i;
        for(i=0; i<8; i++){
            // o点:
            scl = 0;            // SCL置低电平:允许数据翻转
            sda = cdata & 0x80; // SDA发送1bit数据
            // o—>a:
            _nop_();            // SCL延时5微秒
                                // SDA正在发送数据
            // a点:
            scl = 1;            // SCL从低电平转折为高电平
            // a—>b:
            _nop_();            // SCL延时5微秒
                                // SDA正在发送数据
            // b点:
            scl = 0;            // SCL从高电平转折为低电平:为下一次数据翻转做准备
            // b—>c:
            _nop_();            // SCL延时5微秒
            cdata = cdata << 1;
        }
    }
    
    void Oled_Write_Cmd(char dataCmd)
    {
        IIC_Start();            //发送起始信号
        IIC_Send_Byte(0x78);    //确认从机地址,准备写入
        IIC_ACK();              //应答信号ACK读取
        IIC_Send_Byte(0x00);    //发送控制字节,确认发送指令
        IIC_ACK();              //应答信号ACK读取
        IIC_Send_Byte(dataCmd); //发送指令
        IIC_ACK();              //应答信号ACK读取
        IIC_Stop();             //发送停止信号
    }
    
    void Oled_Write_Data(char dataData)
    {
        IIC_Start();            //发送起始信号
        IIC_Send_Byte(0x78);    //确认从机地址,准备写入
        IIC_ACK();              //应答信号ACK读取
        IIC_Send_Byte(0x40);    //发送控制字节,确认发送数据
        IIC_ACK();              //应答信号ACK读取
        IIC_Send_Byte(dataData);//发送数据
        IIC_ACK();              //应答信号ACK读取
        IIC_Stop();             //发送停止信号
    }
    
    void Oled_Init()
    {
        Oled_Write_Cmd(0xAE);   //(01)display off (0xae)
        Oled_Write_Cmd(0x00);   //(02)set low column address (0x00)
        Oled_Write_Cmd(0x10);   //(03)set high column address (0x10)
        Oled_Write_Cmd(0x40);   //(04)set start line address (0x40)
        Oled_Write_Cmd(0xB0);   //(05)set page address (0xb0)
        Oled_Write_Cmd(0x81);   //(06)contract control (0x81)
        Oled_Write_Cmd(0xFF);   //(07)send 0xff(多字节指令)
        Oled_Write_Cmd(0xA1);   //(08)set segment remap (0xa1)
        Oled_Write_Cmd(0xA6);   //(09)set normal/reverse (0xa6)
        Oled_Write_Cmd(0xA8);   //(10)set multiplex ratio (1 to 64) (0xa8)
        Oled_Write_Cmd(0x3F);   //(11)set duty 1/32 (0x3f)
        Oled_Write_Cmd(0xC8);   //(12)com scan direction (0xc8)
        Oled_Write_Cmd(0xD3);   //(13)set display offset (0xd3)
        Oled_Write_Cmd(0x00);   //(14)send 0x00 
        Oled_Write_Cmd(0xD5);   //(15)set osc division(0xd5)
        Oled_Write_Cmd(0x80);   //(16)send 0x80
        Oled_Write_Cmd(0xD8);   //(17)set area color mode off (0xd8)
        Oled_Write_Cmd(0x05);   //(18)send 0x05
        Oled_Write_Cmd(0xD9);   //(19)set pre-charge period (0xd9)
        Oled_Write_Cmd(0xF1);   //(20)send 0xf1
        Oled_Write_Cmd(0xDA);   //(21)set com pin configuration (0xda)
        Oled_Write_Cmd(0x12);   //(22)send 0x12
        Oled_Write_Cmd(0xDB);   //(23)set Vcomh(0xdb)
        Oled_Write_Cmd(0x30);   //(24)send0x30
        Oled_Write_Cmd(0x8D);   //(25)set charge pump enable(0x8d)
        Oled_Write_Cmd(0x14);   //(26)send 0x14
        Oled_Write_Cmd(0xAF);   //(27)turn on oled panel(0xaf)
    }
    
    void Oled_Clear()
    {
        int i;
        int j;
        for(i=0; i<8; i++){
            Oled_Write_Cmd(0xB0 + i); // 依次指定PAGE0~PAGE7
            // 指定SEG0
            Oled_Write_Cmd(0x00);
            Oled_Write_Cmd(0x10);
            // 0到127列,每次写入0,每次写入数据列地址自动偏移
            for(j=0; j<128; j++){
                Oled_Write_Data(0);
            }
        }
    }
    
    void Oled_Show_Hanzi()
    {
        /* 显示汉字"嵌入式软件"的上半部分 */
        // 指定PAGE0
        Oled_Write_Cmd(0xB0);
        // 指定SEG0
        Oled_Write_Cmd(0x00);
        Oled_Write_Cmd(0x10);
        // 写数据,显示汉字'嵌'的上半部分
        for(i=0; i<16; i++){
            Oled_Write_Data(qian1[i]);
        }
        // 写数据,显示汉字'入'的上半部分
        for(i=0; i<16; i++){
            Oled_Write_Data(ru1[i]);
        }
        // 写数据,显示汉字'式'的上半部分
        for(i=0; i<16; i++){
            Oled_Write_Data(shi1[i]);
        }
        // 写数据,显示汉字'软'的上半部分
        for(i=0; i<16; i++){
            Oled_Write_Data(ruan1[i]);
        }
        // 写数据,显示汉字'件'的上半部分
        for(i=0; i<16; i++){
            Oled_Write_Data(jian1[i]);
        }
        /* 显示汉字"嵌入式软件"的下半部分 */
        // 指定PAGE1
        Oled_Write_Cmd(0xB1);
        // 指定SEG0
        Oled_Write_Cmd(0x00);
        Oled_Write_Cmd(0x10);
        // 写数据,显示汉字'嵌'的下半部分
        for(i=0; i<16; i++){
            Oled_Write_Data(qian2[i]);
        }
        // 写数据,显示汉字'入'的下半部分
        for(i=0; i<16; i++){
            Oled_Write_Data(ru2[i]);
        }
        // 写数据,显示汉字'式'的下半部分
        for(i=0; i<16; i++){
            Oled_Write_Data(shi2[i]);
        }
        // 写数据,显示汉字'软'的下半部分
        for(i=0; i<16; i++){
            Oled_Write_Data(ruan2[i]);
        }
        // 写数据,显示汉字'件'的下半部分
        for(i=0; i<16; i++){
            Oled_Write_Data(jian2[i]);
        }
    }

9.OLED显示图片

1、图片字模提取:

  • 首先是使用电脑上的画图软件将图片的像素确定在128×64以内。最后,保存成bmp(单色位图)数据格式,bmp格式相对于jpeg格式的特点是没有压缩。这种数据格式方便取模软件识别像素,而不需要解压。
  • 基本操作:打开图像图标
  • 取模方式:C51 格式
  • 图片的取模不像字符那样逐个字符取模,所以不需要用多个字符数组来保存要写入的数据,可以将取模来的数据直接放在一个字符数组中。如果图片的像素沾满整个OLED屏幕的话,那么字符数组的长度是128×8个字节。现在我这里的图片大小是127×64像素的,所以设置的字符数组是127×8字节长度的。

2、OLED显示图片:

  1. 思路:显示一张图片的逻辑和清屏代码的逻辑很像,但要注意循环变量 i 和 j 的数据类型选择,代表PAGE编号的外层循环变量 i 的数据类型可以选择为char,代表字符数组下标的循环变量 j 的数据类型要选择为int 或 unsigned int。如果图片像素大小不同,不可以直接套用以下代码,需要修改一下循环变量的初值和终值。
  2. 代码:其中API 1~API 8 是通用代码
    #include "reg52.h"
    #include "intrins.h"
    
    sbit scl = P0^1;
    sbit sda = P0^3;
    
    /* API1. 启动I2C */
    void IIC_Start();
    /* API2. 终止I2C */
    void IIC_Stop();
    /* API3. 获取IIC的应答位 */
    char IIC_ACK();
    /* API4. I2C发送1字节数据 */
    void IIC_Send_Byte(char cdata);
    /* API5. OLED写指令(1Byte) */
    void Oled_Write_Cmd(char dataCmd);
    /* API6. OLED写数据(1Byte) */
    void Oled_Write_Data(char dataData);
    /* API7. OLED初始化 */ 
    void Oled_Init();
    /* API8. OLED清屏 */
    void Oled_Clear();
    /* API9. OLED显示一张图片 */
    void Oled_Show_Pic(char *image);
    
    code unsigned char bmp[] = {
        /*--  调入了一幅图像:F:\51project\6.IIC协议\IIC协议与OLED\OLED显示图片\ikun.bmp  --*/
        /*--  宽度x高度=127x64  --*/
        0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
        0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x80,0x80,
        0xC0,0xC0,0xC0,0xC0,0xC0,0xE0,0xE0,0xE0,0x60,0x70,0x70,0x70,0x30,0x30,0x30,0x38,
        0x38,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x38,0x38,0x30,0x30,0x30,0x30,0x30,
        0x30,0x30,0x38,0x3C,0x3C,0x1C,0x0E,0x0E,0x0E,0x06,0x07,0x07,0x07,0x07,0x07,0x07,
        0x07,0x07,0x06,0x06,0x06,0x0E,0x0E,0x0C,0x0C,0x0C,0x1C,0x1C,0x18,0x38,0x38,0x30,
        0x30,0x70,0x70,0x70,0xE0,0xE0,0xC0,0xC0,0xC0,0x80,0x80,0x80,0x00,0x00,0x00,0x00,
        0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
        0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x80,0xC0,0xC0,0xE0,0xE0,0x70,
        0x70,0x78,0x38,0x38,0x1C,0x1C,0x1E,0x0E,0x0E,0x06,0x07,0x07,0x07,0x03,0x03,0x01,
        0x01,0x01,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
        0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xC0,0xE0,0xF0,0xFC,0xFE,0xFE,
        0xFE,0xFE,0x76,0x7A,0x78,0x7C,0x7C,0x7C,0x7E,0x7E,0x7E,0x77,0x76,0x7E,0x7E,0x7E,
        0x7C,0x7C,0x78,0x78,0x78,0x70,0xF0,0xE0,0xE0,0xE0,0xC0,0x80,0x00,0x00,0x00,0x00,
        0x40,0xC0,0xC0,0xC0,0xC1,0x81,0x81,0x81,0x03,0x07,0x07,0x07,0x0E,0x0E,0x1E,0x1C,
        0x3C,0x38,0x78,0x78,0xF0,0xE0,0xE0,0xC0,0xC0,0x00,0x00,0x00,0x00,0x00,0xC0,0xE0,
        0xF0,0xF8,0x78,0x7C,0x1E,0x9E,0x8F,0x8F,0x87,0x83,0x01,0x01,0x01,0x00,0x00,0x00,
        0x00,0x00,0x00,0x00,0xC0,0xF8,0xF8,0xFC,0xFC,0x3C,0x04,0x00,0x00,0x00,0x00,0x00,
        0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
        0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0x1F,0x1D,0x1C,0x1C,
        0x18,0x18,0x18,0x18,0x18,0x18,0x38,0x30,0x30,0x70,0x60,0xE0,0xE0,0xC0,0x80,0x80,
        0x00,0x80,0x80,0xC0,0xC0,0xE1,0x69,0x73,0x77,0x3F,0x3F,0xFF,0xFF,0xFE,0xF0,0xE0,
        0x80,0x80,0x81,0x83,0x07,0x07,0x0F,0x3E,0x7E,0x7C,0xF8,0xF0,0xE0,0xC0,0x80,0x00,
        0x00,0x00,0x00,0x00,0x01,0x03,0x07,0x07,0x3F,0x7F,0xFE,0xFC,0xE0,0x0F,0x0F,0x0F,
        0x1D,0x1C,0x18,0xFF,0xFF,0xFF,0xFF,0xFF,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
        0x00,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
        0x00,0x00,0xC0,0xE0,0xF0,0xF8,0xF8,0xF8,0xD0,0xC0,0xE0,0xE0,0xE0,0xEC,0xFC,0xFC,
        0xFC,0xFC,0xF8,0xB8,0x38,0x30,0x30,0x3F,0x3F,0x3F,0x3F,0x20,0x00,0x00,0x00,0x00,
        0x00,0x18,0x3C,0x3C,0x3C,0x3C,0x3C,0x3C,0x3C,0x3C,0x18,0x81,0xEF,0xFF,0xFF,0xFF,
        0xFF,0xFF,0xC3,0x81,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x03,0x03,0x03,0x03,0x1B,
        0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x9F,0xDF,0xE7,0xFF,0xFF,0x7F,0x7F,0x7C,
        0xFC,0xF0,0xE0,0xC0,0xC0,0xC0,0xC0,0xE0,0xE0,0xF1,0xFF,0x3F,0xC0,0xC0,0xC0,0xC0,
        0xC0,0xE0,0x67,0x6F,0x6F,0x6F,0xEF,0xEC,0xEE,0xEE,0xFE,0xFE,0xFE,0xFF,0xFF,0x07,
        0x07,0x0F,0x1F,0xFF,0xFF,0xFF,0xFF,0xFC,0xF8,0xF8,0xF8,0xF8,0xF8,0xF8,0xF8,0xFC,
        0xFC,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFE,0xFC,0xF8,0xF8,0x00,0x00,0x01,
        0x01,0x03,0x03,0x03,0x07,0x07,0x06,0x0E,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0xEC,0xFC,
        0xFC,0xFC,0x9C,0x1C,0x0C,0x0C,0x0E,0x07,0x07,0x07,0x07,0x07,0x07,0x06,0x06,0x06,
        0x07,0x07,0x07,0x07,0x07,0x06,0x0E,0x8E,0x8C,0xCC,0xDC,0xFC,0xFC,0xFC,0xFC,0xEC,
        0x0C,0x0C,0x06,0x06,0x06,0xE3,0xF3,0xFB,0xFB,0xFC,0xFC,0xFC,0xFE,0xFE,0xFE,0xFE,
        0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x30,0x00,0x00,0x00,0x00,0x5D,0x1C,0x7D,0x1D,0x3C,
        0x1C,0x1C,0x9C,0x9C,0x1C,0x3D,0xFD,0xFF,0xFF,0x7F,0xDF,0xFF,0xFF,0xFF,0xFE,0xFE,
        0xFC,0xF8,0xF1,0xE1,0xE3,0xE7,0xE7,0xCF,0xCF,0x8F,0x8F,0x9F,0xDF,0xDF,0xDF,0xDF,
        0xDF,0xDF,0xDF,0xDF,0xCF,0xCF,0xCF,0xE7,0xE7,0xE3,0xE1,0xE1,0xE0,0xE0,0xE0,0xE0,
        0xE0,0xE0,0xE0,0xE0,0xE0,0xE0,0xE0,0xE0,0xE0,0xE0,0xE0,0xE0,0xE0,0xE3,0xC7,0xC7,
        0xC7,0xCF,0xCF,0xCF,0xDF,0xDB,0xDB,0xDF,0xDF,0xDE,0x96,0x96,0xB6,0xB6,0xB7,0xB7,
        0x93,0x93,0x93,0x9B,0x9B,0x9B,0x99,0x1D,0x0D,0x0F,0x0F,0x07,0x03,0x03,0x01,0x00,
        0x00,0x00,0x00,0x80,0xC1,0xC7,0xC7,0xCF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xBF,0x9F,
        0x9F,0x8F,0x87,0x87,0x03,0x00,0x00,0x00,0x00,0x00,0x07,0x87,0x67,0x47,0x67,0x67,
        0xE6,0xE6,0x2E,0x0E,0x0E,0xCF,0x4E,0x1E,0x1C,0xDF,0xFF,0xFF,0xFF,0xBF,0x9F,0x9F,
        0x9F,0x9F,0x9F,0x9F,0x9F,0x9F,0x1F,0x1F,0x1F,0x1F,0x1F,0x1F,0x3F,0x3F,0x3F,0x3F,
        0x3F,0x7F,0x7F,0x7F,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
        0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
        0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
        0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
        0xFE,0x7F,0x7F,0x7F,0x7F,0x3F,0x3F,0x3F,0x3F,0x3F,0x1F,0x1F,0x1F,0x1F,0x9F,0xFF,
        0xFF,0xFF,0x07,0x07,0x00,0x00,0x00,0x00,0x00,0x87,0x8F,0x4E,0x5E,0x2F,0x4F,0x1C,
        0x3C,0x7D,0x79,0x7A,0x74,0x77,0xF8,0xFC,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
        0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFE,0xFE,0xFE,0xFC,0xFC,0xFC,
        0xFC,0xF8,0x60,0x00,0x00,0x01,0x03,0x0F,0xFF,0xFD,0xFD,0xFD,0xFF,0xFF,0xFB,0xFB,
        0xFB,0xFB,0xFB,0xFB,0xFB,0xF3,0xF3,0xF7,0xF7,0xF7,0xF7,0xF7,0x37,0x37,0x37,0x0F,
        0x0F,0x07,0x17,0x37,0x37,0x37,0x77,0xF7,0xF7,0xF3,0xF3,0xFB,0xFB,0xFB,0xFB,0xFB,
        0xFB,0xFF,0xFF,0xFF,0xFF,0xFD,0xFD,0xFD,0xFD,0x1F,0x07,0x03,0x03,0x01,0xC0,0xF0,
        0xF8,0xF8,0xFC,0xFC,0xFC,0xFE,0xFE,0xFE,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
        0xFC,0xFC,0xF8,0xF0,0xE0,0xC0,0x80,0x00
    };
    
    void main(void)
    {
        Oled_Init();
        // 设置页寻址模式
        Oled_Write_Cmd(0x20);
        Oled_Write_Cmd(0x02);
        // 清屏
        Oled_Clear();
        Oled_Show_Pic(bmp);
        while(1);   //防止程序退出
    }
    
    void IIC_Start()
    {
        scl = 0;    // 防止OLED雪花
        // o点:
        scl = 1;    // SCL置高电平
        sda = 0;    // SDA置低电平
        // a点:
                    // SCL维持高电平
        sda = 1;    // SDA从低电平转折为高电平,时间未知
        // a—>b:
                    // SCL维持高电平
        _nop_();    // SDA延时4.7微秒
        // b—>c:
                    // SCL维持高电平
        sda = 0;    // SDA从高电平转折为低电平,时间未知
        // c—>d:
        _nop_();    // SCL延时4微秒
                    // SDA维持低电平
        // d点:
        scl = 0;    // SCL从高电平转折为低电平,时间未知
                    // SDA维持低电平
    }
    
    void IIC_Stop()
    {
        // o点:
        scl = 0;    // SCL置低电平
        sda = 0;    // SDA置低电平
        // a点:
        scl = 1;    // SCL从低电平转折为高电平,时间未知
                    // SDA维持低电平
        // a—>b:
                    // SCL维持高电平
        _nop_();    // SDA延时4微秒
        // b—>c:
                    // SCL维持高电平
        sda = 1;    // SDA从低电平折为高电平,时间未知
        // c—>d:
                    // SCL维持低高电平
        _nop_();    // SDA延时4.7微秒
        // d点:
                    // SCL维持高电平
        sda = 0;    // SDA从高电平转折为低电平,时间未知
    }
    
    char IIC_ACK()
    {
        char flag;
        // o点:
                    // SCL置低电平
        sda = 1;    // SDA:在主控设备发送完成一个字节数据后,就立马发送一个高电平,代表释放SDA数据线
        // o—>a:
        _nop_();    // SCL延时
        // a点:
        scl = 1;    // SCL从低电平转折为高电平,代表释放SCL数据线
        // a—>b:
        _nop_();    // SCL延时4微秒,因为读取SDA的时候一定要保证SCL处于高电平的状态
        flag = sda; // SDA被读
        _nop_();    // SCL延时4微秒,因为读取SDA的时候一定要保证SCL处于高电平的状态
        // b点:
        scl = 0;    // SCL从高电平转折为低电平
        // b—>c:
        _nop_();    // SCL延时
        return flag;
    }
    
    void IIC_Send_Byte(char cdata)
    {
        int i;
        for(i=0; i<8; i++){
            // o点:
            scl = 0;            // SCL置低电平:允许数据翻转
            sda = cdata & 0x80; // SDA发送1bit数据
            // o—>a:
            _nop_();            // SCL延时5微秒
                                // SDA正在发送数据
            // a点:
            scl = 1;            // SCL从低电平转折为高电平
            // a—>b:
            _nop_();            // SCL延时5微秒
                                // SDA正在发送数据
            // b点:
            scl = 0;            // SCL从高电平转折为低电平:为下一次数据翻转做准备
            // b—>c:
            _nop_();            // SCL延时5微秒
            cdata = cdata << 1;
        }
    }
    
    void Oled_Write_Cmd(char dataCmd)
    {
        IIC_Start();            //发送起始信号
        IIC_Send_Byte(0x78);    //确认从机地址,准备写入
        IIC_ACK();              //应答信号ACK读取
        IIC_Send_Byte(0x00);    //发送控制字节,确认发送指令
        IIC_ACK();              //应答信号ACK读取
        IIC_Send_Byte(dataCmd); //发送指令
        IIC_ACK();              //应答信号ACK读取
        IIC_Stop();             //发送停止信号
    }
    
    void Oled_Write_Data(char dataData)
    {
        IIC_Start();            //发送起始信号
        IIC_Send_Byte(0x78);    //确认从机地址,准备写入
        IIC_ACK();              //应答信号ACK读取
        IIC_Send_Byte(0x40);    //发送控制字节,确认发送数据
        IIC_ACK();              //应答信号ACK读取
        IIC_Send_Byte(dataData);//发送数据
        IIC_ACK();              //应答信号ACK读取
        IIC_Stop();             //发送停止信号
    }
    
    void Oled_Init()
    {
        Oled_Write_Cmd(0xAE);   //(01)display off (0xae)
        Oled_Write_Cmd(0x00);   //(02)set low column address (0x00)
        Oled_Write_Cmd(0x10);   //(03)set high column address (0x10)
        Oled_Write_Cmd(0x40);   //(04)set start line address (0x40)
        Oled_Write_Cmd(0xB0);   //(05)set page address (0xb0)
        Oled_Write_Cmd(0x81);   //(06)contract control (0x81)
        Oled_Write_Cmd(0xFF);   //(07)send 0xff(多字节指令)
        Oled_Write_Cmd(0xA1);   //(08)set segment remap (0xa1)
        Oled_Write_Cmd(0xA6);   //(09)set normal/reverse (0xa6)
        Oled_Write_Cmd(0xA8);   //(10)set multiplex ratio (1 to 64) (0xa8)
        Oled_Write_Cmd(0x3F);   //(11)set duty 1/32 (0x3f)
        Oled_Write_Cmd(0xC8);   //(12)com scan direction (0xc8)
        Oled_Write_Cmd(0xD3);   //(13)set display offset (0xd3)
        Oled_Write_Cmd(0x00);   //(14)send 0x00 
        Oled_Write_Cmd(0xD5);   //(15)set osc division(0xd5)
        Oled_Write_Cmd(0x80);   //(16)send 0x80
        Oled_Write_Cmd(0xD8);   //(17)set area color mode off (0xd8)
        Oled_Write_Cmd(0x05);   //(18)send 0x05
        Oled_Write_Cmd(0xD9);   //(19)set pre-charge period (0xd9)
        Oled_Write_Cmd(0xF1);   //(20)send 0xf1
        Oled_Write_Cmd(0xDA);   //(21)set com pin configuration (0xda)
        Oled_Write_Cmd(0x12);   //(22)send 0x12
        Oled_Write_Cmd(0xDB);   //(23)set Vcomh(0xdb)
        Oled_Write_Cmd(0x30);   //(24)send0x30
        Oled_Write_Cmd(0x8D);   //(25)set charge pump enable(0x8d)
        Oled_Write_Cmd(0x14);   //(26)send 0x14
        Oled_Write_Cmd(0xAF);   //(27)turn on oled panel(0xaf)
    }
    
    void Oled_Clear()
    {
        int i;
        int j;
        for(i=0; i<8; i++){
            Oled_Write_Cmd(0xB0 + i); // 依次指定PAGE0~PAGE7
            // 指定SEG0
            Oled_Write_Cmd(0x00);
            Oled_Write_Cmd(0x10);
            // 0到127列,每次写入0,每次写入数据列地址自动偏移
            for(j=0; j<128; j++){
                Oled_Write_Data(0);
            }
        }
    }
    
    void Oled_Show_Pic(char *image)
    {
        char i;
        int j;
        for(i=0; i<8; i++){
            Oled_Write_Cmd(0xB0 + i); // 依次指定PAGE0~PAGE7
            // 指定SEG0
            Oled_Write_Cmd(0x00);
            Oled_Write_Cmd(0x10);
            // 0到127列,每次写入0,每次写入数据列地址自动偏移
            for(j=127*i; j<127*(i+1); j++){
                Oled_Write_Data(image[j]);
            }
        }
    }

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/599425.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【性能调优】真实体验 “系统调用是重开销”

实践背景是开发云原生背景下的指纹识别插件&#xff0c;主要针对的是镜像、容器等云时代的软件资产。 信息安全语境下的 指纹识别 指的是定位软件的特征&#xff0c;如名称、版本号、开源许可证等&#xff0c;就像指纹是人的独特生物凭证&#xff0c;这些特征是软件的独特电子凭…

六一儿童节 全网最全的微服务+Outh2套餐,你确定不来试一试?(入门到精通,附源码)满足你的味蕾需要(二)

咱们废话不多说&#xff0c;直接开干&#xff01;&#xff01;&#xff01; 目录 一、项目目录 二、Token 三、授权服务器oauth 1.pom 2.application 3.OauthApp启动类 4.DiyUserDetails 5.MyUserDetailService 6.KeyPairController 7.TokenConfig 8.WebSecurityCo…

LNMP架构

LNMP架构 一、LNMP架构原理二、LNMP部署1、安装 Nginx 服务1.安装依赖包2.创建运行用户3.编译安装4.优化路径5.添加 Nginx 系统服务 2、安装 MySQL 服务1.安装Mysql环境依赖包2.创建运行用户3.编译安装4.修改mysql 配置文件5.更改mysql安装目录和配置文件的属主属组6.设置路径环…

电子模块|压力传感器模块HX711---硬件介绍

电子模块|压力传感器模块HX711---硬件介绍与C51&&STM32驱动 实物照片模块简介模块特点 硬件模拟输入供电电源时钟选择串口通讯复位和断电HX711相关部分的 PCB 设计 实物照片 模块简介 HX711是一款专为高精度称重传感器而设计的24位A/D转换器芯片。与同类型其它芯片相比…

后端接口调式工具

后端接口调式工具 目录概述需求&#xff1a; 设计思路实现思路分析1.Postman2.Swagger 文档测试工具3.Sniff 文档测试工具4.APIpost 参考资料和推荐阅读 Survive by day and develop by night. talk for import biz , show your perfect code,full busy&#xff0c;skip hardne…

【SCADA】启动KingSCADA运行系统,提示加载报警库服务失败?

大家好&#xff0c;我是雷工&#xff01; 今天启动KingSCADA时&#xff0c;发现无法运行&#xff0c;提示“加载 报警库服务 失败”&#xff0c;现将问题排查及解决问题的过程记录如下。 一、问题描述&#xff1a; 1、提示如下图&#xff1a; 2、信息窗口提示&#xff1a; …

性能测试从零开始落地实施全过程指南之性能测试计划怎么写?

目录 前言 一、测试背景 二、测试目的 三、测试范围 四、术语约定 五、环境说明 六、需求分析 七、测试策略 八、准备工作 九、组织架构 十、风险分析 十一、交付清单 十二、阶段进度 前言 最近有些同学找我咨询关于性能测试计划相关的问题&#xff0c;原因是他们…

《C++ list的模拟实现》

本文主要介绍list容器的模拟实现 文章目录 1、迭代器正向迭代器类反向迭代器类 2、push_back尾插函数3、 push_front头插函数4、 insert插入函数5、erase删除函数6、pop_front函数7、pop_back函数8、 构造函数9、 拷贝构造函数10、 list赋值重载函数11、clear12、 析构函数程序…

AI注册流程

1、首先需要有一个OpenAI账号&#xff0c;如果有方法的&#xff0c;就可以自己先注册一下。如果没有方法的&#xff0c;还有一个付费版本的可以备选&#xff0c;亲测可用。 2、注册建议使用谷歌账号关联登录&#xff0c;最方便。微软账号太慢了&#xff0c;也可以使用。注册使用…

SAP-MM库存进销存报表

1、总览&#xff1a; 事务代码MB5B是查询选择期间之内的收发存报表&#xff1b; 其中&#xff0c;收、发为汇总选择期间的收、发信息&#xff0c;存为选择期间的期初、期末库存数据&#xff1b;我们也可以用该报表查询历史上某一天的库存&#xff0c;但注意有一些限制条件。 …

【Selenium】提高测试爬虫效率:Selenium与多线程的完美结合

前言 使用Selenium 创建多个浏览器&#xff0c;这在自动化操作中非常常见。 而在Python中&#xff0c;使用 Selenium threading 或 Selenium ThreadPoolExecutor 都是很好的实现方法。 应用场景&#xff1a; 创建多个浏览器用于测试或者数据采集&#xff1b;使用Selenium…

Region Proposal Network (RPN) 架构详解

动动发财的小手&#xff0c;点个赞吧&#xff01; 简介 如果您正在阅读这篇文章[1]&#xff0c;那么我假设您一定听说过用于目标检测的 RCNN 系列&#xff0c;如果是的话&#xff0c;那么您一定遇到过 RPN&#xff0c;即区域提议网络。如果您不了解 RCNN 系列&#xff0c;那么我…

Github copilot的详细介绍,竞品比对分析,效率使用方法总结。

Copilot介绍&#xff0c;与竞品对比 Copilot是GitHub和OpenAI合作开发的一款人工智能代码助手&#xff0c;它可以根据用户输入的注释和代码片段&#xff0c;自动生成高质量的代码。Copilot使用了OpenAI的GPT模型&#xff0c;可以学习和理解大量的代码库和文档&#xff0c;从而…

javascript基础十三:说说 typeof 与 instanceof 区别?

一、typeof typeof 操作符返回一个字符串&#xff0c;表示未经计算的操作数的类型 举个粟子&#xff1a; typeof 1 number typeof 2 string typeof undefined undefined typeof false boolean typeof Symbol() symbol typeof null object typeof [] object typeof {} object…

TCP传输性能的关键因素除了流量控制,还有这些!

TCP网络通信基本原理 文章目录 TCP网络通信基本原理TCP效率&#xff08;滑动窗口&#xff09;流量控制拥塞控制延时应答捎带应答 面向字节流异常情况分析总结UDP/TCP特性与不同应用场景 TCP效率&#xff08;滑动窗口&#xff09; 滑动窗口&#xff1a;在TCP通信协议下&#xf…

【UnityShader入门精要】【总结记录】【第二章-2】

☀️博客主页&#xff1a;CSDN博客主页 &#x1f4a8;本文由 萌萌的小木屋 原创&#xff0c;首发于 CSDN&#x1f4a2; &#x1f525;学习专栏推荐&#xff1a;面试汇总 ❗️游戏框架专栏推荐&#xff1a;游戏实用框架专栏 ⛅️点赞 &#x1f44d; 收藏 ⭐留言 &#x1f4dd;&a…

1901-2021年1km分辨率逐月最高气温栅格数据(全国/分省)

气温数据是我们最常用的气象指标之一&#xff0c;之前我们给大家分享过来源于国家青藏高原科学数据中心提供的1901-2021年1km分辨率逐月平均气温栅格数据&#xff08;可查看之前的文章获悉详情&#xff09;&#xff01; 本次我们分享的同样是来自国家青藏高原科学数据中心的高…

【探索】在 JavaScript 中使用 C 程序

JavaScript 是个灵活的脚本语言&#xff0c;能方便的处理业务逻辑。当需要传输通信时&#xff0c;我们大多选择 JSON 或 XML 格式。 但在数据长度非常苛刻的情况下&#xff0c;文本协议的效率就非常低了&#xff0c;这时不得不使用二进制格式。 去年的今天&#xff0c;在折腾…

Redis中的整数集合(IntSet)

Redis节省内存的两个优秀设计思想&#xff1a;一个是使用连续的内存空间&#xff0c;避免内存碎片开销&#xff1b;二个是针对不同长度的数据&#xff0c;采用不同大小的元数据&#xff0c;以避免使用统一大小的元数据&#xff0c;造成内存空间的浪费。IntSet便具备以上两个设计…

160套小程序源码

源码列表如下&#xff1a; AppleMusic (知乎日报) 微信小程序 d artand 今日更新求职招聘类 医药网 口碑外卖点餐 城市天气 外卖小程序 定位天气 家居在线 微信小程序-大好商城&#xff0c;wechat-weapp 微信小程序的掘金信息流 微信跳一跳小游戏源码 微票源码-demo 急救应急处…