OLED屏幕的IIC驱动程序

目录

一、IIC驱动

二、自己动手写的模拟IIC

三、OLED驱动

四、常用的OLED函数

五、咕咕咕


今天有个小老弟问我OLED的IIC驱动问题,正好我前段时间写了SPI的OLED驱动程序,也想把IIC的驱动补上,凑成一对O(∩_∩)O哈哈~ 今天就稍微总结一下IIC的驱动吧。

本次实验的平台是NodeMCU(ESP8266) 开发环境为安信可 ESP 系列一体化开发环境  SDK版本为 ver: 2.2.1 

一、IIC驱动

IIC有硬件IIC和软件(模拟)IIC之分,本次使用的SDK里有一份提供的IIC的库,但仔细查看之后发现这个库也是软件模拟IIC。必须用到的函数有写命令函数,写数据函数

 其中IIC引脚初始化函数为

i2c_master_gpio_init();//IIC初始化

 

使用ESP8266SDK提供的IIC库实现的写命令函数,写数据函数:

void OLED_WrCmd(uint8 WrCmd)
{
	i2c_master_start();
	i2c_master_writeByte(0x78);
	i2c_master_getAck();
	i2c_master_writeByte(0x00);
	i2c_master_getAck();
	i2c_master_writeByte(WrCmd);
	i2c_master_getAck();
	i2c_master_stop();
}
void OLED_WrData(uint8 WrData)
{
	i2c_master_start();
	i2c_master_writeByte(0x78);
	i2c_master_getAck();
	i2c_master_writeByte(0x40);
	i2c_master_getAck();
	i2c_master_writeByte(WrData);
	i2c_master_getAck();
	i2c_master_stop();
}

需要注意的是 IIC有七位地址、八位地址和十位地址之分

在7位寻址过程中,从机地址在启动信号后的第一个字节开始传输,该字节的前7位为从机地址,第8位为读写位,其中0表示写,1表示读。

我们用到的OLED是七位寻址的,有些库函数中写地址的时候自动加入最后一位,而有些库中没有这样的操作(为了写库时候的方便),需要自己考虑最后一位(读写位)的值。比如arduino中IIC的驱动,我们写的SSD1306的地址是0x3C(‭00111100‬),而网上很多IIC的例程中 写地址的时候写的是0x78(01111000‬),其实他们是同一个意思‭,在arduino的库中 最后一位读写位由库负责添加进去(比如address = (address << 1);这样的操作),最终通信的时候在线上传输的都是0x78(01111000‬)。

 

二、自己动手写的模拟IIC

#define I2C_MASTER_SDA_GPIO 4
#define I2C_MASTER_SCL_GPIO 5


#define I2C_MASTER_GPIO_OUT(pin,val) \
    if(val) I2C_MASTER_GPIO_SET(pin);\
    else I2C_MASTER_GPIO_CLR(pin)

#define delay_us os_delay_us

//开始信号
void IIC_Start(void)
{
    GPIO_OUTPUT_SET(I2C_MASTER_SDA_GPIO,0);//SDA_OUT();
    I2C_MASTER_GPIO_OUT(I2C_MASTER_SDA_GPIO,1);//IIC_SDA=1;
    I2C_MASTER_GPIO_OUT(I2C_MASTER_SCL_GPIO,1);//IIC_SCL=1;
    delay_us(2);
    I2C_MASTER_GPIO_OUT(I2C_MASTER_SDA_GPIO,0);//IIC_SDA=0;
    delay_us(2);
    I2C_MASTER_GPIO_OUT(I2C_MASTER_SCL_GPIO,0);//IIC_SCL=0;
    delay_us(2);
}

void IIC_Stop(void)
{
    I2C_MASTER_GPIO_OUT(I2C_MASTER_SCL_GPIO,1);//IIC_SCL=1;
    I2C_MASTER_GPIO_OUT(I2C_MASTER_SDA_GPIO,0);//IIC_SDA=0;
    delay_us(2);
    I2C_MASTER_GPIO_OUT(I2C_MASTER_SDA_GPIO,1);//IIC_SDA=1;
    delay_us(2);
}

/*
*   返回1--应答出错
*   返回0--应答正确
*/
uint8_t IIC_Wait_Ask(void)
{
    int count=0;

    GPIO_DIS_OUTPUT(I2C_MASTER_SDA_GPIO);//    SDA_IN();

    I2C_MASTER_GPIO_OUT(I2C_MASTER_SCL_GPIO,1);//IIC_SCL=1;
    delay_us(2);
    while(GPIO_INPUT_GET(I2C_MASTER_SDA_GPIO)) //
    {
        count++;
        if(count>250)
        {
            IIC_Stop();
            return 1;
        }
    }
    I2C_MASTER_GPIO_OUT(I2C_MASTER_SCL_GPIO,0);//IIC_SCL=0;
    delay_us(2);
    return 0;
}

//写一个字节
void IIC_WriteByte(uint8_t data)
{
    uint8_t i;
    GPIO_OUTPUT_SET(I2C_MASTER_SDA_GPIO,0);//SDA_OUT();
    for(i=0;i<8;i++)
    {
    	I2C_MASTER_GPIO_OUT(I2C_MASTER_SCL_GPIO,0);//IIC_SCL=0;
        delay_us(2);
        if(data & 0x80)     //MSB,从高位开始一位一位传输
            I2C_MASTER_GPIO_OUT(I2C_MASTER_SDA_GPIO,1);//IIC_SDA=1;
        else
            I2C_MASTER_GPIO_OUT(I2C_MASTER_SDA_GPIO,0);//IIC_SDA=0;
        I2C_MASTER_GPIO_OUT(I2C_MASTER_SCL_GPIO,1);//IIC_SCL=1;
        delay_us(2);
        I2C_MASTER_GPIO_OUT(I2C_MASTER_SCL_GPIO,0);//IIC_SCL=0;
        data<<=1;

    }
}


uint8_t IIC_ReadByte(void)
{
    uint8_t data,i;
    I2C_MASTER_GPIO_OUT(I2C_MASTER_SDA_GPIO,1);//IIC_SDA=1;
    delay_us(2);
    for(i=0;i<8;i++)
    {
        data<<=1;
        I2C_MASTER_GPIO_OUT(I2C_MASTER_SCL_GPIO,0);//IIC_SCL=0;
        delay_us(2);
        I2C_MASTER_GPIO_OUT(I2C_MASTER_SCL_GPIO,1);//IIC_SCL=1;
        delay_us(2);
        if(GPIO_INPUT_GET(I2C_MASTER_SDA_GPIO))//(GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_7))
            data=data | 0x01;
        else
            data=data & 0xFE;

    }
    I2C_MASTER_GPIO_OUT(I2C_MASTER_SCL_GPIO,0);//IIC_SCL=0;
    delay_us(2);
    return data;

}


void WriteCmd(uint8_t command)
{
    IIC_Start();
    IIC_WriteByte(0x78);//OLED地址
    IIC_Wait_Ask();
    IIC_WriteByte(0x00);//寄存器地址
    IIC_Wait_Ask();
    IIC_WriteByte(command);
    IIC_Wait_Ask();
    IIC_Stop();
}


void WriteDat(uint8_t data)
{
    IIC_Start();
    IIC_WriteByte(0x78);//OLED地址
    IIC_Wait_Ask();
    IIC_WriteByte(0x40);//寄存器地址
    IIC_Wait_Ask();
    IIC_WriteByte(data);
    IIC_Wait_Ask();
    IIC_Stop();
}

#define OLED_WrCmd WriteCmd   //给写命令函数重命名
#define OLED_WrData WriteDat  //给写数据函数重命名

三、OLED驱动

接下来驱动OLED屏幕的方法就和SPI是一样的了,向对应的寄存器中写入数据,就可以实现不同的功能

必须用到的函数有 初始化函数

常用的功能函数有清屏函数,设置起点位置函数,写字符函数等

下面是初始化函数,各个寄存器的功能写在了注释中

void OLED_Init()
{
	unsigned int a;
	for(a=0;a<5000;a++);
	OLED_WrCmd(0xAE);//--turn off oled panel
 	OLED_WrCmd(0x00);//---set low column address
 	OLED_WrCmd(0x10);//---set high column address
 	OLED_WrCmd(0x40);//--set start line address  Set Mapping RAM Display Start Line (0x00~0x3F)
	OLED_WrCmd(0xB0);
 	OLED_WrCmd(0x81);//--set contrast control register
 	OLED_WrCmd(0xFF); // Set SEG Output Current Brightness
 	OLED_WrCmd(0xa1);//--Set SEG/Column Mapping     0xa0左右反置 0xa1正常
 	OLED_WrCmd(0xc8);//Set COM/Row Scan Direction   0xc0上下反置 0xc8正常
 	OLED_WrCmd(0xa6);//--set normal display
 	OLED_WrCmd(0xa8);//--set multiplex ratio(1 to 64)
 	OLED_WrCmd(0x3f);//--1/64 duty
 	OLED_WrCmd(0xd3);//-set display offset Shift Mapping RAM Counter (0x00~0x3F)
 	OLED_WrCmd(0x00);//-not offset
	OLED_WrCmd(0xd5);//--set display clock divide ratio/oscillator frequency
 	OLED_WrCmd(0x80);//--set divide ratio, Set Clock as 100 Frames/Sec
	OLED_WrCmd(0xd9);//--set pre-charge period
 	OLED_WrCmd(0xf1);//Set Pre-Charge as 15 Clocks & Discharge as 1 Clock
 	OLED_WrCmd(0xda);//--set com pins hardware configuration
 	OLED_WrCmd(0x12);
 	OLED_WrCmd(0xdb);//--set vcomh
 	OLED_WrCmd(0x40);//Set VCOM Deselect Level
 	OLED_WrCmd(0x20);//-Set Page Addressing Mode (0x00/0x01/0x02)
 	OLED_WrCmd(0x02);//
 	OLED_WrCmd(0x8d);//--set Charge Pump enable/disable
	OLED_WrCmd(0x14);//--set(0x10) disable
 	OLED_WrCmd(0xa4);// Disable Entire Display On (0xa4/0xa5)
 	OLED_WrCmd(0xa6);// Disable Inverse Display On (0xa6/a7)
 	OLED_WrCmd(0xaf);//--turn on oled panel
	OLED_Clear();//OLED清屏
}

四、常用的OLED函数

void OLED_Clear(void)
{
  unsigned char i,n;
  for(i=0; i<8; i++)
  {
    OLED_WrCmd(0xb0+i); //设置页地址(0~7)
    OLED_WrCmd(0x00); //设置显示位置—列低地址
    OLED_WrCmd(0x10); //设置显示位置—列高地址
    for(n=0; n<128; n++)	OLED_WrData(0x00); //写0x00到屏幕寄存器上
  }
}

void OLED_SetPos(uint8 x, uint8 y)
{
	WriteCmd(0xb0+y);
	WriteCmd(((x&0xf0)>>4)|0x10);
	WriteCmd(x&0x0f);
}

//------将OLED从休眠中唤醒------
void OLED_ON(void)
{
  WriteCmd(0X8D);  //设置电荷泵
  WriteCmd(0X14);  //开启电荷泵
  WriteCmd(0XAF);  //OLED唤醒
}

//------让OLED休眠 -- 休眠模式下,OLED功耗不到10uA------
void OLED_OFF(void)
{
  WriteCmd(0X8D);  //设置电荷泵
  WriteCmd(0X10);  //关闭电荷泵
  WriteCmd(0XAE);  //OLED休眠
}

//--------------------------------------------------------------
// Parameters     : x,y -- 起始点坐标(x:0~127, y:0~7); ch[] -- 要显示的字符串; TextSize -- 字符大小(1:6*8 ; 2:8*16)
// Description    : 显示codetab.h中的ASCII字符,有6*8和8*16可选择
//--------------------------------------------------------------
void OLED_ShowStr(unsigned char x, unsigned char y, unsigned char ch[], unsigned char TextSize)
{
  unsigned char c = 0,i = 0,j = 0;
  switch(TextSize)
  {
  case 1:
  {
    while(ch[j] != '\0')
    {
      c = ch[j] - 32;
      if(x > 126)
      {
        x = 0;
        y++;
      }
      OLED_SetPos(x,y);
      for(i=0; i<6; i++)
        WriteDat(F6x8[c][i]);
      x += 6;
      j++;
    }
  }
  break;
  case 2:
  {
    while(ch[j] != '\0')
    {
      c = ch[j] - 32;
      if(x > 120)
      {
        x = 0;
        y++;
      }
      OLED_SetPos(x,y);
      for(i=0; i<8; i++)
        WriteDat(F8X16[c*16+i]);
      OLED_SetPos(x,y+1);
      for(i=0; i<8; i++)
        WriteDat(F8X16[c*16+i+8]);
      x += 8;
      j++;
    }
  }
  break;
  }
}

//****************功能描述: 显示6*8或8*16的5位整数   显示的坐标(x,y),y为页范围0~7****************************

/*例
OLED_ShowInt(0,0,0,1);   //在(0,0)处,显示6*8的"0"
OLED_ShowInt(5,4,12345,2);//在(5,4)处,显示8*16的"12345"
*/
void OLED_ShowInt(unsigned char x, unsigned char y, int Data, unsigned char TextSize)
{
  unsigned char temp;
  OLED_SetPos(x,y);
  switch(TextSize)
  {
  case 1:
  {
    if(Data<0)
    {
      OLED_ShowChar(x,y,'-',1);
      x+=6;
      Data=-Data;
    }
    //接下来要显示正数,清空上一次显示负数的个位
    //负数比正数多一个负号,额外占了一个显示位
    OLED_ShowChar(x+30,y,' ',1);

    temp=Data/10000;
    OLED_ShowChar(x,y,(temp+'0'),1);

    Data%=10000;
    temp=Data/1000;
    OLED_ShowChar(x+6,y,(temp+'0'),1);

    Data%=1000;
    temp=Data/100;
    OLED_ShowChar(x+12,y,(temp+'0'),1);

    Data%=100;
    temp=Data/10;
    OLED_ShowChar(x+18,y,(temp+'0'),1);

    Data%=10;
    temp=Data;
    OLED_ShowChar(x+24,y,(temp+'0'),1);
  }
  break;
  case 2:
  {
    if(Data<0)
    {
      OLED_ShowChar(x,y,'-',2);
      x+=8;
      Data=-Data;
    }
    //接下来要显示正数,清空上一次显示负数的个位
    //负数比正数多一个负号,额外占了一个显示位
    OLED_ShowChar(x+40,y,' ',2);

    temp=Data/10000;
    OLED_ShowChar(x,y,(temp+'0'),2);

    Data%=10000;
    temp=Data/1000;
    OLED_ShowChar(x+8,y,(temp+'0'),2);

    Data%=1000;
    temp=Data/100;
    OLED_ShowChar(x+16,y,(temp+'0'),2);

    Data%=100;
    temp=Data/10;
    OLED_ShowChar(x+24,y,(temp+'0'),2);

    Data%=10;
    temp=Data;
    OLED_ShowChar(x+32,y,(temp+'0'),2);
  }
  break;
  }
}

/***************功能描述:显示6*8或8*16一个标准ASCII字符串	显示的坐标(x,y),y为页范围0~7****************/
/*例:  OLED_ShowChar(39,0,'A',1)*/
void OLED_ShowChar(unsigned char x, unsigned char y, unsigned char ch, unsigned char TextSize)
{
  unsigned char c=0,i=0;
  c =ch-32;
  if(x>120)
  {
    x=0;
    y++;
  }
  OLED_SetPos(x,y);
  switch(TextSize)
  {
  case 1:
  {
    for(i=0; i<6; i++)
      WriteDat(F6x8[c][i]);
    break;
  }
  case 2:
  {
    for(i=0; i<8; i++)
      WriteDat(F8X16[c*16+i]);
    OLED_SetPos(x,y+1);
    for(i=0; i<8; i++)
      WriteDat(F8X16[c*16+i+8]);
    x += 8;
    break;
  }
  }
}

//--------------------------------------------------------------
// Parameters     : x0,y0 -- 起始点坐标(x0:0~127, y0:0~7); x1,y1 -- 起点对角线(结束点)的坐标(x1:1~128,y1:1~8)
// Description    : 显示BMP位图
//--------------------------------------------------------------
void OLED_DrawBMP(unsigned char x0,unsigned char y0,unsigned char x1,unsigned char y1,unsigned char BMP[])
{
  unsigned int j=0;
  unsigned char x,y;

  if(y1%8==0)
    y = y1/8;
  else
    y = y1/8 + 1;
  for(y=y0; y<y1; y++)
  {
    OLED_SetPos(x0,y);
    for(x=x0; x<x1; x++)
    {
      WriteDat(BMP[j++]);
    }
  }
}

五、咕咕咕

以上常用的OLED函数还可以替换为另外一套完整的OLED屏幕显示系统,是参照了Arduino的oled函数库的C语言版本,该版本我会看心情上传到我的github上。

 

 

 

  • 49
    点赞
  • 298
    收藏
    觉得还不错? 一键收藏
  • 12
    评论
### 回答1: 0.96寸OLED是一种小型的有机发光二极管显示屏。它是一种高亮度、高对比度、高分辨率的显示屏,适用于各种应用场合。IIC驱动程序则是操作系统或电路板与其它设备之间的通信协议,它可以进行数据传输和控制信号的交互。因此,编写0.96寸OLEDIIC驱动程序就是要实现电路板与显示屏之间的通信交互,使显示屏能够正确地显示图像和文字。 在编写0.96寸OLEDIIC驱动程序时,应首先了解其IIC接口的相关规范,包括SCL时钟速率、SDA数据线的电平和传输协议等。其次,需要了解OLED屏幕的显示控制指令,以便能够控制屏幕的显示效果。最后,就是编写具体的IIC驱动程序,主要包括向OLED发送指令和数据信息,以及控制屏幕的显示效果。 在具体的编写过程中,需要注意一些问题。首先,要确保发送的指令和数据信息能够正确地被OLED屏幕接收和解析,以便实现正确的显示效果。其次,要考虑不同操作系统和电路板的兼容性,以确保程序的可移植性和通用性。此外,还需要对程序进行不断测试和优化,以提高程序的稳定性和运行效率。 总之,编写0.96寸OLEDIIC驱动程序需要有一定的电子组装和编程知识,但也是一种具有挑战性和实用性的任务。通过不断地学习和实践,我们可以掌握其编写技巧,为各种应用场合提供高品质的显示屏幕。 ### 回答2: 0.96寸oled是一种小型显示屏,由于其小巧、高清和低功耗特点,被广泛应用于各种便携式电子产品中。而其iic驱动程序则是控制该显示屏的重要部分。 iic驱动程序的主要作用是通过iic总线与显示屏通信,将需要显示的图像或数据发送到oled屏幕上。在编写iic驱动程序时,需要考虑以下几个关键因素: 首先,需要明确o了屏幕iic地址。通常,该地址为0x3C或0x3D。然后,还需要通过iic总线初始化oled屏幕,并设置其显示模式、亮度和对比度等参数。 其次,需要将设计好的图像或数据转换成oled屏幕能够识别的格式,并通过iic总线发送到屏幕上。这通常需要使用一些特殊的函数和库,例如Adafruit或u8g2等。 最后,需要考虑屏幕的尺寸和分辨率,以保证显示效果清晰和稳定。此外,还需要在程序中加入一些延迟、清屏和休眠等功能,以降低功耗和延长oled屏幕的使用寿命。 总之,编写0.96寸olediic驱动程序需要考虑诸多因素,但只要掌握一定的知识和经验,就能轻松完成该任务,让oled屏幕在各种应用场景中发挥出其优异的性能和效果。 ### 回答3: IIC是一种串行通信总线协议,也称为I2C(Inter-Integrated Circuit)。IIC协议可以实现多个设备之间的通信,这些设备可以是不同的芯片或者模块。对于0.96寸OLED显示屏来说,我们需要使用IIC驱动程序来使其正常显示。 IIC协议中传输的数据是通过使用SCL和SDA两个线路进行同步传输的,其中SCL线路用于时钟信号的传输,SDA线路用于数据的传输。在将OLED屏幕连接到单片机或者开发板之后,我们需要根据OLED的型号和接线方式编写相应的IIC驱动程序。 在编写IIC驱动程序之前,需要先确定OLED显示屏的IIC地址,多数情况下该地址为0x3C或者0x3D。然后,我们需要根据OLED屏幕的接口协议编写读取和写入数据的函数。对于写入数据函数,通常的流程如下:首先启动IIC总线,然后发送OLED屏幕IIC地址,接着发送控制字节和具体的数据,最后释放IIC总线。对于读取数据函数,流程类似,只需要在发送IIC地址之后,向OLED屏幕发送读取命令即可。 需要注意的是,不同厂商的OLED屏幕可能存在接口协议的差异,因此在编写IIC驱动程序时需要了解具体的厂商数据手册和接口定义。同时,在实际使用过程中,还需要根据具体的应用场景对IIC驱动程序进行优化和改进,以实现更高效的数据传输和显示效果。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 12
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值