mirror of
https://github.com/lowobservable/coax.git
synced 2026-02-09 01:51:10 +00:00
Use PlatformIO for firmware
This commit is contained in:
1
interface1/firmware/.gitignore
vendored
Normal file
1
interface1/firmware/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
.pio
|
||||
@@ -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 <Arduino.h>
|
||||
|
||||
#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;
|
||||
}
|
||||
@@ -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 <Arduino.h>
|
||||
|
||||
#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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -14,19 +14,17 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
#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();
|
||||
6
interface1/firmware/platformio.ini
Normal file
6
interface1/firmware/platformio.ini
Normal file
@@ -0,0 +1,6 @@
|
||||
[env]
|
||||
framework = arduino
|
||||
|
||||
[env:megaatmega2560]
|
||||
platform = atmelavr
|
||||
board = megaatmega2560
|
||||
311
interface1/firmware/src/CoaxTransceiver.cpp
Normal file
311
interface1/firmware/src/CoaxTransceiver.cpp
Normal file
@@ -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 <Arduino.h>
|
||||
|
||||
#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;
|
||||
}
|
||||
226
interface1/firmware/src/main.cpp
Normal file
226
interface1/firmware/src/main.cpp
Normal file
@@ -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 <Arduino.h>
|
||||
|
||||
#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;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user