【JokerのZYNQ7020】TIMER。

软件环境:vivado 2017.4        硬件平台:XC7Z020 


 手册说的很清楚呀,ZYNQ的每个CPU都有自己的私有定时器,私有定时器工作频率是CPU工作频率的一半,即ARM工作频率是666MHz时候,私有定时器的工作频率是333MHz。其主要特性有以下四点:

(1)32位计数器,过零时产生中断。       (2)8位预分频器,能够更好的控制中断周期。

(3)可配置为单次模式或自动重载模式。(4)定时时间=(1/定时器频率)*(预装载值+1)。

另外,在上一章中断控制,表7-3中可以看出,私有定时器是上升沿触发中断。


Vivado 2017.4 Create Block Design后,添加ZYNQ7 Processing system,然后自动连接就行,Generate the output products,Create a HDL wrapper,Generate Bitstream,Export Hardware(注意勾选include biestream),最后launch SDK进行测试。。

 

 SDK中new application然后添加如下测试代码。

/*
 * main.c
 *
 *  Created on: 2016年6月26日
 *      Author: Administrator
 */

#include <stdio.h>
#include "xadcps.h"

#include "xil_types.h"
#include "Xscugic.h"
#include "Xil_exception.h"
#include "xscutimer.h"

//timer info
#define TIMER_DEVICE_ID     XPAR_XSCUTIMER_0_DEVICE_ID
#define INTC_DEVICE_ID      XPAR_SCUGIC_SINGLE_DEVICE_ID
#define TIMER_IRPT_INTR     XPAR_SCUTIMER_INTR

#define TIMER_LOAD_VALUE    0x13D92D3F		//332999999

static XScuGic Intc; 		//GIC
static XScuTimer Timer;     //timer

static void TimerIntrHandler(void *CallBackRef)
{

    static int sec = 0;   //计数清零
    XScuTimer *TimerInstancePtr = (XScuTimer *) CallBackRef;
    XScuTimer_ClearInterruptStatus(TimerInstancePtr);
    sec++;
    printf(" %d Second\n\r",sec);  //每秒打印输出一次
}

void SetupInterruptSystem(XScuGic *GicInstancePtr,
        XScuTimer *TimerInstancePtr, u16 TimerIntrId)
{

        XScuGic_Config *IntcConfig; //GIC config
        Xil_ExceptionInit();
        //initialise the GIC
        IntcConfig = XScuGic_LookupConfig(INTC_DEVICE_ID);
        XScuGic_CfgInitialize(GicInstancePtr, IntcConfig,
                        IntcConfig->CpuBaseAddress);

        Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT,
                    (Xil_ExceptionHandler)XScuGic_InterruptHandler,//connect to the hardware
                    GicInstancePtr);

        XScuGic_Connect(GicInstancePtr, TimerIntrId,
                        (Xil_ExceptionHandler)TimerIntrHandler,//set up the timer interrupt
                        (void *)TimerInstancePtr);

        XScuGic_Enable(GicInstancePtr, TimerIntrId);//enable the interrupt for the Timer at GIC
        XScuTimer_EnableInterrupt(TimerInstancePtr);//enable interrupt on the timer
        Xil_ExceptionEnableMask(XIL_EXCEPTION_IRQ); //Enable interrupts in the Processor.
    }


int main()
{
     XScuTimer_Config *TMRConfigPtr;     //timer config
     printf("------------BEGIN-------------\n");
     //私有定时器初始化
     TMRConfigPtr = XScuTimer_LookupConfig(TIMER_DEVICE_ID);
     XScuTimer_CfgInitialize(&Timer, TMRConfigPtr,TMRConfigPtr->BaseAddr);
     //set up the interrupts
     SetupInterruptSystem(&Intc,&Timer,TIMER_IRPT_INTR);
     //加载计数周期,私有定时器的时钟为CPU的一般,为333MHZ,如果计数1S,加载值为1sx(333x1000x1000)(1/s)-1=0x13D92D3F
     XScuTimer_LoadTimer(&Timer, TIMER_LOAD_VALUE);
     //自动装载
     XScuTimer_EnableAutoReload(&Timer);
     //启动定时器
     XScuTimer_Start(&Timer);
     while(1);

     return 0;
}

接下来对程序中的一些函数做一些解释说明:.

先来看几个结构体,一个是程序最开始处定义的XScuTimer,包括定时器的配置,是否初始化完毕,以及是否处于运行状态。

typedef struct {
	XScuTimer_Config Config; /**< Hardware Configuration */
	u32 IsReady;		/**< Device is initialized and ready */
	u32 IsStarted;		/**< Device timer is running */
} XScuTimer;

另一个是main里面最开始的XScuTimer_Config,包括设备ID和设备的基址。

typedef struct {
	u16 DeviceId;	/**< Unique ID of device */
	u32 BaseAddr;	/**< Base address of the device */
} XScuTimer_Config;

