Use of multiple SPI Channels and global instantiation

RogerClark
Fri Jun 05, 2015 11:39 am
Guys,

I was looking at the VS1003 lib and thought it would be good to try to playback and MP3 from the SD card, but rather than running both the SD and the VS1003 MP3 player on the same SPI, I thought I’d change the VS1003 lib so that I can add an extra argument to the constructor for the SPI device / Class instance, which is defaulted to SPI

However, I ram into 2 issues

I’m not keen on the style of code here, either,… run with me on this one.

First add an argument for the _spi and default it to SPI

VS1003( uint8_t _cs_pin, uint8_t _dcs_pin, uint8_t _dreq_pin, uint8_t _reset_pin, SPIClass _spi = SPI);

then set the member my_SPI with this in the constructor.

VS1003::VS1003( uint8_t _cs_pin, uint8_t _dcs_pin, uint8_t _dreq_pin, uint8_t _reset_pin, SPIClass _spiChan):
cs_pin(_cs_pin), dcs_pin(_dcs_pin), dreq_pin(_dreq_pin), reset_pin(_reset_pin), my_SPI(_spiChan)
{
}


victor_pv
Fri Jun 05, 2015 12:00 pm
Roger, I think the constructor problem in this case could have a solution, but I don’t like it.
You could set the priority of the SPI constructor to a lower number (higher priority) than the VS1003 library constructor.
But that means having to change both libraries to add priority numbers.
If we start down that path, of assigning different priorities to each constructor, we may end up with too many of those and then having to go checking libraries to find what has a higher priority than something else.
Either we carefully go around assigning priorities to everything depending on what depends on what else, or we better don’t try to use devices in the constructor.
That is just my opinion. Now, I am sure that if you set a priority to those 2 making the VS the lowest priority one, that should solve the problem for this case. I just don’t like doing it.

Besides that, another option could be to change the VS library constructor, so it instantiates the SPI ports itself before trying to do anything else


RogerClark
Fri Jun 05, 2015 12:14 pm
Victor

I’d rather not go down the route of assigning priorities either.

I will find another way.

At the moment I’m not sure whether my_SPI is null, when the constructor of the VS1003 is called, or whether its something inside SPI that is not valid.

Thinking about it, I’m not even sure what the compiler would have done when I assigned my_SPI = SPI

I.e

I assumed that my_SPI is effectively a pointer to the class object, as is SPI, But I’m a bit hazy on this, as normally if I was not coding for Arduino I would always use new to instantiate a class, which returns pointer to a class, which makes everything a lot more obvious.

Btw, there is also something else odd going on, which hangs the compiler

If a lib needs SPI, it can include SPI.h in its header or cpp, well it seems to need to do this, but if I omit including SPI.h from the sketch, the compiler seems to hang when compiling the lib in question.
I presume this is an Arduino quirk, but it seems odd to hang the compiler.


victor_pv
Fri Jun 05, 2015 1:47 pm
RogerClark wrote:

If a lib needs SPI, it can include SPI.h in its header or cpp, well it seems to need to do this, but if I omit including SPI.h from the sketch, the compiler seems to hang when compiling the lib in question.
I presume this is an Arduino quirk, but it seems odd to hang the compiler.

Rick Kimball
Fri Jun 05, 2015 4:03 pm
You could try placement new. I took my ActiveLowLED class and used it with placement new in the code below. This class does pinMode() calls in its constructor. If you used placement new as I have done below, the constructor isn’t called until the first led().blink() call. Of course there is overhead with this and it uses strange syntax to access the led object. The one advantage to this, is that the memory associated with the class is part of the data segment not the heap so you aren’t going to run into memory managment issues of not having space. If it doesn’t fit, when you compile it will tell you.

/*
* ActiveLowBlink - example of using class and inline new
*
*/
#include "ActiveLowLED.h"

inline void* operator new( size_t sz, void* here ) { return here; }

ActiveLowLED &led() {
static char _ActiveLowLED_mem[sizeof(ActiveLowLED)];
static ActiveLowLED *ptr;
if ( !ptr ) {
ptr = new ((void *)_ActiveLowLED_mem) ActiveLowLED(BOARD_LED_PIN,false);
}

return *ptr;
}

void setup() {
// led is initialized in first blink, no setup needed
}

void loop() {
// 2 long blinks
led().blink(50,950);
led().blink(50,950);
delay(1000);

// 4 short blinks
int x = 4;
do {
led().blink(50,450);
} while(--x);
delay(1000);
}


mrburnette
Fri Jun 05, 2015 4:55 pm
The one advantage to this, is that the memory associated with the class is part of the data segment not the heap so you aren’t going to run into memory managment issues of not having space.

Very, very interesting!

Ray


martinayotte
Fri Jun 05, 2015 5:45 pm
mrburnette wrote:Very, very interesting!

Rick Kimball
Fri Jun 05, 2015 7:35 pm
Actually I take that back, It ends up in the .bss section the way I have that declared. Which is still better than the heap.

RogerClark
Fri Jun 05, 2015 9:43 pm
IMHO , I think we need to try to educate people into not using global instantiation of vars.

I can understand why the Arduno team originally did this, but as the capabilities of all devices get more complex, we end up with more and more hacks and work arounds.

I’m inclined to copy the SPI class folder and rename it SPI_STM and remove the global instantiation

Actually, I’d really rather call it something more useful but SPI_NOGLOBAL is perhaps a bit long for a name (but perhaps not)

Then if someone wanted SPI to be SPI device 2, they could declar it themselves

SPIClass SPI(2)

Though I admit, I’m not sure whether other Arduino libs that use SPI as a global would freak out.

I’d need to try it. But perhaps the extern in the SPI header may stop the other libs going mad.


mrburnette
Fri Jun 05, 2015 10:30 pm
Rick Kimball wrote:Actually I take that back, It ends up in the .bss section the way I have that declared. Which is still better than the heap.

RogerClark
Sat Jun 06, 2015 12:57 am
Just a quick update.

If instantiate the VS1003 class inside setup, it works fine on SPI channel 1 and SPI channel 2.

SPIClass *SPI_2;
void setup () {
Serial.begin(115200);

SPI_2 = new SPIClass(2);

Serial.println("VS1003 test");
player= new VS1003(PC14, PB10, PA8, PA9,*SPI_2); // cs_pin, dcs_pin, dreq_pin, reset_pin

player->begin();
player->modeSwitch(); //Change mode from MIDI to MP3 decoding (Vassilis Serasidis).

// set maximum output volume
player->setVolume(0x00);
}

void loop() {
// play hellomp3 flow each 0.5s ;)
player->playChunk(HelloMP3, sizeof(HelloMP3));
delay(500);
}


RogerClark
Sat Jun 06, 2015 1:21 am
OK
I think I have a workaround for this, but I admit I’m not sure why this works.

If I use this as the declaration for the constructor, it doesn’t work

VS1003( uint8_t _cs_pin, uint8_t _dcs_pin, uint8_t _dreq_pin, uint8_t _reset_pin, SPIClass _spi = SPI);


Leave a Reply

Your email address will not be published. Required fields are marked *