79.ZYNQ内部私有定时器中断

本文介绍如何在ZYNQ的纯PS部分实现私有定时器中断,每秒中断一次并在中断函数中计数加1,通过串口打印输出。文中详细介绍了中断配置过程,包括硬件系统的配置与软件工程的建立。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

上篇文章实现了了PS接受来自PL的中断,本片文章将在ZYNQ的纯PS里实现私有定时器中断。每个一秒中断一次,在中断函数里计数加1,通过串口打印输出。

*本文所使用的开发板是Miz702(兼容zedboard) 
PC 开发环境版本:Vivado 2015.2 Xilinx SDK 2015.2*

中断原理

中断对于保证任务的实时性非常必要,在ZYNQ里集成了中断控制器GIC(Generic Interrupt Controller).GIC可以接受I/O外设中断IOP和PL中断,将这些中断发给CPU。 
中断体系结构框图图下: 
这里写图片描述

软件中断(SGI)

SGI通过写ICDSGIR寄存器产生SGI.

共享中断SPI

通过PS和PL内各种I/O和存储器控制器产生。

私有中断(PPI)

包含:全局定时器,私有看门狗定时器,私有定时器以及来自PL的FIQ/IRQ。本文主要介绍PPI,其它的请参考官方手册ug585_Zynq_7000_TRM.pdf。 
ZYNQ每个CPU链接5个私有外设中断,所有中断的触发类型都是固定不变的。并且来自PL的快速中断信号FIQ和中断信号IRQ反向,然后送到中断控制器因此尽管在ICDICFR1寄存器内反应的他们是低电平触发,但是PS-PL接口中为高电平触发。如图所示: 
这里写图片描述

私有定时器

zynq中每个ARM core都有自己的私有定时器,私有定时器的工作频率为CPU的一半,比如Miz702或者zedboard的ARM工作频率为666MHZ,则私有定时器的频率为333MHz. 
私有定时器的特性如下: 
(1)32为计数器,达到零时产生一个中断 
(2)8位预分频计数器,可以更好的控制中断周期 
(3)可配置一次性或者自动重加载模式 
(4)定时器时间可以通过下式计算: 
定时时间 = [(预分频器的值 + 1) (加载值 + 1)]/定时器频率

搭建硬件系统工程:

配置ZYNQ PS

把ZYNQ配置为只保留UART1,然后点击OK如图所示 
这里写图片描述
点击Run Connection Automation,取消掉Apply Board Preset,如图 
这里写图片描述
最终的框图很简洁,如图: 
这里写图片描述

建立软件工程

建立一个Hello World工程 
把Helloworld.c的代码修改如下:

 1 #include <stdio.h>
 2 #include "platform.h"
 3 #include "xadcps.h"
 4 
 5 #include "xil_types.h"
 6 #include "Xscugic.h"
 7 #include "Xil_exception.h"
 8 #include "xscutimer.h"
 9 
10 //timer info
11 #define TIMER_DEVICE_ID     XPAR_XSCUTIMER_0_DEVICE_ID
12 #define INTC_DEVICE_ID      XPAR_SCUGIC_SINGLE_DEVICE_ID
13 #define TIMER_IRPT_INTR     XPAR_SCUTIMER_INTR
14 
15 //#define TIMER_LOAD_VALUE  0x0FFFFFFF
16 #define TIMER_LOAD_VALUE    0x13D92D3F
17 
18 static XAdcPs  XADCMonInst; //XADC
19 static XScuGic Intc; //GIC
20 static XScuTimer Timer;//timer
21 
22 static void SetupInterruptSystem(XScuGic *GicInstancePtr,
23         XScuTimer *TimerInstancePtr, u16 TimerIntrId);
24 
25 static void TimerIntrHandler(void *CallBackRef);
26 
27 int main()
28 {
29      XScuTimer_Config *TMRConfigPtr;     //timer config
30      printf("------------START-------------\n");
31      init_platform();
32      //
33      //私有定时器初始化
34      TMRConfigPtr = XScuTimer_LookupConfig(TIMER_DEVICE_ID);
35      XScuTimer_CfgInitialize(&Timer, TMRConfigPtr,TMRConfigPtr->BaseAddr);
36      XScuTimer_SelfTest(&Timer);
37      //加载计数周期,私有定时器的时钟为CPU的一般,为333MHZ,如果计数1S,加载值为1sx(333x1000x1000)(1/s)-1=0x13D92D3F
38      XScuTimer_LoadTimer(&Timer, TIMER_LOAD_VALUE);
39      //自动装载
40      XScuTimer_EnableAutoReload(&Timer);
41      //启动定时器
42      XScuTimer_Start(&Timer);
43 
44 
45      //set up the interrupts
46      SetupInterruptSystem(&Intc,&Timer,TIMER_IRPT_INTR);
47 
48      while(1){
49      }
50 
51      return 0;
52 }
53 
54 void SetupInterruptSystem(XScuGic *GicInstancePtr,
55         XScuTimer *TimerInstancePtr, u16 TimerIntrId)
56 {
57 
58         XScuGic_Config *IntcConfig; //GIC config
59         Xil_ExceptionInit();
60         //initialise the GIC
61         IntcConfig = XScuGic_LookupConfig(INTC_DEVICE_ID);
62         XScuGic_CfgInitialize(GicInstancePtr, IntcConfig,
63                         IntcConfig->CpuBaseAddress);
64         //connect to the hardware
65         Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT,
66                     (Xil_ExceptionHandler)XScuGic_InterruptHandler,
67                     GicInstancePtr);
68         //set up the timer interrupt
69         XScuGic_Connect(GicInstancePtr, TimerIntrId,
70                         (Xil_ExceptionHandler)TimerIntrHandler,
71                         (void *)TimerInstancePtr);
72         //enable the interrupt for the Timer at GIC
73         XScuGic_Enable(GicInstancePtr, TimerIntrId);
74         //enable interrupt on the timer
75         XScuTimer_EnableInterrupt(TimerInstancePtr);
76         // Enable interrupts in the Processor.
77         Xil_ExceptionEnableMask(XIL_EXCEPTION_IRQ);
78     }
79 
80 static void TimerIntrHandler(void *CallBackRef)
81 {
82 
83     static int sec = 0;   //计数
84     XScuTimer *TimerInstancePtr = (XScuTimer *) CallBackRef;
85     XScuTimer_ClearInterruptStatus(TimerInstancePtr);
86     sec++;
87     printf(" %d Second\n\r",sec);  //每秒打印输出一次
88 
89 }

上电测试

可以看到串口终端每秒输出一次,并且值加1递增

转载于:https://www.cnblogs.com/geekite/p/5572582.html

<think>好的,我现在需要帮助用户解决关于Zynq 7020 AMP架构下CPU1的私有定时器中断裸机开发的问题。首先,我需要回忆一下Zynq 7020的双核结构。Zynq-7000系列有两个Cortex-A9核,AMP模式下两个核可以独立运行不同的操作系统或裸机程序。用户的问题集中在CPU1的私有定时器设置,我需要确保自己了解私有定时器的相关寄存器配置。 首先,私有定时器Private Timer)是每个CPU核心独有的,地址应该从0xF8F00600开始。需要配置加载值、控制寄存器,并启用中断中断号方面,私有定时器中断属于PPI(私有外设中断),CPU0的PPI中断号是29,而CPU1的应该是对应的。不过需要确认一下,可能用户在使用CPU1时,中断号是否相同或者不同,这里可能需要查文档。 接下来,中断控制器的配置。Zynq使用的是GIC(通用中断控制器),在裸机开发中需要配置GIC的Distributor和CPU Interface。对于CPU1来说,可能需要确保中断分配到对应的CPU,并且优先级和使能位设置正确。同时,中断服务函数的注册需要将中断处理函数地址写入向量表,或者通过设置VBR(向量基址寄存器)来指向自定义的向量表。 然后,代码结构方面,用户可能需要初始化定时器,配置中断控制器,编写中断服务函数,并启用全局中断。需要注意的是,在AMP模式下,两个核之间的资源访问需要协调,比如共享内存或者使用核间通信机制,但用户的问题主要聚焦在CPU1的私有定时器,可能暂时不需要处理核间通信部分,但可能需要确保内存分配正确,避免冲突。 此外,启动流程方面,在AMP环境中,CPU1通常由CPU0启动,通过写入CPU1的启动地址并释放复位。因此,用户可能需要将CPU1的代码编译成不同的可执行文件,并确保其加载到正确的内存地址。同时,在代码中需要设置好CPU1的堆栈和异常向量表。 可能还需要考虑缓存和MMU的配置,确保定时器相关寄存器的访问是有效的。在裸机开发中,通常需要禁用MMU,但缓存可能需要根据情况进行配置,比如是否启用数据缓存来加速访问,但需要确保寄存器映射的内存区域配置正确,避免缓存导致的问题。 最后,测试和调试部分,用户可能需要使用JTAG调试器分别连接到两个核,或者在代码中加入日志输出到共享内存,通过串口或其他方式查看CPU1的中断触发情况。同时,需要确保中断处理函数正确清除中断标志,避免重复进入中断。 可能遇到的问题包括中断未触发,原因可能是中断号配置错误、GIC未正确使能、定时器控制寄存器的配置有误,或者中断处理函数没有正确清除中断标志。需要逐步排查这些方面,并参考Xilinx的官方文档和例程,确保寄存器的配置值正确。</think>### Zynq 7020 AMP 裸机开发中 CPU1 私有定时器中断配置步骤 #### 1. **硬件基础** - Zynq 7020 包含双 Cortex-A9 核(CPU0 和 CPU1),私有定时器Private Timer)是每个核的独立外设,地址为 $0xF8F00600$(CPU0)和 $0xF8F01600$(CPU1)[^1]。 - 私有定时器中断属于 PPI(私有外设中断),CPU1 的私有定时器中断号为 **30**。 #### 2. **关键寄存器配置** ##### a. **定时器初始化** ```c // 设置定时器加载值(32 位) *(volatile uint32_t*)(0xF8F01600 + 0x00) = LOAD_VALUE; // Timer Load Register // 控制寄存器配置 *(volatile uint32_t*)(0xF8F01600 + 0x08) = 0x07; // Timer Control Register: // 0x01: 使能定时器 // 0x02: 自动重载模式 // 0x04: 中断使能 ``` ##### b. **中断控制器(GIC)配置** ```c // GIC Distributor 配置(使能中断分配) *(volatile uint32_t*)(0xF8F01000 + 0x100) |= (1 << 30); // GICD_ISENABLERn(使能 PPI 30) // GIC CPU Interface 配置(CPU1 侧) *(volatile uint32_t*)(0xF8F00100 + 0x0C) = 0x1F0; // ICCPMR(优先级掩码) *(volatile uint32_t*)(0xF8F00100 + 0x00) = 0x01; // ICCICR(使能 CPU Interface) ``` #### 3. **中断服务函数(ISR)** ```c void __attribute__((interrupt)) timer_isr(void) { // 清除定时器中断标志 *(volatile uint32_t*)(0xF8F01600 + 0x0C) = 0x01; // Timer Interrupt Status Register // 用户自定义操作(如翻转 GPIO) } ``` #### 4. **向量表与中断绑定** - 在启动代码中修改向量表,将 IRQ 入口指向通用中断处理函数: ```assembly .section .vectors ldr pc, [pc, #24] // IRQ 跳转指令 .word irq_handler // IRQ 处理函数地址 ``` - 在 C 代码中绑定具体中断: ```c void irq_handler(void) { uint32_t int_id = *(volatile uint32_t*)(0xF8F00100 + 0x0C); // ICCIAR if (int_id == 30) timer_isr(); *(volatile uint32_t*)(0xF8F00100 + 0x10) = int_id; // ICCEOIR } ``` #### 5. **启动流程(AMP 模式)** - CPU0 需通过写 **SLCR.ACP_CTRL** 寄存器释放 CPU1 复位,并设置其启动地址: ```c *(volatile uint32_t*)(0xF8000000 + 0x244) = CPU1_ENTRY_ADDR; // 设置 CPU1 启动地址 *(volatile uint32_t*)(0xF8000000 + 0x8) |= 0x1; // 释放 CPU1 复位 ``` #### 6. **注意事项** - **内存隔离**:在 AMP 模式下需通过 MPU 或 MMU 配置隔离 CPU0 和 CPU1 的内存访问。 - **共享资源同步**:若操作共享外设(如 GPIO),需使用原子操作或软件锁。 - **调试方法**:可通过 UART 打印日志或 LED 指示灯验证中断触发。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值