ether2ser 0.1.0
Ethernet <-> synchronous V.24 bridge firmware for RP2040 + W5500
Loading...
Searching...
No Matches
baudrate_monitor.c
Go to the documentation of this file.
1/*
2 * ether2ser - Ethernet <-> synchronous V.24 (RS-232/V.28) bridge
3 *
4 * File: src/system/baudrate_monitor.c
5 * Purpose: RXC edge-counting baudrate estimator implementation.
6 *
7 * SPDX-License-Identifier: Apache-2.0
8 *
9 * Copyright (c) 2026 Florian <f.leuze@outlook.de>
10 */
11
12// Related headers
13#include "baudrate_monitor.h"
14
15// Standard library headers
16#include <assert.h>
17#include <stdbool.h>
18#include <stddef.h>
19#include <stdint.h>
20
21// Library Headers
22#include "hardware/gpio.h"
23#include "hardware/sync.h"
24#include "hardware/timer.h"
25#include "pico/time.h"
26#include "pico/types.h"
27
28// Project Headers
29#include "platform/pinmap.h"
30#include "system/common.h"
31
32// Generated headers
33
34#define PIN_COUNT 25
35// Mark estimate stale if no edges for this long (sporadic clocks).
36#define BAUD_STALE_US 50000U
37// Short sampling period to quickly react to bursts.
38#define BAUD_TIMER_MS 20
39// EMA smoothing for burst-to-burst stability.
40#define BAUD_EMA_ALPHA 0.2F
41static volatile uint32_t edge_count[PIN_COUNT] = {0};
42static repeating_timer_t baud_timer;
43static volatile bool baud_ready[PIN_COUNT] = {false};
44static volatile float baud_hz[PIN_COUNT] = {0.0F};
45static size_t current_pin_count = 0;
46static volatile uint8_t monitored_pin[PIN_COUNT] = {0};
47// First/last edge timestamps within the current sample window.
48static volatile uint64_t first_edge_time_us[PIN_COUNT] = {0};
49static volatile uint64_t last_edge_time_us[PIN_COUNT] = {0};
50
51static void rxc_edge_isr(uint gpio, uint32_t events)
52{
53 // (void)gpio;
54 (void)events;
55 // Record the first edge in the current window, then update last edge time.
56 uint64_t now_us = time_us_64();
57 if (edge_count[gpio] == 0)
58 {
59 first_edge_time_us[gpio] = now_us;
60 }
61 last_edge_time_us[gpio] = now_us;
62 edge_count[gpio]++;
63}
64
66{
67 baud_ready[pin] = false;
68 return baud_hz[pin];
69}
70
71static bool baud_timer_cb(repeating_timer_t* timer)
72{
73 (void)timer;
74
75 uint64_t now_us = time_us_64();
76 for (size_t pin_index = 0; pin_index < current_pin_count; ++pin_index)
77 {
78 uint32_t edges =
79 __atomic_exchange_n(&edge_count[monitored_pin[pin_index]], 0, __ATOMIC_RELAXED);
80 if (edges > 1)
81 {
82 // Use first/last edge timestamps to avoid timer jitter skewing Hz.
83 uint32_t save = save_and_disable_interrupts();
84 uint64_t first_us = first_edge_time_us[monitored_pin[pin_index]];
85 uint64_t last_us = last_edge_time_us[monitored_pin[pin_index]];
86 restore_interrupts(save);
87 if (last_us > first_us)
88 {
89 uint64_t elapsed_us = last_us - first_us;
90 float inst_hz = ((float)(edges - 1) * (float)US_PER_SECOND) / (float)elapsed_us;
91 if (baud_hz[monitored_pin[pin_index]] <= 0.0F)
92 {
93 baud_hz[monitored_pin[pin_index]] = inst_hz;
94 }
95 else
96 {
97 // Smooth bursts with a simple EMA.
98 baud_hz[monitored_pin[pin_index]] =
99 (BAUD_EMA_ALPHA * inst_hz) +
100 ((1.0F - BAUD_EMA_ALPHA) * baud_hz[monitored_pin[pin_index]]);
101 }
102 baud_ready[monitored_pin[pin_index]] = true;
103 }
104 }
105 else
106 {
107 // If no edges recently, mark estimate as stale without forcing zero.
108 if (now_us - last_edge_time_us[monitored_pin[pin_index]] > BAUD_STALE_US)
109 {
110 baud_ready[monitored_pin[pin_index]] = false;
111 }
112 }
113 }
114
115 return true;
116}
117
119{
120 assert(pin <= PIN_COUNT);
121 static bool initialized = false;
122 if (!initialized)
123 {
124 gpio_set_irq_enabled_with_callback(pin, GPIO_IRQ_EDGE_RISE, true, &rxc_edge_isr);
125 add_repeating_timer_ms(BAUD_TIMER_MS, baud_timer_cb, NULL, &baud_timer);
126 initialized = true;
127 }
128 else
129 {
130 gpio_set_irq_enabled(pin, GPIO_IRQ_EDGE_RISE, true);
131 }
133 {
135 }
136}
static void rxc_edge_isr(uint gpio, uint32_t events)
static volatile bool baud_ready[PIN_COUNT]
static bool baud_timer_cb(repeating_timer_t *timer)
static volatile float baud_hz[PIN_COUNT]
static repeating_timer_t baud_timer
#define BAUD_EMA_ALPHA
static volatile uint64_t last_edge_time_us[PIN_COUNT]
void baudrate_estimator_init(V24_PIN_T pin)
Initialize baudrate estimator on a specific RX clock pin.
#define BAUD_STALE_US
float baudrate_estimator_get_current_estimation(V24_PIN_T pin)
Get latest baudrate estimate.
#define BAUD_TIMER_MS
static volatile uint8_t monitored_pin[PIN_COUNT]
static size_t current_pin_count
#define PIN_COUNT
static volatile uint64_t first_edge_time_us[PIN_COUNT]
static volatile uint32_t edge_count[PIN_COUNT]
#define US_PER_SECOND
The number of microseconds per second.
Definition common.h:52
V24_PIN_T
Typed aliases for V.24 GPIO assignments.
Definition pinmap.h:46