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