Skip to content

Instantly share code, notes, and snippets.

@gabonator
Created March 27, 2018 15:04
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save gabonator/4634b32c2d50c79210b198c791a47793 to your computer and use it in GitHub Desktop.
Save gabonator/4634b32c2d50c79210b198c791a47793 to your computer and use it in GitHub Desktop.
interrupt driven multisampled UART receiver for debugging of serial buses (esp8266 arduino)
// interrupt driven multisampled UART receiver
// useful for sniffing on both comm lines of buses with custom uart implementation with
// artifical data length or reversed polarity
#include <RingBufCPP.h>
//#define _ASSERT(cond) {if (!(cond)) {Serial.print("Assertion failed :"); \
Serial.print(#cond); while(1) yield();}}
#define _ASSERT(cond)
enum Config {
BaudRate = 9600,
DataBits = 8,
Text = 0
};
class CTimer
{
typedef void (*THandler)();
public:
void setup(long frequency, THandler handler)
{
timer1_isr_init();
timer1_attachInterrupt(handler);
timer1_enable(TIM_DIV16, TIM_EDGE, TIM_LOOP);
set(frequency);
}
void set(long frequency)
{
const long intervalUs = 1000000UL / frequency;
timer1_write((clockCyclesPerMicrosecond() / 16) * intervalUs);
}
};
class CReceiver
{
enum {
BufferSize = 32
};
RingBufCPP<int, BufferSize> buffer;
int& mError;
public:
CReceiver(int& error) : mError(error)
{
}
void operator << (int n)
{
if (buffer.isFull())
mError = 'R1';
_ASSERT(!buffer.isFull());
buffer.add(n);
}
bool available()
{
return !buffer.isEmpty();
}
int get()
{
int v;
buffer.pull(&v);
return v;
}
};
class CUartStream
{
public:
enum {
StartBit = 1,
DataBits = Config::DataBits,
StopBit = 1,
ReadBits = StartBit + DataBits,
TotalBits = ReadBits + StopBit, // 10
MSB = 1<<DataBits
};
byte index{0};
int value{0};
CReceiver& mReceiver;
int& mError;
byte temp[32];
public:
CUartStream(CReceiver& receiver, int& error) : mReceiver(receiver), mError(error)
{
}
void reset()
{
index = 0;
value = 0;
}
void operator << (byte b)
{
temp[index] = b;
// 0: start bit
//Serial.print(b);
if (index == 0)
{
if (b!=0)
mError = 'U1';
_ASSERT(b==0); // start bit
} else
// 1, 2, 3, 4, 5, 6, 7, 8: data
// 9: mode
if (index < CUartStream::ReadBits) // i < 9 -> 1 2 3 4 5 6 7 8
{
if (b)
value |= MSB;
value >>= 1;
}
if (index == CUartStream::ReadBits) // i == 9
{
if(b!=1)
mError = 'U2';
_ASSERT(b==1); // stop bit
mReceiver << value;
}
if (index >= CUartStream::ReadBits + StopBit)
{
mError = 'U3';
_ASSERT(0);
}
index++;
}
};
class CSampler
{
byte phase{0};
byte b1, b2;
bool error{false};
const int readBits{CUartStream::TotalBits};
CUartStream& mUartStream;
int& mError;
public:
CSampler(CUartStream& stream, int& error) : mUartStream(stream), mError(error)
{
}
void operator << (byte b)
{
if (phase == 0)
{
// waiting for start bit
if (b == 1)
return;
mUartStream.reset();
phase++;
return;
}
if ((phase&3)==1)
b1 = b;
if ((phase&3)==2)
{
b2 = b;
if (b1 != b2)
mError = 'S1';
_ASSERT(b1 == b2);
if (b1 != b2)
{
error = 1;
phase = 0;
return;
}
mUartStream << b1;
}
phase++;
// cut sampling one quarter sooner,
// so it can reliably sync next start bit
if (phase == readBits*4 - 1)
{
phase = 0;
}
}
};
class CMultisampledUart
{
CReceiver mReceiver;
CUartStream mStream;
CSampler mSampler;
int mError{0};
int mPin;
public:
CMultisampledUart(int pin) :
mPin{pin},
mStream{mReceiver, mError},
mSampler{mStream, mError},
mReceiver{mError}
{
pinMode(mPin, INPUT);
}
inline bool isError()
{
return mError;
}
inline int getError()
{
int _error = mError;
mError = 0;
return _error;
}
inline void operator << (byte b)
{
mSampler << b;
}
inline bool available()
{
return mReceiver.available();
}
inline int get()
{
return mReceiver.get();
}
void operator ()()
{
mSampler << digitalRead(mPin);
}
void debugStreamSync(byte b)
{
static byte buffer[256];
static int nOfs = -1;
if (b==1 && nOfs == -1)
{
nOfs = 0;
}
if (nOfs >= 0)
{
buffer[nOfs++] = b;
if (nOfs >= 128)
{
Serial.print("dump: ");
for (int i=0; i<128; i++)
{
if ((i&3)==0)
Serial.print(' ');
Serial.print(buffer[i]);
}
Serial.print("\n");
nOfs = -1;
}
}
}
void debugStreamRaw(byte b)
{
static int nc = 0;
Serial.print(b);
nc++;
if (nc == 80)
{
Serial.print("\n");
nc = 0;
}
}
};
CTimer mTimer;
CMultisampledUart uarts[] = {
CMultisampledUart{D8},
CMultisampledUart{D9}
};
CMultisampledUart* lastUart = NULL;
void setup()
{
Serial.begin(115200);
Serial.print("\nStarting...\n");
Serial.print("Built on " __DATE__ " " __TIME__ " (c) 2018 valky.eu\n");
pinMode(D10, OUTPUT);
digitalWrite(D10, 0);
mTimer.setup(Config::BaudRate*4, []() {
uarts[0]();
uarts[1]();
});
}
void loop()
{
for (int i=0; i<sizeof(uarts)/sizeof(uarts[0]); i++)
{
CMultisampledUart& uart = uarts[i];
if (uart.isError())
{
Serial.print("[UART");
Serial.print(i);
Serial.print(" error: ");
int err = uart.getError();
Serial.print((char)(err>>8));
Serial.print((char)(err));
Serial.print("]\n");
}
if (!Config::Text && uart.available())
{
static int nRcvd = 0;
static long lastData = 0;
long now = millis();
if (now - lastData > 500 || nRcvd++ > 20)
{
Serial.print("\n");
nRcvd = 0;
}
lastData = now;
}
while (uart.available())
{
if (lastUart != &uart)
{
Serial.print("[UART");
Serial.print(i);
Serial.print("]");
lastUart = &uart;
}
if (Config::Text)
{
char c = uart.get();
Serial.print(c);
} else
{
unsigned int data = uart.get();
if (data >= 0x100)
{
Serial.print(data >> 8, HEX);
data &= 255;
}
Serial.print(data >> 4, HEX);
Serial.print(data & 15, HEX);
Serial.print(' ');
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment