PlatformIO Community

Compiling ISR() on a Teensy

I’m trying to use a hardware interrupt on a Teensy 3.6. I assume this all compiles fine within the Arduino IDE but have chosen PlatformIO for it’s much improved debugging capabilities.

By including these at the top of the source file:
#include <avr/io.h>
#include <avr/interrupt.h>

The code, should compile
ISR (USART1_RX_vect)
{

}

But instead, it’s as if the headers were never included in the build:

error: expected constructor, destructor, or type conversion before ‘(’ token

No.

Well, yeah, because the Teensy 3.6 has an ARM chipset and ARM GCC compiler, however you’re attempting to use AVR ISR declarations… That won’t work.

You will find that a project with platformio.ini

[env:teensy2pp]
platform = teensy
board = teensy2pp
framework = arduino
#include <Arduino.h>
#include <avr/io.h>
#include <avr/interrupt.h>

ISR (USART1_RX_vect)
{
}

void setup() {
	Serial.begin(115200);
}

void loop() {
}

will compile perfectly fine due to the Teensy2.0++ being an Atmel AVR style AT90USB1286.

For an ARM based Teensy, look into the NVIC (nested vectored interrupt controller) and some code using it (here, here, the VECTOR table). Vectors can be dynamically re-appointed using NVIC_SetVector().

For some reason unknown to me, this function was stripped from the core_cm4.h header file (but is present in e.g. core_cm7.h). It can still be hacked in there by adding the initially removed code.

For changing the intterrupt vector for the, say, IRQ_UART0_STATUS (IRQs are listed here) you would ‘just’ do…

#include <Arduino.h>

#define NVIC_USER_IRQ_OFFSET          16
#ifndef   __STATIC_INLINE
  #define __STATIC_INLINE                        static inline
#endif
#ifdef __cplusplus
  #define   __I     volatile             /*!< Defines 'read only' permissions */
#else
  #define   __I     volatile const       /*!< Defines 'read only' permissions */
#endif
#define     __O     volatile             /*!< Defines 'write only' permissions */
#define     __IO    volatile             /*!< Defines 'read / write' permissions */
/* following defines should be used for structure members */
#define     __IM     volatile const      /*! Defines 'read only' structure member permissions */
#define     __OM     volatile            /*! Defines 'write only' structure member permissions */
#define     __IOM    volatile            /*! Defines 'read / write' structure member permissions */
/**
  \brief  Structure type to access the System Control Block (SCB).
 */
typedef struct
{
  __IM  uint32_t CPUID;                  /*!< Offset: 0x000 (R/ )  CPUID Base Register */
  __IOM uint32_t ICSR;                   /*!< Offset: 0x004 (R/W)  Interrupt Control and State Register */
  __IOM uint32_t VTOR;                   /*!< Offset: 0x008 (R/W)  Vector Table Offset Register */
  __IOM uint32_t AIRCR;                  /*!< Offset: 0x00C (R/W)  Application Interrupt and Reset Control Register */
  __IOM uint32_t SCR;                    /*!< Offset: 0x010 (R/W)  System Control Register */
  __IOM uint32_t CCR;                    /*!< Offset: 0x014 (R/W)  Configuration Control Register */
  __IOM uint8_t  SHP[12U];               /*!< Offset: 0x018 (R/W)  System Handlers Priority Registers (4-7, 8-11, 12-15) */
  __IOM uint32_t SHCSR;                  /*!< Offset: 0x024 (R/W)  System Handler Control and State Register */
  __IOM uint32_t CFSR;                   /*!< Offset: 0x028 (R/W)  Configurable Fault Status Register */
  __IOM uint32_t HFSR;                   /*!< Offset: 0x02C (R/W)  HardFault Status Register */
  __IOM uint32_t DFSR;                   /*!< Offset: 0x030 (R/W)  Debug Fault Status Register */
  __IOM uint32_t MMFAR;                  /*!< Offset: 0x034 (R/W)  MemManage Fault Address Register */
  __IOM uint32_t BFAR;                   /*!< Offset: 0x038 (R/W)  BusFault Address Register */
  __IOM uint32_t AFSR;                   /*!< Offset: 0x03C (R/W)  Auxiliary Fault Status Register */
  __IM  uint32_t PFR[2U];                /*!< Offset: 0x040 (R/ )  Processor Feature Register */
  __IM  uint32_t DFR;                    /*!< Offset: 0x048 (R/ )  Debug Feature Register */
  __IM  uint32_t ADR;                    /*!< Offset: 0x04C (R/ )  Auxiliary Feature Register */
  __IM  uint32_t MMFR[4U];               /*!< Offset: 0x050 (R/ )  Memory Model Feature Register */
  __IM  uint32_t ISAR[5U];               /*!< Offset: 0x060 (R/ )  Instruction Set Attributes Register */
        uint32_t RESERVED0[5U];
  __IOM uint32_t CPACR;                  /*!< Offset: 0x088 (R/W)  Coprocessor Access Control Register */
} SCB_Type;
#define SCS_BASE            (0xE000E000UL)                            /*!< System Control Space Base Address */
#define SCB_BASE            (SCS_BASE +  0x0D00UL)                    /*!< System Control Block Base Address */
#define SCnSCB              ((SCnSCB_Type    *)     SCS_BASE      )   /*!< System control Register not in SCB */
#define SCB                 ((SCB_Type       *)     SCB_BASE      )   /*!< SCB configuration struct */

__STATIC_INLINE void __NVIC_SetVector(IRQ_NUMBER_t  IRQn, uint32_t vector)
{
  uint32_t *vectors = (uint32_t *)SCB->VTOR;
  vectors[(int32_t)IRQn + NVIC_USER_IRQ_OFFSET] = vector;
}

/* -- start of actual program --*/

void my_uart0_status_irq() {
	//do stuff
}


void setup() {
	//steal UART0 status IRQ
	//disable first
	NVIC_DISABLE_IRQ(IRQ_UART0_STATUS);
	__NVIC_SetVector(IRQ_UART0_STATUS, (uint32_t) &my_uart0_status_irq);
	NVIC_ENABLE_IRQ(IRQ_UART0_STATUS);
}

void loop() {
}

One might think that due to the ISRs being marked as weak, one should be able to override them. But if the framework already implements them themselves, overriding is not possible anymore.

So code like this

#include <Arduino.h>

/* overwrite UART0 status IRQ from C++ code. Must be linked with C linkage. */
extern "C" void uart0_status_isr() {

}

void setup() {
}

void loop() {
}

won’t work

arm-none-eabi/bin/ld.exe: Disabling relaxation: it will not work with multiple definitions

But instead cmp0_isr (comparator 0?) as a function name will succeed, because it’s not used by the framework. So you have 3 possibilities for getting your interrupt code running:

  • Steal handler for any IRQ using NVIC_SetVector
  • if the framework implements vector, change its base implementation.
  • if the framework doesn’t implement it, just implement it yourself using the decleration found above

Thanks Max. I’ll work through your suggestions. I appreciate the help.