灵感手环第一步——0.96寸OLED显示实验
这算是我这个系列的第一篇博客吧。首先要解决的就是屏幕显示问题。我选择了目前新兴起的OLED显示模块。
OLED(OrganicLightEmittingDiode),中文译作有机发光二极管,目前被广泛的应用于移动设备甚至电视上。它既拥有超快的响应速度和轻薄的优势,又存在寿命与对大尺寸支持不足的瓶颈。
OLED的优点
1、厚度可以小于1毫米,仅为LCD屏幕的1/3,并且重量也更轻;
2、固态机构,没有液体物质,因此抗震性能更好,不怕摔;
3、几乎没有可视角度的问题,即使在很大的视角下观看,画面仍然不失真;
4、响应时间是LCD的千分之一,显示运动画面绝对不会有拖影的现象;
5、低温特性好,在零下40度时仍能正常显示,而LCD则无法做到;
6、制造工艺简单,成本更低;
7、发光效率更高,能耗比LCD要低;
8、能够在不同材质的基板上制造,可以做成能弯曲的柔软显示器。
OLED的缺点
1、寿命通常只有5000小时,要低于LCD至少1万小时的寿命;
2、不能实现大尺寸屏幕的量产,因此目前只适用于便携类的数码类产品;
3、存在色彩纯度不够的问题,不容易显示出鲜艳、浓郁的色彩。 ******************【摘自百度】
首先,该模块采用SPI 或 IIC 通信方式,最多占用5个IO口。我使用的是7针模块,采用4线SPI 通信方式。
该模块有以下特点:
1. 模块有单色和双色可选,单色为纯蓝色,双色为黄蓝双色(本人选用单色);
2. 显示尺寸为0.96寸
3. 分辨率为128*64
4. 多种接口方式,该模块提供了总共 5 种接口包括: 6800、 8080 两种并行接口方式、 3线或4线的SPI接口,IIC接口方式
5. 不需要高压,直接接3.3V就可以工作;(可以与stm32的引脚直接相接)
该模块内部采用SSD1306驱动,显存为128*64bit大小, SSD1306将全部显存分为8页,每页128字节
OLED相当于64行128列点阵,每个像素点,0点亮,1熄灭
OLED将纵向64行分为8页,每页8行
该实验的难点就在于理解取模的ASCII码表与写入程序的关系。下面我们来详细分析一下。
首先根据这个官方给出的设置格式,我们采用列行式,就是先取列,再取行。比如我们取个大写的 “A” 的字模。
{0x00,0x00,0xC0,0x38,0xE0,0x00,0x00,0x00,0x20,0x3C,0x23,0x02,0x02,0x27,0x38,0x20} ,/*"A",0*/
/* (8 X 16 , 宋体 )*/
分析:
第一个0X00------表示第一列前8个像素,从高位向低位,也就是从下往上写,全灭,所以是0X00,所以在SPI_Write()函数中,是从高位往低位写的。
第二个0X00------表示第二列前8个像素,同上。
第三个0XC0--> 1100 0000,从高位往低位,正好下面两个像素亮了。
后面都是这样分析,大家可以自己对一下。
也就是说按照他的设置,这个取模软件取的是按照从高位往低位取,前8个字节是第一页的所有像素状态。一共可以取128个字节。因为每一页有128列,8行。但是这个大写字母和汉字不一样,他的宽度是汉字的一半,所以生成的ASCII码表只有16个,一列,因为前8个字节是第一页的,后8个字节是第二页的,一个16*16的汉字需要占用两页(16行),16列。
下面是节选的显示汉字的程序分析:
我使用STM32F103C8T6对该模块进行驱动,程序修改自中景园科技官方驱动程序。亲测可用。
OLED引脚介绍:
CS:OLED片选信号
RST:OLED复位端口
DC: 命令/数据选择端口(0:读写命令, 1: 读写数据)
SCLK(D0):串口时钟线
SDIN(D1): 串口数据线
关于SPI的相关知识,可以参见这篇博客:http://www.cnblogs.com/qsyll0916/p/8053905.html
首先是SPI.C,包含了对该模块的各种操作,就是对SPI 写进行符合OLED的包装。写字符,写数字,写字符串,可调显示字体大小,但是需要包含两个ASCII字库。
#include "spi.h"
#include "word.h" //字库头文件 #define OLED_Order 0 //定义写命令
#define OLED_Data 1 //定义写数据 //尽在内部调用函数
static u32 oled_pow(u8 m,u8 n);
static void OLED_GPIO_INIT(void);
static void SPI_Write(u8 data, u8 Mode);
static void OLED_Coord(u8 x, u8 y); //使用管脚初始化
static void OLED_GPIO_INIT(void)
{
GPIO_InitTypeDef GPIO_InitStruct; //开启GPIOD的时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
//设置GPIO的基本参数
GPIO_InitStruct.GPIO_Pin = OLED_CS_PIN | OLED_RST_PIN | OLED_DC_PIN | OLED_D0_PIN | OLED_D1_PIN;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP; //推挽输出
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz; //输出速度50MHz GPIO_Init(OLED_PORT, &GPIO_InitStruct); GPIO_SetBits(OLED_PORT, OLED_CS_PIN | OLED_RST_PIN | OLED_DC_PIN | OLED_D0_PIN | OLED_D1_PIN);
} /* SPI写数据/命令
* Mode :OLED_Order:写命令 OLED_Data:写数据
* data :数据/命令
*/
static void SPI_Write(u8 data, u8 Mode)
{
u8 i = ; if(Mode)
{
OLED_DC(); //DC引脚输入高,表示写数据
}
else
{
OLED_DC(); //DC引脚输入低,表示写命令
}
OLED_CS(); //CS引脚输入低,片选使能
for(i = ; i < ; i++)
{
OLED_D0(); //D0引脚输入低
if(data&0x80) //判断传输的数据最高位为1还是0
{
OLED_D1(); //D1引脚输入高
}
else
{
OLED_D1(); //D1引脚输入低
}
OLED_D0(); //D1引脚输入高
data<<=; //将数据左移一位
}
OLED_DC(); //DC引脚输入低
OLED_CS(); //CS引脚输入高,片选失能
} /* 设置OLED屏的显示坐标
* X : 表示OLED的水平坐标(0—127)
* Y : 表示OLED的页(0—7)
*/
static void OLED_Coord(u8 x, u8 y)
{
SPI_Write((0xb0 + y) ,OLED_Order);
SPI_Write((((x & 0xf0)>>) | 0x10), OLED_Order);//高4位
SPI_Write((x & 0x0f)|0x01, OLED_Order);//低4位
}
//清屏,一开始这里写错了,把写命令写成了写数据,导致清屏不正确,发现屏幕上有很多噪点,说明没有清屏成功。
void OLED_Clear(void)
{
u8 i = , j = ; for(i = ; i < ; i++)
{
SPI_Write(0xb0 + i,OLED_Order);
SPI_Write(0x00,OLED_Order);
SPI_Write(0x10,OLED_Order);
for(j = ; j < ; j++)
{
SPI_Write(0x00, OLED_Data);
}
}
} //关oled显示
void OLED_Display_Off(void)
{
SPI_Write(0x8D,OLED_Order);
SPI_Write(0x10,OLED_Order);
SPI_Write(0xAE,OLED_Order);
}
//开oled显示
void OLED_Display_On(void)
{
//电荷泵设置(初始化时必须打开,否则看不到显示)
SPI_Write(0x8D, OLED_Order);
SPI_Write(0x14, OLED_Order);//bit2 0:关闭 1:打开
SPI_Write(0xAF, OLED_Order);//0xAF:开显示
} //oled参数初始化
void OLED_Init(void)
{
OLED_GPIO_INIT(); //端口初始化 OLED_RST();
delay_ms();
OLED_RST();
delay_ms();
OLED_RST(); SPI_Write(0xAE, OLED_Order);//0xAE:关显示 SPI_Write(0x00, OLED_Order);//设置低列地址
SPI_Write(0x10, OLED_Order);//设置高列地址 //设置行显示的开始地址(0-63)
//40-47: (01xxxxx)
SPI_Write(0x40, OLED_Order); //设置对比度
SPI_Write(0x81, OLED_Order);
SPI_Write(0xff, OLED_Order);//这个值越大,屏幕越亮(和上条指令一起使用)(0x00-0xff) SPI_Write(0xA1, OLED_Order);//0xA1: 左右反置, 0xA0: 正常显示(默认0xA0)
SPI_Write(0xC8, OLED_Order);//0xC8: 上下反置, 0xC0: 正常显示(默认0xC0) //0xA6: 表示正常显示(在面板上1表示点亮,0表示不亮)
//0xA7: 表示逆显示(在面板上0表示点亮,1表示不亮)
SPI_Write(0xA6, OLED_Order); SPI_Write(0xA8, OLED_Order);//设置多路复用率(1-64)
SPI_Write(0x3F, OLED_Order);//(0x01-0x3f)(默认为3f) //设置显示抵消移位映射内存计数器
SPI_Write(0xD3, OLED_Order);
SPI_Write(0x00, OLED_Order);//(0x00-0x3f)(默认为0x00) //设置显示时钟分频因子/振荡器频率
SPI_Write(0xD5, OLED_Order);
//低4位定义显示时钟(屏幕的刷新时间)(默认:0000)分频因子= [3:0]+1
//高4位定义振荡器频率(默认:1000)
SPI_Write(0x80, OLED_Order);// //时钟预充电周期
SPI_Write(0xD9, OLED_Order);
SPI_Write(0xF1, OLED_Order);//[3:0],PHASE 1; [7:4] PHASE 2 //设置COM硬件应脚配置
SPI_Write(0xDA, OLED_Order);
SPI_Write(0x12, OLED_Order);//[5:4] 默认:01 SPI_Write(0xDB, OLED_Order);//
SPI_Write(0x40, OLED_Order);// //设置内存寻址方式
SPI_Write(0x20, OLED_Order);
//00: 表示水平寻址方式
//01: 表示垂直寻址方式
//10: 表示页寻址方式(默认方式)
SPI_Write(0x02, OLED_Order);// //电荷泵设置(初始化时必须打开,否则看不到显示)
SPI_Write(0x8D, OLED_Order);
SPI_Write(0x14, OLED_Order);//bit2 0:关闭 1:打开 //设置是否全部显示 0xA4: 禁止全部显示
SPI_Write(0xA4, OLED_Order); //0xA6: 表示正常显示(在面板上1表示点亮,0表示不亮)
//0xA7: 表示逆显示(在面板上0表示点亮,1表示不亮)
SPI_Write(0xA6, OLED_Order);// SPI_Write(0xAF, OLED_Order);//0xAF:开显示
SPI_Write(0xAF, OLED_Order); //不知道为什么要写两次 OLED_Clear();
OLED_Coord(,);
} //显示汉字,设置坐标,
void OLED_ShowChinese(u8 x, u8 y, u8 chinese)
{
u8 t,adder=; OLED_Coord(x,y); for(t=;t<;t++) //每行16个元素,一个字需要两行字符串
{
SPI_Write(Hzk[*chinese][t],OLED_Data);
adder+=;
} OLED_Coord(x,y+); for(t=;t<;t++)
{
SPI_Write(Hzk[*chinese+][t],OLED_Data);
adder+=;
}
} //在指定位置显示一个字符,包括部分字符
//x:0~127
//y:0~63
//mode:0,反白显示;1,正常显示
//size:选择字体 16/12
void OLED_ShowChar(u8 x, u8 y, u8 chr)
{
unsigned char c=, i=; c = chr - ' '; //得到偏移后的值 if(x > Max_Column - )
{x=;y=y+;} if(SIZE ==) //8*16字符
{
OLED_Coord(x,y); for(i=;i<;i++)
SPI_Write(F8X16[c*+i],OLED_Data); OLED_Coord(x,y+); for(i=;i<;i++)
SPI_Write(F8X16[c*+i+],OLED_Data);
}
else //6*8字符
{
OLED_Coord(x,y+); for(i=;i<;i++)
SPI_Write(F6x8[c][i],OLED_Data); }
} //显示字符串
void OLED_Show_String(u8 x, u8 y, u8 *chr)
{
u8 j=;
while (chr[j]!='\0')
{
OLED_ShowChar(x,y,chr[j]); x+= ; if(x>){x=;y+=;} //自动换行写 j++;
}
} //m^n函数
static u32 oled_pow(u8 m,u8 n)
{
u32 result = ;
while(n--)result*=m;
return result;
} //显示数字
//x,y :起点坐标
//len :数字的位数
//size:字体大小
//mode:模式 0,填充模式;1,叠加模式
//num:数值(0~4294967295);
void OLED_ShowNum(u8 x,u8 y,u32 num,u8 len,u8 size)
{
u8 t = , temp = ;
u8 enshow=; for(t=;t<len;t++)
{
temp=(num/oled_pow(,len-t-))%; if(enshow==&&t<(len-))
{
if(temp==)
{
OLED_ShowChar(x+(size/)*t,y,' ');
continue;
}else enshow=; }
OLED_ShowChar(x+(size/)*t,y,temp+'');
}
}
然后就是头文件:SPI.h
#ifndef __SPI_H
#define __SPI_H #include <stm32f10x.h>
#include "systick.h" #define OLED_PORT GPIOA #define OLED_CS_PIN GPIO_Pin_3
#define OLED_RST_PIN GPIO_Pin_4
#define OLED_DC_PIN GPIO_Pin_5
#define OLED_D0_PIN GPIO_Pin_6
#define OLED_D1_PIN GPIO_Pin_7 //X为1时对应GPIO端口输出高电平,X为0时对应GPIO端口输出低电平
#define OLED_CS(X) X?GPIO_SetBits(OLED_PORT, OLED_CS_PIN):GPIO_ResetBits(OLED_PORT, OLED_CS_PIN) #define OLED_RST(X) X?GPIO_SetBits(OLED_PORT, OLED_RST_PIN):GPIO_ResetBits(OLED_PORT, OLED_RST_PIN) #define OLED_DC(X) X?GPIO_SetBits(OLED_PORT, OLED_DC_PIN):GPIO_ResetBits(OLED_PORT, OLED_DC_PIN) #define OLED_D0(X) X?GPIO_SetBits(OLED_PORT, OLED_D0_PIN):GPIO_ResetBits(OLED_PORT, OLED_D0_PIN) #define OLED_D1(X) X?GPIO_SetBits(OLED_PORT, OLED_D1_PIN):GPIO_ResetBits(OLED_PORT, OLED_D1_PIN) //OLED模式设置
#define SIZE 16
//#define SIZE 8 //SIZE选择英文字体的大小
#define XLevelL 0x00
#define XLevelH 0x10
#define Max_Column 128
#define Max_Row 64
#define Brightness 0xFF
#define X_WIDTH 128
#define Y_WIDTH 64 void OLED_Init(void);
void OLED_Clear(void);
void OLED_Display_Off(void);
void OLED_Display_On(void);
void OLED_ShowChinese(u8 x, u8 y, u8 chinese);
void OLED_ShowChar(u8 x, u8 y, u8 chr);
void OLED_Show_String(u8 x, u8 y, u8 *chr);
void OLED_ShowNum(u8 x,u8 y,u32 num,u8 len,u8 size); #endif
写完这两个文件,就可以在主函数调用 OLED_ShowChinese() ,进行参数配置后,就可以显示了。
具体调用方式为:OLED_ShowChinese(32,0,0);//钱
这个里面前两个参数是进行显示坐标选择,第三个参数是选择你的字库里面第几个汉字的行数。比如我选择了 0,那么在我的字库头文件中前两个ASCII码表就是我要显示汉字的ASCII码表,这个码表是采用字模软件生成的。
char Hzk[][]={ {0x20,0x10,0x2C,0xE7,0x24,0x24,0x00,0x90,0x90,0xFF,0x90,0x49,0x4A,0x48,0x40,0x00},
{0x01,0x01,0x01,0x7F,0x21,0x11,0x40,0x40,0x20,0x13,0x0C,0x14,0x22,0x41,0xF8,0x00},/*"钱",7*/
/* (16 X 16 , 宋体 )*/
使用PCtoLCD2002完美版进行取模。具体字模生成方式可参见中景园官方教程:https://wenku.baidu.com/view/42efcb877cd184254a353584.html
汉字生成为两行16进制码表。
显示效果:![]()
灵感手环第一步——0.96寸OLED显示实验的更多相关文章
- 0.96寸OLED显示屏驱动手册(SSD1306)
MCU IIC接口 IIC通信接口由从地址位SA0,IIC总线数据信号SDA(输出SDAout/D2和输入SDAin /D1)和IIC总线时钟信号SCL(D0).不管是数据线还是时钟线都需要连接上拉电 ...
- Arduino SPI驱动7引脚0.96寸OLED SSD1306 调试笔记
https://www.geek-workshop.com/thread-37818-1-1.html 2.下载最新库https://learn.adafruit.com/monoc ... ibra ...
- 基于I2C总线的0.96寸OLED显示屏驱动
资料未整理,先占位置,以后补充
- 张高兴的 Windows 10 IoT 开发笔记:0.96 寸 I2C OLED
This is a Windows 10 IoT Core project on the Raspberry Pi 2/3, coded by C#. GitHub:https://github.co ...
- Arduino通过I2C(SSD1306)驱动0.96寸12864OLED
I2C驱动的128x64 OLED I2C (Inter-Integrated Circuit) 集成电路总线是I2CBus的简称, 是一种串行通信总线, 使用多主从架构. 飞利浦公司在1980年代为 ...
- MicroPython之TPYBoard v102开发板控制OLED显示中文
转载请以链接形式注明文章来源,公众号:MicroPython玩家汇 0x00前言 之前看到一篇文章是关于TPYBoardv102控制OLED屏显示的,看到之后就想尝试一下使用OLED屏来显示中文.最近 ...
- MicroPython实例之TPYBoard开发板控制OLED显示中文
0x00 前言 之前看到一篇文章是关于TPYBoard v102控制OLED屏显示的,看到之后就想尝试一下使用OLED屏来显示中文.最近利用空余时间搞定了这个实验,特此将实验过程及源码分享出来,方便以 ...
- 玩转X-CTR100 l STM32F4 l OLED显示-SSD1306无字库
我造轮子,你造车,创客一起造起来!塔克创新资讯[塔克社区 www.xtark.cn ][塔克博客 www.cnblogs.com/xtark/ ] OLED显示屏具有自发光特性,不需要背光, ...
- 用STM32玩SR04(测距、串口显示、OLED显示)
目录 用STM32玩SR04(测距.串口显示.OLED显示) 超声波模块使用 SR04初始化 SR04使用串口打印数据 SR04使用OLED来传输数据,并显示在OLED上 用STM32玩SR04(测距 ...
随机推荐
- 磁盘管理之 raid 文件系统 分区
第1章 RAID 磁盘阵列 1.1 使用raid的目的 1)获得更大的容量 2)让数据更安全 3)读写速度更快 1.2 raid0.raid1.raid5.raid10对比 磁头 0磁道 1扇区 前4 ...
- HTML资源定位器-URL
URL 也被称为网址. URL 可以由单词组成,比如 "www.baidu.com",或者是因特网协议(IP)地址:192.168.1.253. URL - Uniform Res ...
- Android开发之漫漫长途 Ⅱ——Activity的显示之Window和View(2)
该文章是一个系列文章,是本人在Android开发的漫漫长途上的一点感想和记录,我会尽量按照先易后难的顺序进行编写该系列.该系列引用了<Android开发艺术探索>以及<深入理解And ...
- centos6.7系统安装流程
虚拟机创建centos的过程,如下: 1.首先创建一个空白文件 2.打开虚拟机,打开文件,或者页面的<创建虚拟机>,如下: 3.打开之后如下所示,选择自定义,Linux崇尚自由 4.第四步 ...
- 一个简单的基于BIO的RPC框架
github地址:https://github.com/Luyu05/BioRpcExample PART1:先来整体看下项目的构成 其中bio-rpc-core就是所谓的rpc框架 bio-rpc- ...
- 40个Java多线程问题
1.多线程有什么用? 一个可能在很多人看来很扯淡的一个问题:我会用多线程就好了,还管它有什么用?在我看来,这个回答更扯淡.所谓”知其然知其所以然”,”会用”只是”知其然”,”为什么用”才是”知其所以然 ...
- axios在vue中的简单配置与使用
一.axios 简介 axios 是一个基于Promise 用于浏览器和 nodejs 的 HTTP 客户端,它本身具有以下特征:https://hzzly.github.io/2017/03/12/ ...
- [O]SQL SERVER下有序GUID和无序GUID作为主键&聚集索引的性能表现
背景 前段时间学习<Microsoft SQL Server 2008技术内幕:T-SQL查询>时,看到里面关于无序GUID作为主键与聚集索引的建议,无序GUID作为主键以及作为聚集索引 ...
- 这次真的忽略了一些ActiveMQ内心的娇艳
好久没总结了,内心有点空虚了,所以今天主要给园里的朋友们分享一点儿这几天使用ActiveMQ过程中踩过的小坑,虽然说这东西简单易用,代码几行配置也就几行,问题不大但是后果有点严重,所以就要必要总结一下 ...
- webpack 3.X学习之CSS处理
Loaders Loaders是Webpack最重要的功能之一,他也是Webpack如此盛行的原因.通过使用不同的Loader,Webpack可以的脚本和工具,从而对不同的文件格式进行特定处理. Lo ...