15 Commits

Author SHA1 Message Date
c682d43fe4 HAL update 2025-02-14 15:31:40 +01:00
1a83f932b5 Merge pull request 'document probe-rs' (#52) from document-probe-rs into main
Reviewed-on: #52
2025-02-13 18:48:47 +01:00
cdc4807686 document probe-rs 2025-02-13 18:48:12 +01:00
62a4123f82 Merge pull request 'minor improvments for embassy lib' (#51) from minor-improvements-embassy into main
Reviewed-on: #51
2025-02-13 18:41:15 +01:00
17f13fc4dc minor improvments for embassy lib 2025-02-13 18:27:05 +01:00
b4f1512463 docs patch 2 2025-02-13 15:47:50 +01:00
e9ec01fc60 Merge pull request 'doc fixes' (#48) from va108xx-embassy-doc-fix into main
Reviewed-on: #48
2025-02-13 15:38:50 +01:00
d57cd383cd doc fixes 2025-02-13 15:38:38 +01:00
df4e943f48 Merge pull request 'use released package versions' (#47) from use-released-package-versions into main
Reviewed-on: #47
2025-02-13 15:26:41 +01:00
93b67a4795 README update 2025-02-13 15:26:24 +01:00
a9f2e6dcee use released package versions 2025-02-13 15:25:01 +01:00
271c853df1 Merge pull request 'prepare va108xx-embassy release' (#46) from prepare-va108xx-embassy-release into main
Reviewed-on: #46
2025-02-13 15:22:36 +01:00
107189b166 prepare va108xx-embassy release 2025-02-13 15:22:18 +01:00
872944bebf Merge pull request 'prepare BSP release' (#45) from vorago-reb1-release into main
Reviewed-on: #45
2025-02-13 15:08:22 +01:00
d077bb6210 prepare BSP release 2025-02-13 15:06:02 +01:00
25 changed files with 256 additions and 411 deletions

View File

@ -4,10 +4,9 @@
# runner = "arm-none-eabi-gdb -q -x openocd.gdb"
# runner = "gdb-multiarch -q -x openocd.gdb"
# runner = "gdb -q -x openocd.gdb"
runner = "gdb-multiarch -q -x jlink.gdb"
# runner = "gdb-multiarch -q -x jlink.gdb"
# Probe-rs is currently problematic: https://github.com/probe-rs/probe-rs/issues/2567
# runner = "probe-rs run --chip VA108xx --chip-description-path ./scripts/VA108xx_Series.yaml"
runner = "probe-rs run --chip VA108xx_RAM --protocol jtag"
# runner = ["probe-rs", "run", "--chip", "$CHIP", "--log-format", "{L} {s}"]
rustflags = [

View File

@ -14,6 +14,8 @@ This workspace contains the following released crates:
crate containing basic low-level register definition.
- The [`va108xx-hal`](https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/src/branch/main/va108xx-hal)
HAL crate containing higher-level abstractions on top of the PAC register crate.
- The [`va108xx-embassy`](https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/src/branch/main/va108xx-embassy)
crate containing support for running the embassy-rs RTOS.
- The [`vorago-reb1`](https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/src/branch/main/vorago-reb1)
BSP crate containing support for the REB1 development board.
@ -58,14 +60,56 @@ You can then adapt the files in `.vscode` to your needs.
You can use CLI or VS Code for flashing, running and debugging. In any case, take
care of installing the pre-requisites first.
### Pre-Requisites
### Using CLI with probe-rs
Install [probe-rs](https://probe.rs/docs/getting-started/installation/) first.
You can use `probe-rs` to run the software and display RTT log output. However, debugging does not
work yet.
After installation, you can run the following command
```sh
probe-rs run --chip VA108xx_RAM --protocol jtag target/thumbv6m-none-eabi/debug/examples/blinky
```
to flash and run the blinky program on the RAM. There is also a `VA108xx` chip target
available for persistent flashing.
Runner configuration avilable in the `.cargo/def-config.toml` file to use `probe-rs` for
convenience.
### Using VS Code
Assuming a working debug connection to your VA108xx board, you can debug using VS Code with
the [`Cortex-Debug` plugin](https://marketplace.visualstudio.com/items?itemName=marus25.cortex-debug).
Please make sure that [`objdump-multiarch` and `nm-multiarch`](https://forums.raspberrypi.com/viewtopic.php?t=333146)
are installed as well.
Some sample configuration files for VS code were provided and can be used by running
`cp -rT vscode .vscode` like specified above. After that, you can use `Run and Debug`
to automatically rebuild and flash your application.
If you would like to use a custom GDB application, you can specify the gdb binary in the following
configuration variables in your `settings.json`:
- `"cortex-debug.gdbPath"`
- `"cortex-debug.gdbPath.linux"`
- `"cortex-debug.gdbPath.windows"`
- `"cortex-debug.gdbPath.osx"`
The provided VS Code configurations also provide an integrated RTT logger, which you can access
via the terminal at `RTT Ch:0 console`. In order for the RTT block address detection to
work properly, `objdump-multiarch` and `nm-multiarch` need to be installed.
### Using CLI with GDB and Segger J-Link Tools
Install the following two tools first:
1. [SEGGER J-Link tools](https://www.segger.com/downloads/jlink/) installed
2. [gdb-multiarch](https://packages.debian.org/sid/gdb-multiarch) or similar
cross-architecture debugger installed. All commands here assume `gdb-multiarch`.
### Using CLI
You can build the blinky example application with the following command
```sh
@ -99,25 +143,8 @@ runner = "gdb-multiarch -q -x jlink/jlink.gdb"
After that, you can simply use `cargo run --example blinky` to flash the blinky
example.
### Using VS Code
### Using the RTT Viewer
Assuming a working debug connection to your VA108xx board, you can debug using VS Code with
the [`Cortex-Debug` plugin](https://marketplace.visualstudio.com/items?itemName=marus25.cortex-debug).
Please make sure that [`objdump-multiarch` and `nm-multiarch`](https://forums.raspberrypi.com/viewtopic.php?t=333146)
are installed as well.
Some sample configuration files for VS code were provided and can be used by running
`cp -rT vscode .vscode` like specified above. After that, you can use `Run and Debug`
to automatically rebuild and flash your application.
If you would like to use a custom GDB application, you can specify the gdb binary in the following
configuration variables in your `settings.json`:
- `"cortex-debug.gdbPath"`
- `"cortex-debug.gdbPath.linux"`
- `"cortex-debug.gdbPath.windows"`
- `"cortex-debug.gdbPath.osx"`
The provided VS Code configurations also provide an integrated RTT logger, which you can access
via the terminal at `RTT Ch:0 console`. In order for the RTT block address detection to
work properly, `objdump-multiarch` and `nm-multiarch` need to be installed.
The Segger RTT viewer can be used to display log messages received from the target. The base
address for the RTT block placement is 0x10000000. It is recommended to use a search range of
0x1000 around that base address when using the RTT viewer.

View File

@ -122,14 +122,14 @@ fn main() -> ! {
}
TestCase::Pulse => {
let mut output_pulsed = pinsa.pa0.into_push_pull_output();
output_pulsed.pulse_mode(true, PinState::Low);
output_pulsed.configure_pulse_mode(true, PinState::Low);
rprintln!("Pulsing high 10 times..");
output_pulsed.set_low().unwrap();
for _ in 0..10 {
output_pulsed.set_high().unwrap();
cortex_m::asm::delay(25_000_000);
}
output_pulsed.pulse_mode(true, PinState::High);
output_pulsed.configure_pulse_mode(true, PinState::High);
rprintln!("Pulsing low 10 times..");
for _ in 0..10 {
output_pulsed.set_low().unwrap();
@ -137,15 +137,12 @@ fn main() -> ! {
}
}
TestCase::DelayGpio => {
let mut out_0 = pinsa
.pa0
.into_readable_push_pull_output()
.delay(true, false);
let mut out_1 = pinsa
.pa1
.into_readable_push_pull_output()
.delay(false, true);
let mut out_2 = pinsa.pa3.into_readable_push_pull_output().delay(true, true);
let mut out_0 = pinsa.pa0.into_readable_push_pull_output();
out_0.configure_delay(true, false);
let mut out_1 = pinsa.pa1.into_readable_push_pull_output();
out_1.configure_delay(false, true);
let mut out_2 = pinsa.pa3.into_readable_push_pull_output();
out_2.configure_delay(true, true);
for _ in 0..20 {
out_0.toggle().unwrap();
out_1.toggle().unwrap();

View File

@ -15,10 +15,12 @@ num_enum = { version = "0.7", default-features = false }
static_assertions = "1"
[dependencies.va108xx-hal]
path = "../va108xx-hal"
version = "0.9"
# path = "../va108xx-hal"
[dependencies.vorago-reb1]
path = "../vorago-reb1"
version = "0.7"
# path = "../vorago-reb1"
[features]
default = []

View File

@ -27,8 +27,8 @@ embassy-executor = { version = "0.7", features = [
"executor-interrupt"
]}
va108xx-hal = { path = "../../va108xx-hal" }
va108xx-embassy = { path = "../../va108xx-embassy", default-features = false }
va108xx-hal = "0.9"
va108xx-embassy = "0.1"
[features]
default = ["ticks-hz-1_000", "va108xx-embassy/irq-oc30-oc31"]

View File

@ -22,5 +22,5 @@ rtic-sync = { version = "1.3", features = ["defmt-03"] }
once_cell = {version = "1", default-features = false, features = ["critical-section"]}
ringbuf = { version = "0.4.7", default-features = false, features = ["portable-atomic"] }
va108xx-hal = { version = "0.9", path = "../../va108xx-hal" }
vorago-reb1 = { path = "../../vorago-reb1" }
va108xx-hal = "0.9"
vorago-reb1 = "0.7"

View File

@ -16,9 +16,8 @@ embedded-io = "0.6"
cortex-m-semihosting = "0.5.0"
[dependencies.va108xx-hal]
path = "../../va108xx-hal"
version = "0.9"
features = ["rt", "defmt"]
[dependencies.vorago-reb1]
path = "../../vorago-reb1"
version = "0.7"

View File

@ -29,7 +29,9 @@ rtic-monotonics = { version = "2", features = ["cortex-m-systick"] }
rtic-sync = {version = "1", features = ["defmt-03"]}
[dependencies.va108xx-hal]
path = "../va108xx-hal"
version = "0.9"
# path = "../va108xx-hal"
[dependencies.vorago-reb1]
path = "../vorago-reb1"
version = "0.7"
# path = "../vorago-reb1"

View File

@ -0,0 +1,17 @@
Change Log
=======
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](http://keepachangelog.com/)
and this project adheres to [Semantic Versioning](http://semver.org/).
## [unreleased]
## [v0.1.2] and [v0.1.1] 2025-02-13
Docs patch
## [v0.1.0] 2025-02-13
Initial release

View File

@ -1,11 +1,17 @@
[package]
name = "va108xx-embassy"
version = "0.1.0"
version = "0.1.2"
edition = "2021"
authors = ["Robin Mueller <muellerr@irs.uni-stuttgart.de>"]
description = "Embassy-rs support for the Vorago VA108xx family of microcontrollers"
homepage = "https://egit.irs.uni-stuttgart.de/rust/va108xx-rs"
repository = "https://egit.irs.uni-stuttgart.de/rust/va108xx-rs"
license = "Apache-2.0"
keywords = ["no-std", "hal", "cortex-m", "vorago", "va108xx"]
categories = ["aerospace", "embedded", "no-std", "hardware-support"]
[dependencies]
critical-section = "1"
portable-atomic = { version = "1", features = ["unsafe-assume-single-core"]}
embassy-sync = "0.6"
embassy-executor = "0.7"
@ -14,8 +20,12 @@ embassy-time-queue-utils = "0.1"
once_cell = { version = "1", default-features = false, features = ["critical-section"] }
[dependencies.va108xx-hal]
path = "../va108xx-hal"
va108xx-hal = "0.9"
[target.'cfg(all(target_arch = "arm", target_os = "none"))'.dependencies]
portable-atomic = { version = "1", features = ["unsafe-assume-single-core"] }
[target.'cfg(not(all(target_arch = "arm", target_os = "none")))'.dependencies]
portable-atomic = "1"
[features]
default = ["irq-oc30-oc31"]
@ -25,3 +35,6 @@ irqs-in-lib = []
irq-oc28-oc29 = ["irqs-in-lib"]
irq-oc29-oc30 = ["irqs-in-lib"]
irq-oc30-oc31 = ["irqs-in-lib"]
[package.metadata.docs.rs]
rustdoc-args = ["--generate-link-to-definition"]

3
va108xx-embassy/docs.sh Executable file
View File

@ -0,0 +1,3 @@
#!/bin/bash
export RUSTDOCFLAGS="--cfg docsrs --generate-link-to-definition -Z unstable-options"
cargo +nightly doc --open

View File

@ -23,17 +23,17 @@
//!
//! You can disable the default features and then specify one of the features above to use the
//! documented combination of IRQs. It is also possible to specify custom IRQs by importing and
//! using the [embassy::embassy_time_driver_irqs] macro to declare the IRQ handlers in the
//! using the [embassy_time_driver_irqs] macro to declare the IRQ handlers in the
//! application code. If this is done, [embassy::init_with_custom_irqs] must be used
//! method to pass the IRQ numbers to the library.
//!
//! ## Examples
//!
//! [embassy example project](https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/src/branch/main/examples/embassy)
//! [embassy example projects](https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/src/branch/main/examples/embassy)
#![no_std]
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
use core::cell::{Cell, RefCell};
use critical_section::CriticalSection;
use embassy_sync::blocking_mutex::CriticalSectionMutex as Mutex;
use critical_section::{CriticalSection, Mutex};
use portable_atomic::{AtomicU32, Ordering};
use embassy_time_driver::{time_driver_impl, Driver, TICK_HZ};
@ -45,7 +45,7 @@ use va108xx_hal::{
clock::enable_peripheral_clock,
enable_nvic_interrupt, pac,
prelude::*,
timer::{enable_tim_clk, get_tim_raw, TimRegInterface},
timer::{enable_tim_clk, get_tim_raw, TimRegInterface, ValidTim},
PeripheralSelect,
};
@ -62,7 +62,7 @@ time_driver_impl!(
/// the feature flags specified. However, the macro is exported to allow users to specify the
/// interrupt handlers themselves.
///
/// Please note that you have to explicitely import the [va108xx_hal::pac::interrupt]
/// Please note that you have to explicitely import the [macro@va108xx_hal::pac::interrupt]
/// macro in the application code in case this macro is used there.
#[macro_export]
macro_rules! embassy_time_driver_irqs {
@ -115,12 +115,15 @@ pub mod embassy {
/// This has to be called once at initialization time to initiate the time driver for
/// embassy.
#[cfg(feature = "irqs-in-lib")]
pub unsafe fn init(
pub unsafe fn init<
TimekeeperTim: TimRegInterface + ValidTim,
AlarmTim: TimRegInterface + ValidTim,
>(
syscfg: &mut pac::Sysconfig,
irqsel: &pac::Irqsel,
sysclk: impl Into<Hertz>,
timekeeper_tim: impl TimRegInterface,
alarm_tim: impl TimRegInterface,
timekeeper_tim: TimekeeperTim,
alarm_tim: AlarmTim,
) {
TIME_DRIVER.init(
syscfg,
@ -139,12 +142,15 @@ pub mod embassy {
///
/// This has to be called once at initialization time to initiate the time driver for
/// embassy.
pub unsafe fn init_with_custom_irqs(
pub unsafe fn init_with_custom_irqs<
TimekeeperTim: TimRegInterface + ValidTim,
AlarmTim: TimRegInterface + ValidTim,
>(
syscfg: &mut pac::Sysconfig,
irqsel: &pac::Irqsel,
sysclk: impl Into<Hertz>,
timekeeper_tim: impl TimRegInterface,
alarm_tim: impl TimRegInterface,
timekeeper_tim: TimekeeperTim,
alarm_tim: AlarmTim,
timekeeper_irq: pac::Interrupt,
alarm_irq: pac::Interrupt,
) {
@ -187,21 +193,21 @@ pub struct TimerDriver {
impl TimerDriver {
#[allow(clippy::too_many_arguments)]
fn init(
fn init<TimekeeperTim: TimRegInterface + ValidTim, AlarmTim: TimRegInterface + ValidTim>(
&self,
syscfg: &mut pac::Sysconfig,
irqsel: &pac::Irqsel,
sysclk: impl Into<Hertz>,
timekeeper_tim: impl TimRegInterface,
alarm_tim: impl TimRegInterface,
timekeeper_tim: TimekeeperTim,
alarm_tim: AlarmTim,
timekeeper_irq: pac::Interrupt,
alarm_irq: pac::Interrupt,
) {
if ALARM_TIM.get().is_some() {
if ALARM_TIM.get().is_some() || TIMEKEEPER_TIM.get().is_some() {
return;
}
ALARM_TIM.set(alarm_tim.tim_id()).ok();
TIMEKEEPER_TIM.set(timekeeper_tim.tim_id()).ok();
ALARM_TIM.set(AlarmTim::TIM_ID).ok();
TIMEKEEPER_TIM.set(TimekeeperTim::TIM_ID).ok();
enable_peripheral_clock(syscfg, PeripheralSelect::Irqsel);
enable_tim_clk(syscfg, timekeeper_tim.tim_id());
let timekeeper_reg_block = timekeeper_tim.reg_block();

View File

@ -8,6 +8,16 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
## [unreleased]
## [v0.10.0]
## Added
- A lot of missing `defmt::Format` implementations.
## Changed
- Missing GPIO API replacements from `x` to `configure_x`
## [v0.9.0]
## Fixed

View File

@ -42,7 +42,7 @@ portable-atomic = "1"
[features]
default = ["rt"]
rt = ["va108xx/rt"]
defmt = ["dep:defmt", "fugit/defmt"]
defmt = ["dep:defmt", "fugit/defmt", "embedded-hal/defmt-03"]
[package.metadata.docs.rs]
all-features = true

View File

@ -115,7 +115,7 @@ impl InputPinFuture {
EDGE_DETECTION[pin_id_to_offset(pin.id())]
.store(false, core::sync::atomic::Ordering::Relaxed);
pin.interrupt_edge(
pin.configure_edge_interrupt(
edge,
InterruptConfig::new(irq, true, true),
Some(sys_cfg),

View File

@ -181,6 +181,7 @@ pub struct DynPinId {
/// This `struct` takes ownership of a [`DynPinId`] and provides an API to
/// access the corresponding regsiters.
#[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub(crate) struct DynRegisters(DynPinId);
// [`DynRegisters`] takes ownership of the [`DynPinId`], and [`DynPin`]
@ -392,11 +393,15 @@ impl DynPin {
/// - Delay 2: 2
/// - Delay 1 + Delay 2: 3
#[inline]
pub fn delay(self, delay_1: bool, delay_2: bool) -> Result<Self, InvalidPinTypeError> {
pub fn configure_delay(
&mut self,
delay_1: bool,
delay_2: bool,
) -> Result<(), InvalidPinTypeError> {
match self.mode {
DynPinMode::Output(_) => {
self.regs.delay(delay_1, delay_2);
Ok(self)
self.regs.configure_delay(delay_1, delay_2);
Ok(())
}
_ => Err(InvalidPinTypeError(self.mode)),
}
@ -406,7 +411,7 @@ impl DynPin {
/// When configured for pulse mode, a given pin will set the non-default state for exactly
/// one clock cycle before returning to the configured default state
#[inline]
pub fn pulse_mode(
pub fn configure_pulse_mode(
&mut self,
enable: bool,
default_state: PinState,
@ -422,14 +427,14 @@ impl DynPin {
/// See p.37 and p.38 of the programmers guide for more information.
#[inline]
pub fn filter_type(
pub fn configure_filter_type(
&mut self,
filter: FilterType,
clksel: FilterClkSel,
) -> Result<(), InvalidPinTypeError> {
match self.mode {
DynPinMode::Input(_) => {
self.regs.filter_type(filter, clksel);
self.regs.configure_filter_type(filter, clksel);
Ok(())
}
_ => Err(InvalidPinTypeError(self.mode)),
@ -437,7 +442,7 @@ impl DynPin {
}
#[inline]
pub fn interrupt_edge(
pub fn configure_edge_interrupt(
&mut self,
edge_type: InterruptEdge,
irq_cfg: InterruptConfig,
@ -446,7 +451,7 @@ impl DynPin {
) -> Result<(), InvalidPinTypeError> {
match self.mode {
DynPinMode::Input(_) | DynPinMode::Output(_) => {
self.regs.interrupt_edge(edge_type);
self.regs.configure_edge_interrupt(edge_type);
self.irq_enb(irq_cfg, syscfg, irqsel);
Ok(())
}
@ -455,7 +460,7 @@ impl DynPin {
}
#[inline]
pub fn interrupt_level(
pub fn configure_level_interrupt(
&mut self,
level_type: InterruptLevel,
irq_cfg: InterruptConfig,
@ -464,7 +469,7 @@ impl DynPin {
) -> Result<(), InvalidPinTypeError> {
match self.mode {
DynPinMode::Input(_) | DynPinMode::Output(_) => {
self.regs.interrupt_level(level_type);
self.regs.configure_level_interrupt(level_type);
self.irq_enb(irq_cfg, syscfg, irqsel);
Ok(())
}

View File

@ -89,6 +89,7 @@ use paste::paste;
//==================================================================================================
#[derive(Debug, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum InterruptEdge {
HighToLow,
LowToHigh,
@ -96,12 +97,14 @@ pub enum InterruptEdge {
}
#[derive(Debug, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum InterruptLevel {
Low = 0,
High = 1,
}
#[derive(Debug, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum PinState {
Low = 0,
High = 1,
@ -353,6 +356,7 @@ impl<I: PinId, M: PinMode> Pin<I, M> {
}
}
#[inline]
pub fn id(&self) -> DynPinId {
self.inner.id()
}
@ -482,11 +486,6 @@ impl<I: PinId, M: PinMode> Pin<I, M> {
self.inner.regs.write_pin(false)
}
#[inline]
pub(crate) fn _toggle_with_toggle_reg(&mut self) {
self.inner.regs.toggle();
}
#[inline]
pub(crate) fn _is_low(&self) -> bool {
!self.inner.regs.read_pin()
@ -599,7 +598,7 @@ impl<I: PinId, C: InputConfig> Pin<I, Input<C>> {
syscfg: Option<&mut Sysconfig>,
irqsel: Option<&mut Irqsel>,
) {
self.inner.regs.interrupt_edge(edge_type);
self.inner.regs.configure_edge_interrupt(edge_type);
self.irq_enb(irq_cfg, syscfg, irqsel);
}
@ -610,7 +609,7 @@ impl<I: PinId, C: InputConfig> Pin<I, Input<C>> {
syscfg: Option<&mut Sysconfig>,
irqsel: Option<&mut Irqsel>,
) {
self.inner.regs.interrupt_level(level_type);
self.inner.regs.configure_level_interrupt(level_type);
self.irq_enb(irq_cfg, syscfg, irqsel);
}
}
@ -622,23 +621,34 @@ impl<I: PinId, C: OutputConfig> Pin<I, Output<C>> {
/// - Delay 2: 2
/// - Delay 1 + Delay 2: 3
#[inline]
pub fn delay(self, delay_1: bool, delay_2: bool) -> Self {
self.inner.regs.delay(delay_1, delay_2);
self
pub fn configure_delay(&mut self, delay_1: bool, delay_2: bool) {
self.inner.regs.configure_delay(delay_1, delay_2);
}
#[inline]
pub fn toggle_with_toggle_reg(&mut self) {
self._toggle_with_toggle_reg()
self.inner.regs.toggle()
}
#[deprecated(
since = "0.9.0",
note = "Please use the `configure_pulse_mode` method instead"
)]
pub fn pulse_mode(&mut self, enable: bool, default_state: PinState) {
self.configure_pulse_mode(enable, default_state);
}
/// See p.52 of the programmers guide for more information.
/// When configured for pulse mode, a given pin will set the non-default state for exactly
/// one clock cycle before returning to the configured default state
pub fn pulse_mode(&mut self, enable: bool, default_state: PinState) {
pub fn configure_pulse_mode(&mut self, enable: bool, default_state: PinState) {
self.inner.regs.pulse_mode(enable, default_state);
}
#[deprecated(
since = "0.9.0",
note = "Please use the `configure_edge_interrupt` method instead"
)]
pub fn interrupt_edge(
&mut self,
edge_type: InterruptEdge,
@ -646,18 +656,43 @@ impl<I: PinId, C: OutputConfig> Pin<I, Output<C>> {
syscfg: Option<&mut Sysconfig>,
irqsel: Option<&mut Irqsel>,
) {
self.inner.regs.interrupt_edge(edge_type);
self.inner.regs.configure_edge_interrupt(edge_type);
self.irq_enb(irq_cfg, syscfg, irqsel);
}
pub fn interrupt_level(
pub fn configure_edge_interrupt(
&mut self,
edge_type: InterruptEdge,
irq_cfg: InterruptConfig,
syscfg: Option<&mut Sysconfig>,
irqsel: Option<&mut Irqsel>,
) {
self.inner.regs.configure_edge_interrupt(edge_type);
self.irq_enb(irq_cfg, syscfg, irqsel);
}
#[deprecated(
since = "0.9.0",
note = "Please use the `configure_level_interrupt` method instead"
)]
pub fn level_interrupt(
&mut self,
level_type: InterruptLevel,
irq_cfg: InterruptConfig,
syscfg: Option<&mut Sysconfig>,
irqsel: Option<&mut Irqsel>,
) {
self.inner.regs.interrupt_level(level_type);
self.configure_level_interrupt(level_type, irq_cfg, syscfg, irqsel);
}
pub fn configure_level_interrupt(
&mut self,
level_type: InterruptLevel,
irq_cfg: InterruptConfig,
syscfg: Option<&mut Sysconfig>,
irqsel: Option<&mut Irqsel>,
) {
self.inner.regs.configure_level_interrupt(level_type);
self.irq_enb(irq_cfg, syscfg, irqsel);
}
}
@ -666,7 +701,7 @@ impl<I: PinId, C: InputConfig> Pin<I, Input<C>> {
/// See p.37 and p.38 of the programmers guide for more information.
#[inline]
pub fn configure_filter_type(&mut self, filter: FilterType, clksel: FilterClkSel) {
self.inner.regs.filter_type(filter, clksel);
self.inner.regs.configure_filter_type(filter, clksel);
}
}

View File

@ -240,7 +240,7 @@ pub(super) unsafe trait RegisterInterface {
/// Only useful for interrupt pins. Configure whether to use edges or level as interrupt soure
/// When using edge mode, it is possible to generate interrupts on both edges as well
#[inline]
fn interrupt_edge(&mut self, edge_type: InterruptEdge) {
fn configure_edge_interrupt(&mut self, edge_type: InterruptEdge) {
unsafe {
self.port_reg()
.irq_sen()
@ -267,7 +267,7 @@ pub(super) unsafe trait RegisterInterface {
/// Configure which edge or level type triggers an interrupt
#[inline]
fn interrupt_level(&mut self, level: InterruptLevel) {
fn configure_level_interrupt(&mut self, level: InterruptLevel) {
unsafe {
self.port_reg()
.irq_sen()
@ -286,7 +286,7 @@ pub(super) unsafe trait RegisterInterface {
/// Only useful for input pins
#[inline]
fn filter_type(&mut self, filter: FilterType, clksel: FilterClkSel) {
fn configure_filter_type(&mut self, filter: FilterType, clksel: FilterClkSel) {
self.iocfg_port().modify(|_, w| {
// Safety: Only write to register for this Pin ID
unsafe {
@ -349,7 +349,7 @@ pub(super) unsafe trait RegisterInterface {
}
/// Only useful for output pins
fn delay(&self, delay_1: bool, delay_2: bool) {
fn configure_delay(&mut self, delay_1: bool, delay_2: bool) {
let portreg = self.port_reg();
unsafe {
if delay_1 {

View File

@ -36,8 +36,6 @@ pub struct InvalidTimingParamsError;
#[derive(Debug, PartialEq, Eq, thiserror::Error)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum Error {
//#[error("Invalid timing parameters")]
//InvalidTimingParams,
#[error("arbitration lost")]
ArbitrationLost,
#[error("nack address")]
@ -82,6 +80,7 @@ impl embedded_hal::i2c::Error for Error {
}
#[derive(Debug, PartialEq, Copy, Clone)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
enum I2cCmd {
Start = 0b00,
Stop = 0b10,
@ -252,6 +251,8 @@ impl Default for MasterConfig {
impl Sealed for MasterConfig {}
#[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct SlaveConfig {
pub tx_fe_mode: FifoEmptyMode,
pub rx_fe_mode: FifoEmptyMode,
@ -455,13 +456,6 @@ impl<I2c: Instance> I2cBase<I2c> {
}
}
// Unique mode to use the loopback functionality
// pub struct I2cLoopback<I2C> {
// i2c_base: I2cBase<I2C>,
// master_cfg: MasterConfig,
// slave_cfg: SlaveConfig,
// }
//==================================================================================================
// I2C Master
//==================================================================================================
@ -673,275 +667,6 @@ impl<I2c: Instance, Addr> I2cMaster<I2c, Addr> {
}
}
/*
macro_rules! i2c_master {
($($I2CX:path: ($i2cx:ident, $clk_enb:path),)+) => {
$(
impl<ADDR> I2cMaster<$I2CX, ADDR> {
pub fn $i2cx(
i2c: $I2CX,
cfg: MasterConfig,
sys_clk: impl Into<Hertz> + Copy,
speed_mode: I2cSpeed,
sys_cfg: Option<&mut pac::Sysconfig>,
) -> Self {
I2cMaster {
i2c_base: I2cBase::$i2cx(
i2c,
sys_clk,
speed_mode,
Some(&cfg),
None,
sys_cfg
),
_addr: PhantomData,
}
.enable_master()
}
#[inline]
pub fn cancel_transfer(&self) {
self.i2c_base
.i2c
.cmd()
.write(|w| unsafe { w.bits(I2cCmd::Cancel as u32) });
}
#[inline]
pub fn clear_tx_fifo(&self) {
self.i2c_base.i2c.fifo_clr().write(|w| w.txfifo().set_bit());
}
#[inline]
pub fn clear_rx_fifo(&self) {
self.i2c_base.i2c.fifo_clr().write(|w| w.rxfifo().set_bit());
}
#[inline]
pub fn enable_master(self) -> Self {
self.i2c_base.i2c.ctrl().modify(|_, w| w.enable().set_bit());
self
}
#[inline]
pub fn disable_master(self) -> Self {
self.i2c_base.i2c.ctrl().modify(|_, w| w.enable().clear_bit());
self
}
#[inline(always)]
fn load_fifo(&self, word: u8) {
self.i2c_base
.i2c
.data()
.write(|w| unsafe { w.bits(word as u32) });
}
#[inline(always)]
fn read_fifo(&self) -> u8 {
self.i2c_base.i2c.data().read().bits() as u8
}
fn error_handler_write(&mut self, init_cmd: &I2cCmd) {
self.clear_tx_fifo();
if *init_cmd == I2cCmd::Start {
self.i2c_base.stop_cmd()
}
}
fn write_base(
&mut self,
addr: I2cAddress,
init_cmd: I2cCmd,
bytes: impl IntoIterator<Item = u8>,
) -> Result<(), Error> {
let mut iter = bytes.into_iter();
// Load address
let (addr, addr_mode_bit) = I2cBase::<$I2CX>::unwrap_addr(addr);
self.i2c_base.i2c.address().write(|w| unsafe {
w.bits(I2cDirection::Send as u32 | (addr << 1) as u32 | addr_mode_bit)
});
self.i2c_base
.i2c
.cmd()
.write(|w| unsafe { w.bits(init_cmd as u32) });
let mut load_if_next_available = || {
if let Some(next_byte) = iter.next() {
self.load_fifo(next_byte);
}
};
loop {
let status_reader = self.i2c_base.i2c.status().read();
if status_reader.arblost().bit_is_set() {
self.error_handler_write(&init_cmd);
return Err(Error::ArbitrationLost);
} else if status_reader.nackaddr().bit_is_set() {
self.error_handler_write(&init_cmd);
return Err(Error::NackAddr);
} else if status_reader.nackdata().bit_is_set() {
self.error_handler_write(&init_cmd);
return Err(Error::NackData);
} else if status_reader.idle().bit_is_set() {
return Ok(());
} else {
while !status_reader.txnfull().bit_is_set() {
load_if_next_available();
}
}
}
}
fn write_from_buffer(
&mut self,
init_cmd: I2cCmd,
addr: I2cAddress,
output: &[u8],
) -> Result<(), Error> {
let len = output.len();
// It should theoretically possible to transfer larger data sizes by tracking
// the number of sent words and setting it to 0x7fe as soon as only that many
// bytes are remaining. However, large transfer like this are not common. This
// feature will therefore not be supported for now.
if len > 0x7fe {
return Err(Error::DataTooLarge);
}
// Load number of words
self.i2c_base
.i2c
.words()
.write(|w| unsafe { w.bits(len as u32) });
let mut bytes = output.iter();
// FIFO has a depth of 16. We load slightly above the trigger level
// but not all of it because the transaction might fail immediately
const FILL_DEPTH: usize = 12;
// load the FIFO
for _ in 0..core::cmp::min(FILL_DEPTH, len) {
self.load_fifo(*bytes.next().unwrap());
}
self.write_base(addr, init_cmd, output.iter().cloned())
}
fn read_internal(&mut self, addr: I2cAddress, buffer: &mut [u8]) -> Result<(), Error> {
let len = buffer.len();
// It should theoretically possible to transfer larger data sizes by tracking
// the number of sent words and setting it to 0x7fe as soon as only that many
// bytes are remaining. However, large transfer like this are not common. This
// feature will therefore not be supported for now.
if len > 0x7fe {
return Err(Error::DataTooLarge);
}
// Clear the receive FIFO
self.clear_rx_fifo();
// Load number of words
self.i2c_base
.i2c
.words()
.write(|w| unsafe { w.bits(len as u32) });
let (addr, addr_mode_bit) = match addr {
I2cAddress::Regular(addr) => (addr as u16, 0 << 15),
I2cAddress::TenBit(addr) => (addr, 1 << 15),
};
// Load address
self.i2c_base.i2c.address().write(|w| unsafe {
w.bits(I2cDirection::Read as u32 | (addr << 1) as u32 | addr_mode_bit)
});
let mut buf_iter = buffer.iter_mut();
let mut read_bytes = 0;
// Start receive transfer
self.i2c_base
.i2c
.cmd()
.write(|w| unsafe { w.bits(I2cCmd::StartWithStop as u32) });
let mut read_if_next_available = || {
if let Some(next_byte) = buf_iter.next() {
*next_byte = self.read_fifo();
}
};
loop {
let status_reader = self.i2c_base.i2c.status().read();
if status_reader.arblost().bit_is_set() {
self.clear_rx_fifo();
return Err(Error::ArbitrationLost);
} else if status_reader.nackaddr().bit_is_set() {
self.clear_rx_fifo();
return Err(Error::NackAddr);
} else if status_reader.idle().bit_is_set() {
if read_bytes != len {
return Err(Error::InsufficientDataReceived);
}
return Ok(());
} else if status_reader.rxnempty().bit_is_set() {
read_if_next_available();
read_bytes += 1;
}
}
}
}
//======================================================================================
// Embedded HAL I2C implementations
//======================================================================================
impl embedded_hal::i2c::ErrorType for I2cMaster<$I2CX, SevenBitAddress> {
type Error = Error;
}
impl embedded_hal::i2c::I2c for I2cMaster<$I2CX, SevenBitAddress> {
fn transaction(
&mut self,
address: SevenBitAddress,
operations: &mut [Operation<'_>],
) -> Result<(), Self::Error> {
for operation in operations {
match operation {
Operation::Read(buf) => self.read_internal(I2cAddress::Regular(address), buf)?,
Operation::Write(buf) => self.write_from_buffer(
I2cCmd::StartWithStop,
I2cAddress::Regular(address),
buf,
)?,
}
}
Ok(())
}
}
impl embedded_hal::i2c::ErrorType for I2cMaster<$I2CX, TenBitAddress> {
type Error = Error;
}
impl embedded_hal::i2c::I2c<TenBitAddress> for I2cMaster<$I2CX, TenBitAddress> {
fn transaction(
&mut self,
address: TenBitAddress,
operations: &mut [Operation<'_>],
) -> Result<(), Self::Error> {
for operation in operations {
match operation {
Operation::Read(buf) => self.read_internal(I2cAddress::TenBit(address), buf)?,
Operation::Write(buf) => self.write_from_buffer(
I2cCmd::StartWithStop,
I2cAddress::TenBit(address),
buf,
)?,
}
}
Ok(())
}
}
)+
}
}
i2c_master!(
pac::I2ca: (i2ca, PeripheralClocks::I2c0),
pac::I2cb: (i2cb, PeripheralClocks::I2c1),
);
*/
//======================================================================================
// Embedded HAL I2C implementations
//======================================================================================

View File

@ -31,7 +31,7 @@ pub enum PortSel {
PortB,
}
#[derive(Copy, Clone, PartialEq, Eq)]
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum PeripheralSelect {
PortA = 0,
@ -54,6 +54,7 @@ pub enum PeripheralSelect {
/// Cortex-M0 NVIC. Both are generally necessary for IRQs to work, but the user might want to
/// perform those steps themselves.
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct InterruptConfig {
/// Interrupt target vector. Should always be set, might be required for disabling IRQs
pub id: pac::Interrupt,

View File

@ -33,6 +33,7 @@ use embedded_hal::spi::{Mode, MODE_0};
const FILL_DEPTH: usize = 12;
pub const BMSTART_BMSTOP_MASK: u32 = 1 << 31;
pub const BMSKIPDATA_MASK: u32 = 1 << 30;
pub const DEFAULT_CLK_DIV: u16 = 2;
@ -288,6 +289,7 @@ pub trait TransferConfigProvider {
/// This struct contains all configuration parameter which are transfer specific
/// and might change for transfers to different SPI slaves
#[derive(Copy, Clone, Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct TransferConfigWithHwcs<HwCs> {
pub hw_cs: Option<HwCs>,
pub cfg: TransferConfig,
@ -296,6 +298,7 @@ pub struct TransferConfigWithHwcs<HwCs> {
/// Type erased variant of the transfer configuration. This is required to avoid generics in
/// the SPI constructor.
#[derive(Copy, Clone, Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct TransferConfig {
pub clk_cfg: Option<SpiClkConfig>,
pub mode: Option<Mode>,
@ -383,6 +386,8 @@ impl<HwCs: HwCsProvider> TransferConfigProvider for TransferConfigWithHwcs<HwCs>
}
/// Configuration options for the whole SPI bus. See Programmer Guide p.92 for more details
#[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct SpiConfig {
clk: SpiClkConfig,
// SPI mode configuration
@ -532,6 +537,7 @@ pub struct Spi<SpiInstance, Pins, Word = u8> {
pins: Pins,
}
#[inline(always)]
pub fn mode_to_cpo_cph_bit(mode: embedded_hal::spi::Mode) -> (bool, bool) {
match mode {
embedded_hal::spi::MODE_0 => (false, false),
@ -575,6 +581,7 @@ impl SpiClkConfig {
}
#[derive(Debug, thiserror::Error)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum SpiClkConfigError {
#[error("division by zero")]
DivIsZero,

View File

@ -8,6 +8,12 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
## [unreleased]
## [v0.7.0] 2025-02-13
- Bumped `va108xx-hal` dependency to 0.9
- Minor adjustments to `Button` API.
- `Button`, `Led` and `Leds` now simply wrap a type using a tuple struct.
## [v0.6.0] 2024-09-30
- Added M95M01 EEPROM module/API

View File

@ -1,6 +1,6 @@
[package]
name = "vorago-reb1"
version = "0.6.0"
version = "0.7.0"
authors = ["Robin Mueller <muellerr@irs.uni-stuttgart.de>"]
edition = "2021"
description = "Board Support Crate for the Vorago REB1 development board"
@ -19,7 +19,6 @@ bitfield = ">=0.17, <=0.18"
max116xx-10bit = "0.3"
[dependencies.va108xx-hal]
path = "../va108xx-hal"
version = "0.9"
features = ["rt"]

View File

@ -11,23 +11,21 @@ use va108xx_hal::{
};
#[derive(Debug)]
pub struct Button {
button: Pin<PA11, InputFloating>,
}
pub struct Button(pub Pin<PA11, InputFloating>);
impl Button {
pub fn new(pin: Pin<PA11, InputFloating>) -> Button {
Button { button: pin }
Button(pin)
}
#[inline]
pub fn pressed(&mut self) -> bool {
self.button.is_low().ok().unwrap()
self.0.is_low().ok().unwrap()
}
#[inline]
pub fn released(&mut self) -> bool {
self.button.is_high().ok().unwrap()
self.0.is_high().ok().unwrap()
}
/// Configures an IRQ on edge.
@ -38,7 +36,7 @@ impl Button {
syscfg: Option<&mut pac::Sysconfig>,
irqsel: Option<&mut pac::Irqsel>,
) {
self.button
self.0
.configure_edge_interrupt(edge_type, irq_cfg, syscfg, irqsel);
}
@ -50,7 +48,7 @@ impl Button {
syscfg: Option<&mut pac::Sysconfig>,
irqsel: Option<&mut pac::Irqsel>,
) {
self.button
self.0
.configure_level_interrupt(level, irq_cfg, syscfg, irqsel);
}
@ -59,6 +57,6 @@ impl Button {
/// Please note that you still have to set a clock divisor yourself using the
/// [`va108xx_hal::clock::set_clk_div_register`] function in order for this to work.
pub fn configure_filter_type(&mut self, filter: FilterType, clksel: FilterClkSel) {
self.button.configure_filter_type(filter, clksel);
self.0.configure_filter_type(filter, clksel);
}
}

View File

@ -15,15 +15,12 @@ pub type LD2 = Pin<PA10, PushPullOutput>;
pub type LD3 = Pin<PA7, PushPullOutput>;
pub type LD4 = Pin<PA6, PushPullOutput>;
pub struct Leds {
leds: [Led; 3],
}
#[derive(Debug)]
pub struct Leds(pub [Led; 3]);
impl Leds {
pub fn new(led_pin1: LD2, led_pin2: LD3, led_pin3: LD4) -> Leds {
Leds {
leds: [led_pin1.into(), led_pin2.into(), led_pin3.into()],
}
Leds([led_pin1.into(), led_pin2.into(), led_pin3.into()])
}
}
@ -31,13 +28,13 @@ impl core::ops::Deref for Leds {
type Target = [Led];
fn deref(&self) -> &[Led] {
&self.leds
&self.0
}
}
impl core::ops::DerefMut for Leds {
fn deref_mut(&mut self) -> &mut [Led] {
&mut self.leds
&mut self.0
}
}
@ -45,28 +42,25 @@ impl core::ops::Index<usize> for Leds {
type Output = Led;
fn index(&self, i: usize) -> &Led {
&self.leds[i]
&self.0[i]
}
}
impl core::ops::IndexMut<usize> for Leds {
fn index_mut(&mut self, i: usize) -> &mut Led {
&mut self.leds[i]
&mut self.0[i]
}
}
pub struct Led {
pin: DynPin,
}
#[derive(Debug)]
pub struct Led(pub DynPin);
macro_rules! ctor {
($($ldx:ident),+) => {
$(
impl From<$ldx> for Led {
fn from(led: $ldx) -> Self {
Led {
pin: led.into()
}
Led(led.into())
}
}
)+
@ -79,18 +73,18 @@ impl Led {
/// Turns the LED off. Setting the pin high actually turns the LED off
#[inline]
pub fn off(&mut self) {
self.pin.set_high().ok();
self.0.set_high().ok();
}
/// Turns the LED on. Setting the pin low actually turns the LED on
#[inline]
pub fn on(&mut self) {
self.pin.set_low().ok();
self.0.set_low().ok();
}
/// Toggles the LED
#[inline]
pub fn toggle(&mut self) {
self.pin.toggle_with_toggle_reg().ok();
self.0.toggle_with_toggle_reg().ok();
}
}