FPGA实现0.96 oled驱动

module OledDriver 
( 
input [27:0]data,
input clk_in,  //clk_in = 25mhz
input rst_n_in,  //rst_n_in, active low
output reg run_flag,
output reg oled_rst_n_out,  //nokia5110 reset, active low
output reg oled_cs_n_out,  //nokia5110 chip select, active low
output reg oled_dc_out,  //nokia5110 data or command control
output oled_clk_out,  //nokia5110 clock
output reg oled_data_out  //nokia5110 data
);

parameter CLK_DIV_PERIOD=20; //related with clk_div's frequency
parameter DELAY_PERIOD=25000;  //related with delay time and refresh frequency

parameter CLK_L=2'd0;
parameter CLK_H=2'd1;
parameter CLK_RISING_DEGE=2'd2;
parameter CLK_FALLING_DEGE=2'd3;

parameter IDLE=3'd0;
parameter SHIFT=3'd1;
parameter CLEAR=3'd2;
parameter SETXY=3'd3;
parameter DISPLAY=3'd4;
parameter DELAY=3'd5;

parameter LOW =1'b0;
parameter HIGH =1'b1;
parameter CMD =1'b0;
parameter DATA =1'b1;

//assign oled_rst_n_out = 1;  //active low level, set 1 for normal
//assign oled_dc_out = 1;


//initial for memory register
reg [47:0] cmd_r [9:0];
initial
	begin
		cmd_r[0]= {8'hae, 8'h00, 8'h10, 8'h00, 8'hb0, 8'h81};   // command for initial
		cmd_r[1]= {8'hff, 8'ha1, 8'ha6, 8'ha8, 8'h1f, 8'hc8};   // command for initial
		cmd_r[2]= {8'hd3, 8'h00, 8'hd5, 8'h80, 8'hd9, 8'h1f};   // command for initial
		cmd_r[3]= {8'hda, 8'h00, 8'hdb, 8'h40, 8'h8d, 8'h14};   // command for initial
		cmd_r[4]= {8'haf, 8'he3, 8'he3, 8'he3, 8'he3, 8'he3};   // command for initial
		cmd_r[5]= {8'hb0, 8'h01, 8'h12, 8'hE3, 8'he3, 8'he3};   // command for set row1
		cmd_r[6]= {8'hb1, 8'h01, 8'h12, 8'hE3, 8'he3, 8'he3};   // command for set row2
		cmd_r[7]= {8'hb2, 8'h01, 8'h12, 8'hE3, 8'he3, 8'he3};   // command for set row3
		cmd_r[8]= {8'hb3, 8'h01, 8'h12, 8'hE3, 8'he3, 8'he3};   // command for set row4
		cmd_r[9]= {8'hb4, 8'h01, 8'h12, 8'hE3, 8'he3, 8'he3};   // command for set row1
		cmd_r[10]= {8'hb5, 8'h01, 8'h12, 8'hE3, 8'he3, 8'he3};   // command for set row2
		cmd_r[11]= {8'hb6, 8'h01, 8'h12, 8'hE3, 8'he3, 8'he3};   // command for set row3
		cmd_r[12]= {8'hb7, 8'h01, 8'h12, 8'hE3, 8'he3, 8'he3};   // command for set row4
	end

