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

Thread: AVR-like ISR()

  1. #1

    Lightbulb AVR-like ISR()

    Hello

    I began to "play" with my Teensy 3.0 board today, directly with FlexTimers...

    I found a solution to have a AVR-like ISR() macro which doesn't appears to be implemented in Teensyduino for now. Actually it's the AVR ISR macro itself...
    I just pasted the macro from avr-libc to mk20dx128.h (just before the isr functions prototypes):

    Code:
    #ifdef __cplusplus
    #  define ISR(vector, ...)            \
        extern "C" void vector (void) __attribute__ ((signal,__INTR_ATTRS)) __VA_ARGS__; \
        void vector (void)
    #else
    #  define ISR(vector, ...)            \
        void vector (void) __attribute__ ((signal,__INTR_ATTRS)) __VA_ARGS__; \
        void vector (void)
    #endif
    Use the ISR() macro with the isr functions names (defined in mk20dx128.h, and just below for convenience), for example:

    Code:
    ISR(ftm0_isr)
    {
      FTM0_SC &= ~FTM_SC_TOF;
      digitalWrite(13, state ^= HIGH);
    }
    Tested successfully with ftm0 and ftm1.

    Have fun with Teensy


    ***
    And just a tip for anyone using the FlexTimers, you need to write 0 in FTM0_SC before writing to FTM0_MOD, otherwise FTM0_MOD will not be updated (stay at 0xBFFF for me). I spent some time to find that...


    ***
    Here is the list of Teensy 3.0 ISRs:
    Code:
    	nmi_isr,					//  2 ARM: Non-maskable Interrupt (NMI)
    	hard_fault_isr,					//  3 ARM: Hard Fault
    	memmanage_fault_isr,				//  4 ARM: MemManage Fault
    	bus_fault_isr,					//  5 ARM: Bus Fault
    	usage_fault_isr,				//  6 ARM: Usage Fault
    	fault_isr,					//  7 --
    	fault_isr,					//  8 --
    	fault_isr,					//  9 --
    	fault_isr,					// 10 --
    	svcall_isr,					// 11 ARM: Supervisor call (SVCall)
    	debugmonitor_isr,				// 12 ARM: Debug Monitor
    	fault_isr,					// 13 --
    	pendablesrvreq_isr,				// 14 ARM: Pendable req serv(PendableSrvReq)
    	systick_isr,					// 15 ARM: System tick timer (SysTick)
    	dma_ch0_isr,					// 16 DMA channel 0 transfer complete
    	dma_ch1_isr,					// 17 DMA channel 1 transfer complete
    	dma_ch2_isr,					// 18 DMA channel 2 transfer complete
    	dma_ch3_isr,					// 19 DMA channel 3 transfer complete
    	dma_error_isr,					// 20 DMA error interrupt channel
    	unused_isr,					// 21 DMA --
    	flash_cmd_isr,					// 22 Flash Memory Command complete
    	flash_error_isr,				// 23 Flash Read collision
    	low_voltage_isr,				// 24 Low-voltage detect/warning
    	wakeup_isr,					// 25 Low Leakage Wakeup
    	watchdog_isr,					// 26 Both EWM and WDOG interrupt
    	i2c0_isr,					// 27 I2C0
    	spi0_isr,					// 28 SPI0
    	i2s0_tx_isr,					// 29 I2S0 Transmit
    	i2s0_rx_isr,					// 30 I2S0 Receive
    	uart0_lon_isr,					// 31 UART0 CEA709.1-B (LON) status
    	uart0_status_isr,				// 32 UART0 status
    	uart0_error_isr,				// 33 UART0 error
    	uart1_status_isr,				// 34 UART1 status
    	uart1_error_isr,				// 35 UART1 error
    	uart2_status_isr,				// 36 UART2 status
    	uart2_error_isr,				// 37 UART2 error
    	adc0_isr,					// 38 ADC0
    	cmp0_isr,					// 39 CMP0
    	cmp1_isr,					// 40 CMP1
    	ftm0_isr,					// 41 FTM0
    	ftm1_isr,					// 42 FTM1
    	cmt_isr,					// 43 CMT
    	rtc_alarm_isr,					// 44 RTC Alarm interrupt
    	rtc_seconds_isr,				// 45 RTC Seconds interrupt
    	pit0_isr,					// 46 PIT Channel 0
    	pit1_isr,					// 47 PIT Channel 1
    	pit2_isr,					// 48 PIT Channel 2
    	pit3_isr,					// 49 PIT Channel 3
    	pdb_isr,					// 50 PDB Programmable Delay Block
    	usb_isr,					// 51 USB OTG
    	usb_charge_isr,					// 52 USB Charger Detect
    	tsi0_isr,					// 53 TSI0
    	mcg_isr,					// 54 MCG
    	lptmr_isr,					// 55 Low Power Timer
    	porta_isr,					// 56 Pin detect (Port A)
    	portb_isr,					// 57 Pin detect (Port B)
    	portc_isr,					// 58 Pin detect (Port C)
    	portd_isr,					// 59 Pin detect (Port D)
    	porte_isr,					// 60 Pin detect (Port E)
    	software_isr,					// 61 Software interrupt

  2. #2
    Quote Originally Posted by BBenj View Post
    Hello

    I began to "play" with my Teensy 3.0 board today, directly with FlexTimers...

    I found a solution to have a AVR-like ISR() macro which doesn't appears to be implemented in Teensyduino for now. Actually it's the AVR ISR macro itself...
    I just pasted the macro from avr-libc to mk20dx128.h (just before the isr functions prototypes):
    ...
    If I put that into main.cpp and try to compile it with a Makefile, I get warnings that the attributes are ignored:
    Code:
    main.cpp:44:1: warning: 'signal' attribute directive ignored [-Wattributes]
    main.cpp:44:1: warning: '__INTR_ATTRS' attribute directive ignored [-Wattributes]
    If I compile your ISR(ftm0_vect) code inside of Arduino, I don't see the warnings, but I do if I turn on Arduino/Preferences/Show verbose output during compilation, then they do.

    From http://gcc.gnu.org/onlinedocs/gcc/Fu...ttributes.html it looks like __attribute signal is AVR only, and __attribute ((interrupt)) or __attribute((isr)) might work.

    This gives no warnings, whether it is in the mk20dx128.h file, in a sketch, or in the main.cpp file:

    Code:
    #if (__GNUC__ == 4 && __GNUC_MINOR__ >= 1) || (__GNUC__ > 4)
    #  define __INTR_ATTRS used, externally_visible
    #else /* GCC < 4.1 */
    #  define __INTR_ATTRS used
    #endif
    
    #ifndef ISR
    # ifdef __cplusplus
    #  define ISR(vector, ...)            \
        extern "C" void vector (void) __attribute__ ((interrupt,__INTR_ATTRS)) __VA_ARGS__; \
        void vector (void)
    # else
    #  define ISR(vector, ...)            \
        void vector (void) __attribute__ ((interrupt,__INTR_ATTRS)) __VA_ARGS__; \
        void vector (void)
    # endif
    #endif
    But I'm not sure about this. How do I tell how the code is managing cli()/sei() or what? From the GNU document it looks like AVRs cli() on interrupt, __attribute ((interrupt )) would cli, but the __attribute ((signal)) would not. It is unclear to me how __attribute ((interrupt)) acts on an ARM? Does cli before and sei after?

    What does a good ISR look like? With or without the ISR macro?
    Last edited by Dave X; 03-16-2013 at 03:13 AM. Reason: typo

  3. #3
    Senior Member PaulStoffregen's Avatar
    Join Date
    Nov 2012
    Posts
    28,460
    Quote Originally Posted by Dave X View Post
    How do I tell how the code is managing cli()/sei() or what? From the GNU document it looks like AVRs cli() on interrupt, __attribute ((interrupt )) would cli, but the __attribute ((signal)) would not. It is unclear to me how __attribute ((interrupt)) acts on an ARM? Does cli before and sei after?

    What does a good ISR look like? With or without the ISR macro?
    I'm pretty sure all that stuff makes absolutely no difference. On ARM, that interrupt stuff is managed by hardware.

    It may be hard to believe if you're used to AVR, but truly, all you do on Teensy 3.0 is write an ordinary function using the right name and it automatically works.

    On AVR, interrupt functions are special. The compiler generates a special epilogue and prologue that saves and restores any registers. The prologue has the special RETI instruction. Those extra options to ISR(name, options) cause it to generate that epilogue and prologue code differently, possibly enabling interrupts while the interrupt function runs.

    ARM does far more in hardware. In fact, ARM does so much that interrupt functions need no special code at all. There's simply no need for an ISR() macro. Just an ordinary function is used. The only thing that makes it an interrupt is the address is stored in the vector table. Teensy's core library defines those names as weak symbols, so all you have to do is write a function with the right name and it automatically becomes an interrupt function, because the linker puts your function's address into that table.

    Very smart people at ARM put a lot of thought and good design into the Cortex interrupt system. The ARM Cortex processor handles interrupts with a lot of special work, and the Nested Interrupt Vector Controller (NVIC) controls which interrupts can run.

    When an interrupt happens, the processor automatically saves the registers onto the stack. It also fetches the interrupt vector address, and then begins fetching code from that address. The ARM core has separate buses to simultaneously access the RAM and Flash, so the code is fetched while the registers are being saved. The saved return address includes a special value which is recognized by the ordinary return instruction, so the compiler can generate ordinary code and it will automatically return properly to whatever was interrupted. Because the hardware handles saving and restoring registers, it does a crafty "tail chain" optimization if another interrupt is pending, where it jumps pretty much from the end of one interrupt to the beginning of the next without needlessly restoring and resaving the registers.

    You can globally disable and enable interrupts on ARM. Teensy implements the AVR names cli(), sei() and Arduino names noInterrupts() and interrupts() which do this. But unlike AVR, the state of interrupt enable/disable is not the only thing controls when your interrupt can run. The global interrupt state is not automatically disabled when your interrupt code runs, because that's not how the NVIC manages things.....

    The NVIC implements interrupt priority levels, from 0 to 255 (however, only the upper 4 bits are implemented for 16 different levels, so 0-15 are the same, 16-31 are the same, and so on). The lower the number, the higher the priority. By default, all interrupts are priority 0, which means no other interrupt is allowed until it returns. The NVIC keeps track of the current priority level as interrupts are run and when they return. So it doesn't matter if you use sei() inside your interrupt routine (or an ISR macro which puts a SEI instruction early in the function epilogue). The NVIC doesn't allow another interrupt of the same or lower priority until your function returns. If you want to use this feature to allow some interrupts to interrupt others, instead of fiddling with state at run-time, you just assign each interrupt a priority ahead of time. The NVIC and ARM hardware automatically manages everything.

    So the result is you can just use the names defined in that table as ordinary functions, without a special ISR macro, because the ARM Cortex processor doesn't need any of that special stuff that's done on AVR.

    I had originally thought about trying to incorporate some sort of emulation of AVR interrupts, with the exact names defined for Arduino Uno's chip. But that turned out to be impractical for many reasons. Instead, I'm focusing my efforts to simply port those libraries to native code. Many of them are already ported. I have a list of others I intend to port, based on the IntervalTimer library (which will be included in Teensyduino's core library soon) rather than directly embedded interrupt code.

    I have no plans to ever include an ISR() macro in Teensyduino, because it isn't necessary for ARM code and properly emulating every aspect of the AVR hardware just isn't very practical. However, I could do more to document all this stuff. Hopefully this message is a good start?
    Last edited by PaulStoffregen; 03-17-2013 at 07:40 AM.

  4. #4
    Thanks. That helps a lot. I'm sort of messing with porting some ISR()-using code, and I'm happy to not have to mess with it.

    Maybe pasting your message on http://www.pjrc.com/teensy/interrupts.html for Teensy 3.0 would be nice.

  5. #5
    i am trying to make DMX code that i used on a maple mini (ARM) work on the teensy3.

    every time data comes in on the serial/usart pin this interrupt gets called and data is written to an array.
    hwo would i do this now on a teensy3, since there is no ISR macro ?

    Code:
    extern "C" {
      void __irq_usart1(void) {
        unsigned char status = USART1_BASE->SR;
        data = USART1_BASE->DR;
    
        //onboard LED at D33 get toggled when timer is called
        //gpio_toggle_bit(LED_PORT, LED_BIT);
    
        switch (dmx_state)
        {
        case DMX_IDLE:
          if (status & 1<<USART_SR_FE_BIT)
          {
            dmx_addr = 0;
            dmx_state = DMX_BREAK;
           // update = 1;
            //delayMicroseconds(8);
          }
          break;
    
        case DMX_BREAK:
          if (data == 0)
          {
            dmx_state = DMX_START;
          }
          else
          {
            dmx_state = DMX_IDLE;
          }
          break;
    
        case DMX_START:
          dmx_addr++;
          /*
          if(printTestStage == 1){
    
            dmx_test_data[dmx_addr] = data;
    //        SerialUSB.print(dmx_addr);
    //        SerialUSB.print(" , ");
    //        SerialUSB.println(data);
          }
          */
          if (dmx_addr == dmx_start_addr)
          {
            chan_cnt = 0;
            dmx_data[chan_cnt++] = data; //constrain(data,0,maxDMXvalue); //data;
            dmx_state = DMX_RUN;
          }
          break;
    
        case DMX_RUN:
          //for (int wasteTime =0; wasteTime <2; wasteTime++) {}
          dmx_data[chan_cnt++] = data; //constrain(data,0,maxDMXvalue); //data;
          if (chan_cnt >= DMX_NUM_CHANNELS)
          {
           // printTestStage = 2;
            dmx_state = DMX_IDLE;
          }
          break;
    
        default:
          dmx_state = DMX_IDLE;
          break;
        }
      }
    }

  6. #6
    Senior Member PaulStoffregen's Avatar
    Join Date
    Nov 2012
    Posts
    28,460
    Look at the serial code in hardware/teensy/cores/teensy3/serial1.c, or the simpler non-fifo version in serial2.c & serial3.c. You'll find the interrupt code in those files.

    For receiving DMX, you might also look at this message:

    http://forum.pjrc.com/threads/19662-...ll=1#post24993

Tags for this Thread

Posting Permissions

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