【FPGA学习笔记】第十六章:实验5——SPI实战:NOKIA5110液晶

本文详细介绍了如何使用Nokia5110液晶模块显示中文,包括内部寄存器设计、状态机操作、字符库构建等关键步骤。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Nokia5110液晶,显示中文。

字模需要自己制作,单位字体像素:16×16


/*************************************************
//Module:       nokia5110lcd_chinese
//File Name:    nokia5110lcd_chinese.v
//Version:      2.0
//Date:         2011.11.30
//Author:       wang li
//Code Type:    RTL
//Description:  诺基亚5110液晶中文显示,每行显示5个字,显3行。
//              clk——时钟输入(1位)
//              sel——数码管位选信号输出,不同的数位(8位)
//              data——数码管某一位上的数字输出(8位)
**************************************************/
module nokia5110lcd_chinese(sys_clk,reset,lcd_rst,sce,sclk,sdin,cd);
input sys_clk;		//系统时钟
input reset;		//时钟复位,为0进行复位,为1正常运行
output lcd_rst;		//输出信号到51i10的复位引脚,低电平进行复位
output sclk;		//输出信号到5110的时钟信号引脚
output sce;			//输出信号到5110的片选信号引脚,低电平芯片选通
output sdin;
output cd;

//-----内部寄存器-----
reg [7:0] cnt;		//分频计数器		reg [6:0] cnt;
reg clk;			//分频时钟
reg sce;			//5110片选信号,低电平选通芯片
reg cd;			//数据命令切换脚,1为数据,0为命令
reg sdin;			//串行数据输入

reg [1:0] p;		//状态机1
reg [3:0] p2;		//状态机2
reg [3:0] p_back;	//状态返回

parameter clk_l=2'd0;
parameter clk_h=2'd1;
parameter clk_rising_edge=2'd2;
parameter clk_falling_edge=2'd3;

parameter idle=4'd0;		//状态机2的初始状态
parameter shift_data=4'd1;
parameter shift_data1=4'd2;
parameter clear_screen=43'd3;
parameter set_xy=4'd4;
parameter disp_char=4'd5;
parameter set_xy2=4'd6;
parameter disp_char2=4'd7;
parameter set_xy3=4'd8;

reg [7:0] data_reg;			//数据寄存器
reg [3:0] cnt2;				//串行传输data_reg时的移位计数器
reg [15:0] cnt3;			//状态机2的运行步骤计数器,25步reg [4:0] cnt3;
reg [8:0] cnt4;				//显示位置计数器
reg [6:0] char_reg;			//字符寄存器

reg [2:0] y_reg;		//y坐标寄存器,Y:0~5(3’b101)
reg [6:0] x_reg;		//x坐标寄存器,X:0~83(7'b1010011)

reg [0:255] men[5:0];		//字符库,为menory变量,第一个[每个存储单元深度],第二个[存储器的深度]
reg [0:255] temp;				//字符暂存寄存器

parameter ONN=1'b0;
parameter OFF=1'b1;
parameter CMD=1'b0;
parameter DATA=1'b1;

//-----字符库,每个字符占8行6列-----
initial
  begin
  //空白
  men[0] =	{8'h00,8'h00,8'h00,8'h00,8'h00,8'h00,8'h00,8'h00,
	 					 8'h00,8'h00,8'h00,8'h00,8'h00,8'h00,8'h00,8'h00,
						 8'h00,8'h00,8'h00,8'h00,8'h00,8'h00,8'h00,8'h00,
						 8'h00,8'h00,8'h00,8'h00,8'h00,8'h00,8'h00,8'h00};
						 
/*--  文字:  力反    宋体12--现调整为:宽度x高度=16x16  */
  men[1] =	{8'hFF,8'hEF,8'hEF,8'hEF,8'hEF,8'hEF,8'h00,8'hEF,
  					 8'hEF,8'hEF,8'hEF,8'hEF,8'h0F,8'hFF,8'hFF,8'hFF,
						 8'hFF,8'h7F,8'hBF,8'hDF,8'hE7,8'hF9,8'hFE,8'hFF,
						 8'hDF,8'hBF,8'h7F,8'hBF,8'hC0,8'hFF,8'hFF,8'hFF};
						 
/*--  文字:  虎    宋体12--现调整为:宽度x高度=16x16  */
  men[2] =	{8'h00,8'h00,8'hF8,8'h08,8'h48,8'h48,8'h48,8'hFF,
  					 8'h4A,8'h2A,8'h2A,8'h0A,8'hCA,8'h18,8'h00,8'h00,
						 8'h80,8'h60,8'h1F,8'h80,8'h40,8'h20,8'h1C,8'h04,
						 8'h05,8'h05,8'h7D,8'h81,8'h81,8'hE0,8'h00,8'h00};

  men[3] =	{8'hFF,8'hFF,8'hFF,8'hFF,8'hFF,8'hFF,8'hFF,8'hFF,
  					 8'hFF,8'hFF,8'hFF,8'hFF,8'hFF,8'hFF,8'hFF,8'hFF,
  					 8'hFF,8'hFF,8'hFF,8'hFF,8'hFF,8'hFF,8'hFF,8'hFF,
  					 8'hFF,8'hFF,8'hFF,8'hFF,8'hFF,8'hFF,8'hFF,8'hFF};
  					 
/*--  文字:  雄    楷体12--现调整为:宽度x高度=16x16  -*/
  men[4] =	{8'h00,8'h40,8'h60,8'hE0,8'hFC,8'hBC,8'hA0,8'hE0,
  					 8'hF8,8'hFC,8'h4C,8'hFC,8'hF8,8'hA0,8'h20,8'h00,
						 8'h10,8'h1C,8'h0F,8'h0F,8'h0F,8'h0F,8'h0F,8'h2C,
						 8'h7F,8'h7F,8'h15,8'h1F,8'h1F,8'h1A,8'h1A,8'h00};	
						 
/*--  文字:  起    楷体12--现调整为:宽度x高度=16x16  -*/
  men[5] =	{8'h00,8'h00,8'h00,8'h40,8'hFC,8'hFC,8'hFC,8'hA0,
  					 8'hA0,8'hB0,8'hF0,8'hF0,8'hF0,8'h10,8'h00,8'h00,
						 8'h21,8'h31,8'h3F,8'h0F,8'h0F,8'h1F,8'h1F,8'h34,
						 8'h37,8'h2F,8'h6C,8'h6C,8'h66,8'h66,8'h24,8'h20};						 
	
//	men[0] = {8'h00, 8'h42, 8'h42, 8'h42, 8'h42, 8'hFE};   // 0  sp
//	men[1] = {8'h42,8'h42,8'h42,8'h42,8'h00,8'h00};   // 1  !
//	men[2] = {8'h08,8'h08,8'h08,8'h08,8'h08,8'h0F};   // 3  #
//  men[3] = {8'h08,8'h08,8'h08,8'h08,8'h08,8'h00};   // 4  $
  end 
  
//-----
assign sclk=clk;		//slck:5110的同步时针输入=分频时钟
assign lcd_rst=1;		//lcd_rst:5110的复位信号输出,低电平进行复位
  
//-----时钟分频-----
always @(posedge sys_clk)
begin
	if(!reset)	cnt<=0;		//rest=0,进行时钟复位
	else
		begin
			cnt<=cnt+1;
			if(cnt==49)	cnt<=0;		//50*20ns=1000ns=1us,产生频率为1Mhz的时钟
			
			if(cnt<25)	clk<=0;		//占空比调整50%
			else	clk<=1;
		end
end
  