XScuTimer_LookupConfig()和XScuTimer_CfgInitialize()就不多说了,这两个函数一个是查找设备ID,看设备是否存在,另一个是设备存在的前提下,获取设备的配置表的基址。

接下来和中断控制初始化时候差不多,结构体XScuGic是配置必须的。Gic(Generic Interrupt Controller)通用中断控制器。

typedef struct
{
	XScuGic_Config *Config;  /**< Configuration table entry */
	u32 IsReady;		 /**< Device is initialized and ready */
	u32 UnhandledInterrupts; /**< Intc Statistics */
} XScuGic;

XScuGic_Config,包括设备ID号,基址,向量表。

typedef struct
{
	u16 DeviceId;		/**< Unique ID  of device */
	u32 CpuBaseAddress;	/**< CPU Interface Register base address */
	u32 DistBaseAddress;	/**< Distributor Register base address */
	XScuGic_VectorTableEntry HandlerTable[XSCUGIC_MAX_NUM_INTR_INPUTS];/**<
				 Vector table of interrupt handlers */
} XScuGic_Config;

Xil_ExceptionInit()在这实际是个空函数,里面直接return的,据函数前的说明意思是这函数是个通用API,用于在所有支持的arm处理器上初始化异常处理程序,还放在这里主要是为了避免与之前软件版本产生意外的兼容性问题。

XScuGic_LookupConfig()和XScuGic_CfgInitialize()是与通用中断控制器相关的查找设备ID和获取设备配置表基址的函数,与前面定时器相关的XScuTimer_LookupConfig()和XScuTimer_CfgInitialize()可以看出来,各自配置各自的,各自有各自的初始化函数,分开配置,分开调用。

Xil_ExceptionRegisterHandler()处理特定异常时用的。在上一章里已经说过了,这就不多哔哔了。

void Xil_ExceptionRegisterHandler(u32 Exception_id,
				    Xil_ExceptionHandler Handler,
				    void *Data)
{
	XExc_VectorTable[Exception_id].Handler = Handler;
	XExc_VectorTable[Exception_id].Data = Data;
}

绑定的这个向量表理所当然也是用于异常处理的。

XExc_VectorTableEntry XExc_VectorTable[XIL_EXCEPTION_ID_LAST + 1] =
{
	{Xil_ExceptionNullHandler, NULL},
	{Xil_UndefinedExceptionHandler, NULL},
	{Xil_ExceptionNullHandler, NULL},
	{Xil_PrefetchAbortHandler, NULL},
	{Xil_DataAbortHandler, NULL},
	{Xil_ExceptionNullHandler, NULL},
	{Xil_ExceptionNullHandler, NULL},
};

XScuGic_Connect()将定时器中断的中断函数和中断控制器绑定,当定时器触发中断时,找TimerIntrHandler()中断处理函数。

s32  XScuGic_Connect(XScuGic *InstancePtr, u32 Int_Id,
                      Xil_InterruptHandler Handler, void *CallBackRef)
{
	/*
	 * Assert the arguments
	 */
	Xil_AssertNonvoid(InstancePtr != NULL);
	Xil_AssertNonvoid(Int_Id < XSCUGIC_MAX_NUM_INTR_INPUTS);
	Xil_AssertNonvoid(Handler != NULL);
	Xil_AssertNonvoid(InstancePtr->IsReady == XIL_COMPONENT_IS_READY);

	/*
	 * The Int_Id is used as an index into the table to select the proper
	 * handler
	 */
	InstancePtr->Config->HandlerTable[Int_Id].Handler = Handler;
	InstancePtr->Config->HandlerTable[Int_Id].CallBackRef = CallBackRef;

	return XST_SUCCESS;
}

 XScuGic_Enable()使能定时器中断。

void XScuGic_Enable(XScuGic *InstancePtr, u32 Int_Id)
{
	u32 Mask;

	/*
	 * Assert the arguments
	 */
	Xil_AssertVoid(InstancePtr != NULL);
	Xil_AssertVoid(Int_Id < XSCUGIC_MAX_NUM_INTR_INPUTS);
	Xil_AssertVoid(InstancePtr->IsReady == XIL_COMPONENT_IS_READY);

	/*
	 * The Int_Id is used to create the appropriate mask for the
	 * desired bit position. Int_Id currently limited to 0 - 31
	 */
	Mask = 0x00000001U << (Int_Id % 32U);

	/*
	 * Enable the selected interrupt source by setting the
	 * corresponding bit in the Enable Set register.
	 */
	XScuGic_DistWriteReg(InstancePtr, (u32)XSCUGIC_ENABLE_SET_OFFSET +
						((Int_Id / 32U) * 4U), Mask);
}

XScuTimer_EnableInterrupt()使能定时器中断,大概过程是,将XSCUTIMER_CONTROL_IRQ_ENABLE_MASK = 0x00000004U或进定时器控制寄存器(基址Config.BaseAddr+偏移量XSCUTIMER_CONTROL_OFFSET = 0xF8F00608)。

