资讯详情

【正点原子MP157连载】第十九章 OLED实验-摘自【正点原子】STM32MP1 M4裸机CubeIDE开发指南

1)实验平台:正点原子STM32MP157开发板 2)购买链接:https://item.taobao.com/item.htm?&id=629270721801 3)全套实验源码 手册 视频下载地址:http://www.openedv.com/thread-318813-1-1.html 4)正点原子官方B站:https://space.bilibili.com/394620890 5)正点原子STM32MP157技术交流群:691905614 在这里插入图片描述

第十九章 OLED实验

让我们学习使用本章OLED我们在开发板上预留了液晶显示屏OLED需要准备一个模块接口OLED显示模块。让我们一起点亮它。OLED,并显示字符和图片。 本章分为以下几个部分: 19.1、字符编码; 19.二、制作字模; 19.3、OLED简介; 19.4、OLED字符、数字显示实验; 19.5、OLED显示图片实验; 19.6、OLED显示动图实验; 19.7、OLED显示中文字体;

19.1 字符编码 计算机中存储的信息以二进制的0或1表示,我们在屏幕上看到的汉字、英语和数字是二进制转换的结果。将字符存储在计算机中,例如按照规则ASCII 字符集中,字符a用十进制97来表示字符。A用十进制65来表示,我们称之为编码,相反,解释计算机中的二进制数据,我们称之为解码。 字符集是各种文字和符号的集合,常见的字符集有ASCII字符集、GB2312字符集、BIG5字符集、Unicode字符集等。我们将在下面介绍ASCII如果您想深入了解其他字符集,可以参考百度百科的详细说明。为了准确处理各种字符集的文本,计算机需要编码字符。字符编码,又称字集码,是一套编码规则,是信息处理的基本技术它在符号集和数字系统之间建立了相应的关系,并将符号转换为计算机可以识别和存储的数字。 ASCII代码用7位2进制数表示一个字符,7位2进制数表示27个字符,共128个字符,其中包括 96 可打印字符,包括常用字母、数字、标点符号等。 32 个控制字符,控制字符中,如LF(换行)、CR(回车)、FF(换页)、DEL(删除)、BS(退格)、BEL(振铃)等,如下图中是ASCII字符代码表:

图19.1. 1 ASCII字符代码表ASCII字符代码表 本章将使用以下实验ASCII字符集显示英文字符和数字(第一个字符是空格): !"#$%&’() ,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[]^_`abcdefghijklmnopqrstuvwxyz{|}~ 19.2 制作字模 19.2.1 像素 图像显示是通过点阵显示的。屏幕上的小点是像素,像素点类似于灯(在 OLED 在显示器中,像素点是一个小灯通过控制这些小灯的亮度和灭度,可以显示不同的图像。图像是最基本的单元,屏幕上的像素点越小,像素密度越高,图像的像素越高,显示效果越清晰。高分辨率图像比低分辨率图像包含更多的像素,因此使用高分辨率屏幕比低分辨率屏幕显示更清晰。说到显示器,我们都会听到 720P、1080P、2K 或 4K 这样的字眼,1080P 意思是屏幕上的像素数是19201080 就是这个屏幕的列表 1080 像素点,一共 1920 列,如下图所示:

图19.2.1. 像素点和分辨率关系示意图 上图中,X 轴就是1080P横轴显示器,Y 轴就是1080P显示器的垂直轴。图中的小方块是像素点,共有 1920*1080=2073600 像素点。左上角 A 点是第一个像素点,右下角 C 点是最后一个像素点。 19.2.2 字模

  1. 字模简介 字体模型是指在点阵上显示字符或图像时对应的代码。字体模型是我们想要计算机识别的符号或图像的数据表示。因为计算机只能识别数字0和1,所以我们必须首先通过字模软件将图像或符号转换为字模。如下图所示,如果我们想显示一个英文字母B”,以816个二进制数据位表示,如果每个二进制数据位记录一个像素点的状态,则需要8个英文字母16/8=16个字节的二进制数据位来表示。字符的宽为1个字节,高为2个字节:

图19.2.2. 1 显示英文字符 如果是汉字,汉字是英文字符的两倍,一个汉字需要16个*16/8=根据32个字节的二进制数据位,字符宽度为2个字节,高度为2个字节,即汉字宽度是英文字符宽度的2倍:

图19.2.2. 2显示中文字体 2. 制作字模 制作字模有很多优秀的软件。本实验采用开发板光盘A-基础资料\3、软件\PCtoLCD2002年完美版字模软件制作字模,可提供包括汉字(字体和大小可自行设置)在内的各种字符点阵提取,可设置多种取模方式。软件支持常用的取模方式。除了支持字符模式外,软件还支持图形模式,即如果用户想显示图片,他们可以定义图片的大小,然后手动绘制图片,或导入现有图片,软件根据图片提取点阵数据。我们来介绍一下这个字模软件的使用方法。 (1)字符模式 ①双击打开字模软件PCtoLCD2002完美版:

图19.2.2. 3 PCtoLCD2002完美版 ②要显示字符,我们首先将要显示的字符输入字符输入框,然后点击设置选项设置取模方法:

图19.2.2. 4 输入字符 ③然后我们设置模式,如下图所示,红框可以配置,其他部分我们首先保持默认:

图19.2.2. 5设置取模方法 阴码或阳码可用于点阵格式,字模即像素点数据,每个点都需要一个bit位来存储,0代表像素点不显示,1代表显示。字模点阵中笔迹像素位的状态为1(亮),无笔记像素位的状态为0(灭),字模点阵中笔迹像素位的状态为0(灭),无笔记像素位的状态为1(亮)。在本节实验中选择阴码。 取模走向可以选择顺向或者逆向,也就是屏幕上同列中上行与下行哪个行对应生成字节的高位还是低位的问题,顺向就是屏幕下行属于高位,逆向就是屏幕上行属于低位。本节试验选择顺向。 每行显示数据可以自行配置。在点阵表示生成的字模中,每行最大可以显示多少数据,索引表示生成的索引中每行显示多少索引值。

图19.2.2. 6点阵数据 例如,配置为点阵:12 索引:12 {0x00,0x04,0x00,0x3C,0x03,0xC4,0x1C,0x40,0x07,0x40,0x00,0xE4}, {0x00,0x1C,0x00,0x04},/“A”,0/ 字宽和字高,字宽和字高可以自行配置,包括英文字宽和字宽,中文字宽和字宽。正如我们前面所说,汉字的字宽是英文字的两倍,如果配置汉字的字宽和字高都是16,那么英文的字宽和字高分别是8和16:

图19.2.2. 7设置字宽和字高 自定义格式中,A51按汇编生成,C51按c格式生成,显然我们是c编程,选择C51即可。自定义格式处如果勾选后,我们可以配置生成的字模数据的格式。例如,如果生成的数据中不想要大括号“{}”,可以自定义去掉大括号:

图19.2.2. 8自定义点阵数据的前缀和后缀 生成的点阵数据中就没有了大括号: 0x10,0x04,0x1F,0xFC,0x10,0x04,0x00,0x04,0x00,0x04,0x00,0x04, 0x00,0x0C,0x00,0x00,/“L”,1/ 取模方式有逐列式、逐行式、列行式和行列式。不同的取模方式需要结合不同的算法。在右上角的取模说明里面有,即:从第一列开始向下每取8个点作为一个字节,如果最后不足8个点就补满8位。取模顺序是从高到低,即第一个点作为最高位。如*-------取为10000000。其实就是按如下图所示路径的这种方式:

图19.2.2. 9取模方式图解 从上到下,从左到右,高位在前。我们按这样的取模方式,然后把ASCII字符集按126大小、168和2412大小取模出来(对应汉字大小为1212、1616和2424,字符的只有汉字的一半大!),每个126的字符占用12个字节,每个168的字符占用16个字节,每个24*12的字符占用36个字节。 ④点击生成字模,再保存字模,可以选择保存生成的字模:

图19.2.2. 10生成和保存字模 (2)图形模式 PCtoLCD2002完美版字模软件支持BMP格式的图片,如果要对某一张点阵图片取模,需要将此图片转化成.bmp格式的图片才可以。

图19.2.2. 11图形模式 如下图,选择图形模式后,点击文件打开,选择打开一张.bmp格式的文件即可:

图19.2.2. 12选择打开图形 或者可以选择文件新建,新建一张大小规定的图像(如果屏幕很小,建议设置的长和宽要比屏幕小,这样才可以显示完全),然后手动画图:

图19.2.2. 13手动画图 19.2.3 根据取模方式控制点阵显示字符 下面,我们先以显示简单的字符为例子,对字符显示做一个简单的讲解,为了方便讲解,此处讲解的代码先不在开发板上运行,我们先在以前学习C语言的工具(例如Visual C++ 6.0)上操作实现。此处的代码非常简单,如果没有安装此软件的也无关紧要,理解一遍代码即可。

  1. 显示英文字符 (1)字模提取 如果我们要显示一个英文字符“A”,如下图,设置字体为宋体,字宽和字高都为16,这个是汉字格式,那么对应的英文格式字长就是8,字宽就是16。如果A这个字符按阴码、顺向、逐列式、十六进制方式取模(即从下到上、从左到右、从低位到高位取模),如下图,因为点阵格式是阴码,为1的地方表示亮,为0的地方表示灭:

19.2.3. 1设置取模方式

19.2.3. 2英文字符A

19.2.3. 3字符A对应的点阵数据 那么,取模得到的十六进制数据为: 0x00,0x04,0x00,0x3C,0x03,0xC4,0x1C,0x40,0x07,0x40,0x00,0xE4,0x00,0x1C,0x00,0x04,/“A”,0/ 不同的取模方式得到的数据不一样,如果设置字体为宋体,字宽和字高都为16,我们的取模方式为阴码、顺向、行列式、十六进制方式提取(即从上到下、从右到左、从),则取模得到的十六进制的数据为:

19.2.3. 4设置取模方式

19.2.3. 5字符A对应的点阵数据 取模得到的点阵数据如下: 0x00,0x00,0x00,0x10,0x10,0x18,0x28,0x28,0x24,0x3C,0x44,0x42,0x42,0xE7,0x00,0x00,/“A”,0/ (2)程序实现 以上字模提取过程称为编码,不同的取模方式,算法会有些差别,我们使用程序将字符“A”打印出来,使用以上阴码、顺向、行列式、十六进制取模方式的编码数据,在Visual C++ 6.0下的程序如下:

1   #include <stdio.h>  
2   #include <stdlib.h>  
3     
4   unsigned char ch[] = { 
        0x00,0x00,0x00,0x10,0x10,0x18,0x28,0x28,0x24,0x3C,0x44,0x42,0x42,0xE7,0x00,0x00};  
5 
6   void showA(){ 
          
7       int i,j;  
8       unsigned char t;  
9       for (i = 0; i < 16; ++i)     	/* 总共16个十六进制数据 */  
10      { 
         
11          t = ch[i];               		/* 依次取出以上数组的数据 */
12          for (j = 0; j < 8; ++j)  	/* 对于某行中的每个点 */
13          { 
            
14              if (0x80 & t)
15              { 
                           
16                  printf("*");  /* 从左到右如果最左位为1,则显示*号 */
17              }
18              else
19              { 
          
20                  printf(" ");  /* 从左到右如果最左位为0,则显示空格 */
21              }  
22              t <<= 1;          /* 将右边的数据往左移动 */
23          }  
24          printf("\n");  
25      }  
26  }  
27    
28  int main(void) { 
          
29      showA();  
30      return EXIT_SUCCESS;  
31  }  
我们简单分析以上代码的实现逻辑。
第4行是讲取模得到的数据按照顺序排列到一个一维数组中;
第9行,一维数组共16个数据,每个数据代表一行(一行有8位),使用for循环依次取出这16个数据;
第11行,依次取出的数据存放到变量t中;
第12行,每个数据是8bit的, j表示这8个位的第几位,有0~7个位,0表示第0位,每个位表示一个像素点。
第14~17行,将这8位的每个位进行判断,如果某位为1,则打印*号;
第18~21行,如果某位为0,则打印空格;
第22行,t左移,从第0个数据开始,直到将这16个数据都判断完毕为止。
第28~30行是固定格式,使用控制台输出。
以上代码使用for循环嵌套,对数据逐位进行判断。
编译无报错,执行后控制台打印出A,效果如下:

19.2.3. 6运行结果 2. 显示中文字符 我们前面说过,一个中文字符的字宽是一个英文字符的两倍,每个汉字每行有16个像素点,即2个字节的像素点,所以每行需要对2个字节的二进制数据进行打印,参考前面英文字符的显示方法,程序中我们将汉字分为左半部分和右半部分来实现,所以要再增加一个for循环。 (1)汉字取模 前面我们说了,取模方式的不同,算法会有差别,前面的英文字符显示我们是采用阴码、顺向、行列式、十六进制取模方式。这里我们设置字体为宋体,字宽和字高都为16,使用阴码、顺向、逐行式、十六进制取模方式:

19.2.3. 7设置取模方式

19.2.3. 8字体效果

19.2.3. 9点阵数据排列 取模后点阵的数据为: 0x01,0x00,0x01,0x00,0x01,0x00,0x01,0x00,0x01,0x00,0xFF,0xFE,0x01,0x00,0x01,0x00, 0x02,0x80,0x02,0x80,0x04,0x40,0x04,0x40,0x08,0x20,0x10,0x10,0x20,0x08,0xC0,0x06,/“大”,0/ (2)程序实现 取模的数据中有两行十六进制数据,先以第一行的数据为例,第0个数据是左半边,第1个数据是右半边,第2个数据是左半边,第3个数据是右半边,也就是排序中,偶数对应左半边,奇数对应右半边,依此类推,数据交替存放。程序实现的代码如下:

