one , summary :

       
Send initialization command byte by byte to OLED, set up OLED Some registers of . of course , I certainly haven't studied what orders should be issued myself ( After all 32 The use of is direct copy Code of ), And now it's also before reference 32 drive OLED The initialization command OLED Initialization of .

        Stick one first 32 Code of ( You can also adapt it for yourself Verilog Let's practice )
void OLED_Init(void) { OLED_SPI_Init(); OLED_CLK = 1; OLED_RST = 0;
OLED_DLY_ms(100); OLED_RST = 1; // There should be enough time from power on to initialization , Namely waiting RC Reset complete WriteCmd(0xAE);
// Display Off (0x00) WriteCmd(0xD5); WriteCmd(0x80); // Set Clock as 100
Frames/Sec WriteCmd(0xA8); WriteCmd(0x3F); // 1/64 Duty (0x0F~0x3F)
WriteCmd(0xD3); WriteCmd(0x00); // Shift Mapping RAM Counter (0x00~0x3F)
WriteCmd(0x40 | 0x00); // Set Mapping RAM Display Start Line (0x00~0x3F)
WriteCmd(0x8D); WriteCmd(0x10 | 0x04); // Enable Embedded DC/DC Converter
(0x00/0x04) WriteCmd(0x20); WriteCmd(0x02); // Set Page Addressing Mode
(0x00/0x01/0x02) WriteCmd(0xA0 | 0x01); // Set SEG/Column Mapping
WriteCmd(0xC0); // Set COM/x Scan Direction WriteCmd(0xDA); WriteCmd(0x02 |
0x10); // Set Sequential Configuration (0x00/0x10) WriteCmd(0x81);
WriteCmd(0xCF); // Set SEG Output Current WriteCmd(0xD9); WriteCmd(0xF1); //
Set Pre-Charge as 15 Clocks & Discharge as 1 Clock WriteCmd(0xDB);
WriteCmd(0x40); // Set VCOM Deselect Level WriteCmd(0xA4 | 0x00); // Disable
Entire Display On (0x00/0x01) WriteCmd(0xA6 | 0x00); // Disable Inverse Display
On (0x00/0x01) WriteCmd(0xAE | 0x01); // Display On (0x01) OLED_Clear(); // Initial screen clearing
} /*************************************************************************/
/* Function function : take OLED Wake up from sleep */
/*************************************************************************/
void OLED_ON(void) { WriteCmd(0X8D); // Set charge pump WriteCmd(0X14); // Turn on the charge pump
WriteCmd(0XAF); //OLED awaken }
/*************************************************************************/
/* Function function : Update video to OLED */
/*************************************************************************/
void OLED_Refresh_Gram(void) { u8 i,n; for(i=0;i<8;i++) { WriteCmd(0xb0+i);
// Set page address (0~7) WriteCmd(0x00); // Set display position — Column low address WriteCmd(0x10); // Set display position — Column height address
for(n=0;n<128;n++)WriteData(OLED_GRAM[n][i]); } }
two ,Verilog Code writing         

         The commands to be used are used first reg Type variable register
// Initialization command // This initialization point is more dense initial begin init_cmd[0] = 8'hAE; init_cmd[1] = 8'hD5;
init_cmd[2] = 8'h80; init_cmd[3] = 8'hA8; init_cmd[4] = 8'h3F; init_cmd[5] =
8'hD3; init_cmd[6] = 8'h00; init_cmd[7] = 8'h40; init_cmd[8] = 8'h8D;
init_cmd[9] = 8'h10|8'h04; init_cmd[10] = 8'h20; init_cmd[11] = 8'h02;
init_cmd[12] = 8'hA0|8'h01; init_cmd[13] = 8'hC0; init_cmd[14] = 8'hDA;
init_cmd[15] = 8'h02|8'h10; init_cmd[16] = 8'h81; init_cmd[17] = 8'hCF;
init_cmd[18] = 8'hD9; init_cmd[19] = 8'hF1; init_cmd[20] = 8'hDB; init_cmd[21]
= 8'h40; init_cmd[22] = 8'hA4|8'h00; init_cmd[23] = 8'hA6|8'h00; init_cmd[24] =
8'hAE|8'h01; end //oled Open command initial begin oled_on_cmd[0] = 8'h8D;oled_on_cmd[1]
= 8'h14;oled_on_cmd[2] = 8'hAF; end //oled Clear command // That is, set the page address , Set the displayed low address and set the displayed high address
initial begin clear_cmd[0] = 8'hB0;clear_cmd[1] = 8'h00;clear_cmd[2] =
8'h10;// The first 0 page clear_cmd[3] = 8'hB1;clear_cmd[4] = 8'h00;clear_cmd[5] =
8'h10;// The first 1 page clear_cmd[6] = 8'hB2;clear_cmd[7] = 8'h00;clear_cmd[8] =
8'h10;// The first 2 page clear_cmd[9] = 8'hB3;clear_cmd[10] = 8'h00;clear_cmd[11] =
8'h10;// The first 3 page clear_cmd[12] = 8'hB4;clear_cmd[13] = 8'h00;clear_cmd[14] =
8'h10;// The first 4 page clear_cmd[15] = 8'hB5;clear_cmd[16] = 8'h00;clear_cmd[17] =
8'h10;// The first 5 page clear_cmd[18] = 8'hB6;clear_cmd[19] = 8'h00;clear_cmd[20] =
8'h10;// The first 6 page clear_cmd[21] = 8'hB7;clear_cmd[22] = 8'h00;clear_cmd[23] =
8'h10;// The first 7 page end
       
Then the next step is to write the state machine for state transition , The general logic is , There is a state for spi Write data data assignment , There is also a state to detect whether the command in the current state has been written , After writing, jump to the next state , Otherwise, continue to write the next data , Of course I have to wait spi Finish the last data , produce done Write the next data after the signal

OLED All codes initialized
module oled_init( input clk, // clock signal 1m Clock input rst_n, // Reset signal input
write_done, //spi Write completion signal After obtaining this signal, start the next write output reg oled_rst, //oled Reset pin signal of output
reg oled_dc, //oled of dc Write data Write command control signal output reg [7:0] data, // Output data for spi Write data in
output reg ena_write, //spi Write enable signal output init_done // Initialization completion signal ); reg [20:0]
us_cnt; //us Counter Power on delay waiting reg us_cnt_clr; // Counter reset signal parameter RST_NUM = 10;
//1000_000 // wait for 1s // Status description // Reset status Initialize write command status oled Open write command status oled Displays the status of the reset write command oled Display reset write data status
// Wait for the initialization write command to complete wait for oled Start write command completed Wait for the reset write command to complete Wait for the completion of clearing and writing data parameter
Rst=0,Init=1,OledOn=2,ClearCmd=3,ClearData=4,WaitInit=5,WaitOn=6,WaitClearCmd=7,WaitClearData=8,Done=9;
reg[3:0] state,next_state;// The current state and the next state of the state machine reg [7:0] init_cmd[27:0]; // Initialize command store
reg [4:0] init_cmd_cnt; // Initialization command count reg [7:0] oled_on_cmd[2:0];//oled Open command storage reg
[1:0] oled_on_cmd_cnt; //oled On command count reg [7:0] clear_cmd[24:0]; // Reset command storage reg
[4:0] clear_cmd_cnt; // Clear command count reg [10:0] clear_data_cnt; // Clear write data count // Initialization command
// This initialization point is more dense initial begin init_cmd[0] = 8'hAE; init_cmd[1] = 8'hD5;
init_cmd[2] = 8'h80; init_cmd[3] = 8'hA8; init_cmd[4] = 8'h3F; init_cmd[5] =
8'hD3; init_cmd[6] = 8'h00; init_cmd[7] = 8'h40; init_cmd[8] = 8'h8D;
init_cmd[9] = 8'h10|8'h04; init_cmd[10] = 8'h20; init_cmd[11] = 8'h02;
init_cmd[12] = 8'hA0|8'h01; init_cmd[13] = 8'hC0; init_cmd[14] = 8'hDA;
init_cmd[15] = 8'h02|8'h10; init_cmd[16] = 8'h81; init_cmd[17] = 8'hCF;
init_cmd[18] = 8'hD9; init_cmd[19] = 8'hF1; init_cmd[20] = 8'hDB; init_cmd[21]
= 8'h40; init_cmd[22] = 8'hA4|8'h00; init_cmd[23] = 8'hA6|8'h00; init_cmd[24] =
8'hAE|8'h01; end /* // Initialization command // The initialized points are sparse // The resolution should be set differently ( guess ) initial begin
init_cmd[0] = 8'hAE; init_cmd[1] = 8'h00; init_cmd[2] = 8'h10; init_cmd[3] =
8'h00; init_cmd[4] = 8'hB0; init_cmd[5] = 8'h81; init_cmd[6] = 8'hFF;
init_cmd[7] = 8'hA1; init_cmd[8] = 8'hA6; init_cmd[9] = 8'hA8; init_cmd[10] =
8'h1F;init_cmd[11] = 8'hC8; init_cmd[12] = 8'hD3;init_cmd[13] =
8'h00;init_cmd[14] = 8'hD5;init_cmd[15] = 8'h80; init_cmd[16] =
8'hD9;init_cmd[17] = 8'h1f;init_cmd[18] = 8'hD9;init_cmd[19] = 8'hF1;
init_cmd[20] = 8'hDA;init_cmd[21] = 8'h00;init_cmd[22] = 8'hDB;init_cmd[23] =
8'h40; end */ //oled Open command initial begin oled_on_cmd[0] = 8'h8D;oled_on_cmd[1] =
8'h14;oled_on_cmd[2] = 8'hAF; end //oled Clear command // That is, set the page address , Set the displayed low address and set the displayed high address
initial begin clear_cmd[0] = 8'hB0;clear_cmd[1] = 8'h00;clear_cmd[2] =
8'h10;// The first 0 page clear_cmd[3] = 8'hB1;clear_cmd[4] = 8'h00;clear_cmd[5] =
8'h10;// The first 1 page clear_cmd[6] = 8'hB2;clear_cmd[7] = 8'h00;clear_cmd[8] =
8'h10;// The first 2 page clear_cmd[9] = 8'hB3;clear_cmd[10] = 8'h00;clear_cmd[11] =
8'h10;// The first 3 page clear_cmd[12] = 8'hB4;clear_cmd[13] = 8'h00;clear_cmd[14] =
8'h10;// The first 4 page clear_cmd[15] = 8'hB5;clear_cmd[16] = 8'h00;clear_cmd[17] =
8'h10;// The first 5 page clear_cmd[18] = 8'hB6;clear_cmd[19] = 8'h00;clear_cmd[20] =
8'h10;// The first 6 page clear_cmd[21] = 8'hB7;clear_cmd[22] = 8'h00;clear_cmd[23] =
8'h10;// The first 7 page end //1 Microsecond counter always @ (posedge clk or negedge rst_n) begin if
(!rst_n) us_cnt <= 21'd0; else if (us_cnt_clr) us_cnt <= 21'd0; else us_cnt <=
us_cnt + 1'b1; end // There's a story that tells us always(*) Don't mess with it ( feel sad ) // Prone to problems ... Although I don't know why
// Don't crowd everything together Assignment is still separate from state transition // Put it in the sequential circuit // But the next state of state transition cannot be put into the sequential circuit
// It will delay the current state to the next state by one clock cycle , The timing may be chaotic always @(*) begin if(!rst_n) begin next_state
= Rst; end else begin case(state) // Reset wait state // Wait for power on reset Rst: next_state = us_cnt >
RST_NUM ? Init : Rst; // Initialization status Init: next_state = WaitInit; // Enter the state of waiting for the write command to complete
// Wait for initialization command write completion status // When this state is reached cmd cnt Just add 1, Therefore, it is necessary to judge by a larger value // whether 25 Command write complete Write complete and enter the next state
// Otherwise Yes No spi Write complete spi After writing, continue to write the next command Otherwise, keep waiting spi Write complete // Remember to add &&write_done Wait for the last time WaitInit:
next_state = (init_cmd_cnt == 5'd25&&write_done) ? OledOn : (write_done ? Init
: WaitInit); //oled Open write command status OledOn: next_state = WaitOn; // wait for oled Start write command completion status
// Judge whether the command is finished Enter the next state after writing // otherwise Then judge whether spi Write complete After writing, continue to write the next data WaitOn: next_state =
(oled_on_cmd_cnt == 2'd3&&write_done) ? ClearCmd : (write_done ? OledOn :
WaitOn); // Clear write command status ClearCmd: next_state = WaitClearCmd; // Wait for reset write command status // Write three commands at a time
So yes 3 Remainder // here 0 It will cause you to jump when you enter this state WaitClearCmd: next_state = (clear_cmd_cnt % 2'd3 ==
0 && write_done) ? ClearData : (write_done ? ClearCmd : WaitClearCmd);
// Clear write data status ClearData: next_state = WaitClearData; // Waiting for reset to write data
//1 Need to write page 128 Data , finish writing sth. 7 Page is 1024 Data // finish writing sth. 1 page , That is, every time you finish writing 128 A data needs to write a command , So be right 128 Surplus , Then enter the write command state
// among 0 It will not interfere with the state , Because the counter has been added when entering this state 1 Yes WaitClearData: next_state =
(clear_data_cnt == 11'd1024&&write_done) ? Done : (clear_data_cnt % 11'd128 ==
0&&write_done ? ClearCmd : (write_done ? ClearData : WaitClearData)); // Completion status
Done: next_state = Done; default: next_state = Rst; endcase end end
// This must not be written into the above combinational logic // Will cause Latch // As for the reason ,, I don't know either always @(posedge clk,negedge rst_n)
begin if(!rst_n) begin oled_rst <= 1'b0; us_cnt_clr <= 1'b1; oled_dc <= 1'b1;
data <= 8'h10; ena_write <= 1'b0; end else begin case(state) // Reset wait state Rst:begin
oled_rst <= 1'b0; us_cnt_clr <= 1'b0; end // Initialization status Init:begin oled_rst <= 1'b1;
us_cnt_clr <= 1'b1; // Reset counter ena_write <= 1'b1; // Write enable oled_dc <= 1'b0; // Write command
data <= init_cmd[init_cmd_cnt];// Write data assignment end // Wait for initialization command write completion status WaitInit: begin
ena_write <= 1'b0; // Write disability end //oled Open write command status OledOn:begin ena_write <= 1'b1; // Write enable
oled_dc <= 1'b0; // Write command data <= oled_on_cmd[oled_on_cmd_cnt]; end
// wait for oled Start write command completion status WaitOn:begin ena_write <= 1'b0; // Write disability end // Clear write command status
ClearCmd:begin ena_write <= 1'b1; oled_dc <= 1'b0; data <=
clear_cmd[clear_cmd_cnt]; end // Wait for reset write command status WaitClearCmd:begin ena_write <= 1'b0;
end // Clear write data status ClearData:begin ena_write <= 1'b1; oled_dc <= 1'b1; data <=
8'hff; end // Waiting for reset to write data WaitClearData:begin ena_write <= 1'b0; end endcase end end
// state transition always @(posedge clk,negedge rst_n) begin if(!rst_n) state <= Rst; else
state <= next_state; end // Counter count always @(posedge clk,negedge rst_n) begin
if(!rst_n) begin init_cmd_cnt <= 5'd0; oled_on_cmd_cnt <= 4'd0; clear_cmd_cnt
<=3'd0; clear_data_cnt <= 11'd0; end else begin case(state) Init: init_cmd_cnt
<= init_cmd_cnt + 1'b1; OledOn: oled_on_cmd_cnt <= oled_on_cmd_cnt + 1'b1;
ClearCmd: clear_cmd_cnt <= clear_cmd_cnt + 1'b1; ClearData: clear_data_cnt <=
clear_data_cnt + 1'b1; default:begin init_cmd_cnt <= init_cmd_cnt;
oled_on_cmd_cnt <= oled_on_cmd_cnt; clear_cmd_cnt <= clear_cmd_cnt;
clear_data_cnt <= clear_data_cnt; end endcase end end assign init_done = (state
== Done); endmodule
        Because the initial data value is written to 0xff, therefore OLED When lit, it should look like the full screen is filled

        ( Next, write the top-level module , Light up OLED)

testbench Module test code
`timescale 1ns/1ns // The simulation unit is 1ns, Accuracy is 1ns module oled_init_tb(); reg clk; reg
rst_n; reg write_done; wire oled_rst; wire oled_dc; wire [7:0] data; wire
ena_write; wire init_done; oled_init oled_init_inst( .clk(clk), .rst_n(rst_n),
.write_done(write_done), .oled_rst(oled_rst), .oled_dc(oled_dc), .data(data),
.ena_write(ena_write), .init_done(init_done) ); initial begin #0 clk = 0; rst_n
= 0; write_done = 1; #20 rst_n = 1; end always #5 clk = ~clk; endmodule
         Partial test results ( Adjust the power on reset waiting time to 10 Clock cycles )

Technology