请选择 进入手机版 | 继续访问电脑版
[X]关闭

[米联客-XILINX-H3_CZ08_7100] LINUX驱动篇连载-18 PL 自定义AXI-Lite-GPIO

文档创建者:LINUX课程
浏览次数:358
最后更新:2024-09-10
文档课程分类-AMD-ZYNQ
AMD-ZYNQ: ZYNQ-SOC » 2_LINUX应用开发
本帖最后由 LINUX课程 于 2024-9-11 10:52 编辑

软件版本:vitis2021.1(vivado2021.1)
操作系统:WIN10 64bit
硬件平台:适用XILINX Z7/ZU系列FPGA
登录“米联客”FPGA社区-www.uisrc.com视频课程、答疑解惑!

1 概述
在前文中我们学习了AXI总线协议,而且通过VIVADO自定义了AXI-LITE总线协议的IP CORE,并且实现了寄存器的读写。
那么在实际的应用中,如果我们ARM的IO不够用了,除了在前文中使用官方自带的AXI-GPIO,我们自己也可以定义AXI-GPIO IP CORE。
本文后以几篇文章都会展示AXI-LITE 自定义IP的使用,通过加强的训练,让读者掌握这种总线接口的使用。并且后面我们自定的AXI-FDMA可以完成高速的DMA数据传输。
本文实验目的:
  • 通过前文的学习,把掌握的自定义AXI-LITE-SLAVE寄存器读写方法,用于引出扩展PL的IO
  • 通过VITIS-SDK实现对自定义IP中寄存器的读写访问,以此实现PL IO的控制。
