D:\Xilinx\SDK\2019.1\data\embeddedsw\XilinxProcessorIPLib\drivers\tmrctr_v4_5\examples
中的
xtmrctr_intr_example.c文件,作为移植参考。
这个例子中,核心数据结构是XTmrCtr。这也是我们要封装的对象。
如果需要cascase模式的参考,则查找xtmrctr_intr_64bit_example.c文件即可。
INTC InterruptController; /* The instance of the Interrupt Controller */
XTmrCtr TimerCounterInst; /* The instance of the Timer Counter */
volatile int TimerExpired;
在C++环境下,既可以封装为class,也可以封装为struct,为了尽量多的保持SOD风格,这里封装成struct。
// stamp_timer_dev.h
struct StampTimerDev
{
XTmrCtr tmr;
};
来看main,它里面只调用了TmrCtrIntrExample,这是功能主体。
来看TmrCtrIntrExample。
这个函数里,
首先,初始化timer,XTmrCtr_Initialize完成这个任务,利用传入的DeviceID,来lookup,并cfg_init。
然后,设置中断,TmrCtrSetupIntrSystem完成这个任务。来看看这个函数。
这个函数中,初始化INTC,XINC_connect,XINC_start,XINC_Enable,然后,初始化exception,
exception_register,exception_enable,这些操作都是整板级别的处理,这里需要重点关注的是,
xinc_connect。它将timer的handler挂载到INTC中去。
即XTmrCtr_InterruptHandler。这个函数,是ISR,在这个函数里,遍历所有的timer number,查询是哪个timer到期,然后调用timer的callback函数。这个函数,是BSP包里提供的函数。
回到TmrCtrIntrExample中,来看看下一个,
XTmrCtr_SetHandler,这个函数,在XTMRCTR中,注册callback。当中断发生后,INTC会调用ISR,而ISR又会调用callback,所以,必须要在XTMRCTR结构体中注册callback。这个函数,是BSP包中提供的函数。
回到TmrCtrIntrExample中,来看看下一个,
XTmrCtr_SetOptions,这个函数,设置选项参数,是BSP包中提供的函数。
回到TmrCtrIntrExample中,来看看下一个,
XTmrCtr_SetResetValue,这个函数,设置复位值,是BSP包中提供的函数,
回到TmrCtrIntrExample中,来看看下一个,
XTmrCtr_Start,这个函数,设置启动哪个number的timer,是BSP包中提供的函数。
之后,ISR运行在前台,等待被触发,main的主线程运行在后台,前后台之间,通过共享变量进行通信,前台修改共享变量的值,后台轮询共享变量的值。
回到TmrCtrIntrExample中,来看看下一个,
XTmrCtr_Stop,这个函数,设置停止哪个number的timer,是BSP包中提供的函数。
来看看这个例子中,为XTMRCTR设计的callback,即
TimerCounterHandler,
这个函数中,检查是否是当前number的timer到期,用XTmrCtr_IsExpired函数检查,
如果不是则跳过,如果是,则进行后续处理。
例如,修改共享变量的值,配置当前number的timer的参数。
if (XTmrCtr_IsExpired(InstancePtr, TmrCtrNumber)) {
TimerExpired++;
if (TimerExpired == 3) {
XTmrCtr_SetOptions(InstancePtr, TmrCtrNumber, 0);
}
}
所以,这个函数,是需要我们移植的函数。
首先是封装stamp_timer_init函数。
int stamp_timer_init(struct StampTimerDev* dev, int dev_id)
{
int ret;
/*
* Initialize the timer counter so that it's ready to use,
* specify the device ID that is generated in xparameters.h
*/
ret = XTmrCtr_Initialize(&dev->tmr, dev_id);
if (ret != XST_SUCCESS) {
return -1;
}
/*
* Perform a self-test to ensure that the hardware was built
* correctly, use the 1st timer in the device (0)
*/
ret = XTmrCtr_SelfTest(&dev->tmr, 0);
if (ret != XST_SUCCESS) {
return -1;
}
/*
* Perform a self-test to ensure that the hardware was built
* correctly, use the 2nd timer in the device (0)
*/
ret = XTmrCtr_SelfTest(&dev->tmr, 1);
if (ret != XST_SUCCESS) {
return -1;
}
/*
* Set a reset value for the timer counter such that it will expire
* eariler than letting it roll over from 0, the reset value is loaded
* into the timer counter when it is started
*/
XTmrCtr_SetResetValue(&dev->tmr, 0, 0);
XTmrCtr_SetResetValue(&dev->tmr, 1, 0);
/*
* Enable the interrupt of the timer counter so interrupts will occur
* and use auto reload mode such that the timer counter will reload
* itself automatically and continue repeatedly, without this option
* it would expire once only and set the Cascade mode.
*/
XTmrCtr_SetOptions(&dev->tmr, 0,
XTC_AUTO_RELOAD_OPTION |
XTC_CASCADE_MODE_OPTION);
/*
* Reset the timer counters such that it's incrementing by default
*/
XTmrCtr_Reset(&dev->tmr, 0);
XTmrCtr_Reset(&dev->tmr, 1);
return 0;
}
可以看出,区别在于,main中操作的对象是static的变量,而在封装函数中,替换成参数传入的对象。
然后是stamp_timer_start函数,
int stamp_timer_start(struct StampTimerDev* dev)
{
XTmrCtr_Start(&dev->tmr, 0);
return 0;
}
可以看出,这是对XTmrCtr_Start进行了简单的封装,区别只是在于变量引用上的不同。
我们只是需要时间戳,所以没有使用INT_MODE,而是使用了cascade模式,所以需要设计读取时间戳的函数。
get_timestamp。
u64 get_timestamp(struct StampTimerDev* dev)
{
u32 val0 = XTmrCtr_GetValue(&dev->tmr, 0);
u32 val1 = XTmrCtr_GetValue(&dev->tmr, 1);
u64 ret = (((u64)val1) << 32) + val0;
return ret;
}
为了更方便的读取时间,需要进一步处理读取的时间戳,所以需要设计时间转换函数。
u32 get_timestamp_ms(struct StampTimerDev* dev)
{
u64 val = get_timestamp(dev);
u64 ms = val / 100000;
return (u32)ms;
}
对于使用100MHZ时钟的axi_aclk而言,timer每个周期计数加1,所以1秒钟,计数值是100M,所以计数值与秒的换算关系,是除以100M,如果是毫秒,则先除以100M,再乘以1000。