xxxajk
Well-known member
Noticing that if I try and do a DMA transfer to/from SPI to DMAMEM that if the transfer > 64K I end up with garbage on the output, and no clue if the input is corrupting anything.
So the question is, _WITHOUT_ using any ISRs, how can I do a continuous transfer of 200KB in the background using DMA?
Attaching pictures and sketch...
1MHz/2Mhz work as expected, but then, it's only looping 25000/50000 bytes
But 4MHz,8MHz, nup...
And here's the sketch:
So the question is, _WITHOUT_ using any ISRs, how can I do a continuous transfer of 200KB in the background using DMA?
Attaching pictures and sketch...
1MHz/2Mhz work as expected, but then, it's only looping 25000/50000 bytes
But 4MHz,8MHz, nup...
And here's the sketch:
C:
#include <Arduino.h>
#include <DMAChannel.h>
#include <SPI.h>
/*
* Windowed SPI slave mode loopback using DMA
*
* Connect pin 23 to pin 13 for sampler clock
* Connect pin 9 to 10 for !CS (spi frame reset)
* Connect pin 11 to 12 for loopback
*
*
* Cicular single buffer copies back to self via DMA. RX should be behind TX, always.
*
*/
/*
* Set the sample rate to one of 1,2,4,8
*
* Monitor pins 11/12 on oscilloscope in dropout mode.
* 1MHz MAX_BUFFER_SIZE: 200000, window_bytes: 25000 use 1000nS for detecting dropouts WORKS
* 2MHz MAX_BUFFER_SIZE: 200000, window_bytes: 50000 use 500nS for detecting dropouts WORKS
* 4MHz MAX_BUFFER_SIZE: 200000, window_bytes: 100000 use 250nS for detecting dropouts BAD_OUTPUT
* 8MHz MAX_BUFFER_SIZE: 200000, window_bytes: 200000 use 125nS for detecting dropouts BAD_OUTPUT
*
*/
uint32_t sample_rate = 1; // MHz
#define SPIS SPI
#define SPIS_CSI 10
#define SPIS_CSO 9
#define CLOCK_OUT 23
#define SPIS_DMAMUX_SOURCE_RX DMAMUX_SOURCE_LPSPI4_RX
#define SPIS_DMAMUX_SOURCE_TX DMAMUX_SOURCE_LPSPI4_TX
#define NSEC_SAM ((uint32_t)200000000u)
#define NSECS(x) ((uint32_t)((uint32_t)1000u/x))
#define MAX_RATE ((uint32_t)8u)
#define BYTES_AT_MHZ(x) ((uint32_t)((NSEC_SAM/NSECS(x))/8u))
#define MAX_BUFFER_SIZE (BYTES_AT_MHZ(MAX_RATE))
DMAChannel rx(false);
DMAChannel tx(false);
IMXRT_LPSPI_t *spis_regs = &IMXRT_LPSPI4_S;
uint32_t windowed_bytes;
DMAMEM uint8_t sample_buffer[MAX_BUFFER_SIZE]__attribute__((aligned(32)));
extern "C" uint32_t set_arm_clock(uint32_t frequency);
void initSPISlaveDMA() {
rx.begin(true);
rx.source((uint8_t &) spis_regs->RDR);
rx.destinationCircular(sample_buffer, windowed_bytes);
rx.triggerAtHardwareEvent(SPIS_DMAMUX_SOURCE_RX);
rx.disable();
tx.begin(true);
tx.sourceCircular(sample_buffer, windowed_bytes);
tx.destination((uint8_t &) spis_regs->TDR);
tx.triggerAtHardwareEvent(SPIS_DMAMUX_SOURCE_TX);
tx.disable();
}
void initSPISlave() {
spis_regs->CR &= ~LPSPI_CR_MEN;
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(7); //8 bit Mode
spis_regs->DER = LPSPI_DER_RDDE | LPI2C_MDER_TDDE; //RX/TX DMA Request Enable
spis_regs->FCR = 4; // buffer 4 bytes
spis_regs->CR |= LPSPI_CR_MEN; //Enable SPI Module!
}
bool started=false;
void stop_clock() {
digitalWriteFast(SPIS_CSO, HIGH); // De-select SPI
analogWrite(CLOCK_OUT, 7); // stop clocking
if(started) {
rx.disable();
tx.disable();
started=false;
}
}
void start_clock() {
rx.destinationCircular(sample_buffer, windowed_bytes);
tx.sourceCircular(sample_buffer, windowed_bytes);
rx.enable();
tx.enable();
started=true;
digitalWriteFast(SPIS_CSO, LOW); // should reset input and I hope output...
analogWrite(CLOCK_OUT, 4); // 3 or 4, close enough
}
void set_clock_freq() {
stop_clock();
analogWriteFrequency(CLOCK_OUT,1000000*sample_rate);
}
void set_windowed_bytes(void) {
if(sample_rate>8) sample_rate=8;
if(sample_rate<1) sample_rate=1;
windowed_bytes=BYTES_AT_MHZ(sample_rate);
Serial.printf("\nMAX_BUFFER_SIZE: %i, windowed_bytes: %i\n\n", MAX_BUFFER_SIZE, windowed_bytes);
}
void dump_buf() {
int j=0;
for(int i=0; i<windowed_bytes; i++) {
Serial.printf("%0.2x ", sample_buffer[i]);
j++;
if(j==64) {
j=0;
Serial.printf("\n");
}
}
Serial.printf("\n%i\n\n",windowed_bytes);
Serial.flush();
}
void poison_samples(uint32_t count) {
for(int i=0; i<MAX_BUFFER_SIZE; i++) {
sample_buffer[i]=i<count?0x55:0x81;
}
}
void setup()
{
// manipulate clock.
set_arm_clock(64000000); // 640, slight overclocking required for perfect clocking.
analogWriteResolution(3);
pinMode(SPIS_CSO, OUTPUT); // pin 9, connects to pin 10
pinMode(CLOCK_OUT, OUTPUT); // pin 23, outputs to external logic and pin 13 (CLK)
set_clock_freq();
while (!Serial) ; // wait
SPIS.begin();
pinMode(SPIS_CSI, INPUT);
SPIS.setCS(SPIS_CSI);
IOMUXC_SW_PAD_CTL_PAD_GPIO_B0_01 = IOMUXC_PAD_DSE(3) | IOMUXC_PAD_SPEED(3) | IOMUXC_PAD_PKE; /* LPSPI4 IN (MISO) 12 */
IOMUXC_SW_PAD_CTL_PAD_GPIO_B0_02 = IOMUXC_PAD_DSE(3) | IOMUXC_PAD_SPEED(3) | IOMUXC_PAD_PKE; /* LPSPI4 OUT (MOSI) 11 */
IOMUXC_SW_PAD_CTL_PAD_GPIO_B0_03 = IOMUXC_PAD_DSE(3) | IOMUXC_PAD_SPEED(3) | IOMUXC_PAD_PKE; /* LPSPI4 CLK (SCLK) 13 */
IOMUXC_SW_PAD_CTL_PAD_GPIO_B0_00 = IOMUXC_PAD_DSE(3) | IOMUXC_PAD_SPEED(3) | IOMUXC_PAD_PKE; /* LPSPI4 CSI (NCS) 10 */
set_windowed_bytes();
poison_samples(windowed_bytes); // test if we go too far
initSPISlave();
initSPISlaveDMA();
// for now...
start_clock();
}
void loop()
{
// Nothing to see here...
}