tcs_toroeffner/source/TcsBus.cpp

195 lines
4.4 KiB
C++

#include <Arduino.h>
#include "TcsBus.h"
#define TCS_OVERSAMPLING 8
#define TCS_BAUDRATE 475
#define TCS_IDLE_TIMEOUT 64 //8 bits
TcsBus::TcsBus(int sendPin, int receivePin) :
sendPin { sendPin },
receivePin { receivePin },
state { SamplingState::IDLE },
rxBits { 0 },
oversamplingStep { 0 },
defaultRxEvent { 0 },
txEvent { 0 }
{
}
static hw_timer_t* timer = NULL;
static TcsBus* tcsInstance = NULL;
static void onTimer(void)
{
tcsInstance->TimeBase();
}
static bool TimerSetup(TcsBus* inst)
{
if(inst) {
if(timer != NULL) return false; //already set up
tcsInstance = inst;
//time base is 80 MHz, which will be divided by the value from timerBegin, and then
//a counter increments until it reaches the value from timerAlarmWrite. onTimer will then be fired as a callback.
//80000000 / 50 / 421 = 3800.47 Hz = 8 * 475.06
timer = timerBegin(0, 50, true);
timerAttachInterrupt(timer, &onTimer, true);
timerAlarmWrite(timer, 421, true);
timerAlarmEnable(timer);
} else {
if(timer == NULL) return false; //not set up
timerAlarmDisable(timer);
timerDetachInterrupt(timer);
timerEnd(timer);
timer = NULL;
tcsInstance = NULL;
}
return true;
}
void TcsBus::begin()
{
//Pin setup
pinMode(receivePin, INPUT);
pinMode(sendPin, OUTPUT);
digitalWrite(sendPin, LOW); //disable sending for now
//pinMode(0, OUTPUT);
TimerSetup(this);
}
void TcsBus::end()
{
pinMode(sendPin, INPUT);
TimerSetup(NULL);
}
void TcsBus::TimeBase()
{
bool sample = digitalRead(receivePin);
do {
if(state != SamplingState::IDLE) {
if(sample) {
oversamplingSum++;
}
if(oversamplingStep) oversamplingStep--;
if(oversamplingStep) {
return;
}
}
switch(state) {
case SamplingState::IDLE: {
if(!sample) {
state = SamplingState::RECEIVE;
oversamplingStep = TCS_OVERSAMPLING;
oversamplingSum = 0;
currentBitIndex = 0;
memset(rxBits, 0x00, (sizeof(rxBits) / sizeof(uint8_t)));
continue;
}
} break;
case SamplingState::RECEIVE: {
if(oversamplingSum <= TCS_OVERSAMPLING / 4) {
ReceiveBit(0);
} else if(oversamplingSum >= (TCS_OVERSAMPLING * 3) / 4) {
ReceiveBit(1);
} else {
//If received bit is invalid, go to idle wait
EnterWaitIdle();
}
} break;
case SamplingState::TRANSMIT: {
TransmitBit();
} break;
case SamplingState::WAIT_IDLE: {
WaitIdle();
}
}
} while(0);
}
void TcsBus::RegisterDefaultRxEvent(EventData_t* evt)
{
defaultRxEvent = evt;
}
void TcsBus::RegisterEvent(const EventData_t* evt)
{
events.push_back(evt);
}
void TcsBus::UnRegisterEvent(const EventData_t* evt)
{
for(int i = 0; i < events.size(); i++) {
if(evt == events[i]) {
//events.erase(evt); //no, needs iterators
return;
}
}
}
void TcsBus::ReceiveBit(uint8_t currentBit)
{
//push received bit to buffer.
rxBits[currentBitIndex++] = currentBit;
//check against event list
const EventData_t* found = 0;
for(const EventData_t* evt: events) {
//std::cout << value << "\n";
if(currentBitIndex >= evt->bitCount) {
if(!memcmp(rxBits, evt->bits, evt->bitCount)) {
if(found != 0) {
//duplicate, skip
found = 0;
break;
} else {
found = evt;
}
}
}
}
if(found) {
found->func(found);
}
if(found || (currentBitIndex >= (sizeof(rxBits) / sizeof(uint8_t)))) {
//if done or buffer is full, switch to idle wait state
EnterWaitIdle();
}
}
void TcsBus::EnterWaitIdle()
{
state = SamplingState::WAIT_IDLE;
oversamplingStep = TCS_IDLE_TIMEOUT;
oversamplingSum = 0;
}
void TcsBus::TransmitBit()
{
if(currentBitIndex < txEvent->bitCount) {
digitalWrite(sendPin, !txEvent->bits[currentBitIndex++]);
} else {
//disable if done and return to WAIT_IDLE state
digitalWrite(sendPin, 0); //disable tx mosfet
EnterWaitIdle();
}
}
void TcsBus::WaitIdle()
{
//At this point, the error condition was triggered and the timout is over. If the bus is in idle state, we can
//go over to IDLE, otherwise, we must reset the timeout to ensure an idle time on the bus
if(oversamplingSum >= TCS_IDLE_TIMEOUT) {
state = SamplingState::IDLE;
} else {
oversamplingStep = TCS_IDLE_TIMEOUT;
oversamplingSum = 0;
}
}