0%

ZYNQ入门(三)-定时器中断

ZYNQ入门(三)-定时器中断

zynq的中断体系结构框图

806931-20160925150600830-482897278

中断控制器(GIC)

在zynq中的中断控制比较复杂,主要是因为中断接收端有两个cpu,涉及到cpu的中断协同问题,中断的发起端除了常见的之外还有PL端哔哩吧啦一大推.
官方文档chapter_8别慌,问题不大

All interrupt sources are identified by a unique interrupt ID number.All interrupt sources have their own configurable priority and list of targeted CPUs –chapter7_interrupt

 

SGI: Software Generated Interrupts 软件中断 16个
SPI: Shared Peripheral Interrupts 共享(PS/PL)外设中断 60个
PPI: Private Peripheral Interrupts 私有中断 每个cpu各5个,计数时钟为主频一半
FIQ: Fast inte
rrupt request 快速中断响应
IRQ: interrupt request 中断响应

实现一个简单的一秒PPI

正确认识下,一个PPI是纯PS端的,也就是说他只是我们说的简单的单片机的中断而已.

  1. 新建一个纯PS工程,并导入SDK,挑选helloworld模板和生成bps(见前面的博客)

  2. 将heloworld.c

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    #include <stdio.h>

    #include "xadcps.h"
    #include "xil_types.h"
    #include "Xscugic.h"
    #include "Xil_exception.h"
    #include "xscutimer.h"

    #define TIMER_DEVICE_ID XPAR_XSCUTIMER_0_DEVICE_ID
    #define INTC_DEVICE_ID XPAR_SCUGIC_SINGLE_DEVICE_ID
    #define TIMER_IRPT_INTR XPAR_SCUTIMER_INTR

    //加载计数周期,私有定时器癿时钟为CPU癿一般,为333MHZ,如果计数1S,加载值为1sx(333x1000x1000)(1/s)-1=0x13D92D3F
    #define TIMER_LOAD_VALUE 0x13D92D3F //一秒

    static XScuGic Intc;

    static XScuTimer Timer;

    static void SetupInterruptSystem(XScuGic *Gicptr , XScuTimer *timerptr ,u16 TimerIntrId); //中断注册函数

    static void TimerIntrHandler(void *CallBackRef);

    //#include "platform.h"
    //void print(char *str);

    int main()
    {
    XScuTimer_Config *TMRConfigPtr;

    printf("start/n");
    //PPI setup
    TMRConfigPtr = XScuTimer_LookupConfig(TIMER_DEVICE_ID);

    XScuTimer_CfgInitialize(&Timer, TMRConfigPtr,TMRConfigPtr->BaseAddr);

    XScuTimer_SelfTest(&Timer);

    //加载计数周期,私有定时器的时钟为CPU的一半,为333MHZ,
    //如果计数1S,加载值为1sx(333x1000x1000)(1/s)-1=0x13D92D3F
    XScuTimer_LoadTimer(&Timer, TIMER_LOAD_VALUE);

    //自劢装载
    XScuTimer_EnableAutoReload(&Timer);

    //启劢定时器
    XScuTimer_Start(&Timer);


    //set up the GIC
    SetupInterruptSystem(&Intc,&Timer,TIMER_IRPT_INTR);
    while(1){};

    return 0;

    }

    void SetupInterruptSystem(XScuGic *Gicptr , XScuTimer *timerptr ,u16 TimerIntrId)
    {
    XScuGic_Config *IntcConfig; //GIC config
    Xil_ExceptionInit();
    IntcConfig = XScuGic_LookupConfig(INTC_DEVICE_ID);
    XScuGic_CfgInitialize(Gicptr, IntcConfig, IntcConfig->CpuBaseAddress);
    //connect to the hardware
    Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT, (Xil_ExceptionHandler)XScuGic_InterruptHandler, Gicptr);
    //set up the timer interrupt
    XScuGic_Connect(Gicptr, TimerIntrId,
    (Xil_ExceptionHandler)TimerIntrHandler,
    (void *)timerptr);
    //enable the interrupt for the Timer at GIC
    XScuGic_Enable(Gicptr, TimerIntrId);
    //enable interrupt on the timer
    XScuTimer_EnableInterrupt(timerptr);
    // Enable interrupts in the Processor.
    Xil_ExceptionEnableMask(XIL_EXCEPTION_IRQ);
    }

    static void TimerIntrHandler(void *CallBackRef)
    {
    static int sec=0;
    XScuTimer *timerptr = (XScuTimer *)CallBackRef;
    XScuTimer_ClearInterruptStatus(timerptr);

    sec++;
    printf(" %d Second\n\r",sec); //每秒打印输出一次

    }
  3. 下载到板子上面

##做一点简单的解释

###为什么gic和timer前面都会带有一个SCU
见手册第三章3.3

SCU: snoop control unit (窥探控制单元?)

再看回上一篇博客的block manager那张图,我们可以看到GIC和SCU是连在一起的!
suc

可以看到,SCU主要是解决ARM的L1和L2的缓存协调(因为两个processor的缓存是共用的)
和AXI总线的ACP存取的,也就是DMA等高速中断需求的外设
###流程

  1. 设置定时器
    通过ID号查配置->配置写入->初始化->加入预加载值->开启自动重载->开启定时
  2. 设置GIC
    传入定时器配置->通过ID号查配置->配置写入->注册处理函数->定时器中断信号和中断函数连接->使能定时器中断
    ->使能中断->使能处理器处理中断

