PDA

View Full Version : AVR-like ISR()



BBenj
02-03-2013, 06:00 PM
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... :p
I just pasted the macro from avr-libc to mk20dx128.h (just before the isr functions prototypes):


#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:


ISR(ftm0_isr)
{
FTM0_SC &= ~FTM_SC_TOF;
digitalWrite(13, state ^= HIGH);
}

Tested successfully with ftm0 and ftm1. :cool:

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:


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

Dave X
03-16-2013, 01:27 AM
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... :p
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:

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/Function-Attributes.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:



#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?

PaulStoffregen
03-17-2013, 08:25 AM
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?

Dave X
03-18-2013, 03:04 AM
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.

stephanschulz
11-22-2013, 04:02 PM
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 ?



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;
}
}
}

PaulStoffregen
11-22-2013, 05:09 PM
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-Arduinoesque-overriding-of-core-functionality?p=24993&viewfull=1#post24993