正点原子 发表于 2023-4-10 11:45:25

《ATK-DFPGL22G之FPGA开发指南_V1.0》第九章定时器中断实验

本帖最后由 正点原子 于 2023-4-10 11:45 编辑

1)实验平台:正点原子 DFZU2EG_4EV MPSoC开发板
2)购买链接:https://item.taobao.com/item.htm?&id=692368045899
3)全套实验源码+手册+视频下载地址:http://www.openedv.com/thread-340252-1-1.html
4)正点原子官方B站:https://space.bilibili.com/394620890
5)正点原子FPGA交流群:994244016




第九章定时器中断实验MPSOC中PS部分包含许多不同类型的定时器,包括全局定时器、TTC定时器、系统看门狗定时器等。定时器可以不受CPU的干预,自己独立运行,来完成计时、定时、中断以及计算来自MIO或EMIO引脚的信号脉冲宽度等。本章我们将向大家介绍TTC(三路定时器)以TTC(三路定时器)中断的使用方法。
本章包括以下几个部分:
9.1简介
9.2实验任务
9.3硬件设计
9.4软件设计
9.5下载验证
9.6在线调试

9.1简介
PS有许多不同类型的定时器和计数器,包括APU MPCore AArch64定时器,三路定时器(TTC),系统看门狗定时器。APU MPCore AArch64定时器包括APU MPCore全局定时器和APU私有定时器。MPSOC有4个三路定时器,每个三路定时器中有3个相似的计数器,三路定时器可以计算来自MIO引脚或EMIO引脚信号脉冲宽度,并产生中断。PS中有3个系统看门狗定时器(SWDT),用于检测系统故障并从中恢复。
定时器的系统框图如图 9.1.1所示:
图 9.1.1 定时器系统框图
图中的几种定时器都有连接到中断控制器(Interrupt Controller),我们可以很方便的使用定时器来完成定时器中断的实验。本次实验是基于三路定时器(TTC)来完成定时器的中断实验。
三路定时器(TTC)的特性如下:
1、32位APB编程接口;
2、可选的时钟输入:
       内部总线时钟(LPD_LSBUS_CLK);
       内部时钟(来自PL);
       外部时钟(来自MIO);
3、支持三个独立的32位定时器/计数器;
4、支持16位时钟预分频器;
5、三个系统中断,用于每路定时器;
6、计数器和可编程值相等时,产生中断;
7、递增和递减计数;
8、产生波形输出(例如PWM);
9.2实验任务
本章的实验任务是通过定时器的中断,每1s控制一次PS LED灯的亮灭。
9.3硬件设计
从实验任务我们可以画出如下的系统框图,DDR4中存放和运行程序、三路定时器(TTC)产生定时中断、UART打印信息、MIO驱动LED外设。虽然本实验可以不需要UART控制器,不过为了方便打印一些信息,此处我们加上UART。
图 9.3.1 系统框图
本次实验在《“Hello World”实验》的基础上修改而来,添加MIO和TTC单元。
打开《“Hello World”实验》工程,另存为名为“timer_intr_led”的工程。点击Open Block Design,在打开的Diagram窗口中双击Zynq Ultrascale+ MPSOC打开重定义窗口,分别添加MIO和TTC,然后点击“OK”,如下图所示:
图 9.3.2 添加MIO和TTC
嵌入式系统最终搭建的框图如下:
图 9.3.3 嵌入式系统框图界面
保存设计,然后右键点击design_1_wrapper选择Generate Output Products。导出Hardware,并将导出的design_1_wrapper.xsa放到vitis文件夹,启动Vitis。

