Hello, I am working on building a data acquisition system for 5x FUTEK LSB206 Load cells with UART output using a Teensy 4.1. The load cells use QIA128 controller chips to interface with a host system via UART, which is more thoroughly documented here. The load cells offer a "Stream" Mode, which, upon initialization, causes the device to send 4 byte packets containing ADC readings to the host over UART. The structure of the packets are:
Where the checksum is calculated:
For my initial prototyping I am just using a single load cell.
To the Teensy 4.1, I have attached the load cell's Vin to 3.3V, Gnd to Gnd, and Rx and Tx to Tx1 and Rx1.
I am using Teensyduino and the Arduino IDE. I have written a basic class to send/read commands to/from the load cell using Serial1, which I have used successfully to collect adc data from the single load cell in stream mode.
Given that I will be using 5x of these load cells eventually, I want to use DMA to have the packets being received by the teensy be automatically read into a ping-pong buffer so that it can be saved to a file on an sd card via an interrupt at the end of each half of the buffer.
The full source code is attached to this post, but below is the main script.
As you can see, I have configured the TCD of the dma channel to trigger on Serial Recieve (DMAMUX_SOURCE_LPUART6_RX), to read 4 bytes (1 sample) on each minor loop, with 1 minor loop per major loop. I've attached an ISR to run upon major loop completion, which checks if TDC_DADDR is either at the watermark or at the end of the rx buffer. if so, it writes the first or second half respectively of the buffer to a file on the sd card. If TCD_DADDR is at the end of the buffer, the ISR will additionally reset TCD_DADDR to the beginning of the buffer.
What I am expecting is an output like this:
However, what I am seeing instead is:
My immediate reaction is strong confusion as to how delay() seems to be exiting before 20000ms elapse, especially since it seems like nothing is crashing, and the program exits normally. Additionally, the data that is saved to the SD card is staggered, and the checksums are missing:
.
0x7f08dd, 0x7f08dc, 0x7f08de are all readings that I would expect from the load cell, but I do not understand why the checksum is missing, or why they are striped with 0x0000 so regularly.
Interestingly enough, when I set the number of bytes read per minor loop to 1, or 3 instead of 4, the serial output returns to a more expected output, running for the full 20 seconds, however the bytes written to the SD card are more illegible:
This seems seems like packets are bing are being clipped somehow.
If you have any thoughts or theories about what might be happening here, I would love to hear them. Any help is enormously appreciated!
Thanks a million,
Lev
Code:
<Highest Significance Byte> <Middle Significance Byte> <Lowest Significance Byte> <Checksum>
Code:
Checksum = (HSB * 1) + (MSB * 2) + (LSB * 3)
For my initial prototyping I am just using a single load cell.
To the Teensy 4.1, I have attached the load cell's Vin to 3.3V, Gnd to Gnd, and Rx and Tx to Tx1 and Rx1.
I am using Teensyduino and the Arduino IDE. I have written a basic class to send/read commands to/from the load cell using Serial1, which I have used successfully to collect adc data from the single load cell in stream mode.
Given that I will be using 5x of these load cells eventually, I want to use DMA to have the packets being received by the teensy be automatically read into a ping-pong buffer so that it can be saved to a file on an sd card via an interrupt at the end of each half of the buffer.
The full source code is attached to this post, but below is the main script.
Code:
#include <SD.h>
#include <DMAChannel.h>
#include "FutekLoadCell.h"
#include <imxrt.h> // imx manual: https://www.pjrc.com/teensy/IMXRT1060RM_rev3_annotations.pdf
#define DMA_BUFFER_SIZE 1024
#define DMA_BUFFER_WATER DMA_BUFFER_SIZE / 2
volatile unsigned char dma_rx_buffer[DMA_BUFFER_SIZE];
float force_reading;
FutekLoadCell loadcell(&Serial1, 320000);
File dump_fp;
DMAChannel dma;
uint32_t start, isr_calls;
void setup()
{
Serial.begin(115200);
while(!Serial);
// SD card setup
if(!SD.begin(BUILTIN_SDCARD)) {
Serial.println("SD Card not present or setup failed");
return;
}
SD.remove("dma_dump");
dump_fp = SD.open("dma_dump", FILE_WRITE);
loadcell.begin();
// Sample Rate Change
// loadcell.writeCmd(SPSPR_4SPS);
// loadcell.writeCmd(SPSPR_20SPS);
loadcell.writeCmd(SPSPR_100SPS);
// loadcell.writeCmd(SPSPR_850SPS);
delay(500); // Sample Rate Change takes up to 500ms
char header[5];
loadcell.readPayload(header, 5); // parse 5 byte confirmation packet from load cell
LPUART6_BAUD |= LPUART_BAUD_RDMAE; // set Receiver Full DMA Enable in Serial Baud register (manual pg 2921)
while(loadcell.available())
loadcell.readByte();
// Enable Stream Mode
loadcell.setStreamState(1);
delay(500);
loadcell.readPayload(header, 5); // parse 5 byte confirmation packet from load cell
// Configure DMA for UART
dma.source(LPUART6_DATA); // Use Serial1 as the source
dma.destinationBuffer(dma_rx_buffer, (unsigned int) DMA_BUFFER_SIZE); // Set the destination buffer
dma.transferSize(4); // Transfer 1 byte at a time
dma.transferCount(1); // Set number of bytes to transfer
dma.TCD->CSR &= !DMA_TCD_CSR_ESG; // Disable Scatter Gather
dma.TCD->DLASTSGA = 0; // Destination Address Offset at the end of each Major Loop
dma.triggerAtHardwareEvent(DMAMUX_SOURCE_LPUART6_RX); // Trigger on UART RX
dma.attachInterrupt(isr);
dma.interruptAtCompletion();
dma.enable();
start = millis();
Serial.printf("start time: %dms\n",start);
delay(20000);
dump_fp.close();
LPUART6_BAUD &= !LPUART_BAUD_RDMAE;
dma.disable();
Serial.println("DONE");
Serial.printf("elapsed time: %dms\n",millis()-start);
Serial.printf("isr calls: %d\n\n\n",isr_calls);
}
void loop()
{
}
void isr()
{
uint32_t daddr;
const char *src;
++isr_calls;
daddr = (uint32_t)(dma.TCD->DADDR);
dma.clearInterrupt();
if((daddr == (uint32_t)dma_rx_buffer + DMA_BUFFER_WATER)) {
// "PING"
// DMA is recieving to the second half of the buffer
// need to remove data from the first half
Serial.println("copying from first half");
src = (const char *)dma_rx_buffer;
}
else if(daddr == (uint32_t)dma_rx_buffer + DMA_BUFFER_SIZE - 1) {
// "PONG"
// DMA is recieving to the first half of the buffer
// need to remove data from the second half
Serial.println("copying from second half");
src = (const char *)dma_rx_buffer + DMA_BUFFER_WATER;
dma.TCD->DADDR = dma_rx_buffer; // reset dma destination to start of buffer
}
else {
return;
}
arm_dcache_delete((void *)src, DMA_BUFFER_WATER);
dump_fp.write(src, DMA_BUFFER_WATER);
}
As you can see, I have configured the TCD of the dma channel to trigger on Serial Recieve (DMAMUX_SOURCE_LPUART6_RX), to read 4 bytes (1 sample) on each minor loop, with 1 minor loop per major loop. I've attached an ISR to run upon major loop completion, which checks if TDC_DADDR is either at the watermark or at the end of the rx buffer. if so, it writes the first or second half respectively of the buffer to a file on the sd card. If TCD_DADDR is at the end of the buffer, the ISR will additionally reset TCD_DADDR to the beginning of the buffer.
What I am expecting is an output like this:
Code:
start time: 1766ms
copying from first half
copying from second half
copying from first half
copying from second half
....
....
copying from first half
copying from second half
DONE
elapsed time: 201766ms
However, what I am seeing instead is:
Code:
start time: 1766ms
sizeof int: 4
copying from first half
DONE
elapsed time: 8ms
isr calls: 287
My immediate reaction is strong confusion as to how delay() seems to be exiting before 20000ms elapse, especially since it seems like nothing is crashing, and the program exits normally. Additionally, the data that is saved to the SD card is staggered, and the checksums are missing:
Code:
7f08 0000 7f08 0000 dd00 0000 7f08 0000
dc00 0000 7f08 0000 dc00 0000 7f08 0000
dd00 0000 7f08 0000 dd00 0000 7f08 0000
de00 0000 7f08 0000 dd00 0000 7f08 0000
dd00 0000 7f08 0000 de00 0000 7f08 0000
de00 0000 7f08 0000 de00 0000 7f08 0000
0x7f08dd, 0x7f08dc, 0x7f08de are all readings that I would expect from the load cell, but I do not understand why the checksum is missing, or why they are striped with 0x0000 so regularly.
Interestingly enough, when I set the number of bytes read per minor loop to 1, or 3 instead of 4, the serial output returns to a more expected output, running for the full 20 seconds, however the bytes written to the SD card are more illegible:
Code:
7f7f de7f dc7f dc7f dc7f dc7f dc7f dd7f
dd7f dd7f dd7f dd7f dc7f dd7f dd7f dd7f
dd7f dc7f dd7f dd7f dd7f de7f dd7f dd7f
de7f dd7f dc7f dc7f dc7f dc7f db7f dc7f
dd7f dc7f db7f dc7f dd7f dc7f dc7f dc7f
dd7f dd7f dd7f dd7f dd7f dd7f dc7f dc7f
If you have any thoughts or theories about what might be happening here, I would love to hear them. Any help is enormously appreciated!
Thanks a million,
Lev
Attachments
Last edited: