Xilinx软核AXI Timer 和AXI INTC 的使用心得

转自:http://blog.sina.com.cn/s/blog_98740ded0102uyzo.html

Xilinx软核AXI Timer 和AXI INTC 的使用心得

 (2014-08-08 08:39:46)

这两天做定时器和中断控制器的实验。

在搭建的Microblaze软核基础上,添加了一个AXI Timer和一个AXI INTC。

编译得到比特流文件后,导出到SDK中。编写定时器中断的例程,原本以为跟GPIO 、SPI什么的用法一样,直接调用xtmrctr.c/h 、 xintc.c/h就能完成。

但是,现实就是,无论怎么搞都搞不定。

在SDK中,对于每一个外设,如gpio、spi、timer、intc、tft等等,都提供了三种层次的驱动函数,无论在哪一种层次上编程都应该能实现期望的功能。

下面以timer为例。

1.最低层次是,只提供了读写寄存器的函数,函数参数需要外设地址、寄存器偏移地址、掩码等。这些函数在xtmrctr_l.h中,当然也提供了寄存器偏移的宏变量,与gpio的数据手册中的寄存器描述是一致的。

XTmrCtr_WriteReg(BaseAddress, TmrCtrNumber, RegOffset, ValueToWrite)或者XGpio_Out32(...)

 XTmrCtr_ReadReg(BaseAddress, TmrCtrNumber, RegOffset)或者XGpio_In32(....)

#define XTC_DEVICE_TIMER_COUNT 2

扫描二维码关注公众号,回复: 3944626 查看本文章

#define XTC_TIMER_COUNTER_OFFSET 16

#define XTC_TCSR_OFFSET 0

#define XTC_TLR_OFFSET 4

#define XTC_TCR_OFFSET 8

#define XTC_CSR_CASC_MASK 0x00000800

#define XTC_CSR_ENABLE_ALL_MASK 0x00000400

#define XTC_CSR_ENABLE_PWM_MASK 0x00000200

#define XTC_CSR_INT_OCCURED_MASK 0x00000100

#define XTC_CSR_ENABLE_TMR_MASK 0x00000080

#define XTC_CSR_ENABLE_INT_MASK 0x00000040

#define XTC_CSR_LOAD_MASK 0x00000020

#define XTC_CSR_AUTO_RELOAD_MASK 0x00000010

#define XTC_CSR_EXT_CAPTURE_MASK 0x00000008

#define XTC_CSR_EXT_GENERATE_MASK 0x00000004

#define XTC_CSR_DOWN_COUNT_MASK 0x00000002

#define XTC_CSR_CAPTURE_MODE_MASK 0x00000001

在这个层次上,大家可以看到,不同的外设的读写函数归根到底都是Xil_Out32和Xil_In32。

2.低层次的,是提供了读写不同寄存器的方式,需要用到的函数参数为外设地址,掩码。这些驱动函数还是在xtmrctr_l.h和xtmrctr_l.c中,此时相对于1,是把偏移地址给隐藏起来了,通过函数的字面意思就可以理解到函数的含义,一般只需提供基地址,timer中由于有两个计数器,所以还有一个编号:

控制寄存器写函数:XTmrCtr_SetControlStatusReg(BaseAddress, TmrCtrNumber, RegisterValue)

控制寄存器写函数:XTmrCtr_GetControlStatusReg(BaseAddress, TmrCtrNumber)

读计数器值函数:XTmrCtr_GetTimerCounterReg(BaseAddress, TmrCtrNumber)

写加载寄存器值函数:XTmrCtr_SetLoadReg(BaseAddress, TmrCtrNumber, RegisterValue)

定时器使能函数: XTmrCtr_Enable(BaseAddress, TmrCtrNumber)

定时器使能中断函数: XTmrCtr_EnableIntr(BaseAddress, TmrCtrNumber)

等等,未举完。

3.高层次的,被封装成结构体的形式,用户定义好一个结构体变量,对它使用相应的初始化函数之后。再要进行程序编写,完全不需要用到外设地址、或者寄存器偏移等等。相关函数比较多,xtmrctr_g.c(本工程的所有定时器实例的参数表,由xparameters.h可以得到),xtmrctr_options.c,xtmrctr_selftest.c.c, xtmrctr_intr.c, xtmrctr_stats.c,当然还有最最重要的xtmrctr.h和xtmrctr.c两个文件了。

xtmrctr.h包含了这里好所有的c文件函数的声明。所以在这一层次上调用函数的话,可以直接看这个文件里面的函数定义。

其中最重要的结构体定义是:

typedef struct {

XTmrCtrStats Stats;

u32 BaseAddress;

u32 IsReady;

u32 IsStartedTmrCtr0;

u32 IsStartedTmrCtr1;

XTmrCtr_Handler Handler;

void *CallBackRef;

} XTmrCtr;

然后是各种函数的定义,他们的参数基本上都是上面这个结构体的指针。

int XTmrCtr_Initialize(XTmrCtr * InstancePtr, u16 DeviceId);

void XTmrCtr_Start(XTmrCtr * InstancePtr, u8 TmrCtrNumber);

void XTmrCtr_Stop(XTmrCtr * InstancePtr, u8 TmrCtrNumber);

u32 XTmrCtr_GetValue(XTmrCtr * InstancePtr, u8 TmrCtrNumber);

void XTmrCtr_SetResetValue(XTmrCtr * InstancePtr, u8 TmrCtrNumber,

  u32 ResetValue);

u32 XTmrCtr_GetCaptureValue(XTmrCtr * InstancePtr, u8 TmrCtrNumber);

int XTmrCtr_IsExpired(XTmrCtr * InstancePtr, u8 TmrCtrNumber);

void XTmrCtr_Reset(XTmrCtr * InstancePtr, u8 TmrCtrNumber);

XTmrCtr_Config *XTmrCtr_LookupConfig(u16 DeviceId);

void XTmrCtr_SetOptions(XTmrCtr * InstancePtr, u8 TmrCtrNumber, u32 Options);

u32 XTmrCtr_GetOptions(XTmrCtr * InstancePtr, u8 TmrCtrNumber);

void XTmrCtr_GetStats(XTmrCtr * InstancePtr, XTmrCtrStats * StatsPtr);

void XTmrCtr_ClearStats(XTmrCtr * InstancePtr);

int XTmrCtr_SelfTest(XTmrCtr * InstancePtr, u8 TmrCtrNumber);

void XTmrCtr_SetHandler(XTmrCtr * InstancePtr, XTmrCtr_Handler FuncPtr,

void *CallBackRef);

void XTmrCtr_InterruptHandler(void *InstancePtr);

而且从函数名的字面意义上去理解,也是非常简单的,其实挺喜欢用这一层次的做法,它封装了很多底层细节的东西,编程起来代码看着更舒服。甚至,我们不需要再去看数据手册。

以上是背景,然后我就要开始吐槽了。

**************************************************************************************

我最开始写了一个定时器中断的例子,用到的是高层次里面的驱动函数。(里面有各种被注释掉的代码,就不用看了,懂的人会知道为什么会有这么多恶心的注释掉代码)。

#include

#include "platform.h"

#include "xparameters.h"

#include "xgpio.h"

#include "xil_io.h"

#include "mb_interface.h"

#include "xtmrctr.h"

#include "xintc.h"

#define BTN_BASEADDR   XPAR_AXI_GPIO_2_BASEADDR

#define BTN_DEVICE_ID   XPAR_AXI_GPIO_2_DEVICE_ID

#define BTN_IRTP_ID XPAR_AXI_INTC_0_AXI_GPIO_2_IP2INTC_IRPT_INTR

#define SW_BASEADDR   XPAR_AXI_GPIO_1_BASEADDR

#define SW_DEVICE_ID   XPAR_AXI_GPIO_1_DEVICE_ID

#define SW_IRTP_ID XPAR_AXI_INTC_0_AXI_GPIO_1_IP2INTC_IRPT_INTR

#define TIMER_BASEADDR   XPAR_AXI_TIMER_0_BASEADDR

#define TIMER_DEVICE_ID   XPAR_AXI_TIMER_0_DEVICE_ID

#define TIMER_IRTP_ID XPAR_AXI_INTC_0_AXI_TIMER_0_INTERRUPT_INTR

#define INTC_DEVICE_ID XPAR_AXI_INTC_0_DEVICE_ID

XGpio btn;

XGpio sw;

XTmrCtr timer;

XIntc intCtrl;

XGpio led;

char str[100];

void print(char *str);

void PushButtonHandle(void *pshButton);

void SwitchHandle(void *sw);

void TimerHandle(void *timer);

static int i = 0;

unsigned int flag = 0 ;

void test_pwm();

void delay(int t);

int main()

{

 init_platform();

 xil_printf("Hello World\n");

// test_pwm();

 XGpio_Initialize(&btn, BTN_DEVICE_ID);

 XGpio_Initialize(&sw, SW_DEVICE_ID);

//对定时器的初始化

 XTmrCtr_Initialize(&timer,TIMER_DEVICE_ID);

//对中断控制器的初始化

 XIntc_Initialize(&intCtrl, INTC_DEVICE_ID);

 XGpio_SetDataDirection(&sw, 1, 0xf);

 XGpio_SetDataDirection(&btn, 1, 0xf);

//  XIntc_Connect(&intCtrl, BTN_IRTP_ID, PushButtonHandle, &btn);

//  XIntc_Connect(&intCtrl, SW_IRTP_ID, SwitchHandle, &sw);

//将定时器中断注册到中断控制器上,中断服务函数为TimerHandle

 XIntc_Connect(&intCtrl, TIMER_IRTP_ID , TimerHandle, &timer);

//  XIntc_Enable(&intCtrl, BTN_IRTP_ID);

//  XIntc_Enable(&intCtrl, SW_IRTP_ID);

//使能定时器中断

 XIntc_Enable(&intCtrl, TIMER_IRTP_ID);

//将中断控制器的中断注册到处理器上,中断服务函数为默认的XIntc_DeviceInterruptHandler

 microblaze_register_handler(XIntc_DeviceInterruptHandler, INTC_DEVICE_ID);

//使能处理器的中断

 microblaze_enable_interrupts();

//启动中断控制器

   XIntc_Start(&intCtrl, XIN_REAL_MODE);

//  XGpio_InterruptEnable(&btn, 1);

//  XGpio_InterruptGlobalEnable(&btn);

//  XGpio_InterruptEnable(&sw, 1);

//  XGpio_InterruptGlobalEnable(&sw);

// u32 options = XTmrCtr_GetOptions(&timer,0);

// options = XTmrCtr_ReadReg(TIMER_BASEADDR,0,XTC_TCSR_OFFSET);

//    xil_printf("options =  X\n",options);

// XTmrCtr_WriteReg(TIMER_BASEADDR, 0, XTC_TCSR_OFFSET, 0x7d6);

//设置定时器的工作模式,使能所有的计数器,使能中断,使能自动装载,使能向下计数

 XTmrCtr_SetOptions(&timer,0,XTC_ENABLE_ALL_OPTION| XTC_INT_MODE_OPTION |XTC_AUTO_RELOAD_OPTION | XTC_DOWN_COUNT_OPTION);

//设定计数溢出值

 XTmrCtr_SetResetValue(&timer,0,1<<31);//2000000);//10ms

//启动定时器

 XTmrCtr_Start(&timer,0);

 XGpio_Initialize(&led, XPAR_AXI_GPIO_0_DEVICE_ID);

 XGpio_SetDataDirection(&led, 1, 0x00);

 XGpio_DiscreteWrite(&led, 1, 0x1);

 XGpio_DiscreteWrite(&led, 1, 0x2);

 XGpio_DiscreteWrite(&led, 1, 0x3);

 while(1)

 {

//  delay(153000);//about 10ms

//  u32 countervalue =  XTmrCtr_GetValue(&timer,0);

//  xil_printf("***Main Loop counter value = X\n",countervalue);

//  XGpio_DiscreteWrite(&led, 1, i);

//  xil_printf("***i = %d\n\r",i);

 }

  return 0;

}

//void PushButtonHandle(void *btn)

//{

//

// XGpio* PushButton = (XGpio*) btn;

// u32 btnState = XGpio_DiscreteRead(PushButton, 1);

//    print("PushButtonHandle\n");

//    xil_printf("i = %d\n\r",i);

//

// i++;

// XGpio_InterruptClear(PushButton, 0xff);

//}

//void SwitchHandle(void *sw)

//{

//

// XGpio* Switch = (XGpio*) sw;

// u32 btnState = XGpio_DiscreteRead(Switch, 1);

// xil_printf("SwitchHandle\n");

//    xil_printf("i = %d\n\r",i);

//

// i++;

// XGpio_InterruptClear(Switch, 0xff);

//}

void TimerHandle(void *timer)

{

XTmrCtr* Timer = (XTmrCtr*) timer;

u32 countervalue = XTmrCtr_GetValue(Timer,0);

u32 options = XTmrCtr_GetOptions(Timer,0);

//XGpio_DiscreteWrite(&led, 1, 1<<(i%4));

flag  = 1;

i++;

if(i >= 10){

xil_printf("TimerHandle\n");

   xil_printf("i = %d\n\r",i);

   xil_printf("counter value = X\n",countervalue);

   xil_printf("options =  X\n",options);

   XGpio_DiscreteWrite(&led, 1, i);

   i = 0;

}

//XTmrCtr_ClearStats(Timer);

}

void test_pwm(){

XTmrCtr_WriteReg(TIMER_BASEADDR,0,XTC_TLR_OFFSET,1000);

XTmrCtr_WriteReg(TIMER_BASEADDR,1,XTC_TLR_OFFSET,300);

XTmrCtr_WriteReg(TIMER_BASEADDR,0,XTC_TCSR_OFFSET,0x6f6);

XTmrCtr_WriteReg(TIMER_BASEADDR,1,XTC_TCSR_OFFSET,0x6f6);

u32 options;

options = XTmrCtr_ReadReg(TIMER_BASEADDR,0,XTC_TCSR_OFFSET);

    xil_printf("options =  X\n",options);

options = XTmrCtr_ReadReg(TIMER_BASEADDR,1,XTC_TCSR_OFFSET);

    xil_printf("options =  X\n",options);

options = XTmrCtr_ReadReg(TIMER_BASEADDR,0,XTC_TLR_OFFSET);

    xil_printf("options =  X\n",options);

options = XTmrCtr_ReadReg(TIMER_BASEADDR,1,XTC_TLR_OFFSET);

    xil_printf("options =  X\n",options);

 XGpio_Initialize(&led, XPAR_AXI_GPIO_0_DEVICE_ID);

 XGpio_SetDataDirection(&led, 1, 0x00);

    while(1){

    //  XGpio_DiscreteWrite(&led, 1, 0x1);

    // XGpio_DiscreteWrite(&led, 1, 0x0);2.4MHz

    XGpio_WriteReg(XPAR_AXI_GPIO_0_BASEADDR,XGPIO_DATA_OFFSET,0x1);

    delay(1000);

    XGpio_WriteReg(XPAR_AXI_GPIO_0_BASEADDR,XGPIO_DATA_OFFSET,0x0);//8.3MHz,120ns,60ns ,12*cycle

    delay(1000);//65us,65ns,一个自减一和判断真假操作,凶耗约65ns/5= 13*cycle;

    }

}

void delay(int t){

while(t--);

}

虽然,它能工作,但是,它特别坑啊。我无论将定时器做成1s的,还是10ms的,亦或是10s的,它都工作及其的不正常,时间间隔完全不是那么回事儿,而且还会有卡死的感觉,这些无论是观察led的点亮效果,还是控制台打印信息都可以感受出来。

看着这么优雅的代码,就此逝去。我也是醉了,SDK软件本身的各种BUG就不说了,我觉得他们家的mdm软核调试器模块也有问题,一加上调试器,运行时间就不对了。还得仰仗串口或者led什么的来观察。哎~

于是我绝望的搞起寄存器了。注意这个里面用到的定时器的处理函数都是第二层次的方式。其他的GPIO,INTC中断控制器都是很优雅的结构体操作啊。对于处女座来说,简直就是灾难,有木有~~~~

********************************************************************************************

#include