1   #include <stdio.h>  
2   #include <stdlib.h>  
3     
4   unsigned char ch[] = { 
          
5   0x01,0x00,0x01,0x00,0x01,0x00,0x01,0x00,0x01,0x00,0xFF,0xFE,0x01,0x00,0x01,0x00,
6   0x02,0x80,0x02,0x80,0x04,0x40,0x04,0x40,0x08,0x20,0x10,0x10,0x20,0x08,0xC0,0x06
7   };  
8     
9   void show()  
10  { 
          
11      int i, j;  
12      unsigned char f, s;   /*表示一个汉字每行的左半边的8像素和右半边的8像素 */
13      for (i = 0; i < 16; ++i)  /* 16个组合 */
14      { 
           
15          f = ch[i * 2];        /* 取出左半边的数据 */
16          s = ch[i * 2 + 1];    /* 取出右半边的数据 */
17
18          /* 先判断中文字符的左半边的数据 */
19
20          for (j = 0; j < 8; ++j) /* 每个数据8位 */
21          { 
           
22             if (0x80 & f) 
23              { 
               
24                  printf("*");  /* 从左到右,如果某位为1,则显示*号 */
25              }  
26              else  
27              { 
          
28                  printf(" ");  /* 从左到右,如果某位为0,则显示空格 */
29              }  
30              f <<= 1;          /* 将右边的数据往左移动 */
31          }  
32
33          /* 再判断中文字符右半边的数据 */
34
35          for (j = 0; j < 8; ++j)/* 每个数据8位 */  
36          { 
           
37             if (0x80 & s) 
38              { 
               
39                  printf("*");/* 从左到右,如果某位为1,则显示*号 */  
40              }  
41              else  
42              { 
          
43                  printf(" ");  /* 从左到右,如果某位为0,则显示空格 */
44              }  
45              s <<= 1;         /* 将右边的数据往左移动 */
46          }  
47          printf("\n");  
48      }  
49  }  
50    
51  int main(void) { 
          
52      show();  
53      return EXIT_SUCCESS;  
54  }  
第12行,定义两个变量,f用于左半边的数据计算,f是偶数;s用于右半边的数据计算,s是奇数;
第13行,一维数组中有两行,每行16个十六进制的数据;
第15行,依次取出左半边的数据,
第16行,依次取出右半边的数据;
第20~31行,像前面显示英文字符那样,将汉字的左半边显示出来;
第35行~46行,像前面显示英文字符一样,将汉字的右半边显示出来;
编译不报错,运行程序,结果如下:

19.2.3. 10编译运行效果 3. 显示图片或者动图 关于显示图片和动图,这里就不列出代码了,我们后面会有专门的实验。 (1)显示一张图片 如果只是想显示一张图片,只需要将此图片转化成.bmp格式的图片,再取模即可。要注意的是显示屏幕的分辨率,如果屏幕的分辨率比图片的分辨率要小,则屏幕上无法显示完全图片,可以修改图片的分辨率以后再进行取模。可以使用windows自带的画图工具先打开要修改的.bmp格式的文件,打开以后再手动修改像素:

19.2.3. 11小改像素 (2)显示动图 如果要显示动图,如果只有一张.bmp格式的图片,可以通过程序将图片移动位置,如果是.gif格式的动图文件,可以使用gif分离器软件,例如开发板光盘A-基础资料\3、软件下的GIF2BMP软件,将动图拆分成一张张的.bmp格式的文件,然后再对每张图片取模。其实动图也就是由一帧帧的图片组合成的,拆分出的每一张.bmp格式的图片都是一帧图。 双击打开Gif分离器zhs9.exe,然后选择要分离的.gif格式文件以及分离后的文件保存路径,再点击开始分离,软件则进行分离。

19.2.3. 12分离图片 为了方便大家,在开发板光盘A-基础资料\3、软件\GIF2BMP下有放一张动图文件,文件名为2323.gif,像素为120*60帧。如下图,将2323.gif的动图分离后,最后得到58张.bmp格式的文件,即此动图有58帧:

