• S02_CH15_ AXI_OLED 实验


    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

    wpsE1AD.tmp

    Step3:单击NEXT

    wpsE1AE.tmp

    Step4:由于我们需要挂在到总线上,因此创建一个带AXI总线的用户IP

    wpsE1BE.tmp

    Step5:设置IP的名字为SSD1306_OLED_ML版本号默认,并且记住IP的位置

    wpsE1BF.tmp

    Step6:设置总线形式为Lite总线,Lite总线是简化的AXI总线消耗的资源少,当然性能也是比完全版的AXI总线差一点,但是由于音频的速度并不高,因此采用Lite总线就够了,设置寄存器数量为17,因为后面我们需要用到17个寄存器。

    wpsE1C0.tmp

    Step7:选择Edit IP单击Finish完成

    wpsE1D1.tmp

    15.2 SSD1306_OLED_ML用户IP的修改

    IP创建完成后,并不能立马使用,还需要做一些修改。

    Step1:打开SSD1306_OLED_ML.v文件在以下位置修改:

    wpsE1D2.tmp

    wpsE1D3.tmp

    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文件可在我们提供的源代码程序包中获得。

    wpsE2BE.tmp

    wpsE2BF.tmp

    wpsE2C0.tmp

    Step6:修改完成后重新封装一次自定义IP

    wpsE2D1.tmp

    Step7:单击NEXT

    wpsE2D2.tmp

    Step8:和第一次不同,这次选择第一个单选框然后单击NEXT

    wpsE2D3.tmp

    Step9:选择第一个单选框,然后单击NEXT

    wpsE2E3.tmp

    Step10:点击Overwrite

    wpsE2E4.tmp

    Step11:点击Finish 到此自定义IP结束

    wpsE2E5.tmp

    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。

    wpsE344.tmp

    Step3:选择IP设置区中的repository manager,将上一节我们封装好的IP的路劲添加进去。

    Step:4:单击+号图标,将上一节封装的IP的路劲存放进去,单击OK。

    wpsE345.tmp

    Step5:新建一个BD文件,输入文件名,完成创建。

    Step6:向BD文件中添加一个ZYNQ Processing system,根据自身硬件完成IP的配置。

    Step7:单击添加IP图标,输入上一节我们自定义IP的模块名,将其添加入BD文件中。

    wpsE355.tmp

    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所有的文件复制过来。

    wpsE356.tmp

    Step4:展开OLED_Test,在Src下按Ctrl+V将所有文件粘贴过来。

    wpsE357.tmp

    Step5:右击工程,选择Debug as ->Debug configuration。

    Step6:选中system Debugger,双击创建一个系统调试。

    Step7:设置系统调试。

    Step8:单击运行程序按钮wpsE368.tmp运行程序,此时可在OLED上观察到滚动显示我们定义的字符。

    15.6 本章小结

    本章的方案虽然不及14章的功能强大,但是可以提高CPU的工作效率,充分发挥PL的硬件资源的能力,减轻CPU的负担。

    两种方案各有优缺点,前者很好地平衡了PS和PL部分的工作,但是功能单一,只能够显示字符;后者未能合理使用PL资源,但是灵活度高、功能强大。读者可以尝试将两种方案进行融合,取长补短,设计出更优秀的方案。

  • 相关阅读:
    遮罩层点击空白退出代码
    不同浏览器的margin值与padding值
    让div自适应浏览器窗口居中显示
    导航相关(下方导航指示条居中)
    CSS相邻兄弟选择器
    使用font-size:0去掉inline-block元素之间的空隙
    box-sizing属性
    常见浏览器兼容性问题
    秋季编程总结
    POJ 1193 内存分配
  • 原文地址:https://www.cnblogs.com/milinker/p/6474817.html
Copyright © 2020-2023  润新知