diff --git a/source/Pca9554.cpp b/source/Pca9554.cpp new file mode 100644 index 0000000..84ed628 --- /dev/null +++ b/source/Pca9554.cpp @@ -0,0 +1,56 @@ +#include +#include "Pca9554.h" + +enum class Pca9554_Registers : uint8_t { + INPUT_PORT = 0x00, + OUTPUT_PORT = 0x01, + POLARITY_INVERSION = 0x02, + CONFIGURATION = 0x03, +}; + +Pca9554::Pca9554(uint8_t slaveAddress) : + outputValues { 0 }, slaveAddress { slaveAddress }, inputMask { 0 } +{ + +} + +Pca9554::~Pca9554() +{ + +} + +void Pca9554::begin(uint8_t inputMask, uint8_t outputs) +{ + SetInputPins(inputMask); + SendOutputValues(outputValues = outputs); +} + +void Pca9554::SetInputPins(uint8_t inputMask) +{ + this->inputMask = inputMask; + Wire.beginTransmission(slaveAddress | 0x00); + Wire.write((uint8_t) Pca9554_Registers::CONFIGURATION); + Wire.write(inputMask); + Wire.endTransmission(); +} + +uint8_t Pca9554::ReadInputs() +{ + Wire.beginTransmission(slaveAddress | 0x01); + int result = Wire.read() & inputMask; + Wire.endTransmission(); + return (uint8_t) result; +} + +void Pca9554::SendOutputValues() +{ + SendOutputValues(outputValues); +} + +void Pca9554::SendOutputValues(uint8_t values) +{ + Wire.beginTransmission(slaveAddress | 0x00); + Wire.write((uint8_t) Pca9554_Registers::OUTPUT_PORT); + Wire.write(values & ~inputMask); + Wire.endTransmission(); +} diff --git a/source/Pca9554.h b/source/Pca9554.h new file mode 100644 index 0000000..4f694cb --- /dev/null +++ b/source/Pca9554.h @@ -0,0 +1,26 @@ +#ifndef PCA9554_H_ +#define PCA9554_H_ 1 +#include + +#define PCA9554_ADDR 0x20 //PCA9554 = 0 1 0 0 a2 a1 a0 + +class Pca9554 { +public: + Pca9554(uint8_t slaveAddress = PCA9554_ADDR); + ~Pca9554(); + + void begin(uint8_t inputMask = 0, uint8_t outputs = 0); + void SetInputPins(uint8_t inputMask); + uint8_t ReadInputs(); + void SendOutputValues(); + void SendOutputValues(uint8_t values); + + uint8_t outputValues; + +private: + const uint8_t slaveAddress; + uint8_t inputMask; +}; + + +#endif //PCA9554_H_ \ No newline at end of file diff --git a/source/TcsBus.cpp b/source/TcsBus.cpp index 343a6a6..5df32b4 100644 --- a/source/TcsBus.cpp +++ b/source/TcsBus.cpp @@ -3,28 +3,16 @@ #define TCS_OVERSAMPLING 8 #define TCS_BAUDRATE 475 - - -//tcs_bus datagrams -static const unsigned char acknowledge[] = {0,0,0,1,0,1,0,1,1,0}; -static const unsigned char ack_audio_on[] = {0,0,0,1,0,1,0,0,1,1,0,0}; -static const unsigned char doorbell[] = {0,0,0,1,1,0,1,0,1,0,1,1,0,1,1,0,1,1,0,1,1,0,1,1,0,0,1,1,0,1,0,1,0,1,0,0,1,1,0,0,1,0,1,0,1,0,1,0,0,1}; -static const unsigned char door_open[] = {0,0,0,1,0,1,0,1,1,0,1,0,1,1,0,0,1,0,1,0,1,0,1,0}; -static const unsigned char master_hmmmm[] = {0,0,0,1,0,1,0,0,1,0,1,0,0,1,0,0,1,0,1,0,1,0,1,0,1}; -static const unsigned char pickup[] = {0,0,0,1,1,0,1,0,0,1,1,0,1,1,0,1,1,0,1,1,0,1,1,0,1,1,0,0,1,1,0,1,0,1,0,1,0,0,1,1,0,0,1,0,1,0,1,0,1,0,0}; - -//volatile uint8_t bitIndex = 25; -//const uint8_t datagram[] = { 0,0,0,1,0,1,0,1,1,0,1,0,1,1,0,1,0,1,0,1,0,1,0,0, 1 }; -static const unsigned char hang_up[] = {0,0,0,1,0,1,0,0,1,1,0,1,0,1,0,0,1,0,1,0,1,0,1,0}; -static const unsigned char post_hang_up[] = {0,0,0,1,0,1,0,0,1,0,1,0,1,1,0,1,0,1,0,1,0,1,0,0}; -static const unsigned char light[] = {0,0,0,1,0,1,0,1,1,0,1,0,0,1,0,1,0,1,0,1,0,1,0,0}; +#define TCS_IDLE_TIMEOUT 64 //8 bits TcsBus::TcsBus(int sendPin, int receivePin) : sendPin { sendPin }, receivePin { receivePin }, state { SamplingState::IDLE }, rxBits { 0 }, - oversamplingStep { 0 } + oversamplingStep { 0 }, + defaultRxEvent { 0 }, + txEvent { 0 } { } @@ -37,16 +25,25 @@ static void onTimer(void) static bool TimerSetup(TcsBus* inst) { - if(timer != NULL) return false; //already set up + 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); + 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; } @@ -56,66 +53,142 @@ void TcsBus::begin() pinMode(receivePin, INPUT); pinMode(sendPin, OUTPUT); digitalWrite(sendPin, LOW); //disable sending for now -pinMode(0, OUTPUT); +//pinMode(0, OUTPUT); TimerSetup(this); } +void TcsBus::end() +{ + pinMode(sendPin, INPUT); + TimerSetup(NULL); +} + void TcsBus::TimeBase() { - static int tmp = 0; - static bool send = false; - static int sendIdx = 0; - static int over = 0; -tmp++; + bool sample = digitalRead(receivePin); - if(tmp == 3000) { - send = true; - sendIdx = 0; - over = 0; + 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; + } } +} - if(send) { - over++; - if(over == 8) { - over = 0; - if(sendIdx >= sizeof(door_open)) { - digitalWrite(sendPin, LOW); - send = false; - tmp = 0; - } else { - digitalWrite(sendPin, (door_open[sendIdx++]) ? LOW : HIGH); +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; + } } } } - - bool sample = digitalRead(receivePin); - - digitalWrite(0, sample == 0 ? LOW : HIGH); -return; - - switch(state) { - case SamplingState::IDLE: { - if(sample) { - state = SamplingState::RECEIVE; - } else { - break; - } - } //fall through - - case SamplingState::RECEIVE: { - - } break; - - case SamplingState::TRANSMIT: { - static int i = 0; - i++; - if(i == 1000) - { - digitalWrite(sendPin, HIGH); - } else if(i > 1000) { - digitalWrite(sendPin, LOW); - i = 0; - } - } break; + if(found) { + found->func(found); } -} \ No newline at end of file + + 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; + } +} diff --git a/source/TcsBus.h b/source/TcsBus.h index 9e6de16..163e736 100644 --- a/source/TcsBus.h +++ b/source/TcsBus.h @@ -1,14 +1,33 @@ #ifndef TCS_BUS_H_ #define TCS_BUS_H_ 1 #include +#include + class TcsBus { public: - TcsBus(int sendPin, int receivePin); + struct EventData_t; + typedef struct EventData_t { + void (*func)(const EventData_t* evt); + const uint8_t* bits; + size_t bitCount; + } EventData_t; + + TcsBus(int sendPin, int receivePin); void begin(); + void end(); void TimeBase(); + void RegisterDefaultRxEvent(EventData_t* evt); + void RegisterEvent(const EventData_t* evt); + void UnRegisterEvent(const EventData_t* evt); + + bool StartTransmit(EventData_t* evt); private: + void EnterWaitIdle(); + void ReceiveBit(uint8_t currentBit); + void TransmitBit(); + void WaitIdle(); int sendPin; int receivePin; @@ -17,12 +36,17 @@ private: IDLE = 0, RECEIVE = 1, TRANSMIT = 2, + WAIT_IDLE = 3, }; SamplingState state; - uint16_t rxBits; + uint8_t rxBits[256]; + uint16_t currentBitIndex; uint8_t oversamplingStep; - bool lastSample; + uint8_t oversamplingSum; + std::vector events; + EventData_t* defaultRxEvent; + EventData_t* txEvent; }; #endif //TCS_BUS_H_ \ No newline at end of file diff --git a/source/TcsBusMessages.h b/source/TcsBusMessages.h new file mode 100644 index 0000000..0c246d9 --- /dev/null +++ b/source/TcsBusMessages.h @@ -0,0 +1,21 @@ +#ifndef TCS_BUS_MESSAGES_H_ +#define TCS_BUS_MESSAGES_H_ 1 + + + +//tcs_bus datagrams +static const unsigned char acknowledge[] = {0,0,0,1,0,1,0,1,1,0}; +static const unsigned char ack_audio_on[] = {0,0,0,1,0,1,0,0,1,1,0,0}; +static const unsigned char doorbell[] = {0,0,0,1,1,0,1,0,1,0,1,1,0,1,1,0,1,1,0,1,1,0,1,1,0,0,1,1,0,1,0,1,0,1,0,0,1,1,0,0,1,0,1,0,1,0,1,0,0,1}; +static const unsigned char door_open[] = {0,0,0,1,0,1,0,1,1,0,1,0,1,1,0,0,1,0,1,0,1,0,1,0}; +static const unsigned char master_hmmmm[] = {0,0,0,1,0,1,0,0,1,0,1,0,0,1,0,0,1,0,1,0,1,0,1,0,1}; +static const unsigned char pickup[] = {0,0,0,1,1,0,1,0,0,1,1,0,1,1,0,1,1,0,1,1,0,1,1,0,1,1,0,0,1,1,0,1,0,1,0,1,0,0,1,1,0,0,1,0,1,0,1,0,1,0,0}; + +//volatile uint8_t bitIndex = 25; +//const uint8_t datagram[] = { 0,0,0,1,0,1,0,1,1,0,1,0,1,1,0,1,0,1,0,1,0,1,0,0, 1 }; +static const unsigned char hang_up[] = {0,0,0,1,0,1,0,0,1,1,0,1,0,1,0,0,1,0,1,0,1,0,1,0}; +static const unsigned char post_hang_up[] = {0,0,0,1,0,1,0,0,1,0,1,0,1,1,0,1,0,1,0,1,0,1,0,0}; +static const unsigned char light[] = {0,0,0,1,0,1,0,1,1,0,1,0,0,1,0,1,0,1,0,1,0,1,0,0}; + + +#endif //TCS_BUS_MESSAGES_H_ \ No newline at end of file diff --git a/source/source.ino b/source/source.ino index 02418ee..5e36ea7 100644 --- a/source/source.ino +++ b/source/source.ino @@ -1,7 +1,7 @@ #include +#include "Pca9554.h" #include "TcsBus.h" - -#define PCA9554_ADDR 0x20 //PCA9554 = 0 1 0 0 a2 a1 a0 +#include "TcsBusMessages.h" #define PIN_AUDIO_READ 0 #define PIN_API_SWITCH 4 @@ -9,10 +9,73 @@ #define PIN_TCS_SEND 14 #define PIN_AUDIO_SEND 17 -TcsBus tcs(PIN_TCS_SEND, PIN_TCS_READ); +#define SREG_PIN_API_SWITCH 0x01u +#define SREG_PIN_LED1R 0x02u +#define SREG_PIN_REL1CTRL 0x04u +#define SREG_PIN_AUDIO_SEND_EN 0x08u +#define SREG_PIN_REL2CTRL 0x10u +#define SREG_PIN_LED1G 0x20u +#define SREG_PIN_LED2R 0x40u +#define SREG_PIN_LED2G 0x80u + +#define LEDS 2 + +enum class LedColor : uint8_t { + OFF = 0, + RED = 1, + GREEN = 2, + YELLOW = 3, + + UBOUND +}; + +typedef struct LedState_s { + enum LedColor color; + bool blink; +} LedState_t; + + +const uint8_t LedBitmask[LEDS][(uint8_t) LedColor::UBOUND] = { + { + 0, SREG_PIN_LED1R, SREG_PIN_LED1G, (SREG_PIN_LED1R | SREG_PIN_LED1G) + }, + { + 0, SREG_PIN_LED2R, SREG_PIN_LED2G, (SREG_PIN_LED2R | SREG_PIN_LED2G) + }, +}; + +static void DoorbellEvt(const TcsBus::EventData_t* evt) +{ + +} + +static const TcsBus::EventData_t doorbellEvt = { + .func = DoorbellEvt, + .bits = doorbell, + .bitCount = sizeof(doorbell), +}; + +static LedState_t ledStates[LEDS] = { + { LedColor::OFF, false }, + { LedColor::OFF, false } +}; static uint32_t ms_old = 0; +static TcsBus tcs = TcsBus(PIN_TCS_SEND, PIN_TCS_READ); +static Pca9554 sreg = Pca9554(); +static bool blinkOn = false; + +static void SetLed(uint8_t ledNr, LedState_t newState) { + uint8_t ledBitmask = LedBitmask[ledNr][(uint8_t) newState.color]; + uint8_t ledAll = LedBitmask[ledNr][(uint8_t) LedColor::YELLOW]; + + if(newState.blink && !blinkOn) { + ledBitmask = LedBitmask[ledNr][(uint8_t) LedColor::OFF]; + } + sreg.outputValues = (sreg.outputValues & ~ledAll) | ledBitmask; +} + void setup() { // put your setup code here, to run once: Wire.begin(); @@ -20,7 +83,20 @@ void setup() { tcs.begin(); + tcs.RegisterEvent(&doorbellEvt); + + //const uint8_t sregOutputs = (uint8_t) (SREG_PIN_LED1R | SREG_PIN_REL1CTRL | SREG_PIN_AUDIO_SEND_EN | SREG_PIN_REL2CTRL | SREG_PIN_LED1G | SREG_PIN_LED2R | SREG_PIN_LED2G); + const uint8_t sregInputs = (uint8_t) (SREG_PIN_API_SWITCH | SREG_PIN_AUDIO_SEND_EN); + + sreg.begin(sregInputs); + + sreg.outputValues = 0; ms_old = millis(); + + ledStates[1].color = LedColor::YELLOW; + ledStates[0].blink = true; + //ledStates[1].color = LedColor::YELLOW; + //ledStates[1].blink = true; } @@ -37,8 +113,22 @@ void loop() { if(ms_old != ms) { if(ms_old < ms) ms_old++; else ms_old = ms; //prevent overflow - if(!(ms & 1023)) { - i2cScan(); + static uint32_t blinkCountdown = 500; + blinkCountdown--; + if(!blinkCountdown) { + blinkCountdown = 500; + blinkOn = !blinkOn; + SetLed(0, ledStates[0]); + SetLed(1, ledStates[1]); + Serial.printf("0x%02x\r\n", sreg.outputValues); + sreg.SendOutputValues(); + } + + uint8_t inputs = sreg.ReadInputs(); + static uint8_t inputs_old = 0; + if((inputs & SREG_PIN_API_SWITCH) != (inputs_old & SREG_PIN_API_SWITCH)) { + Serial.printf("Switch=%u\n", inputs & SREG_PIN_API_SWITCH); } + inputs_old = inputs; } }