4线SPI实现OLED显示(基于STM32F103ZET6)

2023-10-25 01:40

本文主要是介绍4线SPI实现OLED显示(基于STM32F103ZET6),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

硬件设备:
(1):0.96寸的oled显示屏
(2):stm32开发板,不带接口也可以可,可以用杜邦线引出来插上即可

目的:

我们将利用精英 STM32 开发板上的 OLED 模块接口来点亮OLED,并实现 ASCII 字符的显示。

原理

LCD 都需要背光,而 OLED 不需要,因为它是自发光的。
OLED有多种点亮方式,包括:
(1):6800并行接口方式
(2):8080并行接口方式
(3):三线spi接口方式
(4):四线spi接口方式
(5):IIC 接口方式(只需要 2 根线就可以控制 OLED 了!)
这五种模式怎们设置呢?就是在模块的背面有一些电阻,分别BS0与BS1在控制模式,想要改变模式,就在相应位置焊上电阻
在这里插入图片描述
当然了,有的小伙伴并不是这样的oled,我的模块就不是正点原子的0.96oled,我是自己定义的引脚来控制的!可以根据厂家提供的数据手册资料来修改模式,原理差不多!
在这里插入图片描述

重点来了(四线spi)

想要写好OLED程序,了解模块的原理,流程,数据写入的方向与方式等等都是很重要的,而不是拿着标准的程序死记硬背!下面就对我使用的OLED进行我的理解说明:
引脚说明
CS:OLED 片选信号。
RST(RES):硬复位 OLED。
DC:命令/数据标志(0,读写命令;1,读写数据)。
SCLK:串行时钟线,D0 信号线作为串行时钟线
SDIN:串行数据线,D1 信号线作为串行数据线
VCC与GND也是必须要有的,这么算下来就是7针的OLED模块
IO口的配置比较简单:配置的引脚也能看到

void oled_init(void)
{GPIO_InitTypeDef GPIO_InitStructure;RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC|RCC_APB2Periph_GPIOD|RCC_APB2Periph_GPIOD|RCC_APB2Periph_GPIOD|RCC_APB2Periph_GPIOG, ENABLE);GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP;GPIO_InitStructure.GPIO_Pin=GPIO_Pin_3|GPIO_Pin_6;GPIO_Init(GPIOD,&GPIO_InitStructure);GPIO_SetBits(GPIOD,GPIO_Pin_3|GPIO_Pin_6);GPIO_InitStructure.GPIO_Pin=GPIO_Pin_1|GPIO_Pin_0;GPIO_Init(GPIOC,&GPIO_InitStructure);GPIO_SetBits(GPIOC,GPIO_Pin_1|GPIO_Pin_0);GPIO_InitStructure.GPIO_Pin=GPIO_Pin_15;GPIO_Init(GPIOG,&GPIO_InitStructure);GPIO_SetBits(GPIOG,GPIO_Pin_15);//oled¸´Î»OLED_RST=0;delay_ms(100);OLED_RST=1; //oled³õʼ»¯oled_writebyte(0xAE,OLED_CMD); oled_writebyte(0xD5,OLED_CMD); oled_writebyte(0x80,OLED_CMD);  oled_writebyte(0xA8,OLED_CMD); oled_writebyte(0X3F,OLED_CMD); oled_writebyte(0xD3,OLED_CMD);oled_writebyte(0X00,OLED_CMD); oled_writebyte(0x40,OLED_CMD); 										    oled_writebyte(0x8D,OLED_CMD); oled_writebyte(0x14,OLED_CMD); oled_writebyte(0x20,OLED_CMD); oled_writebyte(0x02,OLED_CMD); oled_writebyte(0xC8,OLED_CMD);oled_writebyte(0xA1,OLED_CMD); oled_writebyte(0xDA,OLED_CMD); oled_writebyte(0x12,OLED_CMD);oled_writebyte(0x81,OLED_CMD);oled_writebyte(0xEF,OLED_CMD); oled_writebyte(0xD9,OLED_CMD);oled_writebyte(0xf1,OLED_CMD); oled_writebyte(0xDB,OLED_CMD);oled_writebyte(0x30,OLED_CMD); oled_writebyte(0xA4,OLED_CMD); oled_writebyte(0xA6,OLED_CMD); oled_writebyte(0xAF,OLED_CMD); 	OLED_Clear();
}

如图:
在这里插入图片描述
在 4 线 SPI 模式下,每个数据长度均为 8 位,在 SCLK 的上升沿,数据从 SDIN 移入到oled模块的SSD1306,并且是高位在前的。DC 线还是用作命令/数据的标志线。
在这里插入图片描述
口述一下从32传输一个字节到模块SSD1306的时序:
写入的数据我们分为了命令与数据,将片选位拉低,在传输数据的的开始,将时钟拉低,取出传输字节的最高位,拉高时钟,这时,1bit数据成功写入SSD1306,循环八次,方可完成一个字节的写入,此时拉高片选。

void oled_writebyte(unsigned char data,unsigned char oled_RS)
{int i;OLED_CS=0;//片选拉低OLED_RS=oled_RS;//发送的数据还是命令for(i=0;i<8;i++){OLED_SCLK=0;if(data&0x80)OLED_SDIN=1;//数据位else OLED_SDIN=0;//数据位data<<=1;//将次高位移动到最高位OLED_SCLK=1;	//时钟拉高}OLED_CS=1;//片选拉高OLED_RS=1;
}

写入SSD1306的data分为数据和命令,写入的命令是来设置OLED的显示参数,写入的数据用来显示,所以:
(1):了解要写入的命令,初始化oled模块
在这里插入图片描述
可根据此流程校验初始化流程!
(2):清除显示的数据是如何写入的,在显示屏中如何显示,现实的方向,大小等等,都很关键
第二点开始:
一个字节的数据写入了,
如果是命令:我们可以不用管如何让排列在模块里,因为这不会影响我们的显示,
如果是数据:
在这里插入图片描述
这是我们写入的数据对应屏幕的关系表,PAGE0包含128个字节,也就是128x8个像素点,从PAGE0~PAGE7共有八个,也就是128x8x8个像素点,我们取出PAGE0,
在这里插入图片描述
上面的数据只是代表个数,不代表真实数据,数字相同的八位构成一个字节,这个字节,就是我们写入的一个字节的显示数据,那这个遵守什么规律呢?
在这里插入图片描述
由写时序可知,先写高字节,再写低字节,所以数据写入的方向如图,总的方向为:从下到上,从左至右
在这里插入图片描述
写入字节的内部结构清楚了,在此之前我们设置显示这个字节的位置(一个字节控制从上到下的八个像素点我们应该是知道的就不详细说明了),上图只是我们的PAGE0,所以我们要确定在哪个PAGE上写数据,其次每页从左到右也有一个位置,所以写入数据,这是两个必须写入的参数,每一页,只需要配置一次,如何更好的控制写入的数据点呢?
可以本地(自己)定义一个屏幕像素点大小的二维数组,eg:char a[128][8]

unsigned char GRAM[128][8];//128*8个字节=128*8*8个像素点

当我们数据点配置(本地的二维数组)好后,一起发送(refresh)到SSD1306里,想要什么图形自己都可以用一个一个的像素点拼凑出来!

void GRAM_REFRESH(void)
{int i;int j;for(i=0;i<8;i++)//8页循环8次{oled_writebyte(0xb0+i,OLED_CMD);//页地址,每次增1oled_writebyte(0x00,OLED_CMD);//显示时的起始列地址低四位oled_writebyte(0x10,OLED_CMD);//显示时的起始列地址高四位for(j=0;j<128;j++)//循环128次,每次从下到上写一个字节{oled_writebyte(GRAM[j][i],OLED_DATA);	}}
}

基本达成oled点亮

小进阶(显示单个字符)

还是一个原理:任何显示的信息,都可以先修改本地(自己定义的二维数组),修改结束后一起(refresh)写入SSD1306,那么怎们来显示字符呢?
我们现在要用到字符集点阵,这个字符集点阵有大有小也就是控制字体的大小,在这个固定大小的区域内显示某个字符,这个字符点阵集可以是软件合成的,也可以在您周围的小伙伴哪里copy一下都可以,并且是const类型的常量
在这里插入图片描述
上面就是1206大小的点阵字符集,代码太多,不便上码。
举例
如果我们要用这个大小的字体显示’!'号,也就是上图的第二行,1206的意思是指高12,宽为6的像素点组成的大小的显示平面,一个字节一个字节的写入。
现在将一个字节看为整体,方向是从上到下,从左到右,如果把某个字节的1bit看为整体,就是我们前面描述到的。
在这里插入图片描述
虚线部分4bit为没写入的位,丢弃掉,这个大小总共占用12个字节,正好对应点阵字符集上的12个数据,

非常重要的函数

void draw_char(unsigned char x,unsigned char y,unsigned char chr,unsigned char size)
{unsigned char csize;unsigned char num1;unsigned char num2;unsigned char data;unsigned char num3=y;unsigned char chr1;csize= (size/8+((size%8)?1:0))*(size/2);//确定字体所占字节数chr1=chr-' ';	//得到偏移后的值,因为点阵字符集的第一个字符为空''for(num1=0;num1<csize;num1++){if(size==12){data=asc2_1206[chr1][num1];}//1206字体else if(size==16){data=asc2_1608[chr1][num1];}//1608的字体else if(size==24){data=asc2_2412[chr1][num1];}//2412的字体else return ;for(num2=0;num2<8;num2++){if(data&0x80){draw_point(x,y);}else {clean_point(x,y);}data<<=1;y++;if((y-num3)==size){y=num3;x++;break;}}			}
}

这个函数将本地的二维数组已经根据字符布置好了,如果想在(20,20)的位置上用12字体显示字符0,可以这样用函数

draw_char(20,20,'0',12)

当然,想要用此函数显示字符串,除了人为的大间隔法(离旁边的单个字符很远处再写个字符),拼凑出来在显示屏上显示的字符串,当然还有显示字符串的方法:

进阶1(显示字符串)

此时我们用到了大家喜欢的指针,也会用到上面的draw_char();函数,在这里我提供给大家两种算法:
(1):

void draw_string(unsigned char x,unsigned char y, char* a,unsigned char size)
{unsigned char X,i;X=x;for(;*a!='\0';a++){draw_char(x,y,*a,size);x+=size/2;if(x+size>128) //此行已经不能容纳更多字符,换行{x=X;//与上一排字符串同x起始位y+=size;//y显示上增加一个字体高度}}
}

draw_char参数里面传入的是字符,在这里我们应该注意,我们可以循环判断传入的字符串是否一字符串结束符’\0’结束作为标志位
(2):利用95个字符判断,处于/" ",0/<(a+i)</"~",94*/之间就为存在字符

void draw_string(unsigned char x,unsigned char y, char* a,unsigned char size)
{while((*a>=' ')&&(*a<='~'))//判断字符是否为这95个字符中的{draw_char(x,y,*a,size);x+=size/2if(x+size>127)  {x=X;y+=size;}a++;}
}

如果说想要显示文字,可以通过点阵字符制作软件生成,自行探索!
效果如图
在这里插入图片描述

进阶2(显示直线)

相信能做完前面的流程,显示直线也应该能够完成,直接上码,大家应该能看得懂:

void draw_line(unsigned char x1,unsigned char y1,unsigned char x2,unsigned char y2 )
{unsigned  char k1,k2,i,k;if(x1==x2)//画竖线{if(y1>y2){for(y2;y2<=y1;y2++){draw_point(x2,y2);}}else {for(y1;y1<=y2;y1++){draw_point(x1,y1);}}}else if(y1==y2)   //画横线{for(i=0;i<=(x2-x1);i++){draw_point(x1+i,y1);}}else if(x1!=x2&&y1!=y2)//画斜线{k1=y2-y1;k2=x2-x1;k=k1*10/k2;for(i=0;i<(x2-x1);i++){draw_point(x1+i,y1+i*k/10);}}
}

参数x1,y1,x2,y2是直线两点的坐标;

一定在最后不要忘记refresh函数,否则无显示

在此,我只是提供了算法与思路,代码太多,希望对读者有用,懂原理,参考数据手册,其他的oled都不是问题,务必清楚原理后再看代码学习,否则事倍功半!
还有显示圈,图片等等,原理都是一样,希望一定要动手实践操作!
此博客只写了作为写入,没涉及到读出,还有很大的空间可以拓展,谢谢大家,也希望读者有更好的建议给我,互相学习

这篇关于4线SPI实现OLED显示(基于STM32F103ZET6)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/279181

相关文章

python生成随机唯一id的几种实现方法

《python生成随机唯一id的几种实现方法》在Python中生成随机唯一ID有多种方法,根据不同的需求场景可以选择最适合的方案,文中通过示例代码介绍的非常详细,需要的朋友们下面随着小编来一起学习学习... 目录方法 1:使用 UUID 模块(推荐)方法 2:使用 Secrets 模块(安全敏感场景)方法

Windows环境下解决Matplotlib中文字体显示问题的详细教程

《Windows环境下解决Matplotlib中文字体显示问题的详细教程》本文详细介绍了在Windows下解决Matplotlib中文显示问题的方法,包括安装字体、更新缓存、配置文件设置及编码調整,并... 目录引言问题分析解决方案详解1. 检查系统已安装字体2. 手动添加中文字体(以SimHei为例)步骤

Spring StateMachine实现状态机使用示例详解

《SpringStateMachine实现状态机使用示例详解》本文介绍SpringStateMachine实现状态机的步骤,包括依赖导入、枚举定义、状态转移规则配置、上下文管理及服务调用示例,重点解... 目录什么是状态机使用示例什么是状态机状态机是计算机科学中的​​核心建模工具​​,用于描述对象在其生命

Spring Boot 结合 WxJava 实现文章上传微信公众号草稿箱与群发

《SpringBoot结合WxJava实现文章上传微信公众号草稿箱与群发》本文将详细介绍如何使用SpringBoot框架结合WxJava开发工具包,实现文章上传到微信公众号草稿箱以及群发功能,... 目录一、项目环境准备1.1 开发环境1.2 微信公众号准备二、Spring Boot 项目搭建2.1 创建

IntelliJ IDEA2025创建SpringBoot项目的实现步骤

《IntelliJIDEA2025创建SpringBoot项目的实现步骤》本文主要介绍了IntelliJIDEA2025创建SpringBoot项目的实现步骤,文中通过示例代码介绍的非常详细,对大家... 目录一、创建 Spring Boot 项目1. 新建项目2. 基础配置3. 选择依赖4. 生成项目5.

Linux下删除乱码文件和目录的实现方式

《Linux下删除乱码文件和目录的实现方式》:本文主要介绍Linux下删除乱码文件和目录的实现方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录linux下删除乱码文件和目录方法1方法2总结Linux下删除乱码文件和目录方法1使用ls -i命令找到文件或目录

SpringBoot+EasyExcel实现自定义复杂样式导入导出

《SpringBoot+EasyExcel实现自定义复杂样式导入导出》这篇文章主要为大家详细介绍了SpringBoot如何结果EasyExcel实现自定义复杂样式导入导出功能,文中的示例代码讲解详细,... 目录安装处理自定义导出复杂场景1、列不固定,动态列2、动态下拉3、自定义锁定行/列,添加密码4、合并

mybatis执行insert返回id实现详解

《mybatis执行insert返回id实现详解》MyBatis插入操作默认返回受影响行数,需通过useGeneratedKeys+keyProperty或selectKey获取主键ID,确保主键为自... 目录 两种方式获取自增 ID:1. ​​useGeneratedKeys+keyProperty(推

Spring Boot集成Druid实现数据源管理与监控的详细步骤

《SpringBoot集成Druid实现数据源管理与监控的详细步骤》本文介绍如何在SpringBoot项目中集成Druid数据库连接池,包括环境搭建、Maven依赖配置、SpringBoot配置文件... 目录1. 引言1.1 环境准备1.2 Druid介绍2. 配置Druid连接池3. 查看Druid监控

Linux在线解压jar包的实现方式

《Linux在线解压jar包的实现方式》:本文主要介绍Linux在线解压jar包的实现方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录linux在线解压jar包解压 jar包的步骤总结Linux在线解压jar包在 Centos 中解压 jar 包可以使用 u