PaulStoffregen
Well-known member
A new scheme for Ghost posting? Grab one and use part of it to seem like it fits?
Yup, spammers.
(I deleted the message, in case anyone's wondering what this was about...)
A new scheme for Ghost posting? Grab one and use part of it to seem like it fits?
void transfer_lc_fifo(const void * buf, void * retbuf, uint32_t count) {
if (count == 0) return;
const uint8_t *p = (const uint8_t *)buf;
uint8_t *pret = (uint8_t *)retbuf;
uint8_t in;
uint32_t count_in = count;
Serial.println("Try fifo");
Serial.printf("%x %x %x \n", SPI0_S, SPI0_C1, SPI0_C2); Serial.flush();
Serial.printf("%x %x\n", SPI0_C3, SPI0_CI); Serial.flush();
KINETISL_SPI0.C3 |= SPI_C3_FIFOMODE;
Serial.println("fifo turned on"); Serial.flush();
KINETISL_SPI0.S; // Read in status
while (count_in) {
if (count && !(KINETISL_SPI0.S & SPI_S_TXFULLF)) {
// we have more characters to output and fifo not full
KINETISL_SPI0.DL = p? *p++ : 0;
count--;
}
while (!KINETISL_SPI0.S & SPI_S_RFIFOEF) {
// There is data available in fifo queue so extract
in = KINETISL_SPI0.DL;
if (pret) *pret++ = in;
count_in--;
}
}
KINETISL_SPI0.C3 = 0; // turn off FIFO
KINETISL_SPI0.S; // Read in status
}
//=========================================================================
// Try Transfer using DMA.
//=========================================================================
DMAChannel *_dmaTX = NULL;
DMAChannel *_dmaRX = NULL;
uint8_t _dma_state = 0;
uint8_t _dma_dummy_tx = 0;
uint8_t _dma_dummy_rx;
void (*_dma_callback)();
void _dma_rxISR(void) {
Serial.println("_dma_rxISR");
_dmaRX->clearInterrupt();
KINETISL_SPI1.C2 = 0;
_dmaTX->clearComplete();
_dmaRX->clearComplete();
_dma_state = 1; // set back to 1 in case our call wants to start up dma again
if (_dma_callback)
(*_dma_callback)();
}
bool transfer_lc_dma(const void * buf, void * retbuf, uint32_t count, void(*callback)(void)) {
if (!_dma_state) {
Serial.println("First dma call");
_dmaTX = new DMAChannel();
_dmaTX->destination(KINETISL_SPI1.DL);
// _dmaTX->destination(_dma_dummy_tx);
Serial.println("TAD"); Serial.flush();
_dmaTX->triggerAtHardwareEvent(DMAMUX_SOURCE_SPI1_TX);
Serial.println("TAT"); Serial.flush();
_dmaTX->disableOnCompletion();
Serial.println("TDOC"); Serial.flush();
_dmaTX->disable();
Serial.println("TDEST"); Serial.flush();
_dmaRX = new DMAChannel();
_dmaRX->disable();
Serial.println("RDIS"); Serial.flush();
_dmaRX->source(KINETISL_SPI1.DL);
Serial.println("RDEST"); Serial.flush();
_dmaRX->disableOnCompletion();
_dmaRX->triggerAtHardwareEvent(DMAMUX_SOURCE_SPI1_RX);
_dmaRX->attachInterrupt(_dma_rxISR);
_dmaRX->interruptAtCompletion();
_dma_state = 1; // Should be first thing set!
Serial.println("end First dma call");
}
if (_dma_state == 2)
return false; // already active
// Now handle NULL pointers.
if (buf) {
_dmaTX->sourceBuffer((uint8_t*)buf, count);
} else {
_dmaTX->source(_dma_dummy_tx); // maybe have setable value
_dmaTX->transferCount(count);
}
if (retbuf) {
_dmaRX->destinationBuffer((uint8_t*)retbuf, count);
} else {
_dmaRX->destination(_dma_dummy_rx); // NULL ?
_dmaRX->transferCount(count);
}
_dma_callback = callback;
// Now try to start it?
// Setup DMA main object
//Serial.println("Setup _dmatx");
Serial.println("Before DMA C1");
KINETISL_SPI1.C1 &= ~(SPI_C1_SPE);
Serial.println("Before DMA C2");
KINETISL_SPI1.C2 |= SPI_C2_TXDMAE | SPI_C2_RXDMAE;
Serial.println("Before RX enable");
_dmaRX->enable();
Serial.println("Before TX enable");
_dmaTX->enable();
_dma_state = 2;
Serial.println("DMA end of call");
return true;
}
void destination(volatile signed char &p) { destination(*(volatile uint8_t *)&p); }
void destination(volatile unsigned char &p) {
Serial.printf("D: %x %x\n", (uint32_t)&p, (uint32_t)CFG); Serial.flush();
Serial.printf("%x %x %x %x\n\r", (uint32_t)CFG->SAR, (uint32_t)CFG->DAR, CFG->DSR_BCR, CFG->DCR); Serial.flush();
CFG->DCR = (CFG->DCR & 0xF0F0F0FF) | DMA_DCR_DSIZE(1);
Serial.println("set DCR"); Serial.flush();
CFG->DAR = (void*)&p;
Serial.println("set DAR"); Serial.flush();
First dma call
D: 40077006 40008100
0 0 0 20000000
set DCR
DMASPI0.begin();
DMASPI0.start();
// Wonder what happens if I do a SPI.transfer16()
SPI.transfer16(0xffff);
DmaSpi::Transfer trx(nullptr, 0, nullptr);
Serial.println("Testing src -> dest, single transfer");
Serial.println("--------------------------------------------------");
trx = DmaSpi::Transfer(src, DMASIZE, dest);
clrDest((uint8_t*)dest);
DMASPI0.registerTransfer(trx);
while(trx.busy())
{
}
void SPIClass::transfer(const void * buf, void * retbuf, uint32_t count) {
if (count == 0) return;
const uint8_t *p = (const uint8_t *)buf;
uint8_t *pret = (uint8_t *)retbuf;
uint8_t in;
while (!(port.S & SPI_S_SPTEF)) ; // wait
uint8_t out = p ? *p++ : _transferFillTXChar;
port.DL = out;
while (--count > 0) {
[COLOR="#FF0000"]if (p)[/COLOR] {
out = *p++;
}
while (!(port.S & SPI_S_SPTEF)) ; // wait
__disable_irq();
port.DL = out;
while (!(port.S & SPI_S_SPRF)) ; // wait
in = port.DL;
__enable_irq();
[COLOR="#FF0000"]if (pret)[/COLOR]*pret++ = in;
}
while (!(port.S & SPI_S_SPRF)) ; // wait
in = port.DL;
if (pret)*pret = in;
}
This is worth it. It does make a significant performance difference.Another thing I want to decide on, is I had a suggested implementation for the Teensy LC for the Transfer(txbuf, rxbuf, cnt), that broke up the code into three separate sections, READ, WRITE, TRANSFER, as to avoid an if or two in the loop.
Probably a very rare use case.They also special cased the instances where count = 1 or count = 2 and did special code for these cases.
You do. You have 2 transmit bytes scheduled, one in the output shift register and one in port.DL. If you get interrupted, you loose a received byte (only one can be stored). transfer() hangs when a byte is lost.That is the current code looks like:
(Side note to myself - Does it really need to disable interrupts here?)Code:void SPIClass::transfer(const void * buf, void * retbuf, uint32_t count) { if (count == 0) return; const uint8_t *p = (const uint8_t *)buf; uint8_t *pret = (uint8_t *)retbuf; uint8_t in; while (!(port.S & SPI_S_SPTEF)) ; // wait uint8_t out = p ? *p++ : _transferFillTXChar; port.DL = out; while (--count > 0) { [COLOR="#FF0000"]if (p)[/COLOR] { out = *p++; } while (!(port.S & SPI_S_SPTEF)) ; // wait __disable_irq(); port.DL = out; while (!(port.S & SPI_S_SPRF)) ; // wait in = port.DL; __enable_irq(); [COLOR="#FF0000"]if (pret)[/COLOR]*pret++ = in; } while (!(port.S & SPI_S_SPRF)) ; // wait in = port.DL; if (pret)*pret = in; }
Yes. You should also remove the entireWhat I am wondering is does it make enough sense to have three copies of the above code, to remove those two if statements in red for the three different versions?
while (!(port.S & SPI_S_SPRF)) ; // wait
in = port.DL;
__enable_irq();
if (pret)*pret++ = in;
void SPIClass::write(const void * buf, , uint32_t count) {
if (count == 0) return; //while would work here without this but what for character(s) to transmit would hang
while (count-- > 0) {
while (!(port.S & SPI_S_SPTEF)) ; // wait
port.DL = *p++;
}
// Now need to somehow wait until the last character was output and then read in DL to clear.
}
template<bool send, bool receive> __attribute__((always_inline))
void SPIClass::transfer_(const uint8_t* send_ptr, uint8_t* recv_ptr, uint32_t count) {
const uint8_t _transferFillTXChar = 0;
auto& __restrict port = this->port;
uint8_t dummy;
while(!(port.S & SPI_S_SPTEF)) ; // wait
port.DL = send ? *send_ptr++ : _transferFillTXChar;
while(--count > 0) {
while(!(port.S & SPI_S_SPTEF)) ; // wait
__disable_irq();
port.DL = send ? *send_ptr++ : _transferFillTXChar;
while(!(port.S & SPI_S_SPRF)) ; // wait
*(receive ? recv_ptr++ : &dummy) = port.DL;
__enable_irq();
}
while(!(port.S & SPI_S_SPRF)) ; // wait
*(receive ? recv_ptr++ : &dummy) = port.DL;
}
void SPIClass::transfer(const void* buf, void* retbuf, uint32_t count) {
if(count == 0) return;
const uint8_t* send_ptr = (const uint8_t*) buf;
uint8_t* recv_ptr = (uint8_t*) retbuf;
if(send_ptr){
if(recv_ptr) transfer_<true, true>(send_ptr, recv_ptr, count);
else transfer_<true, false>(send_ptr, recv_ptr, count);
} else {
if(recv_ptr) transfer_<false, true>(send_ptr, recv_ptr, count);
else transfer_<false, false>(send_ptr, recv_ptr, count);
}
}
That is a really good idea. The current Arduino situation with stateless callbacks really sucks. E.g. look at the mess of the Encoder library (Encoder.h) and the hoops it jumps through to get 'attach_interrupt()' to emulate a state parameter.As I mentioned in the other thread, I wondered if it made sense to allow the Callback function to maybe have an optional parameter like maybe void* that can be passed to it, that gets passed back.
I saw only one complaint (from Thomas Roell) in the archives and the reasoning behind that complaint is wrong. I would have posted a reply, but the developer mailing list is broken.As I mentioned in the other thread, I wondered if it made sense to allow the Callback function to maybe have an optional parameter like maybe void* that can be passed to it, that gets passed back. I thought I would also ask on the Arduino Mail list and received a few responses were against it....
class S {
public:
void callback();
};
void registerCallback(void (*callback_fn)(void*), void* context) {
// ...
}
void test(S* s) {
// ...
registerCallback([](void* ctx){ ((S*) ctx)->callback(); }, s);
}
uint8_t buffer1_1[6];
uint8_t buffer1_2[512];
volatile uint8_t state1 = 0;
uint8_t buffer2_1[6];
uint8_t buffer2_2[512];
volatile uint8_t state2 = 0;
uint8_t buffer3_1[6];
uint8_t buffer3_2[512];
volatile uint8_t state3 = 0;
void callback1() {
if (state1 == 1) {
SPI.transfer(buffer1_2, NULL, sizeof(buffer1_2), &callback1)
state1 = 2;
} else {
state1 = 0;
}
}
... Same for 2 and 3, but using SPI1 and SPI2...
void loop() {
while (state1 != 0) ;
state1 = 1;
SPI.transfer(buffer1_1, NULL, sizeof(buffer1_1), &callback1);
while (state2 != 0) ;
state2 = 1;
SPI.transfer(buffer2_1, NULL, sizeof(buffer2_1), &callback2);
while (state3 != 0) ;
state3 = 1;
SPI.transfer(buffer3_1, NULL, sizeof(buffer3_1), &callback3);
}
Subscription to the list doesn't work, so email is bounced.@tni - have you tried email to the address mentioned toward the end of the arduino forum posting you mentioned?
I put up a request on the email list, so hopefully they will give some new instructions.Subscription to the list doesn't work, so email is bounced.
#include <SPI.h>
//////////////////////////////////
// TeensyView Object Declaration //
///////////////////////////////////
#define USE_SPI1
//#define USE_SPI2
// Kurt's setup
#define PIN_RESET 15
#define PIN_SCK 13
#define PIN_MOSI 11
#define PIN_DC 21
#define PIN_CS 20
// Setup 2nd one SPI1
#define PIN_RESET1 16
#define PIN_SCK1 32
#define PIN_MOSI1 0
#define PIN_DC1 31
#define PIN_CS1 30
// Pins on connector on Beta T3.6 board (3.3, GND)(48, 47)(57 56) (51 52) (53 55)
#define PIN_RESET2 48
//#define PIN_MISO2 51
#define PIN_MOSI2 52
#define PIN_SCK2 53
#define PIN_DC2 55
#define PIN_CS2 56
// This is real C..p but simple test to see how multipole DMA and
// transfers work called from callback
uint8_t header1[] = {0x21, 0, 0x7f, 0x22, 00, 03};
uint8_t header2[] = {0x21, 0, 0x7f, 0x22, 00, 07}; // larger display.
uint8_t header3[] = {0x21, 0, 0x7f, 0x22, 00, 03};
#define BUFFER1_SIZE 511
#define BUFFER2_SIZE 250
#define BUFFER3_SIZE 128
uint8_t buffer1[BUFFER1_SIZE];
uint16_t buffer1_size = sizeof(buffer1);
volatile uint8_t state1 = 0;
uint8_t buffer2[BUFFER2_SIZE];
uint16_t buffer2_size = sizeof(buffer2);
volatile uint8_t state2 = 0;
uint8_t buffer3[BUFFER2_SIZE];
uint16_t buffer3_size = sizeof(buffer3);
volatile uint8_t state3 = 0;
void setup()
{
while (!Serial && millis() < 3000);
Serial.begin(38400);
SPI.begin();
pinMode(PIN_CS, OUTPUT);
pinMode(PIN_DC, OUTPUT);
digitalWrite(PIN_CS, HIGH);
digitalWrite(PIN_DC, HIGH);
#ifdef USE_SPI1
SPI1.begin();
pinMode(PIN_CS1, OUTPUT);
pinMode(PIN_DC1, OUTPUT);
digitalWrite(PIN_CS1, HIGH);
digitalWrite(PIN_DC1, HIGH);
#else
pinMode(PIN_CS1, OUTPUT); // use to debug
digitalWrite(PIN_CS1, HIGH);
#endif
#ifdef USE_SPI2
SPI2.begin();
pinMode(PIN_CS2, OUTPUT);
pinMode(PIN_DC2, OUTPUT);
#endif
delay(1000); // Delay 1000 ms
}
void callback1() {
if (state1 == 1) {
state1 = 2;
digitalWriteFast(PIN_DC, HIGH);
if (!SPI.transfer(buffer1, NULL, buffer1_size, &callback1))
{
Serial.println("SPI Transfer failed");
}
} else {
state1 = 0;
digitalWriteFast(PIN_CS, HIGH);
SPI.endTransaction();
}
}
#ifdef USE_SPI1
void callback2() {
if (state2 == 1) {
state2 = 2;
digitalWriteFast(PIN_DC1, HIGH);
SPI1.transfer(buffer2, NULL, buffer2_size, &callback2);
} else {
state2 = 0;
digitalWriteFast(PIN_CS1, HIGH);
SPI1.endTransaction();
}
}
#endif
#ifdef USE_SPI2
void callback3() {
if (state3 == 1) {
state3 = 2;
digitalWriteFast(PIN_DC2, HIGH);
SPI2.transfer(buffer3, NULL, buffer3_size, &callback3);
} else {
state3 = 0;
digitalWriteFast(PIN_CS2, HIGH);
SPI2.endTransaction();
}
}
#endif
uint8_t loop_counter = 0;
void loop()
{
elapsedMillis timer;
loop_counter++;
#ifndef USE_SPI1
digitalWrite(PIN_CS1, !digitalRead(PIN_CS1));
#endif
timer = 0;
while (state1 != 0) {
if (timer > 10) {
Serial.printf("Timeout SPI: %d %x %x\n", state1, header1, buffer1);
Serial.printf(" TX: C:%d, err: %d, S: %x, D: %x\n", SPI._dmaTX->complete(), SPI._dmaTX->error(), SPI._dmaTX->sourceAddress(), SPI._dmaTX->destinationAddress());
Serial.printf(" RX: C:%d, err: %d, S: %x, D: %x\n", SPI._dmaRX->complete(), SPI._dmaRX->error(), SPI._dmaRX->sourceAddress(), SPI._dmaRX->destinationAddress());
Serial.println("Hit any key to continue");
while (Serial.read() == -1) ;
while (Serial.read() != -1) ;
break;
}
}
Serial.println("Start 1");
state1 = 1;
memset(buffer1, (loop_counter & 1)? 0xff : 0, buffer1_size);
SPI.beginTransaction(SPISettings(8000000, MSBFIRST, SPI_MODE0));
digitalWriteFast(PIN_CS, LOW);
digitalWriteFast(PIN_DC, LOW);
SPI.transfer(header1, NULL, sizeof(header1), &callback1);
#ifdef USE_SPI1
timer = 0;
while (state2 != 0) {
if (timer > 10) {
Serial.printf("Timeout SPI1: %d %x %x\n", state2, header2, buffer2);
Serial.printf(" TX: C:%d, err: %d, S: %x, D: %x\n", SPI1._dmaTX->complete(), SPI1._dmaTX->error(), SPI1._dmaTX->sourceAddress(), SPI1._dmaTX->destinationAddress());
Serial.printf(" RX: C:%d, err: %d, S: %x, D: %x\n", SPI1._dmaRX->complete(), SPI1._dmaRX->error(), SPI1._dmaRX->sourceAddress(), SPI1._dmaRX->destinationAddress());
Serial.println("Hit any key to continue");
while (Serial.read() == -1) ;
while (Serial.read() != -1) ;
break;
}
}
Serial.println("Start 2");
state2 = 1;
memset(buffer2, (loop_counter & 1)? 0x0 : 0xff, buffer2_size);
SPI1.beginTransaction(SPISettings(8000000, MSBFIRST, SPI_MODE0));
digitalWriteFast(PIN_CS1, LOW);
digitalWriteFast(PIN_DC1, LOW);
SPI1.transfer(header2, NULL, sizeof(header2), &callback2);
#endif
#ifdef USE_SPI2
while (state3 != 0) ;
Serial.println("Start 3");
state3 = 1;
memset(buffer3, (loop_counter & 1)? 0xff : 0, buffer3_size);
SPI2.beginTransaction(SPISettings(8000000, MSBFIRST, SPI_MODE0));
digitalWriteFast(PIN_CS2, LOW);
digitalWriteFast(PIN_DC2, LOW);
SPI2.transfer(header3, NULL, sizeof(header3), &callback3);
#endif
}
_dmaRX->destination((uint8_t&)bit_bucket);
_dmaRX->transferCount(count);
rser = SPI_RSER_RFDF_RE | SPI_RSER_RFDF_DIRS | SPI_RSER_TFFF_RE | SPI_RSER_TFFF_DIRS;
void transferCount(unsigned int len) {
if (len > 32767) return;
if (len >= 512) {
TCD->BITER = len;
TCD->CITER = len;
} else {
TCD->BITER = (TCD->BITER & 0xFE00) | len;
TCD->CITER = (TCD->CITER & 0xFE00) | len;
}
}
void transferCount(unsigned int len) {
if (len > 32767) return;
if (!(TCD->BITER & DMA_TCD_BITER_ELINK))
TCD->BITER = len;
TCD->CITER = len;
} else {
TCD->BITER = (TCD->BITER & 0xFE00) | len;
TCD->CITER = (TCD->CITER & 0xFE00) | len;
}
}