FPGA调用OLED

本文介绍了博主在使用FPGA驱动0.91寸OLED显示屏的过程中,如何进行SPI协议的调用和OLED驱动芯片SSD1306的初始化设置。内容包括OLED的地址设定命令、内存寻址模式以及初始化指令的详细解释,同时分享了FPGA程序的修改和时序控制。此外,还提到了取模软件PCtoLCD2002的使用方法,帮助读者理解如何正确取模并显示数据。
摘要由CSDN通过智能技术生成

FPGA调用OLED

因为参加硬禾学堂的寒假在家一起练的活动,所以记录一些中间遇到的困难和解决方法,当然我因为刚刚接触FPGA,所以在了解了一定的驱动芯片的指令之后,发现电子森林中有驱动TFT_LCD的程序,所以直接拿过来修改了

OLED介绍

样式介绍

OLED屏幕的特点为:0.91寸七针,SPI协议通信;其中我的板子上面的七针有CLK(有些地方是用D0代替),MOSI(也可以用D1)

个人经验

我建议大家如果说是刚刚入手OLED,直接先把通信协议调好,然后把OLED初始化的程序发送给OLED再去调整,不然很容易产生挫败,因为刚入手OLED的话,有很多的指令不清楚,并且一开始想让OLED亮起来,需要发送的指令比较多

OLED驱动芯片指令介绍--SSD1306

先放一下我看过觉得好的指令介绍网址:

一:版权声明:本文为CSDN博主「专注于无线通信的蓬勃」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/XiaoXiaoPengBo/java/article/details/81329770

二:http://blog.sina.com.cn/s/blog_57ad1bd20102wtq8.html,这个博主的文章最下面的指令比较不常用(还是我用的少,所以我觉得不常用)

在本次项目中,因为我上面说了我主要是修改了电子森林的程序,所以具体的指令并没有了解太多,主要是了解了一下Addressing Setting Command Table(地址设定命令表)中的指令,具体的指令如图1所示:

图1:Addressing Setting Command Table(地址设定命令表)

 

按照我了解的顺序介绍一下指令:

最上面的两条指令(Set Lower Column Start Address for Page Addressing和Set Higher Column Start Address for Page Addressing Mode)是说的是在页地址模式下,设置起始地址的低四位和高四位,这样子加在一起刚好就是8位;

然后在看第三条指令(Set Memory Addressing Mode),这条指令是设置内存寻址方式,其中我们可以用三种模式,分别都是:水平地址模式;垂直地址模式;页地址模式(我目前采用的多是这种方式,因为要显示数据,一般不会是在一页上面写数据,需要写 在不同页的同几列上)

之后在看最后一条指令(Set Page Start Address for Page Addressing Mode),这条指令是在页地址模式下,设置页的起始地址

然后我觉得光是说明指令和指令的作用,大家可能对指令的直观印象还是不太清楚,我来举个例字,我们先看图2:

图2:OLED的第三页

这是OLED的第三页(因为页码是从0开始算的),这时候我们假如给OLED发送0xB2,之后给OLED发送命令是0x03(设置起始地址的低四位)之后再次给OLED发送命令0x10(设置起始地址的高四位),这样子就把我们的起始位置确定在第三页的SEG3了,就是说之后在把具体的数据发送到OLED就会从第三页的第三列的LED灯开始亮或者灭。

至于设置内存寻址的三种方式:页地址模式:就是从一页的起始列地址开始知道这一页的结束列地址,在这个过程中一列的数据写完,列地址指针会自动增加1跳到随后的一列

图3:页地址模式

 

水平模式:就是页地址的升级版本,一开始和页地址模式一样,就是从设定的某一页的起始列地址(具体位置根据指令)开始,每次当一列数据写完,列地址指针都会自动增加1,当列地址的指针到达我们设定的结束列地址,列地址指针就会恢复到设定的起始列地址指针,并且页地址指针会增加1,具体图像,请看下图

图4:水平模式

垂直模式:和之前的两种模式不太一样,具体的表现是:它是从某一页的一个起始列地址开始,当输入完一列数据后,它的列地址指针并不会增加,首先增加的页地址指针,之后当页地址指针增加到设定的结束页地址之后,才会增加列地址指针,并且页地址指针重新恢复到起始页地址

图5:垂直模式

 

0.91寸OLED初始化指令

放一下我的小伙伴给我的一开始的网页参照:

https://blog.csdn.net/p1279030826/article/details/107234646,我一开始是参考这位博主写的

	initial	//OLED初始化的命令及数据
		begin
			reg_init[0]		=	{1'b0,8'hAE}; // 关OLED显示
			reg_init[1]		=	{1'b0,8'h00}; // 正常显示(1亮0灭)
			reg_init[2]		=	{1'b0,8'h10}; // 启用电荷泵
			reg_init[3]		=	{1'b0,8'h40}; // 启用电荷泵
			reg_init[4]		=	{1'b0,8'h81}; // 开OLED显示
			reg_init[5]		=	{1'b0,8'hFF};//全部显示
			reg_init[6]		=	{1'b0,8'hA1};//
			reg_init[7]		=	{1'b0,8'hC8};
			reg_init[8]		=	{1'b0,8'hA6};
			reg_init[9]		=	{1'b0,8'hA8};
			reg_init[10]		=	{1'b0,8'h1f};//OLED的尺寸为0.91时
		//	reg_init[10]		=	{1'b0,8'h3f};//OLED的尺寸为0.96时
			reg_init[11]		=	{1'b0,8'hD3};
			reg_init[12]		=	{1'b0,8'h00};
			reg_init[13]		=	{1'b0,8'hd5};
			reg_init[14]		=	{1'b0,8'h80};
			reg_init[15]		=	{1'b0,8'hD9};
			reg_init[16]		=	{1'b0,8'hF1};
			reg_init[17]		=	{1'b0,8'hDA};
			reg_init[18]		=	{1'b0,8'h02};//OLED的尺寸为0.91时
		//	reg_init[18]		=	{1'b0,8'h12};//OLED的尺寸为0.96时
			reg_init[19]		=	{1'b0,8'hDB};
			reg_init[20]		=	{1'b0,8'h40};
			reg_init[21]		=	{1'b0,8'h20};
			reg_init[22]		=	{1'b0,8'h02};
			reg_init[23]		=	{1'b0,8'h8D};
			reg_init[24]		=	{1'b0,8'h14};
			reg_init[25]		=	{1'b0,8'hA4};//这个地方建议一开始调试的新手,发送A5
                                                 //A5指令的命令是全屏点亮开启,可以方便的调            
                                                 // 试SPI协议,但是发送A5之后,会无视你向    
                                                 //OLED的RAM写入的其他数据,如果可以点亮全 
                                                 //屏,用A4(全屏点亮关闭)即可                                               
			reg_init[26]		=	{1'b0,8'hA6};
			reg_init[27]		=	{1'b0,8'hAF};
			reg_init[28]		=	{1'b0,8'hA7};
		end	

引脚介绍,芯片介绍

我的FPGA的芯片是于Lattice XO2-4000HC-4MG132,具体使用的引脚是

OLED_CLK对应E12

OLED_MOSI对应F12

OLED_RES对应G12

OLED_DC对应F13

OLED_CS对应F14

 

程序介绍

首先编写程序之前,我们需要先去了解SPI这个协议,可以通过下面的文章来了解SPI协议的具体细节:

当我们了解了SPI协议的大致过程,我们还需要观察控制SSD1306的时序,这样子才能控制FPGA通过正确的拉高拉低传送正确的命令和数据给SSD1306,图6是SSD1306的开启时序

图6:上电时序

 

之后我又找到一张图,图7是关于4线SPI的时序介绍

图7:4线SPI接口时序

 

因为是不够熟悉FPGA,所以我直接修改了电子森林中调动TFT_LCD的程序,电子森林TFT_LCD的程序具体链接地址:https://www.eetree.cn/wiki/lcd%E6%98%BE%E7%A4%BA%E6%A8%A1%E5%9D%97

module LCD_RGB 
(
	input				clk_in,			//12MHz系统时钟
	input				rst_n_in,		//系统复位,低有效
 
	output	reg			OLED_RES,	//OLED复位
	output	reg			OLED_CS,		//OLED片选控制
	output	reg			OLED_DC,		//OLED数据指令控制
	output	reg			OLED_SCK,	//OLED时钟信号
	output	reg			OLED_MOSI	//OLED数据信号
);
 
	localparam			INIT_DEPTH = 16'd28; //LCD初始化的命令及数据的数量
 
	localparam			IDLE	=	3'd0;
	localparam			MAIN	=	3'd1;
	localparam			INIT	=	3'd2;
	localparam			SCAN	=	3'd3;
	localparam			WRITE	=	3'd4;
	localparam			DELAY	=	3'd5;
 
	localparam			LOW		=	1'b0;
	localparam			HIGH	=	1'b1;
 
	//assign	lcd_bl_out = HIGH;				// backlight active high level
 
	//wire		[15:0]	color_t	=	YELLOW;		//顶层色为黄色
	//wire		[15:0]	color_b	=	BLACK;		//背景色为黑色
 
	//reg			[7:0]	x_cnt;
	//reg			[7:0]	y_cnt;
 
	reg			[8:0]	data_reg;				//
	reg			[8:0]	reg_clear	[10:0];
	reg			[8:0]	reg_setxy	[30:0];
	reg			[8:0]	reg_init	[72:0];
	reg			[2:0]	cnt_main;
	reg			[2:0]	cnt_init;
	reg			[2:0]	cnt_scan;
	reg			[5:0]	cnt_write;
	reg			[15:0]	cnt_delay;
	reg			[15:0]	num_delay;
	reg			[15:0]	cnt;
	///reg			[15:0]	cnt_divide_4;
	//reg					high_word;
	reg			[2:0] 	state = IDLE;
	reg			[2:0] 	state_back = IDLE;
	reg			[7:0] 	cnt_clear;
	reg			[7:0] 	cnt_setxy;
	reg			[2:0] 	cnt_finnal_second;
	
