// ***********************************************************************
// *** ARM Cortex M0 CMSIS Interrupt Handlers FAQ ***
// ***********************************************************************
*** How are interrupt handlers implemented in CMSIS of Cortex M0? ***
0xakhil asked May 26 '11 at 11:23
http://electronics.stackexchange.com/questions/14705/how-are-interrupt-handlers-implemented-in-cmsis-of-cortex-m0
I have a LPC1114 kit. Last few days I have been digging up CMSIS implementation of Cortex M0 to find how things are done in it. So far I understood how each registers are mapped and how I can access it. But still I dont know how interrupts are implemented in it. All I know about interrupts in CMSIS is there are some interrupt handler names mentioned in the startup file. And I can write my own handlers by simply writing a C function with the same names mentioned in the startup file. What confuses me is that in the user guide, it is told that all GPIO can be used as external interrupt sources. But there are only 4 PIO interrupts mentioned in the startup file. So tell me:
How can I implement external interrupt handlers for other GPIOs?
Where is the interrupt table mapped in the CMSIS?
What are the major differences between NVIC and the interrupt implementation in AVRs/PICs? (except NVIC can be mapped anywhere in the flash) arm interrupts
*** Igor Skochinsky answered May 26 '11 at 12:08 ***
(Please note that points 1 and 2 are implementation details and not architectural limitations.)
In bigger NXP chips (such as LPC17xx) there are a couple of dedicated interrupt pins (EINTn) which have their own interrupt handler. The rest of GPIOs have to use one common interrupt (EINT3). You can then poll the interrupt status register to see which pins have triggered the interrupt.
I'm not very familiar with LPC11xx but it seems that it has one interrupt per GPIO port. You again will have to check the status register to figure out the specific pins. There are also up to 12 pins that can act as wakeup sources. I'm not sure if you can hijack them as general interrupts (i.e. they will probably only be triggered when in sleep state).
The default handler table is placed at the address 0 (which is in flash). The first entry is the reset value for the SP register, the second is the reset vector, and the rest are other exceptions and interrupt vectors. A couple of the first ones (such as NMI and HardFault) are fixed by ARM, the rest are chip-specific. If you need to change the vectors at runtime, you can remap it to RAM (you first need to copy the table). In LPC11xx the remapping is fixed to the start of SRAM (0x10000000), other chips can be more flexible.
The NVIC is optimized for efficient interrupt handling:
programmable priority level of 0-3 for each interrupt. A higher-priority interrupt preempts lower-priority ones (nesting). The execution of the lower priority one resumes when the higher-priority interrupt is finished.
automatic stacking of the processor state on interrupt entry; this allows writing interrupt handlers directly in C and removes the need for assembly wrappers.
tail-chaining: instead of popping and pushing the state again, the next pending interrupt is handled immediately
late-arriving: if a higher-priority interrupt arrives while stacking the processor state, it's executed immediately instead of the previously pending one.
Since you're familiar with PICs, have a look at this App Note: Migrating from PIC Microcontrollers to Cortex-M3
It's about M3, but most of the points apply to M0 too.
*** Austin Phillips answered May 27 '11 at 0:18 ***
The following information is in addition to Igor's excellent answer.
From a C programming perspective, the interrupt handlers are defined in the cr_startup_xxx.c file (eg cr_startup_lpc13.c file for LPC1343). All possible interrupt handlers are defined there as a WEAK alias. If you do not define your own XXX_Handler() for an interrupt source, then the default interrupt handler function defined in this file will be used. The linker will sort out which function to include in the final binary along with the interrupt vector table from cr_startup_xxx.c
Example of GPIO interrupts from ports are shown in the demo files in gpio.c. There is one interrupt input to the NVIC per GPIO port. Each individual bit in the port can be enabled/disabled to generate an interrupt on that port. If you require interrupts on ports PIO1_4, and PIO1_5 for example, then you would enable the individual PIO1_4 and PIO1_5 interrupt bits in GPIO0IE. When your PIOINT0_Handler() interrupt handler function fires, it's up to you to determine which of PIO1_4 or PIO1_5 (or both) interrupts are pending by reading the GPIO0RIS register and handling the interrupt appropriately.
// ***********************************************************************
// *** Cororado Uni Interrupt Example ***
// ***********************************************************************
Interrupt Lab - Cororado Edu ECEN3000/3360 - Digital Design Lab - ecee.colorado.edu
http://ecee.colorado.edu/ecen3000/labs/lab3/lab3.html
Lab 3: Arm Cortex M0 Initialization, Peripherals, and Interrupts
3 lab periods
Figure 1. The LPCXpresso LPC1114 board.
...
Pre-lab
For the pre-lab, read the LPC1114 User Manual and Cortex-M0 materials, understand the structure of CMSIS Library and how NVIC works, then write the following 'GPIOInit()' function according to the Code Requirements. You may use physical address or CMSIS defined Register/GPIO symbol referring to a specific register or GPIO.
Part 1 (Creating a New Project)
First, some background information. You will be asked by the project creation wizard if you would like to include two modules in your code, the CMSIS and CRP. For this lab, we will only be using the CMSIS library, as it provides many useful definitions to access the Nested Vector Interrupt Controller (NVIC). Download the CMSIS archive here, and import it into your workspace following the same procedures you used to import the example projects in Lab 1. You may read more about CMSIS and CRP in more detail below.
Begin the new project creation process by File->New->Project, and then select the 'LPCXpresso C Project' wizard option under 'C/C++'.
In the next window, select the first option, 'NXP LPC1100 C Project' and click next.
Name your project how you wish, and keep your current workspace checked as the location to store your new project.
Highlight our target board, the 'LPC1114/302' and click next.
In the next screen, check the box to use CMSIS initialization code, but do not check the box for CRP features. (if you properly installed the CMSIS library, it will create your project, if you did not properly install the CMSIS library, it will not find it and fail at this point).
Figure 2. CMSIS - Cortex Microcontroller Software Interface Standard.
...
What is CMSIS? - Cortex Microcontroller Software Interface Standard
From ARM's website: "The ARM Cortex™ Microcontroller Software Interface Standard (CMSIS) is a vendor-independent hardware abstraction layer for the Cortex-M processor series. The CMSIS enables consistent and simple software interfaces to the processor and the peripherals, simplifying software re-use, reducing the learning curve for new microcontroller developers and reducing the time to market for new devices. Creation of software is acknowledged as a major cost factor by the embedded industry. By standardizing the software interfaces across all Cortex-M silicon vendor products, this cost is significantly reduced, especially when creating new projects or migrating existing software to a new device."
In other words, CMSIS provides an abstraction layer for us, mapping important control register addresses and masking (writing the proper control bits into these registers) to easy to use data structures. Without CMSIS, our task of properly initializing, configuring, and even using most Arm M0 peripherals and features be extremely tedious.
What is CRP? - Code Read Protect
CRP is not important for us academics, but let's learn about it anyway! When releasing an embedded system as a product, the protection of the intellectual property held within it (i.e. the embedded software) is of paramount importance. CRP is intended to provide this protection by preventing people from reading back any data contained in the code space of the Cortex M0. Since our purposes are academic, we will not need to use this feature. See this document from NXP to read more about CRP and the different levels of security offered by it.
Part 2 (An Interrupt-driven Digital Oscilloscope)
This lab revisits the digital oscilloscope portion of Lab 1. In that lab, we accomplished the task of driving our LED with a voltage supplied from the Function Generator on your lab bench through leveraging the ADC peripheral. We polled the ADC on a fixed interval, read its value, and made a decision on how to light the LED based on that value.
In this lab we wish to accomplish a similar effect, but in doing so we will follow a completely different approach (a purely interrupt-driven approach). Using interrupts is incredibly powerful, as doing so allows the Arm to spend almost all of its time asleep.
Being asleep is great and yields us a potentially massive power savings. Because power usage is a critical design factor for embedded systems, we really need such savings and should structure our code with power at the forefront of our mind.
The high-level goal of this lab assignment is this: Light the LED when a square wave from 0V to 3.3V (logic low to logic high--rising edge) provided by the Function Generator. Change between a 25% and 75% LED duty cycle at a 30 second intervals, toggled by an internal process (a timer interrupt). Have the GPIO port 2 pin 1 also configured as an interrupt, sensing for the period of the function generator waveform (see the Reference Materials section at the bottom for resources for where to find this information). When the GPIO interrupt trigger condition occurs, the interrupt handler for the GPIO will execute, whereby we can set the frequency of the LED to match the frequency of the function generator square wave. The Timer will interrupt each 30 seconds, and each time will toggle the duty cycle between 25% and 75%. When the timer interrupts, toggle the duty cycle of the LED. The result should be that the LED toggles at half the frequency of the square wave provided by the function generator, and either at a 25% or 75% duty cycle--all using only interrupts to do the work!
Hint: Since we will be sleeping almost all of the time, we need to periodically wake up to check this global variable to see if it has changed. If so, we need to toggle the LED before going back to sleep. Therefore, we need to set up a Timer Interrupt to wake us up with a fast enough period to catch frequencies of operation from the Function Generator of up to 100Hz. Therefore, we suggest that your timer interrupts at a rate of every 1ms.
The Nested Vector Interrupt Control (NVIC)
Function definition Description
void NVIC_SystemReset (void) Resets the whole system including peripherals.
void NVIC_EnableIRQ(IRQn_Type IRQn) Enables the interrupt IRQn.
void NVIC_DisableIRQ (IRQn_Type IRQn) Disables the interrupt IRQn.
void NVIC_SetPriority (IRQn_Type IRQn, int32_t priority) Sets the priority for the interrupt IRQn.
uint32_t NVIC_GetPriority (IRQn_Type IRQn) Returns the priority for the specified interrupt.
void NVIC_SetPendingIRQ (IRQn_Type IRQn) Sets the interrupt IRQn pending.
IRQn_Type NVIC_GetPendingIRQ (IRQn_Type IRQn) Returns the pending status of the interrupt IRQn.
void NVIC_ClearPendingIRQ (IRQn_Type IRQn) Clears the pending status of the interrupt IRQn, if it is not already running or active.
Table 1. Nested Vector Interrupt Control (NVIC) CMSIS functions. Find their definitions in the file 'core_cm0.h' in your CMSIS directory.
This lab teaches us about the NVIC, its initialization, and its operation. In Table 1, we see all of the NVIC related functions that are provided to you by the CMSIS library. We will only need to leverage NVIC_EnableIRQ() to enable the correct interrupts and NVIC_SetPriority() to set the priority, but it is nice to see the others that are available to us. It will be up to you to read the proper NXP example project's code to figure out how to leverage these to perform the task that we want using interrupts in both the Timer32 and GPIO peripherals. Additionally, we will need to write our own initialization code for both the GPIO and Timer32 peripherals.
Producer/Consumer Model
Code Requirements
You must not copy any of the files from the example projects to your new project. i.e., you must write your own code! However, but you may find the example projects' code very helpful in doing so. Essentially, the example project's driver files, e.g. gpio.c, provide very general purpose functions to do the same things you want to do. Your task is to figure out which lines they use are the minimal (or close to it) to do the job you'd like to do, and then execute those lines directly in your code. These should be at the CMSIS level, which bitwise modify a memory-mapped IO control register (e.g. LPC_GPIO0-DIR) directly.
Use GPIO port 2, pin 1 for the interrupt input.
Use GPIO port 0, pin 7 for the LED control (you have to--they are hardwired!).
Use global variables for the Timer and GPIO interrupts to communicate.
The GPIO interrupt and Timer32 interrupt must be set to the same priority.
You will need 2 interrupt handlers to service each interrupt that comes in:
TIMER32_0_IRQHandler() and PIOINT2_IRQHandler().
Your main function must look exactly like the below main function code.
Look carefully at this code--does it look weird to you? After initialization, you will spend all your time in a while loop that repeatedly puts the Arm to sleep! How does this code framework actually get anything done?
Resolve signals up to 100Hz from the Function Generator with the LED light--your timer will need to interrupt at a duration of at least 1/(2*100) seconds. If you do this correctly, your end demonstration to the TAs or instructor will be that your LED blink rate is controllable at the Function Generator (similar to the Lab 1 result).
Optional: Write both GPIOInit() and TIMERInit() in assembly. Write them in C to make sure you get it working before attempting the assembly!
Code Template (Please copy and work from this exact template)
/* GPIO and GPIO Interrupt Initialization */
void GPIOInit() {
/* Your code here */
}
/* TIMER32 and TIMER32 Interrupt Initialization */
void TIMERInit() {
/* Your code here */
}
/* GPIO Interrupt Handler */
void PIOINT2_IRQHandler(void) {
/* Your code here */
}
/* TIMER32 Interrupt Handler */
void TIMER32_0_IRQHandler(void) {
/* Your code here */
}
int main(void) {
/* Initialization code */
GPIOInit(); // Initialize GPIO ports for both Interrupts and LED control
TIMERInit(); // Initialize Timer and Generate a 1ms interrupt
/* Infinite looping */
while(1);
return 0;
}
***Reference Materials (Being able to read and interpret these documents (along with what's done in the example projects' code) is very, very important for this lab!)
LPC1114 User Manual (from NXP)
Cortex-M0 Technical Reference Manual (from ARM)
Cortex-M0 Generic User Guide (from ARM)
Arm Architecture v6M Reference Manual (from ARM)
// ***********************************************************************
// *** MicroBuilder Interrupt Example ***
// ***********************************************************************
LPC1114 Interrupt - Micro Builder
http://www.microbuilder.eu/Projects/LPC1114ReferenceDesign/CodeBaseDocumentation.aspx
Any GPIO pin on the LPC1114 can be configured as an external interrupt source. The gpioSetInterrupt method allows you to determine which pins act as an interrupt source, and specify the conditions under which an interrupt should be raised (if the pin goes high or low, etc.). Depending on the portNum specified, the interrupt will be handled (by default) by PIOINTx_IRQHandler, where 'x' is the port number in question (PIOINT1_IRQHandler would be entered if an interrupt was raised on GPIO pin 1.8, for example).
void gpioSetInterrupt (uint32_t portNum, uint32_t bitPos, gpioInterruptSense_t sense, gpioInterruptEdge_t edge, gpioInterruptEvent_t event)
Arguments
portNum: the gpio port number [0..3]
bitPos: the bit position for the gpio pin [0..11]
sense: whether the interrupt should be configured as edge (gpioInterruptSense_Edge) or level (gpioInterruptSense_Level) sensitive.
edge: whether an interrupt is triggered on one edge (gpioInterruptEdge_Single) or on both (gpioInterruptEdge_Double).
event: whether the rising edge (gpioInterruptEvent_ActiveHigh) or the falling edge (gpioInterruptEvent_ActiveLow) should be used to trigger the interrupt. ActiveHigh means that a HIGH level on the pin will trigger an interrupt, ActiveLow means that a LOW level on the pin will trigger an interrupt.
void gpioIntEnable (uint32_t portNum, uint32_t bitPos)
Enables an interrupt on the specified GPIO pin.
Arguments
portNum: the gpio port number [0..3]
bitPos: the bit position for the gpio pin [0..11]
See the example for gpioSetInterrupt.
void gpioIntDisable (uint32_t portNum, uint32_t bitPos)
Disables an interrupt on the specified pin.
Arguments
portNum: the gpio port number [0..3]
bitPos: the bit position for the gpio pin [0..11]
See the example for gpioSetInterrupt.
void gpioIntClear (uint32_t portNum, uint32_t bitPos)
Clears the interrupt on the specified pin. This method should only be called from withint the interrupt service routine (ISR) once the interrupt has been raise.
Arguments
portNum: the gpio port number [0..3]
bitPos: the bit position for the gpio pin [0..11]
uint32_t gpioIntStatus (uint32_t portNum, uint32_t bitPos)
Gets the interrupt status for a specific port pin.
Arguments
portNum: the gpio port number [0..3]
bitPos: the bit position for the gpio pin [0..11]
Returns
'1' if an interrupt was raised by the specified pin, otherwise '0'.
See the example for gpioIntClear.
#include "core/cpu/cpu.h"
#include "core/gpio/gpio.h"
int main (void)
{
cpuInit();
gpioInit();
// Set GPIO1.8 to input
gpioSetDir(1, 8, gpioDirection_Input);
// Disable the internal pullup/down resistor on P1.8
gpioSetPullup (&IOCON_PIO1_8, gpioPullupMode_Inactive);
// Setup an interrupt on GPIO1.8
gpioSetInterrupt(1, // Port
8, // Pin
gpioInterruptSense_Edge, // Edge Sensitive
gpioInterruptEdge_Single, // Single Edge
gpioInterruptEvent_ActiveHigh); // Active High
// Enable the interrupt
gpioIntEnable(1, 8);
while (1);
}
// IRQ Handler for GPIO Port 1
void PIOINT1_IRQHandler(void)
{
uint32_t regVal;
// Check if pin 1.8 raised the interrupt
regVal = gpioIntStatus(1, 8);
if (regVal)
{
// Do Something
...
// Clear the interrupt
gpioIntClear(1, 8);
}
return;
}
See the example for gpioSetInterrupt to configure an interrupt.
.END
CooCox NVIC Example - Author: CooCox
http://www.coocox.org/show_exam/NVIC/82.html
Description:
This example describes how to configure the Nested Vectored Interrupt Controller(NVIC)IRQ Channels:
* Enable the following external interrupts in NVIC: LVD, BOD, WDT.
* Configure the priority of each external interrupt.
* Set each external interrupt pending bit during a lower priority ISR(Interrupt Service Routine).
* Show the order of interrupts by LEDs.
This example runs on HT32F175x/275x Development board.
Example Code
#include "ht32f175x_275x.h"
#include "ht32f175x_275x_gpio.h"
#include "ht32f175x_275x_ckcu.h"
#include "ht32f175x_275x_misc.h"
#define DELAY_TIME 0x7FFFFF
void LEDInit(void);
void LVD_IRQHandler(void);
void BOD_IRQHandler(void);
void WDT_IRQHandler(void);
void Delay(vu32 count);
void External_Interrupt(void)
{
//
// Config led pins
//
LEDInit();
//
// Enable the Interrupts
//
NVIC_EnableIRQ(LVD_IRQn);
NVIC_EnableIRQ(BOD_IRQn);
NVIC_EnableIRQ(WDT_IRQn);
//
// Configure 2 bits for preemption priority 2 bits for subpriority
//
NVIC_SetPriorityGrouping(5);
//
// Configure the External Interrupts Priority
//
NVIC_SetPriority(LVD_IRQn, NVIC_EncodePriority(5, 2, 0));
NVIC_SetPriority(BOD_IRQn, NVIC_EncodePriority(5, 1, 0));
NVIC_SetPriority(WDT_IRQn, NVIC_EncodePriority(5, 0, 0));
//
// Generate LVD Interrupt
//
NVIC_SetPendingIRQ(LVD_IRQn);
//
// Check on the LEDs flash sequence
//
while(1);
}
void LEDInit(void)
{
//
// Enable PA,PB clock
//
CKCU_APBPerip0ClockConfig(CKCU_APBEN0_PA, ENABLE);
CKCU_APBPerip0ClockConfig(CKCU_APBEN0_PB, ENABLE);
GPIO_SetOutBits(GPIOA, GPIO_PIN_15);
GPIO_SetOutBits(GPIOB, GPIO_PIN_0);
GPIO_SetOutBits(GPIOB, GPIO_PIN_1);
//
// Set PA15,PB0,PB1 as output mode
//
GPIO_DirectionConfig(GPIOA, GPIO_PIN_15, GPIO_DIR_OUT);
GPIO_DirectionConfig(GPIOB, GPIO_PIN_0, GPIO_DIR_OUT);
GPIO_DirectionConfig(GPIOB, GPIO_PIN_1, GPIO_DIR_OUT);
}
void LVD_IRQHandler(void)
{
//
// Generate BOD Interrupt
//
NVIC_SetPendingIRQ(BOD_IRQn);
//
// Turn on LED3
//
GPIO_ClearOutBits(GPIOB, GPIO_PIN_1);
Delay(DELAY_TIME);
}
void BOD_IRQHandler(void)
{
//
// Generate WDT Interrupt
//
NVIC_SetPendingIRQ(WDT_IRQn);
//
// Turn on LED2
//
GPIO_ClearOutBits(GPIOB, GPIO_PIN_0);
Delay(DELAY_TIME);
}
void WDT_IRQHandler(void)
{
//
// Turn on LED1
//
GPIO_ClearOutBits(GPIOA, GPIO_PIN_15);
Delay(DELAY_TIME);
}
void Delay(vu32 count)
{
while(count--);
}
// ***********************************************************************
// *** Somy Interrupt Example ***
// ***********************************************************************
Somy IRQ Example - Somy
#include "lpc1114.h"
#include "typedef.h"
#define LED2 (1 << 7) //PIO2_7
#define LED3 (1 << 8) //PIO2_8
#define LED4 (1 << 5) //PIO2_5
#define BEEP (1 << 7) //GPIO0-7
#define TMR16B0_CLK (1<<7)
#define KEY1 (1 << 2) //PIO0_2
uint8 KEY1_FLAG = 0;
void delay(uint32 time)
{
uint32 i,j;
for(i=0; i<time; i++)
for(j=0; j<50000; j++);
}
sys_AHB_clk_ctrl(uint32 xxx_ckl)
{
AHBCLKCTRL |= xxx_ckl;
}
sys_ahb_clk_enable(uint32 xxx_ckl)
{
AHBCLKCTRL |= xxx_ckl;
}
sys_ahb_clk_disable(uint32 xxx_ckl)
{
AHBCLKCTRL &= ~xxx_ckl;
}
//
void pio2_data_clr(uint32 num)
{
GPIO2DATA_ALL &= ~num; // On
}
void pio0_data_clr(uint32 num)
{
GPIO0DATA_ALL &= ~num; // Off
}
void pio0_dir_set(uint32 num)
{
GPIO0DIR |= num;
}
void pio2_dir_set(uint32 num)
{
GPIO2DIR |= num;
}
void PIO02_irq_init(uint32 num)
{
GPIO0IS &= ~(num); // select PIO0.2 edge trigger
GPIO0IEV &=~(num);// select PIO0.2 falling edge trigger
GPIO0IM |= (num); // set PIO0.2 interrupt not maskable
}
void pio2_data_set(uint32 num)
{
GPIO2DATA_ALL |= num; // Off
}
void time0_init(void)
{
TMR16B0PR = 999; // set prescale for Hz
TMR16B0MCR = 0x03; // match channel match interrupt and reset TOTC
TMR16B0MR0 = 12000; // match value (1 second比
TMR16B0TCR = 0x3; // start and reset 0011
TMR16B0TCR = 0x01; // 0001
}
static __inline void NVIC_EnableIRQ(IRQn_Type IRQn)
{
NVIC_ISER = (1 << ((uint32)(IRQn) & 0x1F));
}
void PIOINT0_IRQHandler(void)
{
uint8 temp =0;
if((GPIO0MIS&0x004)==0x004) // check if PIO0.2 pin causing interrupt
{
delay(4);
if( (GPIO0DATA_ALL & KEY1) != KEY1 ) // if Key1 pressed
{
while((GPIO0DATA_ALL & KEY1) == 0);
if( (GPIO2DATA_ALL & LED2) == 0 ) // if Key 1 pressed
{
pio2_data_set( LED2 | LED3 | LED4 );
}else{
pio2_data_clr( LED2 | LED3 | LED4 );
}
}
}
GPIO0IC = 0x04; // clear interrupt;
temp = temp;
}
int main(void)
{
pio2_dir_set( LED2 | LED3 | LED4 );
pio2_data_set( LED2 | LED3 | LED4 );
PIO02_irq_init(KEY1);
NVIC_EnableIRQ(EINT0_IRQn);
while(1);
}
.END
*** IRQ numbering ***
typedef enum IRQn
{
SysTick_IRQn = -1, /*!< 15 Cortex-M0 System Tick Interrupt */
TIMER_16_0_IRQn = 16, /*!< 16-bit Timer0 Interrupt */
TIMER_16_1_IRQn = 17, /*!< 16-bit Timer1 Interrupt */
TIMER_32_0_IRQn = 18, /*!< 32-bit Timer0 Interrupt */
TIMER_32_1_IRQn = 19, /*!< 32-bit Timer1 Interrupt */
} IRQn_Type;
__Vectors DCD SysTick_Handler ; SysTick Handler
DCD TIMER16_0_IRQHandler ; 16+16: 16-bit Counter-Timer 0
DCD TIMER16_1_IRQHandler ; 16+17: 16-bit Counter-Timer 1
DCD TIMER32_0_IRQHandler ; 16+18: 32-bit Counter-Timer 0
DCD TIMER32_1_IRQHandler ; 16+19: 32-bit Counter-Timer 1
DCD PIOINT3_IRQHandler ; 16+28: PIO INT3
DCD PIOINT2_IRQHandler ; 16+29: PIO INT2
DCD PIOINT1_IRQHandler ; 16+30: PIO INT1
DCD PIOINT0_IRQHandler ; 16+31: PIO INT0
.END
*** Generate two interrupts with timer 0
http://www.coocox.org/forum/topic.php?id=692
.END
No comments:
Post a Comment