Skip to content

lab3



Experimental goal &requirement

  • Learn to use interrupts.
  • Learn to use dual channel GPIO.

    Interrupt program design:
  • Capture interrupts from PL BTN0 and BTN1, so that when button 0 (1) is pressed, "Interrupt valid, Button 0 (1) is pressed" is output through the UART serial port.
  • After an interrupt is triggered, the second interrupt is not responded within 1 s.
  • Interrupt must be called.

Dual channel GPIO design: * Hang four switch buttons (SW0~3) and two button buttons (BTN2, BTN3) on the AXI bus using the same GPIO interface. * On the basis of lab5, the calculation of the counting period is changed to ONE_TENTH* (4bit SW + 2bit BTN).


Provide materials

Provide materials: Lab3 experimental material: Lab1-Lab5.pdf.

Experimental process and optimization


Figure 1 Generate a hardware system
 

Figure 2 Add watchdog


Figure 3 zynq settings


Figure 4 The frequency of the watchdog is about 108MHz, so to make the watchdog generate an interrupt about one second after the start, you need to set the watchdog to countdown to 108000000 times.



Figure 5 gpio settings (interrupt button)



Figure 6 gpio dual channel settings (2-digit buttons and 4-digit dial switches).

Experimental analysis

Finally, the interrupt is successfully called to capture the interrupts from PL BTN0 and BTN1, so that when the button 0 (1) is pressed, the "Interrupt valid, Button 0 (1) is pressed" is output through the UART serial port, and after an interrupt trigger is implemented, within 1 s. Does not respond to the second interrupt.

The dual-channel GPIO design successfully changed the count period calculation to ONE_TENTH* (4bit SW + 2bit BTN), and used the same GPIO interface for the four switch buttons (SW0~3) and the two button buttons (BTN2, BTN3).

In summary, the experiment was successful.

Experimental summary and thoughts

In this experiment, the main function first initializes each button, LED and interrupt, and sets the countdown of the watchdog, and then uses the while(1) loop to read the dial switch and the button value in the dual channel gpio and correspondingly Switch, etc.

Each time the key value changes, the interrupt processing function BTN_handler is entered. In the handler, the valid interrupt is pressed according to the key value, and the key interrupt is turned off, and the watchdog is restarted. The watchdog will then generate an interrupt after one second, re-enabling the key interrupt in that interrupt, and a new interrupt can be generated after one second.

Experiment results

Finally, add the watchdog code. Now find the system.mss document, open the example to find an example of using the watchdog interrupt, follow the modification, and add the code to re-enable the key interrupt in the handler.

code

  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
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
#include "xparameters.h"
#include "xscugic.h"
#include "xil_exception.h"
#include "xgpio.h"
#include "xscutimer.h"
#include "xscuwdt.h"
#include "led_ip.h"

// Parameter definitions
#define WDT_DEVICE_ID       XPAR_SCUWDT_0_DEVICE_ID
#define INTC_DEVICE_ID      XPAR_PS7_SCUGIC_0_DEVICE_ID
#define LED_DEVICE_ID       XPAR_LED_IP_DEVICE_ID
#define BTNS_DEVICE_ID      XPAR_BUTTONS_DEVICE_ID
#define INT_DEVICE_ID       XPAR_GPIO_0_DEVICE_ID
#define INTC_GPIO_INTERRUPT_ID XPAR_FABRIC_AXI_GPIO_0_IP2INTC_IRPT_INTR
#define BTN_INT             XGPIO_IR_CH1_MASK // This is the interrupt mask for channel one
#define DELAY 100000000
#define ONE_TENTH 37500000 // half of the CPU clock speed/10
#define WDT_IRPT_INTR       XPAR_SCUWDT_INTR

#define HANDLER_CALLED      0xFFFFFFFF
#define WDT_LOAD_VALUE      108000000
XGpio   LED;
XGpio   BTNInst;
XGpio   INTBInst;
XScuGic INTCInst;
XScuTimer Timer;
XScuTimer *TimerInstancePtr;
XGpio intb;
XScuWdt WdtInstance;        /* Cortex SCU Private WatchDog Timer Instance */
XScuGic IntcInstance;       /* Interrupt Controller Instance */

volatile u32 HandlerCalled; /* flag is set when timeout interrupt occurs */
//----------------------------------------------------
// PROTOTYPE FUNCTIONS
//----------------------------------------------------
static void BTN_Intr_Handler(void *baseaddr_p);
static int InterruptSystemSetup(XScuGic *XScuGicInstancePtr);
static int IntcInitFunction(u16 DeviceId, XGpio *GpioInstancePtr);
int ScuWdtIntrExample(XScuGic *IntcInstancePtr, XScuWdt * WdtInstancePtr,
           u16 WdtDeviceId, u16 WdtIntrId);

static void WdtIntrHandler(void *CallBackRef);

static int WdtSetupIntrSystem(XScuGic *IntcInstancePtr,
                  XScuWdt * WdtInstancePtr, u16 WdtIntrId);

static void WdtDisableIntrSystem(XScuGic *IntcInstancePtr, u16 WdtIntrId);
//----------------------------------------------------
//  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)
{

    int intb_check = XGpio_DiscreteRead(&intb, 1);
    if(intb_check==1)
        xil_printf("Interrupt valid, Button 0 is pressed\r\n");
    else if(intb_check==2) xil_printf("Interrupt valid, Button 1 is pressed\r\n");
    // Acknowledge GPIO interrupts
    (void)XGpio_InterruptClear(&INTBInst, BTN_INT);
    // Enable GPIO interrupts
    XGpio_InterruptDisable(&INTBInst, BTN_INT);
    //XScuTimer_LoadTimer(TimerInstancePtr, ONE_TENTH*10);
    //while(!XScuTimer_IsExpired(TimerInstancePtr)){
    //}
    XScuWdt_RestartWdt(&WdtInstance);

}
//----------------------------------------------------
// MAIN FUNCTION
//----------------------------------------------------
int main (void)
{

  XGpio dip, push;
     int psb_check, psb_check_prev, dip_check, dip_check_prev, count, status;

     // ≥ı ºªØSWITCHES,BUTTONS,∫ÕINTERRUPT
     status = XGpio_Initialize(&BTNInst, BTNS_DEVICE_ID);
     if(status != XST_SUCCESS) return XST_FAILURE;
     status = XGpio_Initialize(&INTBInst, INT_DEVICE_ID);
     if(status != XST_SUCCESS) return XST_FAILURE;
     //≥ı ºªØLED
     status = XGpio_Initialize(&LED, LED_DEVICE_ID);
      if(status != XST_SUCCESS) return XST_FAILURE;
     // ≥ı ºªØ÷–∂œøÿ÷∆∆˜
     status = IntcInitFunction(INTC_DEVICE_ID, &INTBInst);
     if(status != XST_SUCCESS) return XST_FAILURE;
     //≥ı ºªØwatchdog
    XScuWdt_Config *ConfigPtr2;

    /*
     * Initialize the SCU Private Wdt driver so that it is ready to use.
     */
    ConfigPtr2 = XScuWdt_LookupConfig(WDT_DEVICE_ID);

    /*
     * This is where the virtual address would be used, this example
     * uses physical address.
     */
    status = XScuWdt_CfgInitialize(&WdtInstance, ConfigPtr2,
                    ConfigPtr2->BaseAddr);
    if (status != XST_SUCCESS) {
        return XST_FAILURE;
    }
    XScuWdt_SetTimerMode(&WdtInstance);

        /*
         * Load the watchdog counter register.
         */
        XScuWdt_LoadWdt(&WdtInstance, WDT_LOAD_VALUE);
    status = WdtSetupIntrSystem(&IntcInstance, &WdtInstance, WDT_IRPT_INTR);
        if (status != XST_SUCCESS) {
            return XST_FAILURE;
        }
        XScuWdt_RestartWdt(&WdtInstance);
    /*
     * Start the ScuWdt device.
     */
    XScuWdt_Start(&WdtInstance);

     // PS Timer related definitions
     XScuTimer_Config *ConfigPtr;
     XScuTimer *TimerInstancePtr = &Timer;


     xil_printf("-- Start of the Program --\r\n");

     XGpio_Initialize(&push, XPAR_BUTTONS_DEVICE_ID);
     XGpio_SetDataDirection(&push, 1, 0xffffffff);
     XGpio_Initialize(&push, XPAR_BUTTONS_DEVICE_ID);
     XGpio_SetDataDirection(&push, 2, 0xffffffff);

     XGpio_Initialize(&intb, XPAR_GPIO_0_DEVICE_ID);
     XGpio_SetDataDirection(&intb, 1, 0xffffffff);

     count = 0;

     // Initialize the timer
     ConfigPtr = XScuTimer_LookupConfig (XPAR_PS7_SCUTIMER_0_DEVICE_ID);
     status = XScuTimer_CfgInitialize   (TimerInstancePtr, ConfigPtr, ConfigPtr->BaseAddr);
     if(status != XST_SUCCESS){
      xil_printf("Timer init() failed\r\n");
      return XST_FAILURE;
     }

     // Read dip switch values
     dip_check_prev = XGpio_DiscreteRead(&push, 2);
     psb_check_prev = XGpio_DiscreteRead(&push, 1);
     // Load timer with delay in multiple of ONE_TENTH
     XScuTimer_LoadTimer(TimerInstancePtr, ONE_TENTH*(dip_check_prev*4+psb_check_prev));
     // Set AutoLoad mode
     XScuTimer_EnableAutoReload(TimerInstancePtr);
     // Start the timer
     XScuTimer_Start (TimerInstancePtr);
     while (1)
         while (1)
            {
              // Read push buttons and break the loop if Center button pressed
              psb_check = XGpio_DiscreteRead(&push, 1);
              dip_check = XGpio_DiscreteRead(&push, 2);
              if ((dip_check != dip_check_prev)||(psb_check!=psb_check_prev)) {
                  //xil_printf("DIP Switch Status %x, %x\r\n", psb_check_prev, psb_check);
                  dip_check_prev = dip_check;
                  psb_check_prev=psb_check;
                  // load timer with the new switch settings
                  XScuTimer_LoadTimer(TimerInstancePtr, ONE_TENTH*(dip_check*4+psb_check));
                  count = 0;
              }
              if(XScuTimer_IsExpired(TimerInstancePtr)) {
                      // clear status bit
                      XScuTimer_ClearInterruptStatus(TimerInstancePtr);
                      // output the count to LED and increment the count
                      LED_IP_mWriteReg(XPAR_LED_IP_S_AXI_BASEADDR, 0, count);
                      count++;
              }
            }
     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;

}
/*****************************************************************************/
/**
*
* This function sets up the interrupt system such that interrupts can occur
* for the device.
*
* @param    IntcInstancePtr is a pointer to the instance of the XScuGic
*       driver.
* @param    WdtInstancePtr is a pointer to the instance of XScuWdt driver.
* @param    WdtIntrId is the Interrupt Id of the XScuWdt device.
*
* @return   XST_SUCCESS if successful, otherwise XST_FAILURE.
*
* @note     None.
*
******************************************************************************/
static int WdtSetupIntrSystem(XScuGic *IntcInstancePtr,
                  XScuWdt *WdtInstancePtr, u16 WdtIntrId)
{
    int Status;
    u32 Reg;

#ifndef TESTAPP_GEN
    XScuGic_Config *IntcConfig;
    /*
     * Initialize the interrupt controller driver so that it is ready to
     * use.
     */
    IntcConfig = XScuGic_LookupConfig(INTC_DEVICE_ID);
    if (NULL == IntcConfig) {
        return XST_FAILURE;
    }

    Status = XScuGic_CfgInitialize(IntcInstancePtr, IntcConfig,
                    IntcConfig->CpuBaseAddress);
    if (Status != XST_SUCCESS) {
        return XST_FAILURE;
    }

    Xil_ExceptionInit();

    /*
     * Connect the interrupt controller interrupt handler to the hardware
     * interrupt handling logic in the processor.
     */
    Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT,
                    (Xil_ExceptionHandler)
                    XScuGic_InterruptHandler,
                    IntcInstancePtr);
#endif
    /*
     * Connect the device driver handler that will be called when an
     * interrupt for the device occurs, the handler defined above performs
     * the specific interrupt processing for the device.
     */
    Status = XScuGic_Connect(IntcInstancePtr, WdtIntrId,
                (Xil_ExceptionHandler)WdtIntrHandler,
                (void *)WdtInstancePtr);
    if (Status != XST_SUCCESS) {
        return Status;
    }

    /*
     * Enable the watchdog interrupts for timer mode.
     */
    Reg = XScuWdt_GetControlReg(WdtInstancePtr);
    XScuWdt_SetControlReg(WdtInstancePtr,
                Reg | XSCUWDT_CONTROL_IT_ENABLE_MASK);

    /*
     * Enable the interrupt for the device.
     */
    XScuGic_Enable(IntcInstancePtr, WdtIntrId);

#ifndef TESTAPP_GEN
    /*
     * Enable interrupts in the Processor.
     */
    Xil_ExceptionEnable();
#endif
    return XST_SUCCESS;
}

/*****************************************************************************/
/**
*
* This function is the Interrupt handler for the WDT Interrupt of the
* Wdt device. It is called on the expiration of the timer counter in
* interrupt context.
*
* @param    CallBackRef is a pointer to the callback function.
*
* @return   None.
*
* @note     None.
*
******************************************************************************/
static void WdtIntrHandler(void *CallBackRef)
{
    /*
     * WDT timed out and interrupt occurred, let main test loop know.
     */
    XGpio_InterruptEnable(&INTBInst, BTN_INT);
}

/*****************************************************************************/
/**
*
* This function disables the interrupts that occur for the device.
*
* @param    IntcInstancePtr is the pointer to the instance of XScuGic
*       driver.
* @param    WdtIntrId is the Interrupt Id for the device.
*
* @return   None.
*
* @note     None.
*
******************************************************************************/
static void WdtDisableIntrSystem(XScuGic *IntcInstancePtr, u16 WdtIntrId)
{
    /*
     * Disconnect and disable the interrupt for the Wdt.
     */
    XScuGic_Disconnect(IntcInstancePtr, WdtIntrId);
}