//initial for memory register
reg [63:0] mem [21:0];
reg [63:0] temp;
initial
	begin
		mem[0]= {8'h00, 8'h00, 8'h00, 8'h00, 8'h00, 8'h00, 8'h00, 8'h00};   // 0  sp 
		mem[1]= {8'h00, 8'h00, 8'h3E, 8'h51, 8'h49, 8'h45, 8'h3E, 8'h00};   // 16 0
		mem[2]= {8'h00, 8'h00, 8'h00, 8'h42, 8'h7F, 8'h40, 8'h00, 8'h00};   // 17 1
		mem[3]= {8'h00, 8'h00, 8'h42, 8'h61, 8'h51, 8'h49, 8'h46, 8'h00};   // 18 2
		mem[4]= {8'h00, 8'h00, 8'h21, 8'h41, 8'h45, 8'h4B, 8'h31, 8'h00};   // 19 3
		mem[5]= {8'h00, 8'h00, 8'h18, 8'h14, 8'h12, 8'h7F, 8'h10, 8'h00};   // 20 4
		mem[6]= {8'h00, 8'h00, 8'h27, 8'h45, 8'h45, 8'h45, 8'h39, 8'h00};   // 21 5
		mem[7]= {8'h00, 8'h00, 8'h3C, 8'h4A, 8'h49, 8'h49, 8'h30, 8'h00};   // 22 6
		mem[8]= {8'h00, 8'h00, 8'h01, 8'h71, 8'h09, 8'h05, 8'h03, 8'h00};   // 23 7
		mem[9]= {8'h00, 8'h00, 8'h36, 8'h49, 8'h49, 8'h49, 8'h36, 8'h00};   // 24 8
		mem[10]= {8'h00, 8'h00, 8'h06, 8'h49, 8'h49, 8'h29, 8'h1E, 8'h00};   // 25 9
		mem[11]= {8'h00, 8'h00, 8'h00, 8'h36, 8'h36, 8'h00, 8'h00, 8'h00};   // 26 :
		mem[12]= {8'h00, 8'h00, 8'h7C, 8'h12, 8'h11, 8'h12, 8'h7C, 8'h00};   // 33 A
		mem[13]= {8'h00, 8'h00, 8'h7F, 8'h49, 8'h49, 8'h49, 8'h36, 8'h00};   // 34 B
		mem[14]= {8'h00, 8'h00, 8'h7F, 8'h41, 8'h41, 8'h22, 8'h1C, 8'h00};   // 36 D
		mem[15]= {8'h00, 8'h00, 8'h7F, 8'h09, 8'h09, 8'h09, 8'h01, 8'h00};   // 38 F
		mem[16]= {8'h00, 8'h00, 8'h7F, 8'h08, 8'h14, 8'h22, 8'h41, 8'h00};   // 43 K
		mem[17]= {8'h00, 8'h00, 8'h7F, 8'h02, 8'h0C, 8'h02, 8'h7F, 8'h00};   // 45 M
		mem[18]= {8'h00, 8'h00, 8'h7F, 8'h09, 8'h09, 8'h09, 8'h06, 8'h00};   // 48 P
		mem[19]= {8'h00, 8'h00, 8'h46, 8'h49, 8'h49, 8'h49, 8'h31, 8'h00};   // 51 S
		mem[20]= {8'h00, 8'h00, 8'h01, 8'h01, 8'h7F, 8'h01, 8'h01, 8'h00};   // 52 T
		mem[21]= {8'h00, 8'h00, 8'h3F, 8'h40, 8'h38, 8'h40, 8'h3F, 8'h00};   // 55 W
	end
	
//clk_div = clk_in/CLK_DIV_PERIOD, 50% is high voltage



reg clk_div; 
reg[15:0] clk_cnt=0;
always@(posedge clk_in or negedge rst_n_in)
begin
	if(!rst_n_in) clk_cnt<=0;
	else begin
		clk_cnt<=clk_cnt+1;  
		if(clk_cnt==(CLK_DIV_PERIOD-1)) clk_cnt<=0;
		if(clk_cnt<(CLK_DIV_PERIOD/2)) clk_div<=0;
		else clk_div<=1;
	end
end

//divide clk_div 4 state, RISING and FALLING state is keeped one cycle of clk_in, like a pulse.
reg[1:0] clk_div_state=CLK_L;  
always@(posedge clk_in or negedge rst_n_in)
begin
	if(!rst_n_in) clk_div_state<=CLK_L;
    else 
		case(clk_div_state)
			CLK_L: begin
					if (clk_div) clk_div_state<=CLK_RISING_DEGE;  
					else clk_div_state<=CLK_L;
				end
			CLK_RISING_DEGE :clk_div_state<=CLK_H;  
			CLK_H:begin                 
					if (!clk_div) clk_div_state<=CLK_FALLING_DEGE;
					else clk_div_state<=CLK_H;
				end 
			CLK_FALLING_DEGE:clk_div_state<=CLK_L;  
			default;
		endcase
end

