Project: SPI_MSTransfer

Would it be so different to dump an entire queue into a another queue just to read in cycles when you'll run the events() in loops anyways? Either way, the CS line has to be reasserted to call the queue for whatevers in there, so with or without the queue you'll be reasserting the line till the slave dequeues everything, which doesnt make sense to have a callback on master, no?

Oh yeah! 2-way callback transmitting with led blinking @ 20 ms... flyyyyyyyyyyyyyyyyyyyyy!!! lol

I also added a queue limiter for queue waiting for the master to pull in. if the master doesnt call events, the queues will limit on slave to 32 max.
 
Another crazy test I just did. 20ms wild loops on master and 20ms wild transfer loops on slave.

https://www.youtube.com/watch?v=zjedZRxpNdw

It's a teensy battle! slave queuing up a 20ms storm while the master runs its own 20ms loop! :D


I also fixed a single onTransfer() for both master & slave, silly, but it works :)

Code:
void SPI_MSTransfer::onTransfer(_slave_handler_ptr handler){
  if ( _master_access ) _master_handler = handler;
  if ( _slave_access ) _slave_handler = handler;
}

both pointers seem to work in the same function, so mightes well abuse :)
 
Last edited:
Hello version 0.0.3 !! :D

1) slave -> master callback implemented. set your callback the same way you set it on the slave. Queues are stored on slave, master must run events() to capture it. Slave queue limit is set at 32 max if no answer from master.
2) got rid of unecessary extra for loops
3) changed buffer back to 1024 and MHz still set to 24000000.

View attachment SPI_MSTransfer_0_0_3.zip

12 hours of callback work on both ends and another 3 hours before i goto work yay! Time to hit up some coffee soon, I'll prolly be chatting from my phone :D

Sorry defragster, I didnt add a F&F yet for you, tomorrow is another day :) I'll have to hook it before it enters the CRC routine by a different header ID and it'll do the crc check later before the callback is fired if it passes
and it may be implemented by an overload as well

something like:
Code:
 virtual uint16_t  transfer16(uint16_t *buffer, uint16_t length, uint16_t packetID, bool fire_and_forget = 0);

so instead of:
Code:
teensy_gpio.transfer16(buf, sizeof(buf) / 2, 55));
you can run this to enable it:
Code:
teensy_gpio.transfer16(buf, sizeof(buf) / 2, 55,1));
 
Last edited:
Good, downloaded and will give it a look.

Were the extra for loops the recalculating the chksum? I saw it calc chksum as it went - then looped over it again before sending to over write that chksum.
 
yeah i combined them, 1 run better than 2 :)

previously it buffered to a local array then checksummed, now it does both one shot

i hope my coding style of compaction doesnt gross you out, i use wordpad *wink*
 
Last edited:
Indeed - doing it twice might be helpful for debugging - if one way was faster but less clear to start, and they were compared.

I wouldn't say gross ... but it is ... different :) I like to save newlines sometimes - but that is a bit much :)

Made me go look for and find 'SublimeAStyleFormatter' that does normal looking formatting in SublimeText editor.

I just did it on prior SPI_MST.cpp and it did not multiline break compacted lines - like the IDE would have.

Good luck making the F&F code clean and fast - and don't forget to make a pretty sample :)

Ran out of time today to look at the MST_0_0_3 I unzipped. I was on the other thread some - it seems the impact/gain of the stuff I wanted to move to Slave was less than I thought for the float format to print. Then I turned up the speed to a mere 200/second and getting the USB smashing out that many messages makes the system a bit wonky somewhere: PC or Teensy - not sure but it gets fitful. So hooking it in could allow faster updates of more data.
 
oh i have a nice CLEAN idea for F&F, probably 1-3 lines of code, i presume we’ll go with the transfer16 overload, ill just need to swap id on overload and break out after the transfer, while the slave queues up the entire packet without verification until slave callback occurs, the slave’s callback automatically does the crc check before firing, less work for me :)
 
Last edited:
defragster, F&F implemented.

New overload with transfer16 function, using it enables fire & forget

@ 24MHz
Results went from 92uS with round trip ACK -> 77uS (without calculating CRC and ACK and RESPONSE from ISR. Capture and queue while exiting immediately saved you almost 20 micros :)
Also added deassert() before the master runs it's callback, to release the slave after pulling a queue.
 
If anyone else is willing to try this here are simple demos

Slave:

Code:
#include <SPI_MSTransfer.h>

