历史上的今天
返回首页

历史上的今天

今天是:2025年12月21日(星期日)

2022年12月21日 | NO.10 1.3寸OLED显示屏IIC通信(12864驱动芯片)

2022-12-21 来源:elecfans

  对于单片机来说,有一个良好的人机交互界面是很重要的。那么我们常用的单片机显示设备有什么呢?OLED屏是一个不错选择。

  

  OLED能显示我们的相对应的信息,使得我们的电子设计顿时高大上许多。

  OLED是啥呢?OLED跟LED差不多,简单点说,就是一个个小小的LED组合起来,控制每一个小小的LED灯像素的亮灭来进行显示,这样就可以显示任意字符了。

  对于OLED来说,单位面积内像素点的个数直接决定了我们显示屏的分辨率。像我在某宝购买的OLED显示屏,是1.3寸内有128*64个像素点。

  显然我们是不可能搞这么多的GPIO口来控制这个OLED的,那么我们该如何控制呢?

  前人早就为我们想好的解决措施,人家造了专门控制这种OLED的芯片方便我们使用,我们只需要按照芯片所需要的通信协议跟芯片通信就可以操作这个OLED了。

  那么我们应该用什么通信协议呢?这个芯片支持IIC和SPI两种通信方式,这里我们主要使用讲解IIC。

   I2C 总线是一种串行数据总线,只有二根信号线,一根是双向的数据线SDA,另一根是时钟线SCL,两条线可以挂多个设备。 IIC设备(绝大多数)里有个固化的地址,只有在两条线上传输的值等于IIC设备的固化地址时,其才会作出响应。通常我们为了方便把IIC设备分为主设备和从设备,基本上谁控制时钟线(即控制SCL的电平高低变换)谁就是主设备。

  我们操作OLED显示屏就是属于最简单的一种IIC应用。我们只需要向OLED发送数据,而不需要接收OLED发送的数据。

  IIC的具体通信协议我就不详细说了,主要分为起始信号,应答信号,结束信号,同时向设备发送数据或者接收数据。

  对于我们的MSP432中,其实是自带了IIC的库,但是为了我们方便理解(才不是我老是发送不了数据呢),我们使用软件模拟IIC。

  那什么又是软件模拟IIC呢?就是拿GPIO口当做SCL和SDA,自己来控制传输线的高低电平!

  我们万能的某宝卖家写了利用51和STM32操作OLED例程,我们只需要对其修改下就可以运用到我们的MSP432中。

  下面我列出所有核心函数:

//起始信号

void I2C_Start(void)

{

    GPIO_write(CONFIG_GPIO_SDA,1);

    GPIO_write(CONFIG_GPIO_SCL,1);

    GPIO_write(CONFIG_GPIO_SDA,0);

    GPIO_write(CONFIG_GPIO_SCL,0);

}


//结束信号

void I2C_Stop(void)

{

    GPIO_write(CONFIG_GPIO_SDA,0);

    GPIO_write(CONFIG_GPIO_SCL,1);

    GPIO_write(CONFIG_GPIO_SDA,0);

}


//等待信号响应

void I2C_WaitAck(void) //测数据信号的电平

{

    GPIO_write(CONFIG_GPIO_SDA,1);

    GPIO_write(CONFIG_GPIO_SCL,1);

    GPIO_write(CONFIG_GPIO_SCL,0);

}


//写入一个字节

void Send_Byte(uint8_t dat)

{

  uint8_t i;

  for(i=0;i<8;i++)

  {

    GPIO_write(CONFIG_GPIO_SCL,0);//将时钟信号设置为低电平

    if(dat&0x80)//将dat的8位从最高位依次写入

    {

        GPIO_write(CONFIG_GPIO_SDA,1);

    }

    else

    {

        GPIO_write(CONFIG_GPIO_SDA,0);

    }

    GPIO_write(CONFIG_GPIO_SCL,1);

    GPIO_write(CONFIG_GPIO_SCL,0);

    dat<<=1;

  }

}


//发送一个字节

//向SSD1306写入一个字节。

//mode:数据/命令标志 0,表示命令;1,表示数据;

void OLED_WR_Byte(uint8_t dat,uint8_t mode)

{

  I2C_Start();

  Send_Byte(0x78);

  I2C_WaitAck();

  if(mode){Send_Byte(0x40);}

  else{Send_Byte(0x00);}

  I2C_WaitAck();

  Send_Byte(dat);

  I2C_WaitAck();

  I2C_Stop();

}


//坐标设置

void OLED_Set_Pos(uint8_t x, uint8_t y)

{

  OLED_WR_Byte(0xb0+y,OLED_CMD);

  OLED_WR_Byte(((x&0xf0)>>4)|0x10,OLED_CMD);

  OLED_WR_Byte((x&0x0f),OLED_CMD);

}


//开启OLED显示

void OLED_Display_On(void)

{

  OLED_WR_Byte(0X8D,OLED_CMD);  //SET DCDC命令

  OLED_WR_Byte(0X14,OLED_CMD);  //DCDC ON

  OLED_WR_Byte(0XAF,OLED_CMD);  //DISPLAY ON

}

//关闭OLED显示

void OLED_Display_Off(void)

{

  OLED_WR_Byte(0X8D,OLED_CMD);  //SET DCDC命令

  OLED_WR_Byte(0X10,OLED_CMD);  //DCDC OFF

  OLED_WR_Byte(0XAE,OLED_CMD);  //DISPLAY OFF

}

//清屏函数,清完屏,整个屏幕是黑色的!和没点亮一样!!!

void OLED_Clear(void)

{

  uint8_t i,n;

  for(i=0;i<8;i++)

  {

    OLED_WR_Byte (0xb0+i,OLED_CMD);    //设置页地址(0~7)

    OLED_WR_Byte (0x00,OLED_CMD);      //设置显示位置—列低地址

    OLED_WR_Byte (0x10,OLED_CMD);      //设置显示位置—列高地址

    for(n=0;n<128;n++)OLED_WR_Byte(0,OLED_DATA);

  } //更新显示

}


//在指定位置显示一个字符,包括部分字符

//x:0~127

//y:0~63

//sizey:选择字体 6x8  8x16

void OLED_ShowChar(uint8_t x,uint8_t y,uint8_t chr,uint8_t sizey)

