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
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
//! CHOPCONF - Chopper configuration register (0x6C)
use super::{Address, MicrostepResolution, ReadableRegister, Register, WritableRegister};
/// Chopper configuration register.
///
/// Controls the chopper (current regulation) and microstep settings.
/// This is one of the most important registers for motor tuning.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct Chopconf(u32);
impl Chopconf {
/// Default value recommended by Trinamic.
/// TOFF=3, HSTRT=4, HEND=1, TBL=2, MRES=0 (256 microsteps)
pub const DEFAULT: u32 = 0x10000053;
/// Create with default values.
pub fn new() -> Self {
Self(Self::DEFAULT)
}
/// Get TOFF (0-15).
///
/// Off-time setting controls chopper frequency.
/// - 0: Driver disabled (all bridges off)
/// - 1: Driver enabled with minimum off-time
/// - 2-15: Off-time = 12 + 32*TOFF clocks
///
/// Recommended: 3-5 for most applications.
pub fn toff(&self) -> u8 {
(self.0 & 0x0F) as u8
}
/// Set TOFF (0-15).
pub fn set_toff(&mut self, value: u8) -> &mut Self {
self.0 = (self.0 & !0x0F) | ((value as u32) & 0x0F);
self
}
/// Get HSTRT (0-7).
///
/// Hysteresis start value (adds 1-8 to HEND).
/// HSTRT + HEND must be <= 16.
pub fn hstrt(&self) -> u8 {
((self.0 >> 4) & 0x07) as u8
}
/// Set HSTRT (0-7).
pub fn set_hstrt(&mut self, value: u8) -> &mut Self {
self.0 = (self.0 & !(0x07 << 4)) | (((value as u32) & 0x07) << 4);
self
}
/// Get HEND (0-15).
///
/// Hysteresis end (low) value.
/// Sets the hysteresis low point for current chopping.
/// Effective value: -3 to 12 (register value - 3).
pub fn hend(&self) -> u8 {
((self.0 >> 7) & 0x0F) as u8
}
/// Set HEND (0-15).
pub fn set_hend(&mut self, value: u8) -> &mut Self {
self.0 = (self.0 & !(0x0F << 7)) | (((value as u32) & 0x0F) << 7);
self
}
/// Get TBL (0-3).
///
/// Comparator blank time select:
/// - 0: 16 clocks
/// - 1: 24 clocks
/// - 2: 36 clocks (recommended)
/// - 3: 54 clocks
pub fn tbl(&self) -> u8 {
((self.0 >> 15) & 0x03) as u8
}
/// Set TBL (0-3).
pub fn set_tbl(&mut self, value: u8) -> &mut Self {
self.0 = (self.0 & !(0x03 << 15)) | (((value as u32) & 0x03) << 15);
self
}
/// Get VSENSE.
///
/// Sense resistor voltage-based current scaling:
/// - false: Low sensitivity (high current range)
/// - true: High sensitivity (low current range, 0.18x)
pub fn vsense(&self) -> bool {
(self.0 >> 17) & 1 != 0
}
/// Set VSENSE.
pub fn set_vsense(&mut self, value: bool) -> &mut Self {
if value {
self.0 |= 1 << 17;
} else {
self.0 &= !(1 << 17);
}
self
}
/// Get MRES - microstep resolution (0-8).
///
/// - 0: 256 microsteps (native)
/// - 1: 128 microsteps
/// - 2: 64 microsteps
/// - 3: 32 microsteps
/// - 4: 16 microsteps
/// - 5: 8 microsteps
/// - 6: 4 microsteps
/// - 7: 2 microsteps (half step)
/// - 8: Full step
pub fn mres(&self) -> u8 {
((self.0 >> 24) & 0x0F) as u8
}
/// Set MRES - microstep resolution (0-8).
pub fn set_mres(&mut self, value: u8) -> &mut Self {
self.0 = (self.0 & !(0x0F << 24)) | (((value as u32) & 0x0F) << 24);
self
}
/// Get the microstep resolution as enum.
pub fn microstep_resolution(&self) -> MicrostepResolution {
MicrostepResolution::from_mres(self.mres())
}
/// Set the microstep resolution from enum.
pub fn set_microstep_resolution(&mut self, res: MicrostepResolution) -> &mut Self {
self.set_mres(res.to_mres())
}
/// Get INTPOL.
///
/// Interpolation to 256 microsteps:
/// - true: Enable interpolation (recommended for MRES < 256)
/// - false: No interpolation
pub fn intpol(&self) -> bool {
(self.0 >> 28) & 1 != 0
}
/// Set INTPOL.
pub fn set_intpol(&mut self, value: bool) -> &mut Self {
if value {
self.0 |= 1 << 28;
} else {
self.0 &= !(1 << 28);
}
self
}
/// Get DEDGE.
///
/// Enable double edge step pulses:
/// - true: Step on both rising and falling STEP edges
/// - false: Step on rising edge only
pub fn dedge(&self) -> bool {
(self.0 >> 29) & 1 != 0
}
/// Set DEDGE.
pub fn set_dedge(&mut self, value: bool) -> &mut Self {
if value {
self.0 |= 1 << 29;
} else {
self.0 &= !(1 << 29);
}
self
}
/// Get DISS2G.
///
/// Disable short to GND protection:
/// - true: Disable short to GND detection
/// - false: Enable short to GND detection (default)
pub fn diss2g(&self) -> bool {
(self.0 >> 30) & 1 != 0
}
/// Set DISS2G.
pub fn set_diss2g(&mut self, value: bool) -> &mut Self {
if value {
self.0 |= 1 << 30;
} else {
self.0 &= !(1 << 30);
}
self
}
/// Get DISS2VS.
///
/// Disable short to supply protection:
/// - true: Disable short to VS detection
/// - false: Enable short to VS detection (default)
pub fn diss2vs(&self) -> bool {
(self.0 >> 31) & 1 != 0
}
/// Set DISS2VS.
pub fn set_diss2vs(&mut self, value: bool) -> &mut Self {
if value {
self.0 |= 1 << 31;
} else {
self.0 &= !(1 << 31);
}
self
}
/// Get the raw register value.
pub fn raw(&self) -> u32 {
self.0
}
/// Create from raw value.
pub fn from_raw(value: u32) -> Self {
Self(value)
}
}
impl Default for Chopconf {
fn default() -> Self {
Self::new()
}
}
impl Register for Chopconf {
const ADDRESS: Address = Address::Chopconf;
}
impl ReadableRegister for Chopconf {}
impl WritableRegister for Chopconf {}
impl From<u32> for Chopconf {
fn from(value: u32) -> Self {
Self(value)
}
}
impl From<Chopconf> for u32 {
fn from(reg: Chopconf) -> u32 {
reg.0
}
}