Could there be something like an ISR template function?

You are describing the corner-case where you don't have enough memory to keep a SPI class instantiated
No, I didn't say or mean to imply it's a way to reduce RAM usage, though for AVR users that's an ever-present challenge.

I was addressing the problem of "time sharing" a resource. An SPI port can be time shared as each use has a different SS pin. But as has been debated here, there's no means for mutual exclusion for this among multi-authored code/libraries.
 
After a little research, i had success.

Try this... (Teensy 3.1)

Code:
//See http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0553a/Ciheijba.html

#define _vectorscount 111

void (* _vectorsRAM[_vectorscount])(void)   __attribute__ ((aligned (512))); //Not sure about the align

const int ledPin = 13;

//Our new ISR
void my_new_systick_isr(void)
{
	systick_millis_count+=8;
}

void setup() {
  Serial.begin(9600);
  delay(1000);

  pinMode(ledPin, OUTPUT);
 
  // Copy Vector Table to RAM
  uint32_t *src;
  uint32_t *dst = (uint32_t*) &_vectorsRAM[0];
  int i=0;
  while (i++ < _vectorscount) *dst++ = *src++;

  //Switch to RAM 
  SCB_VTOR = (uint32_t) &_vectorsRAM[0];
  
  //Assign new systick
  _vectorsRAM[15] = &my_new_systick_isr;

}


void loop() {
  digitalWrite(ledPin, HIGH);   // set the LED on
  delay(500);
  digitalWrite(ledPin, LOW);    // set the LED off
  delay(500);
}

and smile :)

No runtime cost.
 

Attachments

  • vectortable.ino
    247 bytes · Views: 148
Last edited:
What i do not understand from the link in my example:
"For example, if you require 21 interrupts, the alignment must be on a 64-word boundary because the required table size is 37 words, and the next power of two is 64."
Why do we need 37 words for 21 Interrupts ? Is this correct ?
 
I also don't quite understand that phrase. Later on they say that the required alignment depends on the chip implementation, but I can't find SCB_VTOR in the datasheet. Where is that? However, if 512 works then we might get away with it.
 
Document K20P64M72SF1RM (aka the mk20dx256 datasheet), Page 63, Table 3-4. There are 16 Core System Handler Vectors placed in front of the interrupts (IRQs), which start at vector 16 (IRQ0). Maybe that's what they're talking about. The mk20dx256 has 101 vectors. As I understand it, we need 128 word alignment for those, which is - in a 32-bit-word-world - 512 bytes.
 
ah... this makes sense.
Then I have guessed correctly.

Edit: Was stellen denn die Hamburger da gerade an ? (Fussball)
 
After a little research, i had success.
...
No runtime cost.

I'm going to try this later, and if it works NICE!

I'm definitely going to try this in swapping in my own pin-IRQ functions.

So getting back to the semi-original topic, if the DMA lib was using something like this, it could both assign a DMA channel and at the same time swap the handler into the vector table. It could presumably also disconnect a handler. This is really cool .... of course, now I have a load of reading to do.
 
Allocating DMA channels wouldn't even be that hard. A getDmaChannel function would take a void (*f)(void) and return a DMA channel number, or -1 if none is available (that's what I would do if I was to write an interface in C). If a channel could be allocated, the function passed as an argument would be the new ISR. Giving back a DMA channel would be done with returnDmaChannel(int channel). The hardest part is keeping track of channels. bools don't scale well, and I wouldn't like wasting too much memory for that.
 
Missing an important little bit:

Code:
  // Copy Vector Table to RAM
  uint32_t *src[COLOR=#ff0000] = (uint32_t*) SCB_VTOR[/COLOR];
  uint32_t *dst = (uint32_t*) &_vectorsRAM[0];
  int i=0;
  while (i++ < _vectorscount) *dst++ = *src++;

  //Switch to RAM 
  SCB_VTOR = (uint32_t) &_vectorsRAM[0];

I prefixed the setup() on one of my sketches with this and everything seemed to run fine. Ah, the havoc one can cause with this...
 
Missing an important little bit
You're right,it is a bit "quick and dirty". The adress is 0, so it worked..

I think, a good place for the table is in the core (optional ?) so it can be used by different libs at the same time because we can only have one such table.
One could write a new headerfile for it, with nice definitions for the interrupt-numbers.
What do you think ?
 
I just found, that all numbers are already defined in mk20dx128.h at line 2020ff.
We have to add an offset of 16: _vectorsRAM[IRQ_DMA_CH0 + 16] = &yourgreatdmafunc;
We know the number of interrupts too: NVIC_NUM_INTERRUPTS (again, add 16)

This should work for Teensy 3.0 & 3.1 without waste of ram for 3.0 :
Code:
//Blink faster than expected for delay(500)

//See http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0553a/Ciheijba.html


#define _NUM_INTERRUPTS (NVIC_NUM_INTERRUPTS + 16)
#define IRQ_SYSTICK 15

void (* _vectorsRAM[_NUM_INTERRUPTS])(void)   __attribute__ ((aligned (512))); //on teensy 3.0, we can use align(256)

const int ledPin = 13;

//Our new ISR
void my_new_systick_isr(void)
{
	systick_millis_count+=8; //make it faster :-)
}

void setup() {
  pinMode(ledPin, OUTPUT);
 
  // Copy Vector Table to RAM
  uint32_t *src = (uint32_t*) SCB_VTOR; 
  uint32_t *dst = (uint32_t*) &_vectorsRAM[0];
  int i=0;
  while (i++ < _NUM_INTERRUPTS) *dst++ = *src++;

  //Switch to RAM 
  SCB_VTOR = (uint32_t) &_vectorsRAM[0];
  
  //Assign new systick
  _vectorsRAM[IRQ_SYSTICK] = &my_new_systick_isr;
}

void loop() {
  digitalWrite(ledPin, HIGH);   // set the LED on
  delay(500);
  digitalWrite(ledPin, LOW);    // set the LED off
  delay(500);
}
 
Yeah, I was working on the code right now and noticed that.
I'm working on some code to control the DMA, something like
DMAControl class, with
DMAControl dmaControl;
int8_t dmaControl.getChannel(void (*f)(void) );
void dmaControl.returnChannel(uint8_t channel );
 
I've created a library for this:
https://github.com/pedvide/DMAControl
The usage is simple. The core library should create an instance of this class, for example
Code:
DMAControl dmaControl;
And then every library that need to use a DMA channel should ask:
Code:
int8_t dma_Channel = dmaControl.getChannel(&my_new_isr);
Where:
Code:
void my_new_isr(void) {
// your code here
}
and dma_Channel returns the channel number that was assigned or -1 if all are in use.
To return a channel:
Code:
returnChannel(channel);


I have an example in the examples folder using my ADC library, but I still haven't pushed the DMA stuff to GitHub, so use it as a "framework", it won't compile even if you have the ADC library.

See that this library doesn't check that the channels are in fact in use or not, if a "rogue" library uses channels without asking for them things will happen.

I've tested it in Teensy 3.0 and it works perfectly, but in Teensy 3.1 I can only use the channels 0-3. I've tried to use the rest of the channels even without this library, just to see if they worked, and I can't. Has someone use dma channels higher than 3?
 
I've tested it in Teensy 3.0 and it works perfectly, but in Teensy 3.1 I can only use the channels 0-3. I've tried to use the rest of the channels even without this library, just to see if they worked, and I can't. Has someone use dma channels higher than 3?

I have not yet used channels 4 and above, but the chip has them if I'm reading the datasheet correctly. In your library example, you are using the ADC and let it configure DMA as it sees fit. How is that implemented? If it uses any macros for setting DMA channel attributes, those macros might mask channels 4 and above if they are written for the Teensy 3.0.
 
I also though that the ADC implementation was wrong, so I tried to setup a simple transfer with channel 7 by itself (no ADC library or anything) and I couldn't make it work! The same code works fine with channel 3.
Of course it's probably a bug in my code somewhere, but I wanted to make sure that somebody was actually able to use those channels.
 
Could it be something with the mux ?

20.4.1 DMA channels with periodic triggering capability
Besides the normal routing functionality, the first four channels of the DMA MUX
provide a special periodic triggering capability that can be used to provide an automatic
mechanism to transmit bytes, frames, or packets at fixed intervals without the need for
processor intervention.
 
The periodic trigger is an extra feature on channels 0..3 - that shouldn't mean that the others are useless.
 
Sorry I said that the DMA code didn't involve the ADC, it actually does (it's attached, needs the ADC library)
View attachment DMA_ADC.ino

It sets up a DMA channel when you press 's', then you read an analog value with 'c' and it adds it into a circular buffer that you can print with 'p'. This code works if I substitute all the *TDC7* registers (and the DMAMUX, NVIC) by for example *TDC3*, etc. So I don't know what's going on!
(Here is the code using channel 2: View attachment DMA_ADC_ch2.ino)
 
I think I have found it. In DMA_ADC.ino:
Code:
DMA_SERQ = DMA_SERQ_SERQ(7); // enable DMA request for channel 3
(Note that the comment lies)

From mk20dx128.h:
Code:
#define DMA_SERQ_SERQ(n)  ((uint8_t)(n & 3)<<0)  // Set Enable Request

Masking the channel number with 3 prevents enabling any of higher channels' IRQs. In the macro definition, change 3 to 15 (or, even better, add a conditional) and see if that helps.

Regards

Christoph
 
Wow, nice catch! I hadn't thought of looking there...
it seems that a few definitions are only for Teensy 3.0:
#define DMA_CEEI_CEEI(n) ((uint8_t)(n & 3)<<0)
#define DMA_SEEI_SEEI(n) ((uint8_t)(n & 3)<<0)
#define DMA_CERQ_CERQ(n) ((uint8_t)(n & 3)<<0)
#define DMA_SERQ_SERQ(n) ((uint8_t)(n & 3)<<0)
#define DMA_CDNE_CDNE(n) ((uint8_t)(n & 3)<<0)
#define DMA_SSRT_SSRT(n) ((uint8_t)(n & 3)<<0)
#define DMA_CERR_CERR(n) ((uint8_t)(n & 3)<<0)
#define DMA_CINT_CINT(n) ((uint8_t)(n & 3)<<0)

All mask the number with 3, I'll change them and try again when I get back home (but it's so sunny outside!)
 
Let's pull those macros out of this thread and post a bug report for Paul to find. I also suggest replacing the constant 3 by a macro DMA_NUM_CHANNELS or NUM_DMA_CHANNELS that can be #defined for each MCU. That way we have only one macro like that DMA_CEEI_CEEI() that internally uses the correct chip definitions.

I'm sure similar issues will pop up when the Teensy++3 comes.
 
Back
Top