reg shift_flag = 0;
reg[6:0] x_reg;
reg[2:0] y_reg;   
reg[7:0] char_reg;
reg[8:0] temp_cnt;
reg[7:0] data_reg; 
reg[2:0] data_state=IDLE; 
reg[2:0] data_state_back; 
reg[7:0] data_state_cnt=0;  
reg[3:0] shift_cnt=0; 
reg[25:0] delay_cnt=0;  
//Finite State Machine, 
always@(posedge clk_in or negedge rst_n_in)      
begin
	if(!rst_n_in) 
		begin 
			data_state<=IDLE;
			run_flag <= 1;
			data_state_cnt<=0;
			shift_flag <= 0;
			oled_cs_n_out<=HIGH;
		end
    else begin
		case (data_state)
			IDLE: begin
					oled_cs_n_out<=HIGH;
					data_state_cnt<=data_state_cnt+1;
					case(data_state_cnt)
						0: oled_rst_n_out <= 0;
						1: data_state<=DELAY;
						2: oled_rst_n_out <= 1;
						3: data_state<=DELAY;
						//display initial
						4: begin data_state<=DISPLAY;data_state_back<=DISPLAY;oled_dc_out<=CMD;char_reg<=0; end
						5: begin data_state<=DISPLAY;data_state_back<=DISPLAY;oled_dc_out<=CMD;char_reg<=1; end
						6: begin data_state<=DISPLAY;data_state_back<=DISPLAY;oled_dc_out<=CMD;char_reg<=2; end
						7: begin data_state<=DISPLAY;data_state_back<=DISPLAY;oled_dc_out<=CMD;char_reg<=3; end
						8: begin data_state<=DISPLAY;data_state_back<=DISPLAY;oled_dc_out<=CMD;char_reg<=4; end
						//clear display
						9: begin data_state<=DISPLAY;data_state_back<=DISPLAY;oled_dc_out<=CMD;char_reg<=5; end
						10: begin data_state<=CLEAR;data_state_back<=CLEAR; end
						11: begin data_state<=DISPLAY;data_state_back<=DISPLAY;oled_dc_out<=CMD;char_reg<=6; end
						12: begin data_state<=CLEAR;data_state_back<=CLEAR; end
						13: begin data_state<=DISPLAY;data_state_back<=DISPLAY;oled_dc_out<=CMD;char_reg<=7; end
						14: begin data_state<=CLEAR;data_state_back<=CLEAR; end
						15: begin data_state<=DISPLAY;data_state_back<=DISPLAY;oled_dc_out<=CMD;char_reg<=8; end
						16: begin data_state<=CLEAR;data_state_back<=CLEAR; end
						17: begin data_state<=DISPLAY;data_state_back<=DISPLAY;oled_dc_out<=CMD;char_reg<=9; end
						18: begin data_state<=CLEAR;data_state_back<=CLEAR; end
						19: begin data_state<=DISPLAY;data_state_back<=DISPLAY;oled_dc_out<=CMD;char_reg<=10; end
						20: begin data_state<=CLEAR;data_state_back<=CLEAR; end
						21: begin data_state<=DISPLAY;data_state_back<=DISPLAY;oled_dc_out<=CMD;char_reg<=11; end
						22: begin data_state<=CLEAR;data_state_back<=CLEAR; end
						23: begin data_state<=DISPLAY;data_state_back<=DISPLAY;oled_dc_out<=CMD;char_reg<=12; end
						24: begin data_state<=CLEAR;data_state_back<=CLEAR; end
						//set start point
						25: begin data_state<=DISPLAY;data_state_back<=DISPLAY;oled_dc_out<=CMD;char_reg<=5; end
						//wite the word
						26: begin data_state<=DISPLAY;data_state_back<=DISPLAY;oled_dc_out<=DATA;char_reg<=21; end
						27: begin data_state<=DISPLAY;data_state_back<=DISPLAY;oled_dc_out<=DATA;char_reg<=11; end
						 28: begin data_state<=DISPLAY;data_state_back<=DISPLAY;oled_dc_out<=DATA;
								if(data[9:8]==2'b00) char_reg <= 19; //正弦波
								else if(data[9:8]==2'b01) char_reg <= 20;//三角波
								else if(data[9:8]==2'b11) char_reg <= 15;//方波
								else if(data[9:8]==2'b10) char_reg<=14;//锯齿波
							  end  //波形选择
						29: begin data_state<=DISPLAY;data_state_back<=DISPLAY;oled_dc_out<=DATA;char_reg<=0; end
						30: begin data_state<=DISPLAY;data_state_back<=DISPLAY;oled_dc_out<=DATA;char_reg<=0; end
						31: begin data_state<=DISPLAY;data_state_back<=DISPLAY;oled_dc_out<=DATA;char_reg<=0; end//空格
						32: begin data_state<=DISPLAY;data_state_back<=DISPLAY;oled_dc_out<=DATA;char_reg<=15; end
						33: begin data_state<=DISPLAY;data_state_back<=DISPLAY;oled_dc_out<=DATA;char_reg<=11; end
						34: begin data_state<=DISPLAY;data_state_back<=DISPLAY;oled_dc_out<=DATA;
								
								  char_reg <= data[27:24]+1;
							 end
						35: begin data_state<=DISPLAY;data_state_back<=DISPLAY;oled_dc_out<=DATA;
								
							       char_reg <= data[23:20]+1;							
							 end
						36: begin data_state<=DISPLAY;data_state_back<=DISPLAY;oled_dc_out<=DATA;
								 char_reg <= data[19:16]+ 1;
								
								data_state_cnt <= data_state_cnt + 1;
							 end                                        //频率数值显示
						37: begin data_state<=DISPLAY;data_state_back<=DISPLAY;oled_dc_out<=DATA;
								if(data[7:6]==2'b10) char_reg <= 17;
								else if(data[7:6]==2'b01)  char_reg <= 16;
                                else  char_reg<=0;//频率单位显示
							end
							
						//set start point
						38: begin data_state<=DISPLAY;data_state_back<=DISPLAY;oled_dc_out<=CMD;char_reg<=6; end
						//wite the word
						39: begin data_state<=DISPLAY;data_state_back<=DISPLAY;oled_dc_out<=DATA;if(data[9:8]==2'b11)char_reg<=14;else char_reg<=0; end
						40: begin data_state<=DISPLAY;data_state_back<=DISPLAY;oled_dc_out<=DATA;if(data[9:8]==2'b11)char_reg<=11; else char_reg<=0;end
						41: begin data_state<=DISPLAY;data_state_back<=DISPLAY;oled_dc_out<=DATA;if(data[9:8]==2'b11)char_reg<=data[13:10]+1; else char_reg<=0;end
						42: begin data_state<=DISPLAY;data_state_back<=DISPLAY;oled_dc_out<=DATA;if(data[9:8]==2'b11)char_reg<=1;else char_reg<=0; end
						43: begin data_state<=DISPLAY;data_state_back<=DISPLAY;oled_dc_out<=DATA;char_reg<=0; end
						44: begin data_state<=DISPLAY;data_state_back<=DISPLAY;oled_dc_out<=DATA;char_reg<=0; end //方波占空比

						45: begin data_state<=DISPLAY;data_state_back<=DISPLAY;oled_dc_out<=DATA;char_reg<=18; end 
						46: begin data_state<=DISPLAY;data_state_back<=DISPLAY;oled_dc_out<=DATA;char_reg<=11; end 
						47: begin data_state<=DISPLAY;data_state_back<=DISPLAY;oled_dc_out<=DATA;
							      case(data[15:14])
									  2'b00: char_reg<=0; 
									  2'b01:char_reg<=1;
								      2'b10:char_reg<=2;
                                      2'b11:char_reg<=3;
                                   endcase									  
									  end 
						48: begin data_state<=DISPLAY;data_state_back<=DISPLAY;oled_dc_out<=DATA;
							        case(data[15:14])
									  2'b00: char_reg<=0; 
									  2'b01:char_reg<=10;
								      2'b10:char_reg<=9;
                                      2'b11:char_reg<=8;
                                    endcase
                                       end 
						49: begin data_state<=DISPLAY;data_state_back<=DISPLAY;oled_dc_out<=DATA;char_reg<=1; end  //相位控制
						//set start point
						50: begin data_state<=DISPLAY;data_state_back<=DISPLAY;oled_dc_out<=CMD;char_reg<=7; end
						//wite the word	
						51: begin data_state<=DISPLAY;data_state_back<=DISPLAY;oled_dc_out<=DATA;char_reg<=12; end 
						52: begin data_state<=DISPLAY;data_state_back<=DISPLAY;oled_dc_out<=DATA;char_reg<=11; end 
						53: begin data_state<=DISPLAY;data_state_back<=DISPLAY;oled_dc_out<=DATA;char_reg<=0; end 
						54: begin data_state<=DISPLAY;data_state_back<=DISPLAY;oled_dc_out<=DATA;char_reg<=data[5:3]+1; end //增益
						55: begin data_state<=DISPLAY;data_state_back<=DISPLAY;oled_dc_out<=DATA;char_reg<=0; end 
						56: begin data_state<=DISPLAY;data_state_back<=DISPLAY;oled_dc_out<=DATA;char_reg<=0; end 
						57: begin data_state<=DISPLAY;data_state_back<=DISPLAY;oled_dc_out<=DATA;char_reg<=13; end 
						58: begin data_state<=DISPLAY;data_state_back<=DISPLAY;oled_dc_out<=DATA;char_reg<=11; end 
						59: begin data_state<=DISPLAY;data_state_back<=DISPLAY;oled_dc_out<=DATA;char_reg<=data[2:0]+1; end //直流偏置
							
						
						60: begin data_state_cnt<=17;run_flag <= ~run_flag; end
						default;
					endcase
				end
				
			SHIFT: begin
					if(!shift_flag)
						begin
							if (clk_div_state==CLK_FALLING_DEGE)  
								begin
									if (shift_cnt==8)  
										begin
											shift_cnt<=0;
											data_state<=data_state_back;
										end
									else begin
											oled_cs_n_out<=LOW;
											oled_data_out<=data_reg[7];   
											shift_flag <= 1;
										end
								end
						end
					else
						begin
							if (clk_div_state==CLK_RISING_DEGE)   
								begin  
									data_reg<={data_reg[6:0], data_reg[7]};  
									shift_cnt<=shift_cnt+1;
									shift_flag <= 0;
								end
						end
				end

			DISPLAY: begin
						temp_cnt<=temp_cnt+1;
						oled_cs_n_out<=HIGH;
						if (temp_cnt==6) 
							begin
								data_state<=IDLE;
								temp_cnt<=0; 
							end
						else 
							begin
								temp = (oled_dc_out==CMD)? cmd_r[char_reg]:mem[char_reg];
								case (temp_cnt)
									0 :  data_reg<=temp[47:40];
									1 :  data_reg<=temp[39:32];
									2 :  data_reg<=temp[31:24];
									3 :  data_reg<=temp[23:16];
									4 :  data_reg<=temp[15:8];
									5 :  data_reg<=temp[7:0];
									default;
								endcase
								data_state<=SHIFT;
							end
					end
			
			CLEAR: begin            
					data_reg<=8'h00;
					temp_cnt<=temp_cnt+1;
					oled_cs_n_out<=HIGH;
					oled_dc_out<=DATA;
					if (temp_cnt>=128) 
						begin
							temp_cnt<=0;
							data_state<=IDLE;
						end
					else data_state<=SHIFT;
				end
			
			DELAY: begin
					if(delay_cnt==DELAY_PERIOD)
						begin
							data_state<=IDLE; 
							delay_cnt<=0;
						end
					else delay_cnt<=delay_cnt+1;
				end
					   
			default;
		endcase
	end
end
assign oled_clk_out = oled_cs_n_out?0:clk_div;
endmodule

 

以下是一个简单的Verilog代码,用于在FPGA实现SPI驱动OLED屏幕: ```verilog module spi_oled( input wire clk, //时钟信号 input wire rst, //复位信号 output wire cs, //片选信号 output wire dc, //数据/命令选择信号 output wire sda, //数据信号 output wire sck //时钟信号 ); //定义寄存器 reg [7:0] command_reg; reg [7:0] data_reg; //状态机 reg [2:0] state; parameter [2:0] IDLE = 3'b000; parameter [2:0] COMMAND = 3'b001; parameter [2:0] DATA = 3'b010; //时钟分频计数器 reg [7:0] cnt; parameter [7:0] CLK_DIV = 8'hFF; //时钟分频器 parameter [7:0] CMD_DELAY = 8'h0F; //命令延迟 parameter [7:0] DATA_DELAY = 8'h0F; //数据延迟 //初始化状态和计数器 initial begin state = IDLE; cnt = 0; end //状态机逻辑 always @(posedge clk) begin if (rst) begin state <= IDLE; cnt <= 0; end else begin case (state) IDLE: begin cs <= 1'b1; dc <= 1'b0; sda <= 1'b1; sck <= 1'b1; if (cnt == CLK_DIV) begin //进入命令状态 state <= COMMAND; cnt <= 0; end else begin cnt <= cnt + 1; end end COMMAND: begin cs <= 1'b0; dc <= 1'b0; sda <= command_reg[7]; command_reg <= {command_reg[6:0], 1'b0}; sck <= 1'b0; if (cnt == CMD_DELAY) begin //进入数据状态 state <= DATA; cnt <= 0; end else begin cnt <= cnt + 1; end end DATA: begin cs <= 1'b0; dc <= 1'b1; sda <= data_reg[7]; data_reg <= {data_reg[6:0], 1'b0}; sck <= 1'b0; if (cnt == DATA_DELAY) begin //返回空闲状态 state <= IDLE; cnt <= 0; end else begin cnt <= cnt + 1; end end endcase end end endmodule ``` 这个代码中的状态机逻辑控制SPI通信以向OLED屏幕发送命令和数据。您需要根据OLED屏幕的规格和数据手册定制代码,以确保正确配置SPI通信和正确发送命令和数据。
评论 28
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

谢娘蓝桥

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值