Project: SPI_MSTransfer

I was sort of curious. So I looked at an old imxrt1050 sdk, at the examples... for SPI with master/slave.

In both master and slave they appeared to configure the pins the same...
Code:
  IOMUXC_SetPinConfig(
      IOMUXC_GPIO_AD_B0_00_LPSPI3_SCK,        /* GPIO_AD_B0_00 PAD functional properties : */
      0x10B0u);                               /* Slew Rate Field: Slow Slew Rate
                                                 Drive Strength Field: R0/6
                                                 Speed Field: medium(100MHz)
                                                 Open Drain Enable Field: Open Drain Disabled
                                                 Pull / Keep Enable Field: Pull/Keeper Enabled
                                                 Pull / Keep Select Field: Keeper
                                                 Pull Up / Down Config. Field: 100K Ohm Pull Down
                                                 Hyst. Enable Field: Hysteresis Disabled */
  IOMUXC_SetPinConfig(
      IOMUXC_GPIO_AD_B0_01_LPSPI3_SDO,        /* GPIO_AD_B0_01 PAD functional properties : */
      0x10B0u);                               /* Slew Rate Field: Slow Slew Rate
                                                 Drive Strength Field: R0/6
                                                 Speed Field: medium(100MHz)
                                                 Open Drain Enable Field: Open Drain Disabled
                                                 Pull / Keep Enable Field: Pull/Keeper Enabled
                                                 Pull / Keep Select Field: Keeper
                                                 Pull Up / Down Config. Field: 100K Ohm Pull Down
                                                 Hyst. Enable Field: Hysteresis Disabled */
  IOMUXC_SetPinConfig(
      IOMUXC_GPIO_AD_B0_02_LPSPI3_SDI,        /* GPIO_AD_B0_02 PAD functional properties : */
      0x10B0u);                               /* Slew Rate Field: Slow Slew Rate
                                                 Drive Strength Field: R0/6
                                                 Speed Field: medium(100MHz)
                                                 Open Drain Enable Field: Open Drain Disabled
                                                 Pull / Keep Enable Field: Pull/Keeper Enabled
                                                 Pull / Keep Select Field: Keeper
                                                 Pull Up / Down Config. Field: 100K Ohm Pull Down
                                                 Hyst. Enable Field: Hysteresis Disabled */
  IOMUXC_SetPinConfig(
      IOMUXC_GPIO_AD_B0_03_LPSPI3_PCS0,       /* GPIO_AD_B0_03 PAD functional properties : */
      0x10B0u);                               /* Slew Rate Field: Slow Slew Rate
                                                 Drive Strength Field: R0/6
                                                 Speed Field: medium(100MHz)
                                                 Open Drain Enable Field: Open Drain Disabled
                                                 Pull / Keep Enable Field: Pull/Keeper Enabled
                                                 Pull / Keep Select Field: Keeper
                                                 Pull Up / Down Config. Field: 100K Ohm Pull Down
                                                 Hyst. Enable Field: Hysteresis Disabled */
Note: they are using different hardware SPI port, but same difference....
 
in my test last night for master the best setting for DSE and SPEED is 3, seems to be running well up to 16MHz, im having issues if I push speeds above that though
 
Just tested the DSE(7) config and you need the second jumper. As Tony said DSE(3) works without that second jumper
 
SPI library there are 3 versions of fastio defined with 2 commented out and none of them are the DSE(6) SPEED(2) of the examples..
But again this is 1050... I don't have the 1060 version...
 
SPI library there are 3 versions of fastio defined with 2 commented out and none of them are the DSE(6) SPEED(2) of the examples..
But again this is 1050... I don't have the 1060 version...
 
@KurtE - @tonton81

with this configuration for master and slave:
Code:
	uint32_t fastio = IOMUXC_PAD_DSE(3) | IOMUXC_PAD_SPEED(1);

I am running at 30Mhz but while the slave is getting data - the master doesn't detect the slave:
Capture.PNG
 
SPI library there are 3 versions of fastio defined with 2 commented out and none of them are the DSE(6) SPEED(2) of the examples..
But again this is 1050... I don't have the 1060 version...

