Forum Rule: Always post complete source code & details to reproduce any issue!
Results 1 to 2 of 2

Thread: Using Interrupts on Teensy 3.1

  1. #1

    Using Interrupts on Teensy 3.1

    Hi all

    (Preliminary note: there is an answer to a similar topic on the main PJRC site but it only applies to ATMEL processors I believe (https://www.pjrc.com/teensy/interrupts.html), so I have decided to share and document my [fairly frustrating] experience to understand how to use interrupts on Teensy 3.1/3.2.

    I come from an ATMEL background and those who do, are usually accustomed to using the ISR_xxx macros to define functions that handle specific interrupts.
    Typically it is the Linker who figures out where each function needs to go on ATMEL provided you declare your function using the ISR_xxx macro.

    On the processor used in Teensy 3.0/3.1/3.2 (and maybe on the new 3.5/3.6?? not sure) it works in a very different way: you need to create an Interrupt Vector Table explicitly in your code.

    Luckily if you are using Teensyduino, this table is already created by Teensyduino and predefined function names have already been assigned to each interrupt (although it doesn't seem to be documented anywhere, hence the reason for writing this post).
    At this point - and if you're using Teensyduino - all you need to do, is find out which name to use when declaring the function for the interrupt you want.

    It is in the mx20dx128.c file (inside the hardware\teensy\cores\teensy3) that these names are already declared and prepared for you. There you will find the following code:

    Code:
    void nmi_isr(void)		__attribute__ ((weak, alias("unused_isr")));
    void hard_fault_isr(void)	__attribute__ ((weak, alias("fault_isr")));
    void memmanage_fault_isr(void)	__attribute__ ((weak, alias("fault_isr")));
    void bus_fault_isr(void)	__attribute__ ((weak, alias("fault_isr")));
    void usage_fault_isr(void)	__attribute__ ((weak, alias("fault_isr")));
    void svcall_isr(void)		__attribute__ ((weak, alias("unused_isr")));
    void debugmonitor_isr(void)	__attribute__ ((weak, alias("unused_isr")));
    void pendablesrvreq_isr(void)	__attribute__ ((weak, alias("unused_isr")));
    void systick_isr(void)		__attribute__ ((weak, alias("systick_default_isr")));
    
    void dma_ch0_isr(void)		__attribute__ ((weak, alias("unused_isr")));
    void dma_ch1_isr(void)		__attribute__ ((weak, alias("unused_isr")));
    void dma_ch2_isr(void)		__attribute__ ((weak, alias("unused_isr")));
    void dma_ch3_isr(void)		__attribute__ ((weak, alias("unused_isr")));
    void dma_ch4_isr(void)		__attribute__ ((weak, alias("unused_isr")));
    void dma_ch5_isr(void)		__attribute__ ((weak, alias("unused_isr")));
    void dma_ch6_isr(void)		__attribute__ ((weak, alias("unused_isr")));
    void dma_ch7_isr(void)		__attribute__ ((weak, alias("unused_isr")));
    void dma_ch8_isr(void)		__attribute__ ((weak, alias("unused_isr")));
    void dma_ch9_isr(void)		__attribute__ ((weak, alias("unused_isr")));
    void dma_ch10_isr(void)		__attribute__ ((weak, alias("unused_isr")));
    void dma_ch11_isr(void)		__attribute__ ((weak, alias("unused_isr")));
    void dma_ch12_isr(void)		__attribute__ ((weak, alias("unused_isr")));
    void dma_ch13_isr(void)		__attribute__ ((weak, alias("unused_isr")));
    void dma_ch14_isr(void)		__attribute__ ((weak, alias("unused_isr")));
    void dma_ch15_isr(void)		__attribute__ ((weak, alias("unused_isr")));
    void dma_error_isr(void)	__attribute__ ((weak, alias("unused_isr")));
    void mcm_isr(void)		__attribute__ ((weak, alias("unused_isr")));
    void randnum_isr(void)		__attribute__ ((weak, alias("unused_isr")));
    void flash_cmd_isr(void)	__attribute__ ((weak, alias("unused_isr")));
    void flash_error_isr(void)	__attribute__ ((weak, alias("unused_isr")));
    void low_voltage_isr(void)	__attribute__ ((weak, alias("unused_isr")));
    void wakeup_isr(void)		__attribute__ ((weak, alias("unused_isr")));
    void watchdog_isr(void)		__attribute__ ((weak, alias("unused_isr")));
    void i2c0_isr(void)		__attribute__ ((weak, alias("unused_isr")));
    void i2c1_isr(void)		__attribute__ ((weak, alias("unused_isr")));
    void i2c2_isr(void)		__attribute__ ((weak, alias("unused_isr")));
    void i2c3_isr(void)		__attribute__ ((weak, alias("unused_isr")));
    void spi0_isr(void)		__attribute__ ((weak, alias("unused_isr")));
    void spi1_isr(void)		__attribute__ ((weak, alias("unused_isr")));
    void spi2_isr(void)		__attribute__ ((weak, alias("unused_isr")));
    void sdhc_isr(void)		__attribute__ ((weak, alias("unused_isr")));
    void enet_timer_isr(void)	__attribute__ ((weak, alias("unused_isr")));
    void enet_tx_isr(void)		__attribute__ ((weak, alias("unused_isr")));
    void enet_rx_isr(void)		__attribute__ ((weak, alias("unused_isr")));
    void enet_error_isr(void)	__attribute__ ((weak, alias("unused_isr")));
    void can0_message_isr(void)	__attribute__ ((weak, alias("unused_isr")));
    void can0_bus_off_isr(void)	__attribute__ ((weak, alias("unused_isr")));
    void can0_error_isr(void)	__attribute__ ((weak, alias("unused_isr")));
    void can0_tx_warn_isr(void)	__attribute__ ((weak, alias("unused_isr")));
    void can0_rx_warn_isr(void)	__attribute__ ((weak, alias("unused_isr")));
    void can0_wakeup_isr(void)	__attribute__ ((weak, alias("unused_isr")));
    void can1_message_isr(void)	__attribute__ ((weak, alias("unused_isr")));
    void can1_bus_off_isr(void)	__attribute__ ((weak, alias("unused_isr")));
    void can1_error_isr(void)	__attribute__ ((weak, alias("unused_isr")));
    void can1_tx_warn_isr(void)	__attribute__ ((weak, alias("unused_isr")));
    void can1_rx_warn_isr(void)	__attribute__ ((weak, alias("unused_isr")));
    void can1_wakeup_isr(void)	__attribute__ ((weak, alias("unused_isr")));
    void i2s0_tx_isr(void)		__attribute__ ((weak, alias("unused_isr")));
    void i2s0_rx_isr(void)		__attribute__ ((weak, alias("unused_isr")));
    void i2s0_isr(void)		__attribute__ ((weak, alias("unused_isr")));
    void uart0_lon_isr(void)	__attribute__ ((weak, alias("unused_isr")));
    void uart0_status_isr(void)	__attribute__ ((weak, alias("unused_isr")));
    void uart0_error_isr(void)	__attribute__ ((weak, alias("unused_isr")));
    void uart1_status_isr(void)	__attribute__ ((weak, alias("unused_isr")));
    void uart1_error_isr(void)	__attribute__ ((weak, alias("unused_isr")));
    void uart2_status_isr(void)	__attribute__ ((weak, alias("unused_isr")));
    void uart2_error_isr(void)	__attribute__ ((weak, alias("unused_isr")));
    void uart3_status_isr(void)	__attribute__ ((weak, alias("unused_isr")));
    void uart3_error_isr(void)	__attribute__ ((weak, alias("unused_isr")));
    void uart4_status_isr(void)	__attribute__ ((weak, alias("unused_isr")));
    void uart4_error_isr(void)	__attribute__ ((weak, alias("unused_isr")));
    void uart5_status_isr(void)	__attribute__ ((weak, alias("unused_isr")));
    void uart5_error_isr(void)	__attribute__ ((weak, alias("unused_isr")));
    void lpuart0_status_isr(void)	__attribute__ ((weak, alias("unused_isr")));
    void adc0_isr(void)		__attribute__ ((weak, alias("unused_isr")));
    void adc1_isr(void)		__attribute__ ((weak, alias("unused_isr")));
    void cmp0_isr(void)		__attribute__ ((weak, alias("unused_isr")));
    void cmp1_isr(void)		__attribute__ ((weak, alias("unused_isr")));
    void cmp2_isr(void)		__attribute__ ((weak, alias("unused_isr")));
    void cmp3_isr(void)		__attribute__ ((weak, alias("unused_isr")));
    void ftm0_isr(void)		__attribute__ ((weak, alias("unused_isr")));
    void ftm1_isr(void)		__attribute__ ((weak, alias("unused_isr")));
    void ftm2_isr(void)		__attribute__ ((weak, alias("unused_isr")));
    void ftm3_isr(void)		__attribute__ ((weak, alias("unused_isr")));
    void tpm0_isr(void)		__attribute__ ((weak, alias("unused_isr")));
    void tpm1_isr(void)		__attribute__ ((weak, alias("unused_isr")));
    void tpm2_isr(void)		__attribute__ ((weak, alias("unused_isr")));
    void cmt_isr(void)		__attribute__ ((weak, alias("unused_isr")));
    void rtc_alarm_isr(void)	__attribute__ ((weak, alias("unused_isr")));
    void rtc_seconds_isr(void)	__attribute__ ((weak, alias("unused_isr")));
    void pit_isr(void)		__attribute__ ((weak, alias("unused_isr")));
    void pit0_isr(void)		__attribute__ ((weak, alias("unused_isr")));
    void pit1_isr(void)		__attribute__ ((weak, alias("unused_isr")));
    void pit2_isr(void)		__attribute__ ((weak, alias("unused_isr")));
    void pit3_isr(void)		__attribute__ ((weak, alias("unused_isr")));
    void pdb_isr(void)		__attribute__ ((weak, alias("unused_isr")));
    void usb_isr(void)		__attribute__ ((weak, alias("unused_isr")));
    void usb_charge_isr(void)	__attribute__ ((weak, alias("unused_isr")));
    void usbhs_isr(void)		__attribute__ ((weak, alias("unused_isr")));
    void usbhs_phy_isr(void)	__attribute__ ((weak, alias("unused_isr")));
    void dac0_isr(void)		__attribute__ ((weak, alias("unused_isr")));
    void dac1_isr(void)		__attribute__ ((weak, alias("unused_isr")));
    void tsi0_isr(void)		__attribute__ ((weak, alias("unused_isr")));
    void mcg_isr(void)		__attribute__ ((weak, alias("unused_isr")));
    void lptmr_isr(void)		__attribute__ ((weak, alias("unused_isr")));
    void porta_isr(void)		__attribute__ ((weak, alias("unused_isr")));
    void portb_isr(void)		__attribute__ ((weak, alias("unused_isr")));
    void portc_isr(void)		__attribute__ ((weak, alias("unused_isr")));
    void portd_isr(void)		__attribute__ ((weak, alias("unused_isr")));
    void porte_isr(void)		__attribute__ ((weak, alias("unused_isr")));
    void portcd_isr(void)		__attribute__ ((weak, alias("unused_isr")));
    void software_isr(void)		__attribute__ ((weak, alias("unused_isr")));

    These define the names that you should use to create functions for each interrupt handler.

    The actual function names are assigned to the processor vector table further down on the file, depending on your processor (for example Teensy 3.0 only has 1 ADC, so the adc1_isr is not assigned for Teensy 3.0 but IS assigned in teensy 3.1/3.2 because the processor has 2 ADCs).

    So now we're done with step 1: you know which name to assign to your function:
    For example if you want to write an interrupt handler on ADC0 complete readings, just write a function (anywhere in your code) with the name "void adc0_isr()".

    Also note that you don't actually need to have all the functions for all interrupts defined. The attribute __attribute__ ((weak, alias("unused_isr"))) tells the compiler to use the "unused_isr" functions if it can't find that actual function declared. So all is good.


    Now, the next step is actually enabling the interrupt flags on the processor so that interruptions are generated when the event you want occurs.

    This is done in two parts for Teensy 3.x:

    A) Refer to the processor datasheet which you can download form the main PJRC website to set the appropriate registers and enable the raising of interruptions for the case you want (pretty much like ATMEL for example)
    ( In the example that I gave to get an interrupt on an ADC complete reading, this is explained on page 655, but bear in mind this won't work with the standard analogRead function because this function will do active waiting for the conversion; you'll need to write your own. I only use it here as an example of a possible Interrupt function)

    B) After you do this you have to further enable the Interrupt by using the NVIC_ENABLE_IRQ function.
    Again, in the example of the ADC I was using, it would be a call to NVIC_ENABLE_IRQ(IRQ_ADC0)
    You'll need to find out which is the IRQ name for your case. The IRQ names are defined in "kinetis.h" (also inside the hardware\teensy\cores\teensy3)

    Conclusion:

    The hardest part for me, coming from ATMEL world, was figuring out what was the equivalent to the ISR macros in Teensy to set up interrupt code.
    Turns out that using Teensyduino, in the end it's quite similar: you simply define a function with a special name.
    The difference is that in Teensy 3.xx those function names are defined in mx20dx128.c (part of which I list above)

    The second part which involves finding the IRQ names and enabling them using the NVIC is pretty straightforward, as is setting the appropriate flags on the processor to raise the interruptions (just read the datasheet).

    I hope this can help someone save a day's work

    Best,
    Pedro
    Last edited by pramilo; 01-19-2017 at 07:39 PM.

  2. #2
    Senior Member+ KurtE's Avatar
    Join Date
    Jan 2014
    Posts
    7,662
    Thanks, look good.

    I thought I would mention, that another way for you to setup your interrupt is by using the API: attachInterruptVector

    For example if you wish to have your own interrupt handler for the LPTIMER interrupt, you could make a call like: attachInterruptVector( IRQ_LPTMR, MyTimerInterrupt);

    You can find the list of interrupts in the file (...hardware\teensy\avr\cores\teensy3\kinetis.h)

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •