前言
最近在学习ZYNQ的PS端,目前是裸机阶段,学习CPU的私有定时器时,发现每个核似乎只有一个,搜索了一下IP库,发现有个AXI TIMER,并且有PWM功能,于是照着官方例程配置了一下,在这里记录一下。 PL+PS的架构确实方便啊,ARM用C开发,还可以上Linux,想要什么定制化的东西还可以用FPGA。
1、ZYNQ配置
一个AXI TIMER有两个通道,这里我全部使用了,并且使能了中断,添加模块的时候发现还有一个PWM引脚,于是我配了一个PL的IO上去,都测试一下。
2、SDK代码
2.1 定时器配置
需要关注的点都写到注释里了
#include "xparameters.h"
#include "xtmrctr.h"
#include "xscugic.h"
#include "xil_exception.h"
#include "xil_printf.h"
/*正确配置ZYNQ后,XPAR_AXI_TIMER_0_DEVICE_ID、XPAR_FABRIC_AXI_TIMER_0_INTERRUPT_INTR可以在xparameters.h中找到*/
#define SCUGIC_DEVICE_ID XPAR_SCUGIC_SINGLE_DEVICE_ID //通用中断 ID
#define AXI_TIMER_DEVICE_ID XPAR_AXI_TIMER_0_DEVICE_ID //AXI_Timer设备ID
#define AXI_TIMER_IRPT_INTR XPAR_FABRIC_AXI_TIMER_0_INTERRUPT_INTR //AXI TIMER 中断号
XTmrCtr xTmrCtr_Inst; //AXI Timer驱动示例
XScuGic xScuGic_Inst; //中断控制器驱动程序实例
//AXI TIMER模块内有两个定时器,这里我更愿意叫做每个AXI TIMER的不同通道,编号为0-1
#define AXI_TIMER_CHANNEL_1 0
#define AXI_TIMER_CHANNEL_2 1
//定义是否测试PWM
#define AXI_TIMER_PWM_TEST_SW 1
//定时器周期(微秒为单位)
#define AXI_TIMER_PERIOD_US 10000
//PWM高电平的持续时间(微秒为单位)
#define AXI_TIMER_PWM_HIGH_TIME_US 5000
//定时器中断回调
void xTmrCtr_Int_Handler(void *CallBackRef, u8 TmrCtrNumber)
{
xil_printf("AXI Timer Int! Channel:%d\n",TmrCtrNumber+1);
}
//定时的微秒数转寄存器值
//AXI TIMER是倒计时,如果想定时1MS,那么寄存器中要写入:最大值-1MS
//AXI TIMER的时钟为50M,1US为50个Tick
u32 xTmr_US_To_RegValue(u32 US)
{
u32 Value;
Value = 50*US;
return 0xFFFFFFFF - Value;
}
//定时的微秒数转纳秒
u32 xTmr_US_To_NS(u32 US)
{
return US*1000;
}
//AXITIMER初始化
int xTmrCtr_Init(XTmrCtr *xTmrCtr_Ptr,u32 DeviceId)
{
int Status,DutyCycle;
//初始化AXI_TIMER 0
Status = XTmrCtr_Initialize(xTmrCtr_Ptr, DeviceId);
if(Status != XST_SUCCESS)
{
return XST_FAILURE;
}
//自检:两个定时器
Status = XTmrCtr_SelfTest(xTmrCtr_Ptr, 2-1);
if(Status != XST_SUCCESS)
{
return XST_FAILURE;
}
//设置中断回调函数
XTmrCtr_SetHandler(xTmrCtr_Ptr,xTmrCtr_Int_Handler,xTmrCtr_Ptr);
//定时器1中断计数
XTmrCtr_SetResetValue(xTmrCtr_Ptr,AXI_TIMER_CHANNEL_1,xTmr_US_To_RegValue(AXI_TIMER_PERIOD_US));
//定时器2中断计数
XTmrCtr_SetResetValue(xTmrCtr_Ptr,AXI_TIMER_CHANNEL_2,xTmr_US_To_RegValue(AXI_TIMER_PERIOD_US));
//定时器中断打开
XTmrCtr_SetOptions(xTmrCtr_Ptr, AXI_TIMER_CHANNEL_1, XTC_INT_MODE_OPTION | XTC_AUTO_RELOAD_OPTION);
XTmrCtr_SetOptions(xTmrCtr_Ptr, AXI_TIMER_CHANNEL_2, XTC_INT_MODE_OPTION | XTC_AUTO_RELOAD_OPTION);
//测试PWM的话,额外配置一下PWM功能
#if AXI_TIMER_PWM_TEST_SW
DutyCycle = XTmrCtr_PwmConfigure(xTmrCtr_Ptr, xTmr_US_To_NS(AXI_TIMER_PERIOD_US),xTmr_US_To_NS(AXI_TIMER_PWM_HIGH_TIME_US));
//打印占空比
xil_printf("AXI Timer PWM DutyCycle:%d%!\n",DutyCycle);
#endif
return XST_SUCCESS;
}
2.2 通用中断配置
这没什么说的。
//通用中断初始化
int xScuGic_Init(XScuGic *ScuGic_Ptr,XTmrCtr *xTmrCtr_Ptr)
{
int Status;
XScuGic_Config *intc_cfg_ptr;
intc_cfg_ptr = XScuGic_LookupConfig(SCUGIC_DEVICE_ID);
if(intc_cfg_ptr == NULL)
{
return XST_FAILURE;
}
Status = XScuGic_CfgInitialize(ScuGic_Ptr, intc_cfg_ptr,intc_cfg_ptr->CpuBaseAddress);
if(Status != XST_SUCCESS)
{
return XST_FAILURE;
}
//设置并打开中断异常处理功能
Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT,(Xil_ExceptionHandler)XScuGic_InterruptHandler, ScuGic_Ptr);
Xil_ExceptionEnable();
//设置定时器中断
XScuGic_Connect(ScuGic_Ptr, AXI_TIMER_IRPT_INTR,(Xil_ExceptionHandler)XTmrCtr_InterruptHandler, (void *)xTmrCtr_Ptr);
//使能 GIC中的定时器中断
XScuGic_Enable(ScuGic_Ptr, AXI_TIMER_IRPT_INTR);
return XST_SUCCESS;
}
2.2 PWM功能
代码在定时器初始化中。
3、实际使用
int main()
{
u32 Status;
xil_printf("AXI Timer Test!\n");
//定时器初始化
Status = xTmrCtr_Init(&xTmrCtr_Inst,AXI_TIMER_DEVICE_ID);
if(Status != XST_SUCCESS)
{
xil_printf("AXI Timer Init Error!\n");
}
//通用中断初始化
Status = xScuGic_Init(&xScuGic_Inst,&xTmrCtr_Inst);
if(Status != XST_SUCCESS)
{
xil_printf("ScuGic Init Error!\n");
}
//AXI TIMER的两个通道都开始工作
XTmrCtr_Start(&xTmrCtr_Inst,AXI_TIMER_CHANNEL_1);
XTmrCtr_Start(&xTmrCtr_Inst,AXI_TIMER_CHANNEL_2);
//AXI_TIMER_PWM_TEST_SW为1时,启用PWM
#if AXI_TIMER_PWM_TEST_SW
XTmrCtr_PwmDisable(&xTmrCtr_Inst);
XTmrCtr_PwmEnable(&xTmrCtr_Inst);
#endif
while(1);
return 0;
}
4、总结
在中断回调里,会打印AXI TIMER的哪个通道产生了中断,更改了几个定时时间,功能正常。
PWM功能,利用示波器点了一下配置的PL引脚,更改了几个占空比,波形正常。