2 系统框图
image.jpg
3 创建IP
3.1 利用模板创建AXI-Lite IP
1:打开VIVADO软件,新建一个工程。
2:单击ToolsàCreate and Package NEW IP
3:单击Next,选择Create a new AXI4 peripheral,单击Next。
image.jpg
4:输入要创建的IP名字,此处命名为GPIO_LITE_ML,选择保存路径,单击Next。
image.jpg
5:NameàS00_AXI
Interface Type(接口类型)àLite
Data Width(Bits)(数据位宽)à32位;
Number of  Registers(寄存器数量)à4 ;单击next
image.jpg
6:选择Edit IP,点击Finish按钮。软件自动打开一个编辑IP的工程,即edit_GPIO_LITE_ML_V1_0.xpr工程。
image.jpg
3.2 修改IP源码
1:打开的edit_GPIO_LITE_ML_V1_0.xpr工程界面如下。
image.jpg
查看生成IP的文件夹
image.jpg
2:现在生成的IP需要进行修改才能满足我们使用需求。选中Project Manager,双击GPIO_LITE_ML_v1_0_S00_AXI_inst,做如下更改。
修改1:
image.jpg
修改2:
image.jpg
将slv_reg0的值赋值给了用户输出逻辑,当我们向slv_reg0写入数据的时候,也就相当于向GPIO_LED赋值。
更改后的文件如下所示。
`timescale 1 ns / 1 ps
        module GPIO_LITE_ML_v1_0_S00_AXI #
        (
                // Users to add parameters here
                // User parameters ends
                // Do not modify the parameters beyond this line
                // 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        = 4
        )
        (
                // Users to add ports here
        output wire [3:0]GPIO_LED,
                // User ports ends
                // Do not modify the ports beyond this line
                // 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 = 1;
        //----------------------------------------------
        //-- Signals for user logic register space example
        //------------------------------------------------
        //-- Number of Slave Registers 4
        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;
        wire         slv_reg_rden;
        wire         slv_reg_wren;
        reg [C_S_AXI_DATA_WIDTH-1:0]         reg_data_out;
        integer         byte_index;
        reg         aw_en;
        // 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.
        always @( posedge S_AXI_ACLK )
        begin
          if ( S_AXI_ARESETN == 1'b0 )
            begin
              axi_awready <= 1'b0;
              aw_en <= 1'b1;
            end
          else
            begin   
              if (~axi_awready && S_AXI_AWVALID && S_AXI_WVALID && aw_en)
                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;
                  aw_en <= 1'b0;
                end
                else if (S_AXI_BREADY && axi_bvalid)
                    begin
                      aw_en <= 1'b1;
                      axi_awready <= 1'b0;
                    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 && aw_en)
                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 && aw_en )
                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;
            end
          else begin
            if (slv_reg_wren)
              begin
                case ( axi_awaddr[ADDR_LSB+OPT_MEM_ADDR_BITS:ADDR_LSB] )
                  2'h0:
                    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  
                  2'h1:
                    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  
                  2'h2:
                    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  
                  2'h3:
                    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  
                  default : begin
                              slv_reg0 <= slv_reg0;
                              slv_reg1 <= slv_reg1;
                              slv_reg2 <= slv_reg2;
                              slv_reg3 <= slv_reg3;
                            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
              // Address decoding for reading registers
              case ( axi_araddr[ADDR_LSB+OPT_MEM_ADDR_BITS:ADDR_LSB] )
                2'h0   : reg_data_out <= slv_reg0;
                2'h1   : reg_data_out <= slv_reg1;
                2'h2   : reg_data_out <= slv_reg2;
                2'h3   : reg_data_out <= slv_reg3;
                default : reg_data_out <= 0;
              endcase
        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   
        // Add user logic here
    assign GPIO_LED[3:0] = slv_reg0[3:0];
        // User logic ends
        endmodule
更改后的文件如下所示
3:双击GPIO_LITE_ML_v1_0文件,做如下修改。
修改1:
image.jpg
修改2:
image.jpg
修改后的文件如下。
`timescale 1 ns / 1 ps
        module GPIO_LITE_ML_v1_0 #
        (
                // Users to add parameters here
                // User parameters ends
                // Do not modify the parameters beyond this line
                // Parameters of Axi Slave Bus Interface S00_AXI
                parameter integer C_S00_AXI_DATA_WIDTH        = 32,
                parameter integer C_S00_AXI_ADDR_WIDTH        = 4
        )
        (
                // Users to add ports here
        output wire [3:0]GPIO_LED,
                // User ports ends
                // Do not modify the ports beyond this line
                // Ports of Axi Slave Bus Interface S00_AXI
                input wire  s00_axi_aclk,
                input wire  s00_axi_aresetn,
                input wire [C_S00_AXI_ADDR_WIDTH-1 : 0] s00_axi_awaddr,
                input wire [2 : 0] s00_axi_awprot,
                input wire  s00_axi_awvalid,
                output wire  s00_axi_awready,
                input wire [C_S00_AXI_DATA_WIDTH-1 : 0] s00_axi_wdata,
                input wire [(C_S00_AXI_DATA_WIDTH/8)-1 : 0] s00_axi_wstrb,
                input wire  s00_axi_wvalid,
                output wire  s00_axi_wready,
                output wire [1 : 0] s00_axi_bresp,
                output wire  s00_axi_bvalid,
                input wire  s00_axi_bready,
                input wire [C_S00_AXI_ADDR_WIDTH-1 : 0] s00_axi_araddr,
                input wire [2 : 0] s00_axi_arprot,
                input wire  s00_axi_arvalid,
                output wire  s00_axi_arready,
                output wire [C_S00_AXI_DATA_WIDTH-1 : 0] s00_axi_rdata,
                output wire [1 : 0] s00_axi_rresp,
                output wire  s00_axi_rvalid,
                input wire  s00_axi_rready
        );
