EBAZ4205 第二十个工程  基于PS端的 定时器中断

和所有的MCU一样ZYNQ的PS端也有自己的定时器中断功能,本节将对该功能进行演示。

定时器中断可以为系统提供标准的时钟,心跳,也可以用来作为为一些计算的时间基准(如脉冲宽度,脉冲周期等)在实际项目中都是必不可少的。

ZYNQ中每个ARM core都有自己的私有定时器,私有定时器的工作频率为CPU的一半,如ZYNQ的工作频率为666MHZ,则定时器的频率为333MHz,后面需要注意这个时钟。

本文在 vivado2018.3版本上 演示, 其他版本请自行研究

系统设计:

系统方面我们在PS端设置一个定时器,并让定时器每计数满1ms触发一次中断,并且每累计一秒钟的时候点亮或者熄灭一个LED灯,来达到演示的效果。

所以整个系统我们只需要在BLOCK DESIGN 中 zynq核内 拓展一个EMIO 的GPIO口,并用PS的SDK 程序的定时器每秒去驱动这个GPIO即可。

EMIO的工程创建这里 简略写,完整的图文教程可以看前面的例程,Tiny ZYNQ板 工程七 用ZYNQ的PS点亮连接到PL端的LED灯EMIO 方式

在ZYNQ 设置界面,设置 DDR MT41K128M16JT的型号 和位宽16bit

添加一个GPIO的接口(EMIO方式)为后面点亮LED做准备

第三 记录下 CPU的时钟(这里不需要修改 只需要看一下记录下当前时钟频率就好)

增加并设置LED的管脚约束(H18 LVCMOS33)

设下的对项目行编译综合就好

程序设计:

建立一个TIMER_TEST空的工程,并且添加main.c 添加如下代码:

#include "xparameters.h"
#include "xgpiops.h"
#include "xstatus.h"
#include "xplatform_info.h"
#include "xscutimer.h"
#include "Xscugic.h"

#define LED    	54

#define GPIO_DEVICE_ID  	XPAR_XGPIOPS_0_DEVICE_ID
XGpioPs Gpio;


void Gpio_Init(void){
	XGpioPs_Config *ConfigPtr;

	ConfigPtr = XGpioPs_LookupConfig(GPIO_DEVICE_ID);
	XGpioPs_CfgInitialize(&Gpio, ConfigPtr,ConfigPtr->BaseAddr);

	XGpioPs_SetDirectionPin(&Gpio, LED, 1);
	XGpioPs_SetOutputEnablePin(&Gpio, LED, 1);
	XGpioPs_WritePin(&Gpio, LED, 0);
}


#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 0x514c7 //666*1000*1/2 666mhz
static XScuGic Intc; //GIC
static XScuTimer Timer;//timer

static void SetupInterruptSystem(XScuGic *GicInstancePtr,
XScuTimer *TimerInstancePtr, u16 TimerIntrId);
static void TimerIntrHandler(void *CallBackRef);

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);
	//connect to the hardware
	Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT,
		(Xil_ExceptionHandler)XScuGic_InterruptHandler,
		GicInstancePtr);
	//set up the timer interrupt
	XScuGic_Connect(GicInstancePtr, TimerIntrId,
		(Xil_ExceptionHandler)TimerIntrHandler,
		(void *)TimerInstancePtr);
	//enable the interrupt for the Timer at GIC
	XScuGic_Enable(GicInstancePtr, TimerIntrId);
	//enable interrupt on the timer
	XScuTimer_EnableInterrupt(TimerInstancePtr);
	// Enable interrupts in the Processor.
	Xil_ExceptionEnableMask(XIL_EXCEPTION_IRQ);
}

volatile int ms_count;
unsigned char led_state=0;

static void TimerIntrHandler(void *CallBackRef){
	static int ms_count = 0; //计数
	XScuTimer *TimerInstancePtr = (XScuTimer *) CallBackRef;
	XScuTimer_ClearInterruptStatus(TimerInstancePtr);

	ms_count++;
	if(ms_count>=1000){
		ms_count=0;
		if(led_state==0)led_state=1;
		else led_state=0;
		XGpioPs_WritePin(&Gpio, LED, led_state);
	}
}


void Timer_Init(){
	XScuTimer_Config *TMRConfigPtr;
	TMRConfigPtr = XScuTimer_LookupConfig(TIMER_DEVICE_ID);
	XScuTimer_CfgInitialize(&Timer, TMRConfigPtr,TMRConfigPtr->BaseAddr);
	XScuTimer_SelfTest(&Timer);
	XScuTimer_LoadTimer(&Timer, TIMER_LOAD_VALUE);
	//自动装载
	XScuTimer_EnableAutoReload(&Timer);
	//启动定时器
	XScuTimer_Start(&Timer);
	//set up the interrupts
	SetupInterruptSystem(&Intc,&Timer,TIMER_IRPT_INTR);
}
int main(void)
{
	Gpio_Init();
	Timer_Init();

	while(1);

	return 0;
}


代码简单介绍 :

Timer_Init(); 是定时器的初始化函数,在系统运行的时候,需要对定时器初始化才能正常工作,其中TIMER_LOAD_VALUE 用来控制 每一次定时器触发的时间, 还记得我们前面的CPU时钟为666mhz吗,那我们的定时器时钟就相当于工作在1/2的CPU时钟即333mhz频率下。 我们的系统设置是每1ms进入一次中断,那1ms就相当于需要计数(333000000)*1/1000=333000个周期,那我们就设置TIMER_LOAD_VALUE 为 333000-1,即0x514c7 (这里代表1ms的数值)

TimerIntrHandler 是定时器的回调函数,相当于单片机的中断服务函数,每当中断被触发,就会调用一次该回调函数。 因为是1ms进入一次,所以假设我们需要计数1S钟,就需要额外增加一个变量ms_count

每次进入该回调函数 ms_count都自增,当大于等于1000的时候让ms_count归零完成一次循环,同时执行我们的LED变换操作。 如果我们有其他程序需要添加,也可以增加到 TimerIntrHandler的回调函数钟。

ms_count++;
if(ms_count>=1000){
    ms_count=0;

    if(led_state==0)led_state=1;
    else led_state=0;
    XGpioPs_WritePin(&Gpio, LED, led_state);
}

对于main函数来说,我们只需要执行GPIO 和定时器的初始化就好

int main(void){
	Gpio_Init();
	Timer_Init();
	while(1);
	return 0;
}

下载到我们的板子上,可以看到 扩展板上的LED 灯每秒钟闪烁一次, 说明我们的定时器程序正常在工作了。

程序比较简单,仅供参考学习,对于ZYNQ的定时器来说,还有很多功能(包括用PL去触发定时器中断等),需要实际做项目的时候自行去拓展了。

完整工程如下(供学习参考):

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注