## OLED显示控制器SSD1306的控制逻辑 来源:[[https://github.com/lupyuen/fpga_oled_ssd1306/blob/master/src/SSD1306.v|SSD1306.v]] // Based on https://github.com/MorgothCreator/Verilog_SSD1306_CFG_IP // and https://git.morgothdisk.com/VERILOG/VERILOG-UTIL-IP/blob/master/spi_master. `include "SSD1306_ROM_cfg_mod_header.v" // Commands for SSD1306 `define SSD1306_SETCONTRAST 81 `define SSD1306_DISPLAYALLON_RESUME A4 `define SSD1306_DISPLAYALLON A5 `define SSD1306_NORMALDISPLAY A6 `define SSD1306_INVERTDISPLAY A7 `define SSD1306_DISPLAYOFF AE `define SSD1306_DISPLAYON AF `define SSD1306_SETDISPLAYOFFSET D3 `define SSD1306_SETCOMPINS DA `define SSD1306_SETVCOMDETECT DB `define SSD1306_SETDISPLAYCLOCKDIV D5 `define SSD1306_SETPRECHARGE D9 `define SSD1306_SETMULTIPLEX A8 `define SSD1306_SETLOWCOLUMN 00 `define SSD1306_SETHIGHCOLUMN 10 `define SSD1306_SETSTARTLINE 40 `define SSD1306_MEMORYMODE 20 `define SSD1306_COMSCANINC C0 `define SSD1306_COMSCANDEC C8 `define SSD1306_SEGREMAP A0 `define SSD1306_CHARGEPUMP 8D `define SSD1306_EXTERNALVCC 01 `define SSD1306_INTERNALVCC 02 `define SSD1306_SWITCHCAPVCC 02 module SSD1306( input clk_50M, // Onboard clock is 50MHz. input rst_n, // Reset pin is also an Input, triggered by board restart or reset button. output reg oled_dc, output reg oled_res, output oled_sclk, output oled_sdin, output reg oled_vbat, output reg oled_vdd, output reg ss, output reg[3:0] led // LED is actually 4 discrete LEDs at 4 Output signals. Each LED Output is 1 bit. ); // The step ID that we are now executing: 0, 1, 2, ... reg[`BLOCK_ROM_INIT_ADDR_WIDTH-1:0] step_id; // The step details, encoded in 48 bits. This will be refetched whenever step_id changes. wire[`BLOCK_ROM_INIT_DATA_WIDTH-1:0] encoded_step; // Whenever step_id is updated, fetch the encoded step from ROM. SSD1306_ROM_cfg_mod oled_rom_init( .addr(step_id), .dout(encoded_step) ); // We define convenience wires to decode our encoded step. Prefix by "step" so we don't mix up. // If encoded_step is changed, these will automatically change. wire step_backward = encoded_step[47]; // 1 if next step is backwards i.e. a negative offset. wire step_next = encoded_step[46:44]; // Offset to the next step, i.e. 1=go to following step. If step_backward=1, go backwards. wire step_time = encoded_step[39:16]; // Number of clk_ssd1306 clock cycles to wait before starting this step. This time is relative to the time of power on. wire step_tx_data = encoded_step[15:8]; // Data to be transmitted via SPI (1 byte). wire step_should_repeat = encoded_step[7]; // 1 if step should be repeated. wire step_repeat = { encoded_step[6:0], step_tx_data }; // How many times the step should be repeated. Only if step_should_repeat=1 wire step_oled_vdd = encoded_step[6]; wire step_oled_vbat = encoded_step[5]; wire step_oled_res = encoded_step[4]; wire step_oled_dc = encoded_step[3]; wire step_wr_spi = encoded_step[2]; wire step_rd_spi = encoded_step[1]; wire step_wait_spi = encoded_step[0]; wire buffempty; wire charreceived; reg wait_spi = 1'b0; reg rd_spi = 1'b0; reg wr_spi = 1'b0; reg clk_ssd1306 = 1'b0; reg rst_ssd1306 = 1'b0; reg internal_state_machine = 1'b0; reg[24:0] cnt = 25'h0; reg[27:0] elapsed_time = 28'h0; reg[27:0] saved_elapsed_time = 28'h0; reg[14:0] repeat_count = 15'h0; reg[7:0] data_tmp = 8'h0; reg[7:0] test_display_on = 8'haf; /* reg [3:0]clk_div; wire clk; assign clk = (step_next) ? clk_div[3] : 1'b0; always @ (posedge clk_ssd1306) begin //if(btnc) //clk_div <= 4'h0; //else clk_div <= clk_div + 1; end */ always@( // Code below is always triggered when these conditions are true... posedge clk_50M or // When the clock signal transitions from low to high (positive edge) OR negedge rst_n // When the reset signal transitions from high to low (negative edge) which ) begin // happens when the board restarts or reset button is pressed. if (!rst_n) begin // If board restarts or reset button is pressed... clk_ssd1306 <= 1'b0; // Init clk_ssd1306 and cnt to 0. "1'b0" means "1-Bit, Binary Value 0" cnt <= 25'd0; // "25'd0" means "25-bit, Decimal Value 0" end else begin ////if (cnt == 25'd249_9999) begin // If our counter has reached its limit... if (cnt == 25'd2499_9999) begin // If our counter has reached its limit... clk_ssd1306 <= ~clk_ssd1306; // Toggle the clk_led from 0 to 1 (and 1 to 0). cnt <= 25'd0; // Reset the counter to 0. end else begin cnt <= cnt + 25'd1; // Else increment counter by 1. "25'd1" means "25-bit, Decimal Value 1" end end end spi_master # ( .WORD_LEN(8), // Default 8 .PRESCALLER_SIZE(8) // Default 8, Max 8 ) spi0( ////.clk(clk_50M), // Fast clock. .clk(clk_ssd1306), // Very slow clock. .prescaller(3'h0), // No prescaler. Fast. ////.prescaller(3'h2), // Prescale by 4. Slow. ////.rst(rst_n), .rst(rst_ssd1306), // Start sending when rst_ssd1306 transitions from low to hi. ////.data_in(step_tx_data), // Send real data. .data_in(test_display_on), // Send test data to switch on display (0xAF). .data_out(data_tmp), .wr(wr_spi), .rd(rd_spi), .buffempty(buffempty), .sck(oled_sclk), .mosi(oled_sdin), .miso(1'b1), .ss(ss), .lsbfirst(1'b0), .mode(2'h0), //.senderr(senderr), .res_senderr(1'b0), .charreceived(charreceived) ); // Synchronous lath to out commands directly from ROM except when is a repeat count load. always @ (posedge clk_ssd1306) begin if (!rst_n) begin // If board restarts or reset button is pressed... // Init the SPI default values. oled_vdd <= 1'b1; oled_vbat <= 1'b1; oled_res <= 1'b0; oled_dc <= 1'b0; wr_spi <= 1'b0; rd_spi <= 1'b0; wait_spi <= 1'b0; end // If this is not a repeated step... else if (!step_should_repeat) begin // Copy the decoded values into registers so they won't change when we go to next step. oled_vdd <= step_oled_vdd; oled_vbat <= step_oled_vbat; oled_res <= step_oled_res; oled_dc <= step_oled_dc; wr_spi <= step_wr_spi; rd_spi <= step_rd_spi; wait_spi <= step_wait_spi; end end always @ (posedge clk_ssd1306) begin if (!rst_n) begin // If board restarts or reset button is pressed... // Init the state machine. elapsed_time <= 28'h0000000; saved_elapsed_time <= 28'h0000000; step_id <= `BLOCK_ROM_INIT_ADDR_WIDTH'h00; internal_state_machine <= 1'b0; repeat_count <= 15'h0000; end led <= { ~step_id[0], ~step_id[1], ~step_id[2], ~step_id[3] }; // Show the state machine step ID in LED. // If the start time is up and the step is ready to execute... if (elapsed_time >= step_time) begin case(internal_state_machine) // First Part: Set up repeating steps. 1'b0 : begin // If this is a repeating step... if (step_should_repeat) begin // Remember in a register how many times to repeat. repeat_count <= step_repeat; // Remember the elapsed time for recalling later. saved_elapsed_time <= elapsed_time + 1; end // If this is not a repeating step... else begin // If we are still repeating... if (repeat_count && step_next > 1) begin // Count down number of times to repeat. repeat_count <= repeat_count - 15'h0001; end end // Handle as a normal step in the next clock tick. internal_state_machine <= 1'b1; if (wr_spi) begin // If this is a write step, signal to SPI module to start the transfer (low to hi transition). //rst_ssd1306 <= 1'b1; end if (rd_spi) begin // If this is a read step, reset the transfer. //rst_ssd1306 <= 1'b0; end end // Second Part: Execute the step. 1'b1 : begin // If we are waiting for SPI command to complete... if (wait_spi) begin // If SPI command has completed... if (charreceived) begin internal_state_machine <= 1'b0; // If we are still repeating... if (repeat_count) begin // Restore the elapsed time. elapsed_time <= saved_elapsed_time; // Jump to the step we should repeat. if (step_backward) step_id <= step_id + ~step_next; else step_id <= step_id + step_next; end // If we are not repeating, or repeat has completed... else begin elapsed_time <= elapsed_time + 1; // Move to following step. step_id <= step_id + `BLOCK_ROM_INIT_ADDR_WIDTH'h1; end end // Else continue waiting for SPI command to complete. end // Else we are not waiting for SPI command to complete... else begin internal_state_machine <= 1'b0; // If we are still repeating... if (repeat_count) begin // Restore the elapsed time. elapsed_time <= saved_elapsed_time; // Jump to the step we should repeat. if (step_backward) step_id <= step_id + ~step_next; else step_id <= step_id + step_next; end // If we are not repeating, or repeat has completed... else begin elapsed_time <= elapsed_time + 1; // Move to following step. step_id <= step_id + `BLOCK_ROM_INIT_ADDR_WIDTH'h1; end end end endcase end // If the step start time has not been reached... else begin // Wait until the step start time has elapsed. elapsed_time <= elapsed_time + 1; end end endmodule