一、IIC协议
1.1 IIC协议概述

IIC全称Inter-Integrated Circuit (集成电路总线)

是由PHILIPS(飞利浦)公司在80年代开发的两线式串行总线,用于连接微控制器及其外围设备。IIC属于半双工同步通信方式

特点

  • 简单性和有效性。
  • 由于接口直接在组件之上,因此IIC总线占用的空间非常小,减少了电路板的空间和芯片管脚的数量,降低了互联成本。总线的长度可高达25英尺,并且能够以10Kbps的最大传输速率支持40个组件

    1. 一根线(SDA),同一时间只能发送或者是接收。、
    2. 蓝牙串口是两个线
    3. IIC有两条总线,一条双向的串行数据线SDA,一条串行时钟线SCL。
    4. SDA(Serial data):串行数据线,用来传送数据;
    5. SCL(Serial clock line):时钟线,用来控制数据发送的时序。
  • 多主控(multimastering)
  • 其中任何能够进行发送和接收的设备都可以成为主总线。一个主控能够控制信号的传输和时钟频率。当然,在任何时间点上只能有一个主控。

    构成

    IIC串行总线一般有两根信号线,

  • 一根是双向的数据线SDA,
  • 另一根是时钟线SCL,
  • 其时钟信号是由主控器件产生。所有接到IIC总线设备上的串行数据SDA都接到总线的SDA上,各设备的时钟线SCL接到总线的SCL上。对于并联在一条总线上的每个IC都有唯一的地址。

    1.2 IIC协议

    IIC总线在传输数据的过程中一共有三种类型信号,分别为:开始信号、结束信号和应答信号。

    //起始位,停止位,数据位,速度

    1. 起终信号

    这些信号中,起始信号是必需的,结束信号和应答信号

  • 起始信号
    1. SCL =1
    2. SDA = 1;
    3. 延时
    4. while(!sda)
    5. 延时
  • 终止信号
  • 2. 应答信号

    发送器每发送一个字节(8个bit),就在时钟脉冲9期间释放数据线,由接收器反馈一个应答信号。

    应答信号为低电平时,规定为有效应答位(ACK,简称应答位),表示接收器已经成功地接收了该字节; 应答信号为高电平时,规定为非应答(ACK),一般表示接收器接收该字节没有成功。

    sda(应答信号)为高电平,延迟5微妙,(时钟线)SCL为高电平。延迟5微妙之后,去读sda(应答信号),为高\低电平,高电平(非应答)\低电平(应答),延迟5微妙,(时钟线)SCL为低电平,延迟5微妙

    char IIC_ACK()     // 应答
    {
    	char flag;
    	sda = 1;//就在时钟脉冲9期间释放数据线
    	_nop_();
    	scl = 1;
    	_nop_();
    	flag = sda;
    	_nop_();
    	scl = 0;
    	_nop_();
    	
    	return flag;
    }
    3. 数据发送的字节

    注意当scl属于高电平期间,就是在传输数据,SDA不能出现数据翻转,防止系统误以为是起终止信号,当SCL属于低电平期间时,数据可以进行翻转

  • 代码实现
  • void IIC_Send_Byte(char dataSend)
    {
    	int i;
    	
    	for(i = 0;i<8;i++){
    		scl = 0;//scl拉低,让sda做好数据准备
    		sda = dataSend & 0x80;//1000 0000获得dataSend的最高位,给sda
    		_nop_();//发送数据建立时间
    		scl = 1;//scl拉高开始发送
    		_nop_();//数据发送时间
    		scl = 0;//发送完毕拉低
    		_nop_();//
    		dataSend = dataSend << 1;
    	}
    }
  • OLED写命令
  • 写命令/数据的代码
  • void Oled_Write_Cmd(char dataCmd)
    {
    	//	1. start()
    	IIC_Start();
    	//		
    	//	2. 写入从机地址  b0111 1000 0x78
    	IIC_Send_Byte(0x78);
    	//	3. ACK
    	IIC_ACK();
    	//	4. cotrol byte: (0)(0)000000 写入命令   (0)(1)000000写入数据
    	IIC_Send_Byte(0x00);
    	//	5. ACK
    	IIC_ACK();
    	//6. 写入指令/数据
    	IIC_Send_Byte(dataCmd);
    	//7. ACK
    	IIC_ACK();
    	//8. STOP
    	IIC_Stop();
    }
    
    void Oled_Write_Data(char dataData)
    {
    	//	1. start()
    	IIC_Start();
    	//		
    	//	2. 写入从机地址  b0111 1000 0x78
    	IIC_Send_Byte(0x78);
    	//	3. ACK
    	IIC_ACK();
    	//	4. cotrol byte: (0)(0)000000 写入命令   (0)(1)000000写入数据
    	IIC_Send_Byte(0x40);
    	//	5. ACK
    	IIC_ACK();
    	///6. 写入指令/数据
    	IIC_Send_Byte(dataData);
    	//7. ACK
    	IIC_ACK();
    	//8. STOP
    	IIC_Stop();
    }
    4. OLED的寻址模式
  • 如何显示一个点?
  • 有三种,分别位页地址模式,水平地址模式和垂直地址模式,可以通过一下表格进行配置

    内存管理

  • 显示一行横线
  • 如果写入0x08(b00001000)会显示什么呢

    一个字节负责一个Page的一列显示

  • 代码实现
  • #include "reg52.h"
    #include "intrins.h"
    
    sbit scl = P2^5;
    sbit sda = P2^6;
    
    void IIC_Start()
    {
    	sda = 1;
    	scl = 1;
    	_nop_();
    	sda = 0;
    	_nop_();
    }
    
    void IIC_Stop()
    {
    	sda = 0;
    	scl = 1;
    	_nop_();
    	sda = 1;
    	_nop_();
    }
    
    char IIC_ACK()
    {
    	char flag;
    	sda = 1;//就在时钟脉冲9期间释放数据线
    	_nop_();
    	scl = 1;
    	_nop_();
    	flag = sda;
    	_nop_();
    	scl = 0;
    	_nop_();
    	
    	return flag;
    }
    
    void IIC_Send_Byte(char dataSend)
    {
    	int i;
    	
    	for(i = 0;i<8;i++){
    		scl = 0;//scl拉低,让sda做好数据准备
    		sda = dataSend & 0x80;//1000 0000获得dataSend的最高位,给sda
    		_nop_();//发送数据建立时间
    		scl = 1;//scl拉高开始发送
    		_nop_();//数据发送时间
    		scl = 0;//发送完毕拉低
    		_nop_();//
    		dataSend = dataSend << 1;
    	}
    }
    
    void Oled_Write_Cmd(char dataCmd)
    {
    	//	1. start()
    	IIC_Start();
    	//		
    	//	2. 写入从机地址  b0111 1000 0x78
    	IIC_Send_Byte(0x78);
    	//	3. ACK
    	IIC_ACK();
    	//	4. cotrol byte: (0)(0)000000 写入命令   
    	IIC_Send_Byte(0x00);
    	//	5. ACK
    	IIC_ACK();
    	//6. 写入指令/数据
    	IIC_Send_Byte(dataCmd);
    	//7. ACK
    	IIC_ACK();
    	//8. STOP
    	IIC_Stop();
    }
    
    void Oled_Write_Data(char dataData)
    {
    	//	1. start()
    	IIC_Start();
    	//		
    	//	2. 写入从机地址  b0111 1000 0x78
    	IIC_Send_Byte(0x78);
    	//	3. ACK
    	IIC_ACK();
    	//	4. cotrol byte:    (0)(1)000000写入数据
    	IIC_Send_Byte(0x40);
    	//	5. ACK
    	IIC_ACK();
    	///6. 写入指令/数据
    	IIC_Send_Byte(dataData);
    	//7. ACK
    	IIC_ACK();
    	//8. STOP
    	IIC_Stop();
    }
    
    
    void Oled_Init(void){   // 初始化
    	Oled_Write_Cmd(0xAE);//--display off
    	Oled_Write_Cmd(0x00);//---set low column address
    	Oled_Write_Cmd(0x10);//---set high column address
    	Oled_Write_Cmd(0x40);//--set start line address  
    	Oled_Write_Cmd(0xB0);//--set page address
    	Oled_Write_Cmd(0x81); // contract control
    	Oled_Write_Cmd(0xFF);//--128   
    	Oled_Write_Cmd(0xA1);//set segment remap 
    	Oled_Write_Cmd(0xA6);//--normal / reverse
    	Oled_Write_Cmd(0xA8);//--set multiplex ratio(1 to 64)
    	Oled_Write_Cmd(0x3F);//--1/32 duty
    	Oled_Write_Cmd(0xC8);//Com scan direction
    	Oled_Write_Cmd(0xD3);//-set display offset
    	Oled_Write_Cmd(0x00);//
    	
    	Oled_Write_Cmd(0xD5);//set osc division
    	Oled_Write_Cmd(0x80);//
    	
    	Oled_Write_Cmd(0xD8);//set area color mode off
    	Oled_Write_Cmd(0x05);//
    	
    	Oled_Write_Cmd(0xD9);//Set Pre-Charge Period
    	Oled_Write_Cmd(0xF1);//
    	
    	Oled_Write_Cmd(0xDA);//set com pin configuartion
    	Oled_Write_Cmd(0x12);//
    	
    	Oled_Write_Cmd(0xDB);//set Vcomh
    	Oled_Write_Cmd(0x30);//
    	
    	Oled_Write_Cmd(0x8D);//set charge pump enable
    	Oled_Write_Cmd(0x14);//
    	
    	Oled_Write_Cmd(0xAF);//--turn on oled panel		
    }
    
    
    void main()
    {
    		int a = 10;
    		int i;
    		//1. OLED初始化
    		Oled_Init();
    	
    		//2. 选择一个位置
    		//2.1 确认页寻址模式
    		Oled_Write_Cmd(0x20);
    		Oled_Write_Cmd(0x02);
    	
    		//2.2 选择PAGE 0   1011 0000
    		//								0xB0(8421法)
    		Oled_Write_Cmd(0xB0);
    //				发送指令0xB0,
    //				这个指令选择第0页(PAGE 0)。
    //				OLED的页地址从0xB0到0xB7,每一个指令分别对应一个页。
    											
    	  //3. 显示一个行  列地址会自动向后偏移
    		
             Oled_Write_Data(0x08);
             Oled_Write_Data(0x08);
             Oled_Write_Data(0x08);
             Oled_Write_Data(0x08);
             Oled_Write_Data(0x08);
             Oled_Write_Data(0x08);
             Oled_Write_Data(0x08);
             Oled_Write_Data(0x08);
             Oled_Write_Data(0x08);
             Oled_Write_Data(0x08);
             Oled_Write_Data(0x08);
             Oled_Write_Data(0x08);
             Oled_Write_Data(0x08);
             Oled_Write_Data(0x08);
             Oled_Write_Data(0x08);
             Oled_Write_Data(0x08);
    		
    	
    		while(1);
    }
  • 在最后一行显示一个点
  • Oled_Write_Cmd(0x0f); Oled_Write_Cmd(0x17);是如何显示为最后一列的
  • 列数为128列
  • 2的7方 =128
  • 低四位:0000 1111 0x0F (取其中的低四位F)
  • 高四位:1000 01111 0x17(取其中的低四位7)
  • 结果为:0x7F 换成十进制为 7^16+15^1=127
  • 为127列 +0列,是最后一行

            Oled_Write_Cmd(0x0f);
    		Oled_Write_Cmd(0x17);
    		Oled_Write_Data(0x08);
  • 清屏
  • void Oled_Clear()
    {
        unsigned char i,j; // 定义两个无符号字符变量 i 和 j,用于循环控制
    
        for(i=0;i<8;i++){  // 外层循环,循环8次,分别清除8页(每页8行,总共64行)
            Oled_Write_Cmd(0xB0 + i);// 设置当前页地址,从 page0 到 page7
            // 每个 page 从第 0 列开始
            Oled_Write_Cmd(0x00);    // 设置列地址的低四位为 0
            Oled_Write_Cmd(0x10);    // 设置列地址的高四位为 1,结合低四位,列地址为 0
            // 从第 0 列到第 127 列,依次写入 0,每写入一个数据,列地址会自动增加
            for(j = 0;j<128;j++){    // 内层循环,循环128次,清除当前页的所有列(每页128列)
                Oled_Write_Data(0);  // 向当前列写入数据 0,即清除该列
            }
        }
    }
    
  • 详细解释:
      1. 变量声明
    unsigned char i,j;
  • 定义两个无符号字符变量 ij,用于循环控制。
      1. 外层循环
    for(i=0;i<8;i++){
  • 外层循环循环8次,对应 OLED 显示屏的8页(每页8行,总共64行)。
      1. 设置当前页地址
    Oled_Write_Cmd(0xB0 + i);
  • 设置当前页地址。从 0xB00xB7 分别对应 page0page7
      1. 设置列地址
    Oled_Write_Cmd(0x00);
    Oled_Write_Cmd(0x10);
  • 将列地址设置为 0。首先发送 0x00 设置低四位为 0,然后发送 0x10 设置高四位为 0,这样列地址被设置为 0x00
      1. 内层循环
    for(j = 0;j<128;j++){
  • 内层循环循环128次,对应每页的128列。
      1. 清除当前列
    Oled_Write_Data(0);
  • 向当前列写入数据 0,即清除该列。写入数据后,列地址会自动增加,直到当前页的所有列都被清除。
  • 另一种写法
  • void Oled_Screen_Clear(void){
        char i,n;
        Oled_Write_Cmd (0x20);                  // 设置内存寻址模式
        Oled_Write_Cmd (0x02);                  // 选择页寻址模式
    
        for(i=0;i<8;i++){
            Oled_Write_Cmd(0xb0+i);             // 设置页地址(0~7)
            Oled_Write_Cmd(0x00);               // 设置显示位置列低地址
            Oled_Write_Cmd(0x10);               // 设置显示位置列高地址   
            for(n=0;n<128;n++)                  // 循环128次,对应每页的128列
                Oled_Write_Data(0x00);          // 写入数据0,清除该列
        }    
    }
    
  • 显示一个字符“A”
  • /*--  文字:  A  --*/
    /*--  宋体12;  此字体下对应的点阵为:宽x高=8x16   --*/
    char A1[8] = {0x00,0x00,0xC0,0x38,0xE0,0x00,0x00,0x00};
    char A2[8] = {0x20,0x3C,0x23,0x02,0x02,0x27,0x38,0x20};
    
    
    void main()
    {
    		int i;
    		//1. OLED初始化
    		Oled_Init();
    		//2. 选择一个位置
    		//2.1 确认页寻址模式
    		Oled_Write_Cmd(0x20);
    		Oled_Write_Cmd(0x02);
    		Oled_Clear();
    		//2.2 选择PAGE0   1011 0000
    		//								0xB0
    	
    		// 打印字符‘A’
    		Oled_Write_Cmd(0xB0);   // 打印上半身
    		Oled_Write_Cmd(0x00);
    		Oled_Write_Cmd(0x10);
    		for(i=0;i<8;i++){
    				Oled_Write_Data(A1[i]);
    		}
    		
    		Oled_Write_Cmd(0xB1);// 打印下半身
    		Oled_Write_Cmd(0x00);
    		Oled_Write_Cmd(0x10);
    		for(i=0;i<8;i++){
    				Oled_Write_Data(A2[i]);
    		}
    		
    	
    		while(1);
    }
    5. OLED 显示字符 'A' 的实现

    先使用点阵液晶取模软件生成字符

      1. 初始化 OLED
    Oled_Init();
      1. 选择页寻址模式
    Oled_Write_Cmd(0x20);
    Oled_Write_Cmd(0x02);
      1. 清屏
    Oled_Clear();
      1. 显示字符 'A'
  • 显示上半部分
  • Oled_Write_Cmd(0xB0);   // 设置页地址为 PAGE0
    Oled_Write_Cmd(0x00);   // 设置列地址低位
    Oled_Write_Cmd(0x10);   // 设置列地址高位
    for (i = 0; i < 8; i++) {
        Oled_Write_Data(A1[i]);
    }
  • 显示下半部分
  • Oled_Write_Cmd(0xB1);   // 设置页地址为 PAGE1
    Oled_Write_Cmd(0x00);   // 设置列地址低位
    Oled_Write_Cmd(0x10);   // 设置列地址高位
    for (i = 0; i < 8; i++) {
        Oled_Write_Data(A2[i]);
    }
    6. 显示一个字符串“LXL”
    /*--  文字:  L  --*/
    /*--  宋体12;  此字体下对应的点阵为:宽x高=8x16   --*/
    code char L1[8]  = {0x08,0xF8,0x08,0x00,0x00,0x00,0x00,0x00};  // "L" 字符的上半部分点阵数据
    code char L2[8]  = {0x20,0x3F,0x20,0x20,0x20,0x20,0x30,0x00};  // "L" 字符的下半部分点阵数据
    
    /*--  文字:  X  --*/
    /*--  宋体12;  此字体下对应的点阵为:宽x高=8x16   --*/
    code char L3[8]  = {0x08,0x18,0x68,0x80,0x80,0x68,0x18,0x08};  // "X" 字符的上半部分点阵数据
    code char L4[8]  = {0x20,0x30,0x2C,0x03,0x03,0x2C,0x30,0x20};  // "X" 字符的下半部分点阵数据
    
    void main()
    {
        int i,j;  // 定义循环变量 i 和 j
    
        // 1. OLED初始化
        Oled_Init();  // 调用 OLED 初始化函数
    
        // 2. 选择一个位置
        // 2.1 确认页寻址模式
        Oled_Write_Cmd(0x20);  // 设置内存寻址模式
        Oled_Write_Cmd(0x02);  // 设置为页寻址模式
    
        Oled_Clear();  // 清屏
    
        // 2.2 选择PAGE0 (1011 0000)
        // 设置起始位置为第0页 (Page 0)
        Oled_Write_Cmd(0xB0);  // 设置页地址为 Page 0
        Oled_Write_Cmd(0x00);  // 设置列地址低4位为 0x00
        Oled_Write_Cmd(0x10);  // 设置列地址高4位为 0x10,即列地址为 0x00
    
        // 显示第一个字符 "L" 的上半部分
        for(i=0;i<8;i++){
            Oled_Write_Data(L1[i]);  // 依次写入 "L" 字符上半部分的点阵数据
        }
    
        // 显示字符 "X" 的上半部分
        for(i=0;i<8;i++){
            Oled_Write_Data(L3[i]);  // 依次写入 "X" 字符上半部分的点阵数据
        }
    
        // 再次显示字符 "L" 的上半部分
        for(i=0;i<8;i++){
            Oled_Write_Data(L1[i]);  // 依次写入 "L" 字符上半部分的点阵数据
        }
    
        // 选择PAGE1 (1011 0001)
        // 设置起始位置为第1页 (Page 1)
        Oled_Write_Cmd(0xB1);  // 设置页地址为 Page 1
        Oled_Write_Cmd(0x00);  // 设置列地址低4位为 0x00
        Oled_Write_Cmd(0x10);  // 设置列地址高4位为 0x10,即列地址为 0x00
    
        // 显示字符 "L" 的下半部分
        for(j=0;j<8;j++){
            Oled_Write_Data(L2[j]);  // 依次写入 "L" 字符下半部分的点阵数据
        }
    
        // 显示字符 "X" 的下半部分
        for(j=0;j<8;j++){
            Oled_Write_Data(L4[j]);  // 依次写入 "X" 字符下半部分的点阵数据
        }
    
        // 再次显示字符 "L" 的下半部分
        for(j=0;j<8;j++){
            Oled_Write_Data(L2[j]);  // 依次写入 "L" 字符下半部分的点阵数据
        }
    
        while(1);  // 无限循环,保持显示
    }
    
  • L1、L2、L3、L4 数组: 每个字符的点阵数据分为上半部分(L1、L3)和下半部分(L2、L4)。在 OLED 显示屏上,数据需要分开写入不同的页面(page)才能显示完整的字符。
  • Oled_Write_Cmd 函数: 用于发送命令到 OLED,比如设置页面地址、列地址等。
  • Oled_Write_Data 函数: 用于发送数据到 OLED,比如每个字符的点阵数据。
  • Oled_Init 函数: 初始化 OLED 显示屏,使其进入可操作状态。
  • Oled_Clear 函数: 清空 OLED 显示屏的内容。
  • 两个 for 循环: 第一个 for 循环显示字符的上半部分(Page 0),第二个 for 循环显示字符的下半部分(Page 1)。这样分开写入可以确保字符显示正确。
  • 原始方法的优势
    1. 字符点阵的排列方式:在原始方法中,每个字符的数据分别通过单独的 for 循环写入 OLED。这意味着第一个字符的所有点阵数据(上半部分)会首先写入,然后是第二个字符的所有点阵数据,依次类推。这样可以确保每个字符的数据连续写入 OLED 显示屏的相应列,不会混淆。
    2. 显示顺序:每个 for 循环对应一个字符的数据块,这样可以保持字符在 OLED 显示屏上的正确显示顺序。例如,第一个字符“上”的上半部分数据在 s1 数组中,依次写入 OLED 显示屏的第一页;第二个字符“官”的上半部分数据在 g1 数组中,依次写入同一页面的后续列。
  • 修改后的方法的问题
    1. 数据混淆:如果你使用修改后的方法,将所有字符的数据在一个 for 循环中交替写入,字符的点阵数据会交错。例如,第一个字符“上”的第一列数据(s1[0])写入后,接着是第二个字符“官”的第一列数据(g1[0]),然后是第三个字符的数据(k1[0]),依次类推。这会导致字符的显示数据混在一起,无法正确显示。
    2. 位置不对:OLED 显示屏的列地址会在写入数据后自动递增,因此在单个 for 循环中交替写入不同字符的数据会导致它们无法正确排列在显示屏上。每个字符的数据会被打乱,显示效果会非常混乱。
  • 结论
  • 原始方法通过多个 for 循环确保每个字符的数据独立、连续地写入 OLED 显示屏,保持字符的正确显示顺序和位置。修改后的方法则会导致字符数据交错,显示效果混乱。因此,必须保持原始方法的写法,以确保 OLED 显示屏上字符的正确显示。

  • 补充说明
  • 为了更好地理解这一点,可以试想一个示例:

    假设有两个字符“A”和“B”,它们的点阵数据如下:

  • A 的上半部分数据:{A1[0], A1[1], A1[2], ..., A1[7]}
  • B 的上半部分数据:{B1[0], B1[1], B1[2], ..., B1[7]}
  • 如果使用原始方法:

  • A 的上半部分数据完整写入 OLED,接着是 B 的上半部分数据完整写入。
  • 如果使用修改后的方法:

  • A 和 B 的数据交替写入,导致显示效果完全混乱。
  • 因此,保持原始方法的 for 循环写法是正确的,确保字符正确显示。

    7. ode 关键字

    code 指定字符存储位置,以防存储空间过大

    在 C51 单片机编程中,可以使用 code 关键字来将常量数据存储在代码存储区(Flash)中,而不是在数据存储区(RAM)中。这对于存储大量的字符点阵数据非常有用,因为 RAM 的空间通常非常有限,而 Flash 的空间相对较大。

    8. 显示文字”越加越强“
    
    /*--  文字:  越  --*/
    /*--  宋体12;  此字体下对应的点阵为:宽x高=16x16   --*/
    code char y1[16] = {0x40,0x48,0x48,0xFF,0x48,0x48,0x00,0xF8,0x08,0x08,0xFF,0x08,0x89,0x6A,0x00,0x00};
    code char y2[16] = {0x80,0x7E,0x10,0x3F,0x44,0x44,0x40,0x4F,0x44,0x50,0x49,0x46,0x49,0x5C,0x40,0x00};
    
    /*--  文字:  加  --*/
    /*--  宋体12;  此字体下对应的点阵为:宽x高=16x16   --*/
    code char j1[16] = {0x10,0x10,0x10,0xFF,0x10,0x10,0xF0,0x00,0x00,0xF8,0x08,0x08,0x08,0xF8,0x00,0x00};
    code char j2[16] = {0x80,0x40,0x30,0x0F,0x40,0x80,0x7F,0x00,0x00,0x7F,0x20,0x20,0x20,0x7F,0x00,0x00};
    
    /*--  文字:  越  --*/
    /*--  宋体12;  此字体下对应的点阵为:宽x高=16x16   --*/
    code char y3[16] = {0x40,0x48,0x48,0xFF,0x48,0x48,0x00,0xF8,0x08,0x08,0xFF,0x08,0x89,0x6A,0x00,0x00};
    code char y4[16] = {0x80,0x7E,0x10,0x3F,0x44,0x44,0x40,0x4F,0x44,0x50,0x49,0x46,0x49,0x5C,0x40,0x00};
    
    /*--  文字:  强  --*/
    /*--  宋体12;  此字体下对应的点阵为:宽x高=16x16   --*/
    code char q1[16] = {0x02,0xE2,0x22,0x22,0x3E,0x00,0x80,0x9E,0x92,0x92,0xF2,0x92,0x92,0x9E,0x80,0x00};
    code char q2[16] = {0x00,0x43,0x82,0x42,0x3E,0x40,0x47,0x44,0x44,0x44,0x7F,0x44,0x44,0x54,0xE7,0x00};
    
    
    
    void main()
    {
    		int  i;
        
    		//1. OLED初始化
    		Oled_Init();
        
    		//2. 选择一个位置
    		//2.1 确认页寻址模式
    		Oled_Write_Cmd(0x20);
    		Oled_Write_Cmd(0x02);
        
    		Oled_Clear();  // 清屏
        
    		//2.2 选择PAGE0   1011 0000
    		//								0xB0
            // 从第一行第0列开始打印上半部分
    		Oled_Write_Cmd(0xB0);
    		Oled_Write_Cmd(0x00);
    		Oled_Write_Cmd(0x10);
    
                
    		for(i=0;i<16;i++){
    			
    				Oled_Write_Data(y1[i]);
    		}
    		for(i=0;i<16;i++){
    			
    				Oled_Write_Data(j1[i]);
    		}
    		for(i=0;i<16;i++){
    			
    				Oled_Write_Data(y3[i]);
    		}
    		for(i=0;i<16;i++){
    			
    				Oled_Write_Data(q1[i]);
    		}
    		
    		// 从第二行第0列开始打印下半部分
    		Oled_Write_Cmd(0xB1);
    		Oled_Write_Cmd(0x00);
    		Oled_Write_Cmd(0x10);
    		for(i=0;i<16;i++){
    			
    				Oled_Write_Data(y2[i]);
    		}
    		for(i=0;i<16;i++){
    			
    				Oled_Write_Data(j2[i]);
    		}
    		for(i=0;i<16;i++){
    			
    				Oled_Write_Data(y4[i]);
    		}
    		for(i=0;i<16;i++){
    			
    				Oled_Write_Data(q2[i]);
    		}
    		
    		while(1);
    }
  • 输出结果
  • 9. 显示一张图片和一段字符串”lxl“
    
    code unsigned char bmpImager[] = {
    
    /*--  调入了一幅图像:C:\Users\86199\Pictures\无标题.bmp  --*/
    /*--  宽度x高度=128x64   --128X8X8*/
    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,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,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,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,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,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,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,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,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,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,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,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,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,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,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
    0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,
    0xF0,0xD0,0xD6,0xD6,0xDE,0xDE,0xDE,0xDE,0x5E,0x5E,0x56,0x56,0x50,0x50,0x50,0x50,
    0x50,0x50,0x50,0x50,0x50,0x50,0x50,0x50,0x50,0x50,0x50,0x50,0xD8,0x88,0x88,0x88,
    0xF8,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,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,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,0x1F,0x10,0x1F,0x08,0x08,0x08,0x0E,0x1A,0x1A,0x1A,
    0x0E,0x02,0x02,0x02,0x0E,0x0A,0x0A,0x0F,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,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,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,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,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,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,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,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,
    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,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,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,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,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,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,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,0x00,0x00,0x00,0x00,0x00,
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
    
    };
    
    void Oled_Show_Image(unsigned char *image)
    {
    	unsigned char i; 
    	unsigned int j;
    	
    	for(i=0;i<8;i++){
    		Oled_Write_Cmd(0xB0 + i);//page0--page7
    		//每个page从0列
    		Oled_Write_Cmd(0x00);
    		Oled_Write_Cmd(0x10);
    		//0到127列,依次写入0,每写入数据,列地址自动偏移
    		for(j = 128 * i; j<(128 * (i+1));j++){
    			Oled_Write_Data(image[j]);
    		}
    	}
    }
    /*--  文字:  L  --*/
    /*--  宋体12;  此字体下对应的点阵为:宽x高=8x16   --*/
    code char L1[8]  = {0x08,0xF8,0x08,0x00,0x00,0x00,0x00,0x00};  // "L" 字符的上半部分点阵数据
    code char L2[8]  = {0x20,0x3F,0x20,0x20,0x20,0x20,0x30,0x00};  // "L" 字符的下半部分点阵数据
    
    /*--  文字:  X  --*/
    /*--  宋体12;  此字体下对应的点阵为:宽x高=8x16   --*/
    code char L3[8]  = {0x08,0x18,0x68,0x80,0x80,0x68,0x18,0x08};  // "X" 字符的上半部分点阵数据
    code char L4[8]  = {0x20,0x30,0x2C,0x03,0x03,0x2C,0x30,0x20};  // "X" 字符的下半部分点阵数据
    
    
    
    void main()
    {
    		int i,j;
    		//1. OLED初始化
    		Oled_Init();
    		//2. 选择一个位置
    		//2.1 确认页寻址模式
    		Oled_Write_Cmd(0x20);
    		Oled_Write_Cmd(0x02);
    		Oled_Clear();
    		Oled_Show_Image(bmpImager);
    	
    		Oled_Write_Cmd(0xB0);  // 设置页地址为 Page 0
        Oled_Write_Cmd(0x00);  // 设置列地址低4位为 0x00
        Oled_Write_Cmd(0x10);  // 设置列地址高4位为 0x10,即列地址为 0x00
    
        // 显示第一个字符 "L" 的上半部分
        for(i=0;i<8;i++){
            Oled_Write_Data(L1[i]);  // 依次写入 "L" 字符上半部分的点阵数据
        }
    
        // 显示字符 "X" 的上半部分
        for(i=0;i<8;i++){
            Oled_Write_Data(L3[i]);  // 依次写入 "X" 字符上半部分的点阵数据
        }
    
        // 再次显示字符 "L" 的上半部分
        for(i=0;i<8;i++){
            Oled_Write_Data(L1[i]);  // 依次写入 "L" 字符上半部分的点阵数据
        }
    
        // 选择PAGE1 (1011 0001)
        // 设置起始位置为第1页 (Page 1)
        Oled_Write_Cmd(0xB1);  // 设置页地址为 Page 1
        Oled_Write_Cmd(0x00);  // 设置列地址低4位为 0x00
        Oled_Write_Cmd(0x10);  // 设置列地址高4位为 0x10,即列地址为 0x00
    
        // 显示字符 "L" 的下半部分
        for(j=0;j<8;j++){
            Oled_Write_Data(L2[j]);  // 依次写入 "L" 字符下半部分的点阵数据
        }
    
        // 显示字符 "X" 的下半部分
        for(j=0;j<8;j++){
            Oled_Write_Data(L4[j]);  // 依次写入 "X" 字符下半部分的点阵数据
        }
    
        // 再次显示字符 "L" 的下半部分
        for(j=0;j<8;j++){
            Oled_Write_Data(L2[j]);  // 依次写入 "L" 字符下半部分的点阵数据
        }
    
        while(1);  // 无限循环,保持显示
    }

    先将照片设置成128×64像素,并且将照片保存为BMP类型的格式,然后使用字模识别软件,打开该图片。生成c51格式代码,封装成函数调用即可

    void Oled_Show_Image(unsigned char *image)
    {
    	unsigned char i; 
    	unsigned int j;
    	
    	for(i=0;i<8;i++){
    		Oled_Write_Cmd(0xB0 + i);//page0--page7
    		//每个page从0列
    		Oled_Write_Cmd(0x00);
    		Oled_Write_Cmd(0x10);
    		//0到127列,依次写入0,每写入数据,列地址自动偏移
    		for(j = 128 * i; j<(128 * (i+1));j++){
    			Oled_Write_Data(image[j]);
    		}
    	}
    }
  • 函数头部
  • c
    复制代码
    void Oled_Show_Image(unsigned char *image)
  • 这个函数名为 Oled_Show_Image,它接受一个指向无符号字符数组(即图像数据)的指针 image 作为参数。
  • 变量声明
  • c
    复制代码
    unsigned char i; 
    unsigned int j;
  • 声明了两个变量:i 用于循环遍历OLED屏幕的页(共8页),j 用于遍历每一页中的像素数据。
  • 遍历每个页
  • c
    复制代码
    for(i=0; i<8; i++) {
        Oled_Write_Cmd(0xB0 + i); // 选择当前页(page0--page7)
  • 这个 for 循环遍历了OLED屏幕的8个页,每个页包含8行像素(总共64行)。Oled_Write_Cmd(0xB0 + i); 选择了当前操作的页,i 的范围是从0到7,对应页地址从0xB0到0xB7。
  • 设置列地址
  • c
    复制代码
        Oled_Write_Cmd(0x00); // 设置当前列的低位地址为0
        Oled_Write_Cmd(0x10); // 设置当前列的高位地址为0
  • 这两条命令将当前页的列地址设置为0列(也就是从左边的第一列开始)。OLED显示屏的列地址有两部分:低位地址和高位地址,分别用0x00和0x10命令设置。
  • 写入图像数据
  • c
    复制代码
        for(j = 128 * i; j < (128 * (i + 1)); j++) {
            Oled_Write_Data(image[j]); // 写入当前页的图像数据
        }
    }
  • 这个 for 循环从图像数据中取出对应当前页的128个字节(对应128列)的数据并写入OLED屏幕。
  • j = 128 * i 计算当前页的起始位置,128 * (i + 1) 计算该页数据的结束位置。
  • Oled_Write_Data(image[j]); 这一行将图像数组中的数据发送到OLED屏幕,显示在当前页的对应列上。
  • 总结
  • Oled_Show_Image 函数的主要功能是将一幅128×64像素的图像(分8页,每页128列)显示到OLED屏幕上。每个页的数据依次写入OLED显示屏的对应位置。
  • 代码实现
  • 作者:越加越强

    物联沃分享整理
    物联沃-IOTWORD物联网 » IIC协议

    发表回复