S02_CH15_ AXI_OLED 实验
在上一个例子中,主要是以软件功能为主,采用了软件模拟SPI时序进行控制OLED。这样做的好处是灵活,但是牺牲了效率。本章采用的方式是让SPI驱动由Verilog实现,字库也是保存到了PL部分的BRAM中。这种方式是减轻了CPU负担,提高了CPU效率。缺点是没有上一章的方法灵活。
15.1 自定义IP的封装
Step1:新建一个名为Miz_sys空的工程。
Step2:选择Tools Create and Package IP 创建IP
Step3:单击NEXT
Step4:由于我们需要挂在到总线上,因此创建一个带AXI总线的用户IP
Step5:设置IP的名字为SSD1306_OLED_ML版本号默认,并且记住IP的位置
Step6:设置总线形式为Lite总线,Lite总线是简化的AXI总线消耗的资源少,当然性能也是比完全版的AXI总线差一点,但是由于音频的速度并不高,因此采用Lite总线就够了,设置寄存器数量为17,因为后面我们需要用到17个寄存器。
Step7:选择Edit IP单击Finish完成
15.2 SSD1306_OLED_ML用户IP的修改
IP创建完成后,并不能立马使用,还需要做一些修改。
Step1:打开SSD1306_OLED_ML.v文件在以下位置修改:
Step2:用以下程序替代SSD1306_OLED_ML_v1_0_S00_AXI.v。
`timescale 1 ns / 1 ps ////////////////////////////////////////////////////////////////////////////////// // // // Create Date: 06:13:25 08/18/2014 // Module Name: SSD1306_OLED_v1_0_S00_AXI // Project Name: SSD1306_OLED // Target Devices: Zynq // Tool versions: Vivado 14.2 (64-bits) // Description: The core is a slave AXI peripheral with 17 software-accessed registers. // registers 0-16 are used for data, register 17 is the control register // // Revision: 1.0 - SSD1306_OLED_v1_0_S00_AXI completed // Revision 0.01 - File Created // ////////////////////////////////////////////////////////////////////////////////// module SSD1306_OLED_v1_0_S00_AXI # ( // Width of S_AXI data bus parameter integer C_S_AXI_DATA_WIDTH = 32, // Width of S_AXI address bus parameter integer C_S_AXI_ADDR_WIDTH = 7 ) ( // Interface with the SSD1306 starts here //SPI Data In (MOSI) output SDIN, //SPI Clock output SCLK, //Data_Command Control output DC, //Power Reset output RES, //Battery Voltage Control - connected to field-effect transistors-active low output VBAT, // Logic Voltage Control - connected to field-effect transistors-active low output VDD, // Interface with the SSD1306 ends here // Global Clock Signal input wire S_AXI_ACLK, // Global Reset Signal. This Signal is Active LOW input wire S_AXI_ARESETN, // Write address (issued by master, acceped by Slave) input wire [C_S_AXI_ADDR_WIDTH-1 : 0] S_AXI_AWADDR, // Write channel Protection type. This signal indicates the // privilege and security level of the transaction, and whether // the transaction is a data access or an instruction access. input wire [2 : 0] S_AXI_AWPROT, // Write address valid. This signal indicates that the master signaling // valid write address and control information. input wire S_AXI_AWVALID, // Write address ready. This signal indicates that the slave is ready // to accept an address and associated control signals. output wire S_AXI_AWREADY, // Write data (issued by master, acceped by Slave) input wire [C_S_AXI_DATA_WIDTH-1 : 0] S_AXI_WDATA, // Write strobes. This signal indicates which byte lanes hold // valid data. There is one write strobe bit for each eight // bits of the write data bus. input wire [(C_S_AXI_DATA_WIDTH/8)-1 : 0] S_AXI_WSTRB, // Write valid. This signal indicates that valid write // data and strobes are available. input wire S_AXI_WVALID, // Write ready. This signal indicates that the slave // can accept the write data. output wire S_AXI_WREADY, // Write response. This signal indicates the status // of the write transaction. output wire [1 : 0] S_AXI_BRESP, // Write response valid. This signal indicates that the channel // is signaling a valid write response. output wire S_AXI_BVALID, // Response ready. This signal indicates that the master // can accept a write response. input wire S_AXI_BREADY, // Read address (issued by master, acceped by Slave) input wire [C_S_AXI_ADDR_WIDTH-1 : 0] S_AXI_ARADDR, // Protection type. This signal indicates the privilege // and security level of the transaction, and whether the // transaction is a data access or an instruction access. input wire [2 : 0] S_AXI_ARPROT, // Read address valid. This signal indicates that the channel // is signaling valid read address and control information. input wire S_AXI_ARVALID, // Read address ready. This signal indicates that the slave is // ready to accept an address and associated control signals. output wire S_AXI_ARREADY, // Read data (issued by slave) output wire [C_S_AXI_DATA_WIDTH-1 : 0] S_AXI_RDATA, // Read response. This signal indicates the status of the // read transfer. output wire [1 : 0] S_AXI_RRESP, // Read valid. This signal indicates that the channel is // signaling the required read data. output wire S_AXI_RVALID, // Read ready. This signal indicates that the master can // accept the read data and response information. input wire S_AXI_RREADY ); // AXI4LITE signals reg [C_S_AXI_ADDR_WIDTH-1 : 0] axi_awaddr; reg axi_awready; reg axi_wready; reg [1 : 0] axi_bresp; reg axi_bvalid; reg [C_S_AXI_ADDR_WIDTH-1 : 0] axi_araddr; reg axi_arready; reg [C_S_AXI_DATA_WIDTH-1 : 0] axi_rdata; reg [1 : 0] axi_rresp; reg axi_rvalid; // Example-specific design signals // local parameter for addressing 32 bit / 64 bit C_S_AXI_DATA_WIDTH // ADDR_LSB is used for addressing 32/64 bit registers/memories // ADDR_LSB = 2 for 32 bits (n downto 2) // ADDR_LSB = 3 for 64 bits (n downto 3) localparam integer ADDR_LSB = (C_S_AXI_DATA_WIDTH/32) + 1; localparam integer OPT_MEM_ADDR_BITS = 4; //---------------------------------------------- //-- Signals for user logic register space example //------------------------------------------------ //-- Number of Slave Registers 17 reg [C_S_AXI_DATA_WIDTH-1:0] slv_reg0; reg [C_S_AXI_DATA_WIDTH-1:0] slv_reg1; reg [C_S_AXI_DATA_WIDTH-1:0] slv_reg2; reg [C_S_AXI_DATA_WIDTH-1:0] slv_reg3; reg [C_S_AXI_DATA_WIDTH-1:0] slv_reg4; reg [C_S_AXI_DATA_WIDTH-1:0] slv_reg5; reg [C_S_AXI_DATA_WIDTH-1:0] slv_reg6; reg [C_S_AXI_DATA_WIDTH-1:0] slv_reg7; reg [C_S_AXI_DATA_WIDTH-1:0] slv_reg8; reg [C_S_AXI_DATA_WIDTH-1:0] slv_reg9; reg [C_S_AXI_DATA_WIDTH-1:0] slv_reg10; reg [C_S_AXI_DATA_WIDTH-1:0] slv_reg11; reg [C_S_AXI_DATA_WIDTH-1:0] slv_reg12; reg [C_S_AXI_DATA_WIDTH-1:0] slv_reg13; reg [C_S_AXI_DATA_WIDTH-1:0] slv_reg14; reg [C_S_AXI_DATA_WIDTH-1:0] slv_reg15; reg [C_S_AXI_DATA_WIDTH-1:0] slv_reg16; wire slv_reg_rden; wire slv_reg_wren; reg [C_S_AXI_DATA_WIDTH-1:0] reg_data_out; integer byte_index; // I/O Connections assignments assign S_AXI_AWREADY = axi_awready; assign S_AXI_WREADY = axi_wready; assign S_AXI_BRESP = axi_bresp; assign S_AXI_BVALID = axi_bvalid; assign S_AXI_ARREADY = axi_arready; assign S_AXI_RDATA = axi_rdata; assign S_AXI_RRESP = axi_rresp; assign S_AXI_RVALID = axi_rvalid; // Implement axi_awready generation // axi_awready is asserted for one S_AXI_ACLK clock cycle when both // S_AXI_AWVALID and S_AXI_WVALID are asserted. axi_awready is // de-asserted when reset is low. // =========================================================================== // Parameters, Regsiters, and Wires // =========================================================================== //Current overall state of the state machine reg [143:0] current_state; //State to go to after the SPI transmission is finished reg [111:0] after_state; //State to go to after the set page sequence reg [142:0] after_page_state; //State to go to after sending the character sequence reg [95:0] after_char_state; //State to go to after the UpdateScreen is finished reg [39:0] after_update_state; //Variable that contains what the screen will be after the next UpdateScreen state reg [7:0] current_screen[0:3][0:15]; //Variable assigned to the SSD1306 interface reg temp_dc = 1'b0; reg temp_res = 1'b1; reg temp_vbat = 1'b1; reg temp_vdd = 1'b1; assign DC = temp_dc; assign RES = temp_res; assign VBAT = temp_vbat; assign VDD = temp_vdd; //-------------- Variables used in the Delay Controller Block -------------- wire [11:0] temp_delay_ms; //amount of ms to delay reg temp_delay_en = 1'b0; //Enable signal for the delay block wire temp_delay_fin; //Finish signal for the delay block assign temp_delay_ms = (after_state == "DispContrast1") ? 12'h074 : 12'h014; //-------------- Variables used in the SPI controller block ---------------- reg temp_spi_en = 1'b0; //Enable signal for the SPI block reg [7:0] temp_spi_data = 8'h00; //Data to be sent out on SPI wire temp_spi_fin; //Finish signal for the SPI block //-------------- Variables used in the characters libtray ---------------- reg [7:0] temp_char; //Contains ASCII value for character reg [10:0] temp_addr; //Contains address to BYTE needed in memory wire [7:0] temp_dout; //Contains byte outputted from memory reg [1:0] temp_page; //Current page reg [3:0] temp_index; //Current character on page //-------------- Variables used in the reset and synchronization circuitry ---------------- reg init_first_r = 1'b1; // Initilaize only one time reg clear_screen_i = 1'b1; // Clear the screen on start up reg ready = 1'b0; // Ready flag reg RST_internal =1'b1; reg[11:0] count =12'h000; wire RST_IN; wire RST=1'b0; // dummy wire - can be connected as a port to provide external reset to the circuit integer i = 0; integer j = 0; assign RST_IN = (RST || RST_internal); //-------------- Core commands assignments start ---------------- wire Display_c; wire Clear_c; assign Display_c = slv_reg16[0]; assign Clear_c =slv_reg16[1]; //-------------- Core commands assignments end ---------------- always @( posedge S_AXI_ACLK ) begin if ( S_AXI_ARESETN == 1'b0 ) begin axi_awready <= 1'b0; end else begin if (~axi_awready && S_AXI_AWVALID && S_AXI_WVALID) begin // slave is ready to accept write address when // there is a valid write address and write data // on the write address and data bus. This design // expects no outstanding transactions. axi_awready <= 1'b1; end else begin axi_awready <= 1'b0; end end end // Implement axi_awaddr latching // This process is used to latch the address when both // S_AXI_AWVALID and S_AXI_WVALID are valid. always @( posedge S_AXI_ACLK ) begin if ( S_AXI_ARESETN == 1'b0 ) begin axi_awaddr <= 0; end else begin if (~axi_awready && S_AXI_AWVALID && S_AXI_WVALID) begin // Write Address latching axi_awaddr <= S_AXI_AWADDR; end end end // Implement axi_wready generation // axi_wready is asserted for one S_AXI_ACLK clock cycle when both // S_AXI_AWVALID and S_AXI_WVALID are asserted. axi_wready is // de-asserted when reset is low. always @( posedge S_AXI_ACLK ) begin if ( S_AXI_ARESETN == 1'b0 ) begin axi_wready <= 1'b0; end else begin if (~axi_wready && S_AXI_WVALID && S_AXI_AWVALID) begin // slave is ready to accept write data when // there is a valid write address and write data // on the write address and data bus. This design // expects no outstanding transactions. axi_wready <= 1'b1; end else begin axi_wready <= 1'b0; end end end // Implement memory mapped register select and write logic generation // The write data is accepted and written to memory mapped registers when // axi_awready, S_AXI_WVALID, axi_wready and S_AXI_WVALID are asserted. Write strobes are used to // select byte enables of slave registers while writing. // These registers are cleared when reset (active low) is applied. // Slave register write enable is asserted when valid address and data are available // and the slave is ready to accept the write address and write data. assign slv_reg_wren = axi_wready && S_AXI_WVALID && axi_awready && S_AXI_AWVALID; always @( posedge S_AXI_ACLK ) begin if ( S_AXI_ARESETN == 1'b0 ) begin slv_reg0 <= 0; slv_reg1 <= 0; slv_reg2 <= 0; slv_reg3 <= 0; slv_reg4 <= 0; slv_reg5 <= 0; slv_reg6 <= 0; slv_reg7 <= 0; slv_reg8 <= 0; slv_reg9 <= 0; slv_reg10 <= 0; slv_reg11 <= 0; slv_reg12 <= 0; slv_reg13 <= 0; slv_reg14 <= 0; slv_reg15 <= 0; slv_reg16 <= 0; end else begin if (slv_reg_wren) begin case ( axi_awaddr[ADDR_LSB+OPT_MEM_ADDR_BITS:ADDR_LSB] ) 5'h00: for ( byte_index = 0; byte_index <= (C_S_AXI_DATA_WIDTH/8)-1; byte_index = byte_index+1 ) if ( S_AXI_WSTRB[byte_index] == 1 ) begin // Respective byte enables are asserted as per write strobes // Slave register 0 slv_reg0[(byte_index*8) +: 8] <= S_AXI_WDATA[(byte_index*8) +: 8]; end 5'h01: for ( byte_index = 0; byte_index <= (C_S_AXI_DATA_WIDTH/8)-1; byte_index = byte_index+1 ) if ( S_AXI_WSTRB[byte_index] == 1 ) begin // Respective byte enables are asserted as per write strobes // Slave register 1 slv_reg1[(byte_index*8) +: 8] <= S_AXI_WDATA[(byte_index*8) +: 8]; end 5'h02: for ( byte_index = 0; byte_index <= (C_S_AXI_DATA_WIDTH/8)-1; byte_index = byte_index+1 ) if ( S_AXI_WSTRB[byte_index] == 1 ) begin // Respective byte enables are asserted as per write strobes // Slave register 2 slv_reg2[(byte_index*8) +: 8] <= S_AXI_WDATA[(byte_index*8) +: 8]; end 5'h03: for ( byte_index = 0; byte_index <= (C_S_AXI_DATA_WIDTH/8)-1; byte_index = byte_index+1 ) if ( S_AXI_WSTRB[byte_index] == 1 ) begin // Respective byte enables are asserted as per write strobes // Slave register 3 slv_reg3[(byte_index*8) +: 8] <= S_AXI_WDATA[(byte_index*8) +: 8]; end 5'h04: for ( byte_index = 0; byte_index <= (C_S_AXI_DATA_WIDTH/8)-1; byte_index = byte_index+1 ) if ( S_AXI_WSTRB[byte_index] == 1 ) begin // Respective byte enables are asserted as per write strobes // Slave register 4 slv_reg4[(byte_index*8) +: 8] <= S_AXI_WDATA[(byte_index*8) +: 8]; end 5'h05: for ( byte_index = 0; byte_index <= (C_S_AXI_DATA_WIDTH/8)-1; byte_index = byte_index+1 ) if ( S_AXI_WSTRB[byte_index] == 1 ) begin // Respective byte enables are asserted as per write strobes // Slave register 5 slv_reg5[(byte_index*8) +: 8] <= S_AXI_WDATA[(byte_index*8) +: 8]; end 5'h06: for ( byte_index = 0; byte_index <= (C_S_AXI_DATA_WIDTH/8)-1; byte_index = byte_index+1 ) if ( S_AXI_WSTRB[byte_index] == 1 ) begin // Respective byte enables are asserted as per write strobes // Slave register 6 slv_reg6[(byte_index*8) +: 8] <= S_AXI_WDATA[(byte_index*8) +: 8]; end 5'h07: for ( byte_index = 0; byte_index <= (C_S_AXI_DATA_WIDTH/8)-1; byte_index = byte_index+1 ) if ( S_AXI_WSTRB[byte_index] == 1 ) begin // Respective byte enables are asserted as per write strobes // Slave register 7 slv_reg7[(byte_index*8) +: 8] <= S_AXI_WDATA[(byte_index*8) +: 8]; end 5'h08: for ( byte_index = 0; byte_index <= (C_S_AXI_DATA_WIDTH/8)-1; byte_index = byte_index+1 ) if ( S_AXI_WSTRB[byte_index] == 1 ) begin // Respective byte enables are asserted as per write strobes // Slave register 8 slv_reg8[(byte_index*8) +: 8] <= S_AXI_WDATA[(byte_index*8) +: 8]; end 5'h09: for ( byte_index = 0; byte_index <= (C_S_AXI_DATA_WIDTH/8)-1; byte_index = byte_index+1 ) if ( S_AXI_WSTRB[byte_index] == 1 ) begin // Respective byte enables are asserted as per write strobes // Slave register 9 slv_reg9[(byte_index*8) +: 8] <= S_AXI_WDATA[(byte_index*8) +: 8]; end 5'h0A: for ( byte_index = 0; byte_index <= (C_S_AXI_DATA_WIDTH/8)-1; byte_index = byte_index+1 ) if ( S_AXI_WSTRB[byte_index] == 1 ) begin // Respective byte enables are asserted as per write strobes // Slave register 10 slv_reg10[(byte_index*8) +: 8] <= S_AXI_WDATA[(byte_index*8) +: 8]; end 5'h0B: for ( byte_index = 0; byte_index <= (C_S_AXI_DATA_WIDTH/8)-1; byte_index = byte_index+1 ) if ( S_AXI_WSTRB[byte_index] == 1 ) begin // Respective byte enables are asserted as per write strobes // Slave register 11 slv_reg11[(byte_index*8) +: 8] <= S_AXI_WDATA[(byte_index*8) +: 8]; end 5'h0C: for ( byte_index = 0; byte_index <= (C_S_AXI_DATA_WIDTH/8)-1; byte_index = byte_index+1 ) if ( S_AXI_WSTRB[byte_index] == 1 ) begin // Respective byte enables are asserted as per write strobes // Slave register 12 slv_reg12[(byte_index*8) +: 8] <= S_AXI_WDATA[(byte_index*8) +: 8]; end 5'h0D: for ( byte_index = 0; byte_index <= (C_S_AXI_DATA_WIDTH/8)-1; byte_index = byte_index+1 ) if ( S_AXI_WSTRB[byte_index] == 1 ) begin // Respective byte enables are asserted as per write strobes // Slave register 13 slv_reg13[(byte_index*8) +: 8] <= S_AXI_WDATA[(byte_index*8) +: 8]; end 5'h0E: for ( byte_index = 0; byte_index <= (C_S_AXI_DATA_WIDTH/8)-1; byte_index = byte_index+1 ) if ( S_AXI_WSTRB[byte_index] == 1 ) begin // Respective byte enables are asserted as per write strobes // Slave register 14 slv_reg14[(byte_index*8) +: 8] <= S_AXI_WDATA[(byte_index*8) +: 8]; end 5'h0F: for ( byte_index = 0; byte_index <= (C_S_AXI_DATA_WIDTH/8)-1; byte_index = byte_index+1 ) if ( S_AXI_WSTRB[byte_index] == 1 ) begin // Respective byte enables are asserted as per write strobes // Slave register 15 slv_reg15[(byte_index*8) +: 8] <= S_AXI_WDATA[(byte_index*8) +: 8]; end 5'h10: for ( byte_index = 0; byte_index <= (C_S_AXI_DATA_WIDTH/8)-1; byte_index = byte_index+1 ) if ( S_AXI_WSTRB[byte_index] == 1 ) begin // Respective byte enables are asserted as per write strobes // Slave register 16 slv_reg16[(byte_index*8) +: 8] <= S_AXI_WDATA[(byte_index*8) +: 8]; end default : begin slv_reg0 <= slv_reg0; slv_reg1 <= slv_reg1; slv_reg2 <= slv_reg2; slv_reg3 <= slv_reg3; slv_reg4 <= slv_reg4; slv_reg5 <= slv_reg5; slv_reg6 <= slv_reg6; slv_reg7 <= slv_reg7; slv_reg8 <= slv_reg8; slv_reg9 <= slv_reg9; slv_reg10 <= slv_reg10; slv_reg11 <= slv_reg11; slv_reg12 <= slv_reg12; slv_reg13 <= slv_reg13; slv_reg14 <= slv_reg14; slv_reg15 <= slv_reg15; slv_reg16 <= slv_reg16; end endcase end end end // Implement write response logic generation // The write response and response valid signals are asserted by the slave // when axi_wready, S_AXI_WVALID, axi_wready and S_AXI_WVALID are asserted. // This marks the acceptance of address and indicates the status of // write transaction. always @( posedge S_AXI_ACLK ) begin if ( S_AXI_ARESETN == 1'b0 ) begin axi_bvalid <= 0; axi_bresp <= 2'b0; end else begin if (axi_awready && S_AXI_AWVALID && ~axi_bvalid && axi_wready && S_AXI_WVALID) begin // indicates a valid write response is available axi_bvalid <= 1'b1; axi_bresp <= 2'b0; // 'OKAY' response end // work error responses in future else begin if (S_AXI_BREADY && axi_bvalid) //check if bready is asserted while bvalid is high) //(there is a possibility that bready is always asserted high) begin axi_bvalid <= 1'b0; end end end end // Implement axi_arready generation // axi_arready is asserted for one S_AXI_ACLK clock cycle when // S_AXI_ARVALID is asserted. axi_awready is // de-asserted when reset (active low) is asserted. // The read address is also latched when S_AXI_ARVALID is // asserted. axi_araddr is reset to zero on reset assertion. always @( posedge S_AXI_ACLK ) begin if ( S_AXI_ARESETN == 1'b0 ) begin axi_arready <= 1'b0; axi_araddr <= 32'b0; end else begin if (~axi_arready && S_AXI_ARVALID) begin // indicates that the slave has acceped the valid read address axi_arready <= 1'b1; // Read address latching axi_araddr <= S_AXI_ARADDR; end else begin axi_arready <= 1'b0; end end end // Implement axi_arvalid generation // axi_rvalid is asserted for one S_AXI_ACLK clock cycle when both // S_AXI_ARVALID and axi_arready are asserted. The slave registers // data are available on the axi_rdata bus at this instance. The // assertion of axi_rvalid marks the validity of read data on the // bus and axi_rresp indicates the status of read transaction.axi_rvalid // is deasserted on reset (active low). axi_rresp and axi_rdata are // cleared to zero on reset (active low). always @( posedge S_AXI_ACLK ) begin if ( S_AXI_ARESETN == 1'b0 ) begin axi_rvalid <= 0; axi_rresp <= 0; end else begin if (axi_arready && S_AXI_ARVALID && ~axi_rvalid) begin // Valid read data is available at the read data bus axi_rvalid <= 1'b1; axi_rresp <= 2'b0; // 'OKAY' response end else if (axi_rvalid && S_AXI_RREADY) begin // Read data is accepted by the master axi_rvalid <= 1'b0; end end end // Implement memory mapped register select and read logic generation // Slave register read enable is asserted when valid address is available // and the slave is ready to accept the read address. assign slv_reg_rden = axi_arready & S_AXI_ARVALID & ~axi_rvalid; always @(*) begin if ( S_AXI_ARESETN == 1'b0 ) begin reg_data_out <= 0; end else begin // Address decoding for reading registers case ( axi_araddr[ADDR_LSB+OPT_MEM_ADDR_BITS:ADDR_LSB] ) 5'h00 : reg_data_out <= slv_reg0; 5'h01 : reg_data_out <= slv_reg1; 5'h02 : reg_data_out <= slv_reg2; 5'h03 : reg_data_out <= slv_reg3; 5'h04 : reg_data_out <= slv_reg4; 5'h05 : reg_data_out <= slv_reg5; 5'h06 : reg_data_out <= slv_reg6; 5'h07 : reg_data_out <= slv_reg7; 5'h08 : reg_data_out <= slv_reg8; 5'h09 : reg_data_out <= slv_reg9; 5'h0A : reg_data_out <= slv_reg10; 5'h0B : reg_data_out <= slv_reg11; 5'h0C : reg_data_out <= slv_reg12; 5'h0D : reg_data_out <= slv_reg13; 5'h0E : reg_data_out <= slv_reg14; 5'h0F : reg_data_out <= slv_reg15; 5'h10 : reg_data_out <= slv_reg16; default : reg_data_out <= 0; endcase end end // Output register or memory read data always @( posedge S_AXI_ACLK ) begin if ( S_AXI_ARESETN == 1'b0 ) begin axi_rdata <= 0; end else begin // When there is a valid read address (S_AXI_ARVALID) with // acceptance of read address by the slave (axi_arready), // output the read dada if (slv_reg_rden) begin axi_rdata <= reg_data_out; // register read data end end end // =========================================================================== // Implementation // =========================================================================== SpiCtrl SPI_COMP( .CLK(S_AXI_ACLK), .RST(RST_IN), .SPI_EN(temp_spi_en), .SPI_DATA(temp_spi_data), .SDO(SDIN), .SCLK(SCLK), .SPI_FIN(temp_spi_fin) ); Delay DELAY_COMP( .CLK(S_AXI_ACLK), .RST(RST_IN), .DELAY_MS(temp_delay_ms), .DELAY_EN(temp_delay_en), .DELAY_FIN(temp_delay_fin) ); charLib CHAR_LIB_COMP( .clka(S_AXI_ACLK), .addra(temp_addr), .douta(temp_dout) ); // State Machine always @(posedge S_AXI_ACLK) begin if(RST_IN == 1'b1) begin current_state <= "Idle"; temp_res <= 1'b0; end else begin temp_res <= 1'b1; case(current_state) // Idle State "Idle" : begin if(init_first_r == 1'b1) begin temp_dc <= 1'b0; // DC= 0 "Commands" , DC=1 "Data" current_state <= "VddOn"; init_first_r <= 1'b0; // Don't go over the initialization more than once end else begin current_state <="WaitRequest"; end end // Initialization Sequence // This should be done only one time when Zedboard starts "VddOn" : begin // turn the power on the logic of the display temp_vdd <= 1'b0; // remember the power FET transistor for VDD is active low current_state <= "Wait1"; end // 3 "Wait1" : begin after_state <= "DispOff"; current_state <= "Transition3"; end // 4 "DispOff" : begin temp_spi_data <= 8'hAE; // 0xAE= Set Display OFF after_state <= "SetClockDiv1"; current_state <= "Transition1"; end // 5 "SetClockDiv1" : begin temp_spi_data <= 8'hD5; //0xD5 after_state <= "SetClockDiv2"; current_state <= "Transition1"; end // 6 "SetClockDiv2" : begin temp_spi_data <= 8'h80; // 0x80 after_state <= "MultiPlex1"; current_state <= "Transition1"; end // 7 "MultiPlex1" : begin temp_spi_data <= 8'hA8; //0xA8 after_state <= "MultiPlex2"; current_state <= "Transition1"; end // 8 "MultiPlex2" : begin temp_spi_data <= 8'h1F; // 0x1F after_state <= "ChargePump1"; current_state <= "Transition1"; end // 9 "ChargePump1" : begin // Access Charge Pump Setting temp_spi_data <= 8'h8D; //0x8D after_state <= "ChargePump2"; current_state <= "Transition1"; end // 10 "ChargePump2" : begin // Enable Charge Pump temp_spi_data <= 8'h14; // 0x14 after_state <= "PreCharge1"; current_state <= "Transition1"; end // 11 "PreCharge1" : begin // Access Pre-charge Period Setting temp_spi_data <= 8'hD9; // 0xD9 after_state <= "PreCharge2"; current_state <= "Transition1"; end // 12 "PreCharge2" : begin //Set the Pre-charge Period temp_spi_data <= 8'hFF; // 0xF1 after_state <= "VCOMH1"; current_state <= "Transition1"; end // 13 "VCOMH1" : begin //Set the Pre-charge Period temp_spi_data <= 8'hDB; // 0xF1 after_state <= "VCOMH2"; current_state <= "Transition1"; end // 14 "VCOMH2" : begin //Set the Pre-charge Period temp_spi_data <= 8'h40; // 0xF1 after_state <= "DispContrast1"; current_state <= "Transition1"; end // 15 "DispContrast1" : begin //Set Contrast Control for BANK0 temp_spi_data <= 8'h81; // 0x81 after_state <= "DispContrast2"; current_state <= "Transition1"; end // 16 "DispContrast2" : begin temp_spi_data <= 8'hF1; // 0x0F after_state <= "InvertDisp1"; current_state <= "Transition1"; end // 17 "InvertDisp1" : begin temp_spi_data <= 8'hA0; // 0xA1 after_state <= "InvertDisp2"; current_state <= "Transition1"; end // 18 "InvertDisp2" : begin temp_spi_data <= 8'hC0; // 0xC0 after_state <= "ComConfig1"; current_state <= "Transition1"; end // 19 "ComConfig1" : begin temp_spi_data <= 8'hDA; // 0xDA after_state <= "ComConfig2"; current_state <= "Transition1"; end // 20 "ComConfig2" : begin temp_spi_data <= 8'h02; // 0x02 after_state <= "VbatOn"; current_state <= "Transition1"; end // 21 "VbatOn" : begin temp_vbat <= 1'b0; current_state <= "Wait3"; end // 22 "Wait3" : begin after_state <= "ResetOn"; current_state <= "Transition3"; end // 23 "ResetOn" : begin temp_res <= 1'b0; current_state <= "Wait2"; end // 24 "Wait2" : begin after_state <= "ResetOff"; current_state <= "Transition3"; end // 25 "ResetOff" : begin temp_res <= 1'b1; current_state <= "WaitRequest"; end // ************ END Initialization sequence but without turnning the dispay on ************ // Main state "WaitRequest" : begin if(Display_c == 1'b1) begin current_state <= "ClearDC"; after_page_state <= "ReadRegisters"; temp_page <= 2'b00; end else if ((Clear_c==1'b1) || (clear_screen_i == 1'b1)) begin current_state <= "ClearDC"; after_page_state <= "ClearScreen"; temp_page <= 2'b00; end else begin current_state<="WaitRequest"; // keep looping in the WaitRequest state untill you receive a command if ((clear_screen_i == 1'b0) && (ready ==1'b0)) begin // this part is only executed once, on start-up temp_spi_data <= 8'hAF; // 0xAF // Dispaly ON after_state <= "WaitRequest"; current_state <= "Transition1"; temp_dc<=1'b0; ready <= 1'b1; end end end //Update Page states //1. Sets DC to command mode //2. Sends the SetPage Command //3. Sends the Page to be set to //4. Sets the start pixel to the left column //5. Sets DC to data mode "ClearDC" : begin temp_dc <= 1'b0; current_state <= "SetPage"; end "SetPage" : begin temp_spi_data <= 8'b00100010; after_state <= "PageNum"; current_state <= "Transition1"; end "PageNum" : begin temp_spi_data <= {6'b000000,temp_page}; after_state <= "LeftColumn1"; current_state <= "Transition1"; end "LeftColumn1" : begin temp_spi_data <= 8'b00000000; after_state <= "LeftColumn2"; current_state <= "Transition1"; end "LeftColumn2" : begin temp_spi_data <= 8'b00010000; after_state <= "SetDC"; current_state <= "Transition1"; end "SetDC" : begin temp_dc <= 1'b1; current_state <= after_page_state; end "ClearScreen" : begin for(i = 0; i <= 3 ; i=i+1) begin for(j = 0; j <= 15 ; j=j+1) begin current_screen[i][j] <= 8'h20; end end after_update_state <= "WaitRequest"; current_state <= "UpdateScreen"; end "ReadRegisters" : begin // Page0 current_screen[0][0]<=slv_reg0[7:0]; current_screen[0][1]<=slv_reg0[15:8]; current_screen[0][2]<=slv_reg0[23:16]; current_screen[0][3]<=slv_reg0[31:24]; current_screen[0][4]<=slv_reg1[7:0]; current_screen[0][5]<=slv_reg1[15:8]; current_screen[0][6]<=slv_reg1[23:16]; current_screen[0][7]<=slv_reg1[31:24]; current_screen[0][8]<=slv_reg2[7:0]; current_screen[0][9]<=slv_reg2[15:8]; current_screen[0][10]<=slv_reg2[23:16]; current_screen[0][11]<=slv_reg2[31:24]; current_screen[0][12]<=slv_reg3[7:0]; current_screen[0][13]<=slv_reg3[15:8]; current_screen[0][14]<=slv_reg3[23:16]; current_screen[0][15]<=slv_reg3[31:24]; //Page1 current_screen[1][0]<=slv_reg4[7:0]; current_screen[1][1]<=slv_reg4[15:8]; current_screen[1][2]<=slv_reg4[23:16]; current_screen[1][3]<=slv_reg4[31:24]; current_screen[1][4]<=slv_reg5[7:0]; current_screen[1][5]<=slv_reg5[15:8]; current_screen[1][6]<=slv_reg5[23:16]; current_screen[1][7]<=slv_reg5[31:24]; current_screen[1][8]<=slv_reg6[7:0]; current_screen[1][9]<=slv_reg6[15:8]; current_screen[1][10]<=slv_reg6[23:16]; current_screen[1][11]<=slv_reg6[31:24]; current_screen[1][12]<=slv_reg7[7:0]; current_screen[1][13]<=slv_reg7[15:8]; current_screen[1][14]<=slv_reg7[23:16]; current_screen[1][15]<=slv_reg7[31:24]; //Page2 current_screen[2][0]<=slv_reg8[7:0]; current_screen[2][1]<=slv_reg8[15:8]; current_screen[2][2]<=slv_reg8[23:16]; current_screen[2][3]<=slv_reg8[31:24]; current_screen[2][4]<=slv_reg9[7:0]; current_screen[2][5]<=slv_reg9[15:8]; current_screen[2][6]<=slv_reg9[23:16]; current_screen[2][7]<=slv_reg9[31:24]; current_screen[2][8]<=slv_reg10[7:0]; current_screen[2][9]<=slv_reg10[15:8]; current_screen[2][10]<=slv_reg10[23:16]; current_screen[2][11]<=slv_reg10[31:24]; current_screen[2][12]<=slv_reg11[7:0]; current_screen[2][13]<=slv_reg11[15:8]; current_screen[2][14]<=slv_reg11[23:16]; current_screen[2][15]<=slv_reg11[31:24]; //Page3 current_screen[3][0]<=slv_reg12[7:0]; current_screen[3][1]<=slv_reg12[15:8]; current_screen[3][2]<=slv_reg12[23:16]; current_screen[3][3]<=slv_reg12[31:24]; current_screen[3][4]<=slv_reg13[7:0]; current_screen[3][5]<=slv_reg13[15:8]; current_screen[3][6]<=slv_reg13[23:16]; current_screen[3][7]<=slv_reg13[31:24]; current_screen[3][8]<=slv_reg14[7:0]; current_screen[3][9]<=slv_reg14[15:8]; current_screen[3][10]<=slv_reg14[23:16]; current_screen[3][11]<=slv_reg14[31:24]; current_screen[3][12]<=slv_reg15[7:0]; current_screen[3][13]<=slv_reg15[15:8]; current_screen[3][14]<=slv_reg15[23:16]; current_screen[3][15]<=slv_reg15[31:24]; after_update_state <= "WaitRequest"; current_state <= "UpdateScreen"; end //UpdateScreen State //1. Gets ASCII value from current_screen at the current page and the current spot of the page //2. If on the last character of the page transition update the page number, if on the last page(3) // then the updateScreen go to "after_update_state" after "UpdateScreen" : begin temp_char <= current_screen[temp_page][temp_index]; if(temp_index == 'd15) begin temp_index <= 'd0; temp_page <= temp_page + 1'b1; after_char_state <= "ClearDC"; if(temp_page == 2'b11) begin after_page_state <= after_update_state; clear_screen_i<=1'b0; end else begin after_page_state <= "UpdateScreen"; end end else begin temp_index <= temp_index + 1'b1; after_char_state <= "UpdateScreen"; end current_state <= "SendChar1"; end //Send Character States //1. Sets the Address to ASCII value of char with the counter appended to the end //2. Waits a clock for the data to get ready by going to ReadMem and ReadMem2 states //3. Send the byte of data given by the block Ram //4. Repeat 7 more times for the rest of the character bytes "SendChar1" : begin temp_addr <= {temp_char, 3'b000}; after_state <= "SendChar2"; current_state <= "ReadMem"; end "SendChar2" : begin temp_addr <= {temp_char, 3'b001}; after_state <= "SendChar3"; current_state <= "ReadMem"; end "SendChar3" : begin temp_addr <= {temp_char, 3'b010}; after_state <= "SendChar4"; current_state <= "ReadMem"; end "SendChar4" : begin temp_addr <= {temp_char, 3'b011}; after_state <= "SendChar5"; current_state <= "ReadMem"; end "SendChar5" : begin temp_addr <= {temp_char, 3'b100}; after_state <= "SendChar6"; current_state <= "ReadMem"; end "SendChar6" : begin temp_addr <= {temp_char, 3'b101}; after_state <= "SendChar7"; current_state <= "ReadMem"; end "SendChar7" : begin temp_addr <= {temp_char, 3'b110}; after_state <= "SendChar8"; current_state <= "ReadMem"; end "SendChar8" : begin temp_addr <= {temp_char, 3'b111}; after_state <= after_char_state; current_state <= "ReadMem"; end "ReadMem" : begin current_state <= "ReadMem2"; end "ReadMem2" : begin temp_spi_data <= temp_dout; current_state <= "Transition1"; end // SPI transitions // 1. Set SPI_EN to 1 // 2. Waits for SpiCtrl to finish // 3. Goes to clear state (Transition5) "Transition1" : begin temp_spi_en <= 1'b1; current_state <= "Transition2"; end "Transition2" : begin if(temp_spi_fin == 1'b1) begin current_state <= "Transition5"; end end // Delay Transitions // 1. Set DELAY_EN to 1 // 2. Waits for Delay to finish // 3. Goes to Clear state (Transition5) "Transition3" : begin temp_delay_en <= 1'b1; current_state <= "Transition4"; end "Transition4" : begin if(temp_delay_fin == 1'b1) begin current_state <= "Transition5"; end end // Clear transition // 1. Sets both DELAY_EN and SPI_EN to 0 // 2. Go to after state "Transition5" : begin temp_spi_en <= 1'b0; temp_delay_en <= 1'b0; current_state <= after_state; end default : current_state <= "Idle"; endcase end end // Internal reset generator always @(posedge S_AXI_ACLK) begin if (RST_IN == 1'b1) count<=count+1'b1; if (count == 12'hFFF) begin RST_internal <=1'b0; end end endmodule |
Step3:添加一个 SPI控制器源码 SpiCtrl.v文件,代码如下所示:
`timescale 1ns / 1ps ////////////////////////////////////////////////////////////////////////////////// // // // // // // Create Date: 12:12:51 08/04/2014 // Module Name: SpiCtrl // Project Name: ZedboardOLED // Target Devices: Zynq // Tool versions: Vivado 14.2 (64-bits) // Description: Spi block that sends SPI data formatted SCLK active low with // SDO changing on the falling edge // // Revision: 1.0 - SPI completed // Revision 0.01 - File Created // ////////////////////////////////////////////////////////////////////////////////// module SpiCtrl( CLK, RST, SPI_EN, SPI_DATA, SDO, SCLK, SPI_FIN ); // =========================================================================== // Port Declarations // =========================================================================== input CLK; input RST; input SPI_EN; input [7:0] SPI_DATA; output SDO; output SCLK; output SPI_FIN; // =========================================================================== // Parameters, Regsiters, and Wires // =========================================================================== wire SDO, SCLK, SPI_FIN; reg [39:0] current_state = "Idle"; // Signal for state machine reg [7:0] shift_register = 8'h00; // Shift register to shift out SPI_DATA saved when SPI_EN was set reg [3:0] shift_counter = 4'h0; // Keeps track how many bits were sent wire clk_divided; // Used as SCLK reg [4:0] counter = 5'b00000; // Count clocks to be used to divide CLK reg temp_sdo = 1'b1; // Tied to SDO reg falling = 1'b0; // signal indicating that the clk has just fell // =========================================================================== // Implementation // =========================================================================== assign clk_divided = ~counter[4]; assign SCLK = clk_divided; assign SDO = temp_sdo; assign SPI_FIN = (current_state == "Done") ? 1'b1 : 1'b0; // State Machine always @(posedge CLK) begin if(RST == 1'b1) begin // Synchronous RST current_state <= "Idle"; end else begin case(current_state) // Wait for SPI_EN to go high "Idle" : begin if(SPI_EN == 1'b1) begin current_state <= "Send"; end end // Start sending bits, transition out when all bits are sent and SCLK is high "Send" : begin if(shift_counter == 4'h8 && falling == 1'b0) begin current_state <= "Done"; end end // Finish SPI transimission wait for SPI_EN to go low "Done" : begin if(SPI_EN == 1'b0) begin current_state <= "Idle"; end end default : current_state <= "Idle"; endcase end end // End of State Machine // Clock Divider always @(posedge CLK) begin // start clock counter when in send state if(current_state == "Send") begin counter <= counter + 1'b1; end // reset clock counter when not in send state else begin counter <= 5'b00000; end end // End Clock Divider // SPI_SEND_BYTE, sends SPI data formatted SCLK active low with SDO changing on the falling edge always @(posedge CLK) begin if(current_state == "Idle") begin shift_counter <= 4'h0; // keeps placing SPI_DATA into shift_register so that when state goes to send it has the latest SPI_DATA shift_register <= SPI_DATA; temp_sdo <= 1'b1; end else if(current_state == "Send") begin // if on the falling edge of Clk_divided if(clk_divided == 1'b0 && falling == 1'b0) begin // Indicate that it is passed the falling edge falling <= 1'b1; // send out the MSB temp_sdo <= shift_register[7]; // Shift through SPI_DATA shift_register <= {shift_register[6:0],1'b0}; // Keep track of what bit it is on shift_counter <= shift_counter + 1'b1; end // on SCLK high reset the falling flag else if(clk_divided == 1'b1) begin falling <= 1'b0; end end end endmodule |
这是一个很好用的SPI控制器,只要通过设置 SPI_EN,SPI_DATA,信号就能发送数据了,这个代码初学者可以当作一个verilog的例子学习下,仔细分析下SPI的工作时序。
Step4:添加一个毫秒延迟模块 Delay.v文件
`timescale 1ns / 1ps ////////////////////////////////////////////////////////////////////////////////// // // // // // // Create Date: 12:12:51 08/04/2014 // Module Name: Delay // Project Name: ZedboardOLED // Target Devices: Zynq // Tool versions: Vivado 14.2 (64-bits) // Description: Creates a delay of DELAY_MS ms // // Revision: 1.0 // Revision 0.01 - File Created // ////////////////////////////////////////////////////////////////////////////////// module Delay( CLK, RST, DELAY_MS, DELAY_EN, DELAY_FIN ); // =========================================================================== // Port Declarations // =========================================================================== input CLK; input RST; input [11:0] DELAY_MS; input DELAY_EN; output DELAY_FIN; // =========================================================================== // Parameters, Regsiters, and Wires // =========================================================================== wire DELAY_FIN; reg [31:0] current_state = "Idle"; // Signal for state machine reg [16:0] clk_counter = 17'b00000000000000000; // Counts up on every rising edge of CLK reg [11:0] ms_counter = 12'h000; // Counts up when clk_counter = 100,000 // =========================================================================== // Implementation // =========================================================================== assign DELAY_FIN = (current_state == "Done" && DELAY_EN == 1'b1) ? 1'b1 : 1'b0; // State Machine always @(posedge CLK) begin // When RST is asserted switch to idle (synchronous) if(RST == 1'b1) begin current_state <= "Idle"; end else begin case(current_state) "Idle" : begin // Start delay on DELAY_EN if(DELAY_EN == 1'b1) begin current_state <= "Hold"; end end "Hold" : begin // Stay until DELAY_MS has occured if(ms_counter == DELAY_MS) begin current_state <= "Done"; end end "Done" : begin // Wait until DELAY_EN is deasserted to go to IDLE if(DELAY_EN == 1'b0) begin current_state <= "Idle"; end end default : current_state <= "Idle"; endcase end end // End State Machine // Creates ms_counter that counts at 1KHz // CLK_DIV always @(posedge CLK) begin if(current_state == "Hold") begin if(clk_counter == 17'b11000011010100000) begin // 100,000 clk_counter <= 17'b00000000000000000; ms_counter <= ms_counter + 1'b1; // increments at 1KHz end else begin clk_counter <= clk_counter + 1'b1; end end else begin // If not in the hold state reset counters clk_counter <= 17'b00000000000000000; ms_counter <= 12'h000; end end endmodule |
Step5:添加一个Block ROM IP,按下图进行设置。ROM的coe文件可在我们提供的源代码程序包中获得。
Step6:修改完成后重新封装一次自定义IP
Step7:单击NEXT
Step8:和第一次不同,这次选择第一个单选框然后单击NEXT
Step9:选择第一个单选框,然后单击NEXT
Step10:点击Overwrite
Step11:点击Finish 到此自定义IP结束
15.3 OLED硬件控制器关键状态机
always @(posedge S_AXI_ACLK) begin if(RST_IN == 1'b1) begin current_state <= "Idle"; temp_res <= 1'b0; end else begin temp_res <= 1'b1; case(current_state) // Idle State "Idle" : begin if(init_first_r == 1'b1) begin temp_dc <= 1'b0; // DC= 0 "Commands" , DC=1 "Data" current_state <= "VddOn"; init_first_r <= 1'b0; // Don't go over the initialization more than once end else begin current_state <="WaitRequest"; end end // Initialization Sequence // This should be done only one time when Zedboard starts "VddOn" : begin // turn the power on the logic of the display temp_vdd <= 1'b0; // remember the power FET transistor for VDD is active low current_state <= "Wait1"; end // 3 "Wait1" : begin after_state <= "DispOff"; current_state <= "Transition3"; end // 4 "DispOff" : begin temp_spi_data <= 8'hAE; // 0xAE= Set Display OFF after_state <= "SetClockDiv1"; current_state <= "Transition1"; end // 5 "SetClockDiv1" : begin temp_spi_data <= 8'hD5; //0xD5 after_state <= "SetClockDiv2"; current_state <= "Transition1"; end // 6 "SetClockDiv2" : begin temp_spi_data <= 8'h80; // 0x80 after_state <= "MultiPlex1"; current_state <= "Transition1"; end // 7 "MultiPlex1" : begin temp_spi_data <= 8'hA8; //0xA8 after_state <= "MultiPlex2"; current_state <= "Transition1"; end // 8 "MultiPlex2" : begin temp_spi_data <= 8'h1F; // 0x1F after_state <= "ChargePump1"; current_state <= "Transition1"; end // 9 "ChargePump1" : begin // Access Charge Pump Setting temp_spi_data <= 8'h8D; //0x8D after_state <= "ChargePump2"; current_state <= "Transition1"; end // 10 "ChargePump2" : begin // Enable Charge Pump temp_spi_data <= 8'h14; // 0x14 after_state <= "PreCharge1"; current_state <= "Transition1"; end // 11 "PreCharge1" : begin // Access Pre-charge Period Setting temp_spi_data <= 8'hD9; // 0xD9 after_state <= "PreCharge2"; current_state <= "Transition1"; end // 12 "PreCharge2" : begin //Set the Pre-charge Period temp_spi_data <= 8'hFF; // 0xF1 after_state <= "VCOMH1"; current_state <= "Transition1"; end // 13 "VCOMH1" : begin //Set the Pre-charge Period temp_spi_data <= 8'hDB; // 0xF1 after_state <= "VCOMH2"; current_state <= "Transition1"; end // 14 "VCOMH2" : begin //Set the Pre-charge Period temp_spi_data <= 8'h40; // 0xF1 after_state <= "DispContrast1"; current_state <= "Transition1"; end // 15 "DispContrast1" : begin //Set Contrast Control for BANK0 temp_spi_data <= 8'h81; // 0x81 after_state <= "DispContrast2"; current_state <= "Transition1"; end // 16 "DispContrast2" : begin temp_spi_data <= 8'hF1; // 0x0F after_state <= "InvertDisp1"; current_state <= "Transition1"; end // 17 "InvertDisp1" : begin temp_spi_data <= 8'hA0; // 0xA1 after_state <= "InvertDisp2"; current_state <= "Transition1"; end // 18 "InvertDisp2" : begin temp_spi_data <= 8'hC0; // 0xC0 after_state <= "ComConfig1"; current_state <= "Transition1"; end // 19 "ComConfig1" : begin temp_spi_data <= 8'hDA; // 0xDA after_state <= "ComConfig2"; current_state <= "Transition1"; end // 20 "ComConfig2" : begin temp_spi_data <= 8'h02; // 0x02 after_state <= "VbatOn"; current_state <= "Transition1"; end // 21 "VbatOn" : begin temp_vbat <= 1'b0; current_state <= "Wait3"; end // 22 "Wait3" : begin after_state <= "ResetOn"; current_state <= "Transition3"; end // 23 "ResetOn" : begin temp_res <= 1'b0; current_state <= "Wait2"; end // 24 "Wait2" : begin after_state <= "ResetOff"; current_state <= "Transition3"; end // 25 "ResetOff" : begin temp_res <= 1'b1; current_state <= "WaitRequest"; end // ************ END Initialization sequence but without turnning the dispay on ************ // Main state "WaitRequest" : begin if(Display_c == 1'b1) begin current_state <= "ClearDC"; after_page_state <= "ReadRegisters"; temp_page <= 2'b00; end else if ((Clear_c==1'b1) || (clear_screen_i == 1'b1)) begin current_state <= "ClearDC"; after_page_state <= "ClearScreen"; temp_page <= 2'b00; end else begin current_state<="WaitRequest"; // keep looping in the WaitRequest state untill you receive a command if ((clear_screen_i == 1'b0) && (ready ==1'b0)) begin // this part is only executed once, on start-up temp_spi_data <= 8'hAF; // 0xAF // Dispaly ON after_state <= "WaitRequest"; current_state <= "Transition1"; temp_dc<=1'b0; ready <= 1'b1; end end end //Update Page states //1. Sets DC to command mode //2. Sends the SetPage Command //3. Sends the Page to be set to //4. Sets the start pixel to the left column //5. Sets DC to data mode "ClearDC" : begin temp_dc <= 1'b0; current_state <= "SetPage"; end "SetPage" : begin temp_spi_data <= 8'b00100010; after_state <= "PageNum"; current_state <= "Transition1"; end "PageNum" : begin temp_spi_data <= {6'b000000,temp_page}; after_state <= "LeftColumn1"; current_state <= "Transition1"; end "LeftColumn1" : begin temp_spi_data <= 8'b00000000; after_state <= "LeftColumn2"; current_state <= "Transition1"; end "LeftColumn2" : begin temp_spi_data <= 8'b00010000; after_state <= "SetDC"; current_state <= "Transition1"; end "SetDC" : begin temp_dc <= 1'b1; current_state <= after_page_state; end "ClearScreen" : begin for(i = 0; i <= 3 ; i=i+1) begin for(j = 0; j <= 15 ; j=j+1) begin current_screen[i][j] <= 8'h20; end end after_update_state <= "WaitRequest"; current_state <= "UpdateScreen"; end "ReadRegisters" : begin // Page0 current_screen[0][0]<=slv_reg0[7:0]; current_screen[0][1]<=slv_reg0[15:8]; current_screen[0][2]<=slv_reg0[23:16]; current_screen[0][3]<=slv_reg0[31:24]; current_screen[0][4]<=slv_reg1[7:0]; current_screen[0][5]<=slv_reg1[15:8]; current_screen[0][6]<=slv_reg1[23:16]; current_screen[0][7]<=slv_reg1[31:24]; current_screen[0][8]<=slv_reg2[7:0]; current_screen[0][9]<=slv_reg2[15:8]; current_screen[0][10]<=slv_reg2[23:16]; current_screen[0][11]<=slv_reg2[31:24]; current_screen[0][12]<=slv_reg3[7:0]; current_screen[0][13]<=slv_reg3[15:8]; current_screen[0][14]<=slv_reg3[23:16]; current_screen[0][15]<=slv_reg3[31:24]; //Page1 current_screen[1][0]<=slv_reg4[7:0]; current_screen[1][1]<=slv_reg4[15:8]; current_screen[1][2]<=slv_reg4[23:16]; current_screen[1][3]<=slv_reg4[31:24]; current_screen[1][4]<=slv_reg5[7:0]; current_screen[1][5]<=slv_reg5[15:8]; current_screen[1][6]<=slv_reg5[23:16]; current_screen[1][7]<=slv_reg5[31:24]; current_screen[1][8]<=slv_reg6[7:0]; current_screen[1][9]<=slv_reg6[15:8]; current_screen[1][10]<=slv_reg6[23:16]; current_screen[1][11]<=slv_reg6[31:24]; current_screen[1][12]<=slv_reg7[7:0]; current_screen[1][13]<=slv_reg7[15:8]; current_screen[1][14]<=slv_reg7[23:16]; current_screen[1][15]<=slv_reg7[31:24]; //Page2 current_screen[2][0]<=slv_reg8[7:0]; current_screen[2][1]<=slv_reg8[15:8]; current_screen[2][2]<=slv_reg8[23:16]; current_screen[2][3]<=slv_reg8[31:24]; current_screen[2][4]<=slv_reg9[7:0]; current_screen[2][5]<=slv_reg9[15:8]; current_screen[2][6]<=slv_reg9[23:16]; current_screen[2][7]<=slv_reg9[31:24]; current_screen[2][8]<=slv_reg10[7:0]; current_screen[2][9]<=slv_reg10[15:8]; current_screen[2][10]<=slv_reg10[23:16]; current_screen[2][11]<=slv_reg10[31:24]; current_screen[2][12]<=slv_reg11[7:0]; current_screen[2][13]<=slv_reg11[15:8]; current_screen[2][14]<=slv_reg11[23:16]; current_screen[2][15]<=slv_reg11[31:24]; //Page3 current_screen[3][0]<=slv_reg12[7:0]; current_screen[3][1]<=slv_reg12[15:8]; current_screen[3][2]<=slv_reg12[23:16]; current_screen[3][3]<=slv_reg12[31:24]; current_screen[3][4]<=slv_reg13[7:0]; current_screen[3][5]<=slv_reg13[15:8]; current_screen[3][6]<=slv_reg13[23:16]; current_screen[3][7]<=slv_reg13[31:24]; current_screen[3][8]<=slv_reg14[7:0]; current_screen[3][9]<=slv_reg14[15:8]; current_screen[3][10]<=slv_reg14[23:16]; current_screen[3][11]<=slv_reg14[31:24]; current_screen[3][12]<=slv_reg15[7:0]; current_screen[3][13]<=slv_reg15[15:8]; current_screen[3][14]<=slv_reg15[23:16]; current_screen[3][15]<=slv_reg15[31:24]; after_update_state <= "WaitRequest"; current_state <= "UpdateScreen"; end //UpdateScreen State //1. Gets ASCII value from current_screen at the current page and the current spot of the page //2. If on the last character of the page transition update the page number, if on the last page(3) // then the updateScreen go to "after_update_state" after "UpdateScreen" : begin temp_char <= current_screen[temp_page][temp_index]; if(temp_index == 'd15) begin temp_index <= 'd0; temp_page <= temp_page + 1'b1; after_char_state <= "ClearDC"; if(temp_page == 2'b11) begin after_page_state <= after_update_state; clear_screen_i<=1'b0; end else begin after_page_state <= "UpdateScreen"; end end else begin temp_index <= temp_index + 1'b1; after_char_state <= "UpdateScreen"; end current_state <= "SendChar1"; end //Send Character States //1. Sets the Address to ASCII value of char with the counter appended to the end //2. Waits a clock for the data to get ready by going to ReadMem and ReadMem2 states //3. Send the byte of data given by the block Ram //4. Repeat 7 more times for the rest of the character bytes "SendChar1" : begin temp_addr <= {temp_char, 3'b000}; after_state <= "SendChar2"; current_state <= "ReadMem"; end "SendChar2" : begin temp_addr <= {temp_char, 3'b001}; after_state <= "SendChar3"; current_state <= "ReadMem"; end "SendChar3" : begin temp_addr <= {temp_char, 3'b010}; after_state <= "SendChar4"; current_state <= "ReadMem"; end "SendChar4" : begin temp_addr <= {temp_char, 3'b011}; after_state <= "SendChar5"; current_state <= "ReadMem"; end "SendChar5" : begin temp_addr <= {temp_char, 3'b100}; after_state <= "SendChar6"; current_state <= "ReadMem"; end "SendChar6" : begin temp_addr <= {temp_char, 3'b101}; after_state <= "SendChar7"; current_state <= "ReadMem"; end "SendChar7" : begin temp_addr <= {temp_char, 3'b110}; after_state <= "SendChar8"; current_state <= "ReadMem"; end "SendChar8" : begin temp_addr <= {temp_char, 3'b111}; after_state <= after_char_state; current_state <= "ReadMem"; end "ReadMem" : begin current_state <= "ReadMem2"; end "ReadMem2" : begin temp_spi_data <= temp_dout; current_state <= "Transition1"; end // SPI transitions // 1. Set SPI_EN to 1 // 2. Waits for SpiCtrl to finish // 3. Goes to clear state (Transition5) "Transition1" : begin temp_spi_en <= 1'b1; current_state <= "Transition2"; end "Transition2" : begin if(temp_spi_fin == 1'b1) begin current_state <= "Transition5"; end end // Delay Transitions // 1. Set DELAY_EN to 1 // 2. Waits for Delay to finish // 3. Goes to Clear state (Transition5) "Transition3" : begin temp_delay_en <= 1'b1; current_state <= "Transition4"; end "Transition4" : begin if(temp_delay_fin == 1'b1) begin current_state <= "Transition5"; end end // Clear transition // 1. Sets both DELAY_EN and SPI_EN to 0 // 2. Go to after state "Transition5" : begin temp_spi_en <= 1'b0; temp_delay_en <= 1'b0; current_state <= after_state; end default : current_state <= "Idle"; endcase end end // Internal reset generator always @(posedge S_AXI_ACLK) begin if (RST_IN == 1'b1) count<=count+1'b1; if (count == 12'hFFF) begin RST_internal <=1'b0; end end |
这个状态机实现了OLED的通电控制、初始化、以及字符的显示。
15.4 硬件工程搭建
Step1:另外新建一个VIVADO工程,根据自己的开发板正确配置芯片型号。
Step2:在Project manager区中单击Project settings。
Step3:选择IP设置区中的repository manager,将上一节我们封装好的IP的路劲添加进去。
Step:4:单击+号图标,将上一节封装的IP的路劲存放进去,单击OK。
Step5:新建一个BD文件,输入文件名,完成创建。
Step6:向BD文件中添加一个ZYNQ Processing system,根据自身硬件完成IP的配置。
Step7:单击添加IP图标,输入上一节我们自定义IP的模块名,将其添加入BD文件中。
Step8:直接点击Run connection automation,然后单击OK。
Step9:选中SSD1306控制IP的输出端口,按Ctrl+T组合键引出端口。
Step10:右键单击Block文件,文件选择Generate the Output Products。
Step11:右键单击Block文件,选择Create a HDL wrapper,根据Block文件内容产生一个HDL 的顶层文件,并选择让vivado自动完成。
Step12:添加一个约束文件,打开对应自己硬件的原理图,查看OLED部分引脚连接情况。Miz702约束文件如下所示:
set_property PACKAGE_PIN U10 [get_ports DC] set_property PACKAGE_PIN U9 [get_ports RES] set_property PACKAGE_PIN AB12 [get_ports SCLK] set_property PACKAGE_PIN AA12 [get_ports SDIN] set_property PACKAGE_PIN U11 [get_ports VBAT] set_property PACKAGE_PIN U12 [get_ports VDD] set_property IOSTANDARD LVCMOS33 [get_ports DC] set_property IOSTANDARD LVCMOS33 [get_ports RES] set_property IOSTANDARD LVCMOS33 [get_ports SCLK] set_property IOSTANDARD LVCMOS33 [get_ports SDIN] set_property IOSTANDARD LVCMOS33 [get_ports VBAT] set_property IOSTANDARD LVCMOS33 [get_ports VDD] set_property PACKAGE_PIN N17 [get_ports VDD] |
其他型号开发板参照对应型号的原理图的OLED部分,修改成对应的引脚即可。
Step13:生成bit文件。
15.5 导入到SDK
Step1:导出硬件。
Step2:新建一个名为OLED_Test的空白工程。
Step3:打开我们提供的源程序包,在第二季,第15章的文件夹中,将SDK所有的文件复制过来。
Step4:展开OLED_Test,在Src下按Ctrl+V将所有文件粘贴过来。
Step5:右击工程,选择Debug as ->Debug configuration。
Step6:选中system Debugger,双击创建一个系统调试。
Step7:设置系统调试。
Step8:单击运行程序按钮运行程序,此时可在OLED上观察到滚动显示我们定义的字符。
15.6 本章小结
本章的方案虽然不及14章的功能强大,但是可以提高CPU的工作效率,充分发挥PL的硬件资源的能力,减轻CPU的负担。
两种方案各有优缺点,前者很好地平衡了PS和PL部分的工作,但是功能单一,只能够显示字符;后者未能合理使用PL资源,但是灵活度高、功能强大。读者可以尝试将两种方案进行融合,取长补短,设计出更优秀的方案。