vivado pl控制ps中断踩坑案例
参考文献
踩坑点1
不止为何我在勾选上PL-PS中断以及PS给PL时钟之后,顶层模块里面会生成FCLK_CLK_0以及PL_IRQ两个信号,然后我的程序下面也会有一堆wire,里面也有这两个信号,
此时生成比特流会报一个多驱动的错,需要将input还有output的这两个信号删掉,顶层模块的输入输出里面也删掉,报错解决。
踩坑点2
例化VIO时需要命名
踩坑点3
这里的始终设置应该设置为33MHz,这样sleep函数里的时间才是准确的。
但是当设置成33MHz后会出现新的报错
这个报错的意思是,我们设置的FCLK_CLK0_0是50MHz,但是实际给不到这么高,因为我们之前把33.333333改成了33,最高只能到49.5MHz,因此我们需要把FCLK_CLK0的频率改为49.5MHz,报错解决。
一些想法
师兄布置的实验是这样的
我在具体实现过程中VIO0打印输出hello world是在终端服务函数里面输出的,然后VIO1控制计数器的服务函数就只增加计数器的周期,VIO2控制计数器开关然后周期打印hello world是在主循环里完成的,并非是再次触发了VIO0的中断,感觉师兄的意思应该是要再次触发中断的。
然后讲讲前面说的sleep函数。sleep函数的源代码如下,我们只需要看sleep_A9函数即可。
sleep_A9函数以及XTime_GetTime函数的代码如下。sleep_A9函数大概意思就是,我们先获取当前时间,然后在当前时间的基础上加上我们要sleep的时间得到终止时间,然后在while循环里不断的返回当前时间直到当前时间与终止时间相等,函数返回。XTime_GetTime函数则给出了如何获取时间的,首先CPU里面有一个64位的全局时钟,而Zynq是32位的,因此需要分别获取高32位和低32位,使用时钟的基地址加上高位的偏移量以及低位的偏移量来分别获取高32位和低32位,但是还存在一点问题就是可能获取完了高32位后获取低32位时产生了进位,此时高32位已经变了,因此加了一层循环来判断,取完之后的高32位与我们已经取到的高32位是否有变化,如果没变化则将时间返回到对应的地址中,如果变化了则再次获取时间。
源代码
#include <stdio.h>
#include "platform.h"
#include "xil_printf.h"
#include "xscugic.h"
#include "xil_exception.h"
#include "xparameters.h"
#include <sleep.h>
#define INTC_DEVICE_ID XPAR_PS7_SCUGIC_0_DEVICE_ID
//static int VIO_0 = 0;
//static int VIO_1 = 0;
static int VIO_2 = 0;
static u8 count = 1;
static XScuGic INTCInst;
static void SW0_intr_Handler();
static void SW1_intr_Handler();
static void SW2_intr_Handler();
static int IntcInitFunction(u16 DeviceId);
static void SW0_intr_Handler()
{
printf("Hello World!\r\n");
}
static void SW1_intr_Handler()
{
count = (count + 1) % 3;
}
static void SW2_intr_Handler()
{
VIO_2 = 1 - VIO_2;
}
int IntcInitFunction(u16 DeviceId)
{
XScuGic_Config *IntcConfig;
int status;
// Interrupt controller initialisation
IntcConfig = XScuGic_LookupConfig(DeviceId);
status = XScuGic_CfgInitialize(&INTCInst, IntcConfig, IntcConfig->CpuBaseAddress);
if(status != XST_SUCCESS) return XST_FAILURE;
// Call to interrupt setup
Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT,
(Xil_ExceptionHandler)XScuGic_InterruptHandler,
&INTCInst);
Xil_ExceptionEnable();
/*********************************(begin)****************************************/
// Connect SW0 interrupt to handler
status = XScuGic_Connect(&INTCInst,
XPAR_FABRIC_PL_IRQ0_INTR,
(Xil_ExceptionHandler)SW0_intr_Handler,
(void *)1);
if(status != XST_SUCCESS) return XST_FAILURE;
// Connect SW1 interrupt to handler
status = XScuGic_Connect(&INTCInst,
XPAR_FABRIC_PL_IRQ1_INTR,
(Xil_ExceptionHandler)SW1_intr_Handler,
(void *)1);
if(status != XST_SUCCESS) return XST_FAILURE;
// Connect SW2 interrupt to handler
status = XScuGic_Connect(&INTCInst,
XPAR_FABRIC_PL_IRQ2_INTR,
(Xil_ExceptionHandler)SW2_intr_Handler,
(void *)1);
if(status != XST_SUCCESS) return XST_FAILURE;
// Set interrupt type of SW0 to rising edge
XScuGic_SetPriorityTriggerType(&INTCInst, XPAR_FABRIC_PL_IRQ0_INTR, 0x00, 0x03);
// Set interrupt type of SW1 to rising edge
XScuGic_SetPriorityTriggerType(&INTCInst, XPAR_FABRIC_PL_IRQ1_INTR, 0x00, 0x03);
// Set interrupt type of SW2 to rising edge
XScuGic_SetPriorityTriggerType(&INTCInst, XPAR_FABRIC_PL_IRQ2_INTR, 0x00, 0x03);
// Enable SW0 interrupts in the controller
XScuGic_Enable(&INTCInst, XPAR_FABRIC_PL_IRQ0_INTR);
// Enable SW1 interrupts in the controller
XScuGic_Enable(&INTCInst, XPAR_FABRIC_PL_IRQ1_INTR);
// Enable SW2 interrupts in the controller
XScuGic_Enable(&INTCInst, XPAR_FABRIC_PL_IRQ2_INTR);
/*********************************(end)****************************************/
return XST_SUCCESS;
}
int main()
{
printf("This is PL IRQ Test!\r\n");
IntcInitFunction(INTC_DEVICE_ID);
while(1){
if(VIO_2 == 1){
print("Hello World!\r\n");
sleep(count);
}
}
return 0;
}