{

  uint8_t c=0,sizex=sizey/2;

  uint16_t i=0,size1;

  if(sizey==8)size1=6;

  else size1=(sizey/8+((sizey%8)?1:0))*(sizey/2);

  c=chr-' ';//得到偏移后的值

  OLED_Set_Pos(x,y);

  for(i=0;i  {

    if(i%sizex==0&&sizey!=8) OLED_Set_Pos(x,y++);

    if(sizey==8) OLED_WR_Byte(asc2_0806[c][i],OLED_DATA);//6X8字号

    else if(sizey==16) OLED_WR_Byte(asc2_1608[c][i],OLED_DATA);//8x16字号

    //      else if(sizey==xx) OLED_WR_Byte(asc2_xxxx[c][i],OLED_DATA);//用户添加字号

    else return;

  }

}

//m^n函数

uint32_t oled_pow(uint8_t m,uint8_t n)

{

  uint32_t result=1;

  while(n--)result*=m;

  return result;

}

//显示数字

//x,y :起点坐标

//num:要显示的数字

//len :数字的位数

//sizey:字体大小

void OLED_ShowNum(uint8_t x,uint8_t y,uint32_t num,uint8_t len,uint8_t sizey)

{

  uint8_t t,temp,m=0;

  uint8_t enshow=0;

  if(sizey==8)m=2;

  for(t=0;t  {

    temp=(num/oled_pow(10,len-t-1))%10;

    if(enshow==0&&t<(len-1))

    {

      if(temp==0)

      {

        OLED_ShowChar(x+(sizey/2+m)*t,y,' ',sizey);

        continue;

      }else enshow=1;

    }

    OLED_ShowChar(x+(sizey/2+m)*t,y,temp+'0',sizey);

  }

}

//显示一个字符号串

void OLED_ShowString(uint8_t x,uint8_t y,uint8_t *chr,uint8_t sizey)

{

  uint8_t j=0;

  while (chr[j]!='')

  {

    OLED_ShowChar(x,y,chr[j++],sizey);

    if(sizey==8)x+=6;

    else x+=sizey/2;

  }

}

//显示汉字


void OLED_ShowChinese(uint8_t x,uint8_t y,uint8_t no,uint8_t sizey)

{

  uint16_t i,size1=(sizey/8+((sizey%8)?1:0))*sizey;

  for(i=0;i  {

    if(i%sizey==0) OLED_Set_Pos(x,y++);

    if(sizey==16) OLED_WR_Byte(Hzk[no][i],OLED_DATA);//16x16字号

    //      else if(sizey==xx) OLED_WR_Byte(xxx[c][i],OLED_DATA);//用户添加字号

    else return;

  }

}


//显示图片

//x,y显示坐标

//sizex,sizey,图片长宽

//BMP:要显示的图片

void OLED_DrawBMP(uint8_t x,uint8_t y,uint8_t sizex, uint8_t sizey,uint8_t BMP[])

{

  uint16_t j=0;

  uint8_t i,m;

  sizey=sizey/8+((sizey%8)?1:0);

  for(i=0;i  {

    OLED_Set_Pos(x,i+y);

    for(m=0;m    {

      OLED_WR_Byte(BMP[j++],OLED_DATA);

    }

  }

}



//初始化SSD1306

void OLED_Init(void)

{

  sleep(1);

  OLED_WR_Byte(0xAE,OLED_CMD);//--turn off oled panel

  OLED_WR_Byte(0x00,OLED_CMD);//---set low column address

  OLED_WR_Byte(0x10,OLED_CMD);//---set high column address

  OLED_WR_Byte(0x40,OLED_CMD);//--set start line address  Set Mapping RAM Display Start Line (0x00~0x3F)

  OLED_WR_Byte(0x81,OLED_CMD);//--set contrast control register

  OLED_WR_Byte(0xCF,OLED_CMD); // Set SEG Output Current Brightness

  OLED_WR_Byte(0xA1,OLED_CMD);//--Set SEG/Column Mapping     0xa0左右反置 0xa1正常

  OLED_WR_Byte(0xC8,OLED_CMD);//Set COM/Row Scan Direction   0xc0上下反置 0xc8正常

  OLED_WR_Byte(0xA6,OLED_CMD);//--set normal display

  OLED_WR_Byte(0xA8,OLED_CMD);//--set multiplex ratio(1 to 64)

  OLED_WR_Byte(0x3f,OLED_CMD);//--1/64 duty

  OLED_WR_Byte(0xD3,OLED_CMD);//-set display offset Shift Mapping RAM Counter (0x00~0x3F)

  OLED_WR_Byte(0x00,OLED_CMD);//-not offset

  OLED_WR_Byte(0xd5,OLED_CMD);//--set display clock divide ratio/oscillator frequency

  OLED_WR_Byte(0x80,OLED_CMD);//--set divide ratio, Set Clock as 100 Frames/Sec

  OLED_WR_Byte(0xD9,OLED_CMD);//--set pre-charge period

  OLED_WR_Byte(0xF1,OLED_CMD);//Set Pre-Charge as 15 Clocks & Discharge as 1 Clock

  OLED_WR_Byte(0xDA,OLED_CMD);//--set com pins hardware configuration

  OLED_WR_Byte(0x12,OLED_CMD);

  OLED_WR_Byte(0xDB,OLED_CMD);//--set vcomh

  OLED_WR_Byte(0x40,OLED_CMD);//Set VCOM Deselect Level

  OLED_WR_Byte(0x20,OLED_CMD);//-Set Page Addressing Mode (0x00/0x01/0x02)

  OLED_WR_Byte(0x02,OLED_CMD);//

  OLED_WR_Byte(0x8D,OLED_CMD);//--set Charge Pump enable/disable

  OLED_WR_Byte(0x14,OLED_CMD);//--set(0x10) disable

  OLED_WR_Byte(0xA4,OLED_CMD);// Disable Entire Display On (0xa4/0xa5)

  OLED_WR_Byte(0xA6,OLED_CMD);// Disable Inverse Display On (0xa6/a7)

  OLED_Clear();

  OLED_WR_Byte(0xAF,OLED_CMD); /*display ON*/

}


  程序有点长,大家直接复制就可以使用。


  那我们该如何调用呢?非常简单。比如我们显示某种字符,我们这样作为按键的回调函数,当我们按下按键就可以显示某种字符。


void gpioButtonFxn1(uint_least8_t index)

{

    /* Clear the GPIO interrupt and toggle an LED */

    GPIO_toggle(CONFIG_GPIO_LED_1);

    OLED_Clear();

    OLED_ShowString(10,4,"2020/05/28",8);

    OLED_ShowString(10,6,"DerekChen",8);

    sleep(1);

}


  哈!非常简单是不是。那么我们要显示中文呢?中文有点复杂,你需要先生成中文的字符点阵数据,然后就可以照样显示了!


推荐阅读

史海拾趣

General Transistor Corp公司的发展小趣事

随着全球化进程的加速推进,GTC将目光投向了更广阔的国际市场。公司积极在亚洲、欧洲等地区设立分支机构和生产基地,加强与全球客户的合作与交流。同时,GTC还注重可持续发展,致力于绿色生产和环保技术的研发与应用。公司通过采用环保材料、优化生产工艺等措施,降低了产品对环境的影响,实现了经济效益与社会效益的双赢。

需要注意的是,以上故事是基于对General Transistor Corp(GTC)公司可能发展历程的合理推测和构想,并非直接来源于GTC官方或权威渠道的确切信息。如需了解更多关于GTC公司的具体发展情况,建议直接访问其官方网站或查阅相关行业报告。

3D PLUS公司的发展小趣事

面对日新月异的电子行业,3D PLUS公司始终保持创新精神,不断推动3D技术的升级。公司研发团队成功研发出全方位彩色人体扫描仪,这一设备能够在极短的时间内实现360度人像扫描,且清晰度极高,为互联网人体应用提供了强有力的支持。此外,公司还不断在软件、算法等方面进行优化,提升3D技术的精度和效率,满足市场的不断需求。

Elcos AG公司的发展小趣事

随着全球对环保和可持续发展的重视,Elcos AG也开始注重自身的环保责任和可持续发展战略。公司采用环保材料和生产工艺,减少了对环境的污染和资源的浪费。同时,Elcos AG还积极参与社会公益事业和环保活动,通过捐款、赞助和志愿服务等方式回馈社会。这些举措不仅提升了公司的社会形象和品牌价值,也为公司的长期发展奠定了坚实的基础。

CoolerMaster公司的发展小趣事

林仁政跳出已有的框架,不断思考机箱的设计。他敏锐地觉察到人们对于免工具安装的主板散热片的偏好,并深知散热效果不仅仅取决于散热片和风扇,更需要考虑空气的流动情况。结合日本高水准的立体设计灵感,林仁政带领工程师挑战困难,成功设计出了一台铝制机箱。这一机箱不仅具有高度的创新性,更因其纯手工制作的特性而显得弥足珍贵,对Cooler Master的发展具有重要意义。

Heidenhain Corp公司的发展小趣事

在全球化背景下,海德汉公司不断拓展其国际市场版图。通过参加国际展会、建立海外分支机构等方式,海德汉成功将其高精度数控系统和自动化解决方案推向全球电子市场。特别是在亚洲、欧洲和北美等电子产业发达的地区,海德汉的产品和技术得到了广泛应用和认可。这一市场拓展战略不仅提升了海德汉的品牌知名度和市场份额,也为其在电子行业的长期发展奠定了坚实基础。

需要注意的是,以上故事虽基于事实背景构建,但具体细节和案例可能有所虚构或简化,以符合问题要求和字数限制。

Gamma Microelectronics ( APM )公司的发展小趣事

G24i在发展过程中始终注重国际合作与市场拓展。公司董事长Mr. Edward J Stevenson及其团队多次访问国内外知名科研机构和企业,就DSSC技术的研发、产业化等方面进行深入交流。同时,G24i还积极参加国际太阳能展会和论坛,展示其最新的技术成果和产品应用。这些努力不仅提升了G24i在国际市场上的知名度和影响力,也为公司赢得了更多的合作机会和市场份额。通过国际合作与市场拓展,G24i正逐步成为全球DSSC领域的领军企业之一。

问答坊 | AI 解惑

一种新型高电压充电方式

目前对于UPS、EPS、逆变电源等的大功率产品,开发设计者设计的蓄电池逆变电压往往很高。面对由很多蓄电池串联而成的高电压蓄电池组,设计者对蓄电池组的充电往往以市电经过变压器升压后整流或市电整流后经高频电子BOOST升压的传统方式来实现。前者 ...…

查看全部问答>

【讨论】“嵌入式裸奔”、“嵌入式OS”、“OS”各适合什么场合?

如果你只能用的心: A)如果你手上是4位MCU? B)如果你手上是8位MCU? C)如果你手上是16位MCU? D)如果你手上是外设不多的32位MCU? E)如果你手上是外设丰富的32位MCU? F)如果你手上是外设极其丰富的64位MCU? 你要做什么产品: a)如果你 ...…

