mirror of
https://github.com/halleysfifthinc/AVCLAN-Mockingboard.git
synced 2026-08-07 01:13:18 +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/avclan_protocol.c
|
||||||
src/avclan/cdchanger.c
|
src/avclan/cdchanger.c
|
||||||
src/avclan/peripheral.cc
|
src/avclan/peripheral.cc
|
||||||
|
src/avclan/bus.cc
|
||||||
)
|
)
|
||||||
|
|
||||||
# avclan exports its public generic headers (src/avclan) to consumers and
|
# 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
|
||||||
+38
-73
@@ -6,7 +6,7 @@
|
|||||||
#include "peripheral.hpp"
|
#include "peripheral.hpp"
|
||||||
#include "avclan_defs.h"
|
#include "avclan_defs.h"
|
||||||
#include "avclan_frame.h"
|
#include "avclan_frame.h"
|
||||||
#include "avclan_phy.h" // bus symbol I/O + transaction guard (target-provided)
|
#include "bus.hpp"
|
||||||
#include "com232.h" // error logging
|
#include "com232.h" // error logging
|
||||||
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
@@ -26,76 +26,61 @@ Peripheral::Error::Read Peripheral::read(AVCLAN_frame_t *in, log_t print) {
|
|||||||
} err = {};
|
} err = {};
|
||||||
|
|
||||||
using enum Error::Read;
|
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;
|
bool shouldACK = false;
|
||||||
uint8_t tmp = 0, parity = 0;
|
uint8_t tmp = 0;
|
||||||
|
|
||||||
err.errno = Error::Read{AVCLAN_readstartbit()};
|
err.errno = Error::Read{static_cast<uint8_t>(handle.readstartbit())};
|
||||||
if (static_cast<uint8_t>(err.errno))
|
if (static_cast<uint8_t>(err.errno))
|
||||||
goto handle_err;
|
goto handle_err;
|
||||||
|
|
||||||
AVCLAN_readbits<1>(&tmp);
|
handle.read<1>(&tmp, false);
|
||||||
in->is_unicast = tmp;
|
in->is_unicast = tmp;
|
||||||
|
|
||||||
parity = AVCLAN_readbits<12>(&in->controller_addr);
|
if (auto rerr = handle.read<12>(&in->controller_addr, false);
|
||||||
AVCLAN_readbits<1>(&tmp);
|
rerr == BAD_PARITY) {
|
||||||
if (parity != (tmp &= 1)) {
|
|
||||||
err.errno = BAD_CONTROLLER_PARITY;
|
err.errno = BAD_CONTROLLER_PARITY;
|
||||||
if (print.verbose) {
|
if (print.verbose) {
|
||||||
err.read_val = in->controller_addr;
|
err.read_val = in->controller_addr;
|
||||||
err.parity = tmp;
|
|
||||||
}
|
}
|
||||||
goto handle_err;
|
goto handle_err;
|
||||||
}
|
}
|
||||||
|
|
||||||
parity = AVCLAN_readbits<12>(&in->peripheral_addr);
|
if (auto rerr = handle.read<12>(&in->peripheral_addr,
|
||||||
AVCLAN_readbits<1>(&tmp);
|
[&]() {
|
||||||
if (parity != (tmp &= 1)) {
|
return !bus.is_muted() &&
|
||||||
|
(in->peripheral_addr == address);
|
||||||
|
});
|
||||||
|
rerr == BAD_PARITY) {
|
||||||
err.errno = BAD_PERIPHERAL_PARITY;
|
err.errno = BAD_PERIPHERAL_PARITY;
|
||||||
if (print.verbose) {
|
if (print.verbose) {
|
||||||
err.read_val = in->peripheral_addr;
|
err.read_val = in->peripheral_addr;
|
||||||
err.parity = tmp;
|
|
||||||
}
|
}
|
||||||
goto handle_err;
|
goto handle_err;
|
||||||
}
|
}
|
||||||
|
|
||||||
shouldACK = !AVCLAN_ismuted() && (in->peripheral_addr == address);
|
shouldACK = !bus.is_muted() && (in->peripheral_addr == address);
|
||||||
|
|
||||||
if (shouldACK)
|
if (auto rerr = handle.read<4>(&in->control, shouldACK);
|
||||||
AVCLAN_sendbit_ACK();
|
rerr == BAD_PARITY) {
|
||||||
else
|
|
||||||
AVCLAN_readbits<1>(&tmp);
|
|
||||||
|
|
||||||
parity = AVCLAN_readbits<4>(&in->control);
|
|
||||||
AVCLAN_readbits<1>(&tmp);
|
|
||||||
if (parity != (tmp &= 1)) {
|
|
||||||
err.errno = BAD_CONTROL_PARITY;
|
err.errno = BAD_CONTROL_PARITY;
|
||||||
if (print.verbose) {
|
if (print.verbose) {
|
||||||
err.read_val = in->control;
|
err.read_val = in->control;
|
||||||
err.parity = tmp;
|
|
||||||
}
|
}
|
||||||
goto handle_err;
|
goto handle_err;
|
||||||
} else if (shouldACK) {
|
|
||||||
AVCLAN_sendbit_ACK();
|
|
||||||
} else {
|
|
||||||
AVCLAN_readbits<1>(&tmp);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
parity = AVCLAN_readbyte(&in->length);
|
if (auto rerr = handle.read<8>(&in->length, shouldACK);
|
||||||
AVCLAN_readbits<1>(&tmp);
|
rerr == BAD_PARITY) {
|
||||||
if (parity != (tmp &= 1)) {
|
|
||||||
err.errno = BAD_LENGTH_PARITY;
|
err.errno = BAD_LENGTH_PARITY;
|
||||||
if (print.verbose) {
|
if (print.verbose) {
|
||||||
err.read_val = in->length;
|
err.read_val = in->length;
|
||||||
err.parity = tmp;
|
|
||||||
}
|
}
|
||||||
goto handle_err;
|
goto handle_err;
|
||||||
} else if (shouldACK) {
|
|
||||||
AVCLAN_sendbit_ACK();
|
|
||||||
} else {
|
|
||||||
AVCLAN_readbits<1>(&tmp);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (in->length == 0 || in->length > MAXMSGLEN) {
|
if (in->length == 0 || in->length > MAXMSGLEN) {
|
||||||
@@ -105,25 +90,19 @@ Peripheral::Error::Read Peripheral::read(AVCLAN_frame_t *in, log_t print) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (uint8_t i = 0; i < in->length; i++) {
|
for (uint8_t i = 0; i < in->length; i++) {
|
||||||
parity = AVCLAN_readbits<8>(&in->data[i]);
|
if (auto rerr = handle.read<8>(&in->data[i], shouldACK);
|
||||||
AVCLAN_readbits<1>(&tmp);
|
rerr == BAD_PARITY) {
|
||||||
if (parity != (tmp &= 1)) {
|
|
||||||
err.errno = BAD_DATA_PARITY;
|
err.errno = BAD_DATA_PARITY;
|
||||||
if (print.verbose) {
|
if (print.verbose) {
|
||||||
err.read_val = in->data[i];
|
err.read_val = in->data[i];
|
||||||
err.parity = tmp;
|
|
||||||
}
|
}
|
||||||
goto handle_err;
|
goto handle_err;
|
||||||
} else if (shouldACK) {
|
|
||||||
AVCLAN_sendbit_ACK();
|
|
||||||
} else {
|
|
||||||
AVCLAN_readbits<1>(&tmp);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} // destroy handle
|
||||||
|
|
||||||
if (false) {
|
if (false) {
|
||||||
handle_err:;
|
handle_err:;
|
||||||
AVCLAN_startEvent();
|
|
||||||
RS232_Print("ERR(read): ");
|
RS232_Print("ERR(read): ");
|
||||||
switch (err.errno) {
|
switch (err.errno) {
|
||||||
case BAD_STARTBIT: RS232_Print("bad start bit (other)"); break;
|
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");
|
RS232_Print("\n");
|
||||||
} else {
|
|
||||||
AVCLAN_startEvent();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Only print if some data has been correctly received
|
// Only print if some data has been correctly received
|
||||||
@@ -177,70 +154,60 @@ Peripheral::Error::Send Peripheral::send(const AVCLAN_frame_t *out,
|
|||||||
} err = {};
|
} err = {};
|
||||||
|
|
||||||
using enum Error::Send;
|
using enum Error::Send;
|
||||||
|
auto NAK = Bus::Error::Send::NAK;
|
||||||
avclan_bit_t parity;
|
|
||||||
|
|
||||||
if (AVCLAN_ismuted()) {
|
if (AVCLAN_ismuted()) {
|
||||||
err.errno = MUTED;
|
err.errno = MUTED;
|
||||||
goto handle_err;
|
goto handle_err;
|
||||||
}
|
}
|
||||||
|
|
||||||
AVCLAN_stopEvent();
|
{ // bound handle lifetime
|
||||||
|
auto handle = bus.get();
|
||||||
|
|
||||||
if (!AVCLAN_sendstartbit()) {
|
if (!handle.sendstartbit()) {
|
||||||
// Some other device is already driving the bus
|
// Some other device is already driving the bus
|
||||||
err.errno = BUSY;
|
err.errno = BUSY;
|
||||||
goto handle_err;
|
goto handle_err;
|
||||||
}
|
}
|
||||||
|
|
||||||
AVCLAN_sendbits<1>(static_cast<uint8_t>(out->is_unicast));
|
handle.send<1>(static_cast<uint8_t>(out->is_unicast), false);
|
||||||
|
|
||||||
parity = AVCLAN_sendbits<12>(out->controller_addr);
|
handle.send<12>(out->controller_addr, false);
|
||||||
AVCLAN_sendbit(parity);
|
|
||||||
|
|
||||||
parity = AVCLAN_sendbits<12>(address);
|
if (auto serr = handle.send<12>(out->controller_addr, out->is_unicast);
|
||||||
AVCLAN_sendbit(parity);
|
serr == NAK) {
|
||||||
|
|
||||||
if (out->is_unicast && !AVCLAN_readbit_ACK()) {
|
|
||||||
err.errno = NAK_ADDRESS;
|
err.errno = NAK_ADDRESS;
|
||||||
goto handle_err;
|
goto handle_err;
|
||||||
}
|
}
|
||||||
|
|
||||||
parity = AVCLAN_sendbits<4>(out->control);
|
if (auto serr = handle.send<4>(out->control, out->is_unicast);
|
||||||
AVCLAN_sendbit(parity);
|
serr == NAK) {
|
||||||
|
|
||||||
if (out->is_unicast && !AVCLAN_readbit_ACK()) {
|
|
||||||
err.errno = NAK_CONTROL;
|
err.errno = NAK_CONTROL;
|
||||||
goto handle_err;
|
goto handle_err;
|
||||||
}
|
}
|
||||||
|
|
||||||
parity = AVCLAN_sendbits<8>(out->length); // data length
|
if (auto serr = handle.send<8>(out->length, out->is_unicast); serr == NAK) {
|
||||||
AVCLAN_sendbit(parity);
|
|
||||||
|
|
||||||
if (out->is_unicast && !AVCLAN_readbit_ACK()) {
|
|
||||||
err.errno = NAK_MESSAGE_LENGTH;
|
err.errno = NAK_MESSAGE_LENGTH;
|
||||||
goto handle_err;
|
goto handle_err;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (uint8_t i = 0; i < out->length; i++) {
|
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
|
// Based on the µPD6708 datasheet, ACK bit for broadcast doesn't seem
|
||||||
// necessary (i.e. This deviates from the previous broadcast specific
|
// necessary (i.e. This deviates from the previous broadcast specific
|
||||||
// function that sent an extra `1` bit after each byte/parity)
|
// function that sent an extra `1` bit after each byte/parity)
|
||||||
if (out->is_unicast && !AVCLAN_readbit_ACK()) {
|
// 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.errno = NAK_DATA;
|
||||||
err.val = i;
|
err.val = i;
|
||||||
goto handle_err;
|
goto handle_err;
|
||||||
}
|
}
|
||||||
// else
|
|
||||||
// AVCLAN_sendbit_1();
|
|
||||||
}
|
}
|
||||||
|
} // destroy handle
|
||||||
|
|
||||||
// back to read mode
|
// back to read mode
|
||||||
if (false) {
|
if (false) {
|
||||||
handle_err:;
|
handle_err:;
|
||||||
AVCLAN_startEvent();
|
|
||||||
RS232_Print("Error");
|
RS232_Print("Error");
|
||||||
switch (err.errno) {
|
switch (err.errno) {
|
||||||
case MUTED: RS232_Print(": Device muted"); break;
|
case MUTED: RS232_Print(": Device muted"); break;
|
||||||
@@ -265,8 +232,6 @@ Peripheral::Error::Send Peripheral::send(const AVCLAN_frame_t *out,
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
RS232_Print("\n");
|
RS232_Print("\n");
|
||||||
} else {
|
|
||||||
AVCLAN_startEvent();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (print.print)
|
if (print.print)
|
||||||
|
|||||||
+23
-16
@@ -5,6 +5,8 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "avclan_defs.h"
|
#include "avclan_defs.h"
|
||||||
|
#include "bus.hpp"
|
||||||
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
|
||||||
namespace avclan {
|
namespace avclan {
|
||||||
@@ -14,33 +16,38 @@ public:
|
|||||||
// Error enums are ordered such that a lower numeric value corresponds to
|
// Error enums are ordered such that a lower numeric value corresponds to
|
||||||
// more progress/success before an error occured, with 0 being no errors
|
// more progress/success before an error occured, with 0 being no errors
|
||||||
enum class Read : uint8_t {
|
enum class Read : uint8_t {
|
||||||
BAD_DATA_PARITY = rBAD_DATA_PARITY, // = 0x01
|
BAD_DATA_PARITY = 0x01,
|
||||||
BAD_LENGTH_RANGE = rBAD_LENGTH_RANGE,
|
BAD_LENGTH_RANGE,
|
||||||
BAD_LENGTH_PARITY = rBAD_LENGTH_PARITY,
|
BAD_LENGTH_PARITY,
|
||||||
BAD_PERIPHERAL_PARITY = rBAD_PERIPHERAL_PARITY,
|
BAD_PERIPHERAL_PARITY,
|
||||||
BAD_CONTROLLER_PARITY = rBAD_CONTROLLER_PARITY,
|
BAD_CONTROLLER_PARITY,
|
||||||
BAD_CONTROL_PARITY = rBAD_CONTROL_PARITY,
|
BAD_CONTROL_PARITY,
|
||||||
STARTBIT_TOO_SHORT = rSTARTBIT_TOO_SHORT,
|
STARTBIT_TOO_SHORT =
|
||||||
STARTBIT_TOO_LONG = rSTARTBIT_TOO_LONG,
|
static_cast<uint8_t>(Bus::Error::Read::STARTBIT_TOO_SHORT),
|
||||||
BAD_STARTBIT = rLATCHED_COMPARATOR,
|
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 {
|
enum class Send : uint8_t {
|
||||||
NAK_DATA = sNAK_DATA, // = 0x01
|
NAK_DATA = 0x01,
|
||||||
NAK_MESSAGE_LENGTH = sNAK_MESSAGE_LENGTH,
|
NAK_MESSAGE_LENGTH,
|
||||||
NAK_CONTROL = sNAK_CONTROL,
|
NAK_CONTROL,
|
||||||
NAK_ADDRESS = sNAK_ADDRESS,
|
NAK_ADDRESS,
|
||||||
BUSY = sBUSY,
|
BUSY,
|
||||||
MUTED = sMUTED,
|
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::Read read(AVCLAN_frame_t *in, log_t print);
|
||||||
Error::Send send(const AVCLAN_frame_t *out, log_t print);
|
Error::Send send(const AVCLAN_frame_t *out, log_t print);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
Bus bus;
|
||||||
const uint16_t address;
|
const uint16_t address;
|
||||||
};
|
};
|
||||||
} // namespace avclan
|
} // namespace avclan
|
||||||
|
|||||||
@@ -3,48 +3,6 @@
|
|||||||
// Copyright (C) 2015 Allen Hill <allenofthehills@gmail.com>
|
// Copyright (C) 2015 Allen Hill <allenofthehills@gmail.com>
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// 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/interrupt.h>
|
||||||
#include <avr/io.h>
|
#include <avr/io.h>
|
||||||
#include <avr/sfr_defs.h>
|
#include <avr/sfr_defs.h>
|
||||||
|
|||||||
+2
-1
@@ -71,7 +71,8 @@ int main() {
|
|||||||
|
|
||||||
const AVCLAN_frame_t *lastStatus = nullptr;
|
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;
|
using Error = avclan::Peripheral::Error;
|
||||||
|
|
||||||
Setup();
|
Setup();
|
||||||
|
|||||||
Reference in New Issue
Block a user