PotatoPie 4.0 实验教程(10)——FPGA驱动SSD1315 IIC接口的OLED显示-Anlogic-安路社区-FPGA CPLD-ChipDebug

PotatoPie 4.0 实验教程(10)——FPGA驱动SSD1315 IIC接口的OLED显示

一、OLED模块简介

OLED,即有机发光二极管( Organic Light Emitting Diode)。 OLED 由于同时具备自发光,不需背
光源、对比度高、厚度薄、视角广、反应速度快、可用于挠曲性面板、使用温度范围广、构造及
制程较简单等优异之特性,被认为是下一代的平面显示器新兴应用技术。
LCD 都需要背光,而 OLED 不需要,因为它是自发光的。这样同样的显示 OLED 效果要来得好一
些。以目前的技术,
OLED 的尺寸还难以大型化,但是分辨率确可以做到很高。在此我们使用的
0.96OLED显示屏,该屏有以下特点:
10.96 OLED 有黄蓝,白,蓝三种颜色可选;其中黄蓝是屏上 1/4 部分为黄光,下 3/4 为蓝;
而且是固定区域显示固定颜色,颜色和显示区域均不能修改;白光则为纯白,也就是黑底白字;
蓝色则为纯蓝,也就是黑底蓝字。
2)分辨率为 128*64
3
)多种接口方式;OLED 裸屏总共种接口包括:68008080 两种并行接口方式、3 线或 4 线的
串行
SPI 接口方式、 IIC 接口方式(只需要 2 根线就可以控制 OLED 了!),这五种接口是通过
屏上的
BS0~BS2 来配置的。
4)OLED接口分别为七针的 SPI/IIC 兼容模块,四针的IIC 模块。我们 demo板配的是4针的IIC OLED模块

20240322083400518-image

1. GND 电源地
2. VCC 电源正(35.5V
3. SCL OLED D0 脚,在 IIC 通信中为时钟管脚
4. SDA OLED D1 脚,在 IIC 通信中为数据管脚

20240322083556972-image

该模块所用的驱动 IC SSD1306(或SSD1315);其具有内部升压功能;所以在设计的时候不需要再专一设计升压电路;当然了本屏也可以选用外部升压,具体的请详查数据手册。SSD1306(或SSD1315) 的每页包含了128 个字节,总共 8 页,这样刚好是 128*64 的点阵大小(每个字节8个点)。这点与 1.3 OLED 驱动 IC SSD1106稍有不同,SSD1106(或SSD1315) 每页是 132 个字节,也是 8 页。所以在用 0.96 OLED 移植 1.3 OLED 程序的时候需要将 0.96 寸的显示地址向右偏移 2,这样显示就正常了;否则在用 1.3 寸的时候 1.3寸屏右边会有 4 个像素点宽度显示不正常或是全白,这点大家注意一下。其它的 SSD1306(或SSD1315) SSD1106 区别不大。

OLED原理图

七针模块的原理图如下,对于4针模块取前面4个脚就行了,

1. GND 电源地
2. VCC 电源正(35.5V
3. D0 OLED D0 脚,在 SPI IIC 通信中为时钟管脚
4. D1 OLED D1 脚,在 SPI IIC 通信中为数据管脚

20240322084011817-image

IIC通信格式

20240322085230167-image

OLED GRAM结构

总共 8 页,每页128 个字节,每个字节8个点。

20240322104836271-image

20240322105235302-image

20240322105404849-image

我们这里不去细究寄存器配置,直接使用厂家的配置,感兴趣的可以SSD1315.pdf手册。

OLED初始化

OLED屏的厂家提供了初始化寄存器的C代码,
OLED_WR_Byte(0xAE,OLED_CMD);//--display off
	OLED_WR_Byte(0x00,OLED_CMD);//---set low column address
	OLED_WR_Byte(0x10,OLED_CMD);//---set high column address
	OLED_WR_Byte(0x40,OLED_CMD);//--set start line address  
	OLED_WR_Byte(0xB0,OLED_CMD);//--set page address
	OLED_WR_Byte(0x81,OLED_CMD); // contract control
	OLED_WR_Byte(0xFF,OLED_CMD);//--128   
	OLED_WR_Byte(0xA1,OLED_CMD);//set segment remap 
	OLED_WR_Byte(0xA6,OLED_CMD);//--normal / reverse
	OLED_WR_Byte(0xA8,OLED_CMD);//--set multiplex ratio(1 to 64)
	OLED_WR_Byte(0x3F,OLED_CMD);//--1/32 duty
	OLED_WR_Byte(0xC8,OLED_CMD);//Com scan direction
	OLED_WR_Byte(0xD3,OLED_CMD);//-set display offset
	OLED_WR_Byte(0x00,OLED_CMD);//
	
	OLED_WR_Byte(0xD5,OLED_CMD);//set osc division
	OLED_WR_Byte(0x80,OLED_CMD);//
	
	OLED_WR_Byte(0xD8,OLED_CMD);//set area color mode off
	OLED_WR_Byte(0x05,OLED_CMD);//
	
	OLED_WR_Byte(0xD9,OLED_CMD);//Set Pre-Charge Period
	OLED_WR_Byte(0xF1,OLED_CMD);//
	
	OLED_WR_Byte(0xDA,OLED_CMD);//set com pin configuartion
	OLED_WR_Byte(0x12,OLED_CMD);//
	
	OLED_WR_Byte(0xDB,OLED_CMD);//set Vcomh
	OLED_WR_Byte(0x30,OLED_CMD);//
	
	OLED_WR_Byte(0x8D,OLED_CMD);//set charge pump enable
	OLED_WR_Byte(0x14,OLED_CMD);//
	
	OLED_WR_Byte(0xAF,OLED_CMD);//--turn on oled panel

那我们只需要改成verilog格式即可。

always@(*)
begin
	case(Init_index)
		'd0:	Init_data_reg <= 8'hAE;//--display off
		'd1:	Init_data_reg <= 8'h00;//---set low column address
		'd2:	Init_data_reg <= 8'h10;//---set high column address
		'd3:	Init_data_reg <= 8'h40;//--set start line address  
		'd4:	Init_data_reg <= 8'hB0;//--set page address
		'd5:	Init_data_reg <= 8'h81;// contract control
		'd6:	Init_data_reg <= 8'hFF;//--128  
		'd7:	Init_data_reg <= 8'hA1;//set segment remap 
		'd8:	Init_data_reg <= 8'hA6;//--normal / reverse
		'd9:	Init_data_reg <= 8'hA8;//--set multiplex ratio(1 to 64)
		'd10:	Init_data_reg <= 8'h3F;//--1/32 duty
		'd11:	Init_data_reg <= 8'hC8;//Com scan direction
		'd12:	Init_data_reg <= 8'hD3;//-set display offset
		'd13:	Init_data_reg <= 8'h00;//
		'd14:	Init_data_reg <= 8'hD5;//set osc division
		'd15:	Init_data_reg <= 8'h80;//
		'd16:	Init_data_reg <= 8'hD8;//set area color mode off
		'd17:	Init_data_reg <= 8'h05;//
		'd18:	Init_data_reg <= 8'hD9;//Set Pre-Charge Period
		'd19:	Init_data_reg <= 8'hF1;//
		'd20:	Init_data_reg <= 8'hDA;//set com pin configuartion
		'd21:	Init_data_reg <= 8'h12;
		'd22:	Init_data_reg <= 8'hDB;//set Vcomh
		'd23:	Init_data_reg <= 8'h30;
		'd24:	Init_data_reg <= 8'h8D;//set charge pump enable
		'd25:	Init_data_reg <= 8'h14;
		'd26:	Init_data_reg <= 8'hAF;//--turn on oled panel
		default:
			Init_data_reg <= 8'hAE;
		endcase
end

代码解析

字符数据取模

显示字符先要生成字符数据,这里需要使用到字符取模工具PCtoLCD2002,该工具可以自定义生产数据的格式,保存为txt文件格式,非常方便。

20240321224201642-image

注意:选项设置为 数据前缀8’h, 数据后缀、行前缀、行后缀为空格。

20240321224326944-image

20240321224053183-image

20240321224129662-image

20240321224137653-image

初始化字符寄存器

 由于Verilog里面的memory只能一个一个赋值初始化,每个16*16大小的字符就是32个数据,手动copy的话,会非常繁琐,这里采用Python来读取txt文件生成初始化always块。最后只要将生成的always块copy到项目中即可。然后根据select的索引值选择对于的data输出即可。
import os
import sys
dirname, filename = os.path.split(os.path.abspath(sys.argv[0]))
fontfileName = dirname + "\\icmaker.TXT"
print("open " + fontfileName)
data = open(fontfileName)  # 打开txt文件
data = data.read().split(" ") # 以空格分割
file_handle=open('1.txt',mode='w')  
file_handle.write("always@(posedge clk or negedge rst)\n")
file_handle.write("begin\n\tif(rst == 1'b0)\n\tbegin\n")
for index,dat in enumerate(data):
    file_handle.write("\t\tdata10[{}] = {};\n".format(index,dat))
file_handle.write("\tend\n")
file_handle.write("end")
file_handle.close()

在covernt.py运行python

20240327081151141-image

字符显示

16*16大小的字符会占用两个page,每个page占用16列。所以可以将Oled看成只有2*16大小,这就和oled清屏是一样的了。
设置page之后,再设置列地址,每输入一个数据,列地址会自动加一,字符数据的显示可以分为以下过程:
  1. 设置page,设置列地址,写入16个数据

  2. 设置page+1,设置列地址,写入16个数据

一共是写入2+16+2+16个数据,这样就完成了一个字符的显示。
show_page的值为0或1。

字符控制

字符控制模块也比较简单,向字符显示模块输入起始点和字符选择,选择要显示的字符。

(待续)

实验结果

20240322095426808-9c3cbfceba3c9a8ff0f0e46958103a1

20240322095643990-221db67a62316b1f779f3477109bbec

请登录后发表评论

    没有回复内容