SPI_MSTransfer slave = SPI_MSTransfer("SLAVE", "STANDALONE");

void setup() {
  Serial.begin(115200);
  slave.onTransfer(myCallback);
}
void led() {
  digitalWrite(13, !digitalRead(13));
}

void loop() {
  slave.events();
  static uint32_t _timer = millis();
  if ( millis() - _timer >= 2000 ) {
    _timer = millis();
    uint16_t buf[5] = { 1, 2, 3, 4, 5 };
    slave.transfer16(buf, 5, 55);
  }
}
void myCallback(uint16_t *buffer, uint16_t length, uint16_t packetID) {
  Serial.print("PacketID: "); Serial.println(packetID);
  Serial.print("Length: "); Serial.println(length);
  for ( uint16_t i = 0; i < length; i++ ) {
    Serial.print(buffer[i], HEX); Serial.print(" ");
  }
  Serial.println();
}


Master:
Code:
#include <SPI.h>
#include <SPI_MSTransfer.h>
SPI_MSTransfer teensy_gpio = SPI_MSTransfer("Serial", 43, &SPI2);


void setup() {
  teensy_gpio.onTransfer(myCallback);
  SPI2.begin();
}
void myCallback(uint16_t *buffer, uint16_t length, uint16_t packetID) {
  Serial.print("PacketID: "); Serial.println(packetID);
  Serial.print("Length: "); Serial.println(length);
  for ( uint16_t i = 0; i < length; i++ ) {
    Serial.print(buffer[i], HEX); Serial.print(" ");
  }
  Serial.println();
}

void loop() {
  static uint32_t _timer = millis();
  if ( millis() - _timer > 2000 ) {
    _timer = millis();
    static bool flip = 0;
    Serial.print("d: ");
    uint32_t _time = micros();
    teensy_gpio.digitalWrite(13, !teensy_gpio.digitalRead(13));
    Serial.println(micros() - _time);
    Serial.print("p: ");
    _time = micros();
    teensy_gpio.pinMode(13, OUTPUT);
    Serial.println(micros() - _time);

    uint16_t buf[60];
    static uint16_t __count = 0;
    for ( uint32_t i = 0; i < sizeof(buf) / 2; i++ ) buf[i] = __count++;
    _time = micros();
    Serial.print("returned value "); Serial.println(teensy_gpio.transfer16(buf, sizeof(buf) / 2, 55,1));
    Serial.println(micros() - _time);
  teensy_gpio.events();
   }
}


The fire and forget is for master only, slave is unaffected since it queues it for master's event() call
 
Tony - does that run on the prior SPI_MSTransfer_0_0_3.zip?

Looking very good - especially having samples to explore with! Hope to try it in the next hour.
 
Am I missing something:
Slave:
T:\TEMP\arduino_build_838742\libraries\SPI_MSTransfer\SPI_MSTransfer.cpp.o: In function `__gnu_cxx::new_allocator<std::vector<unsigned short, std::allocator<unsigned short> >*>::allocate(unsigned int, void const*)':

t:\arduino_1.8.5\hardware\tools\arm\arm-none-eabi\include\c++\5.4.1\ext/new_allocator.h:102: undefined reference to `std::__throw_bad_alloc()'

T:\TEMP\arduino_build_838742\libraries\SPI_MSTransfer\SPI_MSTransfer.cpp.o: In function `__gnu_cxx::new_allocator<unsigned short>::allocate(unsigned int, void const*)':

t:\arduino_1.8.5\hardware\tools\arm\arm-none-eabi\include\c++\5.4.1\ext/new_allocator.h:102: undefined reference to `std::__throw_bad_alloc()'

Master:
master: In function 'void loop()':
master:37: error: no matching function for call to 'SPI_MSTransfer::transfer16(uint16_t [60], unsigned int, int, int)'
Serial.print("returned value "); Serial.println(teensy_gpio.transfer16(buf, sizeof(buf) / 2, 55,1));

^

In file included from T:\tCode\libraries\SPI_MSTransfer\examples\master\master.ino:2:0:

t:\tcode\libraries\SPI_MSTransfer/SPI_MSTransfer.h:29:23: note: candidate: virtual uint16_t SPI_MSTransfer::transfer16(uint16_t*, uint16_t, uint16_t)

virtual uint16_t transfer16(uint16_t *buffer, uint16_t length, uint16_t packetID);

^

t:\tcode\libraries\SPI_MSTransfer/SPI_MSTransfer.h:29:23: note: candidate expects 3 arguments, 4 provided

t:\tcode\libraries\SPI_MSTransfer/SPI_MSTransfer.h:47:23: note: candidate: virtual uint16_t SPI_MSTransfer::transfer16(uint16_t)

virtual uint16_t transfer16(uint16_t data); // SPI 16bit

^

t:\tcode\libraries\SPI_MSTransfer/SPI_MSTransfer.h:47:23: note: candidate expects 1 argument, 4 provided
 
hmm... not throwing here. Can you drop this in your sketch?

Code:
namespace std {
void __throw_bad_alloc() {
  Serial.println("Unable to allocate memory");
}
void __throw_length_error( char const*e ) {
  Serial.print("Length Error :"); Serial.println(e);
}
}

my teensquitto library uses this missing definition as well

ill append it to the CPP file next update

you can at this to the end of the CPP temporarily, it'll be in next update:

Code:
namespace std {
void __attribute__((weak)) __throw_bad_alloc() {
  Serial.println("Unable to allocate memory");
}
void __attribute__((weak)) __throw_length_error( char const*e ) {
  Serial.print("Length Error :"); Serial.println(e);
}
}
 
Last edited:
Master has different issues?

That got SLAVE to compile with notes:

slave: In function 'void std::__throw_bad_alloc()':
T:\tCode\libraries\SPI_MSTransfer\examples\slave\slave.ino:8:1: warning: 'noreturn' function does return

slave: In function 'void std::__throw_length_error(const char*)':
T:\tCode\libraries\SPI_MSTransfer\examples\slave\slave.ino:11:1: warning: 'noreturn' function does return
}
 
yeah ignore the notes. master and slave need those std definitions, whats wrong with master? what issues? add to its sketch too
 
Does that need changes to Master or Slave ino?

Master is T_3.6 and Slave is T_3.5.

No Slave output. Below is from Master:
T:\tCode\libraries\SPI_MSTransfer\examples\master\master.ino Feb 26 2018 13:33:46
d: Resending packet, CRC fail / Out of Sync
Resending packet, CRC fail / Out of Sync
Resending packet, CRC fail / Out of Sync
Resending packet, CRC fail / Out of Sync
Resending packet, CRC fail / Out of Sync
Resending packet, CRC fail / Out of Sync
807999
p: Resending packet, CRC fail / Out of Sync
Resending packet, CRC fail / Out of Sync
Resending packet, CRC fail / Out of Sync
403995
returned value 55
83
Resending packet, CRC fail / Out of Sync
Resending packet, CRC fail / Out of Sync
Resending packet, CRC fail / Out of Sync
d: Resending packet, CRC fail / Out of Sync
Resending packet, CRC fail / Out of Sync
Resending packet, CRC fail / Out of Sync
Resending packet, CRC fail / Out of Sync
Resending packet, CRC fail / Out of Sync
Resending packet, CRC fail / Out of Sync
808000

MASTER:
Code:
#include <SPI.h>
#include <SPI_MSTransfer.h>
SPI_MSTransfer teensy_gpio = SPI_MSTransfer("Serial", 43, &SPI2);


void setup() {
  Serial.begin(115200);
  while (!Serial && millis() < 2000 ) {}
  Serial.println("\n" __FILE__ " " __DATE__ " " __TIME__);
  teensy_gpio.onTransfer(myCallback);
  SPI2.begin();
}
void myCallback(uint16_t *buffer, uint16_t length, uint16_t packetID) {
  Serial.print("PacketID: "); Serial.println(packetID);
  Serial.print("Length: "); Serial.println(length);
  for ( uint16_t i = 0; i < length; i++ ) {
    Serial.print(buffer[i], HEX); Serial.print(" ");
  }
  Serial.println();
}

void loop() {
  static uint32_t _timer = millis();
  if ( millis() - _timer > 2000 ) {
    _timer = millis();
    static bool flip = 0;
    Serial.print("d: ");
    uint32_t _time = micros();
    teensy_gpio.digitalWrite(13, !teensy_gpio.digitalRead(13));
    Serial.println(micros() - _time);
    Serial.print("p: ");
    _time = micros();
    teensy_gpio.pinMode(13, OUTPUT);
    Serial.println(micros() - _time);

    uint16_t buf[60];
    static uint16_t __count = 0;
    for ( uint32_t i = 0; i < sizeof(buf) / 2; i++ ) buf[i] = __count++;
    _time = micros();
    Serial.print("returned value "); Serial.println(teensy_gpio.transfer16(buf, sizeof(buf) / 2, 55, 1));
    Serial.println(micros() - _time);
    teensy_gpio.events();
  }
}

