Created
March 27, 2018 15:04
-
-
Save gabonator/4634b32c2d50c79210b198c791a47793 to your computer and use it in GitHub Desktop.
interrupt driven multisampled UART receiver for debugging of serial buses (esp8266 arduino)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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