ZED-Board从入门到精通系列例程——全局定时器

本文系ZED-Board从入门到精通(三):从传统ARM开发到PS开发的转变之后增加的PS例程。由于原文较长,在原帖后面添加例程会使阅读不便,于是单独开一帖。

 

实际项目中几乎离不开时间的测量。定时器是硬件系统运行状态的忠实记录者,它不受CPU直接干预,自己独立运行,可以完成计时、定时、中断、实时时钟等功能。

 

ARM Cortex-A9内部有一个64bit全局定时器,特性包括:

64bit,增计数;

内存映射至私有内存空间;

只有复位后,在安全模式下才能访问;

可被所有Cortex-A9核访问,每个核有私有比较器;

时钟源为PERIPHCLK;

定时器的精度是由其时钟源决定的,而时钟源来自ARM系统时钟。我们先来看一下硬件系统时钟分配情况,

系统PS_CLK为板上的晶振输入,频率为33.3333MHz

PS-CLK进入芯片后,又做如下分配(摘自Zynq-7000-TRM):

 

可见经过了3个PLL,最终生成的系统时钟有cpu_6x4x,cpu_3x2x,cpu_2x,cpu_1x。具体的系统时钟频率值我们可以查看XPS中的时钟选项,这里不再详述,只要知道全局定时器的输入时钟为cpu_3x2x,它的频率为CPU时钟的一半(333.333MHz),定时精度为3ns,又由于其具有64bit范围,最大定时值可达3e34s。

操作定时器需要访问其对应寄存器,我们看一下TRM中的描述:

 

这里只给出了基地址,具体寄存器的分布需要查看ARM文档cortex_a9_mpcore_r4p1_trm:

 

其中前两个为定时器的计数值存放寄存器,两个32bit凑成一个64bit实现连续增计数。

第三个寄存器为控制寄存器,位定义如下:

我们需要关注的是最低位(b0),即定时器使能位,该位为0时,定时器停止,这时可以读写计数值;而该位为1时,定时器运行,不能写入计数值(只能读出)。

其它的寄存器我们暂时不用,不加解释。需要的话可以自己翻一翻手册。

相比基于操作系统的软件计时器,我们采用硬件计时器具有非常高的精度,可以精确到ns级别!对于非常窄的脉冲,我们照样可以通过计时器完成其脉宽测量。程序中有时需要精确延时(例如红外通信,DS18b20单总线读写),我们先写一个精确延时1s的函数,然后把它用在我们第一个流水灯实验中。本节例程仍基于第一个例程进行,硬件部分不需要改动,只需要改软件,打开helloworld.c,将内容改为:

 

/*
 * Copyright (c) 2009 Xilinx, Inc.  All rights reserved.
 *
 * Xilinx, Inc.
 * XILINX IS PROVIDING THIS DESIGN, CODE, OR INFORMATION "AS IS" AS A
 * COURTESY TO YOU.  BY PROVIDING THIS DESIGN, CODE, OR INFORMATION AS
 * ONE POSSIBLE   IMPLEMENTATION OF THIS FEATURE, APPLICATION OR
 * STANDARD, XILINX IS MAKING NO REPRESENTATION THAT THIS IMPLEMENTATION
 * IS FREE FROM ANY CLAIMS OF INFRINGEMENT, AND YOU ARE RESPONSIBLE
 * FOR OBTAINING ANY RIGHTS YOU MAY REQUIRE FOR YOUR IMPLEMENTATION.
 * XILINX EXPRESSLY DISCLAIMS ANY WARRANTY WHATSOEVER WITH RESPECT TO
 * THE ADEQUACY OF THE IMPLEMENTATION, INCLUDING BUT NOT LIMITED TO
 * ANY WARRANTIES OR REPRESENTATIONS THAT THIS IMPLEMENTATION IS FREE
 * FROM CLAIMS OF INFRINGEMENT, IMPLIED WARRANTIES OF MERCHANTABILITY
 * AND FITNESS FOR A PARTICULAR PURPOSE.
 *
 */

/*
 * helloworld.c: simple test application
 */

#include <stdio.h>
#include "platform.h"
#define MIO_BASE 0xE000A000               //MIO基地址

#define DATA1_RO  0x64
#define DATA2		0x48
#define DATA2_RO	0x68
#define DIRM_2		0x284
#define OEN_2		0x288

#define GTC_BASE 0xF8F00200              //Global Timer基地址
#define GTC_CTRL	0x08                  //控制寄存器偏移量
#define GTC_DATL	0x00                  //数据寄存器(低32bit)
#define GTC_DATH	0x04                  //数据寄存器(高32bit)

#define CLK_3x2x	333333333            //定时器输入时钟频率
void print(char *str);
void delay_1s(int t)                   //t无实际意义
{
	int i = CLK_3x2x,j;
	*((volatile int*)(GTC_BASE+GTC_CTRL)) = 0x00;           //清零定时器使能位,定时器停止
	*((volatile int*)(GTC_BASE+GTC_DATL)) = 0x00000000;     //写入计数值(低32bit)
	*((volatile int*)(GTC_BASE+GTC_DATH)) = 0x00000000;     //写入计数值(高32bit)
	*((volatile int*)(GTC_BASE+GTC_CTRL)) = 0x01;           //开启定时器
	do
	{
		j=*((volatile int*)(GTC_BASE+GTC_DATL));
	}
	while(j<i);					//判断是否计时够1s?
}
void print(char *str);