// Instantiation of Axi Bus Interface S00_AXI
        GPIO_LITE_ML_v1_0_S00_AXI # (
                .C_S_AXI_DATA_WIDTH(C_S00_AXI_DATA_WIDTH),
                .C_S_AXI_ADDR_WIDTH(C_S00_AXI_ADDR_WIDTH)
        ) GPIO_LITE_ML_v1_0_S00_AXI_inst (
                .S_AXI_ACLK(s00_axi_aclk),
                .S_AXI_ARESETN(s00_axi_aresetn),
                .S_AXI_AWADDR(s00_axi_awaddr),
                .S_AXI_AWPROT(s00_axi_awprot),
                .S_AXI_AWVALID(s00_axi_awvalid),
                .S_AXI_AWREADY(s00_axi_awready),
                .S_AXI_WDATA(s00_axi_wdata),
                .S_AXI_WSTRB(s00_axi_wstrb),
                .S_AXI_WVALID(s00_axi_wvalid),
                .S_AXI_WREADY(s00_axi_wready),
                .S_AXI_BRESP(s00_axi_bresp),
                .S_AXI_BVALID(s00_axi_bvalid),
                .S_AXI_BREADY(s00_axi_bready),
                .S_AXI_ARADDR(s00_axi_araddr),
                .S_AXI_ARPROT(s00_axi_arprot),
                .S_AXI_ARVALID(s00_axi_arvalid),
                .S_AXI_ARREADY(s00_axi_arready),
                .S_AXI_RDATA(s00_axi_rdata),
                .S_AXI_RRESP(s00_axi_rresp),
                .S_AXI_RVALID(s00_axi_rvalid),
                .S_AXI_RREADY(s00_axi_rready),
                .GPIO_LED(GPIO_LED)
        );
        // Add user logic here
        // User logic ends
        endmodule
4:进一步修改。去掉“_v1_0”。
1、GPIO_LITE_ML_v1_0_S00_AXI.v中:
module GPIO_LITE_ML_v1_0_S00_AXI #修改为àmodule GPIO_LITE_ML_S00_AXI #
image.jpg 修改后 image.jpg
        
2、GPIO_LITE_ML_v1_0.v中:
module GPIO_LITE_ML_v1_0 #     修改为àmodule GPIO_LITE_ML #
   image.jpg 修改后 image.jpg