9.4软件设计
启动Vitis开发环境后,新建一个名为“timer_intr_led”的应用工程,工程建好后如下图所示:
图9.4.1 新建工程
双击硬件平台工程下的platform.spr,在右侧出现的界面中点击板级支持包Board Support Package,然后可以看到外设驱动Peripheral Drivers,如图9.4.2所示,下拉可以看到三路定时器(TTC)的文档和示例,如图9.4.3所示:
图9.4.2 展开Peripheral Drivers
图9.4.3 导入TTC示例
如果我们点击Import Examples,会弹出下图所示的导入示例界面,关于TTC有4个示例,如下图所示:
图9.4.4 导入示例
感兴趣的朋友可以参考下官方提供的TTC例程,其中xttcps_intr_example是TTC中断的示例。
这里我们不导入官方的例程,而是新建一个源文件。在timer_intr_led/src目录上右键,选择New-> File。在弹出的对话框中File name一栏我们输入文件名“main.c”,然后点击“Finish”。
新建源文件之后,在左侧timer_intr_led/src目录下可以看到main.c文件,同时在主页面已经打开了该文件的文本编辑框。我们在新建的main.c文件中输入以下代码:
1   #include <stdio.h>
2   #include <stdlib.h>
3   #include "xparameters.h"
4   #include "xstatus.h"
5   #include "xil_exception.h"
6   #include "xttcps.h"
7   #include "xscugic.h"
8   #include "xil_printf.h"
9   #include "xgpiops.h"
10
11/************************** Constant Definitions *****************************/
12#define NUM_DEVICES    12U
13#define TTC_TICK_DEVICE_IDXPAR_XTTCPS_1_DEVICE_ID
14#define TTC_TICK_INTR_ID    XPAR_XTTCPS_1_INTR
15#define INTC_DEVICE_ID      XPAR_SCUGIC_SINGLE_DEVICE_ID
16
17#define GPIO_DEVICE_ID      XPAR_XGPIOPS_0_DEVICE_ID   //宏定义 GPIO_PS ID
18
19#define MIO_LED             38         //连接 PS_LED1 到MIO38
20#define MIO_LED2            39         //连接 PS_LED2 到MIO39
21
22/**************************** Type Definitions *******************************/
23typedef struct {
24      u32 OutputHz;
25      XInterval Interval;
26      u8 Prescaler;
27      u16 Options;
28} TmrCntrSetup;
29
30/************************** Function Prototypes ******************************/
31static intSetupTicker(void);
32static intIniTimer(int DeviceID);
33static intSetupInterruptSystem(u16 IntcDeviceID, XScuGic *IntcInstancePtr);
34static void TickHandler(void *CallBackRef, u32 StatusEvent);
35
36/************************** Variable Definitions *****************************/
37
38static XTtcPs TtcPsInst;   /* Number of available timer counters */
39
40XScuGic InterruptController;//中断控制器实例
41XGpioPs Gpio;               //GPIO设备的驱动程序实例
42
43//MIO初始化
44int mio_init(XGpioPs *mio_ptr)
45{
46      XGpioPs_Config *mio_cfg_ptr;
47
48      mio_cfg_ptr = XGpioPs_LookupConfig(GPIO_DEVICE_ID);
49      if (NULL == mio_cfg_ptr)
50          return XST_FAILURE;
51
52      XGpioPs_CfgInitialize(mio_ptr, mio_cfg_ptr,
53            mio_cfg_ptr->BaseAddr);
54
55      //设置指定引脚的方向: 0 输入, 1 输出
56      XGpioPs_SetDirectionPin(mio_ptr, MIO_LED, 1);
57
58      //使能指定引脚输出: 0 禁止输出使能, 1 使能输出
59      XGpioPs_SetOutputEnablePin(mio_ptr, MIO_LED, 1);
60
61      //向指定引脚写入数据: 0 或 1
62      XGpioPs_WritePin(mio_ptr, MIO_LED, 0);
63
64      return XST_SUCCESS;
65}
66
67//中断处理函数
68u32 ttc_InterruptHandler(XTtcPs *InstancePtr)
69{
70      u32 XTtcPsStatusReg;
71
72      XTtcPsStatusReg = XTtcPs_GetInterruptStatus(InstancePtr);
73
74      //LED状态,用于控制LED灯状态翻转
75      static int led_state = 0;
76      if(led_state == 0){
77          led_state = 1;
78      }
79      else{
80          led_state = 0;
81      }
82
83      //向指定引脚写入数据: 0 或 1
84      XGpioPs_WritePin(&Gpio, MIO_LED,led_state);
85
86      return XST_SUCCESS;
87}
88
89int main(void)
90{
91      int Status;
92
93      //MIO初始化
94      mio_init(&Gpio);
95
96      Status = SetupInterruptSystem(INTC_DEVICE_ID, &InterruptController);
97      if (Status != XST_SUCCESS) {
98          xil_printf("TTC Interrupt Example Test Failed\r\n");
99          return XST_FAILURE;
100   }
101
102   SetupTicker();
103
104   xil_printf("Successfully ran TTC Interrupt Test\r\n");
105
106   return XST_SUCCESS;
107 }
108
109 int SetupTicker(void)
110 {
111   int Status;
112   XTtcPs *TtcPsTick;
113
114   Status = IniTimer(TTC_TICK_DEVICE_ID);
115   if(Status != XST_SUCCESS) {
116         return Status;
117   }
118
119   TtcPsTick = &(TtcPsInst);
120
121   Status = XScuGic_Connect(&InterruptController, TTC_TICK_INTR_ID,
122         (Xil_ExceptionHandler)ttc_InterruptHandler, (void *)TtcPsTick);
123   if (Status != XST_SUCCESS) {
124         return XST_FAILURE;
125   }
126
127   XTtcPs_SetStatusHandler(&(TtcPsInst), &(TtcPsInst),
128                     (XTtcPs_StatusHandler)TickHandler);
129
130   //使能定时计数器中断
131   XScuGic_Enable(&InterruptController, TTC_TICK_INTR_ID);
132
133   XTtcPs_EnableInterrupts(TtcPsTick, XTTCPS_IXR_INTERVAL_MASK);
134
135   XTtcPs_Start(TtcPsTick);
136
137   return Status;
138 }
139
140 //配置TTC
141 int IniTimer(int DeviceID)
142 {
143   //int Status;
144   XTtcPs_Config *Config;
145   XTtcPs *Timer;
146
147   Timer = &(TtcPsInst);
148
149   //查找配置
150   Config = XTtcPs_LookupConfig(DeviceID);
151
152   //初始化ttc
153   XTtcPs_CfgInitialize(Timer, Config, Config->BaseAddress);
154
155   XTtcPs_SetOptions   (Timer, 0x24);         //设置Option
156
157   //设置Interval
158   XTtcPs_SetInterval(Timer, 50000000);   //每隔500ms产生中断,led灯间隔1s亮灭一次
159
160   //设置Prescaler
161   XTtcPs_SetPrescaler (Timer, 0);            //不分频,默认输入100Mhz
162
163   return XST_SUCCESS;
164 }
165
166 static int SetupInterruptSystem(u16 IntcDeviceID, XScuGic *IntcInstancePtr)
167 {
168   int Status;
169   XScuGic_Config *IntcConfig;
170
171   IntcConfig = XScuGic_LookupConfig(IntcDeviceID);
172   if (NULL == IntcConfig) {
173         return XST_FAILURE;
174   }
175
176   Status = XScuGic_CfgInitialize(IntcInstancePtr, IntcConfig,
177                     IntcConfig->CpuBaseAddress);
178   if (Status != XST_SUCCESS) {
179         return XST_FAILURE;
180   }
181
182   Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT,
183             (Xil_ExceptionHandler) XScuGic_InterruptHandler,
184             IntcInstancePtr);
185
186   Xil_ExceptionEnable();
187
188   return XST_SUCCESS;
189 }
190
191 static void TickHandler(void *CallBackRef, u32 StatusEvent)
192 {
193
194 }
在代码的19行定义了MIO_LED为38,即PS_LED连接到MIO的38号引脚上。然后在代码的第44行至第65行对MIO进行初始化,并将MIO38设置为输出。
代码的第68行至第87行定义了中断处理函数。在函数中首先定义一个led_state变量,并赋初始值0,每当中断发生时,对led_state的值进行翻转,并将翻转后的值写到MIO38引脚。
在程序的main函数中,首先对GPIO进行初始化,然后在程序的第96行调用中断函数。中断函数在代码的第166行至第189行。在中断函数的第171行,根据器件ID查找GIC的配置,然后在代码的第176行对GIC进行初始化。在代码的第182行至第186行,设置并使能中断异常。
main函数中第102行调用了SetupTicker函数,该函数主要完成TTC中断的配置以及定时器的配置。第114行的SetupTicker函数中调用了IniTimer定时器配置函数。TTC使用系统输入的100Mhz时钟,这里不对系统时钟进行分频,所以分频系数设置为0,如代码的第161行所示。
由于实验任务是led灯间隔1s亮灭一次,所以每隔500ms改变一次led灯的状态。时钟周期是10ns,因此可以计算出定时器Interval的值是50_000_000 (500_000_000/10),如代码第158行所示。
9.5下载验证
首先我们将下载器与开发板上的JTAG接口连接,下载器另外一端与电脑连接。然后使用USB连接线将USB_UART接口(开发板右上角PS PORT)与电脑连接,用于串口通信。最后连接开发板的电源,给开发板上电。
打开Vitis Terminal终端,设置并连接串口。然后下载本次实验的程序,下载完成后,在下方的Terminal中可以看到应用程序打印的信息“Successfully ran TTC Interrupt Test”,如下图所示:
图 9.5.1 程序打印结果
接下来观察开发板,可以看到PS_LED1每隔200ms亮灭一次,如图 9.5.2所示,说明本次实验在MPSOC开发板上面下载验证成功。
图 9.5.2 开发板实验现象
9.6在线调试
至此,定时器中断实验的讲解已经结束。最后,我们来教大家如何对代码进行调试。
在应用工程timer_intr_led上右击,选择“Debug As”,然后选择第一项“1 Launch on Hardware (System Debugger)”,如下图所示:
图 9.6.1 打开调试界面
进入下图所示的调试界面,程序首先从main函数开始运行,如程序第116行所示。
图9.6.2 开始调试界面
图9.6.2标注为1的位置是用于调试的工具栏;标注为3的位置可以观察程序中变量的值;标注为2的位置即为我们所要调试的代码,图中高亮的代码行就是接下来将要执行的代码。
我们来详细看下用于调试的工具栏界面,如下图所示:
图9.6.3 工具栏
图9.6.3标注为1的图标表示程序继续运行(Resume,快捷键F8);标注为2的图标表示暂停(Suspend,只有程序在运行时才可以点击);标注为3的图标表示单步执行(Step Into,快捷键F5);标注为4的图标表示单步执行结束(Step Over,快捷键F6);标注为5的图标表示执行完并返回(Step Return,快捷键F7)。同时,也可以点击菜单栏的Run,找到调试按钮,如下图所示:
图 9.6.4点击Run界面
我们首先打开Vitis Serial Terminal窗口的界面并点击右上角“+”设置串口,以便于观察程序调试的结果,如下图所示。如果不小心把Terminal窗口关掉了,需要点击工具栏Window->Show view…,然后在弹出的窗口中输入Terminal搜索并打开串口终端。
图 9.6.5 窗口打印界面
接下来开始调试代码。点击Step Over图标或者按下快捷键F6来执行代码,执行MIO初始化函数mio_init()。接下来点击Step Info图标或者按下快捷键F5,跳转进入SetupInterruptSystem()函数,如下图所示:
图9.6.6 跳转至SetupInterruptSystem()函数
接下来可以继续按F5单步执行。如果此时想迅速跳出SetupInterruptSystem()函数,可以直接点击Step Return或者按下快捷键F7,跳转结果如下图所示:
图9.6.7 跳出函数
调试界面支持设置断点,直接双击行号前面蓝色区域的位置设置断点,再次双击可取消断点。如在第126行位置设置断点,双击第126行前面蓝色区域的位置,如下图所示:
图9.6.8 设置断点
接下来点击Resume图标或者按下快捷键F8,程序可执行至断点位置,如下图所示:
图9.6.9 程序执行至断点处
再次双击第126行前面蓝色区域的位置可取消断点。此时继续点击Resume图标或者按下快捷键F8,程序会一直执行下去,此时可以看到串口打印信息“Successfully ran TTC Interrupt Test”,并且开发板上的PS_LED1每隔200毫秒亮灭一次。如果想要暂停程序的执行,点击Suspend图标即可。
如果要退出Debug界面,点击下图中的图标即可。
图9.6.10 退出Debug界面
页: [1]
查看完整版本: 《ATK-DFPGL22G之FPGA开发指南_V1.0》第九章定时器中断实验