Skip to content
GitLab
Explore
Sign in
Primary navigation
Search or go to…
Project
DC motor driver hat
Manage
Activity
Members
Labels
Plan
Issues
Issue boards
Milestones
Wiki
Code
Merge requests
Repository
Branches
Commits
Tags
Repository graph
Compare revisions
Snippets
Build
Pipelines
Jobs
Pipeline schedules
Artifacts
Deploy
Releases
Package Registry
Container Registry
Model registry
Operate
Environments
Terraform modules
Monitor
Incidents
Analyze
Value stream analytics
Contributor analytics
CI/CD analytics
Repository analytics
Model experiments
Help
Help
Support
GitLab documentation
Compare GitLab plans
Community forum
Contribute to GitLab
Provide feedback
Keyboard shortcuts
?
Snippets
Groups
Projects
Show more breadcrumbs
Software
DC motor driver hat
Commits
a4a77829
Commit
a4a77829
authored
6 months ago
by
Samuel Tardieu
Browse files
Options
Downloads
Patches
Plain Diff
Add documentation for Rust methods
parent
38516a0f
No related branches found
Branches containing commit
No related tags found
Tags containing commit
1 merge request
!29
Complete rust api
Changes
1
Hide whitespace changes
Inline
Side-by-side
Showing
1 changed file
tele0592/src/device.rs
+173
-3
173 additions, 3 deletions
tele0592/src/device.rs
with
173 additions
and
3 deletions
tele0592/src/device.rs
+
173
−
3
View file @
a4a77829
...
...
@@ -44,24 +44,42 @@ impl<I2CError: core::error::Error> core::error::Error for Error<I2CError> {}
pub
type
Result
<
T
,
I2cError
>
=
core
::
result
::
Result
<
T
,
Error
<
I2cError
>>
;
#[expect(clippy::missing_errors_doc)]
/// The `Device` trait lets us interact with the board and contains methods
/// available both in controller and in bootloader mode.
pub
trait
Device
{
type
I2cError
;
/// Write arbitrary data to the board.
///
/// # Errors
/// This method may fail if the communication with the board fails.
fn
write
(
&
mut
self
,
buf
:
impl
AsRef
<
[
u8
]
>
)
->
Result
<
(),
Self
::
I2cError
>
;
/// Exchange data with the board using a repeated start I²C event.
///
/// # Errors
/// This method may fail if the communication with the board fails.
fn
write_read
(
&
mut
self
,
write
:
impl
AsRef
<
[
u8
]
>
,
read
:
impl
AsMut
<
[
u8
]
>
,
)
->
Result
<
(),
Self
::
I2cError
>
;
/// Retrieve the firmware version as a (major, minor, patch) tuple.
///
/// # Errors
/// This method may fail if the communication with the board fails.
fn
firmware_version
(
&
mut
self
)
->
Result
<
[
u8
;
3
],
Self
::
I2cError
>
{
let
mut
result
=
[
0
;
3
];
self
.write_read
([
CMD_FIRMWARE_VERSION
],
&
mut
result
)
?
;
Ok
(
result
)
}
/// Identify the program currently running on the board as the
/// controller, the bootloader, or an unknown program.
///
/// # Errors
/// This method may fail if the communication with the board fails.
fn
who_am_i
(
&
mut
self
)
->
Result
<
WhoAmI
,
Self
::
I2cError
>
{
let
mut
me
:
u8
=
0
;
self
.write_read
([
CMD_WHO_AM_I
],
slice
::
from_mut
(
&
mut
me
))
?
;
...
...
@@ -72,16 +90,39 @@ pub trait Device {
})
}
/// Reset the board.
///
/// # Errors
/// This method may fail if the communication with the board fails.
fn
reset
(
&
mut
self
)
->
Result
<
(),
Self
::
I2cError
>
{
self
.write
([
CMD_RESET
])
}
/// Return the identity code and continuation code of the microcontroller
/// running on the board if available.
///
/// The [`brand_name`] function might help further identifying the
/// microcontroller.
///
/// # Errors
/// This method may fail if the communication with the board fails.
fn
device_family
(
&
mut
self
)
->
Result
<
Option
<
(
u8
,
u8
)
>
,
Self
::
I2cError
>
{
let
mut
data
=
[
0
;
2
];
self
.write_read
([
CMD_DEVICE_FAMILY
],
&
mut
data
)
?
;
Ok
((
data
!=
[
0
,
0
])
.then_some
((
data
[
0
],
data
[
1
])))
}
/// Return the device id and revision id from the `IDCODE` field of the
/// `DBGMCU` register of the microcontroller if available.
///
/// Note that some microcontrollers require that the JTAG or SWD interface
/// has been used in order for this information to be available.
///
/// This information may be further analyzed using the
/// [`mcu_kind`] function.
///
/// # Errors
/// This method may fail if the communication with the board fails.
fn
mcu_kind
(
&
mut
self
)
->
Result
<
Option
<
(
u16
,
u16
)
>
,
Self
::
I2cError
>
{
let
mut
data
=
[
0
;
4
];
self
.write_read
([
CMD_MCU_IDCODE
],
&
mut
data
)
?
;
...
...
@@ -91,20 +132,36 @@ pub trait Device {
)))
}
/// Retrieve the flash size, in KiB, from the microcontroller
/// present on the board.
///
/// # Errors
/// This method may fail if the communication with the board fails.
fn
flash_size
(
&
mut
self
)
->
Result
<
u16
,
Self
::
I2cError
>
{
let
mut
data
=
[
0
;
2
];
self
.write_read
([
CMD_FLASH_SIZE
],
&
mut
data
)
?
;
Ok
(
u16
::
from_le_bytes
([
data
[
0
],
data
[
1
]]))
}
/// Force the currently running program to switch to bootloader mode
/// even when an application is present.
///
/// # Errors
/// This method may fail if the communication with the board fails.
fn
switch_to_bootloader
(
&
mut
self
)
->
Result
<
(),
Self
::
I2cError
>
{
self
.write
([
CMD_REBOOT_TO_BOOTLOADER
])
}
}
/// Commands available when the board is in controller mode.
#[cfg(feature
=
"controller"
)]
#[expect(clippy::missing_errors_doc)]
pub
trait
Controller
:
Device
{
/// Set the PWM frequency used to drive the motors in Hz, between
/// `1` and `100_000`.
///
/// # Errors
/// This method may fail if the communication with the board fails, or
/// if the frequency is out of the allowed range.
fn
set_pwm_frequency
(
&
mut
self
,
frequency_hz
:
u32
)
->
Result
<
(),
Self
::
I2cError
>
{
if
frequency_hz
>
100_000
{
return
Err
(
Error
::
InvalidFrequency
(
frequency_hz
));
...
...
@@ -112,6 +169,10 @@ pub trait Controller: Device {
self
.write
((
u32
::
from
(
CMD_PWM_FREQUENCY
)
+
(
frequency_hz
<<
8
))
.to_le_bytes
())
}
/// Get the PWM frequency used to drive the motors in Hz.
///
/// # Errors
/// This method may fail if the communication with the board fails.
fn
get_pwm_frequency
(
&
mut
self
)
->
Result
<
u32
,
Self
::
I2cError
>
{
let
mut
data
=
[
0
;
4
];
self
.write_read
([
CMD_PWM_FREQUENCY
],
&
mut
data
[
..
3
])
?
;
...
...
@@ -158,6 +219,11 @@ pub trait Controller: Device {
Ok
(
result
.into
())
}
/// Set the timeout after which motors will shut down if no
/// further I²C communication takes place.
///
/// # Errors
/// This method may fail if the communication with the board fails.
fn
set_motor_shutdown_timeout
(
&
mut
self
,
delay
:
Duration
)
->
Result
<
(),
Self
::
I2cError
>
{
let
timeout
=
delay
.as_millis
();
if
timeout
>
10_000
{
...
...
@@ -167,12 +233,23 @@ pub trait Controller: Device {
self
.write
([
CMD_MOTOR_SHUTDOWN_TIMEOUT
,
timeout
])
}
/// Retrieve the current motors shutdown timeout.
///
/// # Errors
/// This method may fail if the communication with the board fails.
fn
get_motor_shutdown_timeout
(
&
mut
self
)
->
Result
<
Duration
,
Self
::
I2cError
>
{
let
mut
delay
=
0
;
self
.write_read
([
CMD_MOTOR_SHUTDOWN_TIMEOUT
],
slice
::
from_mut
(
&
mut
delay
))
?
;
Ok
(
Duration
::
from_millis
(
u64
::
from
(
delay
)
*
100
))
}
/// Set the raw motor speed between -127 and 127 for each. `None` means
/// that the speed of the corresponding motor is not modified, unless
/// both sides use `None` in which case the motors will be put in standby
/// mode.
///
/// # Errors
/// This method may fail if the communication with the board fails.
#[allow(clippy::cast_sign_loss)]
fn
set_raw_speed
(
&
mut
self
,
left
:
Option
<
i8
>
,
right
:
Option
<
i8
>
)
->
Result
<
(),
Self
::
I2cError
>
{
let
(
left
,
right
)
=
(
left
.map
(|
v
|
v
as
u8
),
right
.map
(|
v
|
v
as
u8
));
...
...
@@ -183,6 +260,10 @@ pub trait Controller: Device {
])
}
/// Get the raw motor speed, or `None` if the motors are in standby mode.
///
/// # Errors
/// This method may fail if the communication with the board fails.
#[allow(clippy::cast_possible_wrap)]
fn
get_raw_speed
(
&
mut
self
)
->
Result
<
Option
<
(
i8
,
i8
)
>
,
Self
::
I2cError
>
{
let
mut
buf
=
[
0
;
2
];
...
...
@@ -190,6 +271,11 @@ pub trait Controller: Device {
Ok
((
buf
!=
[
0x80
,
0x80
])
.then_some
((
buf
[
0
]
as
i8
,
buf
[
1
]
as
i8
)))
}
/// Set the left and right motor speeds. The speed will be controlled
/// using the internal PID.
///
/// # Errors
/// This method may fail if the communication with the board fails.
fn
set_motor_speed
(
&
mut
self
,
left
:
i16
,
right
:
i16
)
->
Result
<
(),
Self
::
I2cError
>
{
let
mut
buf
=
[
CMD_CONTROLLED_MOTOR_SPEED
,
0
,
0
,
0
,
0
];
buf
[
1
..
3
]
.copy_from_slice
(
&
left
.to_le_bytes
());
...
...
@@ -197,6 +283,12 @@ pub trait Controller: Device {
self
.write
(
buf
)
}
/// Get the left and right motor speeds. If the motors are in
/// standby mode, or if a raw speed has been explicitly set,
/// this method will return `None`.
///
/// # Errors
/// This method may fail if the communication with the board fails.
fn
get_motor_speed
(
&
mut
self
)
->
Result
<
Option
<
(
i16
,
i16
)
>
,
Self
::
I2cError
>
{
let
mut
buf
=
[
0
;
4
];
self
.write_read
([
CMD_CONTROLLED_MOTOR_SPEED
],
&
mut
buf
)
?
;
...
...
@@ -208,6 +300,11 @@ pub trait Controller: Device {
}))
}
/// Retrive the number of left and right encoder ticks since the
/// last retrieval.
///
/// # Errors
/// This method may fail if the communication with the board fails.
fn
get_encoder_ticks
(
&
mut
self
)
->
Result
<
(
i16
,
i16
),
Self
::
I2cError
>
{
let
mut
buf
=
[
0
;
4
];
self
.write_read
([
CMD_ENCODER_TICKS
],
&
mut
buf
)
?
;
...
...
@@ -217,12 +314,20 @@ pub trait Controller: Device {
))
}
/// Get the current status of the board.
///
/// # Errors
/// This method may fail if the communication with the board fails.
fn
get_status
(
&
mut
self
)
->
Result
<
Status
,
Self
::
I2cError
>
{
let
mut
status
=
0
;
self
.write_read
([
CMD_STATUS
],
slice
::
from_mut
(
&
mut
status
))
?
;
Ok
(
Status
(
status
))
}
/// Retrieve the firmware features implemented on the controller.
///
/// # Errors
/// This method may fail if the communication with the board fails.
fn
firmware_features
(
&
mut
self
)
->
Result
<
FirmwareFeatures
,
Self
::
I2cError
>
{
let
mut
features
=
0
;
self
.write_read
([
CMD_FIRMWARE_FEATURES
],
slice
::
from_mut
(
&
mut
features
))
?
;
...
...
@@ -233,9 +338,14 @@ pub trait Controller: Device {
#[cfg(feature
=
"controller"
)]
impl
<
D
:
Device
>
Controller
for
D
{}
/// Commands available when the board is in bootloader mode.
#[cfg(feature
=
"bootloader"
)]
#[expect(clippy::missing_errors_doc)]
pub
trait
Bootloader
:
Device
{
/// Write command, data, and a xor of all data bytes. The xor
/// computation is returned.
///
/// # Errors
/// This method may fail if the communication with the board fails.
fn
write_xored
(
&
mut
self
,
command
:
u8
,
data
:
impl
AsRef
<
[
u8
]
>
)
->
Result
<
u8
,
Self
::
I2cError
>
{
let
data
=
data
.as_ref
();
let
data_len
=
data
.len
();
...
...
@@ -248,6 +358,10 @@ pub trait Bootloader: Device {
Ok
(
xor
)
}
/// Get the programming status of the bootloader.
///
/// # Errors
/// This method may fail if the communication with the board fails.
fn
get_programming_status
(
&
mut
self
)
->
Result
<
ProgrammingStatus
,
Self
::
I2cError
>
{
let
mut
buf
=
[
0
;
2
];
self
.write_read
([
CMD_PROGRAMMING_STATUS
],
&
mut
buf
)
?
;
...
...
@@ -257,38 +371,72 @@ pub trait Bootloader: Device {
})
}
/// Enter programming mode by sending the required sequence.
///
/// # Errors
/// This method may fail if the communication with the board fails.
fn
enter_programming_mode
(
&
mut
self
)
->
Result
<
(),
Self
::
I2cError
>
{
self
.write
([
CMD_CHANGE_PROGRAMMING_MODE
,
0x17
,
0x27
,
0x65
,
0x40
])
}
/// Leave the programming mode, and mark or not the application has having been
/// successfully written.
///
/// # Errors
/// This method may fail if the communication with the board fails.
fn
leave_programming_mode
(
&
mut
self
,
commit
:
bool
)
->
Result
<
(),
Self
::
I2cError
>
{
self
.write
([
CMD_CHANGE_PROGRAMMING_MODE
,
u8
::
from
(
commit
)])
}
/// Erase `count` application pages starting at `index`.
///
/// Return a duration for which is it advised to wait before sending other
/// operations.
///
/// # Errors
/// This method may fail if the communication with the board fails.
fn
erase_pages
(
&
mut
self
,
index
:
u8
,
count
:
u8
)
->
Result
<
Duration
,
Self
::
I2cError
>
{
self
.write_xored
(
CMD_ERASE_PAGES
,
[
index
,
count
])
?
;
Ok
(
Duration
::
from_millis
(
u64
::
from
(
count
+
1
)
*
30
))
}
/// Set the current programming address in the application space.
///
/// # Errors
/// This method may fail if the communication with the board fails.
fn
set_programming_address
(
&
mut
self
,
address
:
u32
)
->
Result
<
(),
Self
::
I2cError
>
{
self
.write_xored
(
CMD_SET_PROGRAMMING_ADDRESS
,
address
.to_le_bytes
())
?
;
Ok
(())
}
/// Program data starting at the current programming address.
/// The address will advance automatically.x
/// The checksum will be updated with a xor of the data.
///
/// # Errors
/// This method may fail if the communication with the board fails.
fn
program_data
(
&
mut
self
,
data
:
&
[
u8
],
checksum
:
&
mut
u8
)
->
Result
<
(),
Self
::
I2cError
>
{
let
xor
=
self
.write_xored
(
CMD_PROGRAM_DATA
,
data
)
?
;
*
checksum
^=
xor
;
Ok
(())
}
/// Read memory starting from the current programming address.
/// The address will advance automatically.
/// Zeroes will be returned for data outside the application memory.
///
/// # Errors
/// This method may fail if the communication with the board fails.
fn
read_memory
(
&
mut
self
)
->
Result
<
[
u8
;
8
],
Self
::
I2cError
>
{
let
mut
result
=
[
0
;
8
];
self
.write_read
([
CMD_READ_MEMORY
],
&
mut
result
)
?
;
Ok
(
result
)
}
/// Set the current value of the running checksum.
///
/// # Errors
/// This method may fail if the communication with the board fails.
fn
set_checksum
(
&
mut
self
,
checksum
:
u8
)
->
Result
<
(),
Self
::
I2cError
>
{
self
.write
([
CMD_SET_CHECKSUM
,
checksum
])
}
...
...
@@ -317,23 +465,27 @@ impl<I2c: embedded_hal::i2c::I2c> Device for I2c {
}
}
/// Controller status
#[cfg(feature
=
"controller"
)]
#[derive(Clone,
Copy)]
pub
struct
Status
(
u8
);
#[cfg(feature
=
"controller"
)]
impl
Status
{
/// Check if any of the motors are moving.
#[must_use]
pub
fn
is_moving
(
self
)
->
bool
{
self
.0
&
1
!=
0
}
/// Check if the speed is currently controlled by the internal PID process.
#[must_use]
pub
fn
is_controlled
(
self
)
->
bool
{
self
.0
&
2
!=
0
}
}
/// Bootloader programming status
#[cfg(feature
=
"bootloader"
)]
#[derive(Clone,
Copy,
Debug)]
pub
struct
ProgrammingStatus
{
...
...
@@ -343,45 +495,56 @@ pub struct ProgrammingStatus {
#[cfg(feature
=
"bootloader"
)]
impl
ProgrammingStatus
{
/// Check if the bootloader is in programming mode.
#[must_use]
pub
fn
programming_mode
(
self
)
->
bool
{
self
.status
&
1
!=
0
}
/// Check if an error has been detected since the last time the
/// programming mode has been entered.
#[must_use]
pub
fn
error_detected
(
self
)
->
bool
{
self
.status
&
2
!=
0
}
/// Check if a valid application is marked present.
#[must_use]
pub
fn
application_present
(
self
)
->
bool
{
self
.status
&
4
!=
0
}
/// Current running checksum value.
#[must_use]
pub
fn
checksum
(
self
)
->
u8
{
self
.checksum
}
}
/// Firmware features enabled in the controller.
#[cfg(feature
=
"controller"
)]
#[derive(Clone,
Copy)]
pub
struct
FirmwareFeatures
(
u8
);
#[cfg(feature
=
"controller"
)]
impl
FirmwareFeatures
{
/// Check if the controller has been compiled with bootloader
/// support (`true`), or if it is compiled as a standalone
/// application (`false`).
#[must_use]
pub
fn
has_bootloader_support
(
self
)
->
bool
{
self
.0
&
1
!=
0
}
}
/// Currently running program on the board.
pub
enum
WhoAmI
{
Bootloader
,
Controller
,
Other
(
u8
),
}
/// Decode identity code and continuation code into a brand name.
#[must_use]
pub
fn
brand_name
(
identity_code
:
u8
,
continuation_code
:
u8
)
->
Option
<&
'static
str
>
{
match
(
identity_code
,
continuation_code
)
{
...
...
@@ -391,6 +554,8 @@ pub fn brand_name(identity_code: u8, continuation_code: u8) -> Option<&'static s
}
}
/// Decode device id and revision id obtained from the IDCODE field of the DBGMCU register
/// into a density information, and a model variant.
#[must_use]
pub
fn
mcu_kind
(
dev_id
:
u16
,
rev_id
:
u16
)
->
(
&
'static
str
,
&
'static
str
)
{
match
(
dev_id
,
rev_id
)
{
...
...
@@ -413,3 +578,8 @@ pub fn mcu_kind(dev_id: u16, rev_id: u16) -> (&'static str, &'static str) {
_
=>
(
"?"
,
"?"
),
}
}
/// Number of bits by which the PID coefficients are scaled internally
/// by the microcontroller to make a fixed point numbers from an integral
/// number.
pub
const
FRACTIONAL_BITS
:
usize
=
8
;
This diff is collapsed.
Click to expand it.
Preview
0%
Loading
Try again
or
attach a new file
.
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Save comment
Cancel
Please
register
or
sign in
to comment