GPIO_LITE_ML_v1_0_S00_AXI #  修改为àGPIO_LITE_ML_S00_AXI #
GPIO_LITE_ML_v1_0_S00_AXI_inst 修改为àGPIO_LITE_ML_S00_AXI_inst
image.jpg    修改后 image.jpg
5:修改后,保存。出现如下界面,选择Automatically pick new top module。
image.jpg
更新后界面
image.jpg
6:重新封装。选择ToolsàCreat and Pakage New IP,单击Next
7:选择Package your current project,单击Next。
image.jpg
路径选择原来IP所在的位置,覆盖原来的文件。
image.jpg
8:选择Overwrite。
image.jpg
9:选择Package IPàRewiew and Package àRe-Package IP
image.jpg
image.jpg
自定义IP生成完毕。
4 硬件电路分析
这里GPIO输出的IO引出到LED上
image.jpg
5 搭建SOC系统工程
详细的搭建过程这里不再重复,对于初学读者如果还不清楚如何创建SOC工程的,请学习“01Vitis Soc开发入门”这篇文章。
5.1 SOC系统工程
image.jpg
1:中断设置
本实验可以不设置中断
image.jpg
2:设置GP Master接口
image.jpg
3:设置复位输出
image.jpg
4:设置PL时钟
image.jpg
5:添加自定义IP
设置自定义IP路径,并且添加IP
image.jpg
5.2设置AXI外设地址分配
只要添加的AXI总线外设都要正确分配地址,这一步不能遗漏
5.3 编译并导出平台文件
以下步骤简写,有不清楚的看Linux基础篇第四章。
1:打开soc_prj内工程。
2:生成Bit文件。
3:导出到硬件: FileàExport HardwareàInclude bitstream
4:导出完成后,对应工程路径的soc_hw路径下有硬件平台文件:system_wrapper.xsa的文件。根据硬件平台文件system_wrapper.xsa来创建需要Platform平台。
image.jpg
5:打开vitis,并添加设备树模板。
6:创建工程文件,选择device tree创建,创建完成后编译工程。
7:获得设备树以及启动文件,打开虚拟机将文件拷贝到开发包的指定位置。
5.4 设备树修改及驱动编译
1:设备树修改
本章vitis会生成pl端的设备树,需要手动添加。另外soc_dts内有写好的设备树,新手请不要自行修改,直接使用教程提供的设备树。设备树的修改如下:
image.jpg
这次直接使用vitis生成的设备树来写驱动,所以不对生成的设备树进行修改。
2:编译系统
拷贝上一步写好的设备树到指定路径,拷贝其他文件后,编译uboot,编译kernel,制作镜像并烧录系统。具体步骤参考Linux基础篇第四章。
3:编译驱动
将对应的demo拷贝至/home/uisrc下,同时确保/home/uisrc下有软件开发包uisrc-lab-xlnx。
1725952840754.jpg
进入demo的路径,使用make编译驱动。
image.jpg
其中.ko结尾的文件为我们编译出来的驱动。
4:拷贝程序
将对应的文件拷贝至sd卡上:
image.jpg
6 程序分析
这个驱动需要根据设备树来编写驱动,所以采用了一个比较熟悉的开发模式,即基于设备树的平台驱动开发。
  1. #include <linux/ide.h>
  2. #include <linux/module.h>
  3. #include <linux/of_platform.h>
  4. #include <linux/delay.h>

  5. struct AxiUserGpio
  6. {
  7. int addr_width;
  8. int data_width;
  9. struct device_node *dev_node;
  10. int reg_num;
  11. unsigned int *reg_value;
  12. };
  13. struct AxiUserGpio *AxiUserGpio_data;

  14. static unsigned int *AxiData_0 = NULL;

  15. static struct of_device_id AxiUserGpio_of_match[] = {
  16. {.compatible = "xlnx,GPIO-LITE-ML-v1-0-1.0"},
  17. {},
  18. };

  19. int of_AxiUserGpio_data(struct AxiUserGpio *pdata, struct platform_device *pdev)
  20. {
  21. struct device_node *np = pdev->dev.of_node;
  22. int ret = 0;

  23. pdata->reg_num = of_property_count_elems_of_size(np, "reg", sizeof(int));
  24. if (pdata->reg_num < 0)
  25. {
  26.   dev_err(&pdev->dev, "get reg_num failed\n");
  27.   return ret;
  28. }

  29. pdata->reg_value = (unsigned int *)kmalloc(sizeof(unsigned int) * pdata->reg_num, GFP_KERNEL);
  30. if (!pdata->reg_value)
  31. {
  32.   kfree(pdata->reg_value);
  33.   dev_err(&pdev->dev, "kmalloc failed\n");
  34.   return -1;
  35. }
  36. ret = of_property_read_u32_array(np, "reg", pdata->reg_value, pdata->reg_num);
  37. if (ret != 0)
  38. {
  39.   kfree(pdata->reg_value);
  40.   dev_err(&pdev->dev, "get reg failed\n");
  41.   return ret;
  42. }

  43. ret = of_property_read_u32(np, "xlnx,s00-axi-addr-width", &pdata->addr_width);
  44. if (ret < 0)
  45. {
  46.   dev_err(&pdev->dev, "get addr_width failed\n");
  47.   return ret;
  48. }

  49. ret = of_property_read_u32(np, "xlnx,s00-axi-data-width", &pdata->data_width);
  50. if (ret < 0)
  51. {
  52.   dev_err(&pdev->dev, "get data_width failed\n");
  53.   return ret;
  54. }

  55. return 0;
  56. }

  57. static int AxiUserGpio_probe(struct platform_device *pdev)
  58. {
  59. int ret = 0;
  60. int i = 0;

  61. struct device *dev = &pdev->dev;
  62. struct AxiUserGpio *pdata = dev_get_platdata(dev);
  63. if (!pdata)
  64. {
  65.   pdata = devm_kzalloc(dev, sizeof(struct AxiUserGpio), GFP_KERNEL);
  66.   if (!pdata)
  67.    return -ENOMEM;

  68.   platform_set_drvdata(pdev, pdata);
  69. }
  70. ret = of_AxiUserGpio_data(pdata, pdev);
  71. AxiUserGpio_data = pdata;

  72. // 打印属性值
  73. printk("addr_width = %d\r\n", AxiUserGpio_data->addr_width);
  74. printk("data_width = %d\r\n", AxiUserGpio_data->data_width);
  75. printk("reg_num = %d\r\n", AxiUserGpio_data->reg_num);
  76. for (i = 0; i < AxiUserGpio_data->reg_num; i += 2)
  77. {
  78.   printk("reg = %#X  %#X \r\n", AxiUserGpio_data->reg_value[i], AxiUserGpio_data->reg_value[i + 1]);
  79. }
  80. // printk("for is done!\r\n");

  81. AxiData_0 = ioremap(AxiUserGpio_data->reg_value[0] + AxiUserGpio_data->addr_width * 0, AxiUserGpio_data->addr_width);

  82. for (i = 0; i < 12; i++)
  83. {
  84.   writel(i % 4, AxiData_0);
  85.   printk(KERN_CRIT "AxiData_0:%#X\n", readl(AxiData_0));
  86.   ssleep(1);
  87. }

  88. return ret;
  89. }

  90. static int AxiUserGpio_remove(struct platform_device *pdev)
  91. {
  92. kfree(AxiUserGpio_data->reg_value);
  93. iounmap(AxiData_0);
  94. return 0;
  95. }

  96. static struct platform_driver AxiUserGpio_device_driver = {
  97. .driver = {
  98.   .name = "AxiUserGpio",
  99.   .owner = THIS_MODULE,
  100.   .of_match_table = of_match_ptr(AxiUserGpio_of_match),
  101. },
  102. .probe = AxiUserGpio_probe,
  103. .remove = AxiUserGpio_remove,
  104. };

  105. static int __init AxiUserGpio_init(void)
  106. {
  107. return platform_driver_register(&AxiUserGpio_device_driver);
  108. }

  109. static void __exit AxiUserGpio_exit(void)
  110. {
  111. platform_driver_unregister(&AxiUserGpio_device_driver);
  112. }

  113. late_initcall(AxiUserGpio_init);
  114. module_exit(AxiUserGpio_exit);

  115. MODULE_LICENSE("GPL");
  116. MODULE_AUTHOR("uisrc");