//wire clk_in_1MHz;divide divide_1MHz (.clk(clk_in),.rst_n(rst_n_in),.clkout(clk_in_1MHz));

	always@(posedge clk_in or negedge rst_n_in) begin
		if(!rst_n_in) begin
			//x_cnt <= 8'd0;
			//y_cnt <= 8'd0;
			OLED_RES <= 0;
			cnt_main <= 3'd0;
			cnt_init <= 3'd0;
			cnt_scan <= 3'd0;
			cnt_write <= 6'd0;
			cnt_delay <= 16'd0;
			num_delay <= 16'd50;
			cnt <= 16'd0;
			cnt_finnal_second <= 3'd0;
			cnt_clear <= 8'd0;
			cnt_setxy <= 8'd0;
			//high_word <= 1'b1;
			state <= IDLE;
			state_back <= IDLE;
		end else begin
			case(state)
				IDLE:begin
						//x_cnt <= 8'd0;
						//y_cnt <= 8'd0;
						cnt_main <= 3'd0;
						cnt_init <= 3'd0;
						cnt_scan <= 3'd0;
						cnt_write <= 6'd0;
						cnt_delay <= 16'd0;
						num_delay <= 16'd50;
						cnt_finnal_second <= 3'd0;
						cnt_clear <= 8'd0;
						cnt_setxy <= 8'd0;
						//high_word <= 1'b1;
						state <= MAIN;
						state_back <= MAIN;
					end
				MAIN:begin
						case(cnt_main)	//MAIN状态
							3'd0:	begin state <= INIT; cnt_main <= cnt_main + 1'b1; end
							3'd1:	begin state <= SCAN; cnt_main <= cnt_main + 1'b1; end
							3'd2:	begin cnt_main <= 1'b1; end
							default: state <= IDLE;
						endcase
					end
				INIT:begin	//初始化状态
						case(cnt_init)
							3'd0:	begin OLED_RES <= 1'b0; cnt_init <= cnt_init + 1'b1; end	//复位有效
							3'd1:	begin num_delay <= 16'd120;state <= DELAY; state_back <= INIT; cnt_init <= cnt_init + 1'b1; end	//延时超过3微秒
							3'd2:	begin OLED_RES <= 1'b1; cnt_init <= cnt_init + 1'b1;end	//复位恢复
							3'd3:	begin num_delay <= 16'd120; state <= DELAY; state_back <= INIT; cnt_init <= cnt_init + 1'b1; end	//延时超过3微秒
							3'd4:	begin 
										if(cnt>=INIT_DEPTH) begin	//当73条指令及数据发出后,配置完成
											cnt <= 16'd0;
											cnt_init <= cnt_init + 1'b1;
										end else begin
											data_reg <= reg_init[cnt];	
											num_delay <= 16'd6;
											cnt <= cnt + 16'd1;
											state <= WRITE;
											state_back <= INIT;
											OLED_CS <= 1'b0;
										end
									end
							3'd5:	begin cnt_init <= 1'b0; state <= MAIN; end	//初始化完成,返回MAIN状态
							default: state <= IDLE;
						endcase
					end
				SCAN:begin	//清屏状态和写数据状态
						case(cnt_scan)
							3'd0:	begin //清空屏幕
										if(cnt == 16'd32) begin
											cnt_scan <= cnt_scan + 1'b1;
											cnt <= 16'd0;
											cnt_finnal_second <= 3'd0;
										end else begin
											case(cnt_finnal_second[1:0])
											2'd0:begin
												data_reg <= reg_clear[cnt_finnal_second[1:0]] + cnt / 4;//写页地址
												cnt <= cnt + 16'd1;
												cnt_finnal_second <= cnt_finnal_second + 3'd1;
												num_delay <= 16'd6;
												state <= WRITE;
												state_back <= SCAN;
												OLED_CS <= 1'b0;
											end
											2'd1,2'd2:begin
												data_reg <= reg_clear[cnt_finnal_second[1:0]];//写列起始地址,低和高位
												cnt <= cnt + 16'd1;
												cnt_finnal_second <= cnt_finnal_second + 3'd1;
												num_delay <= 16'd6;
												state <= WRITE;
												state_back <= SCAN;
												OLED_CS <= 1'b0;
											
											end
											2'd3:begin//自动循环128次
												if(cnt_clear == 8'd127)begin
													cnt_clear <= 8'd0;
													cnt <= cnt + 16'd1;
													cnt_finnal_second <= cnt_finnal_second + 3'd1;
												end else begin
													cnt_clear <= cnt_clear + 8'd1;
												end
													data_reg <= reg_clear[cnt_finnal_second[1:0]];//填0,填128次
													num_delay <= 16'd6;
													state <= WRITE;
													state_back <= SCAN;
													OLED_CS <= 1'b0;
											end
											endcase
										end
									end
							3'd1:	begin //负责写数据
											case(cnt_finnal_second)
												3'd0:begin
													data_reg <= reg_setxy[0];//写页地址
													cnt_finnal_second <= cnt_finnal_second + 3'd1;
													num_delay <= 16'd6;
													state <= WRITE;
													state_back <= SCAN;
													OLED_CS <= 1'b0;
												end
												3'd1:begin
													data_reg <= reg_setxy[1];//写列起始地址,低位
													
													cnt_finnal_second <= cnt_finnal_second + 3'd1;
													num_delay <= 16'd6;
													state <= WRITE;
													state_back <= SCAN;
													OLED_CS <= 1'b0;
												
												end
												3'd2:begin
													data_reg <= reg_setxy[2];//写列起始地址,高位
													
													cnt_finnal_second <= cnt_finnal_second + 3'd1;
													num_delay <= 16'd6;
													state <= WRITE;
													state_back <= SCAN;
													OLED_CS <= 1'b0;
												
												end
												3'd3:begin
													if(cnt == 8'd7)begin
													cnt_finnal_second <= cnt_finnal_second + 3'd1;
													end else begin
														cnt <= cnt + 16'd1;
													end
														data_reg <= reg_setxy[cnt + 3];//写需要显示数据的第一页中的数据
														num_delay <= 16'd6;
														state <= WRITE;
														state_back <= SCAN;
														OLED_CS <= 1'b0;
												end
												3'd4:begin
													data_reg <= reg_setxy[cnt + 4];//写页地址
													cnt_finnal_second <= cnt_finnal_second + 3'd1;
													num_delay <= 16'd6;
													state <= WRITE;
													state_back <= SCAN;
													OLED_CS <= 1'b0;
												end
												3'd5:begin
													data_reg <= reg_setxy[cnt + 5];//写列起始地址,低位
													
													cnt_finnal_second <= cnt_finnal_second + 3'd1;
													num_delay <= 16'd6;
													state <= WRITE;
													state_back <= SCAN;
													OLED_CS <= 1'b0;
												
												end
												3'd6:begin
													data_reg <= reg_setxy[cnt + 6];//写列起始地址,高位
													
													cnt_finnal_second <= cnt_finnal_second + 3'd1;
													num_delay <= 16'd6;
													state <= WRITE;
													state_back <= SCAN;
													OLED_CS <= 1'b0;
												
												end
												3'd7:begin
													if(cnt == 8'd16)begin
														cnt <= 16'd0;
														cnt_scan <= cnt_scan + 1'b1;
														cnt_finnal_second <= 3'd0;
													end else begin
														cnt <= cnt + 16'd1;
													end
														data_reg <= reg_setxy[cnt + 7];//写需要显示数据的第二页中的数据
														num_delay <= 16'd6;
														state <= WRITE;
														state_back <= SCAN;
														OLED_CS <= 1'b0;
												end
												endcase
										
									end
							3'd2:	begin cnt_scan <= 1'b0; state <= MAIN; end
							default: state <= IDLE;
						endcase
					end
				WRITE:begin	//WRITE状态,将数据按照SPI时序发送给屏幕
						if(cnt_write >= 6'd18) cnt_write <= 1'b0;
						else cnt_write <= cnt_write + 1'b1;
						case(cnt_write)//**可以考虑10位数据,第10位为CS片选位**
							6'd0:	begin OLED_DC <= data_reg[8];end	//9位数据最高位为命令数据控制位
							6'd1:	begin OLED_SCK <= LOW; OLED_MOSI <= data_reg[7];end	//先发高位数据
							6'd2:	begin OLED_SCK <= HIGH;end
							6'd3:	begin OLED_SCK <= LOW; OLED_MOSI <= data_reg[6];end
							6'd4:	begin OLED_SCK <= HIGH;end
							6'd5:	begin OLED_SCK <= LOW; OLED_MOSI <= data_reg[5];end
							6'd6:	begin OLED_SCK <= HIGH;end
							6'd7:	begin OLED_SCK <= LOW; OLED_MOSI <= data_reg[4];end
							6'd8:	begin OLED_SCK <= HIGH; end
							6'd9:	begin OLED_SCK <= LOW; OLED_MOSI <= data_reg[3];end
							6'd10:	begin OLED_SCK <= HIGH;end
							6'd11:	begin OLED_SCK <= LOW; OLED_MOSI <= data_reg[2];end
							6'd12:	begin OLED_SCK <= HIGH;end
							6'd13:	begin OLED_SCK <= LOW; OLED_MOSI <= data_reg[1];end
							6'd14:	begin OLED_SCK <= HIGH; end
							6'd15:	begin OLED_SCK <= LOW; OLED_MOSI <= data_reg[0];end	//后发低位数据
							6'd16:	begin OLED_SCK <= HIGH;end
							6'd17:	begin OLED_CS <= 1; OLED_DC <=1;end 								
							6'd18:	begin 
								//OLED_SCK <= LOW; 
								state <= DELAY;end	
							default: state <= IDLE;
						endcase
					end
				DELAY:begin	//延时状态
						if(cnt_delay >= num_delay) begin
							cnt_delay <= 16'd0;
							state <= state_back; 
						end else cnt_delay <= cnt_delay + 1'b1;
					end
				default:state <= IDLE;
			endcase
		end
	end
 
	// data for scan_clear
	initial	//设定显示区域指令及数据
		begin
			reg_clear[0]	=	{1'b0,8'hb0};
			reg_clear[1]	=	{1'b0,8'h00};
			reg_clear[2]	=	{1'b0,8'h10};
			reg_clear[3]	=	{1'b1,8'h00};
		end
 
	// data for scan_char
	initial	//设定显示区域指令及数据
		begin
			reg_setxy[0]	=	{1'b0,8'hb1};
			reg_setxy[1]	=	{1'b0,8'h01};
			reg_setxy[2]	=	{1'b0,8'h10};
			
			reg_setxy[11]	=	{1'b0,8'hb2};
			reg_setxy[12]	=	{1'b0,8'h01};
			reg_setxy[13]	=	{1'b0,8'h10};
			//上面的指令代表对第1页的第4行开始写数据
			
			//以下数据取自0.96寸的OLEDC51的SPI的程序的字符库中,字体未知,大小相同都是8*16
			/*
			//0x00,0x00,0xC0,0x38,0xE0,0x00,0x00,0x00, 0x20,0x3C,0x23,0x02,0x02,0x27,0x38,0x20
			//这段数据代表A,显示的大小为8*16(长*宽)
			reg_setxy[3]	=	{1'b1,8'h00};
			reg_setxy[4]	=	{1'b1,8'h00};
			reg_setxy[5]	=	{1'b1,8'hC0};
			reg_setxy[6]	=	{1'b1,8'h38};
			reg_setxy[7]	=	{1'b1,8'hE0};
			reg_setxy[8]	=	{1'b1,8'h00};
			reg_setxy[9]	=	{1'b1,8'h00};
			reg_setxy[10]	=	{1'b1,8'h00};
			
			reg_setxy[14]	=	{1'b1,8'h20};
			reg_setxy[15]	=	{1'b1,8'h3C};
			reg_setxy[16]	=	{1'b1,8'h23};
			reg_setxy[17]	=	{1'b1,8'h02};
			reg_setxy[18]	=	{1'b1,8'h02};
			reg_setxy[19]	=	{1'b1,8'h27};
			reg_setxy[20]	=	{1'b1,8'h38};
			reg_setxy[21]	=	{1'b1,8'h20};
			*/
			
			/*
			//0xE0,0x10,0x08,0x08,0x08,0x10,0xE0,0x00, 0x0F,0x10,0x20,0x20,0x20,0x10,0x0F,0x00
			//这段数据代表O,显示的大小为8*16(长*宽)
			reg_setxy[3]	=	{1'b1,8'hE0};
			reg_setxy[4]	=	{1'b1,8'h10};
			reg_setxy[5]	=	{1'b1,8'h08};
			reg_setxy[6]	=	{1'b1,8'h08};
			reg_setxy[7]	=	{1'b1,8'h08};
			reg_setxy[8]	=	{1'b1,8'h10};
			reg_setxy[9]	=	{1'b1,8'hE0};
			reg_setxy[10]	=	{1'b1,8'h00};
			
			reg_setxy[14]	=	{1'b1,8'h0F};
			reg_setxy[15]	=	{1'b1,8'h10};			
			reg_setxy[16]	=	{1'b1,8'h20};
			reg_setxy[17]	=	{1'b1,8'h20};
			reg_setxy[18]	=	{1'b1,8'h20};
			reg_setxy[19]	=	{1'b1,8'h10};
			reg_setxy[20]	=	{1'b1,8'h0F};
			reg_setxy[21]	=	{1'b1,8'h00};
		*/
			/*
			//0x00,0x00,0x80,0x80,0x80,0x80,0x00,0x00, 0x00,0x19,0x24,0x22,0x22,0x22,0x3F,0x20,
			//这段数据代表a,显示的大小为8*16(长*宽)
			reg_setxy[3]	=	{1'b1,8'h00};
			reg_setxy[4]	=	{1'b1,8'h00};
			reg_setxy[5]	=	{1'b1,8'h80};
			reg_setxy[6]	=	{1'b1,8'h80};
			reg_setxy[7]	=	{1'b1,8'h80};
			reg_setxy[8]	=	{1'b1,8'h80};			
			reg_setxy[9]	=	{1'b1,8'h00};
			reg_setxy[10]	=	{1'b1,8'h00};
			
			reg_setxy[14]	=	{1'b1,8'h00};			
			reg_setxy[15]	=	{1'b1,8'h19};
			reg_setxy[16]	=	{1'b1,8'h24};
			reg_setxy[17]	=	{1'b1,8'h22};
			reg_setxy[18]	=	{1'b1,8'h22};
			reg_setxy[19]	=	{1'b1,8'h22};
			reg_setxy[20]	=	{1'b1,8'h3F};
			reg_setxy[21]	=	{1'b1,8'h20};
			*/
			
			/*
			//0xC0,0x30,0xC8,0x28,0xE8,0x10,0xE0,0x00, 0x07,0x18,0x27,0x24,0x23,0x14,0x0B,0x00
			//这段数据代表O,显示的大小为8*16(长*宽)
			reg_setxy[3]	=	{1'b1,8'hC0};
			reg_setxy[4]	=	{1'b1,8'h30};
			reg_setxy[5]	=	{1'b1,8'hC8};
			reg_setxy[6]	=	{1'b1,8'h28};
			reg_setxy[7]	=	{1'b1,8'hE8};
			reg_setxy[8]	=	{1'b1,8'h10};			
			reg_setxy[9]	=	{1'b1,8'hE0};
			reg_setxy[10]	=	{1'b1,8'h00};
			
			reg_setxy[14]	=	{1'b1,8'h07};			
			reg_setxy[15]	=	{1'b1,8'h18};
			reg_setxy[16]	=	{1'b1,8'h27};
			reg_setxy[17]	=	{1'b1,8'h24};
			reg_setxy[18]	=	{1'b1,8'h23};
			reg_setxy[19]	=	{1'b1,8'h14};
			reg_setxy[20]	=	{1'b1,8'h0B};
			reg_setxy[21]	=	{1'b1,8'h00};
			*/
			
			//以下程序是取用PCtoLCD2002完美版,字体为宋体,大小为8*16(长*宽),其中第一个DB不用放在数组中,根据长度划分组,这是以8(长度)为1组
			
			//DB 00H 00H 80H 80H 80H 00H 00H 00H  00H 19H 24H 24H 12H 3FH 20H 00H;"a",0
			reg_setxy[3]	=	{1'b1,8'h00};
			reg_setxy[4]	=	{1'b1,8'h00};
			reg_setxy[5]	=	{1'b1,8'h80};
			reg_setxy[6]	=	{1'b1,8'h80};
			reg_setxy[7]	=	{1'b1,8'h80};
			reg_setxy[8]	=	{1'b1,8'h00};			
			reg_setxy[9]	=	{1'b1,8'h00};
			reg_setxy[10]	=	{1'b1,8'h00};
			
			reg_setxy[14]	=	{1'b1,8'h00};
			reg_setxy[15]	=	{1'b1,8'h19};
			reg_setxy[16]	=	{1'b1,8'h24};
			reg_setxy[17]	=	{1'b1,8'h24};
			reg_setxy[18]	=	{1'b1,8'h12};
			reg_setxy[19]	=	{1'b1,8'h3F};
			reg_setxy[20]	=	{1'b1,8'h20};
			reg_setxy[21]	=	{1'b1,8'h00};
			
			
			//以下程序是取用PCtoLCD2002完美版,字体为宋体,大小为12*32(长*宽),其中第一个DB不用放在数组中
			
			//DB 00H 00H 00H 00H 00H C0H C0H 00H 00H 00H 00H 00H 00H 00H 00H C0H;
			//DB 3EH 01H 1FH F8H 00H 00H 00H 00H 00H 00H F0H 0FH 08H 08H 08H 0FH;
			//DB FFH C0H 00H 00H 08H 0EH 0DH 08H 00H 00H 00H 08H 09H 0FH 0CH 08H;"A",0

		end
			
	// data for init
	initial	//OLED初始化的命令及数据
		begin
			reg_init[0]		=	{1'b0,8'hAE}; // 关OLED显示
			reg_init[1]		=	{1'b0,8'h00}; // 正常显示(1亮0灭)
			reg_init[2]		=	{1'b0,8'h10}; // 启用电荷泵
			reg_init[3]		=	{1'b0,8'h40}; // 启用电荷泵
			reg_init[4]		=	{1'b0,8'h81}; // 开OLED显示
			reg_init[5]		=	{1'b0,8'hFF};//全部显示
			reg_init[6]		=	{1'b0,8'hA1};//
			reg_init[7]		=	{1'b0,8'hC8};
			reg_init[8]		=	{1'b0,8'hA6};
			reg_init[9]		=	{1'b0,8'hA8};
			reg_init[10]		=	{1'b0,8'h1f};//OLED的尺寸为0.91时
		//	reg_init[10]		=	{1'b0,8'h3f};//OLED的尺寸为0.96时
			reg_init[11]		=	{1'b0,8'hD3};
			reg_init[12]		=	{1'b0,8'h00};
			reg_init[13]		=	{1'b0,8'hd5};
			reg_init[14]		=	{1'b0,8'h80};
			reg_init[15]		=	{1'b0,8'hD9};
			reg_init[16]		=	{1'b0,8'hF1};
			reg_init[17]		=	{1'b0,8'hDA};
			reg_init[18]		=	{1'b0,8'h02};//OLED的尺寸为0.91时
		//	reg_init[18]		=	{1'b0,8'h12};//OLED的尺寸为0.96时
			reg_init[19]		=	{1'b0,8'hDB};
			reg_init[20]		=	{1'b0,8'h40};
			reg_init[21]		=	{1'b0,8'h20};
			reg_init[22]		=	{1'b0,8'h02};
			reg_init[23]		=	{1'b0,8'h8D};
			reg_init[24]		=	{1'b0,8'h14};
			reg_init[25]		=	{1'b0,8'hA4};
			reg_init[26]		=	{1'b0,8'hA6};//设置屏幕正常显示,即1为点亮
			reg_init[27]		=	{1'b0,8'hAF};
		//	reg_init[28]		=	{1'b0,8'hA7};//设置屏幕反向显示
		end	
endmodule

这段代码存在一个问题,它会让FPGA一直刷新我们填写的数据,所以屏幕就会一直抖动显得很不稳定。在一开始的时候,因为没有调试过OLED,所以认为是正常现象,之后观察了一个同期的电子工程师的屏幕发现屏幕抖动是我自己程序编写的问题。解决办法就是只刷新一次,当然这次刷新最好是全屏的刷新,如果不是全屏的刷新会导致某一页有些部分会显示小点点。

说明一下,我怎么使用的取模软件吧

我使用的取模软件是PCtoLCD2002。

首先我们需要知道在程序中是1 亮还是 0 亮,观察初始化中的命令发现有两个A6,说明设置的是屏幕正常显示,如果我们想要点亮对应的点就应该输入1。之后在观察到我们设定的是页模式,所以数据是从我们设定的列起始地址最上面的灯开始,到这一列的最后一个灯,之后列地址加一到后面1列的最上面的灯,循环往复。因此取模软件中,我们也应该这么设定。

标题

 

选择如下图的设定,主要关注画圈的地方。最终设置完之后的结果可以观察右下角的取模演示,其数据输入的结果应该和我们设定的页地址模式一样。之后就是自行取模了。

标题

 

 

 

 

 

  • 3
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值