I do :). Looks identical:
Code:
 IOMUXC_SetPinConfig(
      IOMUXC_GPIO_SD_B0_00_LPSPI1_SCK,        /* GPIO_SD_B0_00 PAD functional properties : */
      0x10B0u);                               /* Slew Rate Field: Slow Slew Rate
                                                 Drive Strength Field: R0/6
                                                 Speed Field: medium(100MHz)
                                                 Open Drain Enable Field: Open Drain Disabled
                                                 Pull / Keep Enable Field: Pull/Keeper Enabled
                                                 Pull / Keep Select Field: Keeper
                                                 Pull Up / Down Config. Field: 100K Ohm Pull Down
                                                 Hyst. Enable Field: Hysteresis Disabled */
  IOMUXC_SetPinConfig(
      IOMUXC_GPIO_SD_B0_01_LPSPI1_PCS0,       /* GPIO_SD_B0_01 PAD functional properties : */
      0x10B0u);                               /* Slew Rate Field: Slow Slew Rate
                                                 Drive Strength Field: R0/6
                                                 Speed Field: medium(100MHz)
                                                 Open Drain Enable Field: Open Drain Disabled
                                                 Pull / Keep Enable Field: Pull/Keeper Enabled
                                                 Pull / Keep Select Field: Keeper
                                                 Pull Up / Down Config. Field: 100K Ohm Pull Down
                                                 Hyst. Enable Field: Hysteresis Disabled */
  IOMUXC_SetPinConfig(
      IOMUXC_GPIO_SD_B0_02_LPSPI1_SDO,        /* GPIO_SD_B0_02 PAD functional properties : */
      0x10B0u);                               /* Slew Rate Field: Slow Slew Rate
                                                 Drive Strength Field: R0/6
                                                 Speed Field: medium(100MHz)
                                                 Open Drain Enable Field: Open Drain Disabled
                                                 Pull / Keep Enable Field: Pull/Keeper Enabled
                                                 Pull / Keep Select Field: Keeper
                                                 Pull Up / Down Config. Field: 100K Ohm Pull Down
                                                 Hyst. Enable Field: Hysteresis Disabled */
  IOMUXC_SetPinConfig(
      IOMUXC_GPIO_SD_B0_03_LPSPI1_SDI,        /* GPIO_SD_B0_03 PAD functional properties : */
      0x10B0u);                               /* Slew Rate Field: Slow Slew Rate
                                                 Drive Strength Field: R0/6
                                                 Speed Field: medium(100MHz)
                                                 Open Drain Enable Field: Open Drain Disabled
                                                 Pull / Keep Enable Field: Pull/Keeper Enabled
                                                 Pull / Keep Select Field: Keeper
                                                 Pull Up / Down Config. Field: 100K Ohm Pull Down
                                                 Hyst. Enable Field: Hysteresis Disabled */
}

Maybe its one of the other settings not matching ?
 
yes exactly you can run 30 but reception breaks, thats one of the issues :)
20 works but you'll have some missed frames
16 seems to be the top
 
@KurtE - @tonton81
Going to be off line for awhile - have a family emergency to take care which will probably take a few hours.
 
@KurtE - @tonton81
Going to be off line for awhile - have a family emergency to take care which will probably take a few hours.
 
@mjs513 - hope all is well!

@ALL - earlier today I played with the Master/Slave.

Changed the slave to be closer to what is in the SDK examples:
Code:
SPI_MSTransfer_T4_FUNC SPI_MSTransfer_T4_OPT::SPI_MSTransfer_T4() {
  if ( port == &SPI ) {
    _LPSPI4 = this;
    _portnum = 3;
    CCM_CCGR1 |= (3UL << 6);
    nvic_irq = 32 + _portnum;
    _VectorsRam[16 + nvic_irq] = lpspi4_slave_isr;

    /* Alternate pins not broken out on Teensy 4.0/4.1 for LPSPI4 */
    SLAVE_PINS_ADDR;
    spiAddr[0] = 0; /* PCS0_SELECT_INPUT */
    spiAddr[1] = 0; /* SCK_SELECT_INPUT */
    spiAddr[2] = 0; /* SDI_SELECT_INPUT */
    spiAddr[3] = 0; /* SDO_SELECT_INPUT */
    IOMUXC_SW_MUX_CTL_PAD_GPIO_B0_03 = 0x3; /* LPSPI4 SCK (CLK) */
    IOMUXC_SW_MUX_CTL_PAD_GPIO_B0_01 = 0x3; /* LPSPI4 SDI (MISO) */
    IOMUXC_SW_MUX_CTL_PAD_GPIO_B0_02 = 0x3; /* LPSPI4 SDO (MOSI) */
    IOMUXC_SW_MUX_CTL_PAD_GPIO_B0_00 = 0x3; /* LPSPI4 PCS0 (CS) */

    IOMUXC_SW_PAD_CTL_PAD_GPIO_B0_01 = IOMUXC_PAD_DSE(3) | IOMUXC_PAD_SPEED(3) | IOMUXC_PAD_PKE;
    IOMUXC_SW_PAD_CTL_PAD_GPIO_B0_02 = IOMUXC_PAD_DSE(3) | IOMUXC_PAD_SPEED(3) | IOMUXC_PAD_PKE;
    IOMUXC_SW_PAD_CTL_PAD_GPIO_B0_03 = IOMUXC_PAD_DSE(3) | IOMUXC_PAD_SPEED(3) | IOMUXC_PAD_PKE;
    IOMUXC_SW_PAD_CTL_PAD_GPIO_B0_00 = IOMUXC_PAD_DSE(3) | IOMUXC_PAD_SPEED(3) | IOMUXC_PAD_PKE; /* LPSPI4 PCS0 (CS) */

  } 
}
Although speed/dse is different. I added on the PKE. As for the CS, not sure having it default to PD is the best, would think best would be to default to PU...

