首页 | 新闻 | 新品 | 文库 | 方案 | 视频 | 下载 | 商城 | 开发板 | 数据中心 | 座谈新版 | 培训 | 工具 | 博客 | 论坛 | 百科 | GEC | 活动 | 主题月 | 电子展
返回列表 回复 发帖

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

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
#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();</i;

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端控制软件的设计,从而为后面进一步编写基于操作系统的驱动程序打下坚实的基础。
记录学习中的点点滴滴,让每一天过的更加有意义!
返回列表