19.2.3. 13分离后的图片 后面的实验中,我们会选出其中的10张图片在OLED上显示出一个动图。 19.3 OLED简介 19.3.1 OLED简介 OLED,即有机发光二极管(Organic Light-Emitting Diode),又称为有机电激光显示(Organic Electroluminesence Display, OELD)。OLED由于同时具备自发光,不需背光源、对比度高、厚度薄、视角广、反应速度快、可用于挠曲性面板、使用温度范围广、构造及制程较简单等优异之特性,被认为是下一代的平面显示器新兴应用技术。 LCD都需要背光,而OLED不需要,因为它是自发光的。这样同样的显示,OLED效果要来得好一些。以目前的技术,OLED的尺寸还难以大型化,但是分辨率确可以做到很高。在本章中,我们使用的是ALINETEK的OLED显示模块,该模块有以下特点: 1)模块有单色和双色两种可选,单色为纯蓝色,而双色则为黄蓝双色。 2)尺寸小,显示尺寸为0.96寸,而模块的尺寸仅为27mm26mm大小。 3)高分辨率,该模块的分辨率为12864。 4)多种接口方式,该模块提供了总共4种接口包括:6800、8080两种并行接口方式、4线SPI接口方式以及IIC接口方式(只需要2根线就可以控制OLED了!)。 5)不需要高压,直接接3.3V就可以工作了。 19.3.2 OLED的模式简介 这里要提醒大家的是,该模块不和5.0V接口兼容,所以请大家在使用的时候一定要小心,别直接接到5V的系统上去,否则可能烧坏模块。以下4种模式通过模块的BS1和BS2设置,BS1和BS2的设置与模块接口模式的关系如下表所示:

表19.3.2. 1 OLED模块接口方式设置表 表中:“1”代表接VCC,而“0”代表接GND。 该模块的外观图如下图所示:

图19.3.2. 1 ALIENTEK OLED模块外观图 ALIENTEK OLED模块默认设置是:BS1和BS2接VCC ,即使用8080并口方式,如果你想要设置为其他模式,则需要在OLED的背面,用烙铁修改BS1和BS2的设置。 模块的原理图如下图所示:

图19.3.2. 2 ALIENTEK OLED模块原理图 该模块采用8*2的2.54排针与外部连接,总共有16个管脚,在16条线中,我们只用了15条,有一个是悬空的。15条线中,电源和地线占了2条,还剩下13条信号线。 在不同模式下,我们需要的信号线数量是不同的,在8080模式下,需要全部13条,而在IIC模式下,仅需要2条线就够了!这其中有一条是共同的,那就是复位线RST(RES),RST上的低电平,将导致OLED复位,在每次初始化之前,都应该复位一下OLED模块。 ALIENTEK OLED模块的控制器是SSD1306,本章,我们将学习如何通过STM32H750来控制该模块显示字符和数字,本章的实例代码将可以支持两种方式与OLED模块连接,一种是8080的并口方式,另外一种是4线SPI方式。

  1. 8080并行接口方式 首先我们介绍一下模块的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,读写数据)。 模块的8080并口读/写的过程为:先根据要写入/读取的数据的类型,设置DC为高(数据)/低(命令),然后拉低片选,选中SSD1306,接着我们根据是读数据,还是要写数据置RD/WR为低,然后: 在RD的上升沿, 使数据存到数据线(D[7:0])上; 在WR的上升沿,使数据写入到SSD1306里面; SSD1306的8080并口写时序图如下图所示:

图19.3.2. 3 8080并口写时序图 SSD1306的8080并口读时序图如下图所示:

图19.3.2. 48080并口读时序图 SSD1306的8080接口方式下,控制脚的信号状态所对应的功能如下表所示:

表19.3.2. 2控制脚信号状态功能表 在8080方式下读数据操作的时候,我们有时候(例如读显存的时候)需要一个假读命(Dummy Read),以使得微控制器的操作频率和显存的操作频率相匹配。在读取真正的数据之前,由一个的假读的过程。这里的假读,其实就是第一个读到的字节丢弃不要,从第二个开始,才是我们真正要读的数据。 一个典型的读显存的时序图,如下图所示:

图19.3.2. 5读显存时序图 可以看到,在发送了列地址之后,开始读数据,第一个是Dummy Read,也就是假读,我们从第二个开始,才算是真正有效的数据。 2. 4 线串行(SPI)方式 我们的代码同时兼容SPI方式的驱动,如果你使用的是这种驱动方式,则应该把代码中的宏OLED_MODE设置为: #define OLED_MODE 0 /* 0: 4线串行模式 */ 接下来介绍一下4线串行(SPI)方式,4线串口模式使用的信号线有如下几条: CS:OLED片选信号。 RST(RES):硬复位OLED。 DC:命令/数据标志(0,读写命令;1,读写数据)。 SCLK:串行时钟线。在4线串行模式下,D0信号线作为串行时钟线SCLK。 SDIN:串行数据线。在4线串行模式下,D1信号线作为串行数据线SDIN。 模块的D2需要悬空,其他引脚可以接到GND。在4线串行模式下,只能往模块写数据而不能读数据。 在4线SPI模式下,每个数据长度均为8位,在SCLK的上升沿,数据从SDIN移入到SSD1306,并且是高位在前的。DC线还是用作命令/数据的标志线。在4线SPI模式下,写操作的时序如下图所示:

图19.3.2. 6 4线SPI写操作时序图 4线串行模式就为大家介绍到这里。其他还有几种模式,在SSD1306的数据手册《SSD1306-Revision 1.1 (Charge Pump)》上都有详细的介绍,如果要使用这些方式,请大家参考该手册,手册位于“开发板光盘A-基础资料\6、硬件资料\1、芯片资料\【正点原子】0.96寸OLED模块12864资料”路径下。 3. SSD1306的显存 接下来,我们介绍一下模块的显存,SSD1306的显存总共为128*64bit大小,SSD1306将这些显存分为了8页,其对应关系如下表所示:

图19.3.2. 7 SSD1306显存与屏幕对应关系表 可以看出,SSD1306的每页包含了128个字节,总共8页,这样刚好是128*64的点阵大小。当GRAM的写入模式为页模式时,需要设置低字节起始的列地址(0x000x0F)和高字节的起始列地址(0x100x1F),芯片手册中给出了写入GRAM与显示的对应关系,写入列地址在写完一字节后自动按列增长,如下图所示:

图19.3.2. 8 GRAM与显示的对应关系 因为每次写入都是按字节写入的,这就存在一个问题,如果我们使用只写方式操作模块,那么,每次要写8个点,这样,我们在画点的时候,就必须把要设置的点所在的字节的每个位都搞清楚当前的状态(0/1?),否则写入的数据就会覆盖掉之前的状态,结果就是有些不需要显示的点,显示出来了,或者该显示的没有显示了。这个问题在能读的模式下,我们可以先读出来要写入的那个字节,得到当前状况,在修改了要改写的位之后再写进GRAM,这样就不会影响到之前的状况了。但是这样需要能读GRAM,对于4线SPI模式/IIC模式,模块是不支持读的,而且读改写的方式速度也比较慢。 所以我们采用的办法是在STM32MP157的内部建立一个虚拟的OLED的GRAM(共128*8=1024个字节),在每次修改的时候,只是修改STM32MP157上的GRAM(实际上就是SRAM),在修改完了之后,一次性把STM3MP157上的GRAM写入到OLED的GRAM。当然这个方法也有坏处,一个是对于那些SRAM很小的单片机(比如51系列)不太友好,另一个是每次都写入全屏,屏幕刷新率会变低。 4. SSD1306的命令 SSD1306的命令比较多,这里我们仅介绍几个比较常用的命令,这些命令如下表所示:

图19.3.2. 9 SSD1306常用命令表 第一个命令为0X81,用于设置对比度的,这个命令包含了两个字节,第一个0X81为命令,随后发送的一个字节为要设置的对比度的值。这个值设置得越大屏幕就越亮。 第二个命令为0XAE/0XAF。0XAE为关闭显示命令;0XAF为开启显示命令。 第三个命令为0X8D,该指令也包含2个字节,第一个为命令字,第二个为设置值,第二个字节的BIT2表示电荷泵的开关状态,该位为1,则开启电荷泵,为0则关闭。在模块初始化的时候,这个必须要开启,否则是看不到屏幕显示的。 第四个命令为0XB0~B7,该命令用于设置页地址,其低三位的值对应着GRAM的页地址。 第五个指令为0X00~0X0F,该指令用于设置显示时的起始列地址低四位。 第六个指令为0X10~0X1F,该指令用于设置显示时的起始列地址高四位。 其他命令,我们就不在这里一一介绍了,大家可以参考SSD1306 datasheet的第28页。从这页开始,对SSD1306的指令有详细的介绍。 5. OLED初始化过程 最后,我们再来介绍一下OLED模块的初始化过程,SSD1306的典型初始化框图如下图所示:

图19.3.2. 10 SSD1306初始化框图 驱动IC的初始化代码,我们直接使用厂家推荐的设置就可以了,只要对细节部分进行一些修改,使其满足我们自己的要求即可,其他不需要变动。 19.4 OLED字符、数字显示实验 本实验配置好的实验工程已经放到了开发板光盘中,路径为:开发板光盘A-基础资料\1、程序源码\11、M4 CubeIDE裸机驱动例程\CubeIDE_project\ 12 OLED_DISPLAY。 前面我们重点向大家介绍了ALIENTEK OLED模块的相关知识,接下来我们将使用这个模块来显示字符和数字。通过以上介绍,我们可以得出OLED显示需要的相关设置步骤如下: 1)设置STM32MP157与OLED模块相连接的IO 这一步,先将我们与OLED模块相连的IO口设置为输出,具体使用哪些IO口,这里需要根据连接电路以及OLED模块所设置的通讯模式来确定。这些将在硬件设计部分向大家介绍。 2)初始化OLED模块 其实这里就是上面的初始化框图的内容,通过对OLED相关寄存器的初始化,来启动OLED的显示。为后续显示字符和数字做准备。 3)通过函数将字符和数字显示到OLED模块上 这里就是通过我们设计的程序,将要显示的字符送到OLED模块就可以了,这些函数将在软件设计部分向大家介绍。 通过以上三步,我们就可以使用ALIENTEK OLED模块来显示字符和数字了。 19.4.1 硬件设计

  1. 例程功能 使用8080并口模式驱动或者使用4线SPI串口模式,驱动OLED模块,不停的显示ASCII码和码值。LED0闪烁,提示程序运行。
  2. 硬件资源 1)LED灯 LED0 - PI0 2)ALIENTEK 0.96寸OLED模块,在硬件上,OLED与开发板的IO口对应关系如下: OLED_RS(OLED 模块上的丝印是DC)对应12C5_SCL,即:PA11; OLED模块上悬空/不接的引脚是DCMI_PIXCLK,即PA6;

表19.4.1. 1 IO口对应表 注意,这里的OLED_D[7:0]因为不是接的连续的IO,所以后面的程序中得用拼凑的方式去组合一下,后续的程序部分会介绍到。 3. 原理图 OLED模块插在开发板底板的CAMERA接口上,接口的原理图如下,对应的IO口在前面已有详细说明了:

表19.4.1. 2 CAMERA接口部分原理图 下面我们介绍OLED模块与我们开发板的连接,开发板上有一个OLED/CAMERA的接口(P2接口)可以和ALIENTEK OLED模块直接对插(靠左插!),这些线的连接,开发板的内部已经连接好了,我们只需要将OLED模块插上去就好了,连接如下图所示:

表19.4.1. 3 OLED模块连接示意图 19.4.2 软件设计 OLED只是用到HAL库中GPIO外设的驱动代码,配置步骤在前面跑马灯实验已经介绍了。

  1. 程序流程图 下面看看本实验的程序流程图:

图19.4.2. 1程序流程图 2. 新建工程和配置工程 由于配置IO口和时钟的配置步骤在前面我们都已经详细讲解,这里就不再重复讲解之前的步骤了。 新建一个工程OLED_DISPLAY,然后配置的IO口如下:

图19.4.2. 2 IO口配置 时钟的话,可以采用内部时钟或者外部时钟,我们就使用外部时钟HSE,配置AHB总线的时钟最大为104.5MHz。其它配置注意事项和前面跑马灯实验的一致,保存配置,然后生成工程,将上一章节实验的BSP文件夹拷贝到Src目录下,如下图所示:

图19.4.2. 3生成的工程 然后,在BSP文件夹下新建oled.c文件,在BSP/Include文件夹下新建oled.h、oledfont.h文件 3. 添加oledfont.h文件代码 oledfont.h文件用于存放ASICII字符集点阵数据,也就是将ASICII字符集取模后得到的数据,这里我们把 ASCII 字符集按字宽和字高为1212、1616和2424的大小取模出来。这里我们以按字宽和字高为1212为例子做讲解。设置字体为隶书,字宽和字高都为12,取模方式设置:阴码+逐列式+顺向+C51格式,最后将以下ASICII码字符拷贝到输入框,注意,第一个字符是空格,ASCII字符集: !"#$%&’()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[]^_`abcdefghijklmnopqrstuvwxyz{|}~

图19.4.2. 4设置取模方式 最后将数据保存在一个.tex文件中,得到的数据如下:

图19.4.2. 5取模得到的点阵数据 其它字宽和字高为1616和2424的数据生成也是同样的方法。oledfont.h文件的代码我们已经在本实验的工程中给出,大家可以直接使用。 4. 添加oled.h文件代码 oled.h文件代码如下:

1   #ifndef __OLED_H
2   #define __OLED_H
3  
4   #include "stdlib.h"
5   #include <stdint.h>
6   /* OLED模式设置 7 * 0: 4线串行模式 (模块的BS1,BS2均接GND) 8 * 1: 并行8080模式 (模块的BS1,BS2均接VCC) 9 */
10  #define OLED_MODE       1   /* 默认使用8080并口模式 */
11 
12  /*****************************************************************/
13  /* OLED SPI模式引脚 定义 */
14  /* 注意:这里仅定义了 OLED 4线SPI模式驱动时的引脚定义 15 8080并口模式引脚定义在gpio.c文件中已经包含了 */
16  #define OLED_SPI_RST_PORT               GPIOE
17  #define OLED_SPI_RST_PIN                GPIO_PIN_1
18  /* PE口时钟使能 */
19  #define OLED_SPI_RST_CLK_ENABLE() do{ 
         __HAL_RCC_GPIOE_CLK_ENABLE(); }while(0)   
