[其他ST产品] STM32-----OLED显示实验

[复制链接]
1337|24
 楼主| 610u 发表于 2023-1-28 16:02 | 显示全部楼层 |阅读模式
STM32-----OLED显示实验(8080并行通信,SPI串行通信)OLED 简介
OLED ,即有机发光二极管( Organic Light-Emitting Diode ),又称为有机电激光显示( Organic
Electroluminesence Display , OELD )。 OLED 由于同时具备自发光,不需背光源、对比度高、
厚度薄、视角广、反应速度快、可用于挠曲性面板、使用温度范围广、构造及制程较简单等优
异之特性,被认为是下一代的平面显示器新兴应用技术。
LCD 都需要背光,而 OLED 不需要,因为它是自发光的。这样同样的显示, OLED 效果要
来得好一些。以目前的技术, OLED 的尺寸还难以大型化,但是分辨率确可以做到很高。在本
章中,我们使用的是 ALINETEK 的 OLED 显示模块,该模块有以下特点:
1 )模块有单色和双色两种可选,单色为纯蓝色,而双色则为黄蓝双色。
2 )尺寸小,显示尺寸为 0.96 寸,而模块的尺寸仅为 27mm*26mm 大小。
3 )高分辨率,该模块的分辨率为 128*64。 (这里注意128和64单位均为:bit)。
4 )多种接口方式,该模块提供了总共 4 种接口包括: 6800 、 8080 两种并行接口方式、 4
线 SPI 接口方式以及 IIC 接口方式(只需要 2 根线就可以控制 OLED 了!)。
5 )不需要高压,直接接 3.3V 就可以工作了。(接5V容易烧毁)
————————————————
版权声明:本文为CSDN博主「夜路难行々」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/weixin_63032791/article/details/126494501


 楼主| 610u 发表于 2023-1-28 16:02 | 显示全部楼层
OLED的四种模式:
1585763d4d6a93b43d.png
买回来的一般都是BS1 和BS2都接的VCC,如果你要更还模式,需要用电烙铁进行修改。
 楼主| 610u 发表于 2023-1-28 16:05 | 显示全部楼层
8080并行通信:

8080 并行接口的发明者是 INTEL ,该总线也被
广泛应用于各类液晶显示器, ALIENTEK OLED 模块也提供了这种接口,使得 MCU 可以快速
的访问 OLED 。 ALIENTEK OLED 模块的 8080 接口方式需要如下一些信号线:
CS : OLED 片选信号。
WR :向 OLED 写入数据。
RD :从 OLED 读取数据。
D[7 : 0] : 8 位双向数据线。
RST(RES) :硬复位 OLED 。
DC :命令 / 数据标志(
0 ,读写命令; 1 ,读写数据)。
 楼主| 610u 发表于 2023-1-28 16:06 | 显示全部楼层
模块的 8080 并口读 / 写的过程为:先根据要写入 / 读取的数据的类型,设置 DC 为高(数据)
/ 低(命令),然后拉低片选,选中 SSD1306 ,接着我们根据是读数据,还是要写数据置 RD/WR
为低,然后:
在 RD 的上升沿, 使数据锁存到数据线( D[7 : 0] )上;
在 WR 的上升沿,使数据写入到 SSD1306 里面;
3592663d4d76218900.png
 楼主| 610u 发表于 2023-1-28 16:07 | 显示全部楼层
SPI串行通信:

CS : OLED 片选信号。
RST(RES) :硬复位 OLED 。
DC :命令 / 数据标志(
0 ,读写命令; 1 ,读写数据)。
SCLK :串行时钟线。在 4 线串行模式下, D0 信号线作为串行时钟线 SCLK 。
SDIN :串行数据线。在 4 线串行模式下, D1 信号线作为串行数据线 SDIN 。
模块的 D2 需要悬空,其他引脚可以接到 GND 。在 4 线串行模式下,只能往模块写数据而
不能读数据。
 楼主| 610u 发表于 2023-1-28 16:08 | 显示全部楼层
在 4 线 SPI 模式下,每个数据长度均为 8 位,在 SCLK 的上升沿,数据从 SDIN 移入到
SSD1306 ,并且是高位在前的。 DC 线还是用作命令 / 数据的标志线。
模块显存:SSD1306 的显存总共为 128*64bit 大小,SSD1306将这些显存分为8页,每一页都是128字节,也就是128*8bit,总共就是128*64bit。
而OLED显示的原理也就是在这些每个位选择(1/0)的问题了。所以我们在OLED内部建立一个GRAM(128*8字节),每次修改的时候直接修改GRAM的数值就可以了,修改完后直接一次性进行更改。
 楼主| 610u 发表于 2023-1-28 16:08 | 显示全部楼层
SSD1306的命令介绍:
2977263d4d81044c05.png
 楼主| 610u 发表于 2023-1-28 16:10 | 显示全部楼层
  1. OLED_WR_Byte(0x81,OLED_CMD); //对比度设置
  2. OLED_WR_Byte(0xEF,OLED_CMD); //1~255;默认0X7F (亮度设置,越大越亮)
 楼主| 610u 发表于 2023-1-28 16:15 | 显示全部楼层
第二个命令为 0XAE/0XAF 。 0XAE 为关闭显示命令; 0XAF 为开启显示命令。
  1. OLED_WR_Byte(0xAE,OLED_CMD); //关闭显示

  2. OLED_WR_Byte(0xAF,OLED_CMD); //开启显示
 楼主| 610u 发表于 2023-1-28 16:15 | 显示全部楼层
第三个命令为 0X8D ,该指令也包含 2 个字节,第一个为命令字,第二个为设置值,第二
个字节的 BIT2 表示电荷泵的开关状态,该位为 1 ,则开启电荷泵,为 0 则关闭。在模块初始化
的时候,这个必须要开启,否则是看不到屏幕显示的。
 楼主| 610u 发表于 2023-1-28 16:15 | 显示全部楼层
  1. //开启OLED显示   
  2. void OLED_Display_On(void)
  3. {
  4.     //开启电荷泵(命令)
  5.         OLED_WR_Byte(0X8D,OLED_CMD);  
  6.     //开启电荷泵的具体设置    0X14 = 10100
  7.         OLED_WR_Byte(0X14,OLED_CMD);  //DCDC ON
  8.     //打开屏显
  9.         OLED_WR_Byte(0XAF,OLED_CMD);  //DISPLAY ON
  10. }
  11. //关闭OLED显示     
  12. void OLED_Display_Off(void)
  13. {
  14.     //开启电荷泵(命令)
  15.         OLED_WR_Byte(0X8D,OLED_CMD);  //SET DCDC命令
  16.     //关闭电荷泵的具体设置    0X10 = 10000
  17.         OLED_WR_Byte(0X10,OLED_CMD);  //DCDC OFF
  18.     //关闭屏显
  19.         OLED_WR_Byte(0XAE,OLED_CMD);  //DISPLAY OFF
  20. }
 楼主| 610u 发表于 2023-1-28 16:19 | 显示全部楼层
这里的命令和设置分开,我的理解是:类似于排长给队长下达了一个命令(攻破碉堡),然后队长还要进行一个具体的实施。
 楼主| 610u 发表于 2023-1-28 16:20 | 显示全部楼层
第四个命令为 0XB0~B7 ,该命令用于设置页地址,其低三位的值对应着 GRAM 的页地址。
第五个指令为 0X00~0X0F ,该指令用于设置显示时的起始列地址低四位。
第六个指令为 0X10~0X1F ,该指令用于设置显示时的起始列地址高四位。

 楼主| 610u 发表于 2023-1-28 16:23 | 显示全部楼层
io口的配置(8080)
  1. #define OLED_CS PCout(9)          //片选信号
  2. #define OLED_RS PCout(8)      //硬复位
  3. #define OLED_WR PCout(7)          //写数据到OLED          
  4. #define OLED_RD PCout(6)      //读取OLED数据

  5. //PB0~7,作为数据线
  6. #define DATAOUT(x) GPIOB->ODR=(GPIOB->ODR&0xff00)|(x&0x00FF); //输出
 楼主| 610u 发表于 2023-1-28 16:23 | 显示全部楼层
这里的DATAOUT(X)就是将命令的八位字节看哪些位需要置高。

选择相应的通信模式

8080通信模式:

//向SSD1306写入一个字节。
//dat:要写入的数据/命令
//cmd:数据/命令标志 0,表示命令;1,表示数据;
void OLED_WR_Byte(u8 dat,u8 cmd)
{
    //将命令传入后将对应的位拉高(推挽输出模式)
        DATAOUT(dat);
    //选择是否是命令或者数据            
        OLED_RS=cmd;
    //拉低片选
        OLED_CS=0;          
    //WR上升沿的情况下,将数据写入到SSD1306
        OLED_WR=0;         
        OLED_WR=1;

        OLED_CS=1;
    //硬复位          
        OLED_RS=1;         
}
 楼主| 610u 发表于 2023-1-28 16:39 | 显示全部楼层
