mirror of
https://github.com/halleysfifthinc/AVCLAN-Mockingboard.git
synced 2026-08-29 08:29:23 +00:00
Finalize C <=> C++ interface
This commit is contained in:
@@ -6,7 +6,7 @@ include(FetchContent)
|
||||
target_sources(avclan PRIVATE
|
||||
phy_avr.c
|
||||
media_avr.c
|
||||
statustick_avr.c
|
||||
cd_timer_avr.c
|
||||
board_avr.c)
|
||||
|
||||
target_include_directories(avclan PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
#include <avr/io.h>
|
||||
#include <avr/xmega.h> // _PROTECTED_WRITE
|
||||
|
||||
#include "board.h"
|
||||
#include "hal/board.h"
|
||||
|
||||
void board_init(void) {
|
||||
// Main clock prescale (CLK_PRESCALE / CLK_PRESCALE_DIV come from the build).
|
||||
@@ -46,4 +46,4 @@ void board_init(void) {
|
||||
PORTC.PIN1CTRL = PORT_ISC_INPUT_DISABLE_gc; // WOD
|
||||
}
|
||||
|
||||
void board_interruptsEnable(void) { sei(); }
|
||||
void board_enable_interrupts(void) { sei(); }
|
||||
|
||||
+8
-8
@@ -8,7 +8,7 @@
|
||||
#include <stdint.h>
|
||||
#include <util/atomic.h>
|
||||
|
||||
#include "statustimer.h"
|
||||
#include "hal/cd_timer.h"
|
||||
|
||||
// Measured wall-clock duration (in ms) of one nominal 32768-tick RTC period,
|
||||
// used to calibrate out the internal OSCULP32K's error. The RTC runs from
|
||||
@@ -29,7 +29,7 @@ static void* changer = nullptr;
|
||||
static void (*increment)(void *) = nullptr;
|
||||
static bool (*isplaying)(void *) = nullptr;
|
||||
|
||||
void statustimer_init(void *ptr, void (inc)(void *), bool (isplay)(void *)) {
|
||||
void cdtimer_init(void *ptr, void (inc)(void *), bool (isplay)(void *)) {
|
||||
// Setup RTC as a ~1 sec periodic timer via the normal counter's overflow.
|
||||
// Use the RTC directly (not PIT) to tune the status report interval closer to
|
||||
// 1 sec (internal osc may be slightly off)
|
||||
@@ -46,7 +46,7 @@ void statustimer_init(void *ptr, void (inc)(void *), bool (isplay)(void *)) {
|
||||
isplaying = isplay;
|
||||
}
|
||||
|
||||
void statustimer_reset() {
|
||||
void cdtimer_reset() {
|
||||
ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {
|
||||
loop_until_bit_is_clear(RTC_STATUS, RTC_CNTBUSY_bp);
|
||||
RTC.CNT = 0;
|
||||
@@ -55,18 +55,18 @@ void statustimer_reset() {
|
||||
}
|
||||
}
|
||||
|
||||
void statustimer_restore() {
|
||||
void cdtimer_restore() {
|
||||
if (isplaying(changer))
|
||||
RTC.INTCTRL |= RTC_OVF_bm;
|
||||
}
|
||||
void statustimer_disable() { RTC.INTCTRL &= ~RTC_OVF_bm; }
|
||||
void cdtimer_disable() { RTC.INTCTRL &= ~RTC_OVF_bm; }
|
||||
|
||||
// Set once per overflow; consumed by the app via statustimer_tickPending().
|
||||
volatile bool tick_pending = false;
|
||||
// Set once per overflow; consumed by the app via cdtimer_pending().
|
||||
volatile bool cdtimer_pending_flag = false;
|
||||
|
||||
// Periodic interrupt with a ~1 sec period; only enabled while playing.
|
||||
ISR(RTC_CNT_vect) {
|
||||
increment(changer);
|
||||
tick_pending = true;
|
||||
cdtimer_pending_flag = true;
|
||||
RTC.INTFLAGS = RTC_OVF_bm;
|
||||
}
|
||||
@@ -6,8 +6,8 @@
|
||||
#include <stdint.h>
|
||||
#include <util/atomic.h>
|
||||
|
||||
#include "media_avr.h" // mediacontrol_syncDuringMask (used by the bus guard)
|
||||
#include "mediacontrol.h"
|
||||
#include "media_avr.h" // media_sync_during_mask (used by the bus guard)
|
||||
#include "hal/media.h"
|
||||
|
||||
// F_CPU defined in timing_avr.h; the mic tick constants below are derived from
|
||||
// it (this hardware generation's TCA0/PB1 button-press implementation).
|
||||
@@ -31,14 +31,14 @@ static constexpr uint16_t close_thresh =
|
||||
|
||||
#ifndef NDEBUG
|
||||
// Toggle PB1 and return its new level.
|
||||
bool AVCLAN_micToggle() {
|
||||
bool media_mic_toggle() {
|
||||
// Take manual control of PB1 (CMP1EN gives TCA0 control of WO1/PB1 level)
|
||||
TCA0.SINGLE.CTRLB &= ~TCA_SINGLE_CMP1EN_bm;
|
||||
VPORTB.OUT ^= PIN1_bm;
|
||||
return (VPORTB.OUT & PIN1_bm) != 0;
|
||||
}
|
||||
|
||||
bool AVCLAN_isMediaFunctioning() { return mic_ntoggles != 0; }
|
||||
bool media_busy() { return mic_ntoggles != 0; }
|
||||
#endif
|
||||
|
||||
// Begin a press waveform of `nphases` × 100 ms level segments.
|
||||
@@ -99,11 +99,21 @@ ISR(TCA0_OVF_vect) { mic_timer_isr_body(false); }
|
||||
|
||||
// Emulate a transport-control button press on the source device. Each action
|
||||
// maps to a press-train of a given length on MIC_CONTROL.
|
||||
void AVCLAN_mediaFunction(AVCLAN_media_fn_t fn) {
|
||||
switch (fn) {
|
||||
case MEDIA_PLAY_PAUSE: mic_pulse(1); break; // single press
|
||||
case MEDIA_SKIP_FORWARD: mic_pulse(3); break; // double-press
|
||||
case MEDIA_SKIP_BACKWARD: mic_pulse(5); break; // triple-press
|
||||
void media_action(enum MediaAction action) {
|
||||
switch (action) {
|
||||
case Play:
|
||||
case Pause:
|
||||
case Play_Pause: mic_pulse(1); break; // single press
|
||||
case Skip_Forward:
|
||||
case Track_Next: mic_pulse(3); break; // double-press
|
||||
case Skip_Backward:
|
||||
case Track_Prev: mic_pulse(5); break; // triple-press
|
||||
case Repeat:
|
||||
case Repeat_Single:
|
||||
case Shuffle:
|
||||
case Volume_Up:
|
||||
case Volume_Down:
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -113,13 +123,13 @@ void AVCLAN_mediaFunction(AVCLAN_media_fn_t fn) {
|
||||
// - peripheral WO1 toggles and mic_ntoggles kept in sync
|
||||
// - maximum frame duration is ~15ms, "early" OVF remains within acceptable
|
||||
// ranges for either high/low pulses
|
||||
// Caller (AVCLAN_stopEvent) guarantees interrupts are disabled.
|
||||
void mediacontrol_syncDuringMask() {
|
||||
// Caller (phy_guard_enter) guarantees interrupts are disabled.
|
||||
void media_sync_during_guard() {
|
||||
if (mic_ntoggles && TCA0.SINGLE.CNT >= (TCA0.SINGLE.CMP0 - close_thresh))
|
||||
mic_timer_isr_body(true);
|
||||
}
|
||||
|
||||
void mediacontrol_init() {
|
||||
void media_init() {
|
||||
// PB1 needs to be set as an output for TCA0 to set the level
|
||||
PORTB.DIRSET = PIN1_bm;
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// AVR-internal media-driver hooks, shared between media_avr.c and the bus
|
||||
// transaction guard (phy_avr.c's AVCLAN_stopEvent). Not part of the public
|
||||
// transaction guard (phy_avr.c's phy_guard_enter). Not part of the public
|
||||
// mediacontrol.h interface.
|
||||
|
||||
#pragma once
|
||||
@@ -13,9 +13,9 @@ extern "C" {
|
||||
|
||||
// Keep the TCA0 press waveform roughly in sync while a bus transaction has
|
||||
// masked interrupts (runs the OVF ISR body early if an overflow is imminent).
|
||||
// MUST be called with interrupts disabled (from within AVCLAN_stopEvent's
|
||||
// MUST be called with interrupts disabled (from within phy_guard_enter's
|
||||
// ATOMIC_BLOCK).
|
||||
void mediacontrol_syncDuringMask();
|
||||
void media_sync_during_guard();
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
||||
@@ -9,10 +9,10 @@
|
||||
#include <stdint.h>
|
||||
#include <util/atomic.h>
|
||||
|
||||
#include "avclan_phy.h"
|
||||
#include "hal/phy.h"
|
||||
#include "com232.h" // RS232_setRxInterrupt (guard); RS232_Print (Measure)
|
||||
#include "media_avr.h" // mediacontrol_syncDuringMask (guard)
|
||||
#include "statustimer.h" // statustimer_enable/disable (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.
|
||||
@@ -58,15 +58,15 @@ static inline void AVCLAN_setBusDriven() {
|
||||
|
||||
// Returns true if device TX is muted on the AVCLAN bus (both drive pins are
|
||||
// configured as inputs).
|
||||
bool AVCLAN_ismuted() {
|
||||
bool phy_is_muted() {
|
||||
return (((VPORTA_DIR & PIN4_bm) | (VPORTA_DIR & PIN0_bm)) == 0);
|
||||
}
|
||||
|
||||
// True when the bus is being driven (i.e. not idle/floating).
|
||||
bool AVCLAN_busActive() { return !BUS_IS_IDLE; }
|
||||
bool phy_active() { return !BUS_IS_IDLE; }
|
||||
|
||||
// Mute device TX on AVCLAN bus
|
||||
void AVCLAN_muteDevice(bool mute) {
|
||||
void phy_mute(bool mute) {
|
||||
if (mute) {
|
||||
// clang-format off
|
||||
__asm__ __volatile__("cbi %[vporta_dir], 4; \n\t" // set as INPUT (output values ignored)
|
||||
@@ -99,7 +99,7 @@ static void set_AVC_logic_for(uint8_t val, uint16_t period) {
|
||||
return;
|
||||
}
|
||||
|
||||
void AVCLAN_sendbit(Bit bit) {
|
||||
void phy_send_bit(Bit bit) {
|
||||
uint16_t zero_length, one_length;
|
||||
switch (bit) {
|
||||
case bit_zero:
|
||||
@@ -120,7 +120,7 @@ void AVCLAN_sendbit(Bit bit) {
|
||||
set_AVC_logic_for(1, one_length);
|
||||
}
|
||||
|
||||
void AVCLAN_sendbit_ACK() {
|
||||
void phy_send_ack() {
|
||||
TCB1.CNT = 0;
|
||||
|
||||
// Wait for controller to begin ACK bit
|
||||
@@ -131,7 +131,7 @@ void AVCLAN_sendbit_ACK() {
|
||||
return;
|
||||
}
|
||||
|
||||
AVCLAN_sendbit(bit_zero);
|
||||
phy_send_bit(bit_zero);
|
||||
}
|
||||
|
||||
/* Returns true if the peripheral sent an ACK bit.
|
||||
@@ -139,7 +139,7 @@ void AVCLAN_sendbit_ACK() {
|
||||
sync period, and allows the receiver to drive the bus (or not) to finish a "1"
|
||||
bit.
|
||||
*/
|
||||
uint8_t AVCLAN_readbit_ACK() {
|
||||
uint8_t phy_read_ack() {
|
||||
TCB1.CNT = 0; // Double reset of TCB1.CNT: here
|
||||
set_AVC_logic_for(0, AVCLAN_BIT1_LOGIC_0); // And here (within)
|
||||
AVCLAN_setBusIdle(); // Stop driving bus
|
||||
@@ -160,7 +160,7 @@ uint8_t AVCLAN_readbit_ACK() {
|
||||
}
|
||||
|
||||
// Send `len` bits on the AVCLAN bus; returns the even parity
|
||||
Bit AVCLAN_sendbitsi(const uint8_t *bits, int8_t len) {
|
||||
Bit phy_send_bits_u8(const uint8_t *bits, int8_t len) {
|
||||
uint8_t b = *bits;
|
||||
uint8_t parity = 0;
|
||||
int8_t len_mod8 = 8;
|
||||
@@ -175,7 +175,7 @@ Bit AVCLAN_sendbitsi(const uint8_t *bits, int8_t len) {
|
||||
for (; len_mod8 > 0; len_mod8--) {
|
||||
Bit bit = (b & 0x80) != 0;
|
||||
parity += (uint8_t)bit;
|
||||
AVCLAN_sendbit(bit);
|
||||
phy_send_bit(bit);
|
||||
b <<= 1;
|
||||
}
|
||||
len_mod8 = 8;
|
||||
@@ -185,18 +185,18 @@ Bit AVCLAN_sendbitsi(const uint8_t *bits, int8_t len) {
|
||||
}
|
||||
|
||||
// Send `len` bits on the AVCLAN bus; returns the even parity
|
||||
Bit AVCLAN_sendbitsl(const uint16_t *bits, int8_t len) {
|
||||
return AVCLAN_sendbitsi((const uint8_t *)bits + 1, len);
|
||||
Bit phy_send_bits_u16(const uint16_t *bits, int8_t len) {
|
||||
return phy_send_bits_u8((const uint8_t *)bits + 1, len);
|
||||
}
|
||||
|
||||
Bit AVCLAN_sendbyte(const uint8_t *byte) {
|
||||
Bit phy_send_byte(const uint8_t *byte) {
|
||||
uint8_t b = *byte;
|
||||
uint8_t parity = 0;
|
||||
|
||||
for (uint8_t nbits = 8; nbits > 0; nbits--) {
|
||||
Bit bit = (b & 0x80) != 0;
|
||||
parity += (uint8_t)bit;
|
||||
AVCLAN_sendbit(bit);
|
||||
phy_send_bit(bit);
|
||||
b <<= 1;
|
||||
}
|
||||
return (parity & 1);
|
||||
@@ -225,7 +225,7 @@ ISR(TCB0_INT_vect) {
|
||||
}
|
||||
|
||||
// Read `len` bits on the AVCLAN bus; returns the even parity
|
||||
uint8_t AVCLAN_readbitsi(uint8_t *bits, uint8_t len) {
|
||||
uint8_t phy_read_bits_u8(uint8_t *bits, uint8_t len) {
|
||||
cli();
|
||||
READING_BYTE = 0;
|
||||
READING_PARITY = 0;
|
||||
@@ -251,20 +251,20 @@ uint8_t AVCLAN_readbitsi(uint8_t *bits, uint8_t len) {
|
||||
}
|
||||
|
||||
// Read `len` bits on the AVCLAN bus; returns the even parity
|
||||
uint8_t AVCLAN_readbitsl(uint16_t *bits, int8_t len) {
|
||||
uint8_t phy_read_bits_u16(uint16_t *bits, int8_t len) {
|
||||
uint8_t parity = 0;
|
||||
if (len > 8) {
|
||||
uint8_t over = len - 8;
|
||||
parity = AVCLAN_readbitsi((uint8_t *)bits + 1, over);
|
||||
parity = phy_read_bits_u8((uint8_t *)bits + 1, over);
|
||||
len -= over;
|
||||
}
|
||||
parity += AVCLAN_readbitsi((uint8_t *)bits + 0, len);
|
||||
parity += phy_read_bits_u8((uint8_t *)bits + 0, len);
|
||||
|
||||
return (parity & 1);
|
||||
}
|
||||
|
||||
// Read a byte on the AVCLAN bus
|
||||
uint8_t AVCLAN_readbyte(uint8_t *byte) {
|
||||
uint8_t phy_read_byte(uint8_t *byte) {
|
||||
cli();
|
||||
READING_BYTE = 0;
|
||||
READING_PARITY = 0;
|
||||
@@ -289,7 +289,7 @@ uint8_t AVCLAN_readbyte(uint8_t *byte) {
|
||||
return (parity & 1);
|
||||
}
|
||||
|
||||
void AVCLAN_busInit() {
|
||||
void phy_init() {
|
||||
// Set pin 6 and 7 as input
|
||||
PORTA.DIRCLR = (PIN6_bm | PIN7_bm);
|
||||
// Disable input buffer; recommended when using AC
|
||||
@@ -321,14 +321,14 @@ void AVCLAN_busInit() {
|
||||
|
||||
AVCLAN_setBusIdle();
|
||||
|
||||
AVCLAN_muteDevice(false); // unmute AVCLAN bus TX
|
||||
phy_mute(false); // unmute AVCLAN bus TX
|
||||
}
|
||||
|
||||
// Wait for and validate an incoming start bit. On an over-long "driven" bus
|
||||
// (AC2 latched high because the bus is actually floating) this kicks PA7 hard
|
||||
// high to unlatch the comparator. The framing layer maps the result to its own
|
||||
// error reporting; no printing happens here.
|
||||
Read AVCLAN_readstartbit() {
|
||||
Read phy_read_startbit() {
|
||||
uint16_t startbitlen = TCB1.CNT = 0;
|
||||
while (!BUS_IS_IDLE) {
|
||||
startbitlen = TCB1.CNT;
|
||||
@@ -370,7 +370,7 @@ Read AVCLAN_readstartbit() {
|
||||
|
||||
// Acquire the bus and emit a start bit. Returns false if another device is
|
||||
// already driving the bus (we can't yet do proper CSMA/CD).
|
||||
bool AVCLAN_sendstartbit() {
|
||||
bool phy_send_startbit() {
|
||||
// wait for free line
|
||||
TCB1.CNT = 0;
|
||||
while (BUS_IS_IDLE) {
|
||||
@@ -395,25 +395,25 @@ bool AVCLAN_sendstartbit() {
|
||||
// set_AVC_logic_for(1, AVCLAN_STARTBIT_LOGIC_1); // wait for end of start
|
||||
return false;
|
||||
}
|
||||
AVCLAN_sendbit(bit_start);
|
||||
phy_send_bit(bit_start);
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Disable non-read related interrupts (USART RX, RTC status tick, mic timer)
|
||||
during AVCLAN bus transactions so framing isn't disturbed. TCB0 must remain
|
||||
enabled. */
|
||||
void AVCLAN_stopEvent() {
|
||||
void phy_guard_enter() {
|
||||
ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {
|
||||
statustimer_disable();
|
||||
cdtimer_disable();
|
||||
RS232_setRxInterrupt(false);
|
||||
mediacontrol_syncDuringMask();
|
||||
media_sync_during_guard();
|
||||
}
|
||||
}
|
||||
|
||||
// Re-enable serial and periodic interrupts after a bus transaction.
|
||||
void AVCLAN_startEvent() {
|
||||
void phy_guard_leave() {
|
||||
ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {
|
||||
statustimer_restore(); // Reenable status interrupt if currently playing
|
||||
cdtimer_restore(); // Reenable status interrupt if currently playing
|
||||
RS232_setRxInterrupt(true);
|
||||
}
|
||||
}
|
||||
@@ -426,8 +426,8 @@ void AVCLAN_startEvent() {
|
||||
static uint16_t pulses[100];
|
||||
static uint16_t periods[100];
|
||||
|
||||
void AVCLan_Measure() {
|
||||
AVCLAN_stopEvent();
|
||||
void phy_measure() {
|
||||
phy_guard_enter();
|
||||
|
||||
uint8_t tmp = 0;
|
||||
|
||||
@@ -457,6 +457,6 @@ void AVCLan_Measure() {
|
||||
}
|
||||
RS232_Print("\nDone.\n");
|
||||
|
||||
AVCLAN_startEvent();
|
||||
phy_guard_leave();
|
||||
}
|
||||
#endif
|
||||
|
||||
Reference in New Issue
Block a user