Teensy 3.0 and interrupts

Status
Not open for further replies.
Actually these lines:

#define startPITPeriod(n, period) {cli(); PIT_LDVAL(n) = F_BUS * float(period); PIT_TCTRL(n) = 3; NVIC_ENABLE_IRQ(IRQ_PIT_CH(n)); sei();}
#define startPITFreq(n, freq) {cli(); PIT_LDVAL(n) = F_BUS / (freq); PIT_TCTRL(n) = 3; NVIC_ENABLE_IRQ(IRQ_PIT_CH(n)); sei();}

should be:
#define startPITPeriod(n, period) {cli(); PIT_LDVAL(n) = F_BUS * float(period)-1; PIT_TCTRL(n) = 3; NVIC_ENABLE_IRQ(IRQ_PIT_CH(n)); sei();}
#define startPITFreq(n, freq) {cli(); PIT_LDVAL(n) = F_BUS / (freq)-1; PIT_TCTRL(n) = 3; NVIC_ENABLE_IRQ(IRQ_PIT_CH(n)); sei();}


The -1 is because the counter counts to 0 from the load value. This is only important it you have high frequency interrupts; you can prove it by setting a PIT at (say) 1 ms (==48000), and printing ( micros() % 1000) in it -- the result will creep up because you will be actually incrementing (delaying) by 48001 cycles instead of the expected 48000
 
I've tested and updated the PITimer library, and posted a new thread about it. The library accounts for the off-by-one issue mentioned by Jp3141, and provides a bunch of other nice functionality as well. -Dan
 
FTM and interrupts.

Thanks Jakob and Pete for the nice demo program and patch for mk20dx128.h. This works nicely.

I think the mk20dx128.c & mk20dx128.h files now have the appropriate lines to let you set up your own interrupts without modification . The only one that isn't predefined as a weak alias to 'unused_isr' is systick_isr, which is a a weak alias to systick_default_isr. You replace these weak aliases with your own code by defining a new handler function.

My Mac's /Applications/Arduino.app/Contents/Resources/Java/hardware/teensy/cores/teensy3/mk20dx128.h is
Code:
void (* const gVectors[])(void) =
{
        (void (*)(void))((unsigned long)&_estack),      //  0 ARM: Initial Stack Pointer
        ResetHandler,                                   //  1 ARM: Initial Program Counter
        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
};

I'm interested in using a void ftm1_isr(void) in "Output Compare Mode" as a non-periodic timer based on this bit:
Using the Output Compare mode is possible with (ELSnB:ELSnA = 0:0). In this case, when the counter reaches the value in the CnV register, the CHnF bit is set and the channel (n) interrupt is generated if CHnIE = 1, however the channel (n) output is not modified and controlled by FTM. -- user guide page 752

By default, the _init_Teensyduino_internal_() function in pins_teensy.c sets up FTM1 to enable PWM on pins 3 & 4 with a 488.28 Hz frequency with:

Code:
        FTM1_CNT = 0;
        FTM1_MOD = DEFAULT_FTM_MOD;
        FTM1_C0SC = 0x28;
        FTM1_C1SC = 0x28;
        FTM1_SC = FTM_SC_CLKS(1) | FTM_SC_PS(DEFAULT_FTM_PRESCALE);

The FTM1_C1SC = 0x28; sets FTM1 to be Edge-aligned PWM, High-true, clear channel 1 on match.

analogWrite() sets up the value and the output pin 4 MUX for FTM1_C1SC PWM control with:

Code:
...
          case 4: // PTA13, FTM1_CH1
                FTM1_C1V = cval;
                CORE_PIN4_CONFIG = PORT_PCR_MUX(3) | PORT_PCR_DSE | PORT_PCR_SRE;
                break;


So, looking at all of that, here's an example of using FTM1 with interrupts:

Code:
/* testing FTM interrupts on Teensy 3.0 as arduino

*/


// Pin for LED
#define LEDPIN 13


//Constants for bitmasks for FTM 
#define FTMx_CnSC_CHIE (1<<6) // per UG p 702
#define FTMx_CnSC_MSB (1 <<4) //
#define FTMx_CnSC_CHF (1<<7) // per UG pg 702

 
volatile int ledVal;

void timer_setup() {
//  FTM1_SC = FTM_SC_CLKS(1) | 7; // system clock, div by 2^n  
//  FTM1_MOD = 65535;
  FTM1_C1V = (FTM1_MOD & 0xff)/2;   // Halfway up the modulo
  FTM1_C1SC = FTMx_CnSC_CHIE | FTMx_CnSC_MSB; // Interupt enable, output compare mode
  NVIC_ENABLE_IRQ(IRQ_FTM1); // enable the interrupt
}
 
void setup(){
  pinMode(LEDPIN, OUTPUT);
  cli();
  timer_setup();
  sei();
  ledVal = 0;
  digitalWrite(LEDPIN, ledVal);  // Start the LED off
}

void ftm1_isr(void) { //
  FTM1_C1SC &=  ~FTMx_CnSC_CHF; // clear channel 1 interrupt
  digitalWrite(LEDPIN, ledVal ^= 1);
  FTM1_C1V = random(FTM1_MOD);
  }

#define OTHERPIN 4
void loop() {
   digitalWrite(OTHERPIN,!digitalRead(OTHERPIN));
  delayMicroseconds(500); // 1KHz square wave on pin 4
 
}
 
Last edited:
Loglow in your description.

Period: 0.000013312 to 89.478485312 seconds (14 ns to 89 s)
should be
Period: 0.00000002083( 20.83ns ) to 89.478485312 seconds ( 89.47s )

0.000013312 is 13.312us, not sure how you got this.


Also

// ------------------------------------------------------------
// this version of period() (with an argument) is used to set the
// period of the timer in terms of units of time (seconds).
// for 48 MHz bus, range is about 14 ns (0.000014) to 89 s (89.0)
// ------------------------------------------------------------
void PITimer::period(float newPeriod) {
uint32_t newValue = roundFloat(F_BUS * newPeriod) - 1;
value(newValue);
}

I don't think you could get 14ns (0.000000014) unless your running at 96Mhz.

Also with PITimer period i think it should be used as clock cycles and not time. Similar to how they use the examples at page 827 in the datasheet.
roundFloat is also confusing me.. 32bit resolution and your rounding it?

Maybe create a Pitimer Cycles function?
 
Last edited:
This code should fire 2 pins every 0-255 clock cycles. (5.31249uS)
Instead im seeing it fire every 13.3uS
Code:
#include "PITimer.h"
#define BLANK 4
#define XLAT 5




void setup() {
PITimer1.period(0.000005312499999);
pinMode(BLANK, OUTPUT);
pinMode(XLAT, OUTPUT);

}
void loop() {
 PITimer1.start(pulse); 
   
}
void pulse()
{
digitalWriteFast(BLANK, HIGH);
digitalWriteFast(XLAT, HIGH);
digitalWriteFast(XLAT, LOW);
digitalWriteFast(BLANK, LOW); 
}

This was taken with my Open Bench Logic Sniffer with a resolution of 5nS
05.jpg

Changing it to 1023 cycles or 21.3125uS ends up almost spot on at 21.3uS every time. Just call me picky but I want more resolution under 13uS :rolleyes:
Code:
PITimer1.period(0.0000213125);

06.jpg


Other then that this looks to be a very nice library, will Paul be including this library at some time?
 
Last edited:
It looks interesting linuxgeek.
Its maximum resolution appears to be 48 clock cycles or about 1us.
If you try setting it to anything under 1 it does nothing. Making 1us your minimum.
 
For simplicity, it's done that way but you could change it.

I believe I remember that the clock (48MHz) was divideded by 1 million, which is why you can only go down to 48 clock cycles.
You could change that line to dividie by 48 million and you would have clock cycles.
 
If you try setting it to anything under 1 it does nothing. Making 1us your minimum.

Are you really going to execute more than 1 million interrupts per second?

The ARM Cortex-M4 takes at least 12 cycles to respond to the interrupt, and 12 more to return to your program (plus maybe a few more depending on flash cache misses). Inside the interrupt, you have to at least manipulate the PIT registers, which involves a few instructions to initialize registers, and at least one write which goes over the peripheral bridge, adding a cycle or two of bus time. Then your code has to actually DO something.....

If you're intending to design a project involving so many interrupts per second, perhaps it would be better to start a new thread to discuss what you're trying to accomplish and ideas for using the on-chip hardware (timers, DMA, etc) to handle such incredibly high speeds without excessive CPU overhead?
 
Im not looking for anything even close to one million interrupts per second, im looking for the accuracy to stay within 100ns of a project design. If a project is running at say 7Khz(142.857us), and updating that timing data every say 576 project cycles as an example.

So rounding up gives 143us * 576 = 82.368ms, adding a whole 116us to the timing.

and if rounding down 142us * 576 = 81.792ms, taking a whole 460us from the timing.

but if looking for say 100ns accuracy 142.8us * 576 = 82.252ms.... which is still 32.9us off but closer then rounding to within 1us with the current code.

For my project at least I think I will look into using counters, to get the number of clock cycles and set the PIT that way. I will probably change the IntervalTimer library here.
Code:
  // check range and calc value based on period
  if (newPeriod == 0 || newPeriod > MAX_PERIOD) return false;
  uint32_t newValue = F_BUS * (newPeriod / 1000000.0) - 1;

to

Code:
  // check range and calc value based on period
  if (newPeriod == 0 || newPeriod > MAX_PERIOD) return false;
  uint32_t newValue = newPeriod - 1;

Which is closer to the examples used in the datasheet for setting timing based on FBUS clock cycles.