SPI通信模式:

  1. //向SSD1306写入一个字节。
  2. //dat:要写入的数据/命令
  3. //cmd:数据/命令标志 0,表示命令;1,表示数据;
  4. void OLED_WR_Byte(u8 dat,u8 cmd)
  5. {       
  6.         u8 i;                          
  7.         OLED_RS=cmd; //写命令
  8.     //拉低片选
  9.         OLED_CS=0;                  
  10.     //因为一个命令是低八位
  11.         for(i=0;i<8;i++)
  12.         {                          
  13.         //拉低时钟线(才可以对数据进行改变)
  14.                 OLED_SCLK=0;
  15.         //判断第八位是否为1,对数据线进行调整
  16.                 if(dat&0x80)OLED_SDIN=1;
  17.                 else OLED_SDIN=0;
  18.         //拉高时钟线
  19.                 OLED_SCLK=1;
  20.         //将刚才的低八位左移一位,后面补零
  21.                 dat<<=1;   
  22.         }                                 
  23.         OLED_CS=1;                  
  24.         OLED_RS=1;             
  25. }
 楼主| 610u 发表于 2023-1-28 16:40 | 显示全部楼层
初始化OLED(选择通信的条件是OLED_MODE == 1)
4522463d4df5481461.png
 楼主| 610u 发表于 2023-1-28 16:40 | 显示全部楼层
在这个代码中已经选择了8080。
  1. //初始化SSD1306                                            
  2. void OLED_Init(void)
  3. {                                                                                                
  4.         RCC->APB2ENR|=1<<3; //使能PORTB时钟
  5.         RCC->APB2ENR|=1<<4; //使能PORTC时钟           
  6. #if OLED_MODE==1                //使用8080并口模式                                 
  7.         JTAG_Set(SWD_ENABLE);
  8.         GPIOB->CRL=0X33333333;
  9.         GPIOB->ODR|=0XFFFF;                                                                             

  10.         GPIOC->CRH&=0XFFFFFF00;
  11.         GPIOC->CRL&=0X00FFFFFF;
  12.         GPIOC->CRH|=0X00000033;
  13.         GPIOC->CRL|=0X33000000;
  14.         GPIOC->ODR|=0X03C0;
  15. #else                                        //使用4线SPI 串口模式
  16.         GPIOB->CRL&=0XFFFFFF00;
  17.         GPIOB->CRL|=0XF0000033;
  18.         GPIOB->ODR|=0X03;

  19.         GPIOC->CRH&=0XFFFFFF00;          
  20.         GPIOC->CRH|=0X00000033;         
  21.         GPIOC->ODR|=3<<8;
  22. #endif
  23.                                                                   
  24.         //OLED_RST=0;
  25.         //delay_ms(100);
  26.         //OLED_RST=1;
  27.                                           
  28.         OLED_WR_Byte(0xAE,OLED_CMD); //关闭显示
  29.         OLED_WR_Byte(0xD5,OLED_CMD); //设置时钟分频因子,震荡频率
  30.         OLED_WR_Byte(80,OLED_CMD);   //[3:0],分频因子;[7:4],震荡频率
  31.         OLED_WR_Byte(0xA8,OLED_CMD); //设置驱动路数
  32.         OLED_WR_Byte(0X3F,OLED_CMD); //默认0X3F(1/64)
  33.         OLED_WR_Byte(0xD3,OLED_CMD); //设置显示偏移
  34.         OLED_WR_Byte(0X00,OLED_CMD); //默认为0

  35.         OLED_WR_Byte(0x40,OLED_CMD); //设置显示开始行 [5:0],行数.
  36.                                                                                                             
  37.         OLED_WR_Byte(0x8D,OLED_CMD); //电荷泵设置
  38.         OLED_WR_Byte(0x14,OLED_CMD); //bit2,开启/关闭
  39.         OLED_WR_Byte(0x20,OLED_CMD); //设置内存地址模式
  40.         OLED_WR_Byte(0x02,OLED_CMD); //[1:0],00,列地址模式;01,行地址模式;10,页地址模式;默认10;
  41.         OLED_WR_Byte(0xA1,OLED_CMD); //段重定义设置,bit0:0,0->0;1,0->127;
  42.         OLED_WR_Byte(0xC0,OLED_CMD); //设置COM扫描方向;bit3:0,普通模式;1,重定义模式 COM[N-1]->COM0;N:驱动路数
  43.         OLED_WR_Byte(0xDA,OLED_CMD); //设置COM硬件引脚配置
  44.         OLED_WR_Byte(0x12,OLED_CMD); //[5:4]配置
  45.                  
  46.         OLED_WR_Byte(0x81,OLED_CMD); //对比度设置
  47.         OLED_WR_Byte(0xEF,OLED_CMD); //1~255;默认0X7F (亮度设置,越大越亮)
  48.         OLED_WR_Byte(0xD9,OLED_CMD); //设置预充电周期
  49.         OLED_WR_Byte(0xf1,OLED_CMD); //[3:0],PHASE 1;[7:4],PHASE 2;
  50.         OLED_WR_Byte(0xDB,OLED_CMD); //设置VCOMH 电压倍率
  51.         OLED_WR_Byte(0x30,OLED_CMD); //[6:4] 000,0.65*vcc;001,0.77*vcc;011,0.83*vcc;

  52.         OLED_WR_Byte(0xA4,OLED_CMD); //全局显示开启;bit0:1,开启;0,关闭;(白屏/黑屏)
  53.         OLED_WR_Byte(0xA6,OLED_CMD); //设置显示方式;bit0:1,反相显示;0,正常显示                                                              
  54.         OLED_WR_Byte(0xAF,OLED_CMD); //开启显示         
  55.         OLED_Clear();
  56. }  
 楼主| 610u 发表于 2023-1-28 16:41 | 显示全部楼层
