Andre Germain
Member
Hello,
I've had code running on qty 2 Teensy 4 and then 4.1 for quite a few years detecting the bits on a serial stream with a Tamagawa (qty 4) 23 bit absolute encoder. The PCB I am using has serial 3 and 4 and the bit stream is 2.5 mbs, and earlier when I tried using the UARTs and twiddling the frequency, I had no success (apparently using FlexIO it might be possible, but that's a major PCB rework). Instead I twiddle the Tx pin with a time waster to achieve 2.5 mbs @600 Mhz core rate) using asm NOP, then change the DE/RE state wait the prescribed Tamagawa time for reception, and then capture about 1000 values for the 60 expected Rx bits from the encoder. Then I hover over the table from either end and detect the stream. Yes bizarre, but it has worked for years at 50 samples per second. it used to be that a single bit covered 14 samples, but recently when I rebuild the code due to library upgrades, the encoders were inop, and my code was telling me the bit length was now 8 or so. I checked power, core speed, temperature, and other possible sources and all look normal. I tried another 4.1, same. Strangely, the Tx timing is unchanged and the encoder responds, so it has to be uniquely in the Rx section.
Finally I changed my code to expect the new bit length, but I rather figure what went wrong.
Regards
P.S: Paul, your Teensy 4+ is simply the best computing device I've ever worked on since 1980 and I've touched SEL, VAX, PDP11, Apple, PET, Vic20, C64, Atari, every type of PC ,assembler, punch cards, dos, windows, AI...), picos, so much more, nothing gave me as much pleasure and reliability as your devices and libraries. I tossed a box full of Phidgets and SBCs for your Teensy and my observatories 250 feet yonder are finally reliable and don't need me to maintain them at -30C and +30C, mosquitoes, dark, sunny, snow, ice, at all as often.
// Mount Encoders
#define BIT_LEN 60
#define TXBITLEN 126 // was 126
#define RXBITLEN 109 // was 109
#define STRMDLY 10
#define DBG 23
const int EncDir[2] = {41, 40};
const int EncTx [2] = {17, 15};
const int EncRx [2] = {16, 14};
unsigned char EncData[4][6] = {0, 0, 0, 0};
bool Bits[BIT_LEN];
void TamEncodersInit()
{
// initialize the digital pins of encoders
pinMode(EncDir[0], OUTPUT);
pinMode(EncTx [0], OUTPUT);
pinMode(EncRx [0], INPUT);
pinMode(EncDir[1], OUTPUT);
pinMode(EncTx [1], OUTPUT);
pinMode(EncRx [1], INPUT);
pinMode(DBG, OUTPUT);
}
void TamEncoders()
{
int Offset = 0;
noInterrupts();
if (0) // Method 1 (temporal capture of mid bit)
{
// Encoder 0 handler
EncoderTx(0); // Send command
TimeWaster(TXBITLEN); // Delay before releasing bus
EncoderRx(0); // Read data
// Encoder 1 handler
EncoderTx(1); // Send command
TimeWaster(TXBITLEN); // Delay before releasing bus
EncoderRx(1); // Read data
}
else // Method 2 (atomic capture with post stream scaling bit parsing)
{
// Encoder 0 Streaming handler
EncoderTx(0); // Send command
TimeWaster(STRMDLY); // Delay before releasing bus
EncoderRx2(0); // Read data
// Encoder 1 Streaming handler
EncoderTx(1); // Send command
TimeWaster(STRMDLY); // Delay before releasing bus
EncoderRx2(1); // Read data
Offset = 2;
}
interrupts();
memcpy(&DevIO.TamEnc[0][0], &EncData[1+Offset][0], 6);
memcpy(&DevIO.TamEnc[1][0], &EncData[0+Offset][0], 6);
static int sub = 0;
#define SUB_MAX 25
if ((++sub > SUB_MAX) && 0) // DEBUG Tamagawa encoders
{
sub = 0;
for (int e=0; e<4; e++)
{
for (int n=0; n<6; n++)
{
Serial.print(EncData[e][n], HEX);
Serial.print(" ");
}
int posn = EncData[e][4]<<16 | EncData[e][3]<<8 | EncData[e][2];
Serial.print(posn);
Serial.print(" - ");
}
Serial.println(" ");
} // Print position - debug
}
// Receive encoder reply - time sequenced method
void EncoderRx(int Encoder)
{
int n = 0;
unsigned char Data = 0;
// Search for encoder start bit but timeout otherwise
do
{
if (digitalReadFast(EncRx[Encoder]) == LOW) break;
n++;
} while (n < 2000);
digitalWriteFast(DBG, HIGH); // Debug
TimeWaster(RXBITLEN * 0.1); // Delay to middle of bit
int b = 0;
for (n = 0; n < 6; n++) // Bytes are CF SF DF0 DF1 DF2 CRC
{
digitalWriteFast(DBG, (n & 0x1)); // Debug
digitalReadFast(EncRx[Encoder]); TimeWaster(RXBITLEN); // Ignore start bit
Bits[b++] = digitalReadFast(EncRx[Encoder]); TimeWaster(RXBITLEN);
Bits[b++] = digitalReadFast(EncRx[Encoder]); TimeWaster(RXBITLEN);
Bits[b++] = digitalReadFast(EncRx[Encoder]); TimeWaster(RXBITLEN);
Bits[b++] = digitalReadFast(EncRx[Encoder]); TimeWaster(RXBITLEN);
Bits[b++] = digitalReadFast(EncRx[Encoder]); TimeWaster(RXBITLEN);
Bits[b++] = digitalReadFast(EncRx[Encoder]); TimeWaster(RXBITLEN);
Bits[b++] = digitalReadFast(EncRx[Encoder]); TimeWaster(RXBITLEN);
Bits[b++] = digitalReadFast(EncRx[Encoder]); TimeWaster(RXBITLEN);
digitalReadFast(EncRx[Encoder]); TimeWaster(RXBITLEN); // Ignore stop bit
}
digitalWriteFast(DBG, LOW); // Debug
b = 0;
for (n = 0; n < 6; n++)
{
Data = 0;
for (int m = 0; m < 8; m++) if (Bits[b++]) Data |= (1<<m);
EncData[Encoder][n] = Data;
}
}
// Receive encoder reply - pattern search and scaling method
unsigned char DataStream[ENC_STREAM_SIZE];
void EncoderRx2(int Encoder)
{
unsigned char Data = 0;
static float BitLenMax[2] = {0.f, 0.f};
float BitLen = 0;
int diff = 0;
// Capture Rx stream
unsigned int n = 0;
do
{
DataStream[n] = digitalReadFast(EncRx[Encoder]);
TimeWaster(5);
n++;
} while (n < ENC_STREAM_SIZE);
// Debug
//if (Encoder == 0) memcpy(DevIO.Data, DataStream, sizeof DevIO.Data);
int Start = 0;
int End = ENC_STREAM_SIZE-1;
while (DataStream[Start] && (Start < (ENC_STREAM_SIZE-1))) { Start++; }
while (DataStream[End ] && (End > 1 )) { End--; }
// Check if detected boudaries are valid
if ((Start < End) && (Start < 150) && (End < (ENC_STREAM_SIZE - 150)))
{
// The following is incorrect as the last stop can also include data bits high!!!
// Thereby wrongly detecting the last stop bit position
diff = End - Start;
BitLen = (float)diff / (float)(BIT_LEN - 1); // Last stop bit not counted
if (BitLen > BitLenMax[Encoder]) BitLenMax[Encoder] = BitLen;
BitLen = 7.97; // was 14.07f;
// int BitCnt = 0;
float off = (float)Start + BitLen/2;
int b = 0;
for (n = 0; n < 6; n++) // Bytes are CF SF DF0 DF1 DF2 CRC
{
//if ((Encoder == 0) && (BitCnt<60)) DevIO.DataBits[BitCnt++] = (short)off;
off += BitLen; // Ignore start bit
for (int m=0; m<8; m++)
{
Bits[b++] = DataStream[(int)off];
//if ((Encoder == 0) && (BitCnt<60)) DevIO.DataBits[BitCnt++] = (short)off;
off += BitLen;
}
//if ((Encoder == 0) && (BitCnt<60)) DevIO.DataBits[BitCnt++] = (short)off;
off += BitLen; // Ignore stop bit
}
b = 0;
for (n = 0; n < 6; n++)
{
Data = 0;
for (int m = 0; m < 8; m++) if (Bits[b++]) Data |= (1<<m);
EncData[Encoder+2][n] = Data;
}
}
static int sub = 0;
#define SUB_CNT 100
if ((++sub > SUB_CNT) && 1)
{
if (sub > (SUB_CNT+1)) sub = 0;
Serial.printf("Enc:%1d S:%4u E:%4u df:%4u ln:%5.2f lnmx:%5.2f\n",
Encoder,
Start,
End,
diff,
BitLen,
BitLenMax[Encoder]);
}
}
// Send command to transmit 0010 0000 01 + pause before releasing bus
void EncoderTx(int Encoder)
{
digitalWriteFast(EncDir[Encoder], HIGH); // set bus for master
digitalWriteFast(EncTx [Encoder], LOW ); TimeWaster(TXBITLEN); // 0
digitalWriteFast(EncTx [Encoder], LOW ); TimeWaster(TXBITLEN); // 0`
digitalWriteFast(EncTx [Encoder], HIGH); TimeWaster(TXBITLEN); // 1
digitalWriteFast(EncTx [Encoder], LOW ); TimeWaster(TXBITLEN); // 0
digitalWriteFast(EncTx [Encoder], LOW ); TimeWaster(TXBITLEN); // 0
digitalWriteFast(EncTx [Encoder], LOW ); TimeWaster(TXBITLEN); // 0
digitalWriteFast(EncTx [Encoder], LOW ); TimeWaster(TXBITLEN); // 0
digitalWriteFast(EncTx [Encoder], LOW ); TimeWaster(TXBITLEN); // 0
digitalWriteFast(EncTx [Encoder], LOW ); TimeWaster(TXBITLEN); // 0
digitalWriteFast(EncTx [Encoder], HIGH); TimeWaster(TXBITLEN); // 1
digitalWriteFast(EncDir[Encoder], LOW); // set bus for slave
}
void TimeWaster(int repetitions)
{
int n = 0;
for (n = 0; n < repetitions; n++)
{
__asm__("nop");
}
}
I've had code running on qty 2 Teensy 4 and then 4.1 for quite a few years detecting the bits on a serial stream with a Tamagawa (qty 4) 23 bit absolute encoder. The PCB I am using has serial 3 and 4 and the bit stream is 2.5 mbs, and earlier when I tried using the UARTs and twiddling the frequency, I had no success (apparently using FlexIO it might be possible, but that's a major PCB rework). Instead I twiddle the Tx pin with a time waster to achieve 2.5 mbs @600 Mhz core rate) using asm NOP, then change the DE/RE state wait the prescribed Tamagawa time for reception, and then capture about 1000 values for the 60 expected Rx bits from the encoder. Then I hover over the table from either end and detect the stream. Yes bizarre, but it has worked for years at 50 samples per second. it used to be that a single bit covered 14 samples, but recently when I rebuild the code due to library upgrades, the encoders were inop, and my code was telling me the bit length was now 8 or so. I checked power, core speed, temperature, and other possible sources and all look normal. I tried another 4.1, same. Strangely, the Tx timing is unchanged and the encoder responds, so it has to be uniquely in the Rx section.
Finally I changed my code to expect the new bit length, but I rather figure what went wrong.
Regards
P.S: Paul, your Teensy 4+ is simply the best computing device I've ever worked on since 1980 and I've touched SEL, VAX, PDP11, Apple, PET, Vic20, C64, Atari, every type of PC ,assembler, punch cards, dos, windows, AI...), picos, so much more, nothing gave me as much pleasure and reliability as your devices and libraries. I tossed a box full of Phidgets and SBCs for your Teensy and my observatories 250 feet yonder are finally reliable and don't need me to maintain them at -30C and +30C, mosquitoes, dark, sunny, snow, ice, at all as often.
// Mount Encoders
#define BIT_LEN 60
#define TXBITLEN 126 // was 126
#define RXBITLEN 109 // was 109
#define STRMDLY 10
#define DBG 23
const int EncDir[2] = {41, 40};
const int EncTx [2] = {17, 15};
const int EncRx [2] = {16, 14};
unsigned char EncData[4][6] = {0, 0, 0, 0};
bool Bits[BIT_LEN];
void TamEncodersInit()
{
// initialize the digital pins of encoders
pinMode(EncDir[0], OUTPUT);
pinMode(EncTx [0], OUTPUT);
pinMode(EncRx [0], INPUT);
pinMode(EncDir[1], OUTPUT);
pinMode(EncTx [1], OUTPUT);
pinMode(EncRx [1], INPUT);
pinMode(DBG, OUTPUT);
}
void TamEncoders()
{
int Offset = 0;
noInterrupts();
if (0) // Method 1 (temporal capture of mid bit)
{
// Encoder 0 handler
EncoderTx(0); // Send command
TimeWaster(TXBITLEN); // Delay before releasing bus
EncoderRx(0); // Read data
// Encoder 1 handler
EncoderTx(1); // Send command
TimeWaster(TXBITLEN); // Delay before releasing bus
EncoderRx(1); // Read data
}
else // Method 2 (atomic capture with post stream scaling bit parsing)
{
// Encoder 0 Streaming handler
EncoderTx(0); // Send command
TimeWaster(STRMDLY); // Delay before releasing bus
EncoderRx2(0); // Read data
// Encoder 1 Streaming handler
EncoderTx(1); // Send command
TimeWaster(STRMDLY); // Delay before releasing bus
EncoderRx2(1); // Read data
Offset = 2;
}
interrupts();
memcpy(&DevIO.TamEnc[0][0], &EncData[1+Offset][0], 6);
memcpy(&DevIO.TamEnc[1][0], &EncData[0+Offset][0], 6);
static int sub = 0;
#define SUB_MAX 25
if ((++sub > SUB_MAX) && 0) // DEBUG Tamagawa encoders
{
sub = 0;
for (int e=0; e<4; e++)
{
for (int n=0; n<6; n++)
{
Serial.print(EncData[e][n], HEX);
Serial.print(" ");
}
int posn = EncData[e][4]<<16 | EncData[e][3]<<8 | EncData[e][2];
Serial.print(posn);
Serial.print(" - ");
}
Serial.println(" ");
} // Print position - debug
}
// Receive encoder reply - time sequenced method
void EncoderRx(int Encoder)
{
int n = 0;
unsigned char Data = 0;
// Search for encoder start bit but timeout otherwise
do
{
if (digitalReadFast(EncRx[Encoder]) == LOW) break;
n++;
} while (n < 2000);
digitalWriteFast(DBG, HIGH); // Debug
TimeWaster(RXBITLEN * 0.1); // Delay to middle of bit
int b = 0;
for (n = 0; n < 6; n++) // Bytes are CF SF DF0 DF1 DF2 CRC
{
digitalWriteFast(DBG, (n & 0x1)); // Debug
digitalReadFast(EncRx[Encoder]); TimeWaster(RXBITLEN); // Ignore start bit
Bits[b++] = digitalReadFast(EncRx[Encoder]); TimeWaster(RXBITLEN);
Bits[b++] = digitalReadFast(EncRx[Encoder]); TimeWaster(RXBITLEN);
Bits[b++] = digitalReadFast(EncRx[Encoder]); TimeWaster(RXBITLEN);
Bits[b++] = digitalReadFast(EncRx[Encoder]); TimeWaster(RXBITLEN);
Bits[b++] = digitalReadFast(EncRx[Encoder]); TimeWaster(RXBITLEN);
Bits[b++] = digitalReadFast(EncRx[Encoder]); TimeWaster(RXBITLEN);
Bits[b++] = digitalReadFast(EncRx[Encoder]); TimeWaster(RXBITLEN);
Bits[b++] = digitalReadFast(EncRx[Encoder]); TimeWaster(RXBITLEN);
digitalReadFast(EncRx[Encoder]); TimeWaster(RXBITLEN); // Ignore stop bit
}
digitalWriteFast(DBG, LOW); // Debug
b = 0;
for (n = 0; n < 6; n++)
{
Data = 0;
for (int m = 0; m < 8; m++) if (Bits[b++]) Data |= (1<<m);
EncData[Encoder][n] = Data;
}
}
// Receive encoder reply - pattern search and scaling method
unsigned char DataStream[ENC_STREAM_SIZE];
void EncoderRx2(int Encoder)
{
unsigned char Data = 0;
static float BitLenMax[2] = {0.f, 0.f};
float BitLen = 0;
int diff = 0;
// Capture Rx stream
unsigned int n = 0;
do
{
DataStream[n] = digitalReadFast(EncRx[Encoder]);
TimeWaster(5);
n++;
} while (n < ENC_STREAM_SIZE);
// Debug
//if (Encoder == 0) memcpy(DevIO.Data, DataStream, sizeof DevIO.Data);
int Start = 0;
int End = ENC_STREAM_SIZE-1;
while (DataStream[Start] && (Start < (ENC_STREAM_SIZE-1))) { Start++; }
while (DataStream[End ] && (End > 1 )) { End--; }
// Check if detected boudaries are valid
if ((Start < End) && (Start < 150) && (End < (ENC_STREAM_SIZE - 150)))
{
// The following is incorrect as the last stop can also include data bits high!!!
// Thereby wrongly detecting the last stop bit position
diff = End - Start;
BitLen = (float)diff / (float)(BIT_LEN - 1); // Last stop bit not counted
if (BitLen > BitLenMax[Encoder]) BitLenMax[Encoder] = BitLen;
BitLen = 7.97; // was 14.07f;
// int BitCnt = 0;
float off = (float)Start + BitLen/2;
int b = 0;
for (n = 0; n < 6; n++) // Bytes are CF SF DF0 DF1 DF2 CRC
{
//if ((Encoder == 0) && (BitCnt<60)) DevIO.DataBits[BitCnt++] = (short)off;
off += BitLen; // Ignore start bit
for (int m=0; m<8; m++)
{
Bits[b++] = DataStream[(int)off];
//if ((Encoder == 0) && (BitCnt<60)) DevIO.DataBits[BitCnt++] = (short)off;
off += BitLen;
}
//if ((Encoder == 0) && (BitCnt<60)) DevIO.DataBits[BitCnt++] = (short)off;
off += BitLen; // Ignore stop bit
}
b = 0;
for (n = 0; n < 6; n++)
{
Data = 0;
for (int m = 0; m < 8; m++) if (Bits[b++]) Data |= (1<<m);
EncData[Encoder+2][n] = Data;
}
}
static int sub = 0;
#define SUB_CNT 100
if ((++sub > SUB_CNT) && 1)
{
if (sub > (SUB_CNT+1)) sub = 0;
Serial.printf("Enc:%1d S:%4u E:%4u df:%4u ln:%5.2f lnmx:%5.2f\n",
Encoder,
Start,
End,
diff,
BitLen,
BitLenMax[Encoder]);
}
}
// Send command to transmit 0010 0000 01 + pause before releasing bus
void EncoderTx(int Encoder)
{
digitalWriteFast(EncDir[Encoder], HIGH); // set bus for master
digitalWriteFast(EncTx [Encoder], LOW ); TimeWaster(TXBITLEN); // 0
digitalWriteFast(EncTx [Encoder], LOW ); TimeWaster(TXBITLEN); // 0`
digitalWriteFast(EncTx [Encoder], HIGH); TimeWaster(TXBITLEN); // 1
digitalWriteFast(EncTx [Encoder], LOW ); TimeWaster(TXBITLEN); // 0
digitalWriteFast(EncTx [Encoder], LOW ); TimeWaster(TXBITLEN); // 0
digitalWriteFast(EncTx [Encoder], LOW ); TimeWaster(TXBITLEN); // 0
digitalWriteFast(EncTx [Encoder], LOW ); TimeWaster(TXBITLEN); // 0
digitalWriteFast(EncTx [Encoder], LOW ); TimeWaster(TXBITLEN); // 0
digitalWriteFast(EncTx [Encoder], LOW ); TimeWaster(TXBITLEN); // 0
digitalWriteFast(EncTx [Encoder], HIGH); TimeWaster(TXBITLEN); // 1
digitalWriteFast(EncDir[Encoder], LOW); // set bus for slave
}
void TimeWaster(int repetitions)
{
int n = 0;
for (n = 0; n < repetitions; n++)
{
__asm__("nop");
}
}