And set newPeriod based on subtracting the clock values between two 576 cycle periods.

Im not looking to cause headaches Paul, althou I probably am anyway.
 
Im not looking for anything even close to one million interrupts per second, im looking for the accuracy to stay within 100ns of a project design. If a project is running at say 7Khz(142.857us), and updating that timing data every say 576 project cycles as an example.

I'm working on a modification to IntervalTimer (as it will appear in Teensyduino 1.14) to accept either integer or float values for the microseconds. So you'll be able to use "mytimer.begin(myfunction, 142.857);" and have it produce at accurate 7 kHz rate.

I'm also working on having it do the calc within an inline function, so for a numeric constant, the compiler will do the float math at compile time and embed the actual integer cycles number into the executable image.
 
That sounds good. More accuracy is always helpful.
Most likely for my main project it will be adjusting the frequency based on the rotation speed. So it wont be a constant value, it will be changing.
But my early testing will be using a constant value, and maybe a few other projects I have been looking at.
I still have a ways to go, but I do have some TLC5940 test boards on the way. I will keep working on my code and using my logic sniffer to see my failures in all their glory.
TLC5940_no_uController_4(1).jpg
 
I need help with another type of interrupt.
This is the interrupt that is called by SPI transfers.

It uses these registers for configuration (I think):
• NVICISER0
• NVICICER0
• NVICISPR0
• NVICICPR0
• NVICIABR0
• NVICIPR3

Can anyone provide some help on this? I haven't been able to find info on these registers.
Here's my basic code:

Code:
void setup(){
NVIC_ENABLE_IRQ(12); //SPI is IRQ 12
//NVIC_SET_PENDING(12);
//NVIC_CLEAR_PENDING(12);
//__enable_irq();
}
Code:
void spi0_isr(void){
data = SPI0_POPR;
packetsReceived++;
}
 
Hi, my first post here. I still can't get an intervalTimer to trigger an interrupt on Teensy 3.0. I had timer interrupts working in an earlier Arduino/Teensyduino install, but no more. Here is a stripped down sketch I think should work:

void smpltimerisr(void);
const int ledPin = 13; // Teensy3 has LED on 13
volatile unsigned long smplidx = 0;

void setup(){
pinMode(ledPin, OUTPUT);
IntervalTimer smpltimer;
smplidx = 0;
smpltimer.begin(smpltimerisr, 1000000);
}
void loop(){
if(smplidx == 0)
digitalWrite(ledPin, HIGH);
else
digitalWrite(ledPin, LOW);
}

void smpltimerisr(void)
{
smplidx++;
}

//The above compiles fine, loads and executes, but the LED stays ON, indicating no interrupts active; any ideas?
 
Hi
I m new here. I receive today my first Teensy 3.0, the first time I use an ARM.In the past I had very good experience with Arduino, MicroChip and other 8-bit Microcontrtoller, but for the ARM this is the first time.
Rigth now I have problems with interrupts, since I didn t find any example or tutorial to use interrupts with an ARM. For my porpouse I need a Timer interrupt und a raising/falling interrupt (to read some PWM signals from an RX receiver). Since I dont know really how to start, could you me please send me a simple example? The link provided by Paul is directly from ARM, but it is not clear to me.

Thanks in advance and gratulations for your products!!
Regards
Dave
 
Hello,

I am working on the OV7670 camera and would like to capture the output byte of it via teensy 3.0 port C. Each byte is shifted from the camera when PCLK(pixel clock) rise up.
I am trying to use interrupts on port C (or other anyway) to detect PCLK and start an interrupt which would implement GPIOC_PDIR to capture the byte on port C.

I used attachInterrupts before, but for some speed reasons, I would like to use directly portc_isr in mk20dx128.c .
I am working under visual micro in Visual Studio 2012. When I uncomment the lines concerning NVIC register, I think the program freeze or there's an infinite loop somewhere because I can't see my serial.println in my loop() {} on serial windows.
Furthermore, to check if the interrupt was working, I wrote "GPIOC_PSOR = (1 << 5);" in the unsused_isr() in mk20dx128.c, but the teensy leds neither seems to light up once the program is downloaded. (no errors in the program compilation & uploading)

void setup()
{

NVIC_ICER1 |= (1<<10); // 42 mod 32
NVIC_ISER1 |= (1<<10);
NVIC_ENABLE_IRQ(IRQ_PORTC); // Interrupt N°42

PORTA_PCR7 = PORT_PCR_MUX(0x1); // GPIO is alt1 function for this pin
PORTA_PCR7 = PORT_PCR_IRQC(0x9); // rising edge interrupts (on portC 5=> teensy3 pin 12)

//__enable_irq(); IS THIS REQUIRED ?

}


Thanks so much for any help, and sorry for my english, I'm actually a french student. =D

firejoss.
 
Status
Not open for further replies.
Back
Top