pololu_tic 0.2.0-beta.3

An embedded-hal driver to control the Tic series of stepper motor controllers created by Pololu.
Documentation
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
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
//! # Pololu Tic Driver
//! A platform agnostic Rust driver for the
//! [Pololu Tic motor driver](https://www.pololu.com/tic) boards.
//! Currently this library only supports the `I2C` interface of the boards,
//! but serial support is planned.
//!
//! This library supports the same boards that the equivalent
//! [Arduino library](https://github.com/pololu/tic-arduino/) supports.
//! Currently the `T500`, `T834`, `T825`, `T249`, `36v4` are supported.
//!
//! ## Feature Flags
//! This crate has a few feature flags to enable or disable support for different interfaces.
//!
//!  - `i2c` (default): Enables support for the I²C interface
//!  - `serial`: Enables support for the UART Serial interface
//!
//! ## Example
//! A basic example of using this library to set up and control a Tic36v4 is as
//! follows. Ensure you replace `<i2c_bus>` with your platform's `embedded_hal`
//! I²C interface.
//!
//! ```rust,ignore
//! use pololu_tic::{TicBase, TicI2C, TicProduct};
//!
//! let mut tic = pololu_tic::TicI2C::new_with_address(
//!     <i2c_bus>,
//!     TicProduct::Tic36v4,
//!     14
//! );
//!
//! tic.set_target_velocity(2000000);
//!
//! loop {
//!     tic.reset_command_timeout();
//! }
//! ```

#![deny(unsafe_code)]
#![no_std]

#[cfg(feature = "std")]
extern crate std;

mod base;

mod backends {
    #[cfg(feature = "i2c")]
    pub mod i2c;
    #[cfg(feature = "serial")]
    pub mod serial;
    #[cfg(feature = "usb")]
    pub mod usb;
}

#[macro_use]
extern crate num_derive;

#[doc(inline)]
#[cfg(feature = "i2c")]
pub use backends::i2c::TicI2C;

#[doc(inline)]
#[cfg(feature = "serial")]
pub use backends::serial::TicSerial;

#[doc(inline)]
#[cfg(feature = "usb")]
pub use backends::usb::TicUsb;

#[doc(inline)]
pub use base::TicBase;

const TIC_03A_CURRENT_TABLE: [u16; 33] = [
    0, 1, 174, 343, 495, 634, 762, 880, 990, 1092, 1189, 1281, 1368, 1452, 1532, 1611, 1687, 1762,
    1835, 1909, 1982, 2056, 2131, 2207, 2285, 2366, 2451, 2540, 2634, 2734, 2843, 2962, 3093,
];

/// The type of Tic driver that is being represented.
#[derive(FromPrimitive, ToPrimitive, Clone, Copy, PartialEq, Eq)]
#[repr(u8)]
pub enum TicProduct {
    /// An unknown driver. Used if not provided, as the default.
    Unknown = 0,
    /// [Tic T825](https://www.pololu.com/product/3130)
    T825 = 1,
    /// [Tic T834](https://www.pololu.com/product/3132)
    T834 = 2,
    /// [Tic T500](https://www.pololu.com/product/3134)
    T500 = 3,
    /// Not available on the website.
    N825 = 4,
    /// [Tic T249](https://www.pololu.com/product/3138)
    T249 = 5,
    /// [Tic 36v4](https://www.pololu.com/product/3140)
    Tic36v4 = 6,
}

/// This constant is used by the library to convert between milliamps and the
/// Tic's native current unit, which is 32 mA.  This is only valid for the Tic
/// T825 and Tic T834.
const TIC_CURRENT_UNITS: u8 = 32;

/// This constant is used by the library to convert between milliamps and the
/// Tic T249 native current unit, which is 40 mA.
const TIC_T249_CURRENT_UNITS: u8 = 40;

/// This enum defines the Tic's error bits. See the
/// [**Error handling**](https://www.pololu.com/docs/0J71/5.4) section of
/// the Tic user's guide for more information about what these errors mean.
///
/// See [`TicBase::error_status()`] and [`TicBase::errors_occurred()`].
#[derive(FromPrimitive, ToPrimitive, Debug)]
pub enum TicError {
    /// The "Intentionally de-energized" error bit is 0 when the Tic starts up.
    /// It can be set with the "De-energize" command and cleared with the
    /// "Energize" command.
    ///
    /// [Read more](https://www.pololu.com/docs/0J71/5.4#cond-int-deenergized)
    IntentionallyDeenergized = 0,
    /// Indicates that motor driver IC on the Tic has detected a problem and
    /// reported it to the main microcontroller
    ///
    /// [Read more](https://www.pololu.com/docs/0J71/5.4#cond-motor-driver-error)
    MotorDriverError = 1,
    /// Indicates that the voltage on VIN has dropped well below the minimum
    /// operating voltage.
    ///
    /// [Read more](https://www.pololu.com/docs/0J71/5.4#cond-low-vin)
    LowVin = 2,
    /// Indicates that one of the pins configured as a kill switch is in its active state.
    ///
    /// [Read more](https://www.pololu.com/docs/0J71/5.4#cond-kill-switch)
    KillSwitch = 3,
    /// Indicates that the Tic’s main input signal is not valid, so it cannot be
    /// used to set the target position or velocity.
    ///
    /// [Read more](https://www.pololu.com/docs/0J71/5.4#cond-required-input-invalid)
    RequiredInputInvalid = 4,
    /// Indicates that something went wrong with the I²C or TTL serial
    /// communication.
    ///
    /// [Read more](https://www.pololu.com/docs/0J71/5.4#cond-serial-error)
    SerialError = 5,
    /// Whenever the Tic’s control mode is “Serial / I²C / USB” and that time
    /// exceeds the timeout period, the Tic sets the “Command timeout” error
    /// bit.
    ///
    /// [Read more](https://www.pololu.com/docs/0J71/5.4#cond-serial-error)
    CommandTimeout = 6,
    /// The Tic’s safe start feature helps to avoid unexpectedly powering the
    /// motor in speed control modes and in “Serial / I²C / USB” mode.
    ///
    /// [Read more](https://www.pololu.com/docs/0J71/5.4#cond-safe-start)
    SafeStartViolation = 7,
    /// The Tic reports an “ERR line high” error if it is not driving its ERR
    /// pin high and the digital reading on the ERR pin input is high.
    ///
    /// [Read more](https://www.pololu.com/docs/0J71/5.4#cond-err-high)
    ErrLineHigh = 8,
    SerialFraming = 16,
    RxOverrun = 17,
    Format = 18,
    Crc = 19,
    EncoderSkip = 20,
}

/// The generic error type for anything in this crate.
#[derive(thiserror::Error, Debug)]
#[non_exhaustive]
pub enum TicHandlerError {
    #[error("the driver experienced an internal error")]
    InternalError(TicError),

    #[cfg(feature = "i2c")]
    #[error("the i2c communication experienced an error")]
    I2cError(embedded_hal::i2c::ErrorKind),

    #[cfg(feature = "serial")]
    #[error("the serial communication experienced an error")]
    StreamError(embedded_io::ErrorKind),

    #[cfg(feature = "usb")]
    #[error("internal nusb error")]
    NusbError(nusb::Error),

    #[cfg(feature = "usb")]
    #[error("the USB communication experienced a transfer error")]
    UsbTransferError(nusb::transfer::TransferError),

    #[cfg(feature = "usb")]
    #[error("the target USB device is invalid")]
    UsbInvalidDevice(u16),

    #[error("the value could not be parsed")]
    ParseError,
}