#include "platform.h"

#include "xparameters.h"

#include "xutil.h"

#include "xintc.h"

#include "xtmrctr.h"

#include "xil_macroback.h"

#include "xgpio.h"

void timer_int_handler(void);

void button_int_handler(void);

void switch_int_handler(void);

unsigned int timer_cnt;

unsigned int btn_cnt;

unsigned int sw_cnt;

#define BTN_BASEADDR   XPAR_AXI_GPIO_2_BASEADDR

#define BTN_DEVICE_ID   XPAR_AXI_GPIO_2_DEVICE_ID

#define BTN_IRTP_ID XPAR_AXI_INTC_0_AXI_GPIO_2_IP2INTC_IRPT_INTR

#define SW_BASEADDR   XPAR_AXI_GPIO_1_BASEADDR

#define SW_DEVICE_ID   XPAR_AXI_GPIO_1_DEVICE_ID

#define SW_IRTP_ID XPAR_AXI_INTC_0_AXI_GPIO_1_IP2INTC_IRPT_INTR

#define TIMER_BASEADDR   XPAR_AXI_TIMER_0_BASEADDR

#define TIMER_DEVICE_ID   XPAR_AXI_TIMER_0_DEVICE_ID

#define TIMER_IRTP_ID XPAR_AXI_INTC_0_AXI_TIMER_0_INTERRUPT_INTR

#define INTC_BASEADDR XPAR_AXI_INTC_0_BASEADDR

#define INTC_DEVICE_ID XPAR_AXI_INTC_0_DEVICE_ID

XGpio btn;

XGpio sw;

XGpio led;

int main()

{

timer_cnt = 0;

xil_printf("--start the program test---\r\n");

//led灯的初始化,方向设置

XGpio_Initialize(&led, XPAR_AXI_GPIO_0_DEVICE_ID);

XGpio_SetDataDirection(&led, 1, 0x00);

XGpio_DiscreteWrite(&led, 1, 0x1);

//button 和switch的初始化、方向设置

XGpio_Initialize(&btn, BTN_DEVICE_ID);

XGpio_Initialize(&sw, SW_DEVICE_ID);

XGpio_SetDataDirection(&sw, 1, 0xf);

XGpio_SetDataDirection(&btn, 1, 0xf);

//设定定时器的载入值为10000000,在时钟为100MHz时,每隔100ms产生一次中断

XTmrCtr_mSetLoadReg(TIMER_BASEADDR, 0, 10000000);

//设定定时器的状态位为:允许定时器,允许中断,自动载入,向下计数(减计数)

XTmrCtr_mSetControlStatusReg(TIMER_BASEADDR, 0,

XTC_CSR_ENABLE_TMR_MASK | XTC_CSR_ENABLE_INT_MASK |

XTC_CSR_AUTO_RELOAD_MASK | XTC_CSR_DOWN_COUNT_MASK);

//使能MB的中断

microblaze_enable_interrupts();

//注册中断控制器到MB上

microblaze_register_handler(

XIntc_DeviceInterruptHandler,

INTC_DEVICE_ID);

//将定时器中断注册到中断控制器上

XIntc_RegisterHandler(

INTC_BASEADDR,

XPAR_AXI_INTC_0_AXI_TIMER_0_INTERRUPT_INTR,

(XInterruptHandler)timer_int_handler,

(void *)0

);

//将button的中断注册到中断控制器上

XIntc_RegisterHandler(

INTC_BASEADDR,

XPAR_AXI_INTC_0_AXI_GPIO_2_IP2INTC_IRPT_INTR,

(XInterruptHandler)button_int_handler,

(void *)0

);

//将switch的中断注册到中断控制器上

XIntc_RegisterHandler(

INTC_BASEADDR,

XPAR_AXI_INTC_0_AXI_GPIO_1_IP2INTC_IRPT_INTR,

(XInterruptHandler)switch_int_handler,

(void *)0

);

//使能btn、sw的全局中断,和每一个位上的中断

XGpio_InterruptEnable(&btn, 0x7);

XGpio_InterruptGlobalEnable(&btn);

XGpio_InterruptEnable(&sw, 0xf);

XGpio_InterruptGlobalEnable(&sw);

//使能中断控制器的主使能,即ME

XIntc_mMasterEnable(INTC_BASEADDR);

//使能中断控制器上的定时器中断源、button中断源核switch中断源

XIntc_mEnableIntr(INTC_BASEADDR,

XPAR_AXI_TIMER_0_INTERRUPT_MASK|

XPAR_AXI_GPIO_2_IP2INTC_IRPT_MASK|

XPAR_AXI_GPIO_1_IP2INTC_IRPT_MASK);

while(1)

{;

}

return 0;

}

extern XGpio led;

int i = 0;

//定时器的中断服务程序

void timer_int_handler(void)

{

unsigned int timer_csr;

timer_cnt++;

//读取定时器的状态寄存器,确认是否真的产生了中断

timer_csr = XTmrCtr_mGetControlStatusReg(TIMER_BASEADDR, 0);

if(timer_csr & XTC_CSR_INT_OCCURED_MASK)

{

// xil_printf("--timer interrupt happened times = %d!--\r\n", timer_cnt);

//将状态寄存器写回

XTmrCtr_mSetControlStatusReg(TIMER_BASEADDR, 0, timer_csr);

XGpio_DiscreteWrite(&led, 1, i);

xil_printf("***i = %d\n\r",i);

i++;

}

}

//button的中断服务程序

void button_int_handler(void)

{

btn_cnt ++;

xil_printf("--button interrupt happened times = %d!--\r\n",btn_cnt);

//完成中断服务程序后,需要清楚中断状态

XGpio_InterruptClear(&btn,0xf);

}

//switch的中断服务程序

void switch_int_handler(void )

{

sw_cnt ++;

xil_printf("--switch interrupt happened times = %d!--\r\n",sw_cnt);

//完成中断服务程序后,需要清楚中断状态

XGpio_InterruptClear(&sw,0xf);

}

然而,这种做法挺靠谱的了,10ms就是10ms的用户体验,1s就是1s的用户体验。节奏很对的了。

严厉谴责他们家的关于定时器的程序员。拉出来鞭尸,如下:

* Ver   Who  Date     Changes

* ----- ---- -------- -----------------------------------------------

* 1.00a ecm  08/16/01 First release

* 1.00b jhl  02/21/02 Repartitioned the driver for smaller files

* 1.10b mta  03/21/07 Updated to new coding style.

* 1.11a sdm  08/22/08 Removed support for static interrupt handlers from the MDD

*      file

* 2.00a ktn  10/30/09 Updated to use HAL API's. _m is removed from all the macro

*      definitions.

* 2.01a ktn  07/12/10 Renamed the macro XTimerCtr_ReadReg as XTmrCtr_ReadReg

*      for naming consistency (CR 559142).

* 2.02a sdm  09/28/10 Updated the driver tcl to generate the xparameters

*      for the timer clock frequency (CR 572679).

* 2.03a rvo  11/30/10 Added check to see if interrupt is enabled before further

*      processing for CR 584557.

* 2.04a sdm  07/12/11 Added support for cascade mode operation.

*      The cascade mode of operation is present in the latest

*      versions of the axi_timer IP. Please check the HW

*      Datasheet to see whether this feature is present in the

*      version of the IP that you are using.

* 2.05a adk  15/05/13 Fixed the CR:693066

*      Added the IsStartedTmrCtr0/IsStartedTmrCtr1 members to the

*      XTmrCtr instance structure.

*      The IsStartedTmrCtrX will be assigned XIL_COMPONENT_IS_STARTED in

*      the XTmrCtr_Start function.

*      The IsStartedTmrCtrX will be cleared in the XTmrCtr_Stop function.

*      There will be no Initialization done in the

*      XTmrCtr_Initialize if both the timers have already started and

*      the XST_DEVICE_IS_STARTED Status is returned.

*      Removed the logic in the XTmrCtr_Initialize function

*      which was checking the Register Value to know whether

*      a timer has started or not.

*

*

都什么破烂玩意儿。

以上吐槽完毕。

bwb@STI 2014.08.08

猜你喜欢

转载自blog.csdn.net/liuzq/article/details/81360330