I made similar changes to SPI for the master one. Again not sure about the CS pin.

But with these changes it appeared to work with just one gnd wire. And that also included having the LA hooked up

At times it is confusing to me, on what the IOMUXC_PAD_DSE(3) means versus IOMUXC_PAD_DSE(7)? It talks about resistor/... but simple question which one produces a stronger more stable signal?

Also wondering about speed. May depend on how long the wires are. I am using some somewhat longer jumpers... Wonder if very short wires will allow higher speeds
 
https://youtu.be/r0fzfzY3GcY

16MHz daisy chain demonstration, no delays

Uploaded the MASTER source, moved the tpp defines in there, added SPI speed in constructor (defaults to 2MHz if not specified). Also added RT1062 pin pad override which is called in begin(), so we dont need to modify SPI.c
Little cleanup in the slave source file.
 
Last edited:
https://youtu.be/r0fzfzY3GcY

16MHz daisy chain demonstration, no delays

Uploaded the MASTER source, moved the tpp defines in there, added SPI speed in constructor (defaults to 2MHz if not specified). Also added RT1062 pin pad override which is called in begin(), so we dont need to modify SPI.c
Little cleanup in the slave source file.

Meant to respond to this earlier but been busy today going to appointment. Did try it with my 2 T4 set up and worked as expected without using 2 ground wires. Changing the SPI Clock worked as well. Now for the next question - how do I send data back to the Master. Same as with previous library?
 
not yet, it's a rewrite so it will need to be added, just wanna check if all good before i continue adding more stuff :)

digitalRead is the only one that actually pulls data from slave at the moment
 
How you want it since we redoing it? Slave queues up data and master pulls from slave queue i guess? I might have the slave inject queue info in initial requests that pass the correct ID to it before even an ACK occurs, so the master will catch an opportunity to pull queues before event() even runs.

I also make sure it works across daisy chained teensy 4s :)
 
Welp, I worked on that for you. events() will check whether there are queues or not, and if there are, it will pull it and dequeue from slave only after a valid crc is exchanged.

slave uses same transfer16 function as master, it queues it and master events() will pick it up afterwards. callback is also same for master as the slave, so just copy paste it over, info.slaveID returns the slave's ID of that payload(in case you did like me, have both nodes in same callback, slaveID identifies where it came from). This is working well in daisy chain mode :)

https://youtu.be/NWsuD4hhnHs
 
Welp, I worked on that for you. events() will check whether there are queues or not, and if there are, it will pull it and dequeue from slave only after a valid crc is exchanged.

slave uses same transfer16 function as master, it queues it and master events() will pick it up afterwards. callback is also same for master as the slave, so just copy paste it over, info.slaveID returns the slave's ID of that payload(in case you did like me, have both nodes in same callback, slaveID identifies where it came from). This is working well in daisy chain mode :)

https://youtu.be/NWsuD4hhnHs

Morning Tony

Absolutely fantastic. Can't wait to set something up and try it. Sorry - its hit or miss when I am on-line. Still taking care of a few family things.
 
@KurtE - @tonton81 - @PaulStoffregen

Was kind of curious of whether the issue with changing the Drive Strength (DSE) and pad speed to 3 was isolated to this case or not. So I broke up @KurtE's SPI Slave test sketch in post #20 in the Teensy 4 as SPI Slave thread into a master and slave sketch using SPI for both master and slave.

And guess what - looks like that DSE/Speed change is necessary since it would not work until I added in the IOMUX settings.

Here are the master and slave sketches for reference:
Code:
#include <DMAChannel.h>
#include <SPI.h>
#define PRREG(x) Serial.print(#x" 0x"); Serial.println(x, BIN) //Serial.println(x,BIN)
#define SAMPLES 1

#define SPIM SPI
#define CS_MASTER 10

volatile bool received_data = false;
DMAMEM static uint16_t rx_buffer[SAMPLES];
uint16_t loop_count = 0;

void setup() {
  while (!Serial);
  Serial.begin(115200);
  // I am going to use SPI1 as the master.
  SPIM.begin(); // initialize it... 
[COLOR="#FF0000"]    IOMUXC_SW_PAD_CTL_PAD_GPIO_B0_01 = IOMUXC_PAD_DSE(3) | IOMUXC_PAD_SPEED(3) | IOMUXC_PAD_PKE; /* LPSPI4 SDI (MISO) */
    IOMUXC_SW_PAD_CTL_PAD_GPIO_B0_02 = IOMUXC_PAD_DSE(3) | IOMUXC_PAD_SPEED(3) | IOMUXC_PAD_PKE; /* LPSPI4 SDO (MOSI) */
    IOMUXC_SW_PAD_CTL_PAD_GPIO_B0_03 = IOMUXC_PAD_DSE(3) | IOMUXC_PAD_SPEED(3) | IOMUXC_PAD_PKE; /* LPSPI4 SCK (CLK) */
    IOMUXC_SW_PAD_CTL_PAD_GPIO_B0_00 = IOMUXC_PAD_DSE(3) | IOMUXC_PAD_SPEED(3) | IOMUXC_PAD_PKE; /* LPSPI4 PCS0 (CS) */[/COLOR]
  
  pinMode(CS_MASTER, OUTPUT);
  digitalWrite(CS_MASTER, HIGH);
  
}

void loop() {
  elapsedMicros emloop = 0;
  received_data = false;
  digitalWrite(CS_MASTER, LOW);
  SPIM.beginTransaction(SPISettings(8000000, MSBFIRST, SPI_MODE0));
  uint16_t spi_returned_data  = SPIM.transfer16(loop_count);
  SPIM.endTransaction();
  while (!received_data && (emloop < 250000)) ;
  Serial.printf("SPI Transfer: %x Return: %x delay: %d\n", loop_count, spi_returned_data,(uint32_t)emloop);
  digitalWrite(CS_MASTER, HIGH);
  delay(250);
  loop_count++;
}

And the slave:
Code:
#include <DMAChannel.h>
#include <SPI.h>
#define PRREG(x) Serial.print(#x" 0x"); Serial.println(x, BIN) //Serial.println(x,BIN)
#define SAMPLES 1

#define SPIS SPI
#define ChipSelectSlave 10
IMXRT_LPSPI_t *spis_regs = &IMXRT_LPSPI4_S;
#define SPIS_DMAMUX_SOURCE_RX DMAMUX_SOURCE_LPSPI4_RX

volatile bool received_data = false;
DMAMEM static uint16_t rx_buffer[SAMPLES];
uint16_t loop_count = 0;

DMAChannel rx(false);

void rxISR() {
  rx.clearInterrupt();
  received_data = true;
  arm_dcache_delete(rx_buffer, SAMPLES); //delete Cache!
  spis_regs->TDR = rx_buffer[0];
  asm volatile ("dsb");
}

bool initSPISlaveDMA() {
  rx.begin(true);
  rx.source((uint16_t &) spis_regs->RDR);
  rx.triggerAtHardwareEvent(SPIS_DMAMUX_SOURCE_RX);
  rx.attachInterrupt(rxISR);
  rx.interruptAtCompletion(); //TCD->CSR |= DMA_TCD_CSR_INTMAJOR;
  rx.destinationBuffer(rx_buffer, SAMPLES + 1);
  rx.enable();
  return 1;
}

bool initSPISlave() {
  spis_regs->CR &= ~LPSPI_CR_MEN; //Modul ausschalten
  spis_regs->CR = LPSPI_CR_RST; //Master Logic reset! (Control Register => Software Reset)
  spis_regs->CR &=  ~LPSPI_CR_RST; //Master Logic reset! (Control Register => Software Reset)
  spis_regs->TCR = LPSPI_TCR_FRAMESZ(15); //16Bit Mode
  spis_regs->DER = LPSPI_DER_RDDE; //RX DMA Request Enable
  spis_regs->CR |= LPSPI_CR_MEN; //Enable SPI Module!
  return 1;
}