#define XScuTimer_EnableInterrupt(InstancePtr)				\
	XScuTimer_WriteReg((InstancePtr)->Config.BaseAddr,		\
			XSCUTIMER_CONTROL_OFFSET,			\
			(XScuTimer_ReadReg((InstancePtr)->Config.BaseAddr, \
					XSCUTIMER_CONTROL_OFFSET) |	\
					XSCUTIMER_CONTROL_IRQ_ENABLE_MASK))

 Xil_ExceptionEnableMask()整个中断使能呗。

#define Xil_ExceptionEnableMask(Mask)	\
		mtcpsr(mfcpsr() & ~ ((Mask) & XIL_EXCEPTION_ALL))

接下来就回到主函数了,XScuTimer_LoadTimer()定时器装载初始值。TIMER_LOAD_VALUE=0x13D92D3F=329999999,计算方法前面已经说过了。被设置的寄存器0xF8F00600。

#define XScuTimer_LoadTimer(InstancePtr, Value)				\
	XScuTimer_WriteReg((InstancePtr)->Config.BaseAddr,		\
			XSCUTIMER_LOAD_OFFSET, (Value))

 XScuTimer_EnableAutoReload()使能自动重装载。被设置的寄存器依旧是0xF8F00608。

#define XScuTimer_EnableAutoReload(InstancePtr)				\
	XScuTimer_WriteReg((InstancePtr)->Config.BaseAddr,		\
			XSCUTIMER_CONTROL_OFFSET,			\
			(XScuTimer_ReadReg((InstancePtr)->Config.BaseAddr, \
				XSCUTIMER_CONTROL_OFFSET) |		 \
				XSCUTIMER_CONTROL_AUTO_RELOAD_MASK))

XScuTimer_Start()最后启动定时器。

void XScuTimer_Start(XScuTimer *InstancePtr)
{
	u32 Register;

	Xil_AssertVoid(InstancePtr != NULL);
	Xil_AssertVoid(InstancePtr->IsReady == XIL_COMPONENT_IS_READY);

	/*
	 * Read the contents of the Control register.
	 */
	Register = XScuTimer_ReadReg(InstancePtr->Config.BaseAddr,
				  XSCUTIMER_CONTROL_OFFSET);

	/*
	 * Set the 'timer enable' bit in the register.
	 */
	Register |= XSCUTIMER_CONTROL_ENABLE_MASK;

	/*
	 * Update the Control register with the new value.
	 */
	XScuTimer_WriteReg(InstancePtr->Config.BaseAddr,
			XSCUTIMER_CONTROL_OFFSET, Register);

	/*
	 * Indicate that the device is started.
	 */
	InstancePtr->IsStarted = XIL_COMPONENT_IS_STARTED;
}

最后, TimerIntrHandler()定时器中断函数,程序想要达到的效果是每秒中断进入一次,然后在串口处输出1 second...2 second...等时间计时,可以看见,每次进入时候sec自加一次。记得在里面清除中断标志XScuTimer_ClearInterruptStatus(),不然就一直处于中断状态。

static void TimerIntrHandler(void *CallBackRef)
{

    static int sec = 0;   //计数清零
    XScuTimer *TimerInstancePtr = (XScuTimer *) CallBackRef;
    XScuTimer_ClearInterruptStatus(TimerInstancePtr);
    sec++;
    printf(" %d Second\n\r",sec);  //每秒打印输出一次
}

XScuTimer_ClearInterruptStatus()记得在中断处理函数里面清除中断标志,不然就一直处于中断状态。

#define XScuTimer_ClearInterruptStatus(InstancePtr)			\
	XScuTimer_WriteReg((InstancePtr)->Config.BaseAddr,		\
		XSCUTIMER_ISR_OFFSET, XSCUTIMER_ISR_EVENT_FLAG_MASK)

所以在最后总结一下TIMER,在SDK中大致的流程:

通过XScuTimer_LookupConfig函数,找到TIMER的基址------------>

通过XScuTimer_CfgInitialize函数,初始化TIMER配置------------>

通过Xil_ExceptionInit函数,避免与前版本有兼容性问题------------>

通过XScuGic_LookupConfig函数,找到通用中断控制器基址------------>

通过XScuGic_CfgInitialize函数,初始化通用中断控制器------------>

通过Xil_ExceptionRegisterHandler函数,使能异常中断处理------------>

通过XScuGic_Connect函数,绑定定时器中断处理函数------------>

通过XScuGic_Enable函数,在通用中断控制器中使能定时器中断------------>

通过XScuTimer_EnableInterrupt函数,使能定时器中断------------>

通过Xil_ExceptionEnableMask函数,使能中断------------>

通过XScuTimer_LoadTimer函数,装载定时器初值------------>

通过XScuTimer_EnableAutoReload函数,使能自动重装载------------>

通过XScuTimer_Start函数,启动定时器。

  • 0
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值