summaryrefslogtreecommitdiff
path: root/firmware/c1224/src/common/afsk_bitbang.c
diff options
context:
space:
mode:
authorinfo@mode42.com <info@mode42.com>2026-08-07 18:15:00 +0000
committerinfo@mode42.com <info@mode42.com>2026-08-07 18:15:00 +0000
commitd393e0c30192611d4cac65a5429fd7180ce1f49c (patch)
tree7e47bd930340c75c4802801a81f3bd17c2469d9f /firmware/c1224/src/common/afsk_bitbang.c
Initial pushmain
Diffstat (limited to 'firmware/c1224/src/common/afsk_bitbang.c')
-rw-r--r--firmware/c1224/src/common/afsk_bitbang.c116
1 files changed, 116 insertions, 0 deletions
diff --git a/firmware/c1224/src/common/afsk_bitbang.c b/firmware/c1224/src/common/afsk_bitbang.c
new file mode 100644
index 0000000..7019100
--- /dev/null
+++ b/firmware/c1224/src/common/afsk_bitbang.c
@@ -0,0 +1,116 @@
+/*
+ * Timed NRZI bitbang to TCM3105 TXD/RXD — v1.0-prototype best effort.
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+#include "afsk_bitbang.h"
+#include "hdlc.h"
+#include "board_pins.h"
+#include "ptt.h"
+#include "status.h"
+#include "hardware/gpio.h"
+#include "pico/stdlib.h"
+#include <string.h>
+
+#define BIT_US (1000000u / (unsigned)C1224_BITRATE)
+#define TX_BITS_MAX ((HDLC_MAX_INFO + 4) * 10 + 64)
+#define RX_IDLE_MARK true
+
+static uint8_t g_tx_bits[TX_BITS_MAX];
+static volatile bool g_tx_busy;
+static bool g_nrzi_level;
+static hdlc_rx_t g_rx;
+static bool g_rx_nrzi;
+static absolute_time_t g_next_sample;
+static afsk_rx_frame_fn g_rx_cb;
+static uint8_t g_rx_out[HDLC_MAX_INFO];
+
+void afsk_bitbang_init(void)
+{
+ gpio_put(PIN_MODEM_TXD, 1);
+ g_tx_busy = false;
+ g_nrzi_level = true;
+ g_rx_nrzi = RX_IDLE_MARK;
+ hdlc_rx_init(&g_rx);
+ g_rx_cb = NULL;
+ g_next_sample = get_absolute_time();
+}
+
+unsigned afsk_bitrate(void)
+{
+ return (unsigned)C1224_BITRATE;
+}
+
+bool afsk_tx_busy(void)
+{
+ return g_tx_busy;
+}
+
+void afsk_rx_set_callback(afsk_rx_frame_fn fn)
+{
+ g_rx_cb = fn;
+}
+
+static void nrzi_emit(bool data_bit)
+{
+ /* AX.25 NRZI: 0 = transition, 1 = no transition */
+ if (!data_bit) {
+ g_nrzi_level = !g_nrzi_level;
+ }
+ gpio_put(PIN_MODEM_TXD, g_nrzi_level ? 1 : 0);
+}
+
+bool afsk_tx_frame(const uint8_t *info, size_t info_len)
+{
+ if (g_tx_busy || !info || info_len == 0 || info_len > HDLC_MAX_INFO) {
+ return false;
+ }
+ size_t nbits = hdlc_encode_bits(info, info_len, g_tx_bits, TX_BITS_MAX, 2);
+ if (nbits == 0) {
+ return false;
+ }
+
+ g_tx_busy = true;
+ status_set(C1224_STATE_SENDING);
+ status_publish();
+ ptt_set(true);
+ sleep_ms(40); /* key-up settle — prototype */
+
+ g_nrzi_level = true;
+ gpio_put(PIN_MODEM_TXD, 1);
+
+ for (size_t i = 0; i < nbits; i++) {
+ nrzi_emit(g_tx_bits[i] != 0);
+ busy_wait_us_32(BIT_US);
+ }
+
+ gpio_put(PIN_MODEM_TXD, 1);
+ sleep_ms(20);
+ ptt_set(false);
+ status_set(C1224_STATE_READY);
+ status_publish();
+ g_tx_busy = false;
+ return true;
+}
+
+void afsk_rx_poll(void)
+{
+ if (g_tx_busy) {
+ return;
+ }
+ absolute_time_t now = get_absolute_time();
+ if (absolute_time_diff_us(g_next_sample, now) < 0) {
+ return;
+ }
+ g_next_sample = delayed_by_us(now, BIT_US);
+
+ bool level = gpio_get(PIN_MODEM_RXD);
+ bool data_bit = (level == g_rx_nrzi); /* 1 = no transition */
+ if (level != g_rx_nrzi) {
+ g_rx_nrzi = level;
+ }
+
+ size_t out_len = 0;
+ if (hdlc_rx_bit(&g_rx, data_bit, g_rx_out, &out_len) && g_rx_cb && out_len > 0) {
+ g_rx_cb(g_rx_out, out_len);
+ }
+}
git clone -b <branch> https://cgit.mode42.com/<repo>.git
git clone -b <branch> git://cgit.mode42.com/<repo>.git

info@mode42.com