diff --git a/CMakeLists.txt b/CMakeLists.txt index dfe256f..a0982fc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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) diff --git a/CMakePresets.json b/CMakePresets.json index 6d336a4..8fd28eb 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -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)", diff --git a/src/avclan/bus.cc b/src/avclan/bus.cc index d735f0a..12cfeb3 100644 --- a/src/avclan/bus.cc +++ b/src/avclan/bus.cc @@ -29,9 +29,10 @@ No acknowledge bits are sent for broadcast frames. */ -#include "bus.hpp" +#include + #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) diff --git a/src/avclan/frame.cc b/src/avclan/frame.cc index bca3a4b..f9d7526 100644 --- a/src/avclan/frame.cc +++ b/src/avclan/frame.cc @@ -4,9 +4,9 @@ // SPDX-License-Identifier: GPL-3.0-or-later #include +#include #include -#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(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(is_unicast)); + printf(" 0x%03X", static_cast(controller_addr & 0x0FFF)); + printf(" 0x%03X", static_cast(peripheral_addr & 0x0FFF)); + printf(" 0x%X", static_cast(control & 0x0F)); + printf(" 0x%X", static_cast(length & 0x0F)); for (uint8_t i = 0; i < length; i++) { - RS232_Print(" 0x"); - RS232_PrintHex8(data[i]); + printf(" 0x%02X", static_cast(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(err.val)); break; default: break; } - RS232_Print("\n"); } return err.errno; diff --git a/src/avclan/hal/stdio.h b/src/avclan/hal/stdio.h new file mode 100644 index 0000000..9743392 --- /dev/null +++ b/src/avclan/hal/stdio.h @@ -0,0 +1,20 @@ +// Copyright (C) 2026 Allen Hill +// 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 +// 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 diff --git a/src/avclan/target/avr-attiny3216/CMakeLists.txt b/src/avclan/target/avr-attiny3216/CMakeLists.txt index d7262b5..941ca7a 100644 --- a/src/avclan/target/avr-attiny3216/CMakeLists.txt +++ b/src/avclan/target/avr-attiny3216/CMakeLists.txt @@ -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 $<$: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 diff --git a/src/avclan/target/avr-attiny3216/phy_avr.c b/src/avclan/target/avr-attiny3216/phy_avr.c index 4caf396..4a1020a 100644 --- a/src/avclan/target/avr-attiny3216/phy_avr.c +++ b/src/avclan/target/avr-attiny3216/phy_avr.c @@ -7,17 +7,35 @@ #include #include #include +#include #include #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(); } diff --git a/src/avclan/target/avr-attiny3216/stdio_avr.c b/src/avclan/target/avr-attiny3216/stdio_avr.c new file mode 100644 index 0000000..bd9a5d2 --- /dev/null +++ b/src/avclan/target/avr-attiny3216/stdio_avr.c @@ -0,0 +1,47 @@ +// Copyright (C) 2026 Allen Hill +// SPDX-License-Identifier: GPL-3.0-or-later + +#include +#include +#include + +#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 +} diff --git a/vendor/jnk0le-AVR-UART-lib/LICENSE b/src/avclan/target/avr-attiny3216/vendor/jnk0le-AVR-UART-lib/LICENSE similarity index 100% rename from vendor/jnk0le-AVR-UART-lib/LICENSE rename to src/avclan/target/avr-attiny3216/vendor/jnk0le-AVR-UART-lib/LICENSE diff --git a/vendor/jnk0le-AVR-UART-lib/README.md b/src/avclan/target/avr-attiny3216/vendor/jnk0le-AVR-UART-lib/README.md similarity index 100% rename from vendor/jnk0le-AVR-UART-lib/README.md rename to src/avclan/target/avr-attiny3216/vendor/jnk0le-AVR-UART-lib/README.md diff --git a/vendor/jnk0le-AVR-UART-lib/usart.c b/src/avclan/target/avr-attiny3216/vendor/jnk0le-AVR-UART-lib/usart.c similarity index 99% rename from vendor/jnk0le-AVR-UART-lib/usart.c rename to src/avclan/target/avr-attiny3216/vendor/jnk0le-AVR-UART-lib/usart.c index 958d4c0..ab59981 100644 --- a/vendor/jnk0le-AVR-UART-lib/usart.c +++ b/src/avclan/target/avr-attiny3216/vendor/jnk0le-AVR-UART-lib/usart.c @@ -11,9 +11,6 @@ #include #include #include - -#include "../../src/timing.h" - #include #include "usart.h" diff --git a/vendor/jnk0le-AVR-UART-lib/usart.h b/src/avclan/target/avr-attiny3216/vendor/jnk0le-AVR-UART-lib/usart.h similarity index 98% rename from vendor/jnk0le-AVR-UART-lib/usart.h rename to src/avclan/target/avr-attiny3216/vendor/jnk0le-AVR-UART-lib/usart.h index f6beeff..42132ae 100644 --- a/vendor/jnk0le-AVR-UART-lib/usart.h +++ b/src/avclan/target/avr-attiny3216/vendor/jnk0le-AVR-UART-lib/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 diff --git a/vendor/jnk0le-AVR-UART-lib/usart_config.h b/src/avclan/target/avr-attiny3216/vendor/jnk0le-AVR-UART-lib/usart_config.h similarity index 99% rename from vendor/jnk0le-AVR-UART-lib/usart_config.h rename to src/avclan/target/avr-attiny3216/vendor/jnk0le-AVR-UART-lib/usart_config.h index 56c4ace..a09e170 100644 --- a/vendor/jnk0le-AVR-UART-lib/usart_config.h +++ b/src/avclan/target/avr-attiny3216/vendor/jnk0le-AVR-UART-lib/usart_config.h @@ -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_ */ \ No newline at end of file +#endif /* USART_CONFIG_H_ */ diff --git a/src/com232.c b/src/com232.c deleted file mode 100644 index f222b0f..0000000 --- a/src/com232.c +++ /dev/null @@ -1,148 +0,0 @@ -// copyright (C) 2006 Marcin Slonicki -// copyright (C) 2007 Louis Frigon -// Copyright (C) 2015 Allen Hill -// SPDX-License-Identifier: GPL-3.0-or-later - -#include -#include -#include -#include -#include - -#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); -} diff --git a/src/com232.h b/src/com232.h deleted file mode 100644 index ee1b3b6..0000000 --- a/src/com232.h +++ /dev/null @@ -1,36 +0,0 @@ -// copyright (C) 2006 Marcin Slonicki -// copyright (C) 2007 Louis Frigon -// Copyright (C) 2015 Allen Hill -// SPDX-License-Identifier: GPL-3.0-or-later - -#pragma once - -#include - -#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 diff --git a/src/sniffer.cc b/src/sniffer.cc index 1a143e2..40ee05e 100644 --- a/src/sniffer.cc +++ b/src/sniffer.cc @@ -5,12 +5,13 @@ #include #include +#include #include #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().mic_toggle(); break; case 'p': - RS232_Print("First play/pause begin ... "); + fputs("First play/pause begin ... ", stdout); peripheral.device().media_action(MediaAction::Play_Pause); while (peripheral.device().media_busy()) {} - RS232_Print("end\nSecond play/pause begin ... "); + fputs("end\nSecond play/pause begin ... ", stdout); peripheral.device().media_action(MediaAction::Play_Pause); while (peripheral.device().media_busy()) {} - RS232_Print("end\n"); + puts("end"); break; case 's': - RS232_Print("Skip begin ... "); + fputs("Skip begin ... ", stdout); peripheral.device().media_action(MediaAction::Track_Next); while (peripheral.device().media_busy()) {} - RS232_Print("end\n"); + puts("end"); break; case 'b': - RS232_Print("Skip back begin ... "); + fputs("Skip back begin ... ", stdout); peripheral.device().media_action(MediaAction::Track_Prev); while (peripheral.device().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(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