| //***************************************************************************** | |
| // | |
| // interrupt.c - Driver for the NVIC Interrupt Controller. | |
| // | |
| // Copyright (c) 2005,2006 Luminary Micro, Inc. All rights reserved. | |
| // | |
| // Software License Agreement | |
| // | |
| // Luminary Micro, Inc. (LMI) is supplying this software for use solely and | |
| // exclusively on LMI's Stellaris Family of microcontroller products. | |
| // | |
| // The software is owned by LMI and/or its suppliers, and is protected under | |
| // applicable copyright laws. All rights are reserved. Any use in violation | |
| // of the foregoing restrictions may subject the user to criminal sanctions | |
| // under applicable laws, as well as to civil liability for the breach of the | |
| // terms and conditions of this license. | |
| // | |
| // THIS SOFTWARE IS PROVIDED "AS IS". NO WARRANTIES, WHETHER EXPRESS, IMPLIED | |
| // OR STATUTORY, INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF | |
| // MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. | |
| // LMI SHALL NOT, IN ANY CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR | |
| // CONSEQUENTIAL DAMAGES, FOR ANY REASON WHATSOEVER. | |
| // | |
| // This is part of revision 991 of the Stellaris Driver Library. | |
| // | |
| //***************************************************************************** | |
| //***************************************************************************** | |
| // | |
| //! \addtogroup interrupt_api | |
| //! @{ | |
| // | |
| //***************************************************************************** | |
| #include "../hw_ints.h" | |
| #include "../hw_nvic.h" | |
| #include "../hw_types.h" | |
| #include "cpu.h" | |
| #include "debug.h" | |
| #include "interrupt.h" | |
| //***************************************************************************** | |
| // | |
| // This is a mapping between priority grouping encodings and the number of | |
| // preemption priority bits. | |
| // | |
| //***************************************************************************** | |
| #if defined(GROUP_pulpriority) || defined(BUILD_ALL) | |
| const unsigned long g_pulPriority[] = | |
| { | |
| NVIC_APINT_PRIGROUP_0_8, NVIC_APINT_PRIGROUP_1_7, NVIC_APINT_PRIGROUP_2_6, | |
| NVIC_APINT_PRIGROUP_3_5, NVIC_APINT_PRIGROUP_4_4, NVIC_APINT_PRIGROUP_5_3, | |
| NVIC_APINT_PRIGROUP_6_2, NVIC_APINT_PRIGROUP_7_1 | |
| }; | |
| #else | |
| extern const unsigned long g_pulPriority[]; | |
| #endif | |
| //***************************************************************************** | |
| // | |
| // This is a mapping between interrupt number and the register that contains | |
| // the priority encoding for that interrupt. | |
| // | |
| //***************************************************************************** | |
| #if defined(GROUP_pulregs) || defined(BUILD_ALL) | |
| const unsigned long g_pulRegs[12] = | |
| { | |
| 0, NVIC_SYS_PRI1, NVIC_SYS_PRI2, NVIC_SYS_PRI3, NVIC_PRI0, NVIC_PRI1, | |
| NVIC_PRI2, NVIC_PRI3, NVIC_PRI4, NVIC_PRI5, NVIC_PRI6, NVIC_PRI7 | |
| }; | |
| #else | |
| extern const unsigned long g_pulRegs[12]; | |
| #endif | |
| //***************************************************************************** | |
| // | |
| //! \internal | |
| //! The default interrupt handler. | |
| //! | |
| //! This is the default interrupt handler for all interrupts. It simply loops | |
| //! forever so that the system state is preserved for observation by a | |
| //! debugger. Since interrupts should be disabled before unregistering the | |
| //! corresponding handler, this should never be called. | |
| //! | |
| //! \return None. | |
| // | |
| //***************************************************************************** | |
| #if defined(GROUP_defaulthandler) || defined(BUILD_ALL) | |
| void | |
| IntDefaultHandler(void) | |
| { | |
| // | |
| // Go into an infinite loop. | |
| // | |
| while(1) | |
| { | |
| } | |
| } | |
| #else | |
| extern void IntDefaultHandler(void); | |
| #endif | |
| //***************************************************************************** | |
| // | |
| // The processor vector table. | |
| // | |
| // This contains a list of the handlers for the various interrupt sources in | |
| // the system. The layout of this list is defined by the hardware; assertion | |
| // of an interrupt causes the processor to start executing directly at the | |
| // address given in the corresponding location in this list. | |
| // | |
| //***************************************************************************** | |
| #if defined(GROUP_vtable) || defined(BUILD_ALL) | |
| #ifdef ewarm | |
| __no_init void (*g_pfnRAMVectors[NUM_INTERRUPTS])(void) @ "VTABLE"; | |
| #else | |
| __attribute__((section("vtable"))) | |
| void (*g_pfnRAMVectors[NUM_INTERRUPTS])(void); | |
| #endif | |
| #else | |
| extern void (*g_pfnRAMVectors[NUM_INTERRUPTS])(void); | |
| #endif | |
| //***************************************************************************** | |
| // | |
| //! Enables the processor interrupt. | |
| //! | |
| //! Allows the processor to respond to interrupts. This does not affect the | |
| //! set of interrupts enabled in the interrupt controller; it just gates the | |
| //! single interrupt from the controller to the processor. | |
| //! | |
| //! \return None. | |
| // | |
| //***************************************************************************** | |
| #if defined(GROUP_masterenable) || defined(BUILD_ALL) || defined(DOXYGEN) | |
| void | |
| IntMasterEnable(void) | |
| { | |
| // | |
| // Enable processor interrupts. | |
| // | |
| CPUcpsie(); | |
| } | |
| #endif | |
| //***************************************************************************** | |
| // | |
| //! Disables the processor interrupt. | |
| //! | |
| //! Prevents the processor from receiving interrupts. This does not affect the | |
| //! set of interrupts enabled in the interrupt controller; it just gates the | |
| //! single interrupt from the controller to the processor. | |
| //! | |
| //! \return None. | |
| // | |
| //***************************************************************************** | |
| #if defined(GROUP_masterdisable) || defined(BUILD_ALL) || defined(DOXYGEN) | |
| void | |
| IntMasterDisable(void) | |
| { | |
| // | |
| // Disable processor interrupts. | |
| // | |
| CPUcpsid(); | |
| } | |
| #endif | |
| //***************************************************************************** | |
| // | |
| //! Registers a function to be called when an interrupt occurs. | |
| //! | |
| //! \param ulInterrupt specifies the interrupt in question. | |
| //! \param pfnHandler is a pointer to the function to be called. | |
| //! | |
| //! This function is used to specify the handler function to be called when the | |
| //! given interrupt is asserted to the processor. When the interrupt occurs, | |
| //! if it is enabled (via IntEnable()), the handler function will be called in | |
| //! interrupt context. Since the handler function can preempt other code, care | |
| //! must be taken to protect memory or peripherals that are accessed by the | |
| //! handler and other non-handler code. | |
| //! | |
| //! \note The use of this function (directly or indirectly via a peripheral | |
| //! driver interrupt register function) moves the interrupt vector table from | |
| //! flash to SRAM. Therefore, care must be taken when linking the application | |
| //! to ensure that the SRAM vector table is located at the beginning of SRAM; | |
| //! otherwise NVIC will not look in the correct portion of memory for the | |
| //! vector table (it requires the vector table be on a 1 kB memory alignment). | |
| //! Normally, the SRAM vector table is so placed via the use of linker scripts; | |
| //! some tool chains, such as the evaluation version of RV-MDK, do not support | |
| //! linker scripts and therefore will not produce a valid executable. See the | |
| //! discussion of compile-time versus run-time interrupt handler registration | |
| //! in the introduction to this chapter. | |
| //! | |
| //! \return None. | |
| // | |
| //***************************************************************************** | |
| #if defined(GROUP_register) || defined(BUILD_ALL) || defined(DOXYGEN) | |
| void | |
| IntRegister(unsigned long ulInterrupt, void (*pfnHandler)(void)) | |
| { | |
| unsigned long ulIdx; | |
| // | |
| // Check the arguments. | |
| // | |
| ASSERT(ulInterrupt < NUM_INTERRUPTS); | |
| // | |
| // Make sure that the RAM vector table is correctly aligned. | |
| // | |
| ASSERT(((unsigned long)g_pfnRAMVectors & 0x000003ff) == 0); | |
| // | |
| // See if the RAM vector table has been initialized. | |
| // | |
| if(HWREG(NVIC_VTABLE) != (unsigned long)g_pfnRAMVectors) | |
| { | |
| // | |
| // Copy the vector table from the beginning of FLASH to the RAM vector | |
| // table. | |
| // | |
| for(ulIdx = 0; ulIdx < NUM_INTERRUPTS; ulIdx++) | |
| { | |
| g_pfnRAMVectors[ulIdx] = (void (*)(void))HWREG(ulIdx * 4); | |
| } | |
| // | |
| // Point NVIC at the RAM vector table. | |
| // | |
| HWREG(NVIC_VTABLE) = (unsigned long)g_pfnRAMVectors; | |
| } | |
| // | |
| // Save the interrupt handler. | |
| // | |
| g_pfnRAMVectors[ulInterrupt] = pfnHandler; | |
| } | |
| #endif | |
| //***************************************************************************** | |
| // | |
| //! Unregisters the function to be called when an interrupt occurs. | |
| //! | |
| //! \param ulInterrupt specifies the interrupt in question. | |
| //! | |
| //! This function is used to indicate that no handler should be called when the | |
| //! given interrupt is asserted to the processor. The interrupt source will be | |
| //! automatically disabled (via IntDisable()) if necessary. | |
| //! | |
| //! \sa IntRegister() for important information about registering interrupt | |
| //! handlers. | |
| //! | |
| //! \return None. | |
| // | |
| //***************************************************************************** | |
| #if defined(GROUP_unregister) || defined(BUILD_ALL) || defined(DOXYGEN) | |
| void | |
| IntUnregister(unsigned long ulInterrupt) | |
| { | |
| // | |
| // Check the arguments. | |
| // | |
| ASSERT(ulInterrupt < NUM_INTERRUPTS); | |
| // | |
| // Reset the interrupt handler. | |
| // | |
| g_pfnRAMVectors[ulInterrupt] = IntDefaultHandler; | |
| } | |
| #endif | |
| //***************************************************************************** | |
| // | |
| //! Sets the priority grouping of the interrupt controller. | |
| //! | |
| //! \param ulBits specifies the number of bits of preemptable priority. | |
| //! | |
| //! This function specifies the split between preemptable priority levels and | |
| //! subpriority levels in the interrupt priority specification. The range of | |
| //! the grouping values are dependent upon the hardware implementation; on | |
| //! the Stellaris family it can range from 0 to 3. | |
| //! | |
| //! \return None. | |
| // | |
| //***************************************************************************** | |
| #if defined(GROUP_prioritygroupingset) || defined(BUILD_ALL) || \ | |
| defined(DOXYGEN) | |
| void | |
| IntPriorityGroupingSet(unsigned long ulBits) | |
| { | |
| // | |
| // Check the arguments. | |
| // | |
| ASSERT(ulBits < NUM_PRIORITY_BITS); | |
| // | |
| // Set the priority grouping. | |
| // | |
| HWREG(NVIC_APINT) = NVIC_APINT_VECTKEY | g_pulPriority[ulBits]; | |
| } | |
| #endif | |
| //***************************************************************************** | |
| // | |
| //! Gets the priority grouping of the interrupt controller. | |
| //! | |
| //! This function returns the split between preemptable priority levels and | |
| //! subpriority levels in the interrupt priority specification. | |
| //! | |
| //! \return The number of bits of preemptable priority. | |
| // | |
| //***************************************************************************** | |
| #if defined(GROUP_prioritygroupingget) || defined(BUILD_ALL) || \ | |
| defined(DOXYGEN) | |
| unsigned long | |
| IntPriorityGroupingGet(void) | |
| { | |
| unsigned long ulLoop, ulValue; | |
| // | |
| // Read the priority grouping. | |
| // | |
| ulValue = HWREG(NVIC_APINT) & NVIC_APINT_PRIGROUP_M; | |
| // | |
| // Loop through the priority grouping values. | |
| // | |
| for(ulLoop = 0; ulLoop < 8; ulLoop++) | |
| { | |
| // | |
| // Stop looping if this value matches. | |
| // | |
| if(ulValue == g_pulPriority[ulLoop]) | |
| { | |
| break; | |
| } | |
| } | |
| // | |
| // Return the number of priority bits. | |
| // | |
| return(ulLoop); | |
| } | |
| #endif | |
| //***************************************************************************** | |
| // | |
| //! Sets the priority of an interrupt. | |
| //! | |
| //! \param ulInterrupt specifies the interrupt in question. | |
| //! \param ucPriority specifies the priority of the interrupt. | |
| //! | |
| //! This function is used to set the priority of an interrupt. When multiple | |
| //! interrupts are asserted simultaneously, the ones with the highest priority | |
| //! are processed before the lower priority interrupts. Smaller numbers | |
| //! correspond to higher interrupt priorities; priority 0 is the highest | |
| //! interrupt priority. | |
| //! | |
| //! The hardware priority mechanism will only look at the upper N bits of the | |
| //! priority level (where N is 3 for the Stellaris family), so any | |
| //! prioritization must be performed in those bits. The remaining bits can be | |
| //! used to sub-prioritize the interrupt sources, and may be used by the | |
| //! hardware priority mechanism on a future part. This arrangement allows | |
| //! priorities to migrate to different NVIC implementations without changing | |
| //! the gross prioritization of the interrupts. | |
| //! | |
| //! \return None. | |
| // | |
| //***************************************************************************** | |
| #if defined(GROUP_priorityset) || defined(BUILD_ALL) || defined(DOXYGEN) | |
| void | |
| IntPrioritySet(unsigned long ulInterrupt, unsigned char ucPriority) | |
| { | |
| unsigned long ulTemp; | |
| // | |
| // Check the arguments. | |
| // | |
| ASSERT((ulInterrupt >= 4) && (ulInterrupt < NUM_INTERRUPTS)); | |
| // | |
| // Set the interrupt priority. | |
| // | |
| ulTemp = HWREG(g_pulRegs[ulInterrupt >> 2]); | |
| ulTemp &= ~(0xFF << (8 * (ulInterrupt & 3))); | |
| ulTemp |= ucPriority << (8 * (ulInterrupt & 3)); | |
| HWREG(g_pulRegs[ulInterrupt >> 2]) = ulTemp; | |
| } | |
| #endif | |
| //***************************************************************************** | |
| // | |
| //! Gets the priority of an interrupt. | |
| //! | |
| //! \param ulInterrupt specifies the interrupt in question. | |
| //! | |
| //! This function gets the priority of an interrupt. See IntPrioritySet() for | |
| //! a definition of the priority value. | |
| //! | |
| //! \return Returns the interrupt priority, or -1 if an invalid interrupt was | |
| //! specified. | |
| // | |
| //***************************************************************************** | |
| #if defined(GROUP_priorityget) || defined(BUILD_ALL) || defined(DOXYGEN) | |
| long | |
| IntPriorityGet(unsigned long ulInterrupt) | |
| { | |
| // | |
| // Check the arguments. | |
| // | |
| ASSERT((ulInterrupt >= 4) && (ulInterrupt < NUM_INTERRUPTS)); | |
| // | |
| // Return the interrupt priority. | |
| // | |
| return((HWREG(g_pulRegs[ulInterrupt >> 2]) >> (8 * (ulInterrupt & 3))) & | |
| 0xFF); | |
| } | |
| #endif | |
| //***************************************************************************** | |
| // | |
| //! Enables an interrupt. | |
| //! | |
| //! \param ulInterrupt specifies the interrupt to be enabled. | |
| //! | |
| //! The specified interrupt is enabled in the interrupt controller. Other | |
| //! enables for the interrupt (such as at the peripheral level) are unaffected | |
| //! by this function. | |
| //! | |
| //! \return None. | |
| // | |
| //***************************************************************************** | |
| #if defined(GROUP_enable) || defined(BUILD_ALL) || defined(DOXYGEN) | |
| void | |
| IntEnable(unsigned long ulInterrupt) | |
| { | |
| // | |
| // Check the arguments. | |
| // | |
| ASSERT(ulInterrupt < NUM_INTERRUPTS); | |
| // | |
| // Determine the interrupt to enable. | |
| // | |
| if(ulInterrupt == FAULT_MPU) | |
| { | |
| // | |
| // Enable the MemManage interrupt. | |
| // | |
| HWREG(NVIC_SYS_HND_CTRL) |= NVIC_SYS_HND_CTRL_MEM; | |
| } | |
| else if(ulInterrupt == FAULT_BUS) | |
| { | |
| // | |
| // Enable the bus fault interrupt. | |
| // | |
| HWREG(NVIC_SYS_HND_CTRL) |= NVIC_SYS_HND_CTRL_BUS; | |
| } | |
| else if(ulInterrupt == FAULT_USAGE) | |
| { | |
| // | |
| // Enable the usage fault interrupt. | |
| // | |
| HWREG(NVIC_SYS_HND_CTRL) |= NVIC_SYS_HND_CTRL_USAGE; | |
| } | |
| else if(ulInterrupt == FAULT_SYSTICK) | |
| { | |
| // | |
| // Enable the System Tick interrupt. | |
| // | |
| HWREG(NVIC_ST_CTRL) |= NVIC_ST_CTRL_INTEN; | |
| } | |
| else if(ulInterrupt >= INT_GPIOA) | |
| { | |
| // | |
| // Enable the general interrupt. | |
| // | |
| HWREG(NVIC_EN0) = 1 << (ulInterrupt - INT_GPIOA); | |
| } | |
| } | |
| #endif | |
| //***************************************************************************** | |
| // | |
| //! Disables an interrupt. | |
| //! | |
| //! \param ulInterrupt specifies the interrupt to be disabled. | |
| //! | |
| //! The specified interrupt is disabled in the interrupt controller. Other | |
| //! enables for the interrupt (such as at the peripheral level) are unaffected | |
| //! by this function. | |
| //! | |
| //! \return None. | |
| // | |
| //***************************************************************************** | |
| #if defined(GROUP_disable) || defined(BUILD_ALL) || defined(DOXYGEN) | |
| void | |
| IntDisable(unsigned long ulInterrupt) | |
| { | |
| // | |
| // Check the arguments. | |
| // | |
| ASSERT(ulInterrupt < NUM_INTERRUPTS); | |
| // | |
| // Determine the interrupt to disable. | |
| // | |
| if(ulInterrupt == FAULT_MPU) | |
| { | |
| // | |
| // Disable the MemManage interrupt. | |
| // | |
| HWREG(NVIC_SYS_HND_CTRL) &= ~(NVIC_SYS_HND_CTRL_MEM); | |
| } | |
| else if(ulInterrupt == FAULT_BUS) | |
| { | |
| // | |
| // Disable the bus fault interrupt. | |
| // | |
| HWREG(NVIC_SYS_HND_CTRL) &= ~(NVIC_SYS_HND_CTRL_BUS); | |
| } | |
| else if(ulInterrupt == FAULT_USAGE) | |
| { | |
| // | |
| // Disable the usage fault interrupt. | |
| // | |
| HWREG(NVIC_SYS_HND_CTRL) &= ~(NVIC_SYS_HND_CTRL_USAGE); | |
| } | |
| else if(ulInterrupt == FAULT_SYSTICK) | |
| { | |
| // | |
| // Disable the System Tick interrupt. | |
| // | |
| HWREG(NVIC_ST_CTRL) &= ~(NVIC_ST_CTRL_INTEN); | |
| } | |
| else if(ulInterrupt >= INT_GPIOA) | |
| { | |
| // | |
| // Disable the general interrupt. | |
| // | |
| HWREG(NVIC_DIS0) = 1 << (ulInterrupt - INT_GPIOA); | |
| } | |
| } | |
| #endif | |
| //***************************************************************************** | |
| // | |
| // Close the Doxygen group. | |
| //! @} | |
| // | |
| //***************************************************************************** |