mirror of
https://github.com/halleysfifthinc/AVCLAN-Mockingboard.git
synced 2026-08-06 17:03:17 +00:00
Add C++ Peripheral class
This commit is contained in:
+13
-10
@@ -33,22 +33,25 @@ set(AVCLAN_TARGET avr-attiny3216 CACHE STRING "Hardware target (port) to build")
|
||||
add_library(avclan STATIC
|
||||
src/avclan/avclan_frame.c
|
||||
src/avclan/avclan_protocol.c
|
||||
src/avclan/cdchanger.c)
|
||||
src/avclan/cdchanger.c
|
||||
src/avclan/peripheral.cc
|
||||
)
|
||||
|
||||
add_executable(mockingboard
|
||||
src/sniffer.cc
|
||||
src/com232.c)
|
||||
|
||||
# avclan exports its public generic headers (src/avclan) to consumers and
|
||||
# reaches into src/ for sibling headers (com232.h, board.h) during its own
|
||||
# build. The selected target adds its own per-target include path.
|
||||
target_include_directories(avclan
|
||||
PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/src/avclan
|
||||
PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src)
|
||||
|
||||
target_link_libraries(mockingboard avclan)
|
||||
target_include_directories(avclan PUBLIC
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/avclan
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src)
|
||||
|
||||
# Pull in the selected hardware target: its port sources, per-target headers,
|
||||
# hardware-specific compile options/definitions, device-pack handling, and the
|
||||
# flashing target. Added after the targets above so it can extend them.
|
||||
add_subdirectory(src/avclan/target/${AVCLAN_TARGET})
|
||||
|
||||
add_executable(mockingboard
|
||||
src/sniffer.cc
|
||||
src/com232.c
|
||||
)
|
||||
|
||||
target_link_libraries(mockingboard avclan)
|
||||
|
||||
@@ -137,7 +137,7 @@ typedef enum avclan_bit : uint8_t {
|
||||
// progress/success before an error occured, with 0 being no errors
|
||||
typedef enum : uint8_t {
|
||||
rNO_ERROR = 0x00,
|
||||
rBAD_DATA_PARITY,
|
||||
rBAD_DATA_PARITY = 0x01,
|
||||
rBAD_LENGTH_RANGE,
|
||||
rBAD_LENGTH_PARITY,
|
||||
rBAD_PERIPHERAL_PARITY,
|
||||
@@ -150,7 +150,7 @@ typedef enum : uint8_t {
|
||||
|
||||
typedef enum : uint8_t {
|
||||
sNO_ERROR = 0x00,
|
||||
sNAK_DATA,
|
||||
sNAK_DATA = 0x01,
|
||||
sNAK_MESSAGE_LENGTH,
|
||||
sNAK_CONTROL,
|
||||
sNAK_ADDRESS,
|
||||
|
||||
@@ -10,262 +10,6 @@
|
||||
#include "avclan_phy.h" // bus symbol I/O + transaction guard (target-provided)
|
||||
#include "com232.h" // error logging
|
||||
|
||||
avclan_readerr_t AVCLAN_readframe(AVCLAN_frame_t *frame, log_t print) {
|
||||
struct errtype {
|
||||
avclan_readerr_t errno;
|
||||
union {
|
||||
uint8_t val; // BAD_LENGTH_RANGE: the out-of-range length value
|
||||
struct {
|
||||
uint16_t read_val;
|
||||
uint8_t parity; // received (bad) parity bit
|
||||
};
|
||||
};
|
||||
} err = {0};
|
||||
|
||||
AVCLAN_stopEvent(); // quiesce contending sources during the read
|
||||
|
||||
uint8_t tmp = 0;
|
||||
|
||||
err.errno = AVCLAN_readstartbit();
|
||||
if (err.errno)
|
||||
goto handle_err;
|
||||
|
||||
AVCLAN_readbits(&tmp, 1);
|
||||
frame->is_unicast = tmp;
|
||||
|
||||
uint8_t parity = AVCLAN_readbits(&frame->controller_addr, 12);
|
||||
AVCLAN_readbits(&tmp, 1);
|
||||
if (parity != (tmp &= 1)) {
|
||||
err.errno = rBAD_CONTROLLER_PARITY;
|
||||
if (print.verbose) {
|
||||
err.read_val = frame->controller_addr;
|
||||
err.parity = tmp;
|
||||
}
|
||||
goto handle_err;
|
||||
}
|
||||
|
||||
parity = AVCLAN_readbits(&frame->peripheral_addr, 12);
|
||||
AVCLAN_readbits(&tmp, 1);
|
||||
if (parity != (tmp &= 1)) {
|
||||
err.errno = rBAD_PERIPHERAL_PARITY;
|
||||
if (print.verbose) {
|
||||
err.read_val = frame->peripheral_addr;
|
||||
err.parity = tmp;
|
||||
}
|
||||
goto handle_err;
|
||||
}
|
||||
|
||||
bool shouldACK = !AVCLAN_ismuted() && (frame->peripheral_addr == DEVICE_ADDR);
|
||||
|
||||
if (shouldACK)
|
||||
AVCLAN_sendbit_ACK();
|
||||
else
|
||||
AVCLAN_readbits(&tmp, 1);
|
||||
|
||||
parity = AVCLAN_readbits(&frame->control, 4);
|
||||
AVCLAN_readbits(&tmp, 1);
|
||||
if (parity != (tmp &= 1)) {
|
||||
err.errno = rBAD_CONTROL_PARITY;
|
||||
if (print.verbose) {
|
||||
err.read_val = frame->control;
|
||||
err.parity = tmp;
|
||||
}
|
||||
goto handle_err;
|
||||
} else if (shouldACK) {
|
||||
AVCLAN_sendbit_ACK();
|
||||
} else {
|
||||
AVCLAN_readbits(&tmp, 1);
|
||||
}
|
||||
|
||||
parity = AVCLAN_readbyte(&frame->length);
|
||||
AVCLAN_readbits(&tmp, 1);
|
||||
if (parity != (tmp &= 1)) {
|
||||
err.errno = rBAD_LENGTH_PARITY;
|
||||
if (print.verbose) {
|
||||
err.read_val = frame->length;
|
||||
err.parity = tmp;
|
||||
}
|
||||
goto handle_err;
|
||||
} else if (shouldACK) {
|
||||
AVCLAN_sendbit_ACK();
|
||||
} else {
|
||||
AVCLAN_readbits(&tmp, 1);
|
||||
}
|
||||
|
||||
if (frame->length == 0 || frame->length > MAXMSGLEN) {
|
||||
err.errno = rBAD_LENGTH_RANGE;
|
||||
err.val = frame->length;
|
||||
goto handle_err;
|
||||
}
|
||||
|
||||
for (uint8_t i = 0; i < frame->length; i++) {
|
||||
parity = AVCLAN_readbyte(&frame->data[i]);
|
||||
AVCLAN_readbits(&tmp, 1);
|
||||
if (parity != (tmp &= 1)) {
|
||||
err.errno = rBAD_DATA_PARITY;
|
||||
if (print.verbose) {
|
||||
err.read_val = frame->data[i];
|
||||
err.parity = tmp;
|
||||
}
|
||||
goto handle_err;
|
||||
} else if (shouldACK) {
|
||||
AVCLAN_sendbit_ACK();
|
||||
} else {
|
||||
AVCLAN_readbits(&tmp, 1);
|
||||
}
|
||||
}
|
||||
|
||||
if (false) {
|
||||
handle_err:;
|
||||
AVCLAN_startEvent();
|
||||
RS232_Print("ERR(read): ");
|
||||
switch (err.errno) {
|
||||
case rLATCHED_COMPARATOR: RS232_Print("latched comparator"); break;
|
||||
case rSTARTBIT_TOO_SHORT: RS232_Print("start bit too short"); break;
|
||||
case rSTARTBIT_TOO_LONG: RS232_Print("start bit too long"); break;
|
||||
case rBAD_CONTROLLER_PARITY:
|
||||
RS232_Print("reading controller addr.");
|
||||
goto VERBOSE;
|
||||
case rBAD_PERIPHERAL_PARITY:
|
||||
RS232_Print("reading peripheral addr.");
|
||||
goto VERBOSE;
|
||||
case rBAD_CONTROL_PARITY: RS232_Print("reading control"); goto VERBOSE;
|
||||
case rBAD_LENGTH_PARITY: RS232_Print("reading length"); goto VERBOSE;
|
||||
case rBAD_LENGTH_RANGE:
|
||||
RS232_Print("bad length 0x");
|
||||
RS232_PrintHex4(err.val);
|
||||
break;
|
||||
case rBAD_DATA_PARITY: RS232_Print("reading data"); goto VERBOSE;
|
||||
case rNO_ERROR:
|
||||
__builtin_unreachable();
|
||||
VERBOSE:
|
||||
if (print.verbose) {
|
||||
RS232_Print("; read 0x");
|
||||
RS232_PrintHex(err.read_val);
|
||||
RS232_Print(" and got bad parity ");
|
||||
RS232_PrintHex4(err.parity);
|
||||
}
|
||||
}
|
||||
RS232_Print("\n");
|
||||
} else {
|
||||
AVCLAN_startEvent();
|
||||
}
|
||||
|
||||
// Only print if some data has been correctly received
|
||||
if (print.print && (err.errno < rSTARTBIT_TOO_SHORT)) {
|
||||
if (err.errno > rBAD_DATA_PARITY)
|
||||
frame->length = 0;
|
||||
AVCLAN_printframe(frame, print.binary);
|
||||
}
|
||||
|
||||
return err.errno;
|
||||
}
|
||||
|
||||
avclan_senderr_t AVCLAN_sendframe(const AVCLAN_frame_t *frame, log_t print) {
|
||||
struct errtype {
|
||||
// Error enum is ordered such that a lower numeric value corresponds to more
|
||||
// success
|
||||
avclan_senderr_t errno;
|
||||
uint8_t val;
|
||||
} err = {0};
|
||||
|
||||
if (AVCLAN_ismuted()) {
|
||||
err.errno = sMUTED;
|
||||
goto handle_err;
|
||||
}
|
||||
|
||||
AVCLAN_stopEvent();
|
||||
|
||||
if (!AVCLAN_sendstartbit()) {
|
||||
// Some other device is already driving the bus
|
||||
err.errno = sBUSY;
|
||||
goto handle_err;
|
||||
}
|
||||
|
||||
AVCLAN_sendbits(&(uint8_t){frame->is_unicast}, 1);
|
||||
|
||||
avclan_bit_t parity = AVCLAN_sendbits(&frame->controller_addr, 12);
|
||||
AVCLAN_sendbit(parity);
|
||||
|
||||
parity = AVCLAN_sendbits(&frame->peripheral_addr, 12);
|
||||
AVCLAN_sendbit(parity);
|
||||
|
||||
if (frame->is_unicast && !AVCLAN_readbit_ACK()) {
|
||||
err.errno = sNAK_ADDRESS;
|
||||
goto handle_err;
|
||||
}
|
||||
|
||||
parity = AVCLAN_sendbits(&frame->control, 4);
|
||||
AVCLAN_sendbit(parity);
|
||||
|
||||
if (frame->is_unicast && !AVCLAN_readbit_ACK()) {
|
||||
err.errno = sNAK_CONTROL;
|
||||
goto handle_err;
|
||||
}
|
||||
|
||||
parity = AVCLAN_sendbyte(&frame->length); // data length
|
||||
AVCLAN_sendbit(parity);
|
||||
|
||||
if (frame->is_unicast && !AVCLAN_readbit_ACK()) {
|
||||
err.errno = sNAK_MESSAGE_LENGTH;
|
||||
goto handle_err;
|
||||
}
|
||||
|
||||
for (uint8_t i = 0; i < frame->length; i++) {
|
||||
parity = AVCLAN_sendbyte(&frame->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 (frame->is_unicast && !AVCLAN_readbit_ACK()) {
|
||||
err.errno = sNAK_DATA;
|
||||
err.val = i;
|
||||
goto handle_err;
|
||||
}
|
||||
// else
|
||||
// AVCLAN_sendbit_1();
|
||||
}
|
||||
|
||||
// back to read mode
|
||||
if (false) {
|
||||
handle_err:;
|
||||
AVCLAN_startEvent();
|
||||
RS232_Print("Error");
|
||||
switch (err.errno) {
|
||||
case sMUTED: RS232_Print(": Device muted"); break;
|
||||
case sBUSY: RS232_Print(": Busy bus"); break;
|
||||
case sNAK_ADDRESS:
|
||||
case sNAK_CONTROL:
|
||||
case sNAK_MESSAGE_LENGTH:
|
||||
case sNAK_DATA:
|
||||
RS232_Print(" NAK: ");
|
||||
switch (err.errno) {
|
||||
case sNAK_ADDRESS: RS232_Print("address"); break;
|
||||
case sNAK_CONTROL: RS232_Print("Control"); break;
|
||||
case sNAK_MESSAGE_LENGTH: RS232_Print("Message length"); break;
|
||||
case sNAK_DATA:
|
||||
RS232_Print(" data[");
|
||||
RS232_PrintDec(err.val);
|
||||
RS232_Print("]");
|
||||
break;
|
||||
case sNO_ERROR:
|
||||
case sMUTED:
|
||||
case sBUSY: __builtin_unreachable();
|
||||
}
|
||||
break;
|
||||
case sNO_ERROR: __builtin_unreachable();
|
||||
}
|
||||
RS232_Print("\n");
|
||||
} else {
|
||||
AVCLAN_startEvent();
|
||||
}
|
||||
|
||||
if (print.print)
|
||||
AVCLAN_printframe(frame, print.binary);
|
||||
|
||||
return err.errno;
|
||||
}
|
||||
|
||||
void AVCLAN_printframe(const AVCLAN_frame_t *frame, bool binary) {
|
||||
if (binary) {
|
||||
uint8_t buffer[8];
|
||||
|
||||
@@ -39,8 +39,6 @@
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
avclan_readerr_t AVCLAN_readframe(AVCLAN_frame_t *frame, log_t print);
|
||||
avclan_senderr_t AVCLAN_sendframe(const AVCLAN_frame_t *frame, log_t print);
|
||||
void AVCLAN_printframe(const AVCLAN_frame_t *frame, bool binary);
|
||||
uint8_t AVCLAN_parseframe(const uint8_t *bytes, uint8_t len,
|
||||
AVCLAN_frame_t *frame);
|
||||
|
||||
+35
-14
@@ -53,20 +53,6 @@ uint8_t AVCLAN_readbitsi(uint8_t *bits, uint8_t len);
|
||||
uint8_t AVCLAN_readbitsl(uint16_t *bits, int8_t len);
|
||||
uint8_t AVCLAN_readbyte(uint8_t *byte);
|
||||
|
||||
#define AVCLAN_sendbits(bits, len) \
|
||||
_Generic((bits), \
|
||||
const uint16_t *: AVCLAN_sendbitsl, \
|
||||
uint16_t *: AVCLAN_sendbitsl, \
|
||||
const uint8_t *: AVCLAN_sendbitsi, \
|
||||
uint8_t *: AVCLAN_sendbitsi)(bits, len)
|
||||
|
||||
#define AVCLAN_readbits(bits, len) \
|
||||
_Generic((bits), \
|
||||
const uint16_t *: AVCLAN_readbitsl, \
|
||||
uint16_t *: AVCLAN_readbitsl, \
|
||||
const uint8_t *: AVCLAN_readbitsi, \
|
||||
uint8_t *: AVCLAN_readbitsi)(bits, len)
|
||||
|
||||
#ifndef NDEBUG
|
||||
// Sample and dump bus bit timing over the serial link (REPL `M`).
|
||||
void AVCLan_Measure(void);
|
||||
@@ -75,3 +61,38 @@ void AVCLan_Measure(void);
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
template <class T, auto N> avclan_bit_t AVCLAN_sendbits(T bits);
|
||||
template <class T, auto N> avclan_bit_t AVCLAN_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 <= 8)
|
||||
avclan_bit_t AVCLAN_sendbits(uint8_t bits) {
|
||||
return AVCLAN_sendbitsi(&bits, N);
|
||||
}
|
||||
template <auto N>
|
||||
requires(N <= 16)
|
||||
avclan_bit_t AVCLAN_sendbits(uint16_t bits) {
|
||||
return AVCLAN_sendbitsl(&bits, N);
|
||||
}
|
||||
template <> inline avclan_bit_t AVCLAN_sendbits<8>(uint8_t byte) {
|
||||
return AVCLAN_sendbyte(&byte);
|
||||
}
|
||||
|
||||
template <auto N>
|
||||
requires(N <= 8)
|
||||
avclan_bit_t AVCLAN_readbits(uint8_t *bits) {
|
||||
return static_cast<avclan_bit_t>(AVCLAN_readbitsi(bits, N));
|
||||
}
|
||||
template <auto N>
|
||||
requires(N <= 16)
|
||||
avclan_bit_t AVCLAN_readbits(uint16_t *bits) {
|
||||
return static_cast<avclan_bit_t>(AVCLAN_readbitsl(bits, N));
|
||||
}
|
||||
template <> inline avclan_bit_t AVCLAN_readbits<8>(uint8_t *byte) {
|
||||
return static_cast<avclan_bit_t>(AVCLAN_readbyte(byte));
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,278 @@
|
||||
// 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 "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 <cstdint>
|
||||
|
||||
namespace avclan {
|
||||
|
||||
Peripheral::Error::Read Peripheral::read(AVCLAN_frame_t *in, log_t print) {
|
||||
struct errtype {
|
||||
Error::Read errno;
|
||||
union {
|
||||
uint8_t val; // BAD_LENGTH_RANGE: the out-of-range length value
|
||||
struct {
|
||||
uint8_t parity; // received (bad) parity bit
|
||||
uint16_t read_val;
|
||||
};
|
||||
};
|
||||
} err = {};
|
||||
|
||||
using enum Error::Read;
|
||||
|
||||
AVCLAN_stopEvent(); // quiesce contending sources during the read
|
||||
|
||||
bool shouldACK = false;
|
||||
uint8_t tmp = 0, parity = 0;
|
||||
|
||||
err.errno = Error::Read{AVCLAN_readstartbit()};
|
||||
if (static_cast<uint8_t>(err.errno))
|
||||
goto handle_err;
|
||||
|
||||
AVCLAN_readbits<1>(&tmp);
|
||||
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 (print.verbose) {
|
||||
err.read_val = in->data[i];
|
||||
err.parity = tmp;
|
||||
}
|
||||
goto handle_err;
|
||||
} else if (shouldACK) {
|
||||
AVCLAN_sendbit_ACK();
|
||||
} else {
|
||||
AVCLAN_readbits<1>(&tmp);
|
||||
}
|
||||
}
|
||||
|
||||
if (false) {
|
||||
handle_err:;
|
||||
AVCLAN_startEvent();
|
||||
RS232_Print("ERR(read): ");
|
||||
switch (err.errno) {
|
||||
case BAD_STARTBIT: RS232_Print("bad start bit (other)"); break;
|
||||
case STARTBIT_TOO_SHORT: RS232_Print("bad start bit (short)"); break;
|
||||
case STARTBIT_TOO_LONG: RS232_Print("bad start bit (long)"); break;
|
||||
case BAD_CONTROLLER_PARITY:
|
||||
RS232_Print("reading controller addr.");
|
||||
goto VERBOSE;
|
||||
case BAD_PERIPHERAL_PARITY:
|
||||
RS232_Print("reading peripheral addr.");
|
||||
goto VERBOSE;
|
||||
case BAD_CONTROL_PARITY: RS232_Print("reading control"); goto VERBOSE;
|
||||
case BAD_LENGTH_PARITY: RS232_Print("reading length"); goto VERBOSE;
|
||||
case BAD_LENGTH_RANGE:
|
||||
RS232_Print("bad length 0x");
|
||||
RS232_PrintHex4(err.val);
|
||||
break;
|
||||
case BAD_DATA_PARITY:
|
||||
RS232_Print("reading data");
|
||||
goto VERBOSE;
|
||||
VERBOSE:
|
||||
if (print.verbose) {
|
||||
RS232_Print("; read 0x");
|
||||
RS232_PrintHex(err.read_val);
|
||||
RS232_Print(" and got bad parity ");
|
||||
RS232_PrintHex4(err.parity);
|
||||
}
|
||||
}
|
||||
RS232_Print("\n");
|
||||
} else {
|
||||
AVCLAN_startEvent();
|
||||
}
|
||||
|
||||
// Only print if some data has been correctly received
|
||||
if (print.print && (err.errno < STARTBIT_TOO_SHORT)) {
|
||||
if (err.errno > BAD_DATA_PARITY)
|
||||
in->length = 0;
|
||||
AVCLAN_printframe(in, print.binary);
|
||||
}
|
||||
|
||||
return err.errno;
|
||||
}
|
||||
|
||||
Peripheral::Error::Send Peripheral::send(const AVCLAN_frame_t *out,
|
||||
log_t print) {
|
||||
struct errtype {
|
||||
// Error enum is ordered such that a lower numeric value corresponds to
|
||||
// more success
|
||||
Error::Send errno;
|
||||
uint8_t val;
|
||||
} err = {};
|
||||
|
||||
using enum Error::Send;
|
||||
|
||||
avclan_bit_t parity;
|
||||
|
||||
if (AVCLAN_ismuted()) {
|
||||
err.errno = MUTED;
|
||||
goto handle_err;
|
||||
}
|
||||
|
||||
AVCLAN_stopEvent();
|
||||
|
||||
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;
|
||||
goto handle_err;
|
||||
}
|
||||
// else
|
||||
// AVCLAN_sendbit_1();
|
||||
}
|
||||
|
||||
// back to read mode
|
||||
if (false) {
|
||||
handle_err:;
|
||||
AVCLAN_startEvent();
|
||||
RS232_Print("Error");
|
||||
switch (err.errno) {
|
||||
case MUTED: RS232_Print(": Device muted"); break;
|
||||
case BUSY: RS232_Print(": Busy bus"); break;
|
||||
case NAK_ADDRESS:
|
||||
case NAK_CONTROL:
|
||||
case NAK_MESSAGE_LENGTH:
|
||||
case NAK_DATA:
|
||||
RS232_Print(" NAK: ");
|
||||
switch (err.errno) {
|
||||
case NAK_ADDRESS: RS232_Print("address"); break;
|
||||
case NAK_CONTROL: RS232_Print("Control"); break;
|
||||
case NAK_MESSAGE_LENGTH: RS232_Print("Message length"); break;
|
||||
case NAK_DATA:
|
||||
RS232_Print(" data[");
|
||||
RS232_PrintDec(err.val);
|
||||
RS232_Print("]");
|
||||
break;
|
||||
case MUTED:
|
||||
case BUSY: __builtin_unreachable();
|
||||
}
|
||||
break;
|
||||
}
|
||||
RS232_Print("\n");
|
||||
} else {
|
||||
AVCLAN_startEvent();
|
||||
}
|
||||
|
||||
if (print.print)
|
||||
AVCLAN_printframe(out, print.binary);
|
||||
|
||||
return err.errno;
|
||||
}
|
||||
|
||||
} // namespace avclan
|
||||
@@ -0,0 +1,46 @@
|
||||
// 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
|
||||
#pragma once
|
||||
|
||||
#include "avclan_defs.h"
|
||||
#include <cstdint>
|
||||
|
||||
namespace avclan {
|
||||
class Peripheral {
|
||||
public:
|
||||
struct Error {
|
||||
// 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,
|
||||
};
|
||||
|
||||
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,
|
||||
};
|
||||
};
|
||||
|
||||
Peripheral(uint16_t address) : address{address} {}
|
||||
|
||||
Error::Read read(AVCLAN_frame_t *in, log_t print);
|
||||
Error::Send send(const AVCLAN_frame_t *out, log_t print);
|
||||
|
||||
private:
|
||||
const uint16_t address;
|
||||
};
|
||||
} // namespace avclan
|
||||
@@ -89,7 +89,7 @@ FetchContent_MakeAvailable(avr_libstdcpp)
|
||||
add_library(libstdcpp INTERFACE)
|
||||
target_include_directories(libstdcpp SYSTEM
|
||||
INTERFACE ${avr_libstdcpp_SOURCE_DIR}/include)
|
||||
target_link_libraries(avclan INTERFACE libstdcpp)
|
||||
target_link_libraries(avclan PUBLIC libstdcpp)
|
||||
endif()
|
||||
|
||||
|
||||
|
||||
+16
-13
@@ -10,6 +10,7 @@
|
||||
#include "avclandrv.h"
|
||||
#include "board.h"
|
||||
#include "com232.h"
|
||||
#include "peripheral.hpp"
|
||||
#include "queue.hpp"
|
||||
|
||||
const char *const offon[] = {"OFF", "ON"};
|
||||
@@ -59,7 +60,6 @@ int main() {
|
||||
uint8_t data_tmp[MAXMSGLEN + sizeof(AVCLAN_frame_t)];
|
||||
uint8_t seqIdx = 0; // current index in data_tmp
|
||||
|
||||
uint8_t err = 0;
|
||||
uint8_t failedStatusReports = 0;
|
||||
|
||||
// Temporary, direct access is questionable since cache has ownership
|
||||
@@ -69,16 +69,19 @@ int main() {
|
||||
|
||||
const AVCLAN_frame_t *lastStatus = nullptr;
|
||||
|
||||
avclan::Peripheral cd_changer(0x360);
|
||||
using Error = avclan::Peripheral::Error;
|
||||
|
||||
Setup();
|
||||
print_help();
|
||||
|
||||
while (true) {
|
||||
if (AVCLAN_busActive()) {
|
||||
if (auto msg = cache.pop()) {
|
||||
err = AVCLAN_readframe(msg.get(), (log_t){.print = printAllFrames,
|
||||
.binary = printBinary,
|
||||
.verbose = verbose});
|
||||
if (!err)
|
||||
auto err = cd_changer.read(msg.get(), (log_t){.print = printAllFrames,
|
||||
.binary = printBinary,
|
||||
.verbose = verbose});
|
||||
if (err == Error::Read{0x00})
|
||||
incoming.push(std::move(msg));
|
||||
} else {
|
||||
RS232_Print("!! Dropping an incoming message; cache is empty !!\n");
|
||||
@@ -108,18 +111,18 @@ int main() {
|
||||
}
|
||||
|
||||
if (auto out = outgoing.pop()) {
|
||||
err = AVCLAN_sendframe(
|
||||
auto err = cd_changer.send(
|
||||
out.get(), (log_t){.print = printAllFrames, .binary = printBinary});
|
||||
if (err || (reaction_t)out->reaction == r_SendOnly) {
|
||||
if (err && out.get() == lastStatus && ++failedStatusReports > 1) {
|
||||
failedStatusReports = 0;
|
||||
AVCLAN_stopPlaying(); // Disable periodic updates if e.g. no-one's
|
||||
// listening (car was turned off?)
|
||||
}
|
||||
} else {
|
||||
if (err == Error::Send{0x00} && (reaction_t)out->reaction > r_SendOnly) {
|
||||
AVCLAN_statemachine(out.get());
|
||||
if (out->reaction)
|
||||
outgoing.push(std::move(out));
|
||||
|
||||
} else if (err == Error::Send::NAK_ADDRESS && out.get() == lastStatus &&
|
||||
++failedStatusReports > 1) {
|
||||
failedStatusReports = 0;
|
||||
AVCLAN_stopPlaying(); // Disable periodic updates if e.g. no-one's
|
||||
// listening (car was turned off?)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user