20 
21  #define OLED_SPI_CS_PORT                GPIOB
22  #define OLED_SPI_CS_PIN                 GPIO_PIN_7
23  /* PB口时钟使能 */
24  #define OLED_SPI_CS_CLK_ENABLE()  do{ 
         __HAL_RCC_GPIOB_CLK_ENABLE(); }while(0)   
25 
26  #define OLED_SPI_RS_PORT                GPIOA
27  #define OLED_SPI_RS_PIN                 GPIO_PIN_11
28  /* PA口时钟使能 */
29  #define OLED_SPI_RS_CLK_ENABLE()  do{ 
         __HAL_RCC_GPIOA_CLK_ENABLE(); }while(0)   
30 
31  #define OLED_SPI_SCLK_PORT              GPIOH
32  #define OLED_SPI_SCLK_PIN               GPIO_PIN_9
33  /* PH口时钟使能 */
34  #define OLED_SPI_SCLK_CLK_ENABLE() do{ 
         __HAL_RCC_GPIOH_CLK_ENABLE(); }while(0)   
35 
36  #define OLED_SPI_SDIN_PORT              GPIOH
37  #define OLED_SPI_SDIN_PIN               GPIO_PIN_10
38  /* PH口时钟使能 */
39  #define OLED_SPI_SDIN_CLK_ENABLE() do{ 
         __HAL_RCC_GPIOH_CLK_ENABLE(); }while(0)   
40 
41  /***************************************************************/
42 
43  /* OLED SPI模式相关端口控制函数 定义 44 * 注意:OLED_RST/OLED_CS/OLED_RS,这三个是和80并口模式共用的,即80模式也必须 45 实现这3个函数!*/
46  #define OLED_RST(x)   do{ 
         x ? \
47               HAL_GPIO_WritePin(GPIOE, GPIO_PIN_1, GPIO_PIN_SET) : \
48               HAL_GPIO_WritePin(GPIOE, GPIO_PIN_1, GPIO_PIN_RESET); \
49                        }while(0)       /* 设置RST引脚 */
50 
51  #define OLED_CS(x)   do{ 
         x ? \
52                HAL_GPIO_WritePin(GPIOB, GPIO_PIN_7, GPIO_PIN_SET) : \
53                HAL_GPIO_WritePin(GPIOB, GPIO_PIN_7, GPIO_PIN_RESET); \
54                        }while(0)       /* 设置CS引脚 */
55 
56  #define OLED_RS(x)   do{ 
         x ? \
57                HAL_GPIO_WritePin(GPIOA, GPIO_PIN_11, GPIO_PIN_SET) : \
58                HAL_GPIO_WritePin(GPIOA, GPIO_PIN_11, GPIO_PIN_RESET); \
59                        }while(0)       /* 设置RS引脚 */
60  #define OLED_SCLK(x)   do{ 
         x ? \
61 HAL_GPIO_WritePin(OLED_SPI_SCLK_PORT, OLED_SPI_SCLK_PIN, GPIO_PIN_SET) : \
62 HAL_GPIO_WritePin(OLED_SPI_SCLK_PORT, OLED_SPI_SCLK_PIN, GPIO_PIN_RESET); \
63                        }while(0)       /* 设置SCLK引脚 */
64 
65  #define OLED_SDIN(x)   do{ 
         x ? \
66  HAL_GPIO_WritePin(OLED_SPI_SDIN_PORT, OLED_SPI_SDIN_PIN, GPIO_PIN_SET) : \
67  HAL_GPIO_WritePin(OLED_SPI_SDIN_PORT, OLED_SPI_SDIN_PIN, GPIO_PIN_RESET); \
68                        }while(0)       /* 设置SDIN引脚 */
69 
70  /* OLED 80并口模式WR,RD端口控制函数 定义 */
71  #define OLED_WR(x)   do{ 
         x ? \
72      HAL_GPIO_WritePin(GPIOH, GPIO_PIN_8, GPIO_PIN_SET) : \
73      HAL_GPIO_WritePin(GPIOH, GPIO_PIN_8, GPIO_PIN_RESET); \
74                        }while(0)       /* 设置WR引脚 */
75 
76  #define OLED_RD(x)   do{ 
         x ? \
77      HAL_GPIO_

标签: 二极管丝印12cx2丝印双二极管丝印快恢复二极管丝印6y1二极管mp1201二极管

锐单商城拥有海量元器件数据手册IC替代型号,打造 电子元器件IC百科大全!

锐单商城 - 一站式电子元器件采购平台