From 9aed72645260e7d39e4bceac6a09af902b78807a Mon Sep 17 00:00:00 2001 From: Andrew Kay Date: Sat, 28 Mar 2020 10:33:40 -0500 Subject: [PATCH] Use PlatformIO for firmware --- interface1/firmware/.gitignore | 1 + interface1/firmware/CoaxTransceiver.cpp | 303 ----------------- interface1/firmware/firmware.ino | 218 ------------ .../firmware/{ => include}/CoaxTransceiver.h | 8 +- interface1/firmware/platformio.ini | 6 + interface1/firmware/src/CoaxTransceiver.cpp | 311 ++++++++++++++++++ interface1/firmware/src/main.cpp | 226 +++++++++++++ 7 files changed, 547 insertions(+), 526 deletions(-) create mode 100644 interface1/firmware/.gitignore delete mode 100644 interface1/firmware/CoaxTransceiver.cpp delete mode 100644 interface1/firmware/firmware.ino rename interface1/firmware/{ => include}/CoaxTransceiver.h (97%) create mode 100644 interface1/firmware/platformio.ini create mode 100644 interface1/firmware/src/CoaxTransceiver.cpp create mode 100644 interface1/firmware/src/main.cpp diff --git a/interface1/firmware/.gitignore b/interface1/firmware/.gitignore new file mode 100644 index 0000000..03f4a3c --- /dev/null +++ b/interface1/firmware/.gitignore @@ -0,0 +1 @@ +.pio diff --git a/interface1/firmware/CoaxTransceiver.cpp b/interface1/firmware/CoaxTransceiver.cpp deleted file mode 100644 index 8f3b70b..0000000 --- a/interface1/firmware/CoaxTransceiver.cpp +++ /dev/null @@ -1,303 +0,0 @@ -// Copyright (c) 2019, Andrew Kay -// -// Permission to use, copy, modify, and/or distribute this software for any -// purpose with or without fee is hereby granted, provided that the above -// copyright notice and this permission notice appear in all copies. -// -// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES -// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR -// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF -// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -#include - -#include "CoaxTransceiver.h" - -// Arduino Mega pins... -// -// Arduino Arduino Port Label DP8340N DP8341N -// Pin and Mask Pin Pin -// ---------|--------------|-----------------|---------|--------- -// 7 | | EVEN/ODD PARITY | 18 | -// 6 | PH3 0x08 | PARITY CONTROL | 19 | -// 5 | | AUTO RESPONSE | 21 | -// 4 | PG5 0x20 | REGISTERS FULL | 22 | -// 3 | PE5 0x20 | REGISTER LOAD | 23 | -// ---------.--------------.-----------------.---------.--------- -// 2 | | DATA CONTROL | | 5 -// 14 | PJ1 0x02 | ERROR | | 8 -// 15 | PJ0 0x01 | DATA AVAILABLE | | 10 -// 16 | PH1 0x02 | REGISTER READ | | 9 -// 17 | PH0 0x01 | OUTPUT CONTROL | | 11 -// 18* | PD3 0x08 | RECEIVER ACTIVE | | 7 -// 19 | PD2 0x04 | OUTPUT ENABLE | | 13 -// ---------.--------------.-----------------.---------.--------- -// 22 | PA0 | D11 | 1 | 23 -// 23 | PA1 | D10 | 2 | 22 -// 24 | PA2 | D9 | 3 | 21 -// 25 | PA3 | D8 | 4 | 20 -// 26 | PA4 | D7 | 5 | 19 -// 27 | PA5 | D6 | 6 | 18 -// 28 | PA6 | D5 | 7 | 17 -// 29 | PA7 | D4 | 8 | 16 -// 36 | PC1 | D2 | 10 | 14 -// 37 | PC0 | D3 | 9 | 15 -// -// * - Interrupt capable pin - -#define TX_EVEN_ODD_PARITY_PIN 7 -#define TX_PARITY_CONTROL_PIN 6 -#define TX_AUTO_RESPONSE_PIN 5 -#define TX_REGISTERS_FULL_PIN 4 -#define TX_REGISTER_LOAD_PIN 3 - -#define RX_DATA_CONTROL_PIN 2 -#define RX_ERROR_PIN 14 -#define RX_DATA_AVAILABLE_PIN 15 -#define RX_REGISTER_READ_PIN 16 -#define RX_OUTPUT_CONTROL_PIN 17 -#define RX_ACTIVE_PIN 18 -#define RX_OUTPUT_ENABLE_PIN 19 - -#define RX_STATE_DISABLED 0 -#define RX_STATE_WAITING 1 -#define RX_STATE_RECEIVING 2 -#define RX_STATE_RECEIVED 3 - -static volatile uint8_t CoaxTransceiver::rxState; -static volatile uint16_t *CoaxTransceiver::rxBuffer; -static volatile size_t CoaxTransceiver::rxBufferSize; -static volatile int /* ssize_t */ CoaxTransceiver::rxBufferCount; - -#define NOP __asm__("nop\n\t") - -static void CoaxTransceiver::setup() { - // Configure data bus. - dataBusSetup(); - - // Configure receiver (DP8341N). - rxSetup(); - - // Configure transmitter (DP8340N). - txSetup(); -} - -static int /* ssize_t */ CoaxTransceiver::transmitReceive(uint16_t *transmitBuffer, size_t transmitBufferCount, uint16_t *receiveBuffer, size_t receiveBufferSize, uint16_t receiveTimeout) { - int returnValue = transmit(transmitBuffer, transmitBufferCount); - - if (returnValue < 0) { - return returnValue; - } - - return receive(receiveBuffer, receiveBufferSize, receiveTimeout); -} - -static void CoaxTransceiver::dataBusSetup() { - DDRA = B00000000; - DDRC = B00000000; -} - -static void CoaxTransceiver::rxSetup() { - // Data Control - Amplifier Inputs - pinMode(RX_DATA_CONTROL_PIN, OUTPUT); - - digitalWrite(RX_DATA_CONTROL_PIN, HIGH); - - // Register Read - pinMode(RX_REGISTER_READ_PIN, OUTPUT); - - digitalWrite(RX_REGISTER_READ_PIN, HIGH); - - // Output Control - Data - pinMode(RX_OUTPUT_CONTROL_PIN, OUTPUT); - - digitalWrite(RX_OUTPUT_CONTROL_PIN, HIGH); - - // Output Enable - Active - pinMode(RX_OUTPUT_ENABLE_PIN, OUTPUT); - - digitalWrite(RX_OUTPUT_ENABLE_PIN, HIGH); - - // Receiver Active - pinMode(RX_ACTIVE_PIN, INPUT); - - attachInterrupt(digitalPinToInterrupt(RX_ACTIVE_PIN), rxActiveInterrupt, RISING); - - // Data Available - pinMode(RX_DATA_AVAILABLE_PIN, INPUT); - - // Error - pinMode(RX_ERROR_PIN, INPUT); -} - -static void CoaxTransceiver::txSetup() { - // Register Load - pinMode(TX_REGISTER_LOAD_PIN, OUTPUT); - - digitalWrite(TX_REGISTER_LOAD_PIN, HIGH); - - // Auto Response - Data - pinMode(TX_AUTO_RESPONSE_PIN, OUTPUT); - - digitalWrite(TX_AUTO_RESPONSE_PIN, HIGH); - - // Even/Odd Parity - Even - pinMode(TX_EVEN_ODD_PARITY_PIN, OUTPUT); - - digitalWrite(TX_EVEN_ODD_PARITY_PIN, HIGH); - - // Parity Control - Data - pinMode(TX_PARITY_CONTROL_PIN, OUTPUT); - - digitalWrite(TX_PARITY_CONTROL_PIN, HIGH); - - // Registers Full - pinMode(TX_REGISTERS_FULL_PIN, INPUT); -} - -static int /* ssize_t */ CoaxTransceiver::transmit(uint16_t *buffer, size_t bufferCount) { - // Ensure receiver is inactive. - if (rxState != RX_STATE_DISABLED) { - return ERROR_TX_RECEIVER_ACTIVE; - } - - if (/* RECEIVER ACTIVE */ (PIND & 0x8) == 0x8) { - return ERROR_TX_RECEIVER_ACTIVE; - } - - // Disable interrupts. - noInterrupts(); - - // Disable receiver output. - PORTD &= ~0x04; // RX Output Enable - Low (Disable) - - // Configure data bus for output. - DDRA = B11111111; - DDRC = B00000011; - - // Transmit. - for (int index = 0; index < bufferCount; index++) { - uint16_t data = buffer[index]; - - // Wait while TX Registers Full is high. - while ((PING & 0x20) == 0x20) { - NOP; - } - - PORTC = (PINC & 0xfc) | ((data >> 8) & 0x3); - PORTA = data & 0xff; - - PORTE &= ~0x20; // TX Register Load - Low (Load) - PORTE |= 0x20; // TX Register Load - High - } - - // Configure data bus for input. - DDRA = B00000000; - DDRC = B00000000; - - // Enable receiver output. - PORTD |= 0x04; // RX Output Enable - High (Enable) - - // Enable interrupts. - interrupts(); - - return bufferCount; -} - -static int /* ssize_t */ CoaxTransceiver::receive(uint16_t *buffer, size_t bufferSize, uint16_t timeout) { - rxBuffer = buffer; - rxBufferSize = bufferSize; - - rxState = RX_STATE_WAITING; - - if (timeout > 0) { - unsigned long startTime = millis(); - - while (rxState == RX_STATE_WAITING) { - // https://www.forward.com.au/pfod/ArduinoProgramming/TimingDelaysInArduino.html#unsigned - if ((millis() - startTime) > timeout) { - rxState = RX_STATE_DISABLED; - return ERROR_RX_TIMEOUT; - } - } - } - - while (rxState != RX_STATE_RECEIVED) { - NOP; - } - - uint16_t count = rxBufferCount; - - rxState = RX_STATE_DISABLED; - - if (count < 0) { - return count; - } - - // Check for receiver errors. - for (int index = 0; index < count; index++) { - if (buffer[index] & 0x8000) { - return ERROR_RX_RECEIVER; - } - } - - return count; -} - -static void CoaxTransceiver::rxActiveInterrupt() { - uint16_t data; - uint8_t mask; - - if (rxState == RX_STATE_DISABLED) { - return; - } - - rxState = RX_STATE_RECEIVING; - - rxBufferCount = 0; - - do { - while (/* ERROR or DATA AVAILABLE */ (PINJ & 0x03) == 0) { - NOP; - } - - if (/* ERROR */ (PINJ & 0x02) == 0x02) { - mask = 0x02; - - PORTH &= ~0x01; // Output Control - Low (Error) - PORTH &= ~0x02; // Register Read - Low - - // Read and mark as error. - data = (((PINC & 0x3) | 0x80) << 8) | PINA; - - PORTH |= 0x02; // Register Read - High - PORTH |= 0x01; // Output Control - High (Data) - } else if (/* DATA AVAILABLE */ (PINJ & 0x01) == 0x01) { - mask = 0x01; - - PORTH &= ~0x02; // Register Read - Low - - // Read. - data = ((PINC & 0x3) << 8) | PINA; - - PORTH |= 0x02; // Register Read - High - } - - if (rxBufferCount >= rxBufferSize) { - rxBufferCount = ERROR_RX_OVERFLOW; - goto EXIT; - } - - rxBuffer[rxBufferCount++] = data; - - while ((PINJ & mask) == mask) { - NOP; - } - } while (/* RECEIVER ACTIVE */ (PIND & 0x8) == 0x8); - -EXIT: - rxState = RX_STATE_RECEIVED; -} diff --git a/interface1/firmware/firmware.ino b/interface1/firmware/firmware.ino deleted file mode 100644 index 9ca5dab..0000000 --- a/interface1/firmware/firmware.ino +++ /dev/null @@ -1,218 +0,0 @@ -// Copyright (c) 2019, Andrew Kay -// -// Permission to use, copy, modify, and/or distribute this software for any -// purpose with or without fee is hereby granted, provided that the above -// copyright notice and this permission notice appear in all copies. -// -// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES -// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR -// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF -// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -#include - -#include "CoaxTransceiver.h" - -#define COMMAND_RESET 0x01 -#define COMMAND_TRANSMIT 0x02 -#define COMMAND_RECEIVE 0x04 -#define COMMAND_TRANSMIT_RECEIVE 0x06 - -#define ERROR_INVALID_MESSAGE 1 -#define ERROR_UNKNOWN_COMMAND 2 - -void handleResetCommand(uint8_t *buffer, int bufferCount) { - uint8_t response[] = { 0x01, 0x00, 0x00, 0x01 }; - - sendMessage(response, 4); -} - -void handleTransmitReceiveCommand(uint8_t *buffer, int bufferCount) { - if (bufferCount < 6) { - sendErrorMessage(ERROR_INVALID_MESSAGE); - return; - } - - uint16_t *transmitBuffer = (uint16_t *) (buffer + 2); - uint16_t transmitBufferCount = (bufferCount - 6) / 2; - - uint16_t transmitRepeatCount = ((buffer[0] << 8) | buffer[1]) & 0x7fff; - uint16_t transmitRepeatOffset = buffer[0] >> 7; - - uint16_t receiveBufferSize = (buffer[bufferCount - 4] << 8) | buffer[bufferCount - 3]; - uint16_t receiveTimeout = (buffer[bufferCount - 2] << 8) | buffer[bufferCount - 1]; - - if (transmitBufferCount < 1) { - sendErrorMessage(ERROR_INVALID_MESSAGE); - return; - } - - // Expand the provided data if applicable. - if (transmitRepeatCount > 1) { - uint8_t *source = ((uint8_t *) transmitBuffer) + (transmitRepeatOffset * 2); - uint8_t *destination = ((uint8_t *) transmitBuffer) + (transmitBufferCount * 2); - - uint16_t sourceCount = transmitBufferCount - transmitRepeatOffset; - - size_t length = sourceCount * 2; - - for (int index = 1; index < transmitRepeatCount; index++) { - memcpy(destination, source, length); - - transmitBufferCount += sourceCount; - - destination += length; - } - } - - uint16_t *receiveBuffer = (uint16_t *) (buffer + 2); - - bufferCount = CoaxTransceiver::transmitReceive(transmitBuffer, transmitBufferCount, receiveBuffer, receiveBufferSize, receiveTimeout); - - if (bufferCount < 0) { - sendErrorMessage(100 + ((-1) * bufferCount)); - return; - } - - // Send the response message. - buffer[1] = 0x01; - - bufferCount = 1 + (bufferCount * 2); - - sendMessage(buffer + 1, bufferCount); -} - -void handleMessage(uint8_t *buffer, int bufferCount) { - if (bufferCount < 1) { - sendErrorMessage(ERROR_INVALID_MESSAGE); - return; - } - - uint8_t command = buffer[0]; - - if (command == COMMAND_RESET) { - handleResetCommand(buffer + 1, bufferCount - 1); - } else if (command == COMMAND_TRANSMIT_RECEIVE) { - handleTransmitReceiveCommand(buffer + 1, bufferCount - 1); - } else { - sendErrorMessage(ERROR_UNKNOWN_COMMAND); - } -} - -#define FRAME_END 0xc0 -#define FRAME_ESCAPE 0xdb -#define FRAME_ESCAPE_END 0xdc -#define FRAME_ESCAPE_ESCAPE 0xdd - -enum { - WAIT_START, - DATA, - ESCAPE -} frameState; - -#define FRAME_BUFFER_SIZE ((25 * 80) * 2) + 32 - -uint8_t frameBuffer[FRAME_BUFFER_SIZE]; -int frameBufferCount = 0; - -void handleFrame(uint8_t *buffer, int bufferCount) { - if (bufferCount < 4) { - sendErrorMessage(ERROR_INVALID_MESSAGE); - return; - } - - int count = (buffer[0] << 8) | buffer[1]; - - if (bufferCount - 4 != count) { - sendErrorMessage(ERROR_INVALID_MESSAGE); - return; - } - - handleMessage(buffer + 2, count); -} - -void sendMessage(uint8_t *buffer, int bufferCount) { - Serial.write((char) FRAME_END); - - // Write the length. - Serial.write((char) bufferCount >> 8); - Serial.write((char) bufferCount); - - for (int index = 0; index < bufferCount; index++) { - if (buffer[index] == FRAME_END) { - Serial.write((char) FRAME_ESCAPE); - Serial.write((char) FRAME_ESCAPE_END); - } else if (buffer[index] == FRAME_ESCAPE) { - Serial.write((char) FRAME_ESCAPE); - Serial.write((char) FRAME_ESCAPE_ESCAPE); - } else { - Serial.write((char) buffer[index]); - } - } - - // Write the placeholder for checksum. - Serial.write((char) 0x00); - Serial.write((char) 0x00); - - Serial.write((char) FRAME_END); - - Serial.flush(); -} - -void sendErrorMessage(uint8_t code) { - uint8_t message[] = { 0x02, code }; - - sendMessage(message, 2); -} - -void setup() { - // Configure serial port and state machine. - Serial.begin(115200); - - frameState = WAIT_START; - - while (Serial.available() > 0) { - Serial.read(); - } - - // Configure the transceiver. - CoaxTransceiver::setup(); -} - -void loop() { - if (Serial.available() > 0) { - uint8_t byte = Serial.read(); - - if (frameState == WAIT_START) { - if (byte == FRAME_END) { - frameState = DATA; - } - } else if (frameState == DATA) { - if (byte == FRAME_END) { - if (frameBufferCount > 0) { - handleFrame(frameBuffer, frameBufferCount); - } - - frameBufferCount = 0; - } else if (byte == FRAME_ESCAPE) { - frameState = ESCAPE; - } else { - // TODO: overflow... - frameBuffer[frameBufferCount++] = byte; - } - } else if (frameState == ESCAPE) { - if (byte == FRAME_ESCAPE_END) { - // TODO: overflow... - frameBuffer[frameBufferCount++] = FRAME_END; - } else if (byte == FRAME_ESCAPE_ESCAPE) { - // TODO: overflow... - frameBuffer[frameBufferCount++] = FRAME_ESCAPE; - } - - frameState = DATA; - } - } -} diff --git a/interface1/firmware/CoaxTransceiver.h b/interface1/firmware/include/CoaxTransceiver.h similarity index 97% rename from interface1/firmware/CoaxTransceiver.h rename to interface1/firmware/include/CoaxTransceiver.h index cc09e3c..b711b12 100644 --- a/interface1/firmware/CoaxTransceiver.h +++ b/interface1/firmware/include/CoaxTransceiver.h @@ -14,19 +14,17 @@ #pragma once -#include - #define ERROR_TX_RECEIVER_ACTIVE -1 #define ERROR_RX_TIMEOUT -2 #define ERROR_RX_OVERFLOW -3 #define ERROR_RX_RECEIVER -4 class CoaxTransceiver { - public: +public: static void setup(); static int /* ssize_t */ transmitReceive(uint16_t *transmitBuffer, size_t transmitBufferCount, uint16_t *receiveBuffer, size_t receiveBufferSize, uint16_t receiveTimeout); - - private: + +private: static void dataBusSetup(); static void rxSetup(); static void txSetup(); diff --git a/interface1/firmware/platformio.ini b/interface1/firmware/platformio.ini new file mode 100644 index 0000000..a6dd2ab --- /dev/null +++ b/interface1/firmware/platformio.ini @@ -0,0 +1,6 @@ +[env] +framework = arduino + +[env:megaatmega2560] +platform = atmelavr +board = megaatmega2560 diff --git a/interface1/firmware/src/CoaxTransceiver.cpp b/interface1/firmware/src/CoaxTransceiver.cpp new file mode 100644 index 0000000..a378cff --- /dev/null +++ b/interface1/firmware/src/CoaxTransceiver.cpp @@ -0,0 +1,311 @@ +// Copyright (c) 2019, Andrew Kay +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +#include + +#include "CoaxTransceiver.h" + +// Arduino Mega pins... +// +// Arduino Arduino Port Label DP8340N DP8341N +// Pin and Mask Pin Pin +// ---------|--------------|-----------------|---------|--------- +// 7 | | EVEN/ODD PARITY | 18 | +// 6 | PH3 0x08 | PARITY CONTROL | 19 | +// 5 | | AUTO RESPONSE | 21 | +// 4 | PG5 0x20 | REGISTERS FULL | 22 | +// 3 | PE5 0x20 | REGISTER LOAD | 23 | +// ---------.--------------.-----------------.---------.--------- +// 2 | | DATA CONTROL | | 5 +// 14 | PJ1 0x02 | ERROR | | 8 +// 15 | PJ0 0x01 | DATA AVAILABLE | | 10 +// 16 | PH1 0x02 | REGISTER READ | | 9 +// 17 | PH0 0x01 | OUTPUT CONTROL | | 11 +// 18* | PD3 0x08 | RECEIVER ACTIVE | | 7 +// 19 | PD2 0x04 | OUTPUT ENABLE | | 13 +// ---------.--------------.-----------------.---------.--------- +// 22 | PA0 | D11 | 1 | 23 +// 23 | PA1 | D10 | 2 | 22 +// 24 | PA2 | D9 | 3 | 21 +// 25 | PA3 | D8 | 4 | 20 +// 26 | PA4 | D7 | 5 | 19 +// 27 | PA5 | D6 | 6 | 18 +// 28 | PA6 | D5 | 7 | 17 +// 29 | PA7 | D4 | 8 | 16 +// 36 | PC1 | D2 | 10 | 14 +// 37 | PC0 | D3 | 9 | 15 +// +// * - Interrupt capable pin + +#define TX_EVEN_ODD_PARITY_PIN 7 +#define TX_PARITY_CONTROL_PIN 6 +#define TX_AUTO_RESPONSE_PIN 5 +#define TX_REGISTERS_FULL_PIN 4 +#define TX_REGISTER_LOAD_PIN 3 + +#define RX_DATA_CONTROL_PIN 2 +#define RX_ERROR_PIN 14 +#define RX_DATA_AVAILABLE_PIN 15 +#define RX_REGISTER_READ_PIN 16 +#define RX_OUTPUT_CONTROL_PIN 17 +#define RX_ACTIVE_PIN 18 +#define RX_OUTPUT_ENABLE_PIN 19 + +#define RX_STATE_DISABLED 0 +#define RX_STATE_WAITING 1 +#define RX_STATE_RECEIVING 2 +#define RX_STATE_RECEIVED 3 + +volatile uint8_t CoaxTransceiver::rxState; +volatile uint16_t *CoaxTransceiver::rxBuffer; +volatile size_t CoaxTransceiver::rxBufferSize; +volatile int /* ssize_t */ CoaxTransceiver::rxBufferCount; + +#define NOP __asm__("nop\n\t") + +void CoaxTransceiver::setup() +{ + // Configure data bus. + dataBusSetup(); + + // Configure receiver (DP8341N). + rxSetup(); + + // Configure transmitter (DP8340N). + txSetup(); +} + +int /* ssize_t */ CoaxTransceiver::transmitReceive(uint16_t *transmitBuffer, size_t transmitBufferCount, uint16_t *receiveBuffer, size_t receiveBufferSize, uint16_t receiveTimeout) +{ + int returnValue = transmit(transmitBuffer, transmitBufferCount); + + if (returnValue < 0) { + return returnValue; + } + + return receive(receiveBuffer, receiveBufferSize, receiveTimeout); +} + +void CoaxTransceiver::dataBusSetup() +{ + DDRA = B00000000; + DDRC = B00000000; +} + +void CoaxTransceiver::rxSetup() +{ + // Data Control - Amplifier Inputs + pinMode(RX_DATA_CONTROL_PIN, OUTPUT); + + digitalWrite(RX_DATA_CONTROL_PIN, HIGH); + + // Register Read + pinMode(RX_REGISTER_READ_PIN, OUTPUT); + + digitalWrite(RX_REGISTER_READ_PIN, HIGH); + + // Output Control - Data + pinMode(RX_OUTPUT_CONTROL_PIN, OUTPUT); + + digitalWrite(RX_OUTPUT_CONTROL_PIN, HIGH); + + // Output Enable - Active + pinMode(RX_OUTPUT_ENABLE_PIN, OUTPUT); + + digitalWrite(RX_OUTPUT_ENABLE_PIN, HIGH); + + // Receiver Active + pinMode(RX_ACTIVE_PIN, INPUT); + + attachInterrupt(digitalPinToInterrupt(RX_ACTIVE_PIN), rxActiveInterrupt, RISING); + + // Data Available + pinMode(RX_DATA_AVAILABLE_PIN, INPUT); + + // Error + pinMode(RX_ERROR_PIN, INPUT); +} + +void CoaxTransceiver::txSetup() +{ + // Register Load + pinMode(TX_REGISTER_LOAD_PIN, OUTPUT); + + digitalWrite(TX_REGISTER_LOAD_PIN, HIGH); + + // Auto Response - Data + pinMode(TX_AUTO_RESPONSE_PIN, OUTPUT); + + digitalWrite(TX_AUTO_RESPONSE_PIN, HIGH); + + // Even/Odd Parity - Even + pinMode(TX_EVEN_ODD_PARITY_PIN, OUTPUT); + + digitalWrite(TX_EVEN_ODD_PARITY_PIN, HIGH); + + // Parity Control - Data + pinMode(TX_PARITY_CONTROL_PIN, OUTPUT); + + digitalWrite(TX_PARITY_CONTROL_PIN, HIGH); + + // Registers Full + pinMode(TX_REGISTERS_FULL_PIN, INPUT); +} + +int /* ssize_t */ CoaxTransceiver::transmit(uint16_t *buffer, size_t bufferCount) +{ + // Ensure receiver is inactive. + if (rxState != RX_STATE_DISABLED) { + return ERROR_TX_RECEIVER_ACTIVE; + } + + if (/* RECEIVER ACTIVE */ (PIND & 0x8) == 0x8) { + return ERROR_TX_RECEIVER_ACTIVE; + } + + // Disable interrupts. + noInterrupts(); + + // Disable receiver output. + PORTD &= ~0x04; // RX Output Enable - Low (Disable) + + // Configure data bus for output. + DDRA = B11111111; + DDRC = B00000011; + + // Transmit. + for (int index = 0; index < bufferCount; index++) { + uint16_t data = buffer[index]; + + // Wait while TX Registers Full is high. + while ((PING & 0x20) == 0x20) { + NOP; + } + + PORTC = (PINC & 0xfc) | ((data >> 8) & 0x3); + PORTA = data & 0xff; + + PORTE &= ~0x20; // TX Register Load - Low (Load) + PORTE |= 0x20; // TX Register Load - High + } + + // Configure data bus for input. + DDRA = B00000000; + DDRC = B00000000; + + // Enable receiver output. + PORTD |= 0x04; // RX Output Enable - High (Enable) + + // Enable interrupts. + interrupts(); + + return bufferCount; +} + +int /* ssize_t */ CoaxTransceiver::receive(uint16_t *buffer, size_t bufferSize, uint16_t timeout) +{ + rxBuffer = buffer; + rxBufferSize = bufferSize; + + rxState = RX_STATE_WAITING; + + if (timeout > 0) { + unsigned long startTime = millis(); + + while (rxState == RX_STATE_WAITING) { + // https://www.forward.com.au/pfod/ArduinoProgramming/TimingDelaysInArduino.html#unsigned + if ((millis() - startTime) > timeout) { + rxState = RX_STATE_DISABLED; + return ERROR_RX_TIMEOUT; + } + } + } + + while (rxState != RX_STATE_RECEIVED) { + NOP; + } + + uint16_t count = rxBufferCount; + + rxState = RX_STATE_DISABLED; + + if (count < 0) { + return count; + } + + // Check for receiver errors. + for (int index = 0; index < count; index++) { + if (buffer[index] & 0x8000) { + return ERROR_RX_RECEIVER; + } + } + + return count; +} + +void CoaxTransceiver::rxActiveInterrupt() +{ + uint16_t data; + uint8_t mask; + + if (rxState == RX_STATE_DISABLED) { + return; + } + + rxState = RX_STATE_RECEIVING; + + rxBufferCount = 0; + + do { + while (/* ERROR or DATA AVAILABLE */ (PINJ & 0x03) == 0) { + NOP; + } + + if (/* ERROR */ (PINJ & 0x02) == 0x02) { + mask = 0x02; + + PORTH &= ~0x01; // Output Control - Low (Error) + PORTH &= ~0x02; // Register Read - Low + + // Read and mark as error. + data = (((PINC & 0x3) | 0x80) << 8) | PINA; + + PORTH |= 0x02; // Register Read - High + PORTH |= 0x01; // Output Control - High (Data) + } else if (/* DATA AVAILABLE */ (PINJ & 0x01) == 0x01) { + mask = 0x01; + + PORTH &= ~0x02; // Register Read - Low + + // Read. + data = ((PINC & 0x3) << 8) | PINA; + + PORTH |= 0x02; // Register Read - High + } + + if (rxBufferCount >= rxBufferSize) { + rxBufferCount = ERROR_RX_OVERFLOW; + goto EXIT; + } + + rxBuffer[rxBufferCount++] = data; + + while ((PINJ & mask) == mask) { + NOP; + } + } while (/* RECEIVER ACTIVE */ (PIND & 0x8) == 0x8); + +EXIT: + rxState = RX_STATE_RECEIVED; +} diff --git a/interface1/firmware/src/main.cpp b/interface1/firmware/src/main.cpp new file mode 100644 index 0000000..260db2a --- /dev/null +++ b/interface1/firmware/src/main.cpp @@ -0,0 +1,226 @@ +// Copyright (c) 2019, Andrew Kay +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +#include + +#include "CoaxTransceiver.h" + +#define FRAME_END 0xc0 +#define FRAME_ESCAPE 0xdb +#define FRAME_ESCAPE_END 0xdc +#define FRAME_ESCAPE_ESCAPE 0xdd + +enum { + WAIT_START, + DATA, + ESCAPE +} frameState; + +#define FRAME_BUFFER_SIZE ((25 * 80) * 2) + 32 + +uint8_t frameBuffer[FRAME_BUFFER_SIZE]; +int frameBufferCount = 0; + +#define ERROR_INVALID_MESSAGE 1 +#define ERROR_UNKNOWN_COMMAND 2 + +void sendMessage(uint8_t *buffer, int bufferCount) +{ + Serial.write((char) FRAME_END); + + // Write the length. + Serial.write((char) bufferCount >> 8); + Serial.write((char) bufferCount); + + for (int index = 0; index < bufferCount; index++) { + if (buffer[index] == FRAME_END) { + Serial.write((char) FRAME_ESCAPE); + Serial.write((char) FRAME_ESCAPE_END); + } else if (buffer[index] == FRAME_ESCAPE) { + Serial.write((char) FRAME_ESCAPE); + Serial.write((char) FRAME_ESCAPE_ESCAPE); + } else { + Serial.write((char) buffer[index]); + } + } + + // Write the placeholder for checksum. + Serial.write((char) 0x00); + Serial.write((char) 0x00); + + Serial.write((char) FRAME_END); + + Serial.flush(); +} + +void sendErrorMessage(uint8_t code) +{ + uint8_t message[] = { 0x02, code }; + + sendMessage(message, 2); +} + +#define COMMAND_RESET 0x01 +#define COMMAND_TRANSMIT 0x02 +#define COMMAND_RECEIVE 0x04 +#define COMMAND_TRANSMIT_RECEIVE 0x06 + +void handleResetCommand(uint8_t *buffer, int bufferCount) +{ + uint8_t response[] = { 0x01, 0x00, 0x00, 0x01 }; + + sendMessage(response, 4); +} + +void handleTransmitReceiveCommand(uint8_t *buffer, int bufferCount) +{ + if (bufferCount < 6) { + sendErrorMessage(ERROR_INVALID_MESSAGE); + return; + } + + uint16_t *transmitBuffer = (uint16_t *) (buffer + 2); + uint16_t transmitBufferCount = (bufferCount - 6) / 2; + + uint16_t transmitRepeatCount = ((buffer[0] << 8) | buffer[1]) & 0x7fff; + uint16_t transmitRepeatOffset = buffer[0] >> 7; + + uint16_t receiveBufferSize = (buffer[bufferCount - 4] << 8) | buffer[bufferCount - 3]; + uint16_t receiveTimeout = (buffer[bufferCount - 2] << 8) | buffer[bufferCount - 1]; + + if (transmitBufferCount < 1) { + sendErrorMessage(ERROR_INVALID_MESSAGE); + return; + } + + // Expand the provided data if applicable. + if (transmitRepeatCount > 1) { + uint8_t *source = ((uint8_t *) transmitBuffer) + (transmitRepeatOffset * 2); + uint8_t *destination = ((uint8_t *) transmitBuffer) + (transmitBufferCount * 2); + + uint16_t sourceCount = transmitBufferCount - transmitRepeatOffset; + + size_t length = sourceCount * 2; + + for (int index = 1; index < transmitRepeatCount; index++) { + memcpy(destination, source, length); + + transmitBufferCount += sourceCount; + + destination += length; + } + } + + uint16_t *receiveBuffer = (uint16_t *) (buffer + 2); + + bufferCount = CoaxTransceiver::transmitReceive(transmitBuffer, transmitBufferCount, receiveBuffer, receiveBufferSize, receiveTimeout); + + if (bufferCount < 0) { + sendErrorMessage(100 + ((-1) * bufferCount)); + return; + } + + // Send the response message. + buffer[1] = 0x01; + + bufferCount = 1 + (bufferCount * 2); + + sendMessage(buffer + 1, bufferCount); +} + +void handleMessage(uint8_t *buffer, int bufferCount) +{ + if (bufferCount < 1) { + sendErrorMessage(ERROR_INVALID_MESSAGE); + return; + } + + uint8_t command = buffer[0]; + + if (command == COMMAND_RESET) { + handleResetCommand(buffer + 1, bufferCount - 1); + } else if (command == COMMAND_TRANSMIT_RECEIVE) { + handleTransmitReceiveCommand(buffer + 1, bufferCount - 1); + } else { + sendErrorMessage(ERROR_UNKNOWN_COMMAND); + } +} + +void handleFrame(uint8_t *buffer, int bufferCount) +{ + if (bufferCount < 4) { + sendErrorMessage(ERROR_INVALID_MESSAGE); + return; + } + + int count = (buffer[0] << 8) | buffer[1]; + + if (bufferCount - 4 != count) { + sendErrorMessage(ERROR_INVALID_MESSAGE); + return; + } + + handleMessage(buffer + 2, count); +} + +void setup() +{ + // Configure serial port and state machine. + Serial.begin(115200); + + frameState = WAIT_START; + + while (Serial.available() > 0) { + Serial.read(); + } + + // Configure the transceiver. + CoaxTransceiver::setup(); +} + +void loop() +{ + if (Serial.available() > 0) { + uint8_t byte = Serial.read(); + + if (frameState == WAIT_START) { + if (byte == FRAME_END) { + frameState = DATA; + } + } else if (frameState == DATA) { + if (byte == FRAME_END) { + if (frameBufferCount > 0) { + handleFrame(frameBuffer, frameBufferCount); + } + + frameBufferCount = 0; + } else if (byte == FRAME_ESCAPE) { + frameState = ESCAPE; + } else { + // TODO: overflow... + frameBuffer[frameBufferCount++] = byte; + } + } else if (frameState == ESCAPE) { + if (byte == FRAME_ESCAPE_END) { + // TODO: overflow... + frameBuffer[frameBufferCount++] = FRAME_END; + } else if (byte == FRAME_ESCAPE_ESCAPE) { + // TODO: overflow... + frameBuffer[frameBufferCount++] = FRAME_ESCAPE; + } + + frameState = DATA; + } + } +}