1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
//! Bus-wide timing configuration ([`BusTiming`]) and the wire-occupancy
//! budget ([`bus_period`]).
//!
//! Pure config and arithmetic: nothing here touches the transport. The
//! constants are the values the crate has always used; [`BusTiming`] gathers
//! the tunable ones so a whole bus can be reconfigured in one call.
use Duration;
/// How long to listen for answers to a broadcast ID query.
const BROADCAST_WAIT: Duration = from_millis;
/// Gap between the five repetitions of a mode-switch frame.
const MODE_REPEAT_GAP: Duration = from_millis;
/// Gap between the five repetitions of a set-ID frame.
const SET_ID_REPEAT_GAP: Duration = from_millis;
/// Settling time after the set-ID sequence before re-querying.
const SET_ID_SETTLE: Duration = from_millis;
/// Gap between the frames of a [`M0601::safe_stop`](crate::M0601::safe_stop)
/// sequence (50 Hz).
const SAFE_STOP_GAP: Duration = from_millis;
/// Acceleration byte for the velocity-0 rounds of a stop sequence.
///
/// **Not** the fastest ramp (`1`): a hard ramp-to-zero on a loaded wheel can
/// trip the motor's own 3 A bus-overcurrent protection *during* the stop, at
/// which point it stops responding to drive commands and the controlled
/// deceleration is defeated — the opposite of what a safe stop wants. A
/// moderate ramp decelerates firmly without provoking that trip, and the
/// brake rounds that follow still deliver the hard final hold. `5` is a
/// firm-but-safe middle of that range; override it with
/// [`Bus::with_stop_accel`](crate::Bus::with_stop_accel) if a heavier or
/// lighter chassis wants a different deceleration.
const SAFE_STOP_ACCEL: u8 = 5;
/// Default minimum idle gap enforced between frames on a bus — see
/// [`Bus::with_min_gap`](crate::Bus::with_min_gap).
///
/// Sized to cover one reply frame (~0.9 ms at 115200 baud) plus an
/// allowance for the motor's turnaround, so the reply a fire-and-forget
/// drive frame elicits cannot still be on the wire when the next frame
/// starts. The turnaround component is an estimate, not a measurement —
/// when tighter scheduling matters, measure it and pass the real number to
/// [`Bus::with_min_gap`](crate::Bus::with_min_gap).
pub const DEFAULT_MIN_GAP: Duration = from_micros;
/// Default acceleration byte for
/// [`M0601::drive_velocity`](crate::M0601::drive_velocity) — the motor's
/// *fastest* ramp. Override the default per handle with
/// [`M0601::with_default_accel`](crate::M0601::with_default_accel), or per
/// call with
/// [`M0601::drive_velocity_accel`](crate::M0601::drive_velocity_accel).
pub const DEFAULT_DRIVE_ACCEL: u8 = 1;
/// Tunable bus-wide timing and stop behavior for one physical bus.
///
/// Every field defaults to the value the crate has always used
/// ([`BusTiming::default`]), so a bus left unconfigured behaves exactly as
/// before. Override what you need — wholesale with
/// [`Bus::with_timing`](crate::Bus::with_timing), or one field at a time with
/// the matching builder ([`Bus::with_stop_accel`](crate::Bus::with_stop_accel),
/// [`Bus::with_min_gap`](crate::Bus::with_min_gap)). Like the idle gap, this
/// lives on the **shared** bus, not per handle: set it once at open time and
/// every motor minted from the bus sees it.
/// The minimum wall-clock a bus needs for one round of `n_drives`
/// fire-and-forget drive frames plus `n_polls` read exchanges, given the
/// enforced idle `min_gap` after every frame and the `reply_wait` each poll
/// blocks for.
///
/// This is the "budget the wire" arithmetic from the crate docs made
/// executable: a drive frame costs one
/// [`frame_time`](crate::protocol::frame_time) plus `min_gap`. A poll costs
/// *two* frame times plus `reply_wait` and `min_gap`: the transport sleeps
/// out its own wire time **and** the reply window
/// ([`Transport::send_recv`](crate::transport::Transport::send_recv) sleeps
/// `frame + reply_wait`), then the trailing idle gap re-budgets a full
/// `frame + min_gap` from the poll's return — the frame's wire time is
/// spaced once inside the transaction and once in the trailing gap. A
/// periodic multi-motor loop's cycle must exceed this, and stay at or under
/// [`drive_floor`](crate::protocol::drive_floor), or it cannot sustain its
/// own period. A loop driving four motors that also polls one per cycle,
/// for instance, sizes its period against `bus_period(4, 1, …)`.
///
/// ```
/// use std::time::Duration;
/// use m0601::bus_period;
/// let gap = Duration::from_millis(2);
/// // Four drives + one poll with a 2 ms reply window ≈ 17.21 ms.
/// let p = bus_period(4, 1, gap, gap);
/// assert!((17_000..17_600).contains(&(p.as_micros() as u64)));
/// ```