int main()
{
	int i;
    init_platform();
    *((volatile int*)(MIO_BASE+OEN_2)) = 0xff;
    *((volatile int*)(MIO_BASE+DIRM_2)) = 0xff;
    print("Hello world!\r\nThe Leds are flowing...\r\n");
    while(1)
    {
    	for(i = 0;i < 8; i++)
    	{
    		*((volatile int*)(MIO_BASE+DATA2)) = 0x01<<i;
    		delay_1s(1000);
    	}
    }
    cleanup_platform();

    return 0;
}


上面例子中,将原来的delay_1s改成了利用64bit全局定时器实现的精确定时(虽然这样做有点浪费,呵呵)。

运行结果仍为流水灯,灯移一位的时间应该是标准的1s。

 

我们可以通过简单的编程,实现对程序性能的监测,例如在运行算法程序之前,先开启计时器,等算法程序结束,再停止计时,读取计时器的计数值从而计算算法运行时间,这样可以评估算法性能。这个功能有点像Matlab里面的tic,toc,为了方便程序编写,我们也如此定义函数:

#define GTC_BASE 0xF8F00200
#define GTC_CTRL	0x08
#define GTC_DATL	0x00
#define GTC_DATH	0x04

#define CLK_3x2x	333333333

void tic(void)
{
	*((volatile int*)(GTC_BASE+GTC_CTRL)) = 0x00;
	*((volatile int*)(GTC_BASE+GTC_DATL)) = 0x00000000;
	*((volatile int*)(GTC_BASE+GTC_DATH)) = 0x00000000;     //清零定时器的计数值
	*((volatile int*)(GTC_BASE+GTC_CTRL)) = 0x01;
}
double toc(void)
{
	*((volatile int*)(GTC_BASE+GTC_CTRL)) = 0x00;
	long long j=*((volatile int*)(GTC_BASE+GTC_DATH));
	double elapsed_time = j<<32;
	j=*((volatile int*)(GTC_BASE+GTC_DATL));              //读取64bit定时器值,转换为double
	elapsed_time+=j;
	elapsed_time/=CLK_3x2x;
	elapsed_time*=1000;
	printf("Elapsed time is %f ms.\r\n",elapsed_time);
	return elapsed_time;
}


调用时非常简单:

tic();
my_algorithm();
toc();


运行时,程序输出和matlab完全一致。这里使用硬件计时,精度可以达到ns级别,具有普通软件计时无法比拟的特性,对于非常窄的脉冲,我们照样可以用上面的方法测量其脉宽。

 

通过本节定时器的例子,相信童鞋们对PS开发有种驾轻就熟的感觉。没错,真正基于Zynq的PS开发流程就是如此,首先查阅文档,知道硬件寄存器定义,然后按照说明进行底层软件编写,并为上层程序提供较为简洁和直观的接口。掌握了这个技巧,后面进行PS与PL协同开发时,只要根据PL相应内存映射地址和寄存器定义,就可以完成PS端控制软件的设计,从而为后面进一步编写基于操作系统的驱动程序打下坚实的基础。

 

大家可以读完本文后,进一步利用官方文档,熟悉一下PS的其他外设操作。

  • 1
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
Zed-f9k-30a是一款先进的电子产品,下面将详细介绍它的功能和使用说明。首先,Zed-f9k-30a是一款全球定位系统(GPS)接收器,用于接收和解码卫星信号,以确定用户的地理位置。它采用了先进的Zed-f9k技术,提供了高精度和高可靠性的定位服务。 Zed-f9k-30a具有多种功能,包括实时定位、速度测量、高度测量等。它可以准确地定位用户的位置,并提供相关的地理信息。此外,它还具有导航功能,可以为用户提供方向指示和路线规划。用户可以通过连接设备,如智能手机或导航系统,来获取定位和导航信息。 Zed-f9k-30a的使用非常简单。首先,确保设备已正确连接,并获得了足够的卫星信号。然后,按下电源按钮启动设备,并等待设备初始化。在初始化完成后,设备将显示当前位置信息和其他相关数据。 如果需要进行导航,用户可以选择目的地,并通过设备上的操作按钮或连接的设备(如智能手机)来获取路线指示。设备将指导用户行驶方向,并提供附加信息,如距离和预计到达时间。用户可以根据需要设置导航偏好,如避开拥堵或选择最快路线。 为了确保最佳的性能和精度,请避免遮挡设备天线的物体,并在使用过程中保持设备干燥和安全。此外,还要定期检查设备的电池电量,并根据需要进行充电。在不使用设备时,建议将其存放在干燥、通风和安全的地方,以延长其使用寿命。 总而言之,Zed-f9k-30a是一款功能强大且易于使用的GPS接收器。它可以实现高精度定位和导航,并为用户提供便利的出行体验。无论是在旅行、户外运动还是日常导航中,Zed-f9k-30a都将是一个可靠的伙伴。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值