[其他ST产品] STM32外设系列—OLED

[复制链接]
 楼主| rzjvv 发表于 2023-7-29 15:45 | 显示全部楼层 |阅读模式
一、OLED简介
OLED是开发时常用的显示器件,这里介绍的是0.96寸的四针脚OLED,IIC控制。

873364c4c37e2b695.png

 楼主| rzjvv 发表于 2023-7-29 15:45 | 显示全部楼层
二、数据手册分析
2.1 供电电压
供电电压介绍
5592564c4c39294248.png
供电电压在3V~5V,用3.3V即可。
 楼主| rzjvv 发表于 2023-7-29 15:50 | 显示全部楼层
2.2 引脚定义
6799764c4c3afec6b1.png
 楼主| rzjvv 发表于 2023-7-29 15:50 | 显示全部楼层
2.3 原理图介绍
OLED原理图
8829164c4c4bc5d56a.png
I2C通信接口由从地址位DC、I2C总线数据信号SDA(输出SDAOUT/D2输出,SDAIN/D1输入)和I2C总线时钟信号SCL(D0)组成。数据信号和时钟信号都必须连接到上拉电阻器上。
 楼主| rzjvv 发表于 2023-7-29 15:51 | 显示全部楼层
从属地址位(SA0)必须在通过I2C总线传输或接收任何信息之前,先识别该从属地址。设备将响应从地址,后面是从地址位(“SA0”位)和读/写选择位(“R/W#”,具有以下字节格式的位)

5556464c4c4e106388.png

“SA0”位为从属地址提供了一个扩展位。可以选择“0111100”或“0111101”作为OLED的从属地址。D/C# pin作为SA0进行从属地址的选择”。“收发#”位用于确定I2c总线接口的操作模式。R/W#=1,它处于读取模式。R/W#=0,它处于写模式。

7093864c4c4f668ed7.png
 楼主| rzjvv 发表于 2023-7-29 15:51 | 显示全部楼层
2.4 数据手册程序
数据手册中会给出一些底层程序,可以通过这写程序快速的了解到如何使用。后面的程序设计中会详细介绍这部分,这里就不再做介绍了。
 楼主| rzjvv 发表于 2023-7-29 15:55 | 显示全部楼层
三、IIC通信
3.1 什么是IIC
IIC(Inter-Integrated Circuit)其实是IICBus简称,中文是集成电路总线,它是一种串行通信总线,使用多主从架构,由飞利浦公司在1980年代为了让主板、嵌入式系统或手机用以连接低速周边设备而发展。IIC支持一主多从,主机通过寻址的方式呼叫从机,然后进行数据传输。

I2C串行总线一般有两根信号线,一根是双向的数据线SDA,另一根是时钟线SCL。所有从设备的SDA和SLK都接到总线上。
 楼主| rzjvv 发表于 2023-7-29 15:55 | 显示全部楼层
3.2 IIC通信协议
IIC协议中有两种帧,一种是地址帧,用来寻找从设备。一种是数据帧,用来作主从机之间的数据交互。IIC协议还规定了起始信号,终止信号和应答信号。

7914364c4c5e83517f.png
空闲状态
I2C总线总线的SDA和SCL两条信号线同时处于高电平时,规定为总线的空闲状态。此时各个器件的输出级场效应管均处在截止状态,即释放总线,由两条信号线各自的上拉电阻把电平拉高。
起始信号
SCL为高电平期间,SDA信号线由高电平向低电平的变化表示起始信号
终止信号
SCL为高电平期间,SDA由低电平向高电平的变化表示终止信号
 楼主| rzjvv 发表于 2023-7-29 15:56 | 显示全部楼层
起始信号和终止信号都是由主机发送的,起始信号产生之后,总线处于被占用的状态,在终止信号产生之后,总线就处于空闲状态。

当从机接收完一帧时,会发送一个应答信号。应答信号为低电平时,规定为有效应答(ACK,简称应答位),表示接收器已经成功地接受了该字节应答位为高电平时,规定为非应答信号(NACK),一般表示接收器接收该字节没有成功。

IIC通信协议有自己的数据格式,每一个字节必须保证是8bit长度。数据传送时,先传送最高位,每一个被传送的字节后面都必须跟随1bit的应答位(即每一帧数据一共有9bit)。
 楼主| rzjvv 发表于 2023-7-29 15:56 | 显示全部楼层
3.3 IIC主从通信过程
3.3.1 写入数据
主设备往从设备写入数据需要有下面的过程

主设备发送一个起始信号(START)
主设备在数据线上广播从设备地址
确定数据传输方向(R/W)
等待从设备应答信号(ACK)
主设备发送数据到从设备,从设备接收到后,会返回给主设备一个应答信号,直到主设备发送完数据,或者从设备返回一个NACK信号,表示从设备不再接收数据
数据发送完毕,主设备发送终止信号(STOP)
7581164c4c63041d11.png
 楼主| rzjvv 发表于 2023-7-29 15:57 | 显示全部楼层
3.3.2 读取数据
主设备发送起始信号(START)
主设备在数据线上广播从设备地址
确定数据传输方向(R/W)
等待从设备应答信号(ACK)
从机向主机发送需要读取的数据
主设备每接收到一个字节数据,后面就会跟着向从设备发送一个应答信号
接收完最后一个数据后,主机会发送一个无效应答信号(NACK)
主设备发送终止信号,数据读取完毕
8070664c4c64abfb4c.png
 楼主| rzjvv 发表于 2023-7-29 15:57 | 显示全部楼层
四、OLED程序设计
4.1 OLED初始化
OLED初始化包括两部分,一部分是初始化STM32的GPIO,另一部分是根据数据手册提供的程序初始化OLED。STM32F103ZET6提供了两个IIC接口
5871064c4c67472872.png
实际使用软件模拟IIC时,使用任意的GPIO即可,不必要使用上面规定的IIC引脚。这里只是使用了IIC1的引脚而已。
 楼主| rzjvv 发表于 2023-7-29 15:58 | 显示全部楼层
首先是数据手册提供的IIC相关函数
  1. /*
  2. *==============================================================================
  3. *函数名称:IIC_delay
  4. *函数功能:IIC延时
  5. *输入参数:无
  6. *返回值:无
  7. *备  注:数据手册提供
  8. *==============================================================================
  9. */
  10. void IIC_delay (void)
  11. {
  12.         u8 t = 1;
  13.         while (t--);
  14. }
  15. /*
  16. *==============================================================================
  17. *函数名称:I2C_Start
  18. *函数功能:IIC起始信号
  19. *输入参数:无
  20. *返回值:无
  21. *备  注:数据手册提供
  22. *==============================================================================
  23. */
  24. void I2C_Start (void)
  25. {
  26.         OLED_SDA_Set();
  27.         OLED_SCL_Set();
  28.         IIC_delay();
  29.         OLED_SDA_Clr();
  30.         IIC_delay();
  31.         OLED_SCL_Clr();
  32.         IIC_delay();
  33. }
  34. /*
  35. *==============================================================================
  36. *函数名称:I2C_Stop
  37. *函数功能:IIC终止信号
  38. *输入参数:无
  39. *返回值:无
  40. *备  注:数据手册提供
  41. *==============================================================================
  42. */
  43. void I2C_Stop (void)
  44. {
  45.         OLED_SDA_Clr();
  46.         OLED_SCL_Set();
  47.         IIC_delay();
  48.         OLED_SDA_Set();
  49. }
  50. /*
  51. *==============================================================================
  52. *函数名称:I2C_WaitAck
  53. *函数功能:IIC等待应答
  54. *输入参数:无
  55. *返回值:无
  56. *备  注:数据手册提供
  57. *==============================================================================
  58. */
  59. void I2C_WaitAck (void)
  60. {
  61.         OLED_SDA_Set();
  62.         IIC_delay();
  63.         OLED_SCL_Set();
  64.         IIC_delay();
  65.         OLED_SCL_Clr();
  66.         IIC_delay();
  67. }
  68. /*
  69. *==============================================================================
  70. *函数名称:Send_Byte
  71. *函数功能:写入一个字节
  72. *输入参数:dat:需要写入的数据
  73. *返回值:无
  74. *备  注:数据手册提供
  75. *==============================================================================
  76. */
  77. void Send_Byte (u8 dat)
  78. {
  79.         u8 i;

  80.         for (i = 0;i < 8;i ++)
  81.         {
  82.                 // 发送数据时,从高位依次写入
  83.                 if (dat & 0x80)
  84.                 {
  85.                         OLED_SDA_Set();
  86.                 }
  87.                 else
  88.                 {
  89.                         OLED_SDA_Clr();
  90.                 }
  91.                 IIC_delay();
  92.                 OLED_SCL_Set();
  93.                 IIC_delay();
  94.                 OLED_SCL_Clr();
  95.                
  96.                 // dat左移1位
  97.                 dat <<= 1;
  98.         }
  99. }
  100. /*
  101. *==============================================================================
  102. *函数名称:OLED_WR_Byte
  103. *函数功能:IIC发送一个字节数据
  104. *输入参数:dat:要发送的数据;mode:0是指令,1是数据
  105. *返回值:无
  106. *备  注:无
  107. *==============================================================================
  108. */
  109. void OLED_WR_Byte (u8 dat,u8 mode)
  110. {
  111.         I2C_Start();
  112.         Send_Byte(0x78);   // 寻址
  113.         I2C_WaitAck();
  114.        
  115.         // 发送数据
  116.         if (mode)
  117.         {
  118.                 Send_Byte(0x40);
  119.         }
  120.         // 发送指令
  121.         else
  122.         {
  123.                 Send_Byte(0x00);
  124.         }
  125.         I2C_WaitAck();
  126.         Send_Byte(dat);
  127.         I2C_WaitAck();
  128.         I2C_Stop();
  129. }
  130. /*
  131. *==============================================================================
  132. *函数名称:Drv_Oled_Init
  133. *函数功能:初始化OLED
  134. *输入参数:无
  135. *返回值:无
  136. *备  注:无
  137. *==============================================================================
  138. */
  139. void Drv_Oled_Init (void)
  140. {
  141.         // 结构体定义
  142.         GPIO_InitTypeDef  GPIO_InitStructure;
  143.        
  144.         // 开启时钟
  145.         RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
  146.        
  147.         // 初始化GPIO结构体
  148.         GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7;         
  149.         GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;   // 推挽式输出
  150.         GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  151.         GPIO_Init(GPIOB, &GPIO_InitStructure);
  152.         // 全部拉高,IIC处于空闲状态
  153.         GPIO_SetBits(GPIOB,GPIO_Pin_6 | GPIO_Pin_7);       
  154.        
  155.         // 根据数据手册提供的例程,初始化OLED
  156.         delay_ms(200);   // 延时200ms
  157.         OLED_WR_Byte(0xAE,OLED_CMD);//--display off
  158.         OLED_WR_Byte(0x00,OLED_CMD);//---set low column address
  159.         OLED_WR_Byte(0x10,OLED_CMD);//---set high column address
  160.         OLED_WR_Byte(0x40,OLED_CMD);//--set start line address  
  161.         OLED_WR_Byte(0xB0,OLED_CMD);//--set page address
  162.         OLED_WR_Byte(0x81,OLED_CMD); // contract control
  163.         OLED_WR_Byte(0xFF,OLED_CMD);//--128   
  164.         OLED_WR_Byte(0xA1,OLED_CMD);//set segment remap
  165.         OLED_WR_Byte(0xA6,OLED_CMD);//--normal / reverse
  166.         OLED_WR_Byte(0xA8,OLED_CMD);//--set multiplex ratio(1 to 64)
  167.         OLED_WR_Byte(0x3F,OLED_CMD);//--1/32 duty
  168.         OLED_WR_Byte(0xC8,OLED_CMD);//Com scan direction
  169.         OLED_WR_Byte(0xD3,OLED_CMD);//-set display offset
  170.         OLED_WR_Byte(0x00,OLED_CMD);//       
  171.         OLED_WR_Byte(0xD5,OLED_CMD);//set osc division
  172.         OLED_WR_Byte(0x80,OLED_CMD);//       
  173.         OLED_WR_Byte(0xD8,OLED_CMD);//set area color mode off
  174.         OLED_WR_Byte(0x05,OLED_CMD);//       
  175.         OLED_WR_Byte(0xD9,OLED_CMD);//Set Pre-Charge Period
  176.         OLED_WR_Byte(0xF1,OLED_CMD);//       
  177.         OLED_WR_Byte(0xDA,OLED_CMD);//set com pin configuartion
  178.         OLED_WR_Byte(0x12,OLED_CMD);//       
  179.         OLED_WR_Byte(0xDB,OLED_CMD);//set Vcomh
  180.         OLED_WR_Byte(0x30,OLED_CMD);//       
  181.         OLED_WR_Byte(0x8D,OLED_CMD);//set charge pump enable
  182.         OLED_WR_Byte(0x14,OLED_CMD);//       
  183.         OLED_WR_Byte(0xAF,OLED_CMD);//--turn on oled panel
  184. }
 楼主| rzjvv 发表于 2023-7-29 15:58 | 显示全部楼层
下面是相关宏定义
  1. // SCL
  2. #define OLED_SCL_Clr()   GPIO_ResetBits(GPIOB,GPIO_Pin_6)
  3. #define OLED_SCL_Set()   GPIO_SetBits(GPIOB,GPIO_Pin_6)
  4. // SDA
  5. #define OLED_SDA_Clr()   GPIO_ResetBits(GPIOB,GPIO_Pin_7)
  6. #define OLED_SDA_Set()   GPIO_SetBits(GPIOB,GPIO_Pin_7)

  7. #define OLED_CMD   0   //写命令
  8. #define OLED_DATA   1   //写数据
 楼主| rzjvv 发表于 2023-7-29 15:59 | 显示全部楼层
4.2 OLED控制函数编写
数据手册中提供了一些指令,在编写OLED控制函数时可以参考。

1354464c4c6cb11252.png

 楼主| rzjvv 发表于 2023-7-29 15:59 | 显示全部楼层
4.2.1 OLED显示开/关程序
根据数据手册描述,发送指令“1010 111x0”,其中x0为0时,显示关闭,x0为1时,显示开启。因此,显示开关程序如下
  1. /*
  2. *==============================================================================
  3. *函数名称:Med_Oled_Display_On
  4. *函数功能:开启OLED显示
  5. *输入参数:无
  6. *返回值:无
  7. *备  注:无
  8. *==============================================================================
  9. */  
  10. void Med_Oled_Display_On(void)
  11. {
  12.         OLED_WR_Byte(0X8D,OLED_CMD);   // 设置充电泵启用/禁用
  13.         OLED_WR_Byte(0X14,OLED_CMD);   // 设置(0x10)禁用
  14.         OLED_WR_Byte(0XAF,OLED_CMD);   // DISPLAY ON
  15. }
  16. /*
  17. *==============================================================================
  18. *函数名称:Med_Oled_Display_Off
  19. *函数功能:关闭OLED显示
  20. *输入参数:无
  21. *返回值:无
  22. *备  注:无
  23. *==============================================================================
  24. */  
  25. void OLED_Display_Off(void)
  26. {
  27.         OLED_WR_Byte(0X8D,OLED_CMD);   // 设置充电泵启用/禁用
  28.         OLED_WR_Byte(0X10,OLED_CMD);   // 设置高列地址
  29.         OLED_WR_Byte(0XAE,OLED_CMD);   // DISPLAY OFF
  30. }       
 楼主| rzjvv 发表于 2023-7-30 00:52 | 显示全部楼层
4.2.2 OLED显示一组汉字程序
想要显示汉字,需要先创建字库。汉字取模使用PCtoLCD2002完美版,取模时的配置已经标注

  1. // 汉字字模数据结构定义
  2. struct Cn16CharTypeDef   // 汉字字模数据结构
  3. {
  4.         unsigned char Index[2];   // 汉字内码索引,一个汉字占两个字节       
  5.         unsigned char Msk[32];   // 点阵码数据(16*16/8)
  6. };

  7. // 汉字取模要求
  8. // 阴码,逆向,列行式,16X16
  9. struct Cn16CharTypeDef const CnChar16x16[]=
  10. {
  11. "太",0x20,0x20,0x20,0x20,0x20,0x20,0x20,0xFF,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x00,
  12. 0x80,0x80,0x40,0x20,0x10,0x0C,0x13,0x60,0x03,0x0C,0x10,0x20,0x40,0x80,0x80,0x00,/*"太",0*/

  13. "陈",0x00,0xFE,0x22,0x5A,0x86,0x08,0x88,0x68,0x18,0x0F,0xE8,0x08,0x08,0x08,0x08,0x00,
  14. 0x00,0xFF,0x04,0x08,0x07,0x20,0x11,0x0D,0x41,0x81,0x7F,0x01,0x05,0x09,0x30,0x00,/*"陈",1*/

  15. "抱",0x10,0x10,0x10,0xFF,0x90,0x20,0x10,0xEC,0x27,0x24,0x24,0xE4,0x04,0xFC,0x00,0x00,
  16. 0x02,0x42,0x81,0x7F,0x00,0x00,0x00,0x3F,0x42,0x42,0x4A,0x53,0x48,0x47,0x70,0x00,/*"抱",2*/

  17. "不",0x00,0x02,0x02,0x02,0x02,0x82,0x42,0xF2,0x0E,0x42,0x82,0x02,0x02,0x02,0x00,0x00,
  18. 0x10,0x08,0x04,0x02,0x01,0x00,0x00,0xFF,0x00,0x00,0x00,0x01,0x02,0x0C,0x00,0x00,/*"不",3*/

  19. "动",0x40,0x44,0xC4,0x44,0x44,0x44,0x40,0x10,0x10,0xFF,0x10,0x10,0x10,0xF0,0x00,0x00,
  20. 0x10,0x3C,0x13,0x10,0x14,0xB8,0x40,0x30,0x0E,0x01,0x40,0x80,0x40,0x3F,0x00,0x00,/*"动",4*/

  21. };
 楼主| rzjvv 发表于 2023-7-30 00:52 | 显示全部楼层
汉字显示函数如下

  1. /*
  2. *==============================================================================
  3. *函数名称:Med_Oled_ShowCHinese16x16
  4. *函数功能:显示一组16*16的汉字
  5. *输入参数:x:横坐标;y:纵坐标(0~3);cn:要显示的汉字
  6. *返回值:无
  7. *备  注:无
  8. *==============================================================================
  9. */
  10. void Med_Oled_ShowCHinese16x16 (u8 x,u8 y,u8 *cn)
  11. {                                  
  12.         u8 i,num;
  13.        
  14.         while (*cn != '\0')
  15.         {
  16.                 // 扫描字库
  17.                 // 这里的100,是字库所能容纳的汉字上限,可修改
  18.                 for (num = 0;num < 100;num ++)
  19.                 {
  20.                         // 如果找到匹配的汉字
  21.                         if ((CnChar16x16[num].Index[0]==*cn)
  22.                                                  &&(CnChar16x16[num].Index[1]==*(cn+1)))
  23.                         {
  24.                                 // 显示前16个点
  25.                                 Med_Oled_Set_Pos(x,y);       
  26.                                 for(i=0;i<16;i++)
  27.                                 {
  28.                                         OLED_WR_Byte(CnChar16x16[num].Msk[i],OLED_DATA);
  29.                                 }
  30.                                 // 显示后16个点
  31.                                 Med_Oled_Set_Pos(x,y+1);       
  32.                                 for(i=16;i<32;i++)
  33.                                 {
  34.                                         OLED_WR_Byte(CnChar16x16[num].Msk[i],OLED_DATA);
  35.                                 }
  36.                         }
  37.                 }
  38.                 cn += 2;
  39.                 x += 16;   //若改字号,需要改
  40.         }       
  41. }
 楼主| rzjvv 发表于 2023-7-30 00:52 | 显示全部楼层
当需要显示汉字时,只需要写下面的程序,即可直接显示一组汉字

  1. Med_Oled_ShowCHinese16x16(1,2,"太陈抱不动");   // 显示一组汉字
 楼主| rzjvv 发表于 2023-7-30 00:53 | 显示全部楼层
4.2.3 OLED显示一串字符串
要显示字符串,也要有对应的字库,这里提供了8号和16号字库,就不再列出了。想要显示一串字符串,需要先编写显示字符函数
  1. /*
  2. *==============================================================================
  3. *函数名称:Med_Oled_ShowChar
  4. *函数功能:显示一个字符
  5. *输入参数:x:横坐标;y:纵坐标;chr:要显示的字符串;Char_Size:大小(8/16)
  6. *返回值:无
  7. *备  注:无
  8. *==============================================================================
  9. */
  10. void Med_Oled_ShowChar(u8 x,u8 y,u8 chr,u8 Char_Size)
  11. {             
  12.         u8 c=0,i=0;       
  13.         c=chr-' ';//得到偏移后的值                       
  14.         if(x > Max_Column-1){x=0;y=y+2;}
  15.         if(Char_Size ==16)
  16.         {
  17.                 Med_Oled_Set_Pos(x,y);       
  18.                 for(i=0;i<8;i++)
  19.                 OLED_WR_Byte(F8X16[c*16+i],OLED_DATA);
  20.                 Med_Oled_Set_Pos(x,y+1);
  21.                 for(i=0;i<8;i++)
  22.                 OLED_WR_Byte(F8X16[c*16+i+8],OLED_DATA);
  23.         }
  24.         else
  25.         {       
  26.                 Med_Oled_Set_Pos(x,y);
  27.                 for(i=0;i<6;i++)
  28.                 OLED_WR_Byte(F6x8[c][i],OLED_DATA);
  29.         }
  30. }
您需要登录后才可以回帖 登录 | 注册

本版积分规则

17

主题

270

帖子

1

粉丝
快速回复 在线客服 返回列表 返回顶部

17

主题

270

帖子

1

粉丝
快速回复 在线客服 返回列表 返回顶部