mirror of
https://github.com/halleysfifthinc/AVCLAN-Mockingboard.git
synced 2026-08-06 17:03:17 +00:00
Complete the switch to jnk0le uart lib
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
+2
-4
@@ -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
@@ -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
@@ -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
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
-3
@@ -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"
|
||||
+19
-6
@@ -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
|
||||
+2
-2
@@ -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
@@ -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);
|
||||
}
|
||||
@@ -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
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user