mirror of
https://github.com/halleysfifthinc/AVCLAN-Mockingboard.git
synced 2026-08-06 17:03:17 +00:00
Add C++ Bus class for the physical layer interface
This commit is contained in:
@@ -35,6 +35,7 @@ add_library(avclan STATIC
|
||||
src/avclan/avclan_protocol.c
|
||||
src/avclan/cdchanger.c
|
||||
src/avclan/peripheral.cc
|
||||
src/avclan/bus.cc
|
||||
)
|
||||
|
||||
# avclan exports its public generic headers (src/avclan) to consumers and
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
// copyright (C) 2006 Marcin Slonicki <marcin@softservice.com.pl>
|
||||
// copyright (C) 2007 Louis Frigon
|
||||
// Copyright (C) 2015 Allen Hill <allenofthehills@gmail.com>
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#include "bus.hpp"
|
||||
#include "avclan_defs.h"
|
||||
#include "avclan_phy.h" // bridge until phy has been ported
|
||||
|
||||
namespace avclan {
|
||||
void Bus::init() { AVCLAN_busInit(); };
|
||||
void Bus::mute(bool mute) { AVCLAN_muteDevice(mute); };
|
||||
bool Bus::is_muted() const { return AVCLAN_ismuted(); };
|
||||
Bus::Handle Bus::get() { return {}; };
|
||||
|
||||
bool Bus::Handle::sendstartbit() { return AVCLAN_sendstartbit(); };
|
||||
Bus::Error::Read Bus::Handle::readstartbit() {
|
||||
using enum Error::Read;
|
||||
auto err = AVCLAN_readstartbit();
|
||||
if (err == rSTARTBIT_TOO_LONG)
|
||||
return STARTBIT_TOO_LONG;
|
||||
else if (err == rLATCHED_COMPARATOR)
|
||||
return BAD_STARTBIT;
|
||||
else if (err == rSTARTBIT_TOO_SHORT)
|
||||
return STARTBIT_TOO_SHORT;
|
||||
else
|
||||
return Read{0};
|
||||
};
|
||||
void Bus::Handle::send_ACK() { AVCLAN_sendbit_ACK(); };
|
||||
uint8_t Bus::Handle::read_ACK() { return AVCLAN_readbit_ACK(); };
|
||||
|
||||
} // namespace avclan
|
||||
@@ -0,0 +1,178 @@
|
||||
// copyright (C) 2006 Marcin Slonicki <marcin@softservice.com.pl>
|
||||
// copyright (C) 2007 Louis Frigon
|
||||
// Copyright (C) 2015 Allen Hill <allenofthehills@gmail.com>
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
/*
|
||||
AVC LAN Theory
|
||||
|
||||
The AVC LAN bus is an implementation of the IEBus (mode 1) which is a
|
||||
differential signal; IEBus is electrically (but not logically) compatible with
|
||||
CAN bus.
|
||||
|
||||
- Logical `1`: Potential difference between bus lines (BUS+ pin and BUS– pin)
|
||||
is 20 mV or lower (floating).
|
||||
- Logical `0`: Potential difference between bus lines (BUS+ pin and BUS– pin)
|
||||
is 120 mV or higher (driving).
|
||||
|
||||
A nominal bit length is 39 us, composed of 3 periods: preparation,
|
||||
synchronization, data.
|
||||
|
||||
Figure 1. AVCLAN Bus bit format
|
||||
|
||||
│ Prep │<─ Sync ─>│<─ Data ─>│ ...
|
||||
Driving (logical `0`) ╭──────────╮──────────╮
|
||||
│ │ │
|
||||
Floating (logical `1`) ─────────╯ ╰──────────╰─────────
|
||||
│ 6 μs │── 19 μs ─│─ 13 μs ──│
|
||||
|
||||
The logical value during the data period signifies the bit value, e.g. a bit
|
||||
`0` continues the logical `0` (high potential difference between bus lines) of
|
||||
the sync period thru the data period, and a bit `1` has a logical `1`
|
||||
(low/floating potential between bus lines) during the data period. Using the
|
||||
TCB pulse-width and frequency measure mode, the total bit length differs for
|
||||
bit `1` and `0`; detailed bit timing can be found in "timing.h". The bus
|
||||
idles at low potential (floating).
|
||||
|
||||
A start bit is nominally 169 us high followed by 20 us low.
|
||||
|
||||
A bit `0` is dominant on the bus, which is a design choice that affects
|
||||
bit/interpretation:
|
||||
- Low addresses have priority upon transmission conflicts
|
||||
- The broadcast bit is `1` (floating, no effort) for normal communication
|
||||
- For acknowledge bits, the receiver extends the logical '0' of the sync
|
||||
period to the length of a normal bit `0`. Hence, a NAK (bit `1`) is
|
||||
literally the absence of an ACK.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <concepts>
|
||||
|
||||
#include "avclan_defs.h"
|
||||
#include "avclan_phy.h" // bridge until phy has been ported
|
||||
|
||||
namespace avclan {
|
||||
class Bus {
|
||||
public:
|
||||
struct Error {
|
||||
enum class Read : uint8_t {
|
||||
BAD_PARITY = 0x01,
|
||||
STARTBIT_TOO_SHORT = 0x80, // Start *well* above Peripher::Error::Read
|
||||
// (which ~inherits these values)
|
||||
STARTBIT_TOO_LONG,
|
||||
BAD_STARTBIT,
|
||||
};
|
||||
|
||||
enum class Send : uint8_t {
|
||||
NAK = 0x01,
|
||||
};
|
||||
};
|
||||
class Handle;
|
||||
|
||||
void init();
|
||||
void mute(bool mute);
|
||||
bool is_muted() const;
|
||||
Handle get();
|
||||
};
|
||||
|
||||
class Bus::Handle {
|
||||
Handle() { AVCLAN_stopEvent(); }
|
||||
friend Bus;
|
||||
|
||||
public:
|
||||
~Handle() { AVCLAN_startEvent(); }
|
||||
Handle(const Handle &) = delete;
|
||||
Handle(Handle &&) = delete;
|
||||
|
||||
bool sendstartbit();
|
||||
Error::Read readstartbit();
|
||||
|
||||
template <auto N, std::unsigned_integral T>
|
||||
requires(sizeof(T) < 3 && N < 16)
|
||||
Error::Send send(T bits, bool ack) {
|
||||
const auto parity = sendbits<N>(bits);
|
||||
sendbits<1>(static_cast<uint8_t>(parity));
|
||||
|
||||
if (ack && !read_ACK())
|
||||
return Send::NAK;
|
||||
|
||||
return Send{0};
|
||||
};
|
||||
|
||||
template <auto N, std::unsigned_integral T, class F>
|
||||
requires(sizeof(T) < 3 && N < 16)
|
||||
Error::Read read(T *bits, F &&ack) {
|
||||
const auto calc_parity = readbits<N>(bits);
|
||||
uint8_t read_parity;
|
||||
readbits<1>(&read_parity);
|
||||
if (calc_parity != read_parity) {
|
||||
return Read::BAD_PARITY;
|
||||
} else if (ack()) {
|
||||
send_ACK();
|
||||
} else
|
||||
readbits<1>(&read_parity);
|
||||
|
||||
return Read{0};
|
||||
};
|
||||
template <auto N, std::unsigned_integral T>
|
||||
requires(sizeof(T) < 3 && N < 16)
|
||||
Error::Read read(T *bits, bool ack) {
|
||||
return read<N>(bits, [=]() { return ack; });
|
||||
}
|
||||
|
||||
private:
|
||||
using Read = Error::Read;
|
||||
using Send = Error::Send;
|
||||
|
||||
// A single bus symbol. bit_zero/bit_one carry data (and double as parity
|
||||
// values); bit_start marks a frame start bit.
|
||||
enum class avclan_bit : uint8_t {
|
||||
bit_zero = 0x00,
|
||||
bit_one = 0x01,
|
||||
bit_start = 0x10
|
||||
};
|
||||
|
||||
void send_ACK();
|
||||
uint8_t read_ACK();
|
||||
|
||||
template <auto N, class T> avclan_bit_t sendbits(T bits);
|
||||
template <auto N, class T> avclan_bit_t readbits(T *bits);
|
||||
|
||||
// Temporary specializations bridging to legacy C API
|
||||
// Replace with proper (single?) template when phy has been ported
|
||||
template <auto N>
|
||||
requires(N > 1 && N < 8)
|
||||
avclan_bit_t sendbits(uint8_t bits) {
|
||||
return AVCLAN_sendbitsi(&bits, N);
|
||||
};
|
||||
template <auto N>
|
||||
requires(N <= 16)
|
||||
avclan_bit_t sendbits(uint16_t bits) {
|
||||
return AVCLAN_sendbitsl(&bits, N);
|
||||
};
|
||||
template <auto N>
|
||||
requires(N < 8)
|
||||
avclan_bit_t readbits(uint8_t *bits) {
|
||||
return static_cast<avclan_bit_t>(AVCLAN_readbitsi(bits, N));
|
||||
};
|
||||
template <auto N>
|
||||
requires(N <= 16)
|
||||
avclan_bit_t readbits(uint16_t *bits) {
|
||||
return static_cast<avclan_bit_t>(AVCLAN_readbitsl(bits, N));
|
||||
};
|
||||
};
|
||||
|
||||
template <> inline avclan_bit_t Bus::Handle::sendbits<8>(uint8_t byte) {
|
||||
return AVCLAN_sendbyte(&byte);
|
||||
};
|
||||
template <> inline avclan_bit_t Bus::Handle::sendbits<1>(uint8_t byte) {
|
||||
const avclan_bit_t b{static_cast<avclan_bit_t>(byte & 1u)};
|
||||
AVCLAN_sendbit(b);
|
||||
return b;
|
||||
};
|
||||
template <> inline avclan_bit_t Bus::Handle::readbits<8>(uint8_t *byte) {
|
||||
return static_cast<avclan_bit_t>(AVCLAN_readbyte(byte));
|
||||
};
|
||||
|
||||
} // namespace avclan
|
||||
+108
-143
@@ -6,8 +6,8 @@
|
||||
#include "peripheral.hpp"
|
||||
#include "avclan_defs.h"
|
||||
#include "avclan_frame.h"
|
||||
#include "avclan_phy.h" // bus symbol I/O + transaction guard (target-provided)
|
||||
#include "com232.h" // error logging
|
||||
#include "bus.hpp"
|
||||
#include "com232.h" // error logging
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
@@ -26,104 +26,83 @@ Peripheral::Error::Read Peripheral::read(AVCLAN_frame_t *in, log_t print) {
|
||||
} err = {};
|
||||
|
||||
using enum Error::Read;
|
||||
auto BAD_PARITY = Bus::Error::Read::BAD_PARITY;
|
||||
|
||||
AVCLAN_stopEvent(); // quiesce contending sources during the read
|
||||
{ // bound handle lifetime
|
||||
auto handle = bus.get();
|
||||
|
||||
bool shouldACK = false;
|
||||
uint8_t tmp = 0, parity = 0;
|
||||
bool shouldACK = false;
|
||||
uint8_t tmp = 0;
|
||||
|
||||
err.errno = Error::Read{AVCLAN_readstartbit()};
|
||||
if (static_cast<uint8_t>(err.errno))
|
||||
goto handle_err;
|
||||
err.errno = Error::Read{static_cast<uint8_t>(handle.readstartbit())};
|
||||
if (static_cast<uint8_t>(err.errno))
|
||||
goto handle_err;
|
||||
|
||||
AVCLAN_readbits<1>(&tmp);
|
||||
in->is_unicast = tmp;
|
||||
handle.read<1>(&tmp, false);
|
||||
in->is_unicast = tmp;
|
||||
|
||||
parity = AVCLAN_readbits<12>(&in->controller_addr);
|
||||
AVCLAN_readbits<1>(&tmp);
|
||||
if (parity != (tmp &= 1)) {
|
||||
err.errno = BAD_CONTROLLER_PARITY;
|
||||
if (print.verbose) {
|
||||
err.read_val = in->controller_addr;
|
||||
err.parity = tmp;
|
||||
}
|
||||
goto handle_err;
|
||||
}
|
||||
|
||||
parity = AVCLAN_readbits<12>(&in->peripheral_addr);
|
||||
AVCLAN_readbits<1>(&tmp);
|
||||
if (parity != (tmp &= 1)) {
|
||||
err.errno = BAD_PERIPHERAL_PARITY;
|
||||
if (print.verbose) {
|
||||
err.read_val = in->peripheral_addr;
|
||||
err.parity = tmp;
|
||||
}
|
||||
goto handle_err;
|
||||
}
|
||||
|
||||
shouldACK = !AVCLAN_ismuted() && (in->peripheral_addr == address);
|
||||
|
||||
if (shouldACK)
|
||||
AVCLAN_sendbit_ACK();
|
||||
else
|
||||
AVCLAN_readbits<1>(&tmp);
|
||||
|
||||
parity = AVCLAN_readbits<4>(&in->control);
|
||||
AVCLAN_readbits<1>(&tmp);
|
||||
if (parity != (tmp &= 1)) {
|
||||
err.errno = BAD_CONTROL_PARITY;
|
||||
if (print.verbose) {
|
||||
err.read_val = in->control;
|
||||
err.parity = tmp;
|
||||
}
|
||||
goto handle_err;
|
||||
} else if (shouldACK) {
|
||||
AVCLAN_sendbit_ACK();
|
||||
} else {
|
||||
AVCLAN_readbits<1>(&tmp);
|
||||
}
|
||||
|
||||
parity = AVCLAN_readbyte(&in->length);
|
||||
AVCLAN_readbits<1>(&tmp);
|
||||
if (parity != (tmp &= 1)) {
|
||||
err.errno = BAD_LENGTH_PARITY;
|
||||
if (print.verbose) {
|
||||
err.read_val = in->length;
|
||||
err.parity = tmp;
|
||||
}
|
||||
goto handle_err;
|
||||
} else if (shouldACK) {
|
||||
AVCLAN_sendbit_ACK();
|
||||
} else {
|
||||
AVCLAN_readbits<1>(&tmp);
|
||||
}
|
||||
|
||||
if (in->length == 0 || in->length > MAXMSGLEN) {
|
||||
err.errno = BAD_LENGTH_RANGE;
|
||||
err.val = in->length;
|
||||
goto handle_err;
|
||||
}
|
||||
|
||||
for (uint8_t i = 0; i < in->length; i++) {
|
||||
parity = AVCLAN_readbits<8>(&in->data[i]);
|
||||
AVCLAN_readbits<1>(&tmp);
|
||||
if (parity != (tmp &= 1)) {
|
||||
err.errno = BAD_DATA_PARITY;
|
||||
if (auto rerr = handle.read<12>(&in->controller_addr, false);
|
||||
rerr == BAD_PARITY) {
|
||||
err.errno = BAD_CONTROLLER_PARITY;
|
||||
if (print.verbose) {
|
||||
err.read_val = in->data[i];
|
||||
err.parity = tmp;
|
||||
err.read_val = in->controller_addr;
|
||||
}
|
||||
goto handle_err;
|
||||
} else if (shouldACK) {
|
||||
AVCLAN_sendbit_ACK();
|
||||
} else {
|
||||
AVCLAN_readbits<1>(&tmp);
|
||||
}
|
||||
}
|
||||
|
||||
if (auto rerr = handle.read<12>(&in->peripheral_addr,
|
||||
[&]() {
|
||||
return !bus.is_muted() &&
|
||||
(in->peripheral_addr == address);
|
||||
});
|
||||
rerr == BAD_PARITY) {
|
||||
err.errno = BAD_PERIPHERAL_PARITY;
|
||||
if (print.verbose) {
|
||||
err.read_val = in->peripheral_addr;
|
||||
}
|
||||
goto handle_err;
|
||||
}
|
||||
|
||||
shouldACK = !bus.is_muted() && (in->peripheral_addr == address);
|
||||
|
||||
if (auto rerr = handle.read<4>(&in->control, shouldACK);
|
||||
rerr == BAD_PARITY) {
|
||||
err.errno = BAD_CONTROL_PARITY;
|
||||
if (print.verbose) {
|
||||
err.read_val = in->control;
|
||||
}
|
||||
goto handle_err;
|
||||
}
|
||||
|
||||
if (auto rerr = handle.read<8>(&in->length, shouldACK);
|
||||
rerr == BAD_PARITY) {
|
||||
err.errno = BAD_LENGTH_PARITY;
|
||||
if (print.verbose) {
|
||||
err.read_val = in->length;
|
||||
}
|
||||
goto handle_err;
|
||||
}
|
||||
|
||||
if (in->length == 0 || in->length > MAXMSGLEN) {
|
||||
err.errno = BAD_LENGTH_RANGE;
|
||||
err.val = in->length;
|
||||
goto handle_err;
|
||||
}
|
||||
|
||||
for (uint8_t i = 0; i < in->length; i++) {
|
||||
if (auto rerr = handle.read<8>(&in->data[i], shouldACK);
|
||||
rerr == BAD_PARITY) {
|
||||
err.errno = BAD_DATA_PARITY;
|
||||
if (print.verbose) {
|
||||
err.read_val = in->data[i];
|
||||
}
|
||||
goto handle_err;
|
||||
}
|
||||
}
|
||||
} // destroy handle
|
||||
|
||||
if (false) {
|
||||
handle_err:;
|
||||
AVCLAN_startEvent();
|
||||
RS232_Print("ERR(read): ");
|
||||
switch (err.errno) {
|
||||
case BAD_STARTBIT: RS232_Print("bad start bit (other)"); break;
|
||||
@@ -153,8 +132,6 @@ Peripheral::Error::Read Peripheral::read(AVCLAN_frame_t *in, log_t print) {
|
||||
}
|
||||
}
|
||||
RS232_Print("\n");
|
||||
} else {
|
||||
AVCLAN_startEvent();
|
||||
}
|
||||
|
||||
// Only print if some data has been correctly received
|
||||
@@ -177,70 +154,60 @@ Peripheral::Error::Send Peripheral::send(const AVCLAN_frame_t *out,
|
||||
} err = {};
|
||||
|
||||
using enum Error::Send;
|
||||
|
||||
avclan_bit_t parity;
|
||||
auto NAK = Bus::Error::Send::NAK;
|
||||
|
||||
if (AVCLAN_ismuted()) {
|
||||
err.errno = MUTED;
|
||||
goto handle_err;
|
||||
}
|
||||
|
||||
AVCLAN_stopEvent();
|
||||
{ // bound handle lifetime
|
||||
auto handle = bus.get();
|
||||
|
||||
if (!AVCLAN_sendstartbit()) {
|
||||
// Some other device is already driving the bus
|
||||
err.errno = BUSY;
|
||||
goto handle_err;
|
||||
}
|
||||
|
||||
AVCLAN_sendbits<1>(static_cast<uint8_t>(out->is_unicast));
|
||||
|
||||
parity = AVCLAN_sendbits<12>(out->controller_addr);
|
||||
AVCLAN_sendbit(parity);
|
||||
|
||||
parity = AVCLAN_sendbits<12>(address);
|
||||
AVCLAN_sendbit(parity);
|
||||
|
||||
if (out->is_unicast && !AVCLAN_readbit_ACK()) {
|
||||
err.errno = NAK_ADDRESS;
|
||||
goto handle_err;
|
||||
}
|
||||
|
||||
parity = AVCLAN_sendbits<4>(out->control);
|
||||
AVCLAN_sendbit(parity);
|
||||
|
||||
if (out->is_unicast && !AVCLAN_readbit_ACK()) {
|
||||
err.errno = NAK_CONTROL;
|
||||
goto handle_err;
|
||||
}
|
||||
|
||||
parity = AVCLAN_sendbits<8>(out->length); // data length
|
||||
AVCLAN_sendbit(parity);
|
||||
|
||||
if (out->is_unicast && !AVCLAN_readbit_ACK()) {
|
||||
err.errno = NAK_MESSAGE_LENGTH;
|
||||
goto handle_err;
|
||||
}
|
||||
|
||||
for (uint8_t i = 0; i < out->length; i++) {
|
||||
parity = AVCLAN_sendbits<8>(out->data[i]);
|
||||
AVCLAN_sendbit(parity);
|
||||
// Based on the µPD6708 datasheet, ACK bit for broadcast doesn't seem
|
||||
// necessary (i.e. This deviates from the previous broadcast specific
|
||||
// function that sent an extra `1` bit after each byte/parity)
|
||||
if (out->is_unicast && !AVCLAN_readbit_ACK()) {
|
||||
err.errno = NAK_DATA;
|
||||
err.val = i;
|
||||
if (!handle.sendstartbit()) {
|
||||
// Some other device is already driving the bus
|
||||
err.errno = BUSY;
|
||||
goto handle_err;
|
||||
}
|
||||
// else
|
||||
// AVCLAN_sendbit_1();
|
||||
}
|
||||
|
||||
handle.send<1>(static_cast<uint8_t>(out->is_unicast), false);
|
||||
|
||||
handle.send<12>(out->controller_addr, false);
|
||||
|
||||
if (auto serr = handle.send<12>(out->controller_addr, out->is_unicast);
|
||||
serr == NAK) {
|
||||
err.errno = NAK_ADDRESS;
|
||||
goto handle_err;
|
||||
}
|
||||
|
||||
if (auto serr = handle.send<4>(out->control, out->is_unicast);
|
||||
serr == NAK) {
|
||||
err.errno = NAK_CONTROL;
|
||||
goto handle_err;
|
||||
}
|
||||
|
||||
if (auto serr = handle.send<8>(out->length, out->is_unicast); serr == NAK) {
|
||||
err.errno = NAK_MESSAGE_LENGTH;
|
||||
goto handle_err;
|
||||
}
|
||||
|
||||
for (uint8_t i = 0; i < out->length; i++) {
|
||||
// Based on the µPD6708 datasheet, ACK bit for broadcast doesn't seem
|
||||
// necessary (i.e. This deviates from the previous broadcast specific
|
||||
// function that sent an extra `1` bit after each byte/parity)
|
||||
// Explanation for why audio-group broadcast state report isn't working?
|
||||
if (auto serr = handle.send<8>(out->data[i], out->is_unicast);
|
||||
serr == NAK) {
|
||||
err.errno = NAK_DATA;
|
||||
err.val = i;
|
||||
goto handle_err;
|
||||
}
|
||||
}
|
||||
} // destroy handle
|
||||
|
||||
// back to read mode
|
||||
if (false) {
|
||||
handle_err:;
|
||||
AVCLAN_startEvent();
|
||||
RS232_Print("Error");
|
||||
switch (err.errno) {
|
||||
case MUTED: RS232_Print(": Device muted"); break;
|
||||
@@ -265,8 +232,6 @@ Peripheral::Error::Send Peripheral::send(const AVCLAN_frame_t *out,
|
||||
break;
|
||||
}
|
||||
RS232_Print("\n");
|
||||
} else {
|
||||
AVCLAN_startEvent();
|
||||
}
|
||||
|
||||
if (print.print)
|
||||
|
||||
+23
-16
@@ -5,6 +5,8 @@
|
||||
#pragma once
|
||||
|
||||
#include "avclan_defs.h"
|
||||
#include "bus.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace avclan {
|
||||
@@ -14,33 +16,38 @@ public:
|
||||
// Error enums are ordered such that a lower numeric value corresponds to
|
||||
// more progress/success before an error occured, with 0 being no errors
|
||||
enum class Read : uint8_t {
|
||||
BAD_DATA_PARITY = rBAD_DATA_PARITY, // = 0x01
|
||||
BAD_LENGTH_RANGE = rBAD_LENGTH_RANGE,
|
||||
BAD_LENGTH_PARITY = rBAD_LENGTH_PARITY,
|
||||
BAD_PERIPHERAL_PARITY = rBAD_PERIPHERAL_PARITY,
|
||||
BAD_CONTROLLER_PARITY = rBAD_CONTROLLER_PARITY,
|
||||
BAD_CONTROL_PARITY = rBAD_CONTROL_PARITY,
|
||||
STARTBIT_TOO_SHORT = rSTARTBIT_TOO_SHORT,
|
||||
STARTBIT_TOO_LONG = rSTARTBIT_TOO_LONG,
|
||||
BAD_STARTBIT = rLATCHED_COMPARATOR,
|
||||
BAD_DATA_PARITY = 0x01,
|
||||
BAD_LENGTH_RANGE,
|
||||
BAD_LENGTH_PARITY,
|
||||
BAD_PERIPHERAL_PARITY,
|
||||
BAD_CONTROLLER_PARITY,
|
||||
BAD_CONTROL_PARITY,
|
||||
STARTBIT_TOO_SHORT =
|
||||
static_cast<uint8_t>(Bus::Error::Read::STARTBIT_TOO_SHORT),
|
||||
STARTBIT_TOO_LONG =
|
||||
static_cast<uint8_t>(Bus::Error::Read::STARTBIT_TOO_LONG),
|
||||
BAD_STARTBIT = static_cast<uint8_t>(Bus::Error::Read::BAD_STARTBIT),
|
||||
};
|
||||
|
||||
enum class Send : uint8_t {
|
||||
NAK_DATA = sNAK_DATA, // = 0x01
|
||||
NAK_MESSAGE_LENGTH = sNAK_MESSAGE_LENGTH,
|
||||
NAK_CONTROL = sNAK_CONTROL,
|
||||
NAK_ADDRESS = sNAK_ADDRESS,
|
||||
BUSY = sBUSY,
|
||||
MUTED = sMUTED,
|
||||
NAK_DATA = 0x01,
|
||||
NAK_MESSAGE_LENGTH,
|
||||
NAK_CONTROL,
|
||||
NAK_ADDRESS,
|
||||
BUSY,
|
||||
MUTED,
|
||||
};
|
||||
};
|
||||
|
||||
Peripheral(uint16_t address) : address{address} {}
|
||||
Peripheral(Bus bus, uint16_t address) : bus{bus}, address{address} {
|
||||
bus.init();
|
||||
}
|
||||
|
||||
Error::Read read(AVCLAN_frame_t *in, log_t print);
|
||||
Error::Send send(const AVCLAN_frame_t *out, log_t print);
|
||||
|
||||
private:
|
||||
Bus bus;
|
||||
const uint16_t address;
|
||||
};
|
||||
} // namespace avclan
|
||||
|
||||
@@ -3,48 +3,6 @@
|
||||
// Copyright (C) 2015 Allen Hill <allenofthehills@gmail.com>
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
/*
|
||||
AVC LAN Theory
|
||||
|
||||
The AVC LAN bus is an implementation of the IEBus (mode 1) which is a
|
||||
differential signal; IEBus is electrically (but not logically) compatible with
|
||||
CAN bus.
|
||||
|
||||
- Logical `1`: Potential difference between bus lines (BUS+ pin and BUS– pin)
|
||||
is 20 mV or lower (floating).
|
||||
- Logical `0`: Potential difference between bus lines (BUS+ pin and BUS– pin)
|
||||
is 120 mV or higher (driving).
|
||||
|
||||
A nominal bit length is 39 us, composed of 3 periods: preparation,
|
||||
synchronization, data.
|
||||
|
||||
Figure 1. AVCLAN Bus bit format
|
||||
|
||||
│ Prep │<─ Sync ─>│<─ Data ─>│ ...
|
||||
Driving (logical `0`) ╭──────────╮──────────╮
|
||||
│ │ │
|
||||
Floating (logical `1`) ─────────╯ ╰──────────╰─────────
|
||||
│ 6 μs │── 19 μs ─│─ 13 μs ──│
|
||||
|
||||
The logical value during the data period signifies the bit value, e.g. a bit
|
||||
`0` continues the logical `0` (high potential difference between bus lines) of
|
||||
the sync period thru the data period, and a bit `1` has a logical `1`
|
||||
(low/floating potential between bus lines) during the data period. Using the
|
||||
TCB pulse-width and frequency measure mode, the total bit length differs for
|
||||
bit `1` and `0`; detailed bit timing can be found in "timing.h". The bus
|
||||
idles at low potential (floating).
|
||||
|
||||
A start bit is nominally 169 us high followed by 20 us low.
|
||||
|
||||
A bit `0` is dominant on the bus, which is a design choice that affects
|
||||
bit/interpretation:
|
||||
- Low addresses have priority upon transmission conflicts
|
||||
- The broadcast bit is `1` (floating, no effort) for normal communication
|
||||
- For acknowledge bits, the receiver extends the logical '0' of the sync
|
||||
period to the length of a normal bit `0`. Hence, a NAK (bit `1`) is
|
||||
literally the absence of an ACK.
|
||||
*/
|
||||
|
||||
#include <avr/interrupt.h>
|
||||
#include <avr/io.h>
|
||||
#include <avr/sfr_defs.h>
|
||||
|
||||
+2
-1
@@ -71,7 +71,8 @@ int main() {
|
||||
|
||||
const AVCLAN_frame_t *lastStatus = nullptr;
|
||||
|
||||
avclan::Peripheral cd_changer(0x360);
|
||||
avclan::Bus phy;
|
||||
avclan::Peripheral cd_changer(phy, 0x360);
|
||||
using Error = avclan::Peripheral::Error;
|
||||
|
||||
Setup();
|
||||
|
||||
Reference in New Issue
Block a user