Complete the switch to jnk0le uart lib

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Allen Hill
2026-07-07 12:39:09 -07:00
parent 1d953adaf2
commit e50bde197d
16 changed files with 221 additions and 344 deletions
+2 -4
View File
@@ -66,11 +66,10 @@ add_library(avclan STATIC
)
# avclan exports its public generic headers (src/avclan) to consumers and
# reaches into src/ for sibling headers (com232.h, board.h) during its own
# reaches into src/ for sibling headers (stdio.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
${CMAKE_CURRENT_SOURCE_DIR}/src)
${CMAKE_CURRENT_SOURCE_DIR}/src/avclan)
# Pull in the selected hardware target: its port sources, per-target headers,
# hardware-specific compile options/definitions, device-pack handling, and the
@@ -79,7 +78,6 @@ add_subdirectory(src/avclan/target/${AVCLAN_TARGET})
add_executable(mockingboard
src/sniffer.cc
src/com232.c
)
target_link_libraries(mockingboard avclan)
+1 -21
View File
@@ -26,7 +26,7 @@
"AVCLAN_TARGET": "avr-attiny3216",
"FREQSEL": "20MHz",
"TCB_CLKSEL": "TCB_CLKSEL_CLKDIV1_gc",
"USART_RXMODE": "USART_RXMODE_CLK2X_gc"
"USART_RXMODE": "DOUBLE_SPEED"
}
},
{
@@ -48,26 +48,6 @@
"CMAKE_BUILD_TYPE": "RelWithDebInfo"
}
},
{
"name": "attiny3216-debug-usb0",
"displayName": "ATtiny3216 Debug (ttyUSB0)",
"description": "ATtiny3216 Debug build, program over /dev/ttyUSB0",
"inherits": [
"avr-attiny3216",
"debug-base",
"usb0"
]
},
{
"name": "attiny3216-debug-usb1",
"displayName": "ATtiny3216 Debug (ttyUSB1)",
"description": "ATtiny3216 Debug build, program over /dev/ttyUSB1",
"inherits": [
"avr-attiny3216",
"debug-base",
"usb1"
]
},
{
"name": "attiny3216-relwithdebinfo-usb0",
"displayName": "ATtiny3216 RelWithDebInfo (ttyUSB0)",
+24 -31
View File
@@ -29,9 +29,10 @@
No acknowledge bits are sent for broadcast frames.
*/
#include "bus.hpp"
#include <cstdio>
#include "avclan.h"
#include "com232.h"
#include "bus.hpp"
#include "frame.hpp"
#include "hal/phy.h" // bridge until phy has been ported
@@ -264,33 +265,29 @@ auto Bus::read(uint16_t address, Frame *in, Frame::Print print) -> Error::Read {
if (false) { // NOLINT(readability-simplify-boolean-expr)
handle_err:;
RS232_Print("ERR(read): ");
fputs("ERR(read): ", stdout);
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_STARTBIT: fputs("bad start bit (other)", stdout); break;
case STARTBIT_TOO_SHORT: fputs("bad start bit (short)", stdout); break;
case STARTBIT_TOO_LONG: fputs("bad start bit (long)", stdout); break;
case BAD_CONTROLLER_PARITY:
RS232_Print("reading controller addr.");
fputs("reading controller addr.", stdout);
goto VERBOSE;
case BAD_PERIPHERAL_PARITY:
RS232_Print("reading peripheral addr.");
fputs("reading peripheral addr.", stdout);
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;
case BAD_CONTROL_PARITY: fputs("reading control", stdout); goto VERBOSE;
case BAD_LENGTH_PARITY: fputs("reading length", stdout); goto VERBOSE;
case BAD_LENGTH_RANGE: printf("bad length 0x%X", err.val & 0x0F); break;
case BAD_DATA_PARITY: fputs("reading data", stdout); goto VERBOSE;
case BAD_PARITY:
__builtin_unreachable();
VERBOSE:
if (print.verbose) {
RS232_Print("; read 0x");
RS232_PrintHex(err.val);
printf("; read 0x%X", err.val);
}
}
RS232_Print("\n");
putchar('\n');
}
// Only print if some data has been correctly received
@@ -370,32 +367,28 @@ auto Bus::send(const Frame *out, Frame::Print print) -> Error::Send {
// back to read mode
if (false) { // NOLINT(readability-simplify-boolean-expr)
handle_err:;
RS232_Print("Error");
fputs("Error", stdout);
switch (err.errno) {
case MUTED: RS232_Print(": Device muted"); break;
case BUSY: RS232_Print(": Busy bus"); break;
case MUTED: fputs(": Device muted", stdout); break;
case BUSY: fputs(": Busy bus", stdout); break;
case NAK_ADDRESS:
case NAK_CONTROL:
case NAK_MESSAGE_LENGTH:
case NAK_DATA:
case NAK:
RS232_Print(" NAK: ");
fputs(" NAK: ", stdout);
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 NAK_ADDRESS: fputs("address", stdout); break;
case NAK_CONTROL: fputs("Control", stdout); break;
case NAK_MESSAGE_LENGTH: fputs("Message length", stdout); break;
case NAK_DATA: printf(" data[%u]", err.val); break;
case NAK:
case MUTED:
case BUSY: __builtin_unreachable();
}
break;
}
RS232_Print("\n");
putchar('\n');
}
if (print.print)
+16 -27
View File
@@ -4,9 +4,9 @@
// SPDX-License-Identifier: GPL-3.0-or-later
#include <cstdint>
#include <cstdio>
#include <cstring>
#include "com232.h" // error logging
#include "frame.hpp"
namespace {
@@ -30,33 +30,25 @@ void Frame::print(Frame::Print print) const {
*bptr++ = control;
*bptr++ = length;
RS232_sendbytes(buffer, 8);
RS232_sendbytes(data, length);
fwrite(buffer, 1, 8, stdout);
fwrite(data, 1, length, stdout);
bptr = buffer;
*bptr++ = 0x17; // End of transmission block
*bptr++ = 0x0D; // \r
*bptr++ = 0x0A; // \n
RS232_sendbytes(buffer, 3);
fwrite(buffer, 1, 3, stdout);
} else {
RS232_PrintHex4(static_cast<uint8_t>(is_unicast));
RS232_Print(" 0x");
RS232_PrintHex12(controller_addr);
RS232_Print(" 0x");
RS232_PrintHex12(peripheral_addr);
RS232_Print(" 0x");
RS232_PrintHex4(control);
RS232_Print(" 0x");
RS232_PrintHex4(length);
printf("%X", static_cast<unsigned>(is_unicast));
printf(" 0x%03X", static_cast<unsigned>(controller_addr & 0x0FFF));
printf(" 0x%03X", static_cast<unsigned>(peripheral_addr & 0x0FFF));
printf(" 0x%X", static_cast<unsigned>(control & 0x0F));
printf(" 0x%X", static_cast<unsigned>(length & 0x0F));
for (uint8_t i = 0; i < length; i++) {
RS232_Print(" 0x");
RS232_PrintHex8(data[i]);
printf(" 0x%02X", static_cast<unsigned>(data[i]));
}
RS232_Print("\n");
putchar('\n');
}
}
@@ -96,21 +88,18 @@ Error::Parse Frame::parse(const uint8_t *bytes, uint8_t len) {
if (false) { // NOLINT(readability-simplify-boolean-expr)
handle_err:;
RS232_Print("ERR(parse): ");
fputs("ERR(parse): ", stdout);
switch (err.errno) {
case TOO_SHORT:
RS232_Print("not enough bytes too fill AVCLAN frame");
break;
case TOO_SHORT: puts("not enough bytes too fill AVCLAN frame"); break;
case MISMATCH_LENGTH:
RS232_Print("frame->length is longer than remaining data");
puts("frame->length is longer than remaining data");
break;
case LENGTH_TOO_BIG:
RS232_Print("frame->length exceeds MAXLENGTH: 0x");
RS232_PrintHex8(err.val);
printf("frame->length exceeds MAXLENGTH: 0x%02X\n",
static_cast<unsigned>(err.val));
break;
default: break;
}
RS232_Print("\n");
}
return err.errno;
+20
View File
@@ -0,0 +1,20 @@
// Copyright (C) 2026 Allen Hill <allenofthehills@gmail.com>
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
// Generic stdio interface initialization. All user I/O goes through <stdio.h>
// functions. Assumptions/invariants:
// - stdin MUST be non-blocking (ie. getchar() returns EOF immediately when
// empty). Necessary to avoid stalling the REPL poll loop.
// - stdout is *raw* — no '\n' -> "\r\n" translation: bare LF and binary frame
// payloads (fwrite) are emitted verbatim
void stdio_init(void);
#ifdef __cplusplus
}
#endif
@@ -7,7 +7,9 @@ target_sources(avclan PRIVATE
phy_avr.c
media_avr.c
cd_timer_avr.c
board_avr.c)
board_avr.c
stdio_avr.c
)
target_include_directories(avclan PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
@@ -63,10 +65,10 @@ set_property(CACHE TCB_CLKSEL PROPERTY STRINGS
TCB_CLKSEL_CLKTCA_gc
)
set(USART_RXMODE "USART_RXMODE_CLK2X_gc" CACHE STRING "USART at normal or double speed operation")
set(USART_RXMODE "NORMAL" CACHE STRING "USART at normal or double speed operation")
set_property(CACHE USART_RXMODE PROPERTY STRINGS
USART_RXMODE_CLK2X_gc
USART_RXMODE_NORMAL_gc
NORMAL
DOUBLE_SPEED
)
# Measured wall-clock duration (ms) of one nominal 32768-tick RTC period, used
@@ -118,6 +120,20 @@ if(NOT LIBC_VERSION_TEST)
endif()
endif()
# --- Vendored serial driver (jnk0le AVR-UART-lib), adapted for avrxmega3 -----
# usart_config.h set to use the reserved-ISR-register optimization, so r2/r3/r4
# must be held off-limits in every TU that shares the MCU.
set(JNK0LE_UART_DIR ${CMAKE_CURRENT_SOURCE_DIR}/vendor/jnk0le-AVR-UART-lib)
add_library(jnk0le_uart STATIC ${JNK0LE_UART_DIR}/usart.c)
target_include_directories(jnk0le_uart SYSTEM PUBLIC ${JNK0LE_UART_DIR})
target_compile_definitions(jnk0le_uart
PUBLIC $<$<STREQUAL:${USART_RXMODE},DOUBLE_SPEED>:USE_DOUBLE_SPEED>
PRIVATE F_CPU=${FREQSEL}
)
target_compile_options(jnk0le_uart PUBLIC
-ffixed-r2 -ffixed-r3 -ffixed-r4) # reserved ISR scratch (SREG=r4, Z=r2:r3)
target_link_libraries(avclan PUBLIC jnk0le_uart)
# --- Compile definitions / options -----------------------------------------
target_compile_definitions(avclan PUBLIC
FREQSEL=${FREQSEL}
@@ -125,7 +141,6 @@ target_compile_definitions(avclan PUBLIC
CLK_PRESCALE_DIV=${CLK_PRESCALE_DIV}
__CLK_PRESCALE_DIV=__${CLK_PRESCALE_DIV}
TCB_CLKSEL=${TCB_CLKSEL}
USART_RXMODE=${USART_RXMODE}
RTC_STATUS_PERIOD_MS=${RTC_STATUS_PERIOD_MS}
)
target_compile_options(avclan PUBLIC
+29 -16
View File
@@ -7,17 +7,35 @@
#include <avr/io.h>
#include <avr/sfr_defs.h>
#include <stdint.h>
#include <stdio.h>
#include <util/atomic.h>
#include "hal/phy.h"
#include "com232.h" // RS232_setRxInterrupt (guard); RS232_Print (Measure)
#include "media_avr.h" // media_sync_during_mask (guard)
#include "media_avr.h" // media_sync_during_mask (guard)
#include "hal/cd_timer.h" // statustimer_enable/disable (guard)
// F_CPU + TICK_US (timing.h) defined here; F_CPU potentially needed by
// avr-libc.
#include "timing_avr.h"
// USART0 TX ring indices owned by the jnk0le lib; the guard consults them to
// decide whether to resume the TX drain (DRE interrupt) on leave.
extern volatile uint8_t tx0_Head, tx0_Tail;
// Mask/unmask the USART interrupts during bit-banged AVC-LAN framing. TX is
// interrupt-driven, so the DRE (data-register-empty) interrupt is gated
// alongside RX; on re-enable, resume the TX drain only if bytes are still
// queued (enabling DREIE on an empty ring would transmit garbage).
static void console_set_irqs(bool enable) {
if (enable) {
USART0.CTRLA |= USART_RXCIE_bm;
if (tx0_Head != tx0_Tail)
USART0.CTRLA |= USART_DREIE_bm;
} else {
USART0.CTRLA &= ~(USART_RXCIE_bm | USART_DREIE_bm);
}
}
// Name difference between avr-libc and Microchip pack
#if defined(EVSYS_ASYNCCH00_bm)
#define EVSYS_ASYNCCH0_0_bm EVSYS_ASYNCCH00_bm
@@ -405,7 +423,7 @@ bool phy_send_startbit() {
void phy_guard_enter() {
ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {
cdtimer_disable();
RS232_setRxInterrupt(false);
console_set_irqs(false);
media_sync_during_guard();
}
}
@@ -414,7 +432,7 @@ void phy_guard_enter() {
void phy_guard_leave() {
ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {
cdtimer_restore(); // Reenable status interrupt if currently playing
RS232_setRxInterrupt(true);
console_set_irqs(true);
}
}
@@ -431,9 +449,8 @@ void phy_measure() {
uint8_t tmp = 0;
RS232_Print(
"Timing config: F_CPU=" STR(F_CPU) ", TCB_CLKSEL=" STR(TCB_CLKSEL) "\n");
RS232_Print("Sampling bit (pulse-width and period) timing...\n");
puts("Timing config: F_CPU=" STR(F_CPU) ", TCB_CLKSEL=" STR(TCB_CLKSEL));
puts("Sampling bit (pulse-width and period) timing...");
for (uint8_t n = 0; n < 100; n++) {
while (pulse_count == tmp) {}
@@ -442,20 +459,16 @@ void phy_measure() {
tmp = pulse_count;
}
RS232_Print("Pulses:\n");
puts("Pulses:");
for (uint8_t i = 0; i < 100; i++) {
RS232_PrintHex8((uint8_t)(pulses[i] >> 8));
RS232_PrintHex8((uint8_t)pulses[i]);
RS232_Print("\n");
printf("%04X\n", pulses[i]);
}
RS232_Print("Periods:\n");
puts("Periods:");
for (uint8_t i = 0; i < 100; i++) {
RS232_PrintHex8((uint8_t)(periods[i] >> 8));
RS232_PrintHex8((uint8_t)periods[i]);
RS232_Print("\n");
printf("%04X\n", periods[i]);
}
RS232_Print("\nDone.\n");
puts("\nDone.");
phy_guard_leave();
}
@@ -0,0 +1,47 @@
// Copyright (C) 2026 Allen Hill <allenofthehills@gmail.com>
// SPDX-License-Identifier: GPL-3.0-or-later
#include <avr/io.h>
#include <stdint.h>
#include <stdio.h>
#include "hal/stdio.h"
#include "timing_avr.h" // IWYU pragma: export
#include "usart.h" // jnk0le AVR-UART-lib
// Raw stdout backend: emit the byte verbatim, no '\n' -> "\r\n" translation
// (unlike the lib's uart_putchar). Keeps binary frame payloads intact.
static int stdio_putchar(char data, FILE *stream) {
(void)stream;
uart0_putc(data);
return 0;
}
// Non-blocking stdin backend: next received byte, or _FDEV_EOF when the RX ring
// is empty (uart0_getData() returns a negative value when there is no data).
static int stdio_getchar(FILE *stream) {
(void)stream;
int16_t c = uart0_getData();
return c < 0 ? _FDEV_EOF : c;
}
// One raw RW stream for the stdio UART: verbatim byte output, non-blocking
// input.
static FILE stdio_stream =
FDEV_SETUP_STREAM(stdio_putchar, stdio_getchar, _FDEV_SETUP_RW);
void stdio_init(void) {
// The lib's uart0_init configures the USART registers/baud but not the pins;
// keep the ATtiny3216 pin-mux the old driver did.
PORTMUX.CTRLB = PORTMUX_USART0_ALTERNATE_gc; // TxD/RxD on PA1/PA2
PORTA.DIRSET = PIN1_bm; // TxD output
PORTA.DIRCLR = PIN2_bm; // RxD input
#ifdef USE_DOUBLE_SPEED
uart0_init(DOUBLE_BAUD_CALC(1200000));
#else
uart0_init(BAUD_CALC(1200000));
#endif
stdout = stdin = &stdio_stream; // printf/fputs/fwrite + non-blocking getchar
}
@@ -11,9 +11,6 @@
#include <stdio.h>
#include <stdlib.h>
#include <util/atomic.h>
#include "../../src/timing.h"
#include <util/delay.h>
#include "usart.h"
@@ -16,11 +16,14 @@
#endif
#if __AVR_ARCH__ == 103
// macro calculating precise UBRR value
#define BAUD_CALC(x) (uint16_t)(((F_CPU / x) * 4UL) + 0.5)
// avrxmega3 (attiny/mega-0) async BAUD = 64*F_CPU/(S*baud), S=16 normal / 8
// double. Compute rounded without truncating F_CPU/x first (the naive
// (F_CPU/x)*S loses precision for non-integer ratios, e.g. 20MHz/1.2Mbps).
#define BAUD_CALC(x) (uint16_t)((64UL * (F_CPU) + 8UL * (x)) / (16UL * (x)))
#define BAUD_CALC_FAST(x) BAUD_CALC(x)
// macro calculating UBRR value for double speed
#define DOUBLE_BAUD_CALC(x) (uint16_t)(((F_CPU / x) * 8UL) + 0.5)
// macro calculating BAUD value for double speed
#define DOUBLE_BAUD_CALC(x) \
(uint16_t)((64UL * (F_CPU) + 4UL * (x)) / (8UL * (x)))
#else
// macro calculating precise UBRR value
#define BAUD_CALC(x) ((F_CPU + (x) * 8UL) / (16UL * (x)) - 1UL)
@@ -42,6 +45,16 @@
#define USART_NO_ABI_BREAKING_PREMATURES
#endif
// avrxmega3 (tinyAVR-1/megaAVR-0, __AVR_ARCH__==103) is an AVRxt core where RETI
// does NOT restore SREG.I outside an ISR. The "ABI-breaking premature" trick
// re-enables interrupts by ending non-ISR helpers (e.g. uart0_putc) with `reti`
// after a `cli`; on AVRxt that leaves interrupts disabled forever, so the TX
// (UDRE) interrupt never fires. Force the safe ATOMIC_BLOCK (cli + SREG-restore)
// path on this architecture.
#if __AVR_ARCH__ == 103
#define USART_NO_ABI_BREAKING_PREMATURES
#endif
#ifndef RX_BUFFER_SIZE
#define RX_BUFFER_SIZE 32 // Size of the ring buffers, must be power of 2
#endif
@@ -1118,7 +1131,7 @@ register uint16_t USART_Z_SAVE_REG_NAME asm(
#define UCSR0C_REGISTER USART0_CTRLC
#define TXCIE0_BIT USART_TXCIE_bp // CTRLA
#define UDRIE0_BIT USART_DREIE_bp // CTRLA
#define RXCIE0_BIT USART_RXSIE_bp // CTRLA
#define RXCIE0_BIT USART_RXCIE_bp // CTRLA
#define TXEN0_BIT USART_TXEN_bp // CTRLB
#define RXEN0_BIT USART_RXEN_bp // CTRLB
#define UDRE0_BIT USART_DREIF_bp // STATUS
@@ -1148,7 +1161,7 @@ register uint16_t USART_Z_SAVE_REG_NAME asm(
#define UCSR1C_REGISTER USART1_CTRLC
#define TXCIE1_BIT USART_TXCIE_bp // CTRLA
#define UDRIE1_BIT USART_DREIE_bp // CTRLA
#define RXCIE1_BIT USART_RXSIE_bp // CTRLA
#define RXCIE1_BIT USART_RXCIE_bp // CTRLA
#define TXEN1_BIT USART_TXEN_bp // CTRLB
#define RXEN1_BIT USART_RXEN_bp // CTRLB
#define UDRE1_BIT USART_DREIF_bp // STATUS
@@ -21,7 +21,7 @@
// #define USART_MPCM_MODE
// enables double speed for all available USART interfaces
#define USE_DOUBLE_SPEED
// #define USE_DOUBLE_SPEED
// echoes back received characters in getchar() function (for reading in
// scanf())
@@ -428,4 +428,4 @@ inline void TXC2_interrupt_event(void) {}
inline void TXC3_interrupt_event(void) __attribute__((always_inline));
inline void TXC3_interrupt_event(void) {}
#endif /* USART_CONFIG_H_ */
#endif /* USART_CONFIG_H_ */
-148
View File
@@ -1,148 +0,0 @@
// 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 <avr/interrupt.h>
#include <avr/io.h>
#include <avr/sfr_defs.h>
#include <stdint.h>
#include <util/atomic.h>
#include "com232.h"
#include "timing_avr.h" // F_CPU (baud-rate calc)
#if USART_RXMODE == USART_RXMODE_CLK2X_gc
#define RXMODE_S 8
#elif USART_RXMODE == USART_RXMODE_NORMAL_gc
#define RXMODE_S 16
#endif
#define USART_BAUD_RATE(BAUD_RATE) \
(uint16_t)((float)(F_CPU * 64 / (RXMODE_S * (float)BAUD_RATE)) + 0.5)
// RX ring, owned entirely by this driver (filled by the ISR, drained by
// RS232_getChar). Kept internal so the app never touches UART buffer state.
static volatile uint8_t RS232_RxCharBuffer[25], RS232_RxCharBegin,
RS232_RxCharEnd;
void RS232_Init(void) {
RS232_RxCharBegin = RS232_RxCharEnd = 0;
PORTMUX.CTRLB = PORTMUX_USART0_ALTERNATE_gc; // Use PA1/PA2 for TxD/RxD
PORTA.DIRSET = PIN1_bm;
PORTA.DIRCLR = PIN2_bm;
USART0.CTRLA = USART_RXCIE_bm; // Enable receive interrupts
USART0.CTRLB = USART_RXEN_bm | USART_TXEN_bm | // Enable Rx/Tx and set receive
USART_RXMODE; // mode
USART0.CTRLC = USART_CMODE_ASYNCHRONOUS_gc | USART_PMODE_DISABLED_gc |
USART_CHSIZE_8BIT_gc |
USART_SBMODE_1BIT_gc; // Async UART with 8N1 config
USART0.BAUD = USART_BAUD_RATE(1200000);
}
ISR(USART0_RXC_vect) {
// Store received character to the End of Buffer
RS232_RxCharBuffer[RS232_RxCharEnd++] = USART0_RXDATAL;
}
// Enable/disable the RX-complete interrupt (used by the bus-transaction guard
// to keep serial RX from disturbing bit-banged framing).
void RS232_setRxInterrupt(bool enable) {
if (enable)
USART0.CTRLA |= USART_RXCIE_bm;
else
USART0.CTRLA &= ~USART_RXCIE_bm;
}
// True if at least one received byte is waiting.
bool RS232_hasChar(void) { return RS232_RxCharEnd != 0; }
// Atomically dequeue the next received byte. Only call when RS232_hasChar().
char RS232_getChar(void) {
char c;
ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {
c = (char)RS232_RxCharBuffer[RS232_RxCharBegin++];
if (RS232_RxCharBegin == RS232_RxCharEnd) // buffer consumed
RS232_RxCharBegin = RS232_RxCharEnd = 0;
}
return c;
}
void RS232_SendByte(uint8_t Data) {
loop_until_bit_is_set(USART0_STATUS,
USART_DREIF_bp); // wait for UART to become available
USART0_TXDATAL = Data; // send character
}
void RS232_sendbytes(const uint8_t *bytes, uint8_t len) {
for (uint8_t i = 0; i < len; i++) {
RS232_SendByte(bytes[i]);
}
}
void RS232_Print(const char *pBuf) {
register uint8_t c;
while ((c = *pBuf++)) {
if (c == '\n')
RS232_SendByte('\r');
RS232_SendByte(c);
}
}
void RS232_PrintHex4(uint8_t Data) {
uint8_t Character = Data & 0x0f;
Character += '0';
if (Character > '9')
Character += 'A' - '0' - 10;
RS232_SendByte(Character);
}
void RS232_PrintHex8(uint8_t Data) {
RS232_PrintHex4(Data >> 4);
RS232_PrintHex4(Data);
}
void RS232_PrintHex12(uint16_t x) {
RS232_PrintHex4((uint8_t)(x >> 8));
RS232_PrintHex8((uint8_t)x);
}
void RS232_PrintHex(uint16_t x) {
if (x > 0x0fff) {
RS232_PrintHex8((uint8_t)(x >> 8));
} else if (x > 0xff) {
RS232_PrintHex4((uint8_t)(x >> 8));
}
if (x > 0x0f) {
RS232_PrintHex8((uint8_t)x);
} else {
RS232_PrintHex4((uint8_t)x);
}
}
void RS232_PrintDec(uint8_t Data) {
if (Data > 99) {
RS232_SendByte('*');
return;
}
if (Data < 10) {
RS232_SendByte('0' + Data);
return;
}
uint8_t c;
unsigned short v, v1;
v = Data;
v1 = v / 10;
c = '0' + (v - v1 * 10);
RS232_SendByte('0' + v1);
RS232_SendByte(c);
}
void RS232_PrintDec2(uint8_t Data) {
if (Data < 10)
RS232_SendByte('0');
RS232_PrintDec(Data);
}
-36
View File
@@ -1,36 +0,0 @@
// 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 <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
void RS232_Init(void);
// Receive path. The RX ring is private to the driver; the app polls hasChar()
// and drains with getChar(). setRxInterrupt() masks/unmasks RX completion (used
// by the bus-transaction guard).
void RS232_setRxInterrupt(bool enable);
bool RS232_hasChar(void);
char RS232_getChar(void);
void RS232_Print_P(const char *str_addr);
void RS232_SendByte(uint8_t Data);
void RS232_sendbytes(const uint8_t *bytes, uint8_t len);
void RS232_Print(const char *pBuf);
void RS232_PrintHex4(uint8_t Data);
void RS232_PrintHex8(uint8_t Data);
void RS232_PrintHex12(uint16_t x);
void RS232_PrintHex(uint16_t x);
void RS232_PrintDec(uint8_t Data);
void RS232_PrintDec2(uint8_t Data);
#ifdef __cplusplus
}
#endif
+41 -45
View File
@@ -5,12 +5,13 @@
#include <cctype>
#include <cstdint>
#include <cstdio>
#include <cstring>
#include "cdchanger.hpp"
#include "com232.h"
#include "frame.hpp"
#include "hal/board.h"
#include "hal/stdio.h"
#include "peripheral.hpp"
#include "queue.hpp"
@@ -29,16 +30,12 @@ constinit Queue outgoing = cache;
void toggle_flag(bool *flag, const char *msg) {
*flag = !*flag;
RS232_Print(msg);
RS232_Print(offon[*flag]);
RS232_Print("\n");
printf("%s%s\n", msg, offon[*flag]);
}
void set_flag(bool *flag, bool val, const char *msg) {
*flag = val;
RS232_Print(msg);
RS232_Print(offon[val]);
RS232_Print("\n");
printf("%s%s\n", msg, offon[val]);
}
void Setup();
@@ -84,7 +81,7 @@ int main() {
if (err == Error::Read{0x00})
incoming.push(std::move(msg));
} else {
RS232_Print("!! Dropping an incoming message; cache is empty !!\n");
puts("!! Dropping an incoming message; cache is empty !!");
}
}
@@ -96,7 +93,7 @@ int main() {
if (out->reaction > 0)
outgoing.push(std::move(out));
} else {
RS232_Print("!! Unable to respond; cache is empty !!\n");
puts("!! Unable to respond; cache is empty !!");
}
}
@@ -117,9 +114,8 @@ int main() {
outgoing.push(std::move(out));
}
// Key handler
if (RS232_hasChar()) {
char readkey = RS232_getChar();
// stdin must be non-blocking: yielding EOF when idle/empty
if (int readkey = getchar(); readkey != EOF) {
switch (readkey) {
case '?': print_help(); break;
case 'v': toggle_flag(&verbose, "Verbose errors: "); break;
@@ -149,7 +145,7 @@ int main() {
out->reaction = 1;
outgoing.push(std::move(out));
} else
RS232_Print("!! Cache empty; unable to queue beep request");
puts("!! Cache empty; unable to queue beep request");
break;
case 'P':
if (auto out = cache.pop()) {
@@ -173,25 +169,25 @@ int main() {
#ifndef NDEBUG
case 'g': peripheral.device<CDChanger>().mic_toggle(); break;
case 'p':
RS232_Print("First play/pause begin ... ");
fputs("First play/pause begin ... ", stdout);
peripheral.device<CDChanger>().media_action(MediaAction::Play_Pause);
while (peripheral.device<CDChanger>().media_busy()) {}
RS232_Print("end\nSecond play/pause begin ... ");
fputs("end\nSecond play/pause begin ... ", stdout);
peripheral.device<CDChanger>().media_action(MediaAction::Play_Pause);
while (peripheral.device<CDChanger>().media_busy()) {}
RS232_Print("end\n");
puts("end");
break;
case 's':
RS232_Print("Skip begin ... ");
fputs("Skip begin ... ", stdout);
peripheral.device<CDChanger>().media_action(MediaAction::Track_Next);
while (peripheral.device<CDChanger>().media_busy()) {}
RS232_Print("end\n");
puts("end");
break;
case 'b':
RS232_Print("Skip back begin ... ");
fputs("Skip back begin ... ", stdout);
peripheral.device<CDChanger>().media_action(MediaAction::Track_Prev);
while (peripheral.device<CDChanger>().media_busy()) {}
RS232_Print("end\n");
puts("end");
break;
case 'M': peripheral.get_bus().measure(); break;
#endif
@@ -205,7 +201,7 @@ int main() {
goto DEFAULT; // reading binary and this is a real data byte
case 'U': // Send command
RS232_Print("READ SEQUENCE (U)> \n");
puts("READ SEQUENCE (U)> ");
lastPrintAllFrames = printAllFrames;
printAllFrames = false;
readSeq = true;
@@ -214,7 +210,7 @@ int main() {
seqIsUnicast = true;
break;
case 'B': // Send broadcast
RS232_Print("READ SEQUENCE (B)> \n");
puts("READ SEQUENCE (B)> ");
lastPrintAllFrames = printAllFrames;
printAllFrames = false;
readSeq = true;
@@ -271,17 +267,17 @@ int main() {
hexDigit = hexChars[0] = hexChars[1] = 0;
}
if (echoCharacters) {
RS232_Print("CURRENT SEQUENCE > ");
fputs("CURRENT SEQUENCE > ", stdout);
for (uint8_t i = 0; i < seqIdx; i++) {
RS232_PrintHex8(data_tmp[i]);
RS232_SendByte(' ');
printf("%02X", static_cast<unsigned>(data_tmp[i]));
putchar(' ');
}
RS232_Print("\n");
putchar('\n');
}
}
}
} // switch (readkey)
} // if (RS232_hasChar())
} // if (readkey != EOF)
}
return 0;
}
@@ -289,30 +285,30 @@ int main() {
namespace {
void Setup() {
board_init(); // clock + GPIO bring-up (target-specific)
RS232_Init();
stdio_init();
board_enable_interrupts();
}
void print_help() {
RS232_Print("AVCLAN Mockingboard v1\n");
RS232_Print("U - begin reading for unicast message\n"
"B - begin reading for broadcast message\n"
"m - Toggle mute for mockingboard bus activity\n"
"v - Toggle verbose error logging\n"
"l - Toggle message logging\n"
"X/x - Turn binary logging ON or OFF, respectively\n"
"k - Toggle character echo\n"
"E - Beep\n"
"P - Play\n"
puts("AVCLAN Mockingboard v1");
puts("U - begin reading for unicast message\n"
"B - begin reading for broadcast message\n"
"m - Toggle mute for mockingboard bus activity\n"
"v - Toggle verbose error logging\n"
"l - Toggle message logging\n"
"X/x - Turn binary logging ON or OFF, respectively\n"
"k - Toggle character echo\n"
"E - Beep\n"
"P - Play\n"
#ifndef NDEBUG
"g - Toggle MIC_CONTROL high/low\n"
"p - double MIC play/pause pulse\n" // Confirm pulse function and
// refractory timing
"s - MIC skip forward\n"
"b - MIC skip backward\n"
"M - Measure bit-timing (pulse-widths and periods)\n"
"g - Toggle MIC_CONTROL high/low\n"
"p - double MIC play/pause pulse\n" // Confirm pulse function and
// refractory timing
"s - MIC skip forward\n"
"b - MIC skip backward\n"
"M - Measure bit-timing (pulse-widths and periods)\n"
#endif
"? - Print this message\n");
"? - Print this message");
}
} // namespace