Blog # 1 - Project Description And Upgrade Installation
Blog # 2 - MiniZed Board Peripheral Configuration and Creating a Boot Image
Blog # 3 - Executing Vivado Design Suite TCL Commands
Blog # 4 - Move Data Between BRAM and DDR3 Memories
Blog # 5 - How to Enable Interrupts With MiniZed
**********************************************************************************************************************
In this tutorial I am going to show you how to enable interrupts with MiniZed board. Here I will use as a reference Lesson 9 of the software training course. An interrupt can be seen as a warning that can be activated by some specific process of the microcontroller such as the end of ADC conversion, reception of data from the USART module, timer overflow, etc; or even due to an external change to it, such as a change in a specific port, a change in a pin, etc. In this example the interruption activates by incorrect data input with the PC keyboard through the serial port connected to the MiniZed board. Lesson 9: Interrupts
As the training course indicates, after having created the workspace I import the compressed Vitis project: File - Import. Then select Vitis project exported zip file.
Make sure all projects are selected and click finish.
Once the project is open, the explorer and the wizard look as follows:
We create a new application: File - New - Application Project, and name it LED_Dimmer_w_Int.
We select the domain standalone on ps7_cortexa9_0
Select an empty application (C) and click finish.
Now we are going to import the source code (main.c) from the Support_Documents folder of the training course. So, right-click on the src folder, and click on the Import sources option in the pop up menu. Selecd main.c file and click finish.
Buld the project
Below I show you the code of the main.c file
/***************************** Include Files *********************************/ #include "xparameters.h" #include "xil_io.h" #include "xstatus.h" /************************** Constant Definitions *****************************/ /* * The following constant maps to the name of the hardware instances that * were created in the EDK XPS system. */ #define PWM_BASE_ADDRESS 0x43C00000 /************************** Variable Definitions *****************************/ /* * The following are declared globally so they are zeroed and so they are * easily accessible from a debugger */ /************************** Main Code Entry **********************************/ int main(void) { int status = XST_SUCCESS; u32 value = 0; u32 period = 0; u32 brightness = 0; /* Initialize the LED Dimmer controller to a safe PWM value. */ Xil_Out32(PWM_BASE_ADDRESS, 0); /* Now that the hardware has been initialized, continuously loop while * prompting the user for updates to the brightness level. */ while (1) { /* Prompt the user to select a brightness value ranging from * 0 to 9. */ print("Select a Brightness between 0 and 9\n\r"); /* Read an input value from the console. */ value = inbyte(); /* Convert the input ASCII character to an integer value. */ period = value - 0x30; /* Print the input value back to the console to provide some * feedback to the user. */ xil_printf("Brightness Level %d selected\n\r", period); /* Since the LED width is 1e6 clk cycles, we need to normalize * the period to that clk. Since we accept values 0-9, that will * scale period from 0-999,000. 0 turns off LEDs, 999,000 is full * brightness. */ brightness = period * 110000; /* Write the duty_cycle width (Period) out to the PL PWM * peripheral. */ Xil_Out32(PWM_BASE_ADDRESS, brightness); } return status; }
After previous steps, set the MiniZed to JTAG as follows.
Right-click on the LED_Dimmer_w_Int project folder. Then, click on the Run As - Launch on Hardware (Single Application Debug) option in the pop up menu. I have used putty to monitor the serial port. Here I show you a screenshot of running the code.
We assign a new brightness value to the PWM block every time we insert a values between 0 to 9 (ASCII value from 0x30 to 0x39 hex). Please take a look at LED D8 on your MiniZed while doing so. However, this application responds wrongly when we enter characters from a to z. In my case the last three values that I entered were the letters a, w and h.
Adding the Interrupt
We can see that the PWM hardware block passes incorrect values. This problem could be solved in software by first checking the values before writing the value to the PWM hardware block.
In this solution, we will enable the GIC to pass the PWM hardware block interrupt to the processor and interrupt execution flow. We will also add an Interrupt Service Routine (ISR) which will handle the interrupt from the PWM hardware block and assign a known safe value to the PWM hardware block before continuing application execution. Below you can see the full code, and then I'll comment you about the changes made to the code.
/***************************** Include Files *********************************/ #include "xparameters.h" #include "xil_io.h" #include "xstatus.h" #include "xscugic.h" #include "xil_exception.h" /************************** Constant Definitions *****************************/ /* * The following constant maps to the name of the hardware instances that * were created in the EDK XPS system. */ #define PWM_BASE_ADDRESS 0x43C00000 /************************** Variable Definitions *****************************/ /* The following definitions are related to handling interrupts from the * PWM controller. */ #define XPAR_PS7_SCUGIC_0_DEVICE_ID 0 #define INTC_PWM_INTERRUPT_ID XPAR_FABRIC_PWM_W_INT_0_INTERRUPT_OUT_INTR #define INTC XScuGic #define INTC_HANDLER XScuGic_InterruptHandler #define INTC_DEVICE_ID XPAR_PS7_SCUGIC_0_DEVICE_ID /* * The following are declared globally so they are zeroed and so they are * easily accessible from a debugger */ /* LED brightness level is now global to make is visible to the ISR. */ volatile u32 brightness; /* The Instance of the Interrupt Controller Driver */ static INTC Intc; void PWMIsr(void *InstancePtr) { /* Inform the user that an invalid value was detected by the PWM * controller. */ print("PWM Value exceeded, brightness reset to zero. Enter new value: \r\n"); /* Set the brightness value to a safe value and write it to the * PWM controller in order to clear the pending interrupt. */ brightness = 0; Xil_Out32(PWM_BASE_ADDRESS, brightness); } /****************************************************************************/ /** * This function sets up the interrupt system for the PWM dimmer controller. * The processing contained in this function assumes the hardware system was * built with an interrupt controller. * * @param None. * * @return A status indicating XST_SUCCESS or a value that is contained in * xstatus.h. * * @note None. * *****************************************************************************/ int SetupInterruptSystem() { int result; INTC *IntcInstancePtr = &Intc; XScuGic_Config *IntcConfig; /* Initialize the interrupt controller driver so that it is ready to * use. */ IntcConfig = XScuGic_LookupConfig(INTC_DEVICE_ID); if (IntcConfig == NULL) { return XST_FAILURE; } /* Initialize the SCU and GIC to enable the desired interrupt * configuration. */ result = XScuGic_CfgInitialize(IntcInstancePtr, IntcConfig, IntcConfig->CpuBaseAddress); if (result != XST_SUCCESS) { return XST_FAILURE; } XScuGic_SetPriorityTriggerType(IntcInstancePtr, INTC_PWM_INTERRUPT_ID, 0xA0, 0x3); /* Connect the interrupt handler that will be called when an * interrupt occurs for the device. */ result = XScuGic_Connect(IntcInstancePtr, INTC_PWM_INTERRUPT_ID, (Xil_ExceptionHandler) PWMIsr, 0); if (result != XST_SUCCESS) { return result; } /* Enable the interrupt for the PWM controller device. */ XScuGic_Enable(IntcInstancePtr, INTC_PWM_INTERRUPT_ID); /* Initialize the exception table and register the interrupt controller * handler with the exception table. */ Xil_ExceptionInit(); Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT, (Xil_ExceptionHandler)INTC_HANDLER, IntcInstancePtr); /* Enable non-critical exceptions */ Xil_ExceptionEnable(); return XST_SUCCESS; } /************************** Main Code Entry **********************************/ int main(void) { int status = XST_SUCCESS; u32 value = 0; u32 period = 0; //u32 brightness = 0; brightness = 0; /* Initialize the LED Dimmer controller to a safe PWM value. */ Xil_Out32(PWM_BASE_ADDRESS, 0); /* Now that the hardware has been initialized, continuously loop while * prompting the user for updates to the brightness level. */ while (1) { /* Prompt the user to select a brightness value ranging from * 0 to 9. */ print("Select a Brightness between 0 and 9\n\r"); /* Read an input value from the console. */ value = inbyte(); /* Convert the input ASCII character to an integer value. */ period = value - 0x30; /* Print the input value back to the console to provide some * feedback to the user. */ xil_printf("Brightness Level %d selected\n\r", period); /* Since the LED width is 1e6 clk cycles, we need to normalize * the period to that clk. Since we accept values 0-9, that will * scale period from 0-999,000. 0 turns off LEDs, 999,000 is full * brightness. */ brightness = period * 110000; /* Write the duty_cycle width (Period) out to the PL PWM * peripheral. */ Xil_Out32(PWM_BASE_ADDRESS, brightness); /* Setup the interrupts such that interrupt processing can occur. If an * error occurs while setting up interrupts, then exit the application. */ status = SetupInterruptSystem(); if (status != XST_SUCCESS) { return XST_FAILURE; } } return status; }
Comments:
- Below you can see static definitions to map the interrupt setup routine to the SCUGIC driver calls.
#define XPAR_PS7_SCUGIC_0_DEVICE_ID 0 #define INTC_PWM_INTERRUPT_ID XPAR_FABRIC_PWM_W_INT_0_INTERRUPT_OUT_INTR #define INTC XScuGic #define INTC_HANDLER XScuGic_InterruptHandler #define INTC_DEVICE_ID XPAR_PS7_SCUGIC_0_DEVICE_ID
- The Intc variable is a static definition used to setup the SCUGIC driver.
static INTC Intc;
- Add this ISR into the source code will serve as the callback function that is called when the hardware interrupt is serviced.
void PWMIsr(void *InstancePtr) { /* Inform the user that an invalid value was detected by the PWM * controller. */ print("PWM Value exceeded, brightness reset to zero. Enter new value: \r\n"); /* Set the brightness value to a safe value and write it to the * PWM controller in order to clear the pending interrupt. */ brightness = 0; Xil_Out32(PWM_BASE_ADDRESS, brightness); }
- This setup function provides the setup needed to enable the PWM interrupt to be recognized and attaches the PWMIsr() as the interrupt handler.
int SetupInterruptSystem() { int result; INTC *IntcInstancePtr = &Intc; XScuGic_Config *IntcConfig; /* Initialize the interrupt controller driver so that it is ready to * use. */ IntcConfig = XScuGic_LookupConfig(INTC_DEVICE_ID); if (IntcConfig == NULL) { return XST_FAILURE; } /* Initialize the SCU and GIC to enable the desired interrupt * configuration. */ result = XScuGic_CfgInitialize(IntcInstancePtr, IntcConfig, IntcConfig->CpuBaseAddress); if (result != XST_SUCCESS) { return XST_FAILURE; } XScuGic_SetPriorityTriggerType(IntcInstancePtr, INTC_PWM_INTERRUPT_ID, 0xA0, 0x3); /* Connect the interrupt handler that will be called when an * interrupt occurs for the device. */ result = XScuGic_Connect(IntcInstancePtr, INTC_PWM_INTERRUPT_ID, (Xil_ExceptionHandler) PWMIsr, 0); if (result != XST_SUCCESS) { return result; } /* Enable the interrupt for the PWM controller device. */ XScuGic_Enable(IntcInstancePtr, INTC_PWM_INTERRUPT_ID); /* Initialize the exception table and register the interrupt controller * handler with the exception table. */ Xil_ExceptionInit(); Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT, (Xil_ExceptionHandler)INTC_HANDLER, IntcInstancePtr); /* Enable non-critical exceptions */ Xil_ExceptionEnable(); return XST_SUCCESS; }
Below I show you a screenshot of the test performed.
Here we can see that the interrupt works well. It tells us the value of the inserted character, it tells us that the PWM value has been exceeded, the brightness value resets it to zero and asks us to insert a new value.
Thank you for reading these five blogs that I have published!