/// The Tic command codes which are used for its serial, I2C, and USB interface.
///
/// These codes are used by the library and you should not need to use them.
#[derive(FromPrimitive, ToPrimitive, Debug, PartialEq, Eq, Clone, Copy)]
pub enum TicCommand {
    SetTargetPosition = 0xE0,
    SetTargetVelocity = 0xE3,
    HaltAndSetPosition = 0xEC,
    HaltAndHold = 0x89,
    GoHome = 0x97,
    ResetCommandTimeout = 0x8C,
    Deenergize = 0x86,
    Energize = 0x85,
    ExitSafeStart = 0x83,
    EnterSafeStart = 0x8F,
    Reset = 0xB0,
    ClearDriverError = 0x8A,
    SetSpeedMax = 0xE6,
    SetStartingSpeed = 0xE5,
    SetAccelMax = 0xEA,
    SetDecelMax = 0xE9,
    SetStepMode = 0x94,
    SetCurrentLimit = 0x91,
    SetDecayMode = 0x92,
    SetAgcOption = 0x98,
    GetVariable = 0xA1,
    GetVariableAndClearErrorsOccurred = 0xA2,
    GetSetting = 0xA8,
}

/// The possible operation states for the Tic.
///
/// See [`TicBase::operation_state()`].
#[derive(FromPrimitive, ToPrimitive, Debug, PartialEq, Eq)]
pub enum TicOperationState {
    Reset = 0,
    Deenergized = 2,
    SoftError = 4,
    WaitingForErrLine = 6,
    StartingUp = 8,
    Normal = 10,
}

/// The possible planning modes for the Tic's step generation code.
///
/// See [`TicBase::planning_mode()`].
#[derive(FromPrimitive, ToPrimitive, Debug, PartialEq, Eq)]
pub enum TicPlanningMode {
    Off = 0,
    TargetPosition = 1,
    TargetVelocity = 2,
}

/// The possible causes of a full microcontroller reset for the Tic.
///
/// See [`TicBase::device_reset_cause()`].
#[derive(FromPrimitive, ToPrimitive, Debug, PartialEq, Eq)]
pub enum TicReset {
    PowerUp = 0,
    Brownout = 1,
    ResetLine = 2,
    Watchdog = 4,
    Software = 8,
    StackOverflow = 16,
    StackUnderflow = 32,
}

/// The possible decay modes.
///
/// See [`TicBase::decay_mode()`] and [`TicBase::set_decay_mode()`].
#[derive(FromPrimitive, ToPrimitive, Debug, PartialEq, Eq)]
pub enum TicDecayMode {
    /// This specifies "Mixed" decay mode on the Tic T825
    /// and "Mixed 50%" on the Tic T824.
    Mixed = 0,

    /// This specifies "Slow" decay mode.
    Slow = 1,

    /// This specifies "Fast" decay mode.
    Fast = 2,

    /// This specifies "Mixed 25%" decay mode on the Tic T824
    /// and is the same as [`TicDecayMode::Mixed`] on the Tic T825.
    Mixed25 = 3,

    /// This specifies "Mixed 75%" decay mode on the Tic T824
    /// and is the same as [`TicDecayMode::Mixed`] on the Tic T825.
    Mixed75 = 4,
}

/// The possible step modes.
///
/// See [`TicBase::step_mode()`] and [`TicBase::set_step_mode()`].
#[derive(FromPrimitive, ToPrimitive, Debug, PartialEq, Eq)]
pub enum TicStepMode {
    Full = 0,
    Half = 1,
    Microstep4 = 2,
    Microstep8 = 3,
    Microstep16 = 4,
    Microstep32 = 5,
    Microstep2_100p = 6,
    Microstep64 = 7,
    Microstep128 = 8,
    Microstep256 = 9,
}

/// Possible AGC modes.
///
/// See [`TicBase::set_agc_mode()`] and [`TicBase::agc_mode()`].
#[derive(FromPrimitive, ToPrimitive, Debug, PartialEq, Eq)]
pub enum TicAgcMode {
    Off = 0,
    On = 1,
    ActiveOff = 2,
}