SLAVE:
Code:
#include <SPI_MSTransfer.h>

SPI_MSTransfer slave = SPI_MSTransfer("SLAVE", "STANDALONE");


void setup() {
	Serial.begin(115200);
	while (!Serial && millis() < 2000 ) {}
	Serial.println("\n" __FILE__ " " __DATE__ " " __TIME__);
	slave.onTransfer(myCallback);
}
void led() {
	digitalWrite(13, !digitalRead(13));
}

void loop() {
	slave.events();
	static uint32_t _timer = millis();
	if ( millis() - _timer >= 2000 ) {
		_timer = millis();
		uint16_t buf[5] = { 1, 2, 3, 4, 5 };
		slave.transfer16(buf, 5, 55);
	}
}
void myCallback(uint16_t *buffer, uint16_t length, uint16_t packetID) {
	Serial.print("PacketID: "); Serial.println(packetID);
	Serial.print("Length: "); Serial.println(length);
	for ( uint16_t i = 0; i < length; i++ ) {
		Serial.print(buffer[i], HEX); Serial.print(" ");
	}
	Serial.println();
}
 
I cloned my folder files that are currently uploaded to both teensies, i doubt its the library, you checked your connections?
ill upload both t3.5 & 3.6 mcus again one sec

didnt you post back you used a diff pin for SPI2? (just guessing)

EDIT#1: uploaded to slave, slave still works
EDIT#2: uploaded to master, master still works

check your spi wiring ? :eek:
 
I connected and powered them with the initial code and got good results with rare failures note for the LED toggle and pinmode test.

I'll check that it is still connected right
 
Needs to be this and it works - I overwrote with the NEW version and forgot about that.

MASTER OUT:
returned value 55
83
PacketID: 55
Length: 5
1 2 3 4 5
d: 32
p: 15

SLAVE OUT:
PacketID: 55
Length: 60
D98 D99 D9A D9B D9C D9D D9E D9F DA0 DA1 DA2 DA3 DA4 DA5 DA6 DA7 DA8 DA9 DAA DAB DAC DAD DAE DAF DB0 DB1 DB2 DB3 DB4 DB5 DB6 DB7 DB8 DB9 DBA DBB DBC DBD DBE DBF DC0 DC1 DC2 DC3 DC4 DC5 DC6 DC7 DC8 DC9 DCA DCB DCC DCD DCE DCF DD0 DD1 DD2 DD3

Code:
#include <SPI.h>
#include <SPI_MSTransfer.h>
[B]SPI_MSTransfer teensy_gpio = SPI_MSTransfer("Serial", 15, &SPI);[/B]


void setup() {
  Serial.begin(115200);
  while (!Serial && millis() < 2000 ) {}
  Serial.println("\n" __FILE__ " " __DATE__ " " __TIME__);
  teensy_gpio.onTransfer(myCallback);
[B]  SPI.setSCK( 14 );[/B]
  SPI.begin();
}
void myCallback(uint16_t *buffer, uint16_t length, uint16_t packetID) {
  Serial.print("PacketID: "); Serial.println(packetID);
  Serial.print("Length: "); Serial.println(length);
  for ( uint16_t i = 0; i < length; i++ ) {
    Serial.print(buffer[i], HEX); Serial.print(" ");
  }
  Serial.println();
}

void loop() {
  static uint32_t _timer = millis();
  if ( millis() - _timer > 2000 ) {
    _timer = millis();
    static bool flip = 0;
    Serial.print("d: ");
    uint32_t _time = micros();
    teensy_gpio.digitalWrite(13, !teensy_gpio.digitalRead(13));
    Serial.println(micros() - _time);
    Serial.print("p: ");
    _time = micros();
    teensy_gpio.pinMode(13, OUTPUT);
    Serial.println(micros() - _time);

    uint16_t buf[60];
    static uint16_t __count = 0;
    for ( uint32_t i = 0; i < sizeof(buf) / 2; i++ ) buf[i] = __count++;
    _time = micros();
    Serial.print("returned value "); Serial.println(teensy_gpio.transfer16(buf, sizeof(buf) / 2, 55, 1));
    Serial.println(micros() - _time);
    teensy_gpio.events();
  }
}
 
Back
Top