void setup() {
  while (!Serial);
  Serial.begin(115200);
  // I am going to use SPI1 as the master.
  //SPIM.begin(); // initialize it... 
  //pinMode(CS_MASTER, OUTPUT);
  //digitalWrite(CS_MASTER, HIGH);
  
  SPIS.begin();
  pinMode(ChipSelectSlave, INPUT);
  uint8_t cs_mask =  SPIS.setCS(ChipSelectSlave);
  Serial.printf("Slave CS: %d set: %x\n", ChipSelectSlave, cs_mask);

[COLOR="#FF0000"]    IOMUXC_SW_PAD_CTL_PAD_GPIO_B0_01 = IOMUXC_PAD_DSE(3) | IOMUXC_PAD_SPEED(3) | IOMUXC_PAD_PKE; /* LPSPI4 SDI (MISO) */
    IOMUXC_SW_PAD_CTL_PAD_GPIO_B0_02 = IOMUXC_PAD_DSE(3) | IOMUXC_PAD_SPEED(3) | IOMUXC_PAD_PKE; /* LPSPI4 SDO (MOSI) */
    IOMUXC_SW_PAD_CTL_PAD_GPIO_B0_03 = IOMUXC_PAD_DSE(3) | IOMUXC_PAD_SPEED(3) | IOMUXC_PAD_PKE; /* LPSPI4 SCK (CLK) */
    IOMUXC_SW_PAD_CTL_PAD_GPIO_B0_00 = IOMUXC_PAD_DSE(3) | IOMUXC_PAD_SPEED(3) | IOMUXC_PAD_PKE; /* LPSPI4 PCS0 (CS) */[/COLOR]
  
  Serial.println("Init SPI!");
  if (initSPISlave()) {
    Serial.println("SPI SLAVE init!");
  }
  if (initSPISlaveDMA()) {
    Serial.println("DMA Channel init!");
  }
}

void loop() {
 //if(received_data) Serial.printf("SPI Transfer: %x\n", rx_buffer[0]);
}
again this was a quick and dirty test.
 
Last edited:
Wow that was fun. Because of the way I defined some defines for centralized access, I was able to make T3.5 to work as a daisy chained slave in this new MST_T4! I am going to do the same method of using offsets since all busses are aligned, just to keep it cleaner than previous versions :)

20220310_031506.jpg

This is a T4.0 master going to daisy-chained first slave T4.0 and 2nd slave T3.5 :)

Although I havn't tested new additions, it's there for testing.

SPI0 slave support for T3.2
SPI0, SPI1, SPI2 slave support for T3.5/T3.6

SPI0 is currently used and tested on a T3.5, and should work on all T3.x mcus as they use the same register.
 
Last edited:
Wow that was fun. Because of the way I defined some defines for centralized access, I was able to make T3.5 to work as a daisy chained slave in this new MST_T4! I am going to do the same method of using offsets since all busses are aligned, just to keep it cleaner than previous versions :)

View attachment 27797

This is a T4.0 master going to daisy-chained first slave T4.0 and 2nd slave T3.5 :)

Although I havn't tested new additions, it's there for testing.

SPI0 slave support for T3.2
SPI0, SPI1, SPI2 slave support for T3.5/T3.6

SPI0 is currently used and tested on a T3.5, and should work on all T3.x mcus as they use the same register.

That great news and good to hear and sounds like it was fun getting it to work. Still doing stuff with family so may not get to setting up until this weekend. But this is going to good.
 
Been trying to add LC support but that's not playing nicely so far, especially since we are not playing with 32bit registers (LC=8). That, and the transmit register is 2 bytes versus 1 word. So shared TDR without ifdef's won't be likely with an LC, it's definately more involved than the T3.x's, which took maybe 5-10 minutes to add :)
 
Been trying to add LC support but that's not playing nicely so far, especially since we are not playing with 32bit registers (LC=8). That, and the transmit register is 2 bytes versus 1 word. So shared TDR without ifdef's won't be likely with an LC, it's definately more involved than the T3.x's, which took maybe 5-10 minutes to add :)

If I remember correctly you had the same problem SPIMSTransfer (non T4 version). LC was a problem.
 
Back
Top