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
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资源,但是灵活度高、功能强大。读者可以尝试将两种方案进行融合,取长补短,设计出更优秀的方案。
S02_CH15_ AXI_OLED 实验的更多相关文章
- S02_CH16 等精度频率计实验
S02_CH16 等精度频率计实验 在了解了AXI总线之后,今天我们自己动手设计一个带AXI4-Lite总线的IP,来完成频率计的实验. 频率计虽然小,但是也算五脏俱全,涉及到zynq的方方面面,比如 ...
- [原] 利用 OVS 建立 VxLAN 虚拟网络实验
OVS 配置 VxLAN HOST A ------------------------------------------ | zh-veth0(10.1.1.1) VM A | | ---|--- ...
- Android中Activity的四大启动模式实验简述
作为Android四大组件之一,Activity可以说是最基本也是最常见的组件,它提供了一个显示界面,从而实现与用户的交互,作为初学者,必须熟练掌握.今天我们就来通过实验演示,来帮助大家理解Activ ...
- SEED实验系列文章目录
美国雪城大学SEEDLabs实验列表 SEEDLabs是一套完整的信息安全实验,涵盖本科信息安全教学中的大部分基本原理.项目组2002年由杜文亮教授创建,目前开发了30个实验,几百所大学已采用.实验楼 ...
- 物联网实验4 alljoyn物联网实验之手机局域网控制设备
AllJoyn开源物联网协议框架,官方描述是一个能够使连接设备之间进行互操作的通用软件框架和系统服务核心集,也是一个跨制造商来创建动态近端网络的软件应用.高通已经将该项目捐赠给了一个名为“AllSee ...
- (转)linux下和云端通讯的例程, ubuntu和openwrt实验成功(一)
一. HTTP请求的数据流总结#上传数据, yeelink的数据流如下POST /v1.0/device/4420/sensor/9089/datapoints HTTP/1.1Host: api. ...
- (原创) alljoyn物联网实验之手机局域网控制设备
AllJoyn开源物联网协议框架,官方描述是一个能够使连接设备之间进行互操作的通用软件框架和系统服务核心集,也是一个跨制造商来创建动态近端网络的软件应用.高通已经将该项目捐赠给了一个名为“AllSee ...
- 实验:Oracle直接拷贝物理存储文件迁移
实验目的:Oracle直接拷贝物理文件迁移,生产库有类似施工需求,故在实验环境简单验证一下. 实验环境: A主机:192.168.1.200 Solaris10 + Oracle 11.2.0.1 B ...
- Oracle RAC 更换存储实验
实验环境准备: RHEL 6.5 + Oracle 11.2.0.4 RAC (2nodes) OCR和Voting Disk使用的是OCR1磁盘组,底层对应3个1G大小的共享LUN,一般冗余: DA ...
随机推荐
- docker 安装redis 注意事项
一. redis配置文件修改(重要) ~/redis.conf 中daemonize=NO.非后台模式,如果为YES 会的导致 redis 无法启动,因为后台会导致docker无任务可做而退出. 三 ...
- 第一次Java测试及感触(2018.9.20)
在本周周四进行了java测试,有一点感触,测试的题目是用Java实现一个ATM机的管理系统.之前老师提前给我们样卷,结果考试的时候换了题型,瞬间脑子空白,一时不知道怎么下手,因为暑假虽然涉猎了java ...
- TCP首部的TimeStamp时间戳选项 转载
TCP应该是以太网协议族中被应用最为广泛的协议之中的一个,这里就聊一聊TCP协议中的TimeStamp选项.这个选项是由RFC 1323引入的,该C建议提交于1992年.到今天已经足足有20个年头.只 ...
- 2016百度之星资格赛 Problem B(大数+组合数)
题意:度熊面前有一个全是由1构成的字符串,被称为全1序列.你可以合并任意相邻的两个1,从而形成一个新的序列.对于给定的一个全1序列,请计算根据以上方法,可以构成多少种不同的序列.最多200个1. 比如 ...
- Python语法 - 生成器
生成器基本概念 1 生成器不会把结果保存在一个系列中,而是保存生成器的状态,在每次进行迭代时返回一个值,直到遇到StopIteration异常结束 2 生成器表达式能做的事情列表解析基本都能处理,只不 ...
- 服务器 Web服务器 应用服务器区别联系
服务器: 通俗的讲,我们访问一个网站就相当于访问一个服务器的文件,如果想要通过自己的域名来访问一个网站,首先得将域名部署到你的服务器上,然后就可以通过域名访问到你服务器上的网 页文件.ip地址就相当于 ...
- mysql —复制
MySQL的扩展 读写分离 复制:每个节点都有相同的数据集 向外扩展 二进制日志 单向 复制的功用: 数据分布 负载均衡读 备份 高可用和故障切换 MySQL升级测试 MySQL复制相关概念 主从复 ...
- AngularJS开发中常用的写法,如:获取URL参数、路由跳转、$http、获取元素等
控制器,带状态 app.controller('editCtrl', ['$http', '$location', '$rootScope', '$scope', '$state', '$stateP ...
- Mac OS xshell xftp 替代工具-finalshell
安装步骤: 1,打开Mac 终端: 2,输入: curl -L -o finalshell_install.sh www.hostbuf.com/downloads/finalshell_instal ...
- Handler常见两种用法
1.Handler在Android的两个功能 1.1表示未来某时做某事 1.2线程间通信 2.演示源码如下: package com.example.datastrorage; import andr ...