[X]关闭

[米联客-XILINX-H3_CZ08_7100] FPGA_SDK入门篇连载-06 PS-MIO/EMIO中断实验

文档创建者:FPGA课程
浏览次数:184
最后更新:2024-09-24
文档课程分类-AMD-ZYNQ
AMD-ZYNQ: ZYNQ-SOC » 1_SDK应用方案(仅旗舰型号) » 1-SDK基础入门方案
本帖最后由 FPGA课程 于 2024-9-24 19:56 编辑

软件版本:VIVADO2021.1
操作系统:WIN10 64bit
硬件平台:适用 XILINX A7/K7/Z7/ZU/KU 系列 FPGA
实验平台:米联客-MLK-H3-CZ08-7100开发板
板卡获取平台:https://milianke.tmall.com/
登录“米联客”FPGA社区 http://www.uisrc.com 视频课程、答疑解惑!​
1 概述
当正常的程序在运行的时候,中断资源可以打断正在运行的程序,让CPU进入中断函数进行一些事务的处理。使用中断处理可以实现多任务的实时处理,可以提高多任务处理的效率。本实验继续前面的实验完成PS GPIO-MIO/EMIO输入中断的实验,请在前一个实验的基础上完成本实验。
本文实验目的:
1:掌握理解中断输入的应用场合
2:PS MIO的中断寄存器功能定义
3:掌握vitis-SDK下MIO中断功能的使用
2系统框图
PS MIO一般会分配到固定的外设,包括FLASH、EMMC、TFCARD、UART、USB2.0、ETH以太网。PS的IO也可以单独配置成普通的GPIO,如果IO不够用也可以通过EMIO扩展更多IO,对于ZYNQ最多支持扩展64个EMIO。
64d966809f7b4a878ec9325f0509d1ed.jpg
3中断资源概述
1c1c13d265e346778d61429a3bc132f2.jpg
上图中,红色部分为PS的IO中断资源通过共享外设中断接口接入到中断控制派发器。共享资源中断资源可以同时发送给CPU0或者CPU1,ZYNQ可以确保只能有一个CPU响应当前激活的中断,另外的CPU会接收到中断号为ID# 1023的虚假中断或者下一个挂起的中断。

