本帖最后由 FPGA课程 于 2024-9-25 17:30 编辑
软件版本: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概述定时器资源是嵌入式系统中重要的定时资源,ZYNQ系统中的定时器资源包含了私有定时器,私有看门狗定时器、以及TTC定时器。本实验演示ZYNQ私有定时器的使用。
实验目的:
1:了解私有定时器的概念
2:了解私有定时器的功能寄存器
3:编写SDK测试代码完成定时器的中断定时使用。
2系统框图本系统中,使用ZYNQ自带的私有定时器资源,程序设计中使用私有定时器每1S中断1次,通过串口打印输出。
3中断资源概述
PS的CPU0和CPU1各有1个私有定时器。上图中,红色部分为PS的私有中断资源,本文中demo就是以CPU0的定时器中断使用作为演示方案。CPU0的定时器中断经过中断控制派发器派发给CPU0。CPU0的私有定时器IRQ ID=29。
4定时器资源介绍
Zynq的PS的CPU0和CPU1各包含1个私有定时器以及一个私有看门狗定时器并且共享一个全局定时器。另外Zynq的PS还有TTC0以及TTC1定时控制器。每个TTC定时器里面包含三个定时器/计数器。本文所讲解的是CPU0的私有定时器中断的使用。 本方案中只对CPU0的私有定时器进行说明。 Zynq的私有定时器和看门狗定时器具有以下共同的特性: 1-定时计数器位宽为32位,当达到0时产生中断 2-参考时钟为CPU时钟的1/2,比如CPU是666.6666M 那么定时器的参考时钟为333.3333M 3-有一个8bit预分频器,可以对参考时钟进一步分频 4-有一个可配置的预加载寄存器 5-可配置为单次模式或者自动重载模式 5私有定时器的寄存器
在UG585中没有关于私有定时器的详细说明,我们通过SDK代码的理解补充了这部分说明。私有定时器的基地址为0xF8F00600。CPU的私有定时器一共有4个寄存器:
XSCUTIMER_LOAD寄存器XSCUTIMER_LOAD_OFFSET (0x00U) Field Name | Bits | Type | Reset Value | Description | XSCUTIMER_LOAD | 31:0 | 至少 可写 | 0x0 | 私有定时器的预加载值 |
XSCUTIMER_COUNTER寄存器XSCUTIMER_COUNTER_OFFSET (0x04U) Field Name | Bits | Type | Reset Value | Description | XSCUTIMER_COUNTER | 31:0 | 至少 可读 | 0x0 | 私有定时器的计数器 |
XSCUTIMER_CONTROL寄存器XSCUTIMER_CONTROL_OFFSET (0x08U) Field Name | Bits | Type | Reset Value | Description | XSCUTIMER_CONTROL | 31:0 | R/W | 0x0 | 私有定时器的控制寄存器 15~8bit:预分频设置 2bit:当为1中断使能 1bit:当为1自动重载使能 0bit:当为1表示定时器使能 |
XSCUTIMER_ISR寄存器XSCUTIMER_ISR_OFFSET (0x0CU) Field Name | Bits | Type | Reset Value | Description | XSCUTIMER_ISR | 31:0 | R/W | 0x0 | 私有定时器的中断寄存器 0bit:当为1的时候表示发生中断 | 6搭建SOC系统工程详细的搭建过程这里不再重复,对于初学读者如果还不清楚如何创建SOC工程的,请学习“01Vitis Soc开发入门”这篇文章。 6.1SOC系统工程直接使用已经搭建好的“01Vitis Soc开发入门”这篇文章中的工程。
6.2编译并导出平台文件以下步骤简写,有不清楚的看第一篇文章。 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平台。
7搭建Vitis-sdk工程创建soc_base sdk platform和APP工程的过程不再重复,如果不清楚请参考本章节第一个demo。 7.1创建SDK Platform工程
右击soc_base编译,编译的时间可能会有点长 7.2创建timer_intr APP工程
8程序分析int init_intr_sys(void)函数,这个函数用于对中断资源进行初始化。 米联客的中断函数都会采用这种构架,系统中断初始化+功能中断初始化, - int init_intr_sys(void)
- {
- Init_Intr_System(&Intc); // 初始化系统中断
- TimerIntrSetup(&Intc,&Timer,TIMER_IRPT_INTR,TIMER_DEVICE_ID);//初始定时器中断
- Setup_Intr_Exception(&Intc);
- }
复制代码
关于Init_Intr_System(&Intc)函数和Setup_Intr_Exception(&Intc)可以阅读“PS-MIO/EMIO中断实验”中的相关程序分析。 重点看下TimerIntrSetup(&Intc,&Timer,TIMER_IRPT_INTR,TIMER_DEVICE_ID)函数: - void TimerIntrSetup(XScuGic *GicInstancePtr,XScuTimer *TimerInstancePtr, u16 TimerIntrId,u16 DeviceId)
- {
- XScuTimer_Config *TMRConfigPtr;
- //初始化定时器参数
- TMRConfigPtr = XScuTimer_LookupConfig(DeviceId);
- XScuTimer_CfgInitialize(TimerInstancePtr, TMRConfigPtr,TMRConfigPtr->BaseAddr);
- //定时器计数器加载初值
- XScuTimer_LoadTimer(TimerPtr, TIMER_LOAD_VALUE);
- //定时器始能自动重载模式
- XScuTimer_EnableAutoReload(TimerInstancePtr);
- //设置中断回调函数
- XScuGic_Connect(GicInstancePtr, TimerIntrId,
- (Xil_ExceptionHandler)timer_callback,
- (void *)TimerInstancePtr);
- //使能定时器全局中断资源
- XScuGic_Enable(GicInstancePtr, TimerIntrId);
- //使能定时器中断
- XScuTimer_EnableInterrupt(TimerInstancePtr);
- }
复制代码
1:XScuTimer_LookupConfig函数SDK中的LookupConfig都是用来查看硬件的参数,并且把硬件的参数保持到一个指针中。 XScuTimer_LookupConfig(DeviceId)函数主要是根据DeviceId值判断下,是否对应的硬件支持,如果有会把定时器的相关硬件参数保存到TMRConfigPtr参数中。
SDK中的ConfigTable参数保存了硬件的基本信息
在系统头文件xparameters.h中保存了硬件参数的具体值
2:XScuTimer_CfgInitialize函数SDK中这个函数用于对硬件实列的参数进行初始化,有时候也会在里面初始化一些硬件的寄存器。
3:XScuTimer_LoadTimer函数XScuTimer_LoadTimer(TimerPtr, TIMER_LOAD_VALUE)这是一个宏定义函数,把定时器的定时参数加载到预加载值寄存器。
4:XScuTimer_EnableAutoReload函数XScuTimer_EnableAutoReload(TimerInstancePtr)这是一个宏定义函数,这个函数设置定时器控制器的重载值,使能重载,重载的含义是当定时计数器计数到0的时候,重新加载预加载值,这样无需程序里面重复加载预加载值,可以减轻CPU的负担。
5:XScuGic_Connect函数 SDK函数中有关键词Connect 的一般都是用来设置中断函数的回调函数,XScuGic_Connect(GicInstancePtr, TimerIntrId, (Xil_ExceptionHandler)timer_callback, (void *)TimerInstancePtr)这个函数设置定时间的中断回调函数。 InstancePtr->Config->HandlerTable保存了所有全局中断函数,如果某一个全局中断对应的中断被CPU相应,CPU就会根据这个中断回调相应的中断函数。CallBackRef参数是一个void类型参数指针,这里是TimerInstancePtr这个指针。
所以timer_callback就是定时器中断时候被调用的函数。 6: timer_callback函数定时器每1S中断一次回调该函数,该函数中与一个中断计数器,统计当前中断了多少次,并且通过串口打印输出。 - void timer_callback(XScuTimer * TimerInstance)
- {
- timer_counter++;
- printf("timer_counter = %u\n",timer_counter);
- XScuTimer_ClearInterruptStatus(TimerInstance);
- }
复制代码
7:XScuGic_Enable函数这个函数使能定时器中断号对应的全局中断使能,可以看到该函数中需要采用自旋锁,确保相同的寄存器不会被多个CPU访问。
8:XScuTimer_EnableInterrupt函数该函数是一个宏定义函数,使能定时器的中断使能位。
9:Timer_start(&Timer)这个函数通过调用XScuTimer_Start设置定时器的使能位。
9方案演示
9.1硬件准备实验需要用到JTAG下载器、USB转串口外设,另外需要把核心板上的2P模式开关设置到JTAG模式,即ON ON
9.2实验结果程序运行后每1S会答应输出定时器的中断次数
|