Hi all,
I'm working on a project that requires a basic command/response structure from a slave teensy. What I want to happen is a fixed-sized command packet is dma'd into a buffer (dma channel 1), and when the packet is received an interrupt is fired and channel 1 stops. In the interrupt we process the packet and put a response in a buffer, and then enable dma channel 2 to clock out the response. When channel 2 is done, it fires an interrupt and stops. Channel 1 is enabled by chip select falling low.
I've been using an interrupt based slave mode library here: https://github.com/btmcmahan/Teensy-3.0-SPI-Master---Slave/blob/master/t3spi.cpp, but being interrupt-dependent seems to be problematic at 6.25Mhz, which is what I'm running at (also it would have to compete with other interrupts, which is bad). I'm hoping DMA would be able to access SPI1_PUSHR fast enough.
I have a setup that does part of this, where I connect a teensy master's spi0 ports to teensy slave's spi1 ports.
Master:
MOSI pin 11
MISO pin 12
CS pin 10
CLK pin 13
Slave:
MISO pin 0 // These pins are the opposite of the teensy pinout documentation
MOSI pin 1
CS pin 31
SCK pin 32
I have 3 dma channels:
dma_pop pushes data out of SPI1_POPR into a 16-bit register and always runs because SPI1_PUSHR won't clock out bits unless POPR is cleared. So, this channel should run even when dma_rx is off.
dma_rx (channel 1 in the example above) takes the 16-bit word from dma_pop and moves it into a receive buffer. This is only enabled when we're receiving a command.
dma_tx (channel 2 in the example above) takes the 16-bit word from the response buffer and moves it to SPI1_PUSHR. This is only enabled when we're clocking out the response. In the example dma_tx echoes from the same buffer that dma_rx clocks into, and the spi clock is 625 khz instead of 6.25mhz
I think this code is breaking because maybe disableOnCompletion(); is firing after every dma execution, instead of after the buffer is completely transferred. dma_rx seems to work fine - the receive buffer looks about correct. dma_tx seems to not work as much though/maybe SPI_PUSHR isn't working. What's going on here?
master code:
slave code:
The response I have is this (spi master serial output):
Sent: 1 Received: 0
Sent: 2 Received: 0
Sent: 3 Received: beef
Sent: 4 Received: beef
Sent: 5 Received: beef
Sent: 6 Received: beef
Sent: 7 Received: beef
Sent: 8 Received: beef
Sent: 9 Received: beef
Sent: a Received: beef
Sent: b Received: beef
Sent: c Received: beef
Sent: d Received: beef
Sent: e Received: abcd
Sent: f Received: 1
Sent: 10 Received: 1
Sent: 11 Received: 1
Sent: 12 Received: 1
Sent: 13 Received: 1
Sent: 14 Received: 1
Sent: 15 Received: 1
Sent: 16 Received: 1
Sent: 17 Received: 1
Sent: 18 Received: 1
---------------------------------------
Sent: 1 Received: 0
Sent: 2 Received: 0
Sent: 3 Received: beef
Sent: 4 Received: beef
Sent: 5 Received: beef
Sent: 6 Received: beef
Sent: 7 Received: beef
Sent: 8 Received: beef
Sent: 9 Received: beef
Sent: a Received: beef
Sent: b Received: beef
Sent: c Received: beef
Sent: d Received: beef
Sent: e Received: abcd
Sent: f Received: 1
Sent: 10 Received: 2
Sent: 11 Received: 3
Sent: 12 Received: 4
Sent: 13 Received: 5
...................................
Sent: n Received: n - e
Sent: n + 1 Received: dcba
---------------------------------------
I'm working on a project that requires a basic command/response structure from a slave teensy. What I want to happen is a fixed-sized command packet is dma'd into a buffer (dma channel 1), and when the packet is received an interrupt is fired and channel 1 stops. In the interrupt we process the packet and put a response in a buffer, and then enable dma channel 2 to clock out the response. When channel 2 is done, it fires an interrupt and stops. Channel 1 is enabled by chip select falling low.
I've been using an interrupt based slave mode library here: https://github.com/btmcmahan/Teensy-3.0-SPI-Master---Slave/blob/master/t3spi.cpp, but being interrupt-dependent seems to be problematic at 6.25Mhz, which is what I'm running at (also it would have to compete with other interrupts, which is bad). I'm hoping DMA would be able to access SPI1_PUSHR fast enough.
I have a setup that does part of this, where I connect a teensy master's spi0 ports to teensy slave's spi1 ports.
Master:
MOSI pin 11
MISO pin 12
CS pin 10
CLK pin 13
Slave:
MISO pin 0 // These pins are the opposite of the teensy pinout documentation
MOSI pin 1
CS pin 31
SCK pin 32
I have 3 dma channels:
dma_pop pushes data out of SPI1_POPR into a 16-bit register and always runs because SPI1_PUSHR won't clock out bits unless POPR is cleared. So, this channel should run even when dma_rx is off.
dma_rx (channel 1 in the example above) takes the 16-bit word from dma_pop and moves it into a receive buffer. This is only enabled when we're receiving a command.
dma_tx (channel 2 in the example above) takes the 16-bit word from the response buffer and moves it to SPI1_PUSHR. This is only enabled when we're clocking out the response. In the example dma_tx echoes from the same buffer that dma_rx clocks into, and the spi clock is 625 khz instead of 6.25mhz
I think this code is breaking because maybe disableOnCompletion(); is firing after every dma execution, instead of after the buffer is completely transferred. dma_rx seems to work fine - the receive buffer looks about correct. dma_tx seems to not work as much though/maybe SPI_PUSHR isn't working. What's going on here?
master code:
Code:
#include <Arduino.h>
#include <SPI.h> // include the SPI library:
void setup() {
// put your setup code here, to run once:
SPI.begin();
SPI.beginTransaction(SPISettings(625000, MSBFIRST, SPI_MODE0));
pinMode(10, OUTPUT);
}
void loop() {
digitalWriteFast(10, LOW);
//delayMicroseconds(0);
// put your main code here, to run repeatedly:
for (int i = 1; i < 25; i++) {
uint16_t blah = SPI.transfer16(i);
Serial.printf("Sent: %x Received: %x\n", i, blah);
}
Serial.printf("---------------------------------------\n");
digitalWriteFast(10, HIGH);
delay(2000);
}
slave code:
Code:
#include <DMAChannel.h>
#define MASTER 1
#define SLAVE 0
#define T3_SPI_CLOCK_DIV2 0b0000 //24.0 MHz
#define T3_SPI_CLOCK_DIV4 0b0001 //12.0 MHz
#define T3_SPI_CLOCK_DIV6 0b0010 //08.0 MHz
#define T3_SPI_CLOCK_DIV8 0b0011 //05.3 MHz
#define T3_SPI_CLOCK_DIV16 0b0100 //03.0 MHz
#define T3_SPI_CLOCK_DIV32 0b0101 //01.5 MHz
#define T3_SPI_CLOCK_DIV64 0b0110 //750 KHz
#define T3_SPI_CLOCK_DIV128 0b0111 //375 Khz
#define T3_SPI_MODE0 0x00
#define T3_SPI_MODE1 0x01
#define T3_SPI_MODE2 0x02
#define T3_SPI_MODE3 0x03
#define MSB_FIRST 0
#define LSB_FIRST 1
#define T3_CTAR_0 0
#define T3_CTAR_1 1
#define T3_CTAR_SLAVE 2
#define SCK 0x0D
#define MOSI 0x0B
#define MISO 0x0C
#define ALT_SCK 0x0E
#define ALT_MOSI 0x07
#define ALT_MISO 0x08
#define SCK1 32
#define MOSI1 0
#define MISO1 1
#define T3_CS0 0x01
#define T3_CS1 0x02
#define T3_CS2 0x04
#define T3_CS3 0x08
#define T3_CS4 0x10
#define ALT_CS0 0x81
#define ALT_CS1 0x82
#define ALT_CS2 0x84
#define ALT_CS3 0x88
#define T3_SPI1_CS0 31
#define CS_ActiveLOW 1
#define CS_ActiveHIGH 0
#define CS0_ActiveLOW 0x00010000
#define CS1_ActiveLOW 0x00020000
#define CS2_ActiveLOW 0x00040000
#define CS3_ActiveLOW 0x00080000
#define CS4_ActiveLOW 0x00100000
#define SPI_SR_TXCTR 0x0000f000 //Mask isolating the TXCTR
/* *************** DMA SETUP ********************* */
DMAChannel dma_rx;
DMAChannel dma_pop;
DMAChannel dma_tx2;
void received_packet_isr(void)
{
dma_rx.clearInterrupt();
SPI1_PUSHR = 0xabcd;
dma_tx2.enable();
Serial.printf("Received packet\n");
}
void sent_packet_isr(void)
{
SPI1_PUSHR = 0xdcba;
dma_tx2.clearInterrupt();
Serial.printf("Sent packet\n");
}
const uint16_t buffer_size = 600;
uint16_t spi_rx_dest[buffer_size];
uint32_t spi_tx_out[buffer_size];
uint16_t spi_word_received = 0xbeef;
void setup_dma_receive(void) {
for (int i = 0; i < 500; i++) {
spi_tx_out[i] = 0x100 + i;
}
dma_rx.source((uint16_t&) spi_word_received);
dma_rx.destinationBuffer((uint16_t*) spi_rx_dest, 24);
dma_rx.triggerAtHardwareEvent(DMAMUX_SOURCE_SPI1_RX);
dma_rx.disableOnCompletion();
dma_rx.interruptAtCompletion();
dma_rx.attachInterrupt(received_packet_isr);
spi_rx_dest[0] = 0xbeef;
dma_pop.sourceBuffer((uint16_t*) &KINETISK_SPI1.POPR, 2);
dma_pop.destination((uint16_t&) spi_word_received);
dma_pop.triggerAtHardwareEvent(DMAMUX_SOURCE_SPI1_RX);
dma_tx2.sourceBuffer((uint32_t *) spi_rx_dest, 24);
dma_tx2.destination(KINETISK_SPI1.PUSHR); // SPI1_PUSHR_SLAVE
dma_tx2.disableOnCompletion();
dma_tx2.interruptAtCompletion();
dma_tx2.attachInterrupt(sent_packet_isr);
dma_tx2.triggerAtHardwareEvent(DMAMUX_SOURCE_SPI1_RX);
SPI1_RSER = SPI_RSER_RFDF_RE | SPI_RSER_RFDF_DIRS; // DMA on receive FIFO drain flag
SPI1_SR = 0xFF0F0000;
dma_rx.enable();
dma_pop.enable();
dma_tx2.enable();
}
/* *********** T3SPI library for slave mode https://github.com/btmcmahan/Teensy-3.0-SPI-Master---Slave/blob/master/t3spi.cpp -- this code should work fine **************** */
void start() {
SPI1_MCR &= ~SPI_MCR_HALT & ~SPI_MCR_MDIS;
}
void stop() {
SPI1_MCR |= SPI_MCR_HALT | SPI_MCR_MDIS;
}
void end() {
SPI1_SR &= ~SPI_SR_TXRXS;
stop();
}
void setMCR(bool mode){
stop();
if (mode==1){
SPI1_MCR=0x80000000;}
else{
SPI1_MCR=0x00000000;}
start();
}
void enablePins_SLAVE(uint8_t sck, uint8_t mosi, uint8_t miso, uint8_t cs) {
if (sck == SCK){
CORE_PIN13_CONFIG = PORT_PCR_MUX(2);}
if (sck == ALT_SCK){
CORE_PIN14_CONFIG = PORT_PCR_MUX(2);}
if (sck == SCK1){
CORE_PIN32_CONFIG = PORT_PCR_MUX(2);}
if (mosi == MOSI){
CORE_PIN11_CONFIG = PORT_PCR_DSE | PORT_PCR_MUX(2);}
if (mosi == ALT_MOSI){
CORE_PIN7_CONFIG = PORT_PCR_DSE | PORT_PCR_MUX(2);}
if (mosi == MOSI1){
CORE_PIN0_CONFIG = PORT_PCR_DSE | PORT_PCR_MUX(2);}
if (miso == MISO){
CORE_PIN12_CONFIG = PORT_PCR_MUX(2);}
if (miso == ALT_MISO){
CORE_PIN8_CONFIG = PORT_PCR_MUX(2);}
if (miso == MISO1){
CORE_PIN1_CONFIG = PORT_PCR_MUX(2);}
if (cs == T3_CS0){
CORE_PIN10_CONFIG = PORT_PCR_MUX(2);}
if (cs == ALT_CS0){
CORE_PIN2_CONFIG = PORT_PCR_MUX(2);}
if (cs == T3_SPI1_CS0){
CORE_PIN31_CONFIG = PORT_PCR_MUX(2);}
}
void begin_SLAVE(uint8_t sck, uint8_t mosi, uint8_t miso, uint8_t cs) {
SIM_SCGC6 |= SIM_SCGC6_SPI1; // enable clock to SPI.
setMCR(SLAVE);
setCTAR_SLAVE(8, T3_SPI_MODE0);
SPI1_RSER = 0x00020000;
enablePins_SLAVE(sck, mosi, miso, cs);
}
void setMode(uint8_t CTARn, uint8_t dataMode) {
stop();
if (CTARn==0){
SPI1_CTAR0 = (SPI1_CTAR0 & ~(SPI_CTAR_CPOL | SPI_CTAR_CPHA)) | dataMode << 25;}
if (CTARn==1){
SPI1_CTAR1 = (SPI1_CTAR1 & ~(SPI_CTAR_CPOL | SPI_CTAR_CPHA)) | dataMode << 25;}
if (CTARn==2){
SPI1_CTAR0_SLAVE = (SPI1_CTAR0_SLAVE & ~(SPI_CTAR_CPOL | SPI_CTAR_CPHA)) | dataMode << 25;}
start();
}
void setFrameSize(uint8_t CTARn, uint8_t size) {
stop();
if (CTARn==0){
SPI1_CTAR0 |= SPI_CTAR_FMSZ(size);}
if (CTARn==1){
SPI1_CTAR1 |= SPI_CTAR_FMSZ(size);}
if (CTARn==2){
SPI1_CTAR0_SLAVE |= SPI_CTAR_FMSZ(size);}
start();
}
void setCTAR_SLAVE(uint8_t size, uint8_t dataMode){
SPI1_CTAR0_SLAVE=0;
setFrameSize(T3_CTAR_SLAVE, (size - 1));
setMode(T3_CTAR_SLAVE, dataMode);
}
/* *************** SETUP ***************** */
void setup() {
Serial.printf("Setting up slave\n");
begin_SLAVE(SCK1, MOSI1, MISO1, T3_SPI1_CS0);
Serial.printf("Setting up CTAR\n");
setCTAR_SLAVE(16, T3_SPI_MODE0);
Serial.printf("Setting up receive\n");
setup_dma_receive();
}
void loop() {
delay(1000);
Serial.printf("\n\nBuf %x %x %x %x %x %x\n\n", spi_rx_dest[0], spi_rx_dest[1], spi_rx_dest[4], spi_tx_out[0], spi_tx_out[1], spi_tx_out[2]);
(void) SPI1_POPR;(void) SPI1_POPR;
SPI1_SR |= SPI_SR_RFDF;
dma_rx.enable();
}
The response I have is this (spi master serial output):
Sent: 1 Received: 0
Sent: 2 Received: 0
Sent: 3 Received: beef
Sent: 4 Received: beef
Sent: 5 Received: beef
Sent: 6 Received: beef
Sent: 7 Received: beef
Sent: 8 Received: beef
Sent: 9 Received: beef
Sent: a Received: beef
Sent: b Received: beef
Sent: c Received: beef
Sent: d Received: beef
Sent: e Received: abcd
Sent: f Received: 1
Sent: 10 Received: 1
Sent: 11 Received: 1
Sent: 12 Received: 1
Sent: 13 Received: 1
Sent: 14 Received: 1
Sent: 15 Received: 1
Sent: 16 Received: 1
Sent: 17 Received: 1
Sent: 18 Received: 1
---------------------------------------
Sent: 1 Received: 0
Sent: 2 Received: 0
Sent: 3 Received: beef
Sent: 4 Received: beef
Sent: 5 Received: beef
Sent: 6 Received: beef
Sent: 7 Received: beef
Sent: 8 Received: beef
Sent: 9 Received: beef
Sent: a Received: beef
Sent: b Received: beef
Sent: c Received: beef
Sent: d Received: beef
Sent: e Received: abcd
Sent: f Received: 1
Sent: 10 Received: 2
Sent: 11 Received: 3
Sent: 12 Received: 4
Sent: 13 Received: 5
...................................
Sent: n Received: n - e
Sent: n + 1 Received: dcba
---------------------------------------
Last edited: