Mi caso de uso requiere que envíe Computadora (USB) -> Teensy 4.0 -> SPI -> Periférico. Estoy intentando enviar datos a través de USB (potencialmente serie USB o hay alguna otra forma). Mi requisito de transferencia de datos es enviar datos de 40,16 bits, 4000 veces por segundo, lo que se traduce en alrededor de 2,56 Mbits por segundo.
Planeo almacenar en caché 1 segundo de datos, que son 2560000 bits o 320 Kilobytes o 312,5 Kibibytes
Busco orientación sobre:
En realidad, para una tasa de datos T4.xa de 300kB sobre el puerto serie virtual no es exigente en absoluto.
Sugiero usar un búfer de anillo simple (por ejemplo, https://github.com/tonton81/Circular_Buffer ) para almacenar los datos recibidos.
Para lograr su requisito de transferencia de datos (1 bloque de 40 palabras de 16 bits cada 250 µs), lo mejor sería enviar un bloque de datos en un temporizador ISR e invocar este ISR con 4 kHz.
Aquí hay una prueba del código de principio que muestra cómo lograr esto:
#include "SPI.h"
#include "circular_buffer.h"
Circular_Buffer<uint16_t, 16*1024> ringBuffer; // adjust buffer size if necessary
constexpr unsigned CS = 0;
constexpr size_t blockSize = 40;
void sendSPI()
{
if (ringBuffer.available() >= blockSize) // send only a complete data block
{
SPI.beginTransaction(SPISettings(4'000'000, MSBFIRST, SPI_MODE0));
digitalWriteFast(CS, LOW); // assuming an active low chip select
for (unsigned i = 0; i < blockSize; i++)
{
SPI.transfer16(ringBuffer.read()); // read one entry from the buffer and send it
}
digitalWriteFast(CS, HIGH);
SPI.endTransaction();
}
}
IntervalTimer spiTimer;
void setup()
{
pinMode(CS, OUTPUT);
SPI.begin();
spiTimer.begin(sendSPI, 1'000'000 / 4000); // transfer one block every 250µs
}
uint16_t oldDatum = -1; // error checking, we expect incrementing data from serial
void loop()
{
while (Serial.available() && ringBuffer.available() < ringBuffer.capacity())
{
uint16_t datum;
Serial.readBytes((char*)&datum, 2);
ringBuffer.write(datum);
uint16_t diff = datum - oldDatum;
if(diff != 1)
{
SerialUSB1.printf("Error @%04X\n", datum);
}
oldDatum = datum;
}
}
Siempre que haya espacio disponible, el código copia los datos en serie recibidos en el búfer circular. La parte de envío impulsada por la interrupción del temporizador lee un bloque de 40 palabras del búfer circular y lo envía a su dispositivo.
Para verificar si se pierde algún dato, el código verifica los bytes entrantes e imprime cualquier error (los datos no se incrementan) en la segunda conexión en serie.
Usé la siguiente aplicación C# simple para probar el firmware.
class Program
{
static void Main(string[] args)
{
int bufSize = 2 * 64 * 1024; // generate some dummy data (incrementing 16bit values)
var data = new byte[bufSize]; // any buffer size would work. I chose 128kB to get a nice data wrap around for the simple error detection in the firmware
for (int i = 0; i < bufSize / 2; i++)
{
var bytes = BitConverter.GetBytes(i); // convert from uint16 to byte[4]
data[i * 2] = bytes[0];
data[i * 2 + 1] = bytes[1];
}
// Send out the data as quick as possible, the underlying algorithms will pause sending if the FW doesn't read the bytes from the stream.
// -> this is auto syncing the rate with the FW read rate.
using (var port = new SerialPort("COM11"))
{
port.Open();
while (!Console.KeyAvailable) // stop program on any key press
{
port.Write(data, 0, bufSize); // send out data, driver will pause if necessary
}
}
}
}
}
Tanto FW como SW funcionan sin problemas aquí y no observé ninguna pérdida de datos. El controlador de PC subyacente es lo suficientemente inteligente como para pausar el envío cuando el FW no está leyendo los datos de la transmisión. Por lo tanto, obtiene una sincronización de la tasa de envío y recepción de datos de forma gratuita.
Aquí algunos resultados de medición para la frecuencia SPI de 4 MHz:
Y aquí una medida con frecuencia SPI de 32 MHz (no puedo resolver detalles con mi LA a esta velocidad). Pero puede ver que enviar un bloque (80 bytes) se realiza en aproximadamente 32 µs.
chris stratton
chris stratton
chris stratton
sandeepzgk
sandeepzgk
chris stratton