复制代码
此程序与上一章的程序基本一致,修改的地方仅有probe函数内的功能,在上一章中我们分别先写再读四个寄存器。在本程序中,我们需要写入特定的参数到第一个寄存器中,写入的参数为00、01、10、11分别对应两个灯的共计4种明暗组合方式。
行94,映射第一个寄存器,本章程序只需要用到第一个寄存器。
行96~101,间隔一秒循环写入寄存器参数,并读取验证,共计三轮。
行98,取余4的目的是保证写入的数据在0~3之间。
7 演示结果
SD2.0 启动 01 而模式开关为 ON OFF(7100 需要先将系统烧录进qspi,然后才能从qspi启动sd卡,参考Linux基础篇第四章)
2f5038eb9880afd532753935815b079.jpg
将 PS 端串口线连接电脑,如果要使用 ssh 登录,将网口线同样连接至电脑,最后给开发板通电。每次重新上电,需要重新插拔 PS 串口,否则会登录失败。
image.jpg
接入12V直流电源开机。
找到刚才放驱动的目录:
image.jpg
使用sudo insmod axi_user_gpio.ko命令装载驱动,密码为root:
image.jpg
驱动会先向寄存器写入数据,然后读取寄存器内的数据,可以看到板子上的pl区域灯在交替遍历闪烁。
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则