//-----切换状态机1:模拟clk的状态-----
always @(posedge sys_clk)
begin
	if(!reset)
		p<=clk_l;		//状态机p1=clk_l=2'd0=0
	else
		case(p)
			clk_l:																	
				begin
					if(clk)	p<=clk_rising_edge;			//clk为高电平,p变成上升沿
					else p<=clk_l;					//clk为低电平,p变成低电平  					
				end
			clk_rising_edge:p<=clk_h;				//p在上升沿后变成高电平
			clk_h:
				begin
					if(!clk)	p<=clk_falling_edge;	//clk为低电平,p变成下降沿
					else p<=clk_h;					//clk为高电平,p变成高电平
				end
			clk_falling_edge:p<=clk_l;				//p在下降沿后变成低电平
			default;
		endcase;
end
  
  //-----切换状态机2-----
  always @(posedge sys_clk)
  begin
  	if(!reset)
  		begin
  			p2<=idle;		//idle=3'd0;
  			sce<=OFF;		//OFF=1'b1;  sce:片选信号,低电平,该芯片工作
  			cnt3<=0;		//cnt3:16位的状态计数器
  		end
  	else
  		case(p2)
  //-----------------------------------------------
  			idle:		//p2为idle状态
  				begin
  					sce<=OFF;
  					cnt3<=cnt3+1;
  					case(cnt3)
//-----data_reg:8位数据寄存器---cd:数据命令切换脚---状态机2的状态------状态返回
  							 0: begin  data_reg<=8'h21;	//8'b0010_0001 表示命令集的功能设置,
  															//第2位PD=0,芯片是活动的;PD=1芯片处于掉电模式;
  															//第1位V=0,水平寻址;V=1垂直寻址模式
  															//第0位H=1,使用扩展指令集	;H=0使用基本指令集	
  												cd<=CMD;
  												p2<=shift_data;		
  												p_back<=idle; end  		
						      1: begin	data_reg<=8'hc8;		//8'b1100_1000设置液晶偏置电压V_op寄存器
						      					//V_op6=1  V_op5=0  V_op4=0  V_op3=1  V_op2=0  V_op1=0  V_op0=0	
						      					cd<=CMD;	
						      					p2<=shift_data;		
						      					p_back<=idle; end
						      2: begin	data_reg<=8'h06;		//8'b0000_0110温度控制,温度校正
						      			//第1位TC_1=1
						      			//第0位TC_0=0    整个表示选用温度系数2	
						      			cd<=CMD;							
						      			p2<=shift_data;		
						      			p_back<=idle; end
						      3: begin 	data_reg<=8'h13;		//8‘b0001_0011设置偏置系统
						      			//第2为BS_2=0	第1位BS_1=1	第0位BD_0=1;
						      			cd<=CMD;							
						      			p2<=shift_data;		
						      			p_back<=idle; end
						      4: begin 	data_reg<=8'h20;		//8'b0010_0000表示命令集的功能设置
						      							//第2位PD=0,芯片是活动的;PD=1芯片处于掉电模式;
  															//第1位V=0,水平寻址;V=1垂直寻址模式
  															//第0位H=0,使用扩展指令集	;H=0使用基本指令集			
						      			cd<=CMD;							
						      			p2<=shift_data;		
						      			p_back<=idle; end
						      					
						      5: begin 	p2<=clear_screen;		//p2状态由shift_data换成clear_screen
						      			p_back<=clear_screen; end
						      					
						      6: begin	data_reg<=8'h0c;		//8'b0000_1100设定显示模式,正常显示
						      							//第2位D=1,
  															//第0位E=0,	DE=01表示普通模式
						      			cd<=CMD;
						      			p2<=shift_data;
						      			p_back<=idle; end
						//-----设置显示坐标-----
						      7: begin 	p2<=set_xy;		//p2状态变成set_xy						  
						      					p_back<=set_xy;		//返回状态也变成set_xy
						      					y_reg<=0; 				//y坐标为0(范围为0-5)
						      					x_reg<=0; 				//x坐标为2(范围为0-83)
						      					end
						//-----显示字符-----
						      8: begin	p2<=disp_char;			//p2状态变成显示字符
						      			p_back<=disp_char;
						      			char_reg<=4;						//雄
						      			end
						      9: begin	p2<=disp_char;			//p2状态变成显示字符
						      			p_back<=disp_char;
						      			char_reg<=0;						//空白
						      			end
						      10: begin	p2<=disp_char;			//p2状态变成显示字符
						      			p_back<=disp_char;
						      			char_reg<=3;						//黑块
						      			end
						      11: begin	p2<=disp_char;			//p2状态变成显示字符
						      			p_back<=disp_char;
						      			char_reg<=1;						//力
						      			end
						
						//-----设置显示坐标-----
						      12: begin 	p2<=set_xy;		//p2状态变成set_xy						  
						      						p_back<=set_xy;		//返回状态也变成set_xy
						      						y_reg<=2; 				//y坐标为0(范围为0-5)
						      						x_reg<=0; 				//x坐标为2(范围为0-83)
						      				end
						      13: begin	p2<=disp_char;			//p2状态变成显示字符
						      			p_back<=disp_char;
						      			char_reg<=1;						//力
						      			end
						      14: begin	p2<=disp_char;			//p2状态变成显示字符
						      			p_back<=disp_char;
						      			char_reg<=4;						//雄
						      			end
						      15: begin	p2<=disp_char;			//p2状态变成显示字符
						      			p_back<=disp_char;
						      			char_reg<=0;						//空格
						      			end
						      16: begin	p2<=disp_char;			//p2状态变成显示字符
						      			p_back<=disp_char;
						      			char_reg<=5;						//起
						      			end
						      17: begin	p2<=disp_char;			//p2状态变成显示字符
						      			p_back<=disp_char;
						      			char_reg<=4;						//雄
						      			end
							 						      			
						//-----设置显示坐标-----
						      18: begin 	p2<=set_xy;		//p2状态变成set_xy						  
						      						p_back<=set_xy;		//返回状态也变成set_xy
						      						y_reg<=4; 				//y坐标为0(范围为0-5)
						      						x_reg<=32; 				//x坐标为2(范围为0-83)
						      					end     
						      19: begin	p2<=disp_char;			//p2状态变成显示字符
						      			p_back<=disp_char;
						      			char_reg<=4;						//雄
						      			end			 
						      	      						      						      							      									      			
						      20: begin cnt3<=20; //执行完后,在这进行停止
						      			end
						      default;
  					endcase;
  				end
 //------------------------------------------------
  			shift_data:		//p2为传输数据状态
  				begin
  					if(p==clk_falling_edge)		//在下降沿进行数据传输
  						begin
  							if(cnt2==8)
  								begin
  									cnt2<=0;
  									p2<=p_back;
  								end
  							else
  								begin
  									p2<=shift_data1;
  									sce<=ONN;
  									sdin<=data_reg[7];	//将数据寄存器的最高位发送
  								end
  						end
  					else
  						p2<=shift_data;
  				end
  //-----------------------------------------------
  			shift_data1:
					begin
						if(p==clk_rising_edge)		//上升沿数据进行移位
							begin
								data_reg<={data_reg[6:0],data_reg[7]};	//进行数据高位移位
								cnt2<=cnt2+1;
								p2<=shift_data;
							end
						else p2<=shift_data1;
					end
  //-------------------------------------------------
  			clear_screen:		//清屏
  				begin
					data_reg<=8'h00;
					cnt4<=cnt4+1;
					sce<=OFF;
					cd<=DATA;
					if(cnt4==504)		//6行*84列=504
						begin
							cnt4<=0;
							p2<=idle;
						end
					else
						p2<=shift_data;
  				end
  //-------------------------------------------------
  			set_xy:				//设置X,y方向坐标
  				begin
					cnt4<=cnt4+1;
					sce<=OFF;
					cd<=CMD;
					case(cnt4)
						0:begin	data_reg<=(8'b0100_0000 | y_reg);
								p2<=shift_data;	end
						1:begin	data_reg<=(8'b1000_0000 | x_reg);
								p2<=shift_data;	end
						2:begin	p2<=idle;
								cnt4<=0;	end
						default;  								
					endcase;
  				end
  				
  //-------------------------------------------------
  			disp_char:		//字符从字符库读入
  				begin
						cnt4<=cnt4+1;
						sce<=OFF;
						cd<=DATA;
						if(cnt4==16)		//需要修改
							begin
								p2<=set_xy2;
								p_back<=set_xy2;			//返回状态也变成set_xy
								y_reg<=y_reg+1;
							end
						else
							begin
								temp=men[char_reg];
								case(cnt4)
									0 :data_reg<=temp[0:7];
									1 :data_reg<=temp[8:15];
									2 :data_reg<=temp[16:23];
									3 :data_reg<=temp[24:31];
									4 :data_reg<=temp[32:39];
									5 :data_reg<=temp[40:47];
									6 :data_reg<=temp[48:55];
									7 :data_reg<=temp[56:63];
									8 :data_reg<=temp[64:71];
									9 :data_reg<=temp[72:79];
									10:data_reg<=temp[80:87];
									11:data_reg<=temp[88:95];
									12:data_reg<=temp[96:103];
									13:data_reg<=temp[104:111];
									14:data_reg<=temp[112:119];
									15:data_reg<=temp[120:127];
									default;
								endcase;
								p2<=shift_data;
							end
  				end	
    //-------------------------------------------------
  			set_xy2:				//设置X,y方向坐标
  				begin
						cnt4<=cnt4+1;
						sce<=OFF;
						cd<=CMD;
						case(cnt4)
							17:begin	data_reg<=(8'b0100_0000 | y_reg);
									p2<=shift_data;	end
							18:begin	data_reg<=(8'b1000_0000 | x_reg);
									p2<=shift_data;	end
							19:begin	p2<=disp_char2;
									p_back<=disp_char2;
									cnt4<=0;	end
							default;  								
						endcase;
  				end		
  //--------------------------------------------------	
			  disp_char2:		//字符从字符库读入
						begin
			  				cnt4<=cnt4+1;
			  				sce<=OFF;
			  				cd<=DATA;
			  				if(cnt4==16)
			  						begin
			  							p2<=set_xy3;
  										p_back<=set_xy3;			//返回状态也变成set_xy
  										y_reg<=y_reg-1;
  										x_reg<=x_reg+16;
  										cnt4<=0;
			  						end
			  				else
			  						begin
			  							case(cnt4)
			  								0: data_reg<=temp[128:135];
			  								1: data_reg<=temp[136:143];
			  								2: data_reg<=temp[144:151];
			  								3: data_reg<=temp[152:159];
			  								4: data_reg<=temp[160:167];
			  								5: data_reg<=temp[168:175];
			  								6: data_reg<=temp[176:183];
			  								7: data_reg<=temp[184:191];
			  								8: data_reg<=temp[192:199];
			  								9: data_reg<=temp[200:207];
			  								10:data_reg<=temp[208:215];
			  								11:data_reg<=temp[216:223];
			  								12:data_reg<=temp[224:231];
			  								13:data_reg<=temp[232:239];
			  								14:data_reg<=temp[240:247];
			  								15:data_reg<=temp[248:255];
			  								default;
			  							endcase;
			  							p2<=shift_data;
			  						end
			  				end	
	
	//-------------------------------------------------
			set_xy3:				//设置X,y方向坐标
				begin
					cnt4<=cnt4+1;
					sce<=OFF;
					cd<=CMD;
					case(cnt4)
						0:begin	data_reg<=(8'b0100_0000 | y_reg);
								p2<=shift_data;	end
						1:begin	data_reg<=(8'b1000_0000 | x_reg);
								p2<=shift_data;	end
						2:begin	p2<=idle;
								p_back<=idle;
								cnt4<=0;	end
						default;  								
					endcase;
				end			
	//-----------------------  		
  		default;				//状态已完		
  		endcase;
  end
  
  
  endmodule
  
  



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值