OLED_ShowString(x,y,"字符串",字节大小);

x,y指的是第一个bit的坐标,字节大小有12,16,24,字符大小为汉字的一半(高不变,宽变为1/2)
  1. //显示字符串
  2. //x,y:起点坐标  
  3. //size:字体大小
  4. //*p:字符串起始地址
  5. void OLED_ShowString(u8 x,u8 y,const u8 *p,u8 size)
  6. {       
  7.     while((*p<='~')&&(*p>=' '))                   //判断是不是非法字符!
  8.     {      
  9.         //当x超出范围,就进行换行(行高为字节大小)
  10.         if(x>(128-(size/2))){x=0;y+=size;}
  11.         //当y超出范围的时候,将会清屏
  12.         if(y>(64-size)){y=x=0;OLED_Clear();}
  13.         //将字符串转化为单个的字符        
  14.         OLED_ShowChar(x,y,*p,size,1);         
  15.         //展现一个字符就往后移动一个字节区域的宽度
  16.         x+=size/2;   
  17.         //指针后移一位,指向下一个字符
  18.         p++;
  19.     }  
  20.        
  21. }       
 楼主| 610u 发表于 2023-1-28 16:42 | 显示全部楼层
  1. void OLED_ShowChar(u8 x,u8 y,u8 chr,u8 size,u8 mode)
  2. {                                  
  3.         u8 temp,t,t1;
  4.         u8 y0=y;
  5.         u8 csize=(size/8+((size%8)?1:0))*(size/2);                //得到字体一个字符对应点阵集所占的字节数
  6.     //减去空格,是因为数值的第零位存放的都是空格
  7.         chr=chr-' ';//得到偏移后的值                 
  8.     for(t=0;t<csize;t++)
  9.     {   
  10.                 if(size==12)temp=asc2_1206[chr][t];                  //调用1206字体
  11.                 else if(size==16)temp=asc2_1608[chr][t];        //调用1608字体
  12.                 else if(size==24)temp=asc2_2412[chr][t];        //调用2412字体
  13.                 else return;                                                                //没有的字库
  14.         //因为一个字节都是低八位        
  15.         for(t1=0;t1<8;t1++)
  16.                 {
  17.             //判断每一位是否为1,若为1,则进行填充,反之,则不
  18.                         if(temp&0x80)OLED_DrawPoint(x,y,mode);
  19.                         else OLED_DrawPoint(x,y,!mode);
  20.             //左移一位,将低位往上移动
  21.                         temp<<=1;
  22.             //因为是列排列,所以是自上而下的连续八位,y++就是在起点的基础上进行下移,
  23.             //当超过你设定的字节高度时候(12,16,24),且退出循环,
  24.             //将会切换到下一列,继续从头开始
  25.                         y++;
  26.                         if((y-y0)==size)
  27.                         {
  28.                                 y=y0;
  29.                                 x++;
  30.                                 break;
  31.                         }
  32.                 }           
  33.     }         
  34. }
您需要登录后才可以回帖 登录 | 注册

本版积分规则

53

主题

568

帖子

0

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