4PS-MIO/PS-EMIO中断寄存器介绍
d2711172e2e248dcab999d8e2085790c.jpg
中断触发可以是上升沿,下降沿,低电平或高电平。触发方式通过INT_TYPE,INT_POLARITY和INT_ANY寄存器进行编程设置。
GPIO(PSMIO/EMIO)共享(IRQ ID#52)中断号。当检测到中断后,GPIO的INT_STAT(中断状态)会被设置成1。INT_EN和INT_DIS寄存器用于使能或者屏蔽中断。如果使能了GPIO中断,(IRQ ID#52)号中断会传入中断控制器。
如果屏蔽中断,INT_STAT的状态会一直保持,直到被清除。由于是共享一个中断号,所以软件还要通过读取INT_MASK和INT_STAT的值用来确认具体哪一个GPIO产生的中断。
通过将写1到 INT_EN使能中断,通过写1到INT_DIS寄存器来屏蔽中断。

PSMIO-BANK1:NT_MASK_1中断掩码寄存器偏移地址:XGPIOPS_INTMASK_OFFSET(0x0000024C)
                        
Field Name
                        
                        
Bits
                        
                        
Type
                        
                        
Reset Value
                        
                        
Description
                        
                        
INT_MASK_1
                        
                        
31:0
                        
                        
Ro
                        
                        
0x0
                        
                        
中断掩码寄存器只读
                        
0:中断使能
                        
1:中断屏蔽
                        

PSMIO-BANK1:INT_DISABLE_1中断禁用寄存器偏移地址:XGPIOPS_INTMASK_OFFSET(0x00000254)
                        
Field Name
                        
                        
Bits
                        
                        
Type
                        
                        
Reset Value
                        
                        
Description
                        
                        
INT_DISABLE_1
                        
                        
31:0
                        
                        
wo
                        
                        
0x0
                        
                        
中断禁用
                        
0:无改变
                        
1:禁用中断
                        

GPIO的INT_ENABLE_1中断使能寄存器偏移地址XGPIOPS_INTEN_OFFSET(0x00000250)
                        
Field Name
                        
                        
Bits
                        
                        
Type
                        
                        
Reset Value
                        
                        
Description
                        
                        
INT_ENABLE_1
                        
                        
31:0
                        
                        
Rw
                        
                        
0x0
                        
                        
0:不改变
                        
1:清除中断屏蔽,使能中断
                        

PSMIO-BANK1:INT_STATUS_1中断状态寄存器偏移地址:XGPIOPS_INTSTS_OFFSET(0x00000258)
                        
Field Name
                        
                        
Bits
                        
                        
Type
                        
                        
Reset Value
                        
                        
Description
                        
                        
INT_STATUS_1
                        
                        
31:0
                        
                        
wtc
                        
                        
0x0
                        
                        
读操作:
                        
0:无中断
                        
1:有中断产生
                        
写操作:
                        
0:无变化
                        
1:清除中断
                        

PSMIO-BANK1:INT_TPYE_1中断类型寄存器偏移地址XGPIOPS_INTTYPE_OFFSET(0x0000025C)
                        
Field Name
                        
                        
Bits
                        
                        
Type
                        
                        
Reset Value
                        
                        
Description
                        
                        
INT_TPYE_1
                        
                        
31:0
                        
                        
Rw
                        
                        
0xFFFFFFFF
                        
                        
0:电平触发
                        
1:边沿触发
                        

PSMIO-BANK1:INT_POLARITY_1中断触发极性寄存器偏移地址XGPIOPS_INTPOL_OFFSET(0x00000260)
                        
Field Name
                        
                        
Bits
                        
                        
Type
                        
                        
Reset Value
                        
                        
Description
                        
                        
INT_TPYE_1
                        
                        
31:0
                        
                        
Rw
                        
                        
0x0
                        
                        
0:低电平或者下降沿
                        
1:高电平或者上升沿
                        

PSMIO-BANK1:INT_ON_ANY_1中断边沿类型寄存器偏移地址XGPIOPS_INTANY_OFFSET (0x00000264)
                        
Field Name
                        
                        
Bits
                        
                        
Type
                        
                        
Reset Value
                        
                        
Description
                        
                        
INT_ON_ANY_1
                        
                        
31:0
                        
                        
Rw
                        
                        
0x0
                        
                        
0:当触发类型为边沿触发,单边沿触发
                        
1:当触发类型为边沿触发,双边沿触发
                        

可以设置的中断类型总结如下:
                        
类型
                        
                        
INT_TYPE
                        
                        
INT_POLARITY
                        
                        
INT_ON_ANY
                        
                        
上升沿中断
                        
                        
1
                        
                        
1
                        
                        
0
                        
                        
下降沿中断
                        
                        
1
                        
                        
0
                        
                        
0
                        
                        
上升沿下降沿都中断
                        
                        
1
                        
                        
x
                        
                        
1
                        
                        
高电平中断
                        
                        
0
                        
                        
1
                        
                        
x
                        
                        
低电平中断
                        
                        
0
                        
                        
0
                        
                        
x
                        
关于更多寄存器的定义说明,可以参考技术手册ug585-Zynq-7000-TRM.pdf
5硬件电路分析
cbafc7add5774518805039a9b06d9f3c.jpg
配套工程的FPGA PIN脚定义路径为soc_prj/uisrc/04_pin/ fpga_pin.xdc。
6搭建SOC系统工程
详细的搭建过程这里不再重复,对于初学者如果还不清楚如何创建SOC工程的,请学习“01Vitis Soc开发入门”这篇文章。
本文中的PS设置内容是新增加的EMIO部分,关于DDR、MIO、CPU时钟等设置请参考“01Vitis Soc开发入门”这篇文章。
6.1MIO/EMIO配置
01Vitis Soc开发入门”这篇文章中已经对特定功能的MIO做了设置,只有剩余的MIO可以用于其他的自定义功能。以下设置未分配功能的MIO,以及需要扩展的EMIO的数量。
307dad1edd614125a81f3f171da935c3.jpg
6.2EMIO在PL中定义
设置好后,右击GPIO_0引出GPIO_0并且改名为ps_emio.
e1739b0551d641d2b42d5305fff5f76a.jpg
由于ps_emio最终需要定义到FPGA的IO中,所以我们需要引出ps_emio信号到顶层文件
aedd31c05d384b4da5d28641bbfb1681.jpg

  1. `timescale 1 ps / 1 ps

  2. module system_wrapper
  3.    (DDR_addr,
  4.     DDR_ba,
  5.     DDR_cas_n,
  6.     DDR_ck_n,
  7.     DDR_ck_p,
  8.     DDR_cke,
  9.     DDR_cs_n,
  10.     DDR_dm,
  11.     DDR_dq,
  12.     DDR_dqs_n,
  13.     DDR_dqs_p,
  14.     DDR_odt,
  15.     DDR_ras_n,
  16.     DDR_reset_n,
  17.     DDR_we_n,
  18.     FIXED_IO_ddr_vrn,
  19.     FIXED_IO_ddr_vrp,
  20.     FIXED_IO_mio,
  21.     FIXED_IO_ps_clk,
  22.     FIXED_IO_ps_porb,
  23.     FIXED_IO_ps_srstb,
  24.     ps_emio_tri_io);
  25.   inout [14:0]DDR_addr;
  26.   inout [2:0]DDR_ba;
  27.   inout DDR_cas_n;
  28.   inout DDR_ck_n;
  29.   inout DDR_ck_p;
  30.   inout DDR_cke;
  31.   inout DDR_cs_n;
  32.   inout [3:0]DDR_dm;
  33.   inout [31:0]DDR_dq;
  34.   inout [3:0]DDR_dqs_n;
  35.   inout [3:0]DDR_dqs_p;
  36.   inout DDR_odt;
  37.   inout DDR_ras_n;
  38.   inout DDR_reset_n;
  39.   inout DDR_we_n;
  40.   inout FIXED_IO_ddr_vrn;
  41.   inout FIXED_IO_ddr_vrp;
  42.   inout [53:0]FIXED_IO_mio;
  43.   inout FIXED_IO_ps_clk;
  44.   inout FIXED_IO_ps_porb;
  45.   inout FIXED_IO_ps_srstb;
  46.   inout [5:0]ps_emio_tri_io;

  47.   wire [14:0]DDR_addr;
  48.   wire [2:0]DDR_ba;
  49.   wire DDR_cas_n;
  50.   wire DDR_ck_n;
  51.   wire DDR_ck_p;
  52.   wire DDR_cke;
  53.   wire DDR_cs_n;
  54.   wire [3:0]DDR_dm;
  55.   wire [31:0]DDR_dq;
  56.   wire [3:0]DDR_dqs_n;
  57.   wire [3:0]DDR_dqs_p;
  58.   wire DDR_odt;
  59.   wire DDR_ras_n;
  60.   wire DDR_reset_n;
  61.   wire DDR_we_n;
  62.   wire FIXED_IO_ddr_vrn;
  63.   wire FIXED_IO_ddr_vrp;
  64.   wire [53:0]FIXED_IO_mio;
  65.   wire FIXED_IO_ps_clk;
  66.   wire FIXED_IO_ps_porb;
  67.   wire FIXED_IO_ps_srstb;
  68.   wire [0:0]ps_emio_tri_i_0;
  69.   wire [1:1]ps_emio_tri_i_1;
  70.   wire [2:2]ps_emio_tri_i_2;
  71.   wire [3:3]ps_emio_tri_i_3;
  72.   wire [4:4]ps_emio_tri_i_4;
  73.   wire [5:5]ps_emio_tri_i_5;
  74.   wire [0:0]ps_emio_tri_io_0;
  75.   wire [1:1]ps_emio_tri_io_1;
  76.   wire [2:2]ps_emio_tri_io_2;
  77.   wire [3:3]ps_emio_tri_io_3;
  78.   wire [4:4]ps_emio_tri_io_4;
  79.   wire [5:5]ps_emio_tri_io_5;
  80.   wire [0:0]ps_emio_tri_o_0;
  81.   wire [1:1]ps_emio_tri_o_1;
  82.   wire [2:2]ps_emio_tri_o_2;
  83.   wire [3:3]ps_emio_tri_o_3;
  84.   wire [4:4]ps_emio_tri_o_4;
  85.   wire [5:5]ps_emio_tri_o_5;
  86.   wire [0:0]ps_emio_tri_t_0;
  87.   wire [1:1]ps_emio_tri_t_1;
  88.   wire [2:2]ps_emio_tri_t_2;
  89.   wire [3:3]ps_emio_tri_t_3;
  90.   wire [4:4]ps_emio_tri_t_4;
  91.   wire [5:5]ps_emio_tri_t_5;

  92.   IOBUF ps_emio_tri_iobuf_0
  93.        (.I(ps_emio_tri_o_0),
  94.         .IO(ps_emio_tri_io[0]),
  95.         .O(ps_emio_tri_i_0),
  96.         .T(ps_emio_tri_t_0));
  97.   IOBUF ps_emio_tri_iobuf_1
  98.        (.I(ps_emio_tri_o_1),
  99.         .IO(ps_emio_tri_io[1]),
  100.         .O(ps_emio_tri_i_1),
  101.         .T(ps_emio_tri_t_1));
  102.   IOBUF ps_emio_tri_iobuf_2
  103.        (.I(ps_emio_tri_o_2),
  104.         .IO(ps_emio_tri_io[2]),
  105.         .O(ps_emio_tri_i_2),
  106.         .T(ps_emio_tri_t_2));
  107.   IOBUF ps_emio_tri_iobuf_3
  108.        (.I(ps_emio_tri_o_3),
  109.         .IO(ps_emio_tri_io[3]),
  110.         .O(ps_emio_tri_i_3),
  111.         .T(ps_emio_tri_t_3));
  112.   IOBUF ps_emio_tri_iobuf_4
  113.        (.I(ps_emio_tri_o_4),
  114.         .IO(ps_emio_tri_io[4]),
  115.         .O(ps_emio_tri_i_4),
  116.         .T(ps_emio_tri_t_4));
  117.   IOBUF ps_emio_tri_iobuf_5
  118.        (.I(ps_emio_tri_o_5),
  119.         .IO(ps_emio_tri_io[5]),
  120.         .O(ps_emio_tri_i_5),
  121.         .T(ps_emio_tri_t_5));
  122.   system system_i
  123.        (.DDR_addr(DDR_addr),
  124.         .DDR_ba(DDR_ba),
  125.         .DDR_cas_n(DDR_cas_n),
  126.         .DDR_ck_n(DDR_ck_n),
  127.         .DDR_ck_p(DDR_ck_p),
  128.         .DDR_cke(DDR_cke),
  129.         .DDR_cs_n(DDR_cs_n),
  130.         .DDR_dm(DDR_dm),
  131.         .DDR_dq(DDR_dq),
  132.         .DDR_dqs_n(DDR_dqs_n),
  133.         .DDR_dqs_p(DDR_dqs_p),
  134.         .DDR_odt(DDR_odt),
  135.         .DDR_ras_n(DDR_ras_n),
  136.         .DDR_reset_n(DDR_reset_n),
  137.         .DDR_we_n(DDR_we_n),
  138.         .FIXED_IO_ddr_vrn(FIXED_IO_ddr_vrn),
  139.         .FIXED_IO_ddr_vrp(FIXED_IO_ddr_vrp),
  140.         .FIXED_IO_mio(FIXED_IO_mio),
  141.         .FIXED_IO_ps_clk(FIXED_IO_ps_clk),
  142.         .FIXED_IO_ps_porb(FIXED_IO_ps_porb),
  143.         .FIXED_IO_ps_srstb(FIXED_IO_ps_srstb),
  144.         .ps_emio_tri_i({ps_emio_tri_i_5,ps_emio_tri_i_4,ps_emio_tri_i_3,ps_emio_tri_i_2,ps_emio_tri_i_1,ps_emio_tri_i_0}),
  145.         .ps_emio_tri_o({ps_emio_tri_o_5,ps_emio_tri_o_4,ps_emio_tri_o_3,ps_emio_tri_o_2,ps_emio_tri_o_1,ps_emio_tri_o_0}),
  146.         .ps_emio_tri_t({ps_emio_tri_t_5,ps_emio_tri_t_4,ps_emio_tri_t_3,ps_emio_tri_t_2,ps_emio_tri_t_1,ps_emio_tri_t_0}));
  147. endmodule
复制代码

以上代码中,原语IOBUF对于很多初学者来说,可能会有疑惑,所以有必要介绍下如何使用。
  1.    IOBUF IOBUF_inst (
  2.       .O(O),   // 1-bit output: Buffer output  方向是外部引脚IO 输入到FPGA内部逻辑
  3.       .I(I),   // 1-bit input: Buffer input方向是FPGA内部逻辑输出到外部引脚IO
  4.       .IO(IO), // 1-bit inout: Buffer inout (connect directly to top-level port) 外部引脚IO
  5.       .T(T)    // 1-bit input: 3-state enable input 定义输入还是输,当T=0 IO是三态的可以用于输入,当T=1 是输出
  6.    );
复制代码

6.3编译并导出平台文件
以下步骤简写,有不清楚的看第一篇文章。
1:单击Block文件à右键àGenerate the Output ProductsàGlobalàGenerate。
2:单击Block文件à右键à Create a HDL wrapper(生成HDL顶层文件)àLet vivado manager wrapper and auto-update(自动更新)。
3:添加配套工程路径下uisrc/04_pin/fpga_pin.xdc约束文件
4:生成Bit文件。
5:导出到硬件: FileàExport HardwareàInclude bitstream
6:导出完成后,对应工程路径的soc_hw路径下有硬件平台文件:system_wrapper.xsa的文件。根据硬件平台文件system_wrapper.xsa来创建需要Platform平台。
dda052ae15f44b2baf4c1f5a0b0afcc9.jpg
7搭建Vitis-sdk工程
创建soc_base sdk platform和APP工程的过程不再重复,如果不清楚请参考本章节第一个demo。
7.1创建SDK Platform工程
8f2c499457ca41758b0042617a4703af.jpg
右击soc_base编译,编译的时间可能会有点长
7.2创建mio_emio_intr APP工程
1:创建测试APP工程
eb2209d912574280be7634cd75ea5752.jpg
5812c4109f064f569919387c6da22b45.jpg
d526eb8e5cf341f5ba7bf76991cfc2e9.jpg
bd41eef6fba747238b51a34e91fd2d5f.jpg
cc63949344a94061ba52699f95729855.jpg
5269637cda8c4a459eeb58cf11715140.jpg
2: 在我们提供例程的文件夹中找到源码文件,并进行复制。
38ce9f57ee2b458bbcd455b5377acc57.jpg
ae122fe11bac4ba483005d9910e38e4e.jpg
3:右击编译工程
4:右击工程,选择Debug as ->Debug configurations。
01fd6e69a0c246a78b4618b7e2f15eee.jpg
打开串口
4cc8335c06e44e738009956eedec83ef.jpg
5:单击窗口上的运行按钮 7bd42d0ceb0e43d3aa5cb842b1952eaf.jpg ​编辑,运行程序

80b2097893624c8facd0bedc9dd629b2.jpg
8程序分析
8.1init_intr_sys函数
  1. void init_intr_sys(void) {
  2. Init_Intr_System(&Intc);
  3. Gpiops_Setup_Intr_System(&Intc, &Gpio, GPIO_INTERRUPT_ID);
  4. Setup_Intr_Exception(&Intc); }
复制代码

此函数在程序中通过调用相关中断函数实现中断功能。为了实现多类型中断,用户必须根据以下方式设置中断。
8.2Init_Intr_System(&Intc)函数
  1. int Init_Intr_System(XScuGic * IntcInstancePtr) {
  2. int Status;
  3. XScuGic_Config *IntcConfig; /*
  4. * Initialize the interrupt controller driver so that it is ready to
  5. * use. */
  6. IntcConfig = XScuGic_LookupConfig(INTC_DEVICE_ID); if (NULL == IntcConfig) {
  7. return XST_FAILURE; }
  8. Status = XScuGic_CfgInitialize(IntcInstancePtr, IntcConfig, IntcConfig->CpuBaseAddress);
  9. if (Status != XST_SUCCESS) {
  10. return XST_FAILURE; }
  11. return XST_SUCCESS; }
复制代码

1:XScuGic_LookupConfig(INTC_DEVICE_ID) 函数
f8f9838cf88a4bb9adc4d90cb966bfa8.jpg
右击XScuGic_ConfigTable查看参数定义:
f78cc8bc634c480498923df50de39b86.jpg
可以继续右击以下参数查看其定义
ZYNQ中定义
eef89ccb6d444eca9b7bd9f515bf78b0.jpg
MPSOC中定义
a67e5f577fbb43bb8c9e91322a2092bb.jpg

2: XScuGic_CfgInitialize(IntcInstancePtr, IntcConfig,IntcConfig->CpuBaseAddress)
中断部分主要内容是回调函数的设置,以下回调函数相关参数定义如下:
d4213a645da045e982bf94fe978cafef.jpg
以下代码对所有为定义的全局中断进行定义回调函数和回调参数。
8e751dcaff904f3196afbcc578645605.jpg
StubHandler函数定义如下:
909370aecf9945d5915511cb4d51ad6b.jpg
8.3Setup_Intr_Exception函数
  1. void Setup_Intr_Exception(XScuGic * IntcInstancePtr) {
  2. /* Enable interrupts from the hardware */
  3. Xil_ExceptionInit();
  4. Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT,
  5. (Xil_ExceptionHandler)XScuGic_InterruptHandler, (void *)IntcInstancePtr);
  6. Xil_ExceptionEnable(); }
复制代码

这个函数中对设置全局中断回调函数,以及使能全局中断
1:Xil_ExceptionInit()函数
这个函数目前说明都没做,只是未来保留兼容性,预留在这里。
bc6e54875bd042bc827eae3e9f292bb5.jpg
2:Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT,(Xil_ExceptionHandler)XScuGic_InterruptHandler,(void *)IntcInstancePtr)函数
703dcbe98f2b4adfaa73fcfe5bd026a1.jpg
2.1XIL_EXCEPTION_ID_INT定义
6595489fe0dc4605be495ae433d5f7c0.jpg
2.2: XExc_VectorTableEntry定义
任何的中断都可以理解为异常处理,这里定义了MPSOC或者ZYNQ支持的异常处理类型。
c337b98f4e7d4992988d626bd8c7fb6f.jpg
2.3XScuGic_InterruptHandler函数
可以看到这个函数会根据读取到的中断号调用相应的回调函数,这个回调函数会在具体的外设中断初始化中设置。
d621fb1458794b04a60a9cf19bf339e5.jpg
2.3中断异常回调函数
541b3613570f4ff7b622ee28bdc5c66b.jpg
由于XIL_EXCEPTION_ID_INT=5所以Xil_ExceptionNullHandler在中断产生的时候会被调用。
396135017ea641f5865d2a896abdabc6.jpg
58658b5fd208481cbebd85886faaba93.jpg
3:Xil_ExceptionEnable()函数
这条函数最终指向了汇编指令:
277aa34a492f498ebb1eb3fe2c35a90d.jpg
8b2a2db9ceb44b35a8d3c78ed0370948.jpg
8.4Gpiops_init函数
  1. int Gpiops_init(XGpioPs *InstancePtr, u32 DeviceId)
  2. {
  3.         u8 Bank;
  4.         u8 PinNumber0;
  5.         u8 PinNumber1;
  6.         u8 PinNumber2;

  7.         int Status;
  8.         XGpioPs_Config *ConfigPtr;
  9.         /* Initialize the GPIO driver. */
  10.         ConfigPtr = XGpioPs_LookupConfig(GPIO_DEVICE_ID);
  11.         Status = XGpioPs_CfgInitialize(InstancePtr, ConfigPtr,ConfigPtr->BaseAddr);

  12.      //set LED0~LED4 输出
  13.         XGpioPs_SetDirectionPin(InstancePtr, LED0, 0x1);
  14.         XGpioPs_SetDirectionPin(InstancePtr, LED1, 0x1);
  15.         XGpioPs_SetDirectionPin(InstancePtr, LED2, 0x1);
  16.         XGpioPs_SetDirectionPin(InstancePtr, LED3, 0x1);
  17.         XGpioPs_SetDirectionPin(InstancePtr, LED4, 0x1);

  18.         //set BTN0~BTN2 输入
  19.         XGpioPs_SetDirectionPin(InstancePtr,BTN0, 0x0);
  20.         XGpioPs_SetDirectionPin(InstancePtr,BTN1, 0x0);
  21.         XGpioPs_SetDirectionPin(InstancePtr,BTN2, 0x0);

  22.      // set LED0~LED4 输出使能
  23.         XGpioPs_SetOutputEnablePin(InstancePtr, LED0, 1);
  24.         XGpioPs_SetOutputEnablePin(InstancePtr, LED1, 1);
  25.         XGpioPs_SetOutputEnablePin(InstancePtr, LED2, 1);
  26.         XGpioPs_SetOutputEnablePin(InstancePtr, LED3, 1);
  27.         XGpioPs_SetOutputEnablePin(InstancePtr, LED4, 1);

  28. // set LED0~LED4 输出高电平1
  29.         XGpioPs_WritePin(InstancePtr, LED0, 1);
  30.         XGpioPs_WritePin(InstancePtr, LED1, 1);
  31.         XGpioPs_WritePin(InstancePtr, LED2, 1);
  32.         XGpioPs_WritePin(InstancePtr, LED3, 1);
  33.         XGpioPs_WritePin(InstancePtr, LED4, 1);

  34. //获取当前GPIO所在的BANK以及所在BANK的IO序号
  35.         XGpioPs_GetBankPin(BTN0, &Bank, &PinNumber0);
  36.         XGpioPs_GetBankPin(BTN1, &Bank, &PinNumber1);

  37.         //边沿触发,下降沿触发
  38.         XGpioPs_SetIntrType(InstancePtr, Bank, 0xFFFFFFFF, 0x0, 0x00);
  39.         XGpioPs_IntrEnable(InstancePtr, Bank, (1 << PinNumber0)|(1 << PinNumber1));

  40.         XGpioPs_GetBankPin(BTN2, &Bank, &PinNumber2);
  41.         //边沿触发,下降沿触发
  42.         XGpioPs_SetIntrType(InstancePtr, Bank, 0xFFFFFFFF, 0x0, 0x00);
  43.         XGpioPs_IntrEnable(InstancePtr, Bank, (1 << PinNumber2));

  44.     return Status;
  45. }
复制代码

gpiops_intr.c文件中,int Gpiops_init(XGpioPs *InstancePtr, u32 DeviceId)函数负责初始化GPIO的输入,输出,以及设置GPIO的中断触发方式。
为了正确初始化相关GPIO BANK的中断寄存器,这里调用来XGpioPs_GetBankPin()函数,通过预先定义的GPIO序号,获取当前GPIO所在的BANK以及所在BANK的IO序号。然后执行XGpioPs_SetIntrType(InstancePtr, Bank, 0xFFFFFFFF, 0x0, 0x00);设置中断触发的方式。最后执行XGpioPs_IntrEnable(InstancePtr, Bank, (1 << PinNumber0)|(1 << PinNumber1));使能相关GPIO中断。
1:XGpioPs_LookupConfig(DeviceId)函数
此函数中XGpioPs_ConfigTable定义了GPIO的参数信息,右击可以查看具体信息
2625510124ee4450941bf017c0ac8e38.jpg

fc305eab8e6e46dc801f6818f8603870.jpg
继续右击以下GPIO地址定义,可以查到GPIO外设的基地址:
1f70d79e044647f9a6ff316c0e777ec8.jpg

fb2a3af7bc19429fb556f8d07307fd4a.jpg
2:XGpioPs_CfgInitialize(InstancePtr, ConfigPtr, ConfigPtr->BaseAddr)函数
dbaecc8732294f8faf28eec82909cd4f.jpg
3:XGpioPs_SetDirectionPin(InstancePtr, LED0,1)函数
设置IO方向,最后一个参数0代表输入,1代表输出。
d5578aeeb28a4d4bb5d060b21ab42587.jpg
以上代码首先需计算IO所在的BANK和在这个BANK中IO序号。通过函数XGpioPs_GetBankPin((u8)Pin, &Bank, &PinNumber)计算。这里以MIO51来说,其位于BANK1中
每个BANK的PS IO方向控制寄存器偏移计算=BANK *XGPIOPS_REG_MASK_OFFSET+ XGPIOPS_DIRM_OFFSET我们主要看XGPIOPS_DIRM_OFFSET。
其中XGPIOPS_DIRM_OFFSET = 0x00000204代表了相对每个BANK的偏移地址,我们看方向寄存器的定义:
                        
Field Name
                        
                        
Bits
                        
                        
Type
                        
                        
Reset Value
                        
                        
Description
                        
                        
DIRECTION_1
                        
                        
31:0
                        
                        
Rw
                        
                        
0x0
                        
                        
0:输入
                        
1:输出
                        
4:XGpioPs_SetOutputEnablePin(InstancePtr, LED, 1)函数
cacb2e97f1064e33b2b9ad01e7b7781c.jpg
以上代码首先需计算IO所在的BANK和在这个BANK中IO序号。通过函数XGpioPs_GetBankPin((u8)Pin, &Bank, &PinNumber)计算。以MIO51来说,其位于BANK1
每个BANK的PS IO方向控制寄存器偏移计算=BANK *XGPIOPS_REG_MASK_OFFSET+ XGPIOPS_OUTEN_OFFSET我们主要看XGPIOPS_ XGPIOPS_OUTEN_OFFSET其中XGPIOPS_OUTEN_OFFSET = 0x00000208代表了相对每个BANK的偏移地址,我们看方向寄存器的定义:
                        
Field Name
                        
                        
Bits
                        
                        
Type
                        
                        
Reset Value
                        
                        
Description
                        
                        
OP_ENABLE_1
                        
                        
31:0
                        
                        
Rw
                        
                        
0x0
                        
                        
0:禁止输出
                        
1:使能输出
                        
5:XGpioPs_SetIntrType(InstancePtr, Bank, 0xFFFFFFFF, 0x0, 0x00)
29de8206c07b4bb9a45c5f7063fca801.jpg
XGpioPs_SetIntrType共有5个参数,我们重点看IntrType、IntrPolarity、IntrOnAny这三个参数:
IntrType寄存器相关位:0电平触发;1边沿触发
IntrPolarity寄存器相关位:0低电平或下降沿触发;1上升沿或高电平触发
IntrOnAny寄存器相关位:0单边沿触发;1双边沿触发
我们这里设置的是单边下降沿触发,所以IntrType=0xffffffff;IntrPolarity=0x0;IntrOnAny=0x0;
参数的设置通过以下三个函数设置寄存器,具体参考ZYNQ ug585中的说明:
IntrType寄存器地址:(Bank) * XGPIOPS_REG_MASK_OFFSET) +XGPIOPS_INTTYPE_OFFSET
IntrPolarity寄存器地址:(Bank) * XGPIOPS_REG_MASK_OFFSET) +XGPIOPS_INTPOL_OFFSET, IntrPolarity)
IntrOnAny寄存器地址:(Bank) * XGPIOPS_REG_MASK_OFFSET) +XGPIOPS_INTANY_OFFSET, IntrOnAny)
我们这里以BANK1的MIO50计算下相关寄存器的地址:
IntrType寄存器地址  = 1* 0x00000040U  + 0x0000021CU
IntrPolarity寄存器地址    =1* 0x00000040U  + 0x00000220U
IntrOnAny寄存器地址     =1* 0x00000040U  + 0x00000224U

PSMIO-BANK1:INT_TPYE_1中断类型寄存器偏移地址XGPIOPS_INTTYPE_OFFSET(0x0000025C)=0x00000040U+0x0000021C
                        
Field Name
                        
                        
Bits
                        
                        
Type
                        
                        
Reset Value
                        
                        
Description
                        
                        
INT_TPYE_1
                        
                        
31:0
                        
                        
Rw
                        
                        
0xFFFFFFFF
                        
                        
0:电平触发
                        
1:边沿触发
                        

PSMIO-BANK1:INT_POLARITY_1中断触发方式寄存器偏移地址XGPIOPS_INTPOL_OFFSET(0x00000260)= 0x00000040U+0x00000220
                        
Field Name
                        
                        
Bits
                        
                        
Type
                        
                        
Reset Value
                        
                        
Description
                        
                        
INT_TPYE_1
                        
                        
31:0
                        
                        
Rw
                        
                        
0x0
                        
                        
0:低电平或者下降沿
                        
1:高电平或者上升沿
                        

PSMIO-BANK1:INT_ON_ANY_1边沿类型寄存器偏移地址XGPIOPS_INTANY_OFFSET (0x00000264)= 0x00000040U+0x00000224
                        
Field Name
                        
                        
Bits
                        
                        
Type
                        
                        
Reset Value
                        
                        
Description
                        
                        
INT_ON_ANY_1
                        
                        
31:0
                        
                        
Rw
                        
                        
0x0
                        
                        
0:当触发类型为边沿触发,单边沿触发
                        
1:当触发类型为边沿触发,双边沿触发
                        

6:XGpioPs_IntrEnable(InstancePtr, Bank, (1 << PinNumber0)|(1 << PinNumber1))
94c0036bbab946b592cf81a1c49d3313.jpg
GPIO的INT_ENABLE_1中断使能寄存器偏移地址XGPIOPS_INTEN_OFFSET=0x00000250
                        
Field Name
                        
                        
Bits
                        
                        
Type
                        
                        
Reset Value
                        
                        
Description
                        
                        
INT_ENABLE_1
                        
                        
31:0
                        
                        
Rw
                        
                        
0x0
                        
                        
0:不改变
                        
1:清除中断屏蔽,使能中断
                        
8.5Gpiops_Setup_Intr_System函数
  1. void Gpiops_Setup_Intr_System(XScuGic *GicInstancePtr, XGpioPs *InstancePtr, u16 IntrId)
  2. {
  3.         XScuGic_Connect(GicInstancePtr, IntrId,
  4.                         (Xil_ExceptionHandler)XGpioPs_IntrHandler,//set up the interrupt
  5.                         (void *)InstancePtr);
  6.             /* Set the handler for gpio interrupts. */
  7.             XGpioPs_SetCallbackHandler(InstancePtr, (void *)InstancePtr, Gpiops_IntrHandler);
  8.     XScuGic_Enable(GicInstancePtr, IntrId);//enable the interrupt for GPIO at GIC
  9. }
复制代码

1:XScuGic_Connect(GicInstancePtr, IntrId, (Xil_ExceptionHandler)XGpioPs_IntrHandler, (void *)InstancePtr);
这里把GIC的48号中断对应的回调函数XGpioPs_IntrHandler和回调参数InstancePtr关联,这样当XGpioPs_IntrHandler函数回调的时候,可以通过参数InstancePtr继续回调
0df037f7f3f34526b254966220397fff.jpg
以下是GPIO的GIC中断号
51c6e1c1ef584694ad1679da066d3698.jpg
读者可以看前面Setup_Intr_Exception函数中关于Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT,(Xil_ExceptionHandler)XScuGic_InterruptHandler,(void *)IntcInstancePtr)函数的说明再看这里就能明白了。
再看下回调函数XGpioPs_IntrHandler(const XGpioPs *InstancePtr)
89a465cf4fe74c7fa2ba367dce88f9f7.jpg
这是GPIO的中断回调函数,当GPIO中断产生后这个函数会被调用,在这个函数中再进一步判断是哪一个BANK,哪一个IO产生了中断,并且再次调用
InstancePtr->Handler(InstancePtr->CallBackRef, Bank,(IntrStatus & IntrEnabled))函数,再次调用回调函数,并且传输IO所在BANK和哪一个IO产生了中断。
这里面我们有必要看下XGpioPs_IntrGetStatus(InstancePtr, Bank)和XGpioPs_IntrGetEnabled(InstancePtr,Bank)函数。
XGpioPs_IntrGetStatus(InstancePtr, Bank)读取当前bank的中断状态寄存器;
17f6ee2ffbfa4d47abf23d81eac46e46.jpg
PSMIO-BANK1:INT_STATUS_1中断状态寄存器偏移地址:XGPIOPS_INTSTS_OFFSET= 0x00000258
                        
Field Name
                        
                        
Bits
                        
                        
Type
                        
                        
Reset Value
                        
                        
Description
                        
                        
INT_STATUS_1
                        
                        
31:0
                        
                        
wtc
                        
                        
0x0
                        
                        
读操作:
                        
0:无中断
                        
1:有中断产生
                        
写操作:
                        
0:无变化
                        
1:清除中断
                        
XGpioPs_IntrGetEnabled(InstancePtr,Bank)读取对应bank已经设置了中断使能
0c154fd51bbb42f3aa74d7a3ce555da5.jpg
INT_MASK_1中断掩码寄存器偏移地址:XGPIOPS_INTMASK_OFFSET=0x0000020C
                        
Field Name
                        
                        
Bits
                        
                        
Type
                        
                        
Reset Value
                        
                        
Description
                        
                        
INT_MASK_1
                        
                        
31:0
                        
                        
Ro
                        
                        
0x0
                        
                        
只读寄存器
                        
0:中断使能
                        
1:中断屏蔽
                        

最后通过if ((IntrStatus & IntrEnabled) != (u32)0)判断那个BANK 哪一个IO产生了中断。

2:XGpioPs_SetCallbackHandler(InstancePtr, (void *)InstancePtr, Gpiops_IntrHandler)函数
这个函数就是前面的中断回调函数中进一步调用
7c9b3e035f8443c0b1c1897c30186263.jpg
我们看下参数XGpioPs_Handler FuncPointer,可以看到整个回调函数就是我们前面GPIO中断回调函数进一步回调的函数。定义这个回调函数的目的主要是未来给用户自己编写的中断回调传递相关的参数,配合vitis-sdk的中断函数使用。

bb17bd3cb7494ee18a25caabfdc52e5c.jpg
3:XScuGic_Enable(GicInstancePtr, IntrId)函数
在以下代码中,首先绑定中断号,CPU号,我们也可以进一步分中断
94ca6a2e7b414830a40276509104620c.jpg
5.1XScuGic_InterruptMaptoCpu(InstancePtr, Cpu_Id, Int_Id)函数
在这个函数中具体中断和CPU如何关联,如何设置SPI(共享外设中断)中断寄存器的相关位我们无法进一步分分析,到此为止。
22c87bb29e1c4456923c2c7dc1c00dae.jpg
8.6 Gpiops_IntrHandler(void *CallBackRef, u32 Bank, u32 Status)函数
这个函数中是用户代码中的回调函数,可以直接获取IO所在BANK以及整个BANK中哪一个IO位产生了中断。
  1. static void Gpiops_IntrHandler(void *CallBackRef, u32 Bank, u32 Status) {
  2. printf("Data read from GPIO intrrupt bank= 0x%x , IO_bit=0x%x \n\r", Bank ,Status); }
复制代码

9方案演示
9.1硬件准备
实验需要用到JTAG下载器、USB转串口外设,另外需要把核心板上的2P模式开关设置到JTAG模式,即ON ON
24b41005d068431c8eaae93279a1fff1.jpg
9.2实验结果
每次有按键按下的时候,会产生一个中断,中断函数中会读取每个按键的按键值
601311fc5a5b48a7bfa0488343877e9a.jpg


您需要登录后才可以回帖 登录 | 立即注册

本版积分规则