//***************************************************************************** // // interrupt.c // // Driver for the NVIC Interrupt Controller. // // Copyright (C) 2014 Texas Instruments Incorporated - http://www.ti.com/ // // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions // are met: // // Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // // Redistributions in binary form must reproduce the above copyright // notice, this list of conditions and the following disclaimer in the // documentation and/or other materials provided with the // distribution. // // Neither the name of Texas Instruments Incorporated nor the names of // its contributors may be used to endorse or promote products derived // from this software without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // //***************************************************************************** //***************************************************************************** // //! \addtogroup interrupt_api //! @{ // //***************************************************************************** #include "inc/hw_ints.h" #include "inc/hw_nvic.h" #include "inc/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. // //***************************************************************************** static 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 }; //***************************************************************************** // // This is a mapping between interrupt number and the register that contains // the priority encoding for that interrupt. // //***************************************************************************** static const unsigned long g_pulRegs[] = { 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, NVIC_PRI8, NVIC_PRI9, NVIC_PRI10, NVIC_PRI11, NVIC_PRI12, NVIC_PRI13, NVIC_PRI14, NVIC_PRI15, NVIC_PRI16, NVIC_PRI17, NVIC_PRI18, NVIC_PRI19, NVIC_PRI20, NVIC_PRI21, NVIC_PRI22, NVIC_PRI23, NVIC_PRI24, NVIC_PRI25, NVIC_PRI26, NVIC_PRI27, NVIC_PRI28, NVIC_PRI29, NVIC_PRI30, NVIC_PRI31, NVIC_PRI32, NVIC_PRI33, NVIC_PRI34, NVIC_PRI35, NVIC_PRI36, NVIC_PRI37, NVIC_PRI38, NVIC_PRI39, NVIC_PRI40, NVIC_PRI41, NVIC_PRI42, NVIC_PRI43, NVIC_PRI44, NVIC_PRI45, NVIC_PRI46, NVIC_PRI47, NVIC_PRI48 }; //***************************************************************************** // // This is a mapping between interrupt number (for the peripheral interrupts // only) and the register that contains the interrupt enable for that // interrupt. // //***************************************************************************** static const unsigned long g_pulEnRegs[] = { NVIC_EN0, NVIC_EN1, NVIC_EN2, NVIC_EN3, NVIC_EN4, NVIC_EN5 }; //***************************************************************************** // // This is a mapping between interrupt number (for the peripheral interrupts // only) and the register that contains the interrupt disable for that // interrupt. // //***************************************************************************** static const unsigned long g_pulDisRegs[] = { NVIC_DIS0, NVIC_DIS1, NVIC_DIS2, NVIC_DIS3, NVIC_DIS4, NVIC_DIS5 }; //***************************************************************************** // // This is a mapping between interrupt number (for the peripheral interrupts // only) and the register that contains the interrupt pend for that interrupt. // //***************************************************************************** static const unsigned long g_pulPendRegs[] = { NVIC_PEND0, NVIC_PEND1, NVIC_PEND2, NVIC_PEND3, NVIC_PEND4, NVIC_PEND5 }; //***************************************************************************** // // This is a mapping between interrupt number (for the peripheral interrupts // only) and the register that contains the interrupt unpend for that // interrupt. // //***************************************************************************** static const unsigned long g_pulUnpendRegs[] = { NVIC_UNPEND0, NVIC_UNPEND1, NVIC_UNPEND2, NVIC_UNPEND3, NVIC_UNPEND4, NVIC_UNPEND5 }; //***************************************************************************** // //! \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. // //***************************************************************************** static void IntDefaultHandler(void) { // // Go into an infinite loop. // while(1) { } } //***************************************************************************** // //! 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. //! //! \note Previously, this function had no return value. As such, it was //! possible to include interrupt.h and call this function without //! having included hw_types.h. Now that the return is a //! tBoolean, a compiler error will occur in this case. The solution //! is to include hw_types.h before including interrupt.h. //! //! \return Returns \b true if interrupts were disabled when the function was //! called or \b false if they were initially enabled. // //***************************************************************************** tBoolean IntMasterEnable(void) { // // Enable processor interrupts. // return(CPUcpsie()); } //***************************************************************************** // //! 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. //! //! \note Previously, this function had no return value. As such, it was //! possible to include interrupt.h and call this function without //! having included hw_types.h. Now that the return is a //! tBoolean, a compiler error will occur in this case. The solution //! is to include hw_types.h before including interrupt.h. //! //! \return Returns \b true if interrupts were already disabled when the //! function was called or \b false if they were initially enabled. // //***************************************************************************** tBoolean IntMasterDisable(void) { // // Disable processor interrupts. // return(CPUcpsid()); } //***************************************************************************** // //! Sets the NVIC VTable base. //! //! \param ulVtableBase specifies the new base address of VTable //! //! This function is used to specify a new base address for the VTable. //! This function must be called before using IntRegister() for registering //! any interrupt handler. //! //! //! \return None. // //***************************************************************************** void IntVTableBaseSet(unsigned long ulVtableBase) { HWREG(NVIC_VTABLE) = ulVtableBase; } //***************************************************************************** // //! 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. //! //! //! \return None. // //***************************************************************************** void IntRegister(unsigned long ulInterrupt, void (*pfnHandler)(void)) { unsigned long *ulNvicTbl; // // Check the arguments. // ASSERT(ulInterrupt < NUM_INTERRUPTS); ulNvicTbl = (unsigned long *)HWREG(NVIC_VTABLE); ulNvicTbl[ulInterrupt]= (unsigned long)pfnHandler; } //***************************************************************************** // //! 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. // //***************************************************************************** void IntUnregister(unsigned long ulInterrupt) { unsigned long *ulNvicTbl; // // Check the arguments. // ASSERT(ulInterrupt < NUM_INTERRUPTS); ulNvicTbl = (unsigned long *)HWREG(NVIC_VTABLE); ulNvicTbl[ulInterrupt]= (unsigned long)IntDefaultHandler; } //***************************************************************************** // //! 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 CC3200 , three bits are available for hardware interrupt //! prioritization and therefore priority grouping values of three through //! seven have the same effect. //! //! \return None. // //***************************************************************************** void IntPriorityGroupingSet(unsigned long ulBits) { // // Check the arguments. // ASSERT(ulBits < NUM_PRIORITY); // // Set the priority grouping. // HWREG(NVIC_APINT) = NVIC_APINT_VECTKEY | g_pulPriority[ulBits]; } //***************************************************************************** // //! 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. // //***************************************************************************** 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 < NUM_PRIORITY; ulLoop++) { // // Stop looping if this value matches. // if(ulValue == g_pulPriority[ulLoop]) { break; } } // // Return the number of priority bits. // return(ulLoop); } //***************************************************************************** // //! 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), 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. //! //! The parameter \e ucPriority can be any one of the following //! -\b INT_PRIORITY_LVL_0 //! -\b INT_PRIORITY_LVL_1 //! -\b INT_PRIORITY_LVL_2 //! -\b INT_PRIORITY_LVL_3 //! -\b INT_PRIORITY_LVL_4 //! -\b INT_PRIORITY_LVL_5 //! -\b INT_PRIORITY_LVL_6 //! -\b INT_PRIORITY_LVL_7 //! //! \return None. // //***************************************************************************** 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; } //***************************************************************************** // //! 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. // //***************************************************************************** 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); } //***************************************************************************** // //! 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. // //***************************************************************************** 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; __asm(" dsb "); __asm(" isb "); } else if(ulInterrupt == FAULT_BUS) { // // Enable the bus fault interrupt. // HWREG(NVIC_SYS_HND_CTRL) |= NVIC_SYS_HND_CTRL_BUS; __asm(" dsb "); __asm(" isb "); } else if(ulInterrupt == FAULT_USAGE) { // // Enable the usage fault interrupt. // HWREG(NVIC_SYS_HND_CTRL) |= NVIC_SYS_HND_CTRL_USAGE; __asm(" dsb "); __asm(" isb "); } else if(ulInterrupt == FAULT_SYSTICK) { // // Enable the System Tick interrupt. // HWREG(NVIC_ST_CTRL) |= NVIC_ST_CTRL_INTEN; __asm(" dsb "); __asm(" isb "); } else if(ulInterrupt >= 16) { // // Enable the general interrupt. // HWREG(g_pulEnRegs[(ulInterrupt - 16) / 32]) = 1 << ((ulInterrupt - 16) & 31); __asm(" dsb "); __asm(" isb "); } } //***************************************************************************** // //! 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. // //***************************************************************************** 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); __asm(" dsb "); __asm(" isb "); } else if(ulInterrupt == FAULT_BUS) { // // Disable the bus fault interrupt. // HWREG(NVIC_SYS_HND_CTRL) &= ~(NVIC_SYS_HND_CTRL_BUS); __asm(" dsb "); __asm(" isb "); } else if(ulInterrupt == FAULT_USAGE) { // // Disable the usage fault interrupt. // HWREG(NVIC_SYS_HND_CTRL) &= ~(NVIC_SYS_HND_CTRL_USAGE); __asm(" dsb "); __asm(" isb "); } else if(ulInterrupt == FAULT_SYSTICK) { // // Disable the System Tick interrupt. // HWREG(NVIC_ST_CTRL) &= ~(NVIC_ST_CTRL_INTEN); __asm(" dsb "); __asm(" isb "); } else if(ulInterrupt >= 16) { // // Disable the general interrupt. // HWREG(g_pulDisRegs[(ulInterrupt - 16) / 32]) = 1 << ((ulInterrupt - 16) & 31); __asm(" dsb "); __asm(" isb "); } } //***************************************************************************** // //! Pends an interrupt. //! //! \param ulInterrupt specifies the interrupt to be pended. //! //! The specified interrupt is pended in the interrupt controller. This will //! cause the interrupt controller to execute the corresponding interrupt //! handler at the next available time, based on the current interrupt state //! priorities. For example, if called by a higher priority interrupt handler, //! the specified interrupt handler will not be called until after the current //! interrupt handler has completed execution. The interrupt must have been //! enabled for it to be called. //! //! \return None. // //***************************************************************************** void IntPendSet(unsigned long ulInterrupt) { // // Check the arguments. // ASSERT(ulInterrupt < NUM_INTERRUPTS); // // Determine the interrupt to pend. // if(ulInterrupt == FAULT_NMI) { // // Pend the NMI interrupt. // HWREG(NVIC_INT_CTRL) |= NVIC_INT_CTRL_NMI_SET; __asm(" dsb "); __asm(" isb "); } else if(ulInterrupt == FAULT_PENDSV) { // // Pend the PendSV interrupt. // HWREG(NVIC_INT_CTRL) |= NVIC_INT_CTRL_PEND_SV; __asm(" dsb "); __asm(" isb "); } else if(ulInterrupt == FAULT_SYSTICK) { // // Pend the SysTick interrupt. // HWREG(NVIC_INT_CTRL) |= NVIC_INT_CTRL_PENDSTSET; __asm(" dsb "); __asm(" isb "); } else if(ulInterrupt >= 16) { // // Pend the general interrupt. // HWREG(g_pulPendRegs[(ulInterrupt - 16) / 32]) = 1 << ((ulInterrupt - 16) & 31); __asm(" dsb "); __asm(" isb "); } } //***************************************************************************** // //! Unpends an interrupt. //! //! \param ulInterrupt specifies the interrupt to be unpended. //! //! The specified interrupt is unpended in the interrupt controller. This will //! cause any previously generated interrupts that have not been handled yet //! (due to higher priority interrupts or the interrupt no having been enabled //! yet) to be discarded. //! //! \return None. // //***************************************************************************** void IntPendClear(unsigned long ulInterrupt) { // // Check the arguments. // ASSERT(ulInterrupt < NUM_INTERRUPTS); // // Determine the interrupt to unpend. // if(ulInterrupt == FAULT_PENDSV) { // // Unpend the PendSV interrupt. // HWREG(NVIC_INT_CTRL) |= NVIC_INT_CTRL_UNPEND_SV; } else if(ulInterrupt == FAULT_SYSTICK) { // // Unpend the SysTick interrupt. // HWREG(NVIC_INT_CTRL) |= NVIC_INT_CTRL_PENDSTCLR; } else if(ulInterrupt >= 16) { // // Unpend the general interrupt. // HWREG(g_pulUnpendRegs[(ulInterrupt - 16) / 32]) = 1 << ((ulInterrupt - 16) & 31); } } //***************************************************************************** // //! Sets the priority masking level //! //! \param ulPriorityMask is the priority level that will be masked. //! //! This function sets the interrupt priority masking level so that all //! interrupts at the specified or lesser priority level is masked. This //! can be used to globally disable a set of interrupts with priority below //! a predetermined threshold. A value of 0 disables priority //! masking. //! //! Smaller numbers correspond to higher interrupt priorities. So for example //! a priority level mask of 4 will allow interrupts of priority level 0-3, //! and interrupts with a numerical priority of 4 and greater will be blocked. //! //! The hardware priority mechanism will only look at the upper N bits of the //! priority level (where N is 3), so any //! prioritization must be performed in those bits. //! //! \return None. // //***************************************************************************** void IntPriorityMaskSet(unsigned long ulPriorityMask) { CPUbasepriSet(ulPriorityMask); } //***************************************************************************** // //! Gets the priority masking level //! //! This function gets the current setting of the interrupt priority masking //! level. The value returned is the priority level such that all interrupts //! of that and lesser priority are masked. A value of 0 means that priority //! masking is disabled. //! //! Smaller numbers correspond to higher interrupt priorities. So for example //! a priority level mask of 4 will allow interrupts of priority level 0-3, //! and interrupts with a numerical priority of 4 and greater will be blocked. //! //! The hardware priority mechanism will only look at the upper N bits of the //! priority level (where N is 3), so any //! prioritization must be performed in those bits. //! //! \return Returns the value of the interrupt priority level mask. // //***************************************************************************** unsigned long IntPriorityMaskGet(void) { return(CPUbasepriGet()); } //***************************************************************************** // // Close the Doxygen group. //! @} // //*****************************************************************************