1 前言
前两天在网上看到能跑linux系统同时又这么小巧的开发板,我见猎心喜直接就剁手买了回来。但买回来就尴尬了,除了能跑自己写的几个小程序后,不知道干嘛了。想到自己之前写过一个基于QT的电子网络词典,把它移植到开发板上倒是一个不错的练手项目。只移植就太单调了,针对这个项目想做一下优化,使用LCD电阻触控屏进行显示及操作。但之前没搞过lcd屏幕,研究了一下就先搞个oled屏幕做前奏练练手。
一上手发现这个oled屏幕上涉及的东西也不少,最重要的是网上几乎没有基于linux驱动oled屏幕的资料,全是基于stm32驱动oled屏幕。正好给大家分享一下我的移植过程,填补这个空白的领域!!!
2 luckfox简介
官方开发板就是一个能跑阉割版linux的小板子,本身没啥值得介绍的。有兴趣的去Luckfox官网看看。
我买的是下面的Luckfox Pico Plus版本开发板。其引脚接口如下所示:
从图中可以看出引脚很少,很多引脚都是复用的。能同时驱动的设备有限,所以本教程就只驱动了一个oled屏幕硬件。
3 编号计算
开发板是成品。这里只需要知道每个引脚的功能即可。所以先算出每个引脚的编号以及在底层设备树的表述形式。
GPIO 有5个bank:GPIO0-GPIO4,每个bank分为4组,共有32个pin:A0~A7, B0~B7, C0~C7, D0~D7
(上面是官方的描述,人话就是gpio0~4共5组端口,刨除8个接地引脚还剩32个引脚,一共5*32=160个引脚,但实际只引出40个引脚。)
GPIO 的命名方式为 GPIO{bank}_{group}{X},如下所示:
GPIO0_A0 ~ A7
GPIO0_B0 ~ B7
GPIO0_C0 ~ C7
GPIO0_D0 ~ D7
GPIO1\_A0 ~ A7
....
GPIO1\_D0 ~ D7
....
GPIO4\_D0 \~ D7
GPIO 编号计算方法如下:
GPIO 引脚编号计算公式:pin = bank * 32 + number
GPIO 组内编号计算公式:number = group * 8 + X
综上:pin = bank * 32 + (group * 8 + X)
以 GPIO1_C7_d 为例说明,其中:
bank :1
group :2 (A=0, B=1, C=2, D=3)
X :7
所以 GPIO1_C7_d 的引脚编号为:1 x 32 + ( 2 x 8 + 7 ) = 55
这样就算出了引脚对应的编号是55,后续接线就简单多了。
注:我研究了官方送的芯片手册,官方手册对应的引脚表和它这个图上的编号是一点都对不上。后面实验测试以上面图片引脚接法为准。
4 硬件连接
4.1 官方接法
如下是官方的readme文件中注明的各功能引脚对应的引脚编号:
VCC -> 5V //5V电源
GND -> GND //接地
//DIN -> 34(SPI0_MOSI) //spi总线数据主出从入引脚(错误)
DIN -> 50(SPI0_MOSI) //spi总线数据主出从入引脚
CLK -> 49(SPI0_SCK) //spi时钟信号引脚
CS -> 48 //片选引脚
DC -> 34 //数据/命令选择引脚
RST -> 51 //复位引脚
BL -> 4 //调节背光引脚(不用接)
注意:
1.官方给的readme上标注的34号是主出从入引脚是错误的,实际上还是以图片为准。
2.从引脚接法可以看出MISO(主入从出)没有使用。所以应该是单工工作方式。
下图是官方的1.3寸oled屏幕,上面对应着2个单独引脚的按钮,使用的是7脚spi协议。
这里我想复习一下spi协议方面的知识,所以选型时选的是7脚的spi协议oled屏幕。屏幕引脚如下图所示:
4.2 正确接法
经过实际验证,正确的适配0.96寸屏幕的引脚接法如下:
GND //接地
VCC //5V电源
D0 -> 49 //spi时钟信号引脚
D1 -> 50 //spi总线数据主出从入引脚
RES -> 51 //复位引脚
DC -> 34 //数据/命令选择引脚
CS -> 48 //片选引脚
4.3 引脚说明
每个功能引脚作用如下:
GND:接地
VCC:3V-5V电源
clk:时钟信号线就是以固定频率发送信号用于控制传输数据的速率和时序,再就i是让主设备从设备知道何时发送和接收数据。
MOSI: 主出从入数据线就是由主机发出数据,从机接收数据。数据的频率和时序由clk时钟线控制。
res:硬件复位引脚,给引脚高电平,屏幕直接硬件初始化。
cs:片选线,用于选择哪个从设备,就一个从设备,所以引脚不用管。
dc:数据/命令选择引脚,在给屏幕传输数据时,屏幕并不知道这个数据是做什么的,这个引脚的作用就是告诉屏幕我传的是数据还是命令,置零传输命令,置一传输数据。
5 屏幕显示原理
我看网上关于oled屏幕的各种教程大都是从官方教程上复制粘贴得来的,讲了很大一篇幅东西;但一个简单的oled屏幕哪有那么复杂,核心东西就那些。简单的讲屏幕就是由长128宽64共8192个led灯组成一个点阵。这个点阵的宽被分成8块,称为b0~b7页。
注:任何数据均从0起始,长128(0~127)宽64(0~63);
因此64行除以8页,得出一页占据的宽度是8个led灯,所以一页的每一列刚好可以用一个char类型的数据表示出来,例如一列全亮就是(1111,1111)–> 0xff。
综上所述,我们是不是就可以准确控制某一个led灯的亮灭了。例如:第二页第5列的第2个灯亮,实际上转换一下就是第19行的第6列的led灯点亮。
插入图片
6 初始化屏幕命令集
oled屏幕在点亮前需要一堆固定的指令初始化屏幕。网上的命令集大体一样但细节又有区别,所以我查阅了很多资料,对比不同的命令集集合,查询每个命令的含义,综合给出了一个全面的命令集。而且每个命令的含义都有,全网应该找不到比我更全的讲解了。
oled_write_reg(0xae); // 关闭显示
oled_write_reg(0x00); // 设置起始地址的低4位,用于确定第一列的位置(页寻址模式下)
oled_write_reg(0x10); // 设置起始地址的高4位,用于确定第一列的位置(页寻址模式下)
// 数据是16进制的,低4位范围是(00h-0fh),高4位范围是(10h-1fh),这里有效位取f,
// 即展开为二进制后,只要后四位,高位当8位数据的高4位,低位当8位数据的低4位,
// 即可组成最大值第127列
oled_write_reg(0x40); // 设置显示的开始行,确定屏幕显示的开始位置
oled_write_reg(0x81); // 设置对比度
oled_write_reg(0xcf); // 对比度的值,值越大,屏越亮
oled_write_reg(0xa1); // 设置显示模式和显示方向,a0左右反置,a1正常
oled_write_reg(0xc8); // 设置显示模式和显示方向 ,c0上下反置,c8正常
oled_write_reg(0xa6); // 设置屏幕的正显模式(正常显示模式)
oled_write_reg(0xa8); // 设置多路复用率(1-64)
oled_write_reg(0x3f); // (0x01-0x3f)(默认为3f)
oled_write_reg(0xd3); // 设置显示偏移,设置显示抵消移位映射内存计数器
oled_write_reg(0x00); // 偏移的起始位置,0x00-0x3f)(默认为0x00)
oled_write_reg(0xd5); // 设置显示时钟分频因子/振荡器频率,影响显示的频率,
oled_write_reg(0x80); // 低4位定义显示时钟(屏幕的刷新时间)
// (默认:0000)分频因子= [3:0]+1 //高4位定义振荡器频率(默认:1000)
oled_write_reg(0xd9); // 设置屏幕的预充电周期,用来设置充电泵的状态
oled_write_reg(0xf1); //[3:0],PHASE 1; [7:4] PHASE 2
// 分为充电阶段和放电阶段,低4位表示充电时间,高4位表示放电时间
oled_write_reg(0xda); // 用来设置com扫描方向,确保图像正确显示
oled_write_reg(0x12); //[5:4]配置,默认:01
oled_write_reg(0xdb); // 设置com引脚硬件配置(作用不清楚,最后实验验证)
oled_write_reg(0x40); // 设置com引脚硬件配置
oled_write_reg(0x20); // 设置页面寻址模式
oled_write_reg(0x10); // 00: 表示水平寻址方式
oled_write_reg(0xc8);
// 01: 表示垂直寻址方式
// 10: 表示页寻址方式(默认方式)
oled_write_reg(0x8d); // 启动电荷泵,提供电源
oled_write_reg(0x14); // bit2 0:关闭 1:打开
oled_write_reg(0xa4); // 设置全屏点亮,a4调用GDDRAM中的数据,a5忽略直接点亮,可以a4恢复被a5调用的状态
// oled_write_reg(0x01); // 置一是开,屏幕全亮,置零是关
oled_write_reg(0xa6); // 设置显示是否反转,a6是1亮,a7是0亮
// oled_write_reg(0x00);
7 总结
至此oled屏幕的硬件信息和连线方式就介绍完成了,有需求的照着教程根据对应编号连线即可。