差不多就是一个流程对应一个函数这样子

  1. ID号可以在xparameters.h 里面找到
  2. 函数(API)可以在bps里面的system.mss里面的每一个模块的docs里面找到

##PS和PL共享中断
也就是图上所说的SPI(Shared Peripheral Interrupts)
###同前面的PS-PL协同开发的项目新建
2. 加入一个GPIO1,全部输入,使能中断
block1
3. 再加入一个gpio0作led灯用
system_block
4. 同协同项目流程一致,封装,生成bit~
5. 导入sdk,新建bsp
###hello源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
#include "xparameters.h"

#include "xscugic.h"

#include "xil_exception.h"

#include "xgpio.h"

// Parameter definitions

#define INTC_DEVICE_ID XPAR_PS7_SCUGIC_0_DEVICE_ID

#define LED_DEVICE_ID XPAR_GPIO_1_DEVICE_ID

#define BTNS_DEVICE_ID XPAR_GPIO_0_DEVICE_ID

#define INTC_GPIO_INTERRUPT_ID XPAR_FABRIC_KEY_4B_IP2INTC_IRPT_INTR

#define BTN_INT XGPIO_IR_CH1_MASK // This is the interrupt mask for channel one


XGpio LED;

XGpio BTNInst;

XScuGic INTCInst;

static u8 btn_value;

//----------------------------------------------------

// PROTOTYPE FUNCTIONS

//----------------------------------------------------

static void BTN_Intr_Handler(void *baseaddr_p);

static int InterruptSystemSetup(XScuGic *XScuGicInstancePtr);

static int IntcInitFunction(u16 DeviceId, XGpio *GpioInstancePtr);

//----------------------------------------------------

// INTERRUPT SERVICE ROUTINE(ISR)

//also know as : INTERRUPT HANDLER FUNCTION

// - called by the buttons interrupt, performs push buttons read

//----------------------------------------------------

void BTN_Intr_Handler(void *InstancePtr)

{

unsigned char led_val = 0;

// Ignore additional button presses

if ((XGpio_InterruptGetStatus(&BTNInst) & BTN_INT) !=

BTN_INT) {

return;

// Disable GPIO interrupts

XGpio_InterruptDisable(&BTNInst, BTN_INT);

}

btn_value = ~XGpio_DiscreteRead(&BTNInst, 1)&0x0f;

switch (btn_value){

case 0x01: led_val = 0x01; break;

case 0x02: led_val = 0x02; break;

case 0x04: led_val = 0x04; break;

case 0x08: led_val = 0x08; break;


default:break; }

XGpio_DiscreteWrite(&LED,1,~led_val);

// Acknowledge GPIO interrupts

(void)XGpio_InterruptClear(&BTNInst, BTN_INT);

// Enable GPIO interrupts

XGpio_InterruptEnable(&BTNInst, BTN_INT);

}

//----------------------------------------------------

// MAIN FUNCTION

//----------------------------------------------------

int main (void)

{

int status;

// 初始化按键

status = XGpio_Initialize(&BTNInst, BTNS_DEVICE_ID);

if(status != XST_SUCCESS) return XST_FAILURE;

//初始化LED

status = XGpio_Initialize(&LED, LED_DEVICE_ID);

if(status != XST_SUCCESS) return XST_FAILURE;

// 设置按键IO的方向为输入

XGpio_SetDataDirection(&BTNInst, 1, 0xF);

//设置LED IO的方向为输出

XGpio_SetDataDirection(&LED, 1, 0x00);

//设置LED 灯熄灭

XGpio_DiscreteWrite(&LED,1,0x0f);

// 初始化按键的中断控制器

status = IntcInitFunction(INTC_DEVICE_ID, &BTNInst);

if(status != XST_SUCCESS) return XST_FAILURE;

while(1){

}

return (0);

}

//----------------------------------------------------

// INTERRUPT SETUP FUNCTIONS

//----------------------------------------------------

int IntcInitFunction(u16 DeviceId, XGpio *GpioInstancePtr)

{

XScuGic_Config *IntcConfig;

int status;

// Interrupt controller initialization

IntcConfig = XScuGic_LookupConfig(DeviceId);

status = XScuGic_CfgInitialize(&INTCInst, IntcConfig, IntcConfig->CpuBaseAddress);

if(status != XST_SUCCESS) return XST_FAILURE;

// Call interrupt setup function

status = InterruptSystemSetup(&INTCInst);

if(status != XST_SUCCESS) return XST_FAILURE;

// Register GPIO interrupt handler

status = XScuGic_Connect(&INTCInst,

INTC_GPIO_INTERRUPT_ID,

(Xil_ExceptionHandler)BTN_Intr_Handler,

(void *)GpioInstancePtr);

if(status != XST_SUCCESS) return XST_FAILURE;

// Enable GPIO interrupts

XGpio_InterruptEnable(GpioInstancePtr, 1);

XGpio_InterruptGlobalEnable(GpioInstancePtr);

// Enable GPIO interrupts in the controller

XScuGic_Enable(&INTCInst, INTC_GPIO_INTERRUPT_ID);

return XST_SUCCESS;

}

int InterruptSystemSetup(XScuGic *XScuGicInstancePtr)

{

// Register GIC interrupt handler

Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT,
(Xil_ExceptionHandler)XScuGic_InterruptHandler,
XScuGicInstancePtr);

Xil_ExceptionEnable();

return XST_SUCCESS;

}

程序也算是很简单了,看到他的初始化流程是先初始化一个指针去查找ID对应的配置,然后用一个函数写入就完事了..

如果你想请我吃个南五的话