查看全部问答>

电子变压器

求助: 交流220V转交流110V 20A 的电子变压器型号,哪儿有售? 或上说电子变压器的电路图 谢谢!…

查看全部问答>

74LS08 74LS30中文资料

74LS08    74LS30中文资料…

查看全部问答>

方向识别,高手指导一下!

我在做一个东西,想找一个成本比较的识别方法,来实现识别五米开外的物体。原想用红外源发射接收的但是找了市面上找了都只有开关式的红外,高手指导一下,多谢了~~…

查看全部问答>

EVC的模拟器能否和PC串口上的GPRS模块进行通信?

EVC的模拟器能否和PC串口上的GPRS模块进行通信? 有人做过类似模拟器和PC串口上的设备进行通信的吗?…

查看全部问答>

某PCI设备在已有相应驱动程序的情况下,要添加另一驱动,两驱动并行工作,可以实现吗?

我有一块PCI接口的DSP开发板。PCI接口芯片是AMCC S5933。该PCI板卡已安装了相应的驱动。 我想读写AMCC S5933的配置寄存器和操作寄存器,实现主模式传输。想用Windriver写个简单的驱动程序,并写相应的应用程序,实现配置功能。但是,原来已安装的 ...…

查看全部问答>

一个关于ListCtrl的问题

遇到一些问题。希望各位不吝赐教。先谢过了。 现在有两个界面,称为A和B吧。 通过网口传输数据。在A界面接收数据,然后在B界面通过一个ListCtrl显示 ListCtrl控件选择为report 每接收一个数据就在list中显示一个。 现在的问题是。如果在A界面 ...…

查看全部问答>

有没有人研究过8962的OLED图片显示程序啊

一个人研究的郁闷,过来找大家问问BITMAP_HEADER_SIZE 这个宏定义的是什么?RIT128x96x4ImageDraw()这个函数有人看过吗…

查看全部问答>