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); } |