/// Possible AGC buttom current limit percentages.
///
/// See [`TicBase::set_agc_bottom_current_limit()`] and [`TicBase::agc_bottom_current_limit()`].
#[derive(FromPrimitive, ToPrimitive, Debug, PartialEq, Eq)]
pub enum TicAgcBottomCurrentLimit {
    P45 = 0,
    P50 = 1,
    P55 = 2,
    P60 = 3,
    P65 = 4,
    P70 = 5,
    P75 = 6,
    P80 = 7,
}

/// Possible AGC current boost steps values.
///
/// See [`TicBase::set_agc_current_boost_steps()`] and [`TicBase::agc_current_boost_steps()`].
#[derive(FromPrimitive, ToPrimitive, Debug, PartialEq, Eq)]
pub enum TicAgcCurrentBoostSteps {
    S5 = 0,
    S7 = 1,
    S9 = 2,
    S11 = 3,
}

/// Possible AGC frequency limit values.
///
/// See [`TicBase::set_agc_frequency_limit()`] and [`TicBase::agc_frequency_limit()`].
#[derive(FromPrimitive, ToPrimitive, Debug, PartialEq, Eq)]
pub enum TicAgcFrequencyLimit {
    Off = 0,
    F225Hz = 1,
    F450Hz = 2,
    F675Hz = 3,
}

/// The Tic's control pins.
#[derive(FromPrimitive, ToPrimitive, Debug, PartialEq, Eq)]
pub enum TicPin {
    SCL = 0,
    SDA = 1,
    TX = 2,
    RX = 3,
    RC = 4,
}

/// The Tic's pin states.
///
/// See [`TicBase::pin_state()`].
#[derive(FromPrimitive, ToPrimitive, Debug, PartialEq, Eq)]
pub enum TicPinState {
    HighImpedance = 0,
    InputPullUp = 1,
    OutputLow = 2,
    OutputHigh = 3,
}

/// The possible states of the Tic's main input.
#[derive(FromPrimitive, ToPrimitive, Debug, PartialEq, Eq)]
pub enum TicInputState {
    /// The input is not ready yet.  More samples are needed, or a command has not
    /// been received yet.
    NotReady = 0,

    /// The input is invalid.
    Invalid = 1,

    /// The input is valid and is telling the Tic to halt the motor.
    Halt = 2,

    /// The input is valid and is telling the Tic to go to a target position,
    /// which you can get with [`TicBase::input_after_scaling()`].
    Position = 3,

    /// The input is valid and is telling the Tic to go to a target velocity,
    /// which you can get with [`TicBase::input_after_scaling()`].
    Velocity = 4,
}

/// The bits in the Tic's Misc Flags 1 register.
///
/// You should not need to use this directly. See [`TicBase::is_energized()`] and
/// [`TicBase::is_position_uncertain()`].
#[derive(FromPrimitive, ToPrimitive, Debug, PartialEq, Eq)]
pub enum TicMiscFlags1 {
    Energized = 0,
    PositionUncertain = 1,
    ForwardLimitActive = 2,
    ReverseLimitActive = 3,
    HomingActive = 4,
}

/// Possible motor driver errors for the Tic T249.
///
/// See [`TicBase::last_motor_driver_error()`].
#[derive(FromPrimitive, ToPrimitive, Debug, PartialEq, Eq)]
pub enum TicMotorDriverError {
    None = 0,
    OverCurrent = 1,
    OverTemperature = 2,
}

/// The bits in the "Last HP driver errors" variable.
///
/// See [`TicBase::last_hp_driver_errors()`].
#[derive(FromPrimitive, ToPrimitive, Debug, PartialEq, Eq)]
pub enum TicHpDriverError {
    OverTemperature = 0,
    OverCurrentA = 1,
    OverCurrentB = 2,
    PreDriverFaultA = 3,
    PreDriverFaultB = 4,
    UnderVoltage = 5,
    Verify = 7,
}