27 Commits

Author SHA1 Message Date
ee383bbdfb update package
All checks were successful
Rust/va108xx-rs/pipeline/pr-main This commit looks good
- Add embassy example
- improve timer API
- restructure examples
- restructure and improve SPI
2024-09-20 10:33:03 +02:00
405cc089c3 update flasher script file
All checks were successful
Rust/va108xx-rs/pipeline/head This commit looks good
2024-07-11 11:52:56 +02:00
f48ee8231a another link correction
All checks were successful
Rust/va108xx-rs/pipeline/head This commit looks good
2024-07-04 18:55:30 +02:00
4fb19fe234 link fix
All checks were successful
Rust/va108xx-rs/pipeline/head This commit looks good
2024-07-04 18:54:37 +02:00
652af5cb3c README correction
All checks were successful
Rust/va108xx-rs/pipeline/head This commit looks good
2024-07-04 18:41:33 +02:00
6e231e2553 prepare next vorago-reb1 release
All checks were successful
Rust/va108xx-rs/pipeline/head This commit looks good
2024-07-04 18:40:26 +02:00
79b7d7b4c2 Merge pull request 'Improve UART CLK calculation' (#12) from improve-uart-clk-calc into main
All checks were successful
Rust/va108xx-rs/pipeline/head This commit looks good
Reviewed-on: #12
2024-07-04 18:33:08 +02:00
3196d74a34 doc correction
Some checks are pending
Rust/va108xx-rs/pipeline/head Build queued...
2024-07-04 18:32:30 +02:00
d7c27446e0 Merge remote-tracking branch 'origin/main' into improve-uart-clk-calc
Some checks are pending
Rust/va108xx-rs/pipeline/head Build started...
2024-07-04 18:27:17 +02:00
3d4e8477c1 improve UART clock calculation 2024-07-04 18:26:56 +02:00
e2e3cc7020 Merge pull request 'Improve HAL' (#11) from improve-hal into main
All checks were successful
Rust/va108xx-rs/pipeline/head This commit looks good
Reviewed-on: #11
2024-07-04 18:16:11 +02:00
4f15cd7a31 another small link fix
Some checks are pending
Rust/va108xx-rs/pipeline/pr-main Build queued...
2024-07-04 18:15:55 +02:00
3c8c455c6f Update and improve HAL library and docs
All checks were successful
Rust/va108xx-rs/pipeline/pr-main This commit looks good
2024-07-04 18:07:34 +02:00
abb78c2682 Merge pull request 'update doc build settings' (#10) from update-doc-settings into main
All checks were successful
Rust/va108xx-rs/pipeline/head This commit looks good
Reviewed-on: #10
2024-06-26 23:05:30 +02:00
51f21fee43 update doc build settings
All checks were successful
Rust/va108xx-rs/pipeline/head This commit looks good
2024-06-26 22:58:12 +02:00
6fb3b0544f Merge pull request 'readme fix' (#9) from readme-fix into main
Some checks failed
Rust/va108xx-rs/pipeline/head There was a failure building this commit
Reviewed-on: #9
2024-06-25 20:18:48 +02:00
e3996d9166 Merge branch 'main' into readme-fix 2024-06-25 20:18:39 +02:00
deee5f6f46 readme fix
All checks were successful
Rust/va108xx-rs/pipeline/head This commit looks good
2024-06-25 19:44:12 +02:00
5d6c7ebf5e Merge pull request 'doc fixes and improvements' (#8) from update-docs into main
All checks were successful
Rust/va108xx-rs/pipeline/head This commit looks good
Reviewed-on: #8
2024-06-25 11:02:54 +02:00
6b3cdf74cc Merge branch 'main' into update-docs
Some checks are pending
Rust/va108xx-rs/pipeline/pr-main Build queued...
2024-06-25 11:02:31 +02:00
f4f378ba4f doc fixes and improvements
All checks were successful
Rust/va108xx-rs/pipeline/head This commit looks good
2024-06-25 10:47:33 +02:00
4224b14545 Merge pull request 'update docs' (#7) from update-docs into main
Some checks failed
Rust/va108xx-rs/pipeline/head There was a failure building this commit
Reviewed-on: #7
2024-06-25 10:26:10 +02:00
e3cdb19a35 Merge branch 'main' into update-docs 2024-06-25 10:26:02 +02:00
95e72cfea6 update docs
Some checks are pending
Rust/va108xx-rs/pipeline/head Build queued...
2024-06-25 10:25:39 +02:00
6854703c5d link corrections
Some checks failed
Rust/va108xx-rs/pipeline/head There was a failure building this commit
2024-06-23 22:05:11 +02:00
edc03d9e6e Merge pull request 'prepare BSP release' (#6) from prep-bsp-release into main
Some checks failed
Rust/va108xx-rs/pipeline/head There was a failure building this commit
Reviewed-on: #6
2024-06-16 19:31:16 +02:00
e434f2e301 prepare BSP release
Some checks are pending
Rust/va108xx-rs/pipeline/head Build started...
2024-06-16 19:30:04 +02:00
62 changed files with 3458 additions and 1747 deletions

View File

@ -24,8 +24,7 @@ rustflags = [
# "-C", "link-arg=-Tdefmt.x", # "-C", "link-arg=-Tdefmt.x",
# Can be useful for debugging. # Can be useful for debugging.
"-Clink-args=-Map=app.map" # "-Clink-args=-Map=app.map"
] ]
[build] [build]

View File

@ -39,7 +39,9 @@ jobs:
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@nightly - uses: dtolnay/rust-toolchain@nightly
- run: cargo +nightly doc --all-features --config 'build.rustdocflags=["--cfg", "docs_rs"]' - run: RUSTDOCFLAGS="--cfg docsrs --generate-link-to-definition -Z unstable-options" cargo +nightly doc -p va108xx
- run: RUSTDOCFLAGS="--cfg docsrs --generate-link-to-definition -Z unstable-options" cargo +nightly doc -p va108xx-hal
- run: RUSTDOCFLAGS="--cfg docsrs --generate-link-to-definition -Z unstable-options" cargo +nightly doc -p vorago-reb1
clippy: clippy:
name: Clippy name: Clippy

View File

@ -5,7 +5,10 @@ members = [
"va108xx", "va108xx",
"va108xx-hal", "va108xx-hal",
"examples/simple", "examples/simple",
"examples/rtic",
"examples/embassy",
"board-tests", "board-tests",
"bootloader",
] ]
exclude = [ exclude = [
@ -17,7 +20,8 @@ codegen-units = 1
debug = 2 debug = 2
debug-assertions = true # <- debug-assertions = true # <-
incremental = false incremental = false
opt-level = 'z' # <- # This is problematic for stepping..
# opt-level = 'z' # <-
overflow-checks = true # <- overflow-checks = true # <-
# cargo build/run --release # cargo build/run --release
@ -29,3 +33,12 @@ incremental = false
lto = 'fat' lto = 'fat'
opt-level = 3 # <- opt-level = 3 # <-
overflow-checks = false # <- overflow-checks = false # <-
[profile.small]
inherits = "release"
codegen-units = 1
debug-assertions = false # <-
lto = true
opt-level = 'z' # <-
overflow-checks = false # <-
strip = true # Automatically strip symbols from the binary.

View File

@ -19,9 +19,13 @@ This workspace contains the following released crates:
It also contains the following helper crates: It also contains the following helper crates:
- The `board-tests` contains an application which can be used to test the libraries on the - The [`board-tests`](https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/src/branch/main/board-tests)
board. contains an application which can be used to test the libraries on the board.
- The `examples` crates contains various example applications for the HAL and the PAC. - The [`examples`](https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/src/branch/main/examples)
folder contains various example applications crates using the HAL and the PAC.
This folder also contains dedicated example applications using the
[`RTIC`](https://rtic.rs/2/book/en/) and [`embassy`](https://github.com/embassy-rs/embassy)
native Rust RTOSes.
## Using the `.cargo/config.toml` file ## Using the `.cargo/config.toml` file
@ -94,6 +98,8 @@ example.
Assuming a working debug connection to your VA108xx board, you can debug using VS Code with 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). 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 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` `cp -rT vscode .vscode` like specified above. After that, you can use `Run and Debug`
@ -106,3 +112,7 @@ configuration variables in your `settings.json`:
- `"cortex-debug.gdbPath.linux"` - `"cortex-debug.gdbPath.linux"`
- `"cortex-debug.gdbPath.windows"` - `"cortex-debug.gdbPath.windows"`
- `"cortex-debug.gdbPath.osx"` - `"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.

View File

@ -25,7 +25,9 @@ pipeline {
stage('Docs') { stage('Docs') {
steps { steps {
sh """ sh """
cargo +nightly doc --all-features --config 'build.rustdocflags=["--cfg", "docs_rs"]' RUSTDOCFLAGS="--cfg docsrs --generate-link-to-definition -Z unstable-options" cargo +nightly doc -p va108xx
RUSTDOCFLAGS="--cfg docsrs --generate-link-to-definition -Z unstable-options" cargo +nightly doc -p va108xx-hal
RUSTDOCFLAGS="--cfg docsrs --generate-link-to-definition -Z unstable-options" cargo +nightly doc -p vorago-reb1
""" """
} }
} }

View File

@ -4,10 +4,9 @@ version = "0.1.0"
edition = "2021" edition = "2021"
[dependencies] [dependencies]
cortex-m-rtic = "1"
panic-halt = "0.2"
cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] }
cortex-m-rt = "0.7" cortex-m-rt = "0.7"
panic-halt = "0.2"
rtt-target = "0.5" rtt-target = "0.5"
panic-rtt-target = "0.1.3" panic-rtt-target = "0.1.3"
embedded-hal = "1" embedded-hal = "1"
@ -15,7 +14,7 @@ embedded-hal-nb = "1"
embedded-io = "0.6" embedded-io = "0.6"
[dependencies.va108xx-hal] [dependencies.va108xx-hal]
version = "0.6" version = "0.7"
path = "../va108xx-hal" path = "../va108xx-hal"
features = ["rt"] features = ["rt"]

25
bootloader/Cargo.toml Normal file
View File

@ -0,0 +1,25 @@
[package]
name = "bootloader"
version = "0.1.0"
edition = "2021"
[dependencies]
cortex-m = "0.7"
cortex-m-rt = "0.7"
embedded-hal = "1"
embedded-hal-bus = "0.2"
dummy-pin = "1"
panic-rtt-target = { version = "0.1.3" }
panic-halt = { version = "0.2" }
rtt-target = { version = "0.5" }
crc = "3"
[dependencies.va108xx-hal]
path = "../va108xx-hal"
[dependencies.vorago-reb1]
path = "../vorago-reb1"
[features]
default = []
rtt-panic = []

10
bootloader/src/lib.rs Normal file
View File

@ -0,0 +1,10 @@
#![no_std]
use core::convert::Infallible;
/// Simple trait which makes swapping the NVM easier. NVMs only need to implement this interface.
pub trait NvmInterface {
fn write(&mut self, address: u32, data: &[u8]) -> Result<(), Infallible>;
fn read(&mut self, address: u32, buf: &mut [u8]) -> Result<(), Infallible>;
fn verify(&mut self, address: u32, data: &[u8]) -> Result<bool, Infallible>;
}

296
bootloader/src/main.rs Normal file
View File

@ -0,0 +1,296 @@
//! Vorago bootloader which can boot from two images.
#![no_main]
#![no_std]
use bootloader::NvmInterface;
use cortex_m_rt::entry;
use crc::{Crc, CRC_16_IBM_3740};
#[cfg(not(feature = "rtt-panic"))]
use panic_halt as _;
#[cfg(feature = "rtt-panic")]
use panic_rtt_target as _;
use rtt_target::{rprintln, rtt_init_print};
use va108xx_hal::{pac, time::Hertz};
use vorago_reb1::m95m01::M95M01;
// Useful for debugging and see what the bootloader is doing. Enabled currently, because
// the binary stays small enough.
const RTT_PRINTOUT: bool = true;
const DEBUG_PRINTOUTS: bool = false;
// Dangerous option! An image with this option set to true will flash itself from RAM directly
// into the NVM. This can be used as a recovery option from a direct RAM flash to fix the NVM
// boot process. Please note that this will flash an image which will also always perform the
// self-flash itself. It is recommended that you use a tool like probe-rs, Keil IDE, or a flash
// loader to boot a bootloader without this feature.
const FLASH_SELF: bool = false;
// Register definitions for Cortex-M0 SCB register.
pub const SCB_AIRCR_VECTKEY_POS: u32 = 16;
pub const SCB_AIRCR_VECTKEY_MSK: u32 = 0xFFFF << SCB_AIRCR_VECTKEY_POS;
pub const SCB_AIRCR_SYSRESETREQ_POS: u32 = 2;
pub const SCB_AIRCR_SYSRESETREQ_MSK: u32 = 1 << SCB_AIRCR_SYSRESETREQ_POS;
const CLOCK_FREQ: Hertz = Hertz::from_raw(50_000_000);
// Important bootloader addresses and offsets, vector table information.
const BOOTLOADER_START_ADDR: u32 = 0x0;
const BOOTLOADER_CRC_ADDR: u32 = BOOTLOADER_END_ADDR - 2;
// This is also the maximum size of the bootloader.
const BOOTLOADER_END_ADDR: u32 = 0x3000;
const APP_A_START_ADDR: u32 = 0x3000;
const APP_A_SIZE_ADDR: u32 = APP_A_END_ADDR - 8;
// Four bytes reserved, even when only 2 byte CRC is used. Leaves flexibility to switch to CRC32.
const APP_A_CRC_ADDR: u32 = APP_A_END_ADDR - 4;
pub const APP_A_END_ADDR: u32 = 0x11000;
// The actual size of the image which is relevant for CRC calculation.
const APP_B_START_ADDR: u32 = 0x11000;
// The actual size of the image which is relevant for CRC calculation.
const APP_B_SIZE_ADDR: u32 = APP_B_END_ADDR - 8;
// Four bytes reserved, even when only 2 byte CRC is used. Leaves flexibility to switch to CRC32.
const APP_B_CRC_ADDR: u32 = APP_B_END_ADDR - 4;
pub const APP_B_END_ADDR: u32 = 0x20000;
pub const APP_IMG_SZ: u32 = 0xE800;
pub const VECTOR_TABLE_OFFSET: u32 = 0x0;
pub const VECTOR_TABLE_LEN: u32 = 0xC0;
pub const RESET_VECTOR_OFFSET: u32 = 0x4;
const CRC_ALGO: Crc<u16> = Crc::<u16>::new(&CRC_16_IBM_3740);
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
enum AppSel {
A,
B,
}
pub struct NvmWrapper(pub M95M01);
// Newtype pattern. We could now more easily swap the used NVM type.
impl NvmInterface for NvmWrapper {
fn write(&mut self, address: u32, data: &[u8]) -> Result<(), core::convert::Infallible> {
self.0.write(address, data)
}
fn read(&mut self, address: u32, buf: &mut [u8]) -> Result<(), core::convert::Infallible> {
self.0.read(address, buf)
}
fn verify(&mut self, address: u32, data: &[u8]) -> Result<bool, core::convert::Infallible> {
self.0.verify(address, data)
}
}
#[entry]
fn main() -> ! {
if RTT_PRINTOUT {
rtt_init_print!();
rprintln!("-- VA108xx bootloader --");
}
let mut dp = pac::Peripherals::take().unwrap();
let cp = cortex_m::Peripherals::take().unwrap();
let mut nvm = M95M01::new(&mut dp.sysconfig, CLOCK_FREQ, dp.spic);
if FLASH_SELF {
let mut first_four_bytes: [u8; 4] = [0; 4];
read_four_bytes_at_addr_zero(&mut first_four_bytes);
let bootloader_data = {
unsafe {
&*core::ptr::slice_from_raw_parts(
(BOOTLOADER_START_ADDR + 4) as *const u8,
(BOOTLOADER_END_ADDR - BOOTLOADER_START_ADDR - 6) as usize,
)
}
};
let mut digest = CRC_ALGO.digest();
digest.update(&first_four_bytes);
digest.update(bootloader_data);
let bootloader_crc = digest.finalize();
nvm.write(0x0, &first_four_bytes)
.expect("writing to NVM failed");
nvm.write(0x4, bootloader_data)
.expect("writing to NVM failed");
if let Err(e) = nvm.verify(0x0, &first_four_bytes) {
if RTT_PRINTOUT {
rprintln!("verification of self-flash to NVM failed: {:?}", e);
}
}
if let Err(e) = nvm.verify(0x4, bootloader_data) {
if RTT_PRINTOUT {
rprintln!("verification of self-flash to NVM failed: {:?}", e);
}
}
nvm.write(BOOTLOADER_CRC_ADDR, &bootloader_crc.to_be_bytes())
.expect("writing CRC failed");
if let Err(e) = nvm.verify(BOOTLOADER_CRC_ADDR, &bootloader_crc.to_be_bytes()) {
if RTT_PRINTOUT {
rprintln!(
"error: CRC verification for bootloader self-flash failed: {:?}",
e
);
}
}
}
let mut nvm = NvmWrapper(nvm);
// Check bootloader's CRC (and write it if blank)
check_own_crc(&dp.sysconfig, &cp, &mut nvm);
if check_app_crc(AppSel::A) {
boot_app(&dp.sysconfig, &cp, AppSel::A)
} else if check_app_crc(AppSel::B) {
boot_app(&dp.sysconfig, &cp, AppSel::B)
} else {
if DEBUG_PRINTOUTS && RTT_PRINTOUT {
rprintln!("both images corrupt! booting image A");
}
// TODO: Shift a CCSDS packet out to inform host/OBC about image corruption.
// Both images seem to be corrupt. Boot default image A.
boot_app(&dp.sysconfig, &cp, AppSel::A)
}
}
fn check_own_crc(sysconfig: &pac::Sysconfig, cp: &cortex_m::Peripherals, nvm: &mut NvmWrapper) {
let crc_exp = unsafe { (BOOTLOADER_CRC_ADDR as *const u16).read_unaligned().to_be() };
// I'd prefer to use [core::slice::from_raw_parts], but that is problematic
// because the address of the bootloader is 0x0, so the NULL check fails and the functions
// panics.
let mut first_four_bytes: [u8; 4] = [0; 4];
read_four_bytes_at_addr_zero(&mut first_four_bytes);
let mut digest = CRC_ALGO.digest();
digest.update(&first_four_bytes);
digest.update(unsafe {
&*core::ptr::slice_from_raw_parts(
(BOOTLOADER_START_ADDR + 4) as *const u8,
(BOOTLOADER_END_ADDR - BOOTLOADER_START_ADDR - 6) as usize,
)
});
let crc_calc = digest.finalize();
if crc_exp == 0x0000 || crc_exp == 0xffff {
if DEBUG_PRINTOUTS && RTT_PRINTOUT {
rprintln!("BL CRC blank - prog new CRC");
}
// Blank CRC, write it to NVM.
nvm.write(BOOTLOADER_CRC_ADDR, &crc_calc.to_be_bytes())
.expect("writing CRC failed");
// The Vorago bootloader resets here. I am not sure why this is done but I think it is
// necessary because somehow the boot will not work if we just continue as usual.
// cortex_m::peripheral::SCB::sys_reset();
} else if crc_exp != crc_calc {
// Bootloader is corrupted. Try to run App A.
if DEBUG_PRINTOUTS && RTT_PRINTOUT {
rprintln!(
"bootloader CRC corrupt, read {} and expected {}. booting image A immediately",
crc_calc,
crc_exp
);
}
// TODO: Shift out minimal CCSDS frame to notify about bootloader corruption.
boot_app(sysconfig, cp, AppSel::A);
}
}
// Reading from address 0x0 is problematic in Rust.
// See https://users.rust-lang.org/t/reading-from-physical-address-0x0/117408/5.
// This solution falls back to assembler to deal with this.
fn read_four_bytes_at_addr_zero(buf: &mut [u8; 4]) {
unsafe {
core::arch::asm!(
"ldr r0, [{0}]", // Load 4 bytes from src into r0 register
"str r0, [{1}]", // Store r0 register into first_four_bytes
in(reg) BOOTLOADER_START_ADDR as *const u8, // Input: src pointer (0x0)
in(reg) buf as *mut [u8; 4], // Input: destination pointer
);
}
}
fn check_app_crc(app_sel: AppSel) -> bool {
if DEBUG_PRINTOUTS && RTT_PRINTOUT {
rprintln!("Checking image {:?}", app_sel);
}
if app_sel == AppSel::A {
check_app_given_addr(APP_A_CRC_ADDR, APP_A_START_ADDR, APP_A_SIZE_ADDR)
} else {
check_app_given_addr(APP_B_CRC_ADDR, APP_B_START_ADDR, APP_B_SIZE_ADDR)
}
}
fn check_app_given_addr(crc_addr: u32, start_addr: u32, image_size_addr: u32) -> bool {
let crc_exp = unsafe { (crc_addr as *const u16).read_unaligned().to_be() };
let image_size = unsafe { (image_size_addr as *const u32).read_unaligned().to_be() };
// Sanity check.
if image_size > APP_A_END_ADDR - APP_A_START_ADDR - 8 {
if RTT_PRINTOUT {
rprintln!("detected invalid app size {}", image_size);
}
return false;
}
let crc_calc = CRC_ALGO.checksum(unsafe {
core::slice::from_raw_parts(start_addr as *const u8, image_size as usize)
});
if crc_calc == crc_exp {
return true;
}
false
}
// The boot works by copying the interrupt vector table (IVT) of the respective app to the
// base address in code RAM (0x0) and then performing a soft reset.
fn boot_app(syscfg: &pac::Sysconfig, cp: &cortex_m::Peripherals, app_sel: AppSel) -> ! {
if DEBUG_PRINTOUTS && RTT_PRINTOUT {
rprintln!("booting app {:?}", app_sel);
}
// Disable ROM protection.
syscfg.rom_prot().write(|w| unsafe { w.bits(1) });
let base_addr = if app_sel == AppSel::A {
APP_A_START_ADDR
} else {
APP_B_START_ADDR
};
// Clear all interrupts set.
unsafe {
cp.NVIC.icer[0].write(0xFFFFFFFF);
cp.NVIC.icpr[0].write(0xFFFFFFFF);
// First 4 bytes done with inline assembly, writing to the physical address 0x0 can not
// be done without it. See https://users.rust-lang.org/t/reading-from-physical-address-0x0/117408/2.
core::ptr::read(base_addr as *const u32);
core::arch::asm!(
"str {0}, [{1}]", // Load 4 bytes from src into r0 register
in(reg) base_addr, // Input: App vector table.
in(reg) BOOTLOADER_START_ADDR as *mut u32, // Input: destination pointer
);
core::slice::from_raw_parts_mut(
(BOOTLOADER_START_ADDR + 4) as *mut u32,
(VECTOR_TABLE_LEN - 4) as usize,
)
.copy_from_slice(core::slice::from_raw_parts(
(base_addr + 4) as *const u32,
(VECTOR_TABLE_LEN - 4) as usize,
));
}
/* Disable re-loading from FRAM/code ROM on soft reset */
syscfg
.rst_cntl_rom()
.modify(|_, w| w.sysrstreq().clear_bit());
soft_reset(cp);
}
// Soft reset based on https://github.com/ARM-software/CMSIS_6/blob/5782d6f8057906d360f4b95ec08a2354afe5c9b9/CMSIS/Core/Include/core_cm0.h#L874.
fn soft_reset(cp: &cortex_m::Peripherals) -> ! {
// Ensure all outstanding memory accesses included buffered write are completed before reset.
cortex_m::asm::dsb();
unsafe {
cp.SCB
.aircr
.write((0x5FA << SCB_AIRCR_VECTKEY_POS) | SCB_AIRCR_SYSRESETREQ_MSK);
}
// Ensure completion of memory access.
cortex_m::asm::dsb();
unreachable!();
}

25
examples/README.md Normal file
View File

@ -0,0 +1,25 @@
VA108xx Example Applications
========
This folder contains various examples
Consult the main README first for setup of the repository.
## Simple examples
```rs
cargo run --example blinky
```
You can have a look at the `simple/examples` folder to see all available simple examples
## RTIC example
```rs
cargo run --bin rtic-example
```
## Embassy example
```rs
cargo run --bin embassy-example
```

View File

@ -0,0 +1,40 @@
[package]
name = "embassy-example"
version = "0.1.0"
edition = "2021"
[dependencies]
cortex-m = { version = "0.7", features = ["critical-section-single-core"] }
cortex-m-rt = "0.7"
embedded-hal = "1"
rtt-target = { version = "0.5" }
panic-rtt-target = { version = "0.1" }
critical-section = "1"
portable-atomic = { version = "1", features = ["unsafe-assume-single-core"]}
embassy-sync = { version = "0.6.0" }
embassy-time = { version = "0.3.2" }
embassy-time-driver = { version = "0.1" }
[dependencies.once_cell]
version = "1"
default-features = false
features = ["critical-section"]
[dependencies.embassy-executor]
version = "0.6.0"
features = [
"arch-cortex-m",
"executor-thread",
"executor-interrupt",
"integrated-timers",
]
[dependencies.va108xx-hal]
path = "../../va108xx-hal"
[features]
default = ["ticks-hz-1_000"]
ticks-hz-1_000 = ["embassy-time/tick-hz-1_000"]
ticks-hz-32_768 = ["embassy-time/tick-hz-32_768"]

View File

@ -0,0 +1,4 @@
#![no_std]
pub mod time_driver;
pub use time_driver::init;

View File

@ -0,0 +1,43 @@
#![no_std]
#![no_main]
use embassy_executor::Spawner;
use embassy_time::{Duration, Instant, Ticker};
use embedded_hal::digital::StatefulOutputPin;
use panic_rtt_target as _;
use rtt_target::{rprintln, rtt_init_print};
use va108xx_hal::{gpio::PinsA, pac, prelude::*};
const SYSCLK_FREQ: Hertz = Hertz::from_raw(50_000_000);
// main is itself an async function.
#[embassy_executor::main]
async fn main(_spawner: Spawner) {
rtt_init_print!();
rprintln!("-- VA108xx Embassy Demo --");
let mut dp = pac::Peripherals::take().unwrap();
// Safety: Only called once here.
unsafe {
embassy_example::init(
&mut dp.sysconfig,
&dp.irqsel,
SYSCLK_FREQ,
dp.tim23,
dp.tim22,
)
};
let porta = PinsA::new(&mut dp.sysconfig, Some(dp.ioconfig), dp.porta);
let mut led0 = porta.pa10.into_readable_push_pull_output();
let mut led1 = porta.pa7.into_readable_push_pull_output();
let mut led2 = porta.pa6.into_readable_push_pull_output();
let mut ticker = Ticker::every(Duration::from_secs(1));
loop {
ticker.next().await;
rprintln!("Current time: {}", Instant::now().as_secs());
led0.toggle().ok();
led1.toggle().ok();
led2.toggle().ok();
}
}

View File

@ -0,0 +1,334 @@
//! This is a sample time driver implementation for the VA108xx family of devices, supporting
//! one alarm and requiring/reserving 2 TIM peripherals. You could adapt this implementation to
//! support more alarms.
//!
//! This driver implementation reserves interrupts OC31 and OC30 for the timekeeping.
use core::{cell::Cell, mem, ptr};
use critical_section::CriticalSection;
use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
use embassy_sync::blocking_mutex::Mutex;
use portable_atomic::{AtomicU32, AtomicU8, Ordering};
use embassy_time_driver::{time_driver_impl, AlarmHandle, Driver, TICK_HZ};
use once_cell::sync::OnceCell;
use va108xx_hal::{
clock::enable_peripheral_clock,
enable_interrupt,
pac::{self, interrupt},
prelude::*,
timer::{enable_tim_clk, ValidTim},
PeripheralSelect,
};
pub type TimekeeperClk = pac::Tim23;
pub type AlarmClk0 = pac::Tim22;
pub type AlarmClk1 = pac::Tim21;
pub type AlarmClk2 = pac::Tim20;
const TIMEKEEPER_IRQ: pac::Interrupt = pac::Interrupt::OC31;
const ALARM_IRQ: pac::Interrupt = pac::Interrupt::OC30;
/// Initialization method for embassy
///
/// # Safety
/// This has to be called once at initialization time to initiate the time driver for
/// embassy.
pub unsafe fn init(
syscfg: &mut pac::Sysconfig,
irqsel: &pac::Irqsel,
sysclk: impl Into<Hertz>,
timekeeper: TimekeeperClk,
alarm_tim: AlarmClk0,
) {
//enable_and_init_irq_router(syscfg, irq_router);
DRIVER.init(syscfg, irqsel, sysclk, timekeeper, alarm_tim)
}
time_driver_impl!(
static DRIVER: TimerDriverEmbassy = TimerDriverEmbassy {
periods: AtomicU32::new(0),
alarm_count: AtomicU8::new(0),
alarms: Mutex::const_new(CriticalSectionRawMutex::new(), [AlarmState::new(); ALARM_COUNT])
});
/// Timekeeper interrupt.
#[interrupt]
#[allow(non_snake_case)]
fn OC31() {
DRIVER.on_interrupt_timekeeping()
}
/// Alarm timer interrupt.
#[interrupt]
#[allow(non_snake_case)]
fn OC30() {
DRIVER.on_interrupt_alarm(0)
}
#[inline(always)]
const fn alarm_tim(idx: usize) -> &'static pac::tim0::RegisterBlock {
// Safety: This is a static memory-mapped peripheral.
match idx {
0 => unsafe { &*AlarmClk0::ptr() },
1 => unsafe { &*AlarmClk1::ptr() },
2 => unsafe { &*AlarmClk2::ptr() },
_ => {
panic!("invalid alarm timer index")
}
}
}
#[inline(always)]
const fn timekeeping_tim() -> &'static pac::tim0::RegisterBlock {
// Safety: This is a memory-mapped peripheral.
unsafe { &*TimekeeperClk::ptr() }
}
struct AlarmState {
timestamp: Cell<u64>,
// This is really a Option<(fn(*mut ()), *mut ())>
// but fn pointers aren't allowed in const yet
callback: Cell<*const ()>,
ctx: Cell<*mut ()>,
}
impl AlarmState {
const fn new() -> Self {
Self {
timestamp: Cell::new(u64::MAX),
callback: Cell::new(ptr::null()),
ctx: Cell::new(ptr::null_mut()),
}
}
}
unsafe impl Send for AlarmState {}
const ALARM_COUNT: usize = 1;
static SCALE: OnceCell<u64> = OnceCell::new();
pub struct TimerDriverEmbassy {
periods: AtomicU32,
alarm_count: AtomicU8,
/// Timestamp at which to fire alarm. u64::MAX if no alarm is scheduled.
alarms: Mutex<CriticalSectionRawMutex, [AlarmState; ALARM_COUNT]>,
}
impl TimerDriverEmbassy {
fn init(
&self,
syscfg: &mut pac::Sysconfig,
irqsel: &pac::Irqsel,
sysclk: impl Into<Hertz>,
timekeeper: TimekeeperClk,
alarm_tim: AlarmClk0,
) {
enable_peripheral_clock(syscfg, PeripheralSelect::Irqsel);
enable_tim_clk(syscfg, TimekeeperClk::TIM_ID);
let sysclk = sysclk.into();
// Initiate scale value here. This is required to convert timer ticks back to a timestamp.
SCALE.set((sysclk.raw() / TICK_HZ as u32) as u64).unwrap();
timekeeper
.rst_value()
.write(|w| unsafe { w.bits(u32::MAX) });
// Decrementing counter.
timekeeper
.cnt_value()
.write(|w| unsafe { w.bits(u32::MAX) });
// Switch on. Timekeeping should always be done.
irqsel
.tim0(TimekeeperClk::TIM_ID as usize)
.write(|w| unsafe { w.bits(TIMEKEEPER_IRQ as u32) });
unsafe {
enable_interrupt(TIMEKEEPER_IRQ);
}
timekeeper.ctrl().modify(|_, w| w.irq_enb().set_bit());
timekeeper.enable().write(|w| unsafe { w.bits(1) });
enable_tim_clk(syscfg, AlarmClk0::TIM_ID);
// Explicitely disable alarm timer until needed.
alarm_tim.ctrl().modify(|_, w| {
w.irq_enb().clear_bit();
w.enable().clear_bit()
});
// Enable general interrupts. The IRQ enable of the peripheral remains cleared.
unsafe {
enable_interrupt(ALARM_IRQ);
}
irqsel
.tim0(AlarmClk0::TIM_ID as usize)
.write(|w| unsafe { w.bits(ALARM_IRQ as u32) });
}
// Should be called inside the IRQ of the timekeeper timer.
fn on_interrupt_timekeeping(&self) {
self.next_period();
}
// Should be called inside the IRQ of the alarm timer.
fn on_interrupt_alarm(&self, idx: usize) {
critical_section::with(|cs| {
if self.alarms.borrow(cs)[idx].timestamp.get() <= self.now() {
self.trigger_alarm(idx, cs)
}
})
}
fn next_period(&self) {
let period = self.periods.fetch_add(1, Ordering::AcqRel) + 1;
let t = (period as u64) << 32;
critical_section::with(|cs| {
for i in 0..ALARM_COUNT {
let alarm = &self.alarms.borrow(cs)[i];
let at = alarm.timestamp.get();
let alarm_tim = alarm_tim(0);
if at < t {
self.trigger_alarm(i, cs);
} else {
let remaining_ticks = (at - t) * *SCALE.get().unwrap();
if remaining_ticks <= u32::MAX as u64 {
alarm_tim.enable().write(|w| unsafe { w.bits(0) });
alarm_tim
.cnt_value()
.write(|w| unsafe { w.bits(remaining_ticks as u32) });
alarm_tim.ctrl().modify(|_, w| w.irq_enb().set_bit());
alarm_tim.enable().write(|w| unsafe { w.bits(1) })
}
}
}
})
}
fn get_alarm<'a>(&'a self, cs: CriticalSection<'a>, alarm: AlarmHandle) -> &'a AlarmState {
// safety: we're allowed to assume the AlarmState is created by us, and
// we never create one that's out of bounds.
unsafe { self.alarms.borrow(cs).get_unchecked(alarm.id() as usize) }
}
fn trigger_alarm(&self, n: usize, cs: CriticalSection) {
alarm_tim(n).ctrl().modify(|_, w| {
w.irq_enb().clear_bit();
w.enable().clear_bit()
});
let alarm = &self.alarms.borrow(cs)[n];
// Setting the maximum value disables the alarm.
alarm.timestamp.set(u64::MAX);
// Call after clearing alarm, so the callback can set another alarm.
// safety:
// - we can ignore the possiblity of `f` being unset (null) because of the safety contract of `allocate_alarm`.
// - other than that we only store valid function pointers into alarm.callback
let f: fn(*mut ()) = unsafe { mem::transmute(alarm.callback.get()) };
f(alarm.ctx.get());
}
}
impl Driver for TimerDriverEmbassy {
fn now(&self) -> u64 {
if SCALE.get().is_none() {
return 0;
}
let mut period1: u32;
let mut period2: u32;
let mut counter_val: u32;
loop {
// Acquire ensures that we get the latest value of `periods` and
// no instructions can be reordered before the load.
period1 = self.periods.load(Ordering::Acquire);
counter_val = u32::MAX - timekeeping_tim().cnt_value().read().bits();
// Double read to protect against race conditions when the counter is overflowing.
period2 = self.periods.load(Ordering::Relaxed);
if period1 == period2 {
let now = (((period1 as u64) << 32) | counter_val as u64) / *SCALE.get().unwrap();
return now;
}
}
}
unsafe fn allocate_alarm(&self) -> Option<AlarmHandle> {
let id = self
.alarm_count
.fetch_update(Ordering::AcqRel, Ordering::Acquire, |x| {
if x < ALARM_COUNT as u8 {
Some(x + 1)
} else {
None
}
});
match id {
Ok(id) => Some(AlarmHandle::new(id)),
Err(_) => None,
}
}
fn set_alarm_callback(
&self,
alarm: embassy_time_driver::AlarmHandle,
callback: fn(*mut ()),
ctx: *mut (),
) {
critical_section::with(|cs| {
let alarm = self.get_alarm(cs, alarm);
alarm.callback.set(callback as *const ());
alarm.ctx.set(ctx);
})
}
fn set_alarm(&self, alarm: embassy_time_driver::AlarmHandle, timestamp: u64) -> bool {
if SCALE.get().is_none() {
return false;
}
critical_section::with(|cs| {
let n = alarm.id();
let alarm_tim = alarm_tim(n.into());
alarm_tim.ctrl().modify(|_, w| {
w.irq_enb().clear_bit();
w.enable().clear_bit()
});
let alarm = self.get_alarm(cs, alarm);
alarm.timestamp.set(timestamp);
let t = self.now();
if timestamp <= t {
alarm.timestamp.set(u64::MAX);
return false;
}
// If it hasn't triggered yet, setup the relevant reset value, regardless of whether
// the interrupts are enabled or not. When they are enabled at a later point, the
// right value is already set.
// If the timestamp is in the next few ticks, add a bit of buffer to be sure the alarm
// is not missed.
//
// This means that an alarm can be delayed for up to 2 ticks (from t+1 to t+3), but this is allowed
// by the Alarm trait contract. What's not allowed is triggering alarms *before* their scheduled time,
// and we don't do that here.
let safe_timestamp = timestamp.max(t + 3);
let timer_ticks = (safe_timestamp - t) * *SCALE.get().unwrap();
alarm_tim.rst_value().write(|w| unsafe { w.bits(u32::MAX) });
if timer_ticks <= u32::MAX as u64 {
alarm_tim
.cnt_value()
.write(|w| unsafe { w.bits(timer_ticks as u32) });
alarm_tim.ctrl().modify(|_, w| w.irq_enb().set_bit());
alarm_tim.enable().write(|w| unsafe { w.bits(1) });
}
// If it's too far in the future, don't enable timer yet.
// It will be enabled later by `next_period`.
true
})
}
}

33
examples/rtic/Cargo.toml Normal file
View File

@ -0,0 +1,33 @@
[package]
name = "rtic-example"
version = "0.1.0"
edition = "2021"
[dependencies]
cortex-m = { version = "0.7", features = ["critical-section-single-core"] }
cortex-m-rt = "0.7"
embedded-hal = "1"
embedded-io = "0.6"
rtt-target = { version = "0.5" }
# Even though we do not use this directly, we need to activate this feature explicitely
# so that RTIC compiles because thumv6 does not have CAS operations natively.
portable-atomic = { version = "1", features = ["unsafe-assume-single-core"]}
panic-rtt-target = { version = "0.1" }
[dependencies.va108xx-hal]
path = "../../va108xx-hal"
[dependencies.vorago-reb1]
path = "../../vorago-reb1"
[dependencies.rtic]
version = "2"
features = ["thumbv6-backend"]
[dependencies.rtic-monotonics]
version = "2"
features = ["cortex-m-systick"]
[dependencies.rtic-sync]
version = "1.3"
features = ["defmt-03"]

View File

@ -5,6 +5,7 @@
#[rtic::app(device = pac)] #[rtic::app(device = pac)]
mod app { mod app {
use panic_rtt_target as _; use panic_rtt_target as _;
use rtic_example::SYSCLK_FREQ;
use rtt_target::{rprintln, rtt_init_default, set_print_channel}; use rtt_target::{rprintln, rtt_init_default, set_print_channel};
use va108xx_hal::{ use va108xx_hal::{
clock::{set_clk_div_register, FilterClkSel}, clock::{set_clk_div_register, FilterClkSel},
@ -16,6 +17,8 @@ mod app {
use vorago_reb1::button::Button; use vorago_reb1::button::Button;
use vorago_reb1::leds::Leds; use vorago_reb1::leds::Leds;
rtic_monotonics::systick_monotonic!(Mono, 1_000);
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq)]
pub enum PressMode { pub enum PressMode {
Toggle, Toggle,
@ -43,10 +46,12 @@ mod app {
struct Shared {} struct Shared {}
#[init] #[init]
fn init(ctx: init::Context) -> (Shared, Local, init::Monotonics) { fn init(cx: init::Context) -> (Shared, Local) {
let channels = rtt_init_default!(); let channels = rtt_init_default!();
set_print_channel(channels.up.0); set_print_channel(channels.up.0);
rprintln!("-- Vorago Button IRQ Example --"); rprintln!("-- Vorago Button IRQ Example --");
Mono::start(cx.core.SYST, SYSCLK_FREQ.raw());
let mode = match CFG_MODE { let mode = match CFG_MODE {
// Ask mode from user via RTT // Ask mode from user via RTT
CfgMode::Prompt => prompt_mode(channels.down.0), CfgMode::Prompt => prompt_mode(channels.down.0),
@ -55,7 +60,7 @@ mod app {
}; };
rprintln!("Using {:?} mode", mode); rprintln!("Using {:?} mode", mode);
let mut dp = ctx.device; let mut dp = cx.device;
let pinsa = PinsA::new(&mut dp.sysconfig, Some(dp.ioconfig), dp.porta); let pinsa = PinsA::new(&mut dp.sysconfig, Some(dp.ioconfig), dp.porta);
let edge_irq = match mode { let edge_irq = match mode {
PressMode::Toggle => InterruptEdge::HighToLow, PressMode::Toggle => InterruptEdge::HighToLow,
@ -90,7 +95,7 @@ mod app {
50.MHz(), 50.MHz(),
dp.tim0, dp.tim0,
); );
(Shared {}, Local { leds, button, mode }, init::Monotonics()) (Shared {}, Local { leds, button, mode })
} }
// `shared` cannot be accessed from this context // `shared` cannot be accessed from this context
@ -108,12 +113,10 @@ mod app {
let mode = cx.local.mode; let mode = cx.local.mode;
if *mode == PressMode::Toggle { if *mode == PressMode::Toggle {
leds[0].toggle(); leds[0].toggle();
} else if button.released() {
leds[0].off();
} else { } else {
if button.released() { leds[0].on();
leds[0].off();
} else {
leds[0].on();
}
} }
} }
@ -129,14 +132,11 @@ mod app {
let mut read; let mut read;
loop { loop {
read = down_channel.read(&mut read_buf); read = down_channel.read(&mut read_buf);
for i in 0..read { for &byte in &read_buf[..read] {
let val = read_buf[i] as char; match byte as char {
if val == '0' || val == '1' { '0' => return PressMode::Toggle,
return if val == '0' { '1' => return PressMode::Keep,
PressMode::Toggle _ => continue, // Ignore other characters
} else {
PressMode::Keep
};
} }
} }
} }

View File

@ -14,14 +14,13 @@
mod app { mod app {
use embedded_io::Write; use embedded_io::Write;
use panic_rtt_target as _; use panic_rtt_target as _;
use rtic_monotonics::systick::Systick; use rtic_example::SYSCLK_FREQ;
use rtic_sync::make_channel; use rtic_sync::make_channel;
use rtt_target::{rprintln, rtt_init_print}; use rtt_target::{rprintln, rtt_init_print};
use va108xx_hal::{ use va108xx_hal::{
gpio::PinsB, gpio::PinsB,
pac, pac,
prelude::*, prelude::*,
time::Hertz,
uart::{self, IrqCfg, IrqResult, UartWithIrqBase}, uart::{self, IrqCfg, IrqResult, UartWithIrqBase},
}; };
@ -44,19 +43,14 @@ mod app {
pub timeout: bool, pub timeout: bool,
} }
rtic_monotonics::systick_monotonic!(Mono, 1_000);
#[init] #[init]
fn init(cx: init::Context) -> (Shared, Local) { fn init(cx: init::Context) -> (Shared, Local) {
rtt_init_print!(); rtt_init_print!();
//set_print_channel(channels.up.0);
rprintln!("-- VA108xx UART IRQ example application--"); rprintln!("-- VA108xx UART IRQ example application--");
// Initialize the systick interrupt & obtain the token to prove that we did Mono::start(cx.core.SYST, SYSCLK_FREQ.raw());
let systick_mono_token = rtic_monotonics::create_systick_token!();
Systick::start(
cx.core.SYST,
Hertz::from(50.MHz()).raw(),
systick_mono_token,
);
let mut dp = cx.device; let mut dp = cx.device;
let gpiob = PinsB::new(&mut dp.sysconfig, Some(dp.ioconfig), dp.portb); let gpiob = PinsB::new(&mut dp.sysconfig, Some(dp.ioconfig), dp.portb);
@ -65,7 +59,7 @@ mod app {
let irq_cfg = IrqCfg::new(pac::interrupt::OC3, true, true); let irq_cfg = IrqCfg::new(pac::interrupt::OC3, true, true);
let (mut irq_uart, _) = let (mut irq_uart, _) =
uart::Uart::uartb(dp.uartb, (tx, rx), 115200.Hz(), &mut dp.sysconfig, 50.MHz()) uart::Uart::new(&mut dp.sysconfig, 50.MHz(), dp.uartb, (tx, rx), 115200.Hz())
.into_uart_with_irq(irq_cfg, Some(&mut dp.sysconfig), Some(&mut dp.irqsel)) .into_uart_with_irq(irq_cfg, Some(&mut dp.sysconfig), Some(&mut dp.irqsel))
.downgrade(); .downgrade();
irq_uart irq_uart
@ -74,7 +68,6 @@ mod app {
let (rx_info_tx, rx_info_rx) = make_channel!(RxInfo, 3); let (rx_info_tx, rx_info_rx) = make_channel!(RxInfo, 3);
let rx_buf: [u8; 64] = [0; 64]; let rx_buf: [u8; 64] = [0; 64];
//reply_handler::spawn().expect("spawning reply handler failed");
( (
Shared { irq_uart, rx_buf }, Shared { irq_uart, rx_buf },
Local { Local {
@ -112,8 +105,8 @@ mod app {
.expect("Read operation init failed"); .expect("Read operation init failed");
let mut end_idx = 0; let mut end_idx = 0;
for idx in 0..rx_buf.len() { for (idx, val) in rx_buf.iter().enumerate() {
if (rx_buf[idx] as char) == '\n' { if (*val as char) == '\n' {
end_idx = idx; end_idx = idx;
break; break;
} }

4
examples/rtic/src/lib.rs Normal file
View File

@ -0,0 +1,4 @@
#![no_std]
use va108xx_hal::time::Hertz;
pub const SYSCLK_FREQ: Hertz = Hertz::from_raw(50_000_000);

71
examples/rtic/src/main.rs Normal file
View File

@ -0,0 +1,71 @@
//! RTIC minimal blinky
#![no_main]
#![no_std]
#[rtic::app(device = pac, dispatchers = [OC31, OC30, OC29])]
mod app {
use cortex_m::asm;
use embedded_hal::digital::StatefulOutputPin;
use panic_rtt_target as _;
use rtic_example::SYSCLK_FREQ;
use rtic_monotonics::systick::prelude::*;
use rtic_monotonics::Monotonic;
use rtt_target::{rprintln, rtt_init_print};
use va108xx_hal::{
gpio::{OutputReadablePushPull, Pin, PinsA, PA10, PA6, PA7},
pac,
};
#[local]
struct Local {
led0: Pin<PA10, OutputReadablePushPull>,
led1: Pin<PA7, OutputReadablePushPull>,
led2: Pin<PA6, OutputReadablePushPull>,
}
#[shared]
struct Shared {}
rtic_monotonics::systick_monotonic!(Mono, 1_000);
#[init]
fn init(mut cx: init::Context) -> (Shared, Local) {
rtt_init_print!();
rprintln!("-- Vorago VA108xx RTIC template --");
Mono::start(cx.core.SYST, SYSCLK_FREQ.raw());
let porta = PinsA::new(
&mut cx.device.sysconfig,
Some(cx.device.ioconfig),
cx.device.porta,
);
let led0 = porta.pa10.into_readable_push_pull_output();
let led1 = porta.pa7.into_readable_push_pull_output();
let led2 = porta.pa6.into_readable_push_pull_output();
blinky::spawn().ok();
(Shared {}, Local { led0, led1, led2 })
}
// `shared` cannot be accessed from this context
#[idle]
fn idle(_cx: idle::Context) -> ! {
loop {
asm::nop();
}
}
#[task(
priority = 3,
local=[led0, led1, led2],
)]
async fn blinky(cx: blinky::Context) {
loop {
rprintln!("toggling LEDs");
cx.local.led0.toggle().ok();
cx.local.led1.toggle().ok();
cx.local.led2.toggle().ok();
Mono::delay(1000.millis()).await;
}
}
}

View File

@ -4,30 +4,20 @@ version = "0.1.0"
edition = "2021" edition = "2021"
[dependencies] [dependencies]
panic-halt = "0.2"
cortex-m = {version = "0.7", features = ["critical-section-single-core"]} cortex-m = {version = "0.7", features = ["critical-section-single-core"]}
panic-rtt-target = "0.1"
cortex-m-rt = "0.7" cortex-m-rt = "0.7"
panic-halt = "0.2"
panic-rtt-target = "0.1"
critical-section = "1"
rtt-target = "0.5" rtt-target = "0.5"
rtic-sync = { version = "1.3", features = ["defmt-03"] }
embedded-hal = "1" embedded-hal = "1"
embedded-hal-nb = "1" embedded-hal-nb = "1"
embedded-io = "0.6" embedded-io = "0.6"
cortex-m-semihosting = "0.5.0" cortex-m-semihosting = "0.5.0"
# I'd really like to use those, but it is tricky without probe-rs..
# defmt = "0.3"
# defmt-brtt = { version = "0.1", default-features = false, features = ["rtt"] }
# panic-probe = { version = "0.3", features = ["print-defmt"] }
[dependencies.rtic]
version = "2"
features = ["thumbv6-backend"]
[dependencies.rtic-monotonics]
version = "1"
features = ["cortex-m-systick"]
[dependencies.va108xx-hal] [dependencies.va108xx-hal]
version = "0.6"
path = "../../va108xx-hal" path = "../../va108xx-hal"
features = ["rt", "defmt"] features = ["rt", "defmt"]
[dependencies.vorago-reb1]
path = "../../vorago-reb1"

View File

@ -48,7 +48,7 @@ fn main() -> ! {
let mut cascade_target_1 = let mut cascade_target_1 =
CountDownTimer::new(&mut dp.sysconfig, 50.MHz(), dp.tim4).auto_deactivate(true); CountDownTimer::new(&mut dp.sysconfig, 50.MHz(), dp.tim4).auto_deactivate(true);
cascade_target_1 cascade_target_1
.cascade_0_source(CascadeSource::TimBase, Some(3)) .cascade_0_source(CascadeSource::Tim(3))
.expect("Configuring cascade source for TIM4 failed"); .expect("Configuring cascade source for TIM4 failed");
let mut csd_cfg = CascadeCtrl { let mut csd_cfg = CascadeCtrl {
enb_start_src_csd0: true, enb_start_src_csd0: true,
@ -75,7 +75,7 @@ fn main() -> ! {
CountDownTimer::new(&mut dp.sysconfig, 50.MHz(), dp.tim5).auto_deactivate(true); CountDownTimer::new(&mut dp.sysconfig, 50.MHz(), dp.tim5).auto_deactivate(true);
// Set TIM4 as cascade source // Set TIM4 as cascade source
cascade_target_2 cascade_target_2
.cascade_1_source(CascadeSource::TimBase, Some(4)) .cascade_1_source(CascadeSource::Tim(4))
.expect("Configuring cascade source for TIM5 failed"); .expect("Configuring cascade source for TIM5 failed");
csd_cfg = CascadeCtrl::default(); csd_cfg = CascadeCtrl::default();

View File

@ -21,9 +21,9 @@ fn main() -> ! {
let mut dp = pac::Peripherals::take().unwrap(); let mut dp = pac::Peripherals::take().unwrap();
let pinsa = PinsA::new(&mut dp.sysconfig, None, dp.porta); let pinsa = PinsA::new(&mut dp.sysconfig, None, dp.porta);
let mut pwm = pwm::PwmPin::new( let mut pwm = pwm::PwmPin::new(
(pinsa.pa3.into_funsel_1(), dp.tim3),
50.MHz(),
&mut dp.sysconfig, &mut dp.sysconfig,
50.MHz(),
(pinsa.pa3.into_funsel_1(), dp.tim3),
10.Hz(), 10.Hz(),
); );
let mut delay = set_up_ms_delay_provider(&mut dp.sysconfig, 50.MHz(), dp.tim0); let mut delay = set_up_ms_delay_provider(&mut dp.sysconfig, 50.MHz(), dp.tim0);

View File

@ -3,12 +3,14 @@
#![no_std] #![no_std]
use cortex_m_rt::entry; use cortex_m_rt::entry;
use panic_halt as _; use panic_rtt_target as _;
use rtt_target::{rprintln, rtt_init_print}; use rtt_target::{rprintln, rtt_init_print};
use va108xx_hal as _;
#[entry] #[entry]
fn main() -> ! { fn main() -> ! {
rtt_init_print!(); rtt_init_print!();
rprintln!("-- VA108XX RTT example --");
let mut counter = 0; let mut counter = 0;
loop { loop {
rprintln!("{}: Hello, world!", counter); rprintln!("{}: Hello, world!", counter);

View File

@ -16,7 +16,7 @@ use va108xx_hal::{
pac::{self, interrupt}, pac::{self, interrupt},
prelude::*, prelude::*,
pwm::{default_ms_irq_handler, set_up_ms_tick}, pwm::{default_ms_irq_handler, set_up_ms_tick},
spi::{self, Spi, SpiBase, TransferConfig}, spi::{self, Spi, SpiBase, SpiClkConfig, TransferConfigWithHwcs},
IrqCfg, IrqCfg,
}; };
@ -55,6 +55,8 @@ fn main() -> ! {
dp.tim0, dp.tim0,
); );
let spi_clk_cfg = SpiClkConfig::from_clk(50.MHz(), SPI_SPEED_KHZ.kHz())
.expect("creating SPI clock config failed");
let spia_ref: RefCell<Option<SpiBase<pac::Spia, u8>>> = RefCell::new(None); let spia_ref: RefCell<Option<SpiBase<pac::Spia, u8>>> = RefCell::new(None);
let spib_ref: RefCell<Option<SpiBase<pac::Spib, u8>>> = RefCell::new(None); let spib_ref: RefCell<Option<SpiBase<pac::Spib, u8>>> = RefCell::new(None);
let pinsa = PinsA::new(&mut dp.sysconfig, None, dp.porta); let pinsa = PinsA::new(&mut dp.sysconfig, None, dp.porta);
@ -73,13 +75,12 @@ fn main() -> ! {
pinsa.pa30.into_funsel_1(), pinsa.pa30.into_funsel_1(),
pinsa.pa29.into_funsel_1(), pinsa.pa29.into_funsel_1(),
); );
let mut spia = Spi::spia( let mut spia = Spi::new(
&mut dp.sysconfig,
50.MHz(),
dp.spia, dp.spia,
(sck, miso, mosi), (sck, miso, mosi),
50.MHz(),
spi_cfg, spi_cfg,
Some(&mut dp.sysconfig),
None,
); );
spia.set_fill_word(FILL_WORD); spia.set_fill_word(FILL_WORD);
spia_ref.borrow_mut().replace(spia.downgrade()); spia_ref.borrow_mut().replace(spia.downgrade());
@ -90,13 +91,12 @@ fn main() -> ! {
pinsb.pb8.into_funsel_2(), pinsb.pb8.into_funsel_2(),
pinsb.pb7.into_funsel_2(), pinsb.pb7.into_funsel_2(),
); );
let mut spia = Spi::spia( let mut spia = Spi::new(
&mut dp.sysconfig,
50.MHz(),
dp.spia, dp.spia,
(sck, miso, mosi), (sck, miso, mosi),
50.MHz(),
spi_cfg, spi_cfg,
Some(&mut dp.sysconfig),
None,
); );
spia.set_fill_word(FILL_WORD); spia.set_fill_word(FILL_WORD);
spia_ref.borrow_mut().replace(spia.downgrade()); spia_ref.borrow_mut().replace(spia.downgrade());
@ -107,13 +107,12 @@ fn main() -> ! {
pinsb.pb4.into_funsel_1(), pinsb.pb4.into_funsel_1(),
pinsb.pb3.into_funsel_1(), pinsb.pb3.into_funsel_1(),
); );
let mut spib = Spi::spib( let mut spib = Spi::new(
&mut dp.sysconfig,
50.MHz(),
dp.spib, dp.spib,
(sck, miso, mosi), (sck, miso, mosi),
50.MHz(),
spi_cfg, spi_cfg,
Some(&mut dp.sysconfig),
None,
); );
spib.set_fill_word(FILL_WORD); spib.set_fill_word(FILL_WORD);
spib_ref.borrow_mut().replace(spib.downgrade()); spib_ref.borrow_mut().replace(spib.downgrade());
@ -123,17 +122,21 @@ fn main() -> ! {
match SPI_BUS_SEL { match SPI_BUS_SEL {
SpiBusSelect::SpiAPortA | SpiBusSelect::SpiAPortB => { SpiBusSelect::SpiAPortA | SpiBusSelect::SpiAPortB => {
if let Some(ref mut spi) = *spia_ref.borrow_mut() { if let Some(ref mut spi) = *spia_ref.borrow_mut() {
let transfer_cfg = let transfer_cfg = TransferConfigWithHwcs::new_no_hw_cs(
TransferConfig::new_no_hw_cs(SPI_SPEED_KHZ.kHz(), SPI_MODE, BLOCKMODE, false); Some(spi_clk_cfg),
Some(SPI_MODE),
BLOCKMODE,
false,
);
spi.cfg_transfer(&transfer_cfg); spi.cfg_transfer(&transfer_cfg);
} }
} }
SpiBusSelect::SpiBPortB => { SpiBusSelect::SpiBPortB => {
if let Some(ref mut spi) = *spib_ref.borrow_mut() { if let Some(ref mut spi) = *spib_ref.borrow_mut() {
let hw_cs_pin = pinsb.pb2.into_funsel_1(); let hw_cs_pin = pinsb.pb2.into_funsel_1();
let transfer_cfg = TransferConfig::new( let transfer_cfg = TransferConfigWithHwcs::new(
SPI_SPEED_KHZ.kHz(), Some(spi_clk_cfg),
SPI_MODE, Some(SPI_MODE),
Some(hw_cs_pin), Some(hw_cs_pin),
BLOCKMODE, BLOCKMODE,
false, false,
@ -195,6 +198,10 @@ fn main() -> ! {
if EXAMPLE_SEL == ExampleSelect::Loopback { if EXAMPLE_SEL == ExampleSelect::Loopback {
// Can't really verify correct reply here. // Can't really verify correct reply here.
spi.write(&[0x42]).expect("write failed"); spi.write(&[0x42]).expect("write failed");
// Need small delay.. otherwise we will read back the sent byte (which we don't want here).
// The write function will return as soon as all bytes were shifted out, ignoring the
// reply bytes.
delay.delay_us(50);
// Because of the loopback mode, we should get back the fill word here. // Because of the loopback mode, we should get back the fill word here.
spi.read(&mut reply_buf[0..1]).unwrap(); spi.read(&mut reply_buf[0..1]).unwrap();
assert_eq!(reply_buf[0], FILL_WORD); assert_eq!(reply_buf[0], FILL_WORD);

View File

@ -3,8 +3,8 @@
#![no_std] #![no_std]
use core::cell::Cell; use core::cell::Cell;
use cortex_m::interrupt::Mutex;
use cortex_m_rt::entry; use cortex_m_rt::entry;
use critical_section::Mutex;
use panic_rtt_target as _; use panic_rtt_target as _;
use rtt_target::{rprintln, rtt_init_print}; use rtt_target::{rprintln, rtt_init_print};
use va108xx_hal::{ use va108xx_hal::{
@ -83,11 +83,12 @@ fn main() -> ! {
} }
} }
loop { loop {
let current_ms = cortex_m::interrupt::free(|cs| MS_COUNTER.borrow(cs).get()); let current_ms = critical_section::with(|cs| MS_COUNTER.borrow(cs).get());
if current_ms - last_ms >= 1000 { if current_ms - last_ms >= 1000 {
last_ms = current_ms; // To prevent drift.
last_ms += 1000;
rprintln!("MS counter: {}", current_ms); rprintln!("MS counter: {}", current_ms);
let second = cortex_m::interrupt::free(|cs| SEC_COUNTER.borrow(cs).get()); let second = critical_section::with(|cs| SEC_COUNTER.borrow(cs).get());
rprintln!("Second counter: {}", second); rprintln!("Second counter: {}", second);
} }
cortex_m::asm::delay(10000); cortex_m::asm::delay(10000);
@ -110,7 +111,7 @@ fn OC0() {
#[interrupt] #[interrupt]
#[allow(non_snake_case)] #[allow(non_snake_case)]
fn OC1() { fn OC1() {
cortex_m::interrupt::free(|cs| { critical_section::with(|cs| {
let mut sec = SEC_COUNTER.borrow(cs).get(); let mut sec = SEC_COUNTER.borrow(cs).get();
sec += 1; sec += 1;
SEC_COUNTER.borrow(cs).set(sec); SEC_COUNTER.borrow(cs).set(sec);

View File

@ -28,7 +28,7 @@ fn main() -> ! {
let tx = gpioa.pa9.into_funsel_2(); let tx = gpioa.pa9.into_funsel_2();
let rx = gpioa.pa8.into_funsel_2(); let rx = gpioa.pa8.into_funsel_2();
let uarta = uart::Uart::uarta(dp.uarta, (tx, rx), 115200.Hz(), &mut dp.sysconfig, 50.MHz()); let uarta = uart::Uart::new(&mut dp.sysconfig, 50.MHz(), dp.uarta, (tx, rx), 115200.Hz());
let (mut tx, mut rx) = uarta.split(); let (mut tx, mut rx) = uarta.split();
writeln!(tx, "Hello World\r").unwrap(); writeln!(tx, "Hello World\r").unwrap();
loop { loop {

View File

@ -11,14 +11,14 @@ variants:
psel: 0x0 psel: 0x0
memory_map: memory_map:
- !Ram - !Ram
name: IRAM1 name: DRAM
range: range:
start: 0x10000000 start: 0x10000000
end: 0x10008000 end: 0x10008000
cores: cores:
- main - main
- !Nvm - !Nvm
name: IROM1 name: NVM
range: range:
start: 0x0 start: 0x0
end: 0x20000 end: 0x20000
@ -30,6 +30,29 @@ variants:
- va108xx_m95m01_128kb_prog - va108xx_m95m01_128kb_prog
- va108xx_mr25h10_1mb_prog - va108xx_mr25h10_1mb_prog
- va108xx_ttflash_prog - va108xx_ttflash_prog
- name: VA108xx_RAM
cores:
- name: main
type: armv6m
core_access_options: !Arm
ap: 0
psel: 0x0
memory_map:
- !Ram
name: DRAM
range:
start: 0x10000000
end: 0x10008000
cores:
- main
- !Ram
name: IRAM
range:
start: 0x0
end: 0x20000
is_boot_memory: true
cores:
- main
flash_algorithms: flash_algorithms:
- name: va108xx_fm25v20a_fram_128kb_prog - name: va108xx_fm25v20a_fram_128kb_prog
description: VA108_FM25V20A_FRAM_128KB description: VA108_FM25V20A_FRAM_128KB

View File

@ -6,6 +6,20 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](http://keepachangelog.com/) The format is based on [Keep a Changelog](http://keepachangelog.com/)
and this project adheres to [Semantic Versioning](http://semver.org/). and this project adheres to [Semantic Versioning](http://semver.org/).
## [unreleased]
- Improves `CascardSource` handling and general API when chosing cascade sources.
- Replaced `utility::unmask_irq` by `enable_interrupt` and `disable_interrupt` API.
## [v0.7.0] 2024-07-04
- Replace `uarta` and `uartb` `Uart` constructors by `new` constructor
- Replace SPI `spia`, `spib` and `spic` constructors by `new` constructor
- Replace I2C `i2ca`, `i2cb` constructors by `new` constructor. Update constructor
to fail on invalid fast I2C speed system clock values
- Renamed `gpio::pins` to `gpio::pin` and `gpio::dynpins` to `gpio::dynpin`
- Simplify UART clock divider calculations and remove `libm` dependency consequently
## [v0.6.0] 2024-06-16 ## [v0.6.0] 2024-06-16
- Updated `embedded-hal` to v1 - Updated `embedded-hal` to v1

View File

@ -1,11 +1,11 @@
[package] [package]
name = "va108xx-hal" name = "va108xx-hal"
version = "0.6.0" version = "0.7.0"
authors = ["Robin Mueller <muellerr@irs.uni-stuttgart.de>"] authors = ["Robin Mueller <muellerr@irs.uni-stuttgart.de>"]
edition = "2021" edition = "2021"
description = "HAL for the Vorago VA108xx family of microcontrollers" description = "HAL for the Vorago VA108xx family of microcontrollers"
homepage = "https://egit.irs.uni-stuttgart.de/rust/va108xx-hal" homepage = "https://egit.irs.uni-stuttgart.de/rust/va108xx-rs"
repository = "https://egit.irs.uni-stuttgart.de/rust/va108xx-hal" repository = "https://egit.irs.uni-stuttgart.de/rust/va108xx-rs"
license = "Apache-2.0" license = "Apache-2.0"
keywords = ["no-std", "hal", "cortex-m", "vorago", "va108xx"] keywords = ["no-std", "hal", "cortex-m", "vorago", "va108xx"]
categories = ["aerospace", "embedded", "no-std", "hardware-support"] categories = ["aerospace", "embedded", "no-std", "hardware-support"]
@ -16,11 +16,10 @@ cortex-m-rt = "0.7"
nb = "1" nb = "1"
paste = "1" paste = "1"
embedded-hal-nb = "1" embedded-hal-nb = "1"
libm = "0.2"
embedded-io = "0.6" embedded-io = "0.6"
fugit = "0.3" fugit = "0.3"
typenum = "1" typenum = "1"
defmt = { version = "0.3", optional = true } critical-section = "1"
delegate = "0.12" delegate = "0.12"
[dependencies.va108xx] [dependencies.va108xx]
@ -39,10 +38,15 @@ default-features = false
version = "1.14" version = "1.14"
default-features = false default-features = false
[dependencies.defmt]
version = "0.3"
optional = true
[features] [features]
default = ["rt"] default = ["rt"]
rt = ["va108xx/rt"] rt = ["va108xx/rt"]
defmt = ["dep:defmt", "fugit/defmt"]
[package.metadata.docs.rs] [package.metadata.docs.rs]
all-features = true all-features = true
rustdoc-args = ["--cfg", "docs_rs", "--generate-link-to-definition"] rustdoc-args = ["--generate-link-to-definition"]

View File

@ -4,7 +4,7 @@
# HAL for the Vorago VA108xx MCU family # HAL for the Vorago VA108xx MCU family
This repository contains the **H**ardware **A**bstraction **L**ayer (HAL), which is an additional This repository contains the **H**ardware **A**bstraction **L**ayer (HAL), which is an additional
hardware abstraction on top of the [peripheral access API](https://egit.irs.uni-stuttgart.de/rust/va108xx). hardware abstraction on top of the [peripheral access API](https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/src/branch/main/va108xx).
It is the result of reading the datasheet for the device and encoding a type-safe layer over the It is the result of reading the datasheet for the device and encoding a type-safe layer over the
raw PAC. This crate also implements traits specified by the raw PAC. This crate also implements traits specified by the
@ -39,7 +39,7 @@ your custom board.
The hello world of embedded development is usually to blinky a LED. This example The hello world of embedded development is usually to blinky a LED. This example
is contained within the is contained within the
[examples folder](https://egit.irs.uni-stuttgart.de/rust/va108xx-hal/src/branch/main/examples/blinky.rs). [examples folder](https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/src/branch/main/examples/simple/examples/blinky.rs).
1. Set up your Rust cross-compiler if you have not done so yet. See more in the [build chapter](#Building) 1. Set up your Rust cross-compiler if you have not done so yet. See more in the [build chapter](#Building)
2. Create a new binary crate with `cargo init` 2. Create a new binary crate with `cargo init`

View File

@ -36,8 +36,7 @@
//! Users may try to convert value-level pins back to their type-level //! Users may try to convert value-level pins back to their type-level
//! equivalents. However, this option is fallible, because the compiler cannot //! equivalents. However, this option is fallible, because the compiler cannot
//! guarantee the pin has the correct ID or is in the correct mode at //! guarantee the pin has the correct ID or is in the correct mode at
//! compile-time. Use [`TryFrom`](core::convert::TryFrom)/ //! compile-time. Use [TryFrom]/[TryInto] for this conversion.
//! [`TryInto`](core::convert::TryInto) for this conversion.
//! //!
//! ``` //! ```
//! // Convert to a `DynPin` //! // Convert to a `DynPin`
@ -55,10 +54,10 @@
//! `Error = core::convert::Infallible`, the value-level API can return a real //! `Error = core::convert::Infallible`, the value-level API can return a real
//! error. If the [`DynPin`] is not in the correct [`DynPinMode`] for the //! error. If the [`DynPin`] is not in the correct [`DynPinMode`] for the
//! operation, the trait functions will return //! operation, the trait functions will return
//! [`InvalidPinType`](PinError::InvalidPinType). //! [InvalidPinTypeError].
use super::{ use super::{
pins::{FilterType, InterruptEdge, InterruptLevel, Pin, PinId, PinMode, PinState}, pin::{FilterType, InterruptEdge, InterruptLevel, Pin, PinId, PinMode, PinState},
reg::RegisterInterface, reg::RegisterInterface,
}; };
use crate::{clock::FilterClkSel, pac, FunSel, IrqCfg}; use crate::{clock::FilterClkSel, pac, FunSel, IrqCfg};

View File

@ -3,10 +3,10 @@
//! The implementation of this GPIO module is heavily based on the //! The implementation of this GPIO module is heavily based on the
//! [ATSAMD HAL implementation](https://docs.rs/atsamd-hal/latest/atsamd_hal/gpio/index.html). //! [ATSAMD HAL implementation](https://docs.rs/atsamd-hal/latest/atsamd_hal/gpio/index.html).
//! //!
//! This API provides two different submodules, [`mod@pins`] and [`dynpins`], //! This API provides two different submodules, [pin] and [dynpin],
//! representing two different ways to handle GPIO pins. The default, [`mod@pins`], //! representing two different ways to handle GPIO pins. The default, [pin],
//! is a type-level API that tracks the state of each pin at compile-time. The //! is a type-level API that tracks the state of each pin at compile-time. The
//! alternative, [`dynpins`] is a type-erased, value-level API that tracks the //! alternative, [dynpin] is a type-erased, value-level API that tracks the
//! state of each pin at run-time. //! state of each pin at run-time.
//! //!
//! The type-level API is strongly preferred. By representing the state of each //! The type-level API is strongly preferred. By representing the state of each
@ -14,14 +14,13 @@
//! compile-time. Furthermore, the type-level API has absolutely zero run-time //! compile-time. Furthermore, the type-level API has absolutely zero run-time
//! cost. //! cost.
//! //!
//! If needed, [`dynpins`] can be used to erase the type-level differences //! If needed, [dynpin] can be used to erase the type-level differences
//! between pins. However, by doing so, pins must now be tracked at run-time, //! between pins. However, by doing so, pins must now be tracked at run-time,
//! and each pin has a non-zero memory footprint. //! and each pin has a non-zero memory footprint.
//! //!
//! ## Examples //! ## Examples
//! //!
//! - [Blinky example](https://egit.irs.uni-stuttgart.de/rust/va108xx-hal/src/branch/main/examples/blinky.rs) //! - [Blinky example](https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/src/branch/main/examples/simple/examples/blinky.rs)
//!
#[derive(Debug, PartialEq, Eq)] #[derive(Debug, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))] #[cfg_attr(feature = "defmt", derive(defmt::Format))]
@ -102,10 +101,10 @@ macro_rules! common_reg_if_functions {
}; };
} }
pub mod dynpins; pub mod dynpin;
pub use dynpins::*; pub use dynpin::*;
pub mod pins; pub mod pin;
pub use pins::*; pub use pin::*;
mod reg; mod reg;

View File

@ -29,73 +29,48 @@
//! } //! }
//! ``` //! ```
//! //!
//! A `PinId` identifies a pin by it's group (A, B, C or D) and pin number. Each //! A [PinId] identifies a pin by it's group (A or B) and pin number. Each
//! `PinId` instance is named according to its datasheet identifier, e.g. //! [PinId] instance is named according to its datasheet identifier, e.g.
//! [`PA02`]. //! [PA2].
//! //!
//! A `PinMode` represents the various pin modes. The available `PinMode` //! A [PinMode] represents the various pin modes. The available [PinMode]
//! variants are [`Disabled`], [`Input`], [`Interrupt`], [`Output`] and //! variants are [`Input`], [`Output`] and [`Alternate`], each with its own
//! [`Alternate`], each with its own corresponding configurations. //! corresponding configurations.
//! //!
//! It is not possible for users to create new instances of a [`Pin`]. Singleton //! It is not possible for users to create new instances of a [`Pin`]. Singleton
//! instances of each pin are made available to users through the [`Pins`] //! instances of each pin are made available to users through the PinsX
//! struct. //! struct.
//! //!
//! To create the [`Pins`] struct, users must supply the PAC //! Example for the pins of PORT A:
//! [`PORT`](crate::pac::PORT) peripheral. The [`Pins`] struct takes //!
//! ownership of the [`PORT`] and provides the corresponding pins. Each [`Pin`] //! To create the [PinsA] struct, users must supply the PAC
//! within the [`Pins`] struct can be moved out and used individually. //! [Port](crate::pac::Porta) peripheral. The [PinsA] struct takes
//! ownership of the [Porta] and provides the corresponding pins. Each [`Pin`]
//! within the [PinsA] struct can be moved out and used individually.
//! //!
//! //!
//! ``` //! ```
//! let mut peripherals = Peripherals::take().unwrap(); //! let mut peripherals = Peripherals::take().unwrap();
//! let pins = Pins::new(peripherals.PORT); //! let pinsa = PinsA::new(peripherals.PORT);
//! ``` //! ```
//! //!
//! Pins can be converted between modes using several different methods. //! Pins can be converted between modes using several different methods.
//! //!
//! ``` //! ```no_run
//! // Use one of the literal function names //! // Use one of the literal function names
//! let pa27 = pins.pa27.into_floating_input(); //! let pa0 = pinsa.pa0.into_floating_input();
//! // Use a generic method and one of the `PinMode` variant types //! // Use a generic method and one of the `PinMode` variant types
//! let pa27 = pins.pa27.into_mode::<FloatingInput>(); //! let pa0 = pinsa.pa0.into_mode::<FloatingInput>();
//! // Specify the target type and use `From`/`Into` //! // Specify the target type and use `From`/`Into`
//! let pa27: Pin<PA27, FloatingInput> = pins.pa27.into(); //! let pa0: Pin<PA0, FloatingInput> = pinsa.pa27.into();
//! ``` //! ```
//! //!
//! # Embedded HAL traits //! # Embedded HAL traits
//! //!
//! This module implements all of the embedded HAL GPIO traits for each [`Pin`] //! This module implements all of the embedded HAL GPIO traits for each [`Pin`]
//! in the corresponding [`PinMode`]s, namely: [`InputPin`], [`OutputPin`], //! in the corresponding [`PinMode`]s, namely: [`InputPin`], [`OutputPin`],
//! [`ToggleableOutputPin`] and [`StatefulOutputPin`]. //! and [`StatefulOutputPin`].
//! use super::dynpin::{DynAlternate, DynGroup, DynInput, DynOutput, DynPinId, DynPinMode};
//! For example, you can control the logic level of an `OutputPin` like so
//!
//! ```
//! use atsamd_hal::pac::Peripherals;
//! use atsamd_hal::gpio::Pins;
//! use crate::ehal_02::digital::v2::OutputPin;
//!
//! let mut peripherals = Peripherals::take().unwrap();
//! let mut pins = Pins::new(peripherals.PORT);
//! pins.pa27.set_high();
//! ```
//!
//! # Type-level features
//!
//! This module also provides additional, type-level tools to work with GPIO
//! pins.
//!
//! The [`OptionalPinId`] and [`OptionalPin`] traits use the [`OptionalKind`]
//! pattern to act as type-level versions of [`Option`] for `PinId` and `Pin`
//! respectively. And the [`AnyPin`] trait defines an [`AnyKind`] type class
//! for all `Pin` types.
//!
//! [type classes]: crate::typelevel#type-classes
//! [type-level enum]: crate::typelevel#type-level-enum
//! [`OptionalKind`]: crate::typelevel#optionalkind-trait-pattern
//! [`AnyKind`]: crate::typelevel#anykind-trait-pattern
use super::dynpins::{DynAlternate, DynGroup, DynInput, DynOutput, DynPinId, DynPinMode};
use super::reg::RegisterInterface; use super::reg::RegisterInterface;
use crate::{ use crate::{
pac::{Irqsel, Porta, Portb, Sysconfig}, pac::{Irqsel, Porta, Portb, Sysconfig},
@ -137,9 +112,9 @@ pub enum PinState {
/// Type-level enum for input configurations /// Type-level enum for input configurations
/// ///
/// The valid options are [`Floating`], [`PullDown`] and [`PullUp`]. /// The valid options are [Floating], [PullDown] and [PullUp].
pub trait InputConfig: Sealed { pub trait InputConfig: Sealed {
/// Corresponding [`DynInput`](super::DynInput) /// Corresponding [DynInput]
const DYN: DynInput; const DYN: DynInput;
} }
@ -297,9 +272,9 @@ pub type Reset = InputFloating;
/// Type-level enum representing pin modes /// Type-level enum representing pin modes
/// ///
/// The valid options are [`Input`], [`Output`] and [`Alternate`]. /// The valid options are [Input], [Output] and [Alternate].
pub trait PinMode: Sealed { pub trait PinMode: Sealed {
/// Corresponding [`DynPinMode`](super::DynPinMode) /// Corresponding [DynPinMode]
const DYN: DynPinMode; const DYN: DynPinMode;
} }
@ -319,7 +294,7 @@ impl<C: AlternateConfig> PinMode for Alternate<C> {
/// Type-level enum for pin IDs /// Type-level enum for pin IDs
pub trait PinId: Sealed { pub trait PinId: Sealed {
/// Corresponding [`DynPinId`](super::DynPinId) /// Corresponding [DynPinId]
const DYN: DynPinId; const DYN: DynPinId;
} }
@ -344,7 +319,7 @@ macro_rules! pin_id {
// Pin // Pin
//================================================================================================== //==================================================================================================
/// A type-level GPIO pin, parameterized by [`PinId`] and [`PinMode`] types /// A type-level GPIO pin, parameterized by [PinId] and [PinMode] types
pub struct Pin<I: PinId, M: PinMode> { pub struct Pin<I: PinId, M: PinMode> {
pub(in crate::gpio) regs: Registers<I>, pub(in crate::gpio) regs: Registers<I>,
@ -352,12 +327,12 @@ pub struct Pin<I: PinId, M: PinMode> {
} }
impl<I: PinId, M: PinMode> Pin<I, M> { impl<I: PinId, M: PinMode> Pin<I, M> {
/// Create a new [`Pin`] /// Create a new [Pin]
/// ///
/// # Safety /// # Safety
/// ///
/// Each [`Pin`] must be a singleton. For a given [`PinId`], there must be /// Each [Pin] must be a singleton. For a given [PinId], there must be
/// at most one corresponding [`Pin`] in existence at any given time. /// at most one corresponding [Pin] in existence at any given time.
/// Violating this requirement is `unsafe`. /// Violating this requirement is `unsafe`.
#[inline] #[inline]
pub(crate) unsafe fn new() -> Pin<I, M> { pub(crate) unsafe fn new() -> Pin<I, M> {

View File

@ -1,5 +1,5 @@
use super::dynpins::{self, DynGroup, DynPinId, DynPinMode}; use super::dynpin::{self, DynGroup, DynPinId, DynPinMode};
use super::pins::{FilterType, InterruptEdge, InterruptLevel, PinState}; use super::pin::{FilterType, InterruptEdge, InterruptLevel, PinState};
use super::IsMaskedError; use super::IsMaskedError;
use crate::clock::FilterClkSel; use crate::clock::FilterClkSel;
use va108xx::{ioconfig, porta}; use va108xx::{ioconfig, porta};
@ -30,7 +30,7 @@ impl From<DynPinMode> for ModeFields {
use DynPinMode::*; use DynPinMode::*;
match mode { match mode {
Input(config) => { Input(config) => {
use dynpins::DynInput::*; use dynpin::DynInput::*;
fields.dir = false; fields.dir = false;
match config { match config {
Floating => (), Floating => (),
@ -44,7 +44,7 @@ impl From<DynPinMode> for ModeFields {
} }
} }
Output(config) => { Output(config) => {
use dynpins::DynOutput::*; use dynpin::DynOutput::*;
fields.dir = true; fields.dir = true;
match config { match config {
PushPull => (), PushPull => (),

File diff suppressed because it is too large Load Diff

View File

@ -1,5 +1,5 @@
#![no_std] #![no_std]
#![cfg_attr(docs_rs, feature(doc_auto_cfg))] #![cfg_attr(docsrs, feature(doc_auto_cfg))]
pub use va108xx; pub use va108xx;
pub use va108xx as pac; pub use va108xx as pac;
@ -15,7 +15,6 @@ pub mod time;
pub mod timer; pub mod timer;
pub mod typelevel; pub mod typelevel;
pub mod uart; pub mod uart;
pub mod utility;
#[derive(Debug, Eq, Copy, Clone, PartialEq)] #[derive(Debug, Eq, Copy, Clone, PartialEq)]
pub enum FunSel { pub enum FunSel {
@ -98,3 +97,21 @@ pub fn port_mux(
} }
} }
} }
/// Enable a specific interrupt using the NVIC peripheral.
///
/// # Safety
///
/// This function is `unsafe` because it can break mask-based critical sections.
#[inline]
pub unsafe fn enable_interrupt(irq: pac::Interrupt) {
unsafe {
cortex_m::peripheral::NVIC::unmask(irq);
}
}
/// Disable a specific interrupt using the NVIC peripheral.
#[inline]
pub fn disable_interrupt(irq: pac::Interrupt) {
cortex_m::peripheral::NVIC::mask(irq);
}

View File

@ -1,3 +1,5 @@
//! Prelude //! Prelude
pub use fugit::ExtU32 as _; pub use fugit::ExtU32 as _;
pub use fugit::RateExtU32 as _; pub use fugit::RateExtU32 as _;
pub use crate::time::*;

View File

@ -4,7 +4,7 @@
//! //!
//! ## Examples //! ## Examples
//! //!
//! - [PWM example](https://egit.irs.uni-stuttgart.de/rust/va108xx-hal/src/branch/main/examples/pwm.rs) //! - [PWM example](https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/src/branch/main/examples/simple/examples/pwm.rs)
use core::convert::Infallible; use core::convert::Infallible;
use core::marker::PhantomData; use core::marker::PhantomData;
@ -158,9 +158,9 @@ where
{ {
/// Create a new stronlgy typed PWM pin /// Create a new stronlgy typed PWM pin
pub fn new( pub fn new(
vtp: (Pin, Tim),
sys_clk: impl Into<Hertz> + Copy,
sys_cfg: &mut pac::Sysconfig, sys_cfg: &mut pac::Sysconfig,
sys_clk: impl Into<Hertz> + Copy,
tim_and_pin: (Pin, Tim),
initial_period: impl Into<Hertz> + Copy, initial_period: impl Into<Hertz> + Copy,
) -> Self { ) -> Self {
let mut pin = PwmPin { let mut pin = PwmPin {
@ -171,7 +171,7 @@ where
current_rst_val: 0, current_rst_val: 0,
sys_clk: sys_clk.into(), sys_clk: sys_clk.into(),
}, },
reg: unsafe { TimAndPinRegister::new(vtp.0, vtp.1) }, reg: unsafe { TimAndPinRegister::new(tim_and_pin.0, tim_and_pin.1) },
mode: PhantomData, mode: PhantomData,
}; };
enable_peripheral_clock(sys_cfg, crate::clock::PeripheralClocks::Gpio); enable_peripheral_clock(sys_cfg, crate::clock::PeripheralClocks::Gpio);
@ -225,12 +225,13 @@ where
(Pin, Tim): ValidTimAndPin<Pin, Tim>, (Pin, Tim): ValidTimAndPin<Pin, Tim>,
{ {
pub fn pwma( pub fn pwma(
vtp: (Pin, Tim),
sys_clk: impl Into<Hertz> + Copy,
sys_cfg: &mut pac::Sysconfig, sys_cfg: &mut pac::Sysconfig,
sys_clk: impl Into<Hertz> + Copy,
pin_and_tim: (Pin, Tim),
initial_period: impl Into<Hertz> + Copy, initial_period: impl Into<Hertz> + Copy,
) -> Self { ) -> Self {
let mut pin: PwmPin<Pin, Tim, PwmA> = Self::new(vtp, sys_clk, sys_cfg, initial_period); let mut pin: PwmPin<Pin, Tim, PwmA> =
Self::new(sys_cfg, sys_clk, pin_and_tim, initial_period);
pin.enable_pwm_a(); pin.enable_pwm_a();
pin pin
} }
@ -241,12 +242,13 @@ where
(Pin, Tim): ValidTimAndPin<Pin, Tim>, (Pin, Tim): ValidTimAndPin<Pin, Tim>,
{ {
pub fn pwmb( pub fn pwmb(
vtp: (Pin, Tim),
sys_clk: impl Into<Hertz> + Copy,
sys_cfg: &mut pac::Sysconfig, sys_cfg: &mut pac::Sysconfig,
sys_clk: impl Into<Hertz> + Copy,
pin_and_tim: (Pin, Tim),
initial_period: impl Into<Hertz> + Copy, initial_period: impl Into<Hertz> + Copy,
) -> Self { ) -> Self {
let mut pin: PwmPin<Pin, Tim, PwmB> = Self::new(vtp, sys_clk, sys_cfg, initial_period); let mut pin: PwmPin<Pin, Tim, PwmB> =
Self::new(sys_cfg, sys_clk, pin_and_tim, initial_period);
pin.enable_pwm_b(); pin.enable_pwm_b();
pin pin
} }

File diff suppressed because it is too large Load Diff

View File

@ -1,18 +1,19 @@
use crate::{pac, PeripheralSelect}; use crate::{pac, PeripheralSelect};
#[derive(PartialEq, Eq, Debug)] #[derive(PartialEq, Eq, Debug)]
pub struct InvalidounterResetVal(pub(crate) ()); #[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct InvalidCounterResetVal(pub(crate) ());
/// Enable scrubbing for the ROM /// Enable scrubbing for the ROM
/// ///
/// Returns [`UtilityError::InvalidCounterResetVal`] if the scrub rate is 0 /// Returns [InvalidCounterResetVal] if the scrub rate is 0
/// (equivalent to disabling) or larger than 24 bits /// (equivalent to disabling) or larger than 24 bits
pub fn enable_rom_scrubbing( pub fn enable_rom_scrubbing(
syscfg: &mut pac::Sysconfig, syscfg: &mut pac::Sysconfig,
scrub_rate: u32, scrub_rate: u32,
) -> Result<(), InvalidounterResetVal> { ) -> Result<(), InvalidCounterResetVal> {
if scrub_rate == 0 || scrub_rate > u32::pow(2, 24) { if scrub_rate == 0 || scrub_rate > u32::pow(2, 24) {
return Err(InvalidounterResetVal(())); return Err(InvalidCounterResetVal(()));
} }
syscfg.rom_scrub().write(|w| unsafe { w.bits(scrub_rate) }); syscfg.rom_scrub().write(|w| unsafe { w.bits(scrub_rate) });
Ok(()) Ok(())
@ -24,14 +25,14 @@ pub fn disable_rom_scrubbing(syscfg: &mut pac::Sysconfig) {
/// Enable scrubbing for the RAM /// Enable scrubbing for the RAM
/// ///
/// Returns [`UtilityError::InvalidCounterResetVal`] if the scrub rate is 0 /// Returns [InvalidCounterResetVal] if the scrub rate is 0
/// (equivalent to disabling) or larger than 24 bits /// (equivalent to disabling) or larger than 24 bits
pub fn enable_ram_scrubbing( pub fn enable_ram_scrubbing(
syscfg: &mut pac::Sysconfig, syscfg: &mut pac::Sysconfig,
scrub_rate: u32, scrub_rate: u32,
) -> Result<(), InvalidounterResetVal> { ) -> Result<(), InvalidCounterResetVal> {
if scrub_rate == 0 || scrub_rate > u32::pow(2, 24) { if scrub_rate == 0 || scrub_rate > u32::pow(2, 24) {
return Err(InvalidounterResetVal(())); return Err(InvalidCounterResetVal(()));
} }
syscfg.ram_scrub().write(|w| unsafe { w.bits(scrub_rate) }); syscfg.ram_scrub().write(|w| unsafe { w.bits(scrub_rate) });
Ok(()) Ok(())

View File

@ -2,11 +2,12 @@
//! //!
//! ## Examples //! ## Examples
//! //!
//! - [MS and second tick implementation](https://egit.irs.uni-stuttgart.de/rust/va108xx-hal/src/branch/main/examples/timer-ticks.rs) //! - [MS and second tick implementation](https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/src/branch/main/examples/simple/examples/timer-ticks.rs)
//! - [Cascade feature example](https://egit.irs.uni-stuttgart.de/rust/va108xx-hal/src/branch/main/examples/cascade.rs) //! - [Cascade feature example](https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/src/branch/main/examples/simple/examples/cascade.rs)
pub use crate::IrqCfg; pub use crate::IrqCfg;
use crate::{ use crate::{
clock::{enable_peripheral_clock, PeripheralClocks}, clock::{enable_peripheral_clock, PeripheralClocks},
enable_interrupt,
gpio::{ gpio::{
AltFunc1, AltFunc2, AltFunc3, DynPinId, Pin, PinId, PA0, PA1, PA10, PA11, PA12, PA13, PA14, AltFunc1, AltFunc2, AltFunc3, DynPinId, Pin, PinId, PA0, PA1, PA10, PA11, PA12, PA13, PA14,
PA15, PA2, PA24, PA25, PA26, PA27, PA28, PA29, PA3, PA30, PA31, PA4, PA5, PA6, PA7, PA8, PA15, PA2, PA24, PA25, PA26, PA27, PA28, PA29, PA3, PA30, PA31, PA4, PA5, PA6, PA7, PA8,
@ -17,10 +18,9 @@ use crate::{
time::Hertz, time::Hertz,
timer, timer,
typelevel::Sealed, typelevel::Sealed,
utility::unmask_irq,
}; };
use core::cell::Cell; use core::cell::Cell;
use cortex_m::interrupt::Mutex; use critical_section::Mutex;
use fugit::RateExtU32; use fugit::RateExtU32;
const IRQ_DST_NONE: u32 = 0xffffffff; const IRQ_DST_NONE: u32 = 0xffffffff;
@ -72,25 +72,46 @@ pub enum CascadeSel {
Csd2 = 2, Csd2 = 2,
} }
#[derive(Debug, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct InvalidCascadeSourceId;
/// The numbers are the base numbers for bundles like PORTA, PORTB or TIM /// The numbers are the base numbers for bundles like PORTA, PORTB or TIM
#[derive(Debug, PartialEq, Eq)] #[derive(Debug, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[repr(u8)]
pub enum CascadeSource { pub enum CascadeSource {
PortABase = 0, PortA(u8),
PortBBase = 32, PortB(u8),
TimBase = 64, Tim(u8),
RamSbe = 96, RamSbe = 96,
RamMbe = 97, RamMbe = 97,
RomSbe = 98, RomSbe = 98,
RomMbe = 99, RomMbe = 99,
Txev = 100, Txev = 100,
ClockDividerBase = 120, ClockDivider(u8),
} }
#[derive(Debug, PartialEq, Eq)] impl CascadeSource {
pub enum TimerErrors { fn id(&self) -> Result<u8, InvalidCascadeSourceId> {
Canceled, let port_check = |base: u8, id: u8, len: u8| {
/// Invalid input for Cascade source if id > len - 1 {
InvalidCsdSourceInput, return Err(InvalidCascadeSourceId);
}
Ok(base + id)
};
match self {
CascadeSource::PortA(id) => port_check(0, *id, 32),
CascadeSource::PortB(id) => port_check(32, *id, 32),
CascadeSource::Tim(id) => port_check(64, *id, 24),
CascadeSource::RamSbe => Ok(96),
CascadeSource::RamMbe => Ok(97),
CascadeSource::RomSbe => Ok(98),
CascadeSource::RomMbe => Ok(99),
CascadeSource::Txev => Ok(100),
CascadeSource::ClockDivider(id) => port_check(120, *id, 8),
}
}
} }
//================================================================================================== //==================================================================================================
@ -360,89 +381,26 @@ pub struct CountDownTimer<TIM: ValidTim> {
listening: bool, listening: bool,
} }
fn enable_tim_clk(syscfg: &mut pac::Sysconfig, idx: u8) { #[inline(always)]
pub fn enable_tim_clk(syscfg: &mut pac::Sysconfig, idx: u8) {
syscfg syscfg
.tim_clk_enable() .tim_clk_enable()
.modify(|r, w| unsafe { w.bits(r.bits() | (1 << idx)) }); .modify(|r, w| unsafe { w.bits(r.bits() | (1 << idx)) });
} }
#[inline(always)]
pub fn disable_tim_clk(syscfg: &mut pac::Sysconfig, idx: u8) {
syscfg
.tim_clk_enable()
.modify(|r, w| unsafe { w.bits(r.bits() & !(1 << idx)) });
}
unsafe impl<TIM: ValidTim> TimRegInterface for CountDownTimer<TIM> { unsafe impl<TIM: ValidTim> TimRegInterface for CountDownTimer<TIM> {
fn tim_id(&self) -> u8 { fn tim_id(&self) -> u8 {
TIM::TIM_ID TIM::TIM_ID
} }
} }
macro_rules! csd_sel {
($func_name:ident, $csd_reg:ident) => {
/// Configure the Cascade sources
pub fn $func_name(
&mut self,
src: CascadeSource,
id: Option<u8>,
) -> Result<(), TimerErrors> {
let mut id_num = 0;
if let CascadeSource::PortABase
| CascadeSource::PortBBase
| CascadeSource::ClockDividerBase
| CascadeSource::TimBase = src
{
if id.is_none() {
return Err(TimerErrors::InvalidCsdSourceInput);
}
}
if id.is_some() {
id_num = id.unwrap();
}
match src {
CascadeSource::PortABase => {
if id_num > 55 {
return Err(TimerErrors::InvalidCsdSourceInput);
}
self.tim.reg().$csd_reg().write(|w| unsafe {
w.cassel().bits(CascadeSource::PortABase as u8 + id_num)
});
Ok(())
}
CascadeSource::PortBBase => {
if id_num > 23 {
return Err(TimerErrors::InvalidCsdSourceInput);
}
self.tim.reg().$csd_reg().write(|w| unsafe {
w.cassel().bits(CascadeSource::PortBBase as u8 + id_num)
});
Ok(())
}
CascadeSource::TimBase => {
if id_num > 23 {
return Err(TimerErrors::InvalidCsdSourceInput);
}
self.tim.reg().$csd_reg().write(|w| unsafe {
w.cassel().bits(CascadeSource::TimBase as u8 + id_num)
});
Ok(())
}
CascadeSource::ClockDividerBase => {
if id_num > 7 {
return Err(TimerErrors::InvalidCsdSourceInput);
}
self.tim.reg().cascade0().write(|w| unsafe {
w.cassel()
.bits(CascadeSource::ClockDividerBase as u8 + id_num)
});
Ok(())
}
_ => {
self.tim
.reg()
.$csd_reg()
.write(|w| unsafe { w.cassel().bits(src as u8) });
Ok(())
}
}
}
};
}
impl<TIM: ValidTim> CountDownTimer<TIM> { impl<TIM: ValidTim> CountDownTimer<TIM> {
/// Configures a TIM peripheral as a periodic count down timer /// Configures a TIM peripheral as a periodic count down timer
pub fn new(syscfg: &mut pac::Sysconfig, sys_clk: impl Into<Hertz>, tim: TIM) -> Self { pub fn new(syscfg: &mut pac::Sysconfig, sys_clk: impl Into<Hertz>, tim: TIM) -> Self {
@ -554,18 +512,18 @@ impl<TIM: ValidTim> CountDownTimer<TIM> {
#[inline(always)] #[inline(always)]
pub fn enable(&mut self) { pub fn enable(&mut self) {
self.tim.reg().ctrl().modify(|_, w| w.enable().set_bit());
if let Some(irq_cfg) = self.irq_cfg { if let Some(irq_cfg) = self.irq_cfg {
self.enable_interrupt(); self.enable_interrupt();
if irq_cfg.enable { if irq_cfg.enable {
unmask_irq(irq_cfg.irq); unsafe { enable_interrupt(irq_cfg.irq) };
} }
} }
self.tim.reg().enable().write(|w| unsafe { w.bits(1) });
} }
#[inline(always)] #[inline(always)]
pub fn disable(&mut self) { pub fn disable(&mut self) {
self.tim.reg().ctrl().modify(|_, w| w.enable().clear_bit()); self.tim.reg().enable().write(|w| unsafe { w.bits(0) });
} }
/// Disable the counter, setting both enable and active bit to 0 /// Disable the counter, setting both enable and active bit to 0
@ -619,9 +577,32 @@ impl<TIM: ValidTim> CountDownTimer<TIM> {
}); });
} }
csd_sel!(cascade_0_source, cascade0); pub fn cascade_0_source(&mut self, src: CascadeSource) -> Result<(), InvalidCascadeSourceId> {
csd_sel!(cascade_1_source, cascade1); let id = src.id()?;
csd_sel!(cascade_2_source, cascade2); self.tim
.reg()
.cascade0()
.write(|w| unsafe { w.cassel().bits(id) });
Ok(())
}
pub fn cascade_1_source(&mut self, src: CascadeSource) -> Result<(), InvalidCascadeSourceId> {
let id = src.id()?;
self.tim
.reg()
.cascade1()
.write(|w| unsafe { w.cassel().bits(id) });
Ok(())
}
pub fn cascade_2_source(&mut self, src: CascadeSource) -> Result<(), InvalidCascadeSourceId> {
let id = src.id()?;
self.tim
.reg()
.cascade2()
.write(|w| unsafe { w.cassel().bits(id) });
Ok(())
}
pub fn curr_freq(&self) -> Hertz { pub fn curr_freq(&self) -> Hertz {
self.curr_freq self.curr_freq
@ -656,12 +637,13 @@ impl<TIM: ValidTim> CountDownTimer<TIM> {
} }
} }
pub fn cancel(&mut self) -> Result<(), TimerErrors> { /// Returns [false] if the timer was not active, and true otherwise.
pub fn cancel(&mut self) -> bool {
if !self.tim.reg().ctrl().read().enable().bit_is_set() { if !self.tim.reg().ctrl().read().enable().bit_is_set() {
return Err(TimerErrors::Canceled); return false;
} }
self.tim.reg().ctrl().write(|w| w.enable().clear_bit()); self.tim.reg().ctrl().write(|w| w.enable().clear_bit());
Ok(()) true
} }
} }
@ -747,7 +729,7 @@ pub fn set_up_ms_delay_provider<TIM: ValidTim>(
/// This function can be called in a specified interrupt handler to increment /// This function can be called in a specified interrupt handler to increment
/// the MS counter /// the MS counter
pub fn default_ms_irq_handler() { pub fn default_ms_irq_handler() {
cortex_m::interrupt::free(|cs| { critical_section::with(|cs| {
let mut ms = MS_COUNTER.borrow(cs).get(); let mut ms = MS_COUNTER.borrow(cs).get();
ms += 1; ms += 1;
MS_COUNTER.borrow(cs).set(ms); MS_COUNTER.borrow(cs).set(ms);
@ -756,7 +738,7 @@ pub fn default_ms_irq_handler() {
/// Get the current MS tick count /// Get the current MS tick count
pub fn get_ms_ticks() -> u32 { pub fn get_ms_ticks() -> u32 {
cortex_m::interrupt::free(|cs| MS_COUNTER.borrow(cs).get()) critical_section::with(|cs| MS_COUNTER.borrow(cs).get())
} }
//================================================================================================== //==================================================================================================

View File

@ -2,23 +2,23 @@
//! //!
//! ## Examples //! ## Examples
//! //!
//! - [UART simple example](https://egit.irs.uni-stuttgart.de/rust/va108xx-hal/src/branch/main/examples/uart.rs) //! - [UART simple example](https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/src/branch/main/examples/simple/examples/uart.rs)
//! - [UART with IRQ and RTIC](https://egit.irs.uni-stuttgart.de/rust/va108xx-hal/src/branch/main/examples/uart-irq-rtic.rs) //! - [UART with IRQ and RTIC](https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/src/branch/main/examples/rtic/bin/uart-rtic.rs)
use core::{marker::PhantomData, ops::Deref}; use core::{marker::PhantomData, ops::Deref};
use embedded_hal_nb::serial::Read; use embedded_hal_nb::serial::Read;
use fugit::RateExtU32; use fugit::RateExtU32;
use libm::floorf;
pub use crate::IrqCfg; pub use crate::IrqCfg;
use crate::{ use crate::{
clock::{self, enable_peripheral_clock, PeripheralClocks}, clock::{enable_peripheral_clock, PeripheralClocks},
gpio::pins::{ enable_interrupt,
gpio::pin::{
AltFunc1, AltFunc2, AltFunc3, Pin, PA16, PA17, PA18, PA19, PA2, PA26, PA27, PA3, PA30, AltFunc1, AltFunc2, AltFunc3, Pin, PA16, PA17, PA18, PA19, PA2, PA26, PA27, PA3, PA30,
PA31, PA8, PA9, PB18, PB19, PB20, PB21, PB22, PB23, PB6, PB7, PB8, PB9, PA31, PA8, PA9, PB18, PB19, PB20, PB21, PB22, PB23, PB6, PB7, PB8, PB9,
}, },
pac::{self, uarta as uart_base}, pac::{self, uarta as uart_base},
time::Hertz, time::Hertz,
utility::unmask_irq, PeripheralSelect,
}; };
//================================================================================================== //==================================================================================================
@ -306,7 +306,7 @@ pub struct UartBase<Uart> {
} }
/// Serial abstraction. Entry point to create a new UART /// Serial abstraction. Entry point to create a new UART
pub struct Uart<Uart, Pins> { pub struct Uart<Uart, Pins> {
uart_base: UartBase<Uart>, inner: UartBase<Uart>,
pins: Pins, pins: Pins,
} }
@ -353,6 +353,7 @@ impl<UART> Tx<UART> {
pub trait Instance: Deref<Target = uart_base::RegisterBlock> { pub trait Instance: Deref<Target = uart_base::RegisterBlock> {
fn ptr() -> *const uart_base::RegisterBlock; fn ptr() -> *const uart_base::RegisterBlock;
const IDX: u8; const IDX: u8;
const PERIPH_SEL: PeripheralSelect;
} }
impl<UART: Instance> UartBase<UART> { impl<UART: Instance> UartBase<UART> {
@ -363,12 +364,19 @@ impl<UART: Instance> UartBase<UART> {
false => 16, false => 16,
true => 8, true => 8,
}; };
// This is the calculation: (64.0 * (x - integer_part as f32) + 0.5) as u32 without floating
// point calculations.
let frac = ((sys_clk.raw() % (config.baudrate.raw() * 16)) * 64
+ (config.baudrate.raw() * 8))
/ (config.baudrate.raw() * 16);
// Calculations here are derived from chapter 4.8.5 (p.79) of the datasheet.
let x = sys_clk.raw() as f32 / (config.baudrate.raw() * baud_multiplier) as f32; let x = sys_clk.raw() as f32 / (config.baudrate.raw() * baud_multiplier) as f32;
let integer_part = floorf(x) as u32; let integer_part = x as u32;
let frac = floorf(64.0 * (x - integer_part as f32) + 0.5) as u32; self.uart.clkscale().write(|w| unsafe {
self.uart w.frac().bits(frac as u8);
.clkscale() w.int().bits(integer_part)
.write(|w| unsafe { w.bits(integer_part * 64 + frac) }); });
let (paren, pareven) = match config.parity { let (paren, pareven) = match config.parity {
Parity::None => (false, false), Parity::None => (false, false),
@ -483,14 +491,34 @@ impl<UART: Instance> UartBase<UART> {
} }
} }
impl<UART, PINS> Uart<UART, PINS> impl<UartInstance, PinsInstance> Uart<UartInstance, PinsInstance>
where where
UART: Instance, UartInstance: Instance,
PinsInstance: Pins<UartInstance>,
{ {
pub fn new(
syscfg: &mut va108xx::Sysconfig,
sys_clk: impl Into<Hertz>,
uart: UartInstance,
pins: PinsInstance,
config: impl Into<Config>,
) -> Self {
crate::clock::enable_peripheral_clock(syscfg, UartInstance::PERIPH_SEL);
Uart {
inner: UartBase {
uart,
tx: Tx::new(),
rx: Rx::new(),
},
pins,
}
.init(config.into(), sys_clk.into())
}
/// This function assumes that the peripheral clock was alredy enabled /// This function assumes that the peripheral clock was alredy enabled
/// in the SYSCONFIG register /// in the SYSCONFIG register
fn init(mut self, config: Config, sys_clk: Hertz) -> Self { fn init(mut self, config: Config, sys_clk: Hertz) -> Self {
self.uart_base = self.uart_base.init(config, sys_clk); self.inner = self.inner.init(config, sys_clk);
self self
} }
@ -501,7 +529,7 @@ where
irq_cfg: IrqCfg, irq_cfg: IrqCfg,
sys_cfg: Option<&mut pac::Sysconfig>, sys_cfg: Option<&mut pac::Sysconfig>,
irq_sel: Option<&mut pac::Irqsel>, irq_sel: Option<&mut pac::Irqsel>,
) -> UartWithIrq<UART, PINS> { ) -> UartWithIrq<UartInstance, PinsInstance> {
let (uart, pins) = self.downgrade_internal(); let (uart, pins) = self.downgrade_internal();
UartWithIrq { UartWithIrq {
pins, pins,
@ -520,75 +548,75 @@ where
#[inline] #[inline]
pub fn enable_rx(&mut self) { pub fn enable_rx(&mut self) {
self.uart_base.enable_rx(); self.inner.enable_rx();
} }
#[inline] #[inline]
pub fn disable_rx(&mut self) { pub fn disable_rx(&mut self) {
self.uart_base.enable_rx(); self.inner.enable_rx();
} }
#[inline] #[inline]
pub fn enable_tx(&mut self) { pub fn enable_tx(&mut self) {
self.uart_base.enable_tx(); self.inner.enable_tx();
} }
#[inline] #[inline]
pub fn disable_tx(&mut self) { pub fn disable_tx(&mut self) {
self.uart_base.disable_tx(); self.inner.disable_tx();
} }
#[inline] #[inline]
pub fn clear_rx_fifo(&mut self) { pub fn clear_rx_fifo(&mut self) {
self.uart_base.clear_rx_fifo(); self.inner.clear_rx_fifo();
} }
#[inline] #[inline]
pub fn clear_tx_fifo(&mut self) { pub fn clear_tx_fifo(&mut self) {
self.uart_base.clear_tx_fifo(); self.inner.clear_tx_fifo();
} }
#[inline] #[inline]
pub fn clear_rx_status(&mut self) { pub fn clear_rx_status(&mut self) {
self.uart_base.clear_rx_status(); self.inner.clear_rx_status();
} }
#[inline] #[inline]
pub fn clear_tx_status(&mut self) { pub fn clear_tx_status(&mut self) {
self.uart_base.clear_tx_status(); self.inner.clear_tx_status();
} }
pub fn listen(&self, event: Event) { pub fn listen(&self, event: Event) {
self.uart_base.listen(event); self.inner.listen(event);
} }
pub fn unlisten(&self, event: Event) { pub fn unlisten(&self, event: Event) {
self.uart_base.unlisten(event); self.inner.unlisten(event);
} }
pub fn release(self) -> (UART, PINS) { pub fn release(self) -> (UartInstance, PinsInstance) {
(self.uart_base.release(), self.pins) (self.inner.release(), self.pins)
} }
fn downgrade_internal(self) -> (UartBase<UART>, PINS) { fn downgrade_internal(self) -> (UartBase<UartInstance>, PinsInstance) {
let base = UartBase { let base = UartBase {
uart: self.uart_base.uart, uart: self.inner.uart,
tx: self.uart_base.tx, tx: self.inner.tx,
rx: self.uart_base.rx, rx: self.inner.rx,
}; };
(base, self.pins) (base, self.pins)
} }
pub fn downgrade(self) -> UartBase<UART> { pub fn downgrade(self) -> UartBase<UartInstance> {
UartBase { UartBase {
uart: self.uart_base.uart, uart: self.inner.uart,
tx: self.uart_base.tx, tx: self.inner.tx,
rx: self.uart_base.rx, rx: self.inner.rx,
} }
} }
pub fn split(self) -> (Tx<UART>, Rx<UART>) { pub fn split(self) -> (Tx<UartInstance>, Rx<UartInstance>) {
self.uart_base.split() self.inner.split()
} }
} }
@ -597,6 +625,8 @@ impl Instance for pac::Uarta {
pac::Uarta::ptr() as *const _ pac::Uarta::ptr() as *const _
} }
const IDX: u8 = 0; const IDX: u8 = 0;
const PERIPH_SEL: PeripheralSelect = PeripheralSelect::Uart0;
} }
impl Instance for pac::Uartb { impl Instance for pac::Uartb {
@ -604,39 +634,11 @@ impl Instance for pac::Uartb {
pac::Uartb::ptr() as *const _ pac::Uartb::ptr() as *const _
} }
const IDX: u8 = 1; const IDX: u8 = 1;
const PERIPH_SEL: PeripheralSelect = PeripheralSelect::Uart1;
} }
macro_rules! uart_impl { impl<Uart: Instance> UartWithIrqBase<Uart> {
($($UARTX:path: ($uartx:ident, $clk_enb_enum:path),)+) => {
$(
impl<PINS: Pins<$UARTX>> Uart<$UARTX, PINS> {
pub fn $uartx(
uart: $UARTX,
pins: PINS,
config: impl Into<Config>,
syscfg: &mut pac::Sysconfig,
sys_clk: impl Into<Hertz>
) -> Self
{
enable_peripheral_clock(syscfg, $clk_enb_enum);
Uart {
uart_base: UartBase {
uart,
tx: Tx::new(),
rx: Rx::new(),
},
pins,
}
.init(config.into(), sys_clk.into())
}
}
)+
}
}
impl<UART: Instance> UartWithIrqBase<UART> {
fn init(self, sys_cfg: Option<&mut pac::Sysconfig>, irq_sel: Option<&mut pac::Irqsel>) -> Self { fn init(self, sys_cfg: Option<&mut pac::Sysconfig>, irq_sel: Option<&mut pac::Irqsel>) -> Self {
if let Some(sys_cfg) = sys_cfg { if let Some(sys_cfg) = sys_cfg {
enable_peripheral_clock(sys_cfg, PeripheralClocks::Irqsel) enable_peripheral_clock(sys_cfg, PeripheralClocks::Irqsel)
@ -644,7 +646,7 @@ impl<UART: Instance> UartWithIrqBase<UART> {
if let Some(irq_sel) = irq_sel { if let Some(irq_sel) = irq_sel {
if self.irq_info.irq_cfg.route { if self.irq_info.irq_cfg.route {
irq_sel irq_sel
.uart0(UART::IDX as usize) .uart0(Uart::IDX as usize)
.write(|w| unsafe { w.bits(self.irq_info.irq_cfg.irq as u32) }); .write(|w| unsafe { w.bits(self.irq_info.irq_cfg.irq as u32) });
} }
} }
@ -674,7 +676,9 @@ impl<UART: Instance> UartWithIrqBase<UART> {
self.uart.enable_tx(); self.uart.enable_tx();
self.enable_rx_irq_sources(enb_timeout_irq); self.enable_rx_irq_sources(enb_timeout_irq);
if self.irq_info.irq_cfg.enable { if self.irq_info.irq_cfg.enable {
unmask_irq(self.irq_info.irq_cfg.irq); unsafe {
enable_interrupt(self.irq_info.irq_cfg.irq);
}
} }
Ok(()) Ok(())
} }
@ -837,7 +841,7 @@ impl<UART: Instance> UartWithIrqBase<UART> {
self.irq_info.rx_len = 0; self.irq_info.rx_len = 0;
} }
pub fn release(self) -> UART { pub fn release(self) -> Uart {
self.uart.release() self.uart.release()
} }
} }
@ -871,10 +875,12 @@ impl<UART: Instance, PINS> UartWithIrq<UART, PINS> {
} }
} }
/*
uart_impl! { uart_impl! {
pac::Uarta: (uarta, clock::PeripheralClocks::Uart0), pac::Uarta: (uarta, clock::PeripheralClocks::Uart0),
pac::Uartb: (uartb, clock::PeripheralClocks::Uart1), pac::Uartb: (uartb, clock::PeripheralClocks::Uart1),
} }
*/
impl<Uart> Tx<Uart> where Uart: Instance {} impl<Uart> Tx<Uart> where Uart: Instance {}

View File

@ -1,16 +0,0 @@
//! # API for utility functions like the Error Detection and Correction (EDAC) block
//!
//! Some more information about the recommended scrub rates can be found on the
//! [Vorago White Paper website](https://www.voragotech.com/resources) in the
//! application note AN1212
use crate::pac;
/// Unmask and enable an IRQ with the given interrupt number
///
/// ## Safety
///
/// The unmask function can break mask-based critical sections
#[inline]
pub(crate) fn unmask_irq(irq: pac::Interrupt) {
unsafe { cortex_m::peripheral::NVIC::unmask(irq) };
}

View File

@ -4,8 +4,8 @@ version = "0.3.0"
authors = ["Robin Mueller <muellerr@irs.uni-stuttgart.de>"] authors = ["Robin Mueller <muellerr@irs.uni-stuttgart.de>"]
edition = "2021" edition = "2021"
description = "PAC for the Vorago VA108xx family of microcontrollers" description = "PAC for the Vorago VA108xx family of microcontrollers"
homepage = "https://egit.irs.uni-stuttgart.de/rust/va108xx" homepage = "https://egit.irs.uni-stuttgart.de/rust/va108xx-rs"
repository = "https://egit.irs.uni-stuttgart.de/rust/va108xx" repository = "https://egit.irs.uni-stuttgart.de/rust/va108xx-rs"
license = "Apache-2.0" license = "Apache-2.0"
keywords = ["no-std", "arm", "cortex-m", "vorago", "va108xx"] keywords = ["no-std", "arm", "cortex-m", "vorago", "va108xx"]
categories = ["embedded", "no-std", "hardware-support"] categories = ["embedded", "no-std", "hardware-support"]
@ -24,4 +24,4 @@ rt = ["cortex-m-rt/device"]
[package.metadata.docs.rs] [package.metadata.docs.rs]
all-features = true all-features = true
rustdoc-args = ["--cfg", "docs_rs", "--generate-link-to-definition"] rustdoc-args = ["--generate-link-to-definition"]

View File

@ -4,7 +4,7 @@ svd2rust release can be generated by cloning the svd2rust [repository], checking
#![allow(non_snake_case)] #![allow(non_snake_case)]
#![no_std] #![no_std]
// Manually inserted. // Manually inserted.
#![cfg_attr(docs_rs, feature(doc_auto_cfg))] #![cfg_attr(docsrs, feature(doc_auto_cfg))]
use core::marker::PhantomData; use core::marker::PhantomData;
use core::ops::Deref; use core::ops::Deref;
#[doc = r"Number available in the NVIC for configuring priority"] #[doc = r"Number available in the NVIC for configuring priority"]

View File

@ -8,9 +8,13 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
## [unreleased] ## [unreleased]
## [v0.5.1] 2024-07-04
- Update `va108xx-hal` dependency to v0.7.0
## [v0.5.0] 2024-06-16 ## [v0.5.0] 2024-06-16
- Updated `va108xx` and `va108xx-hal` dependency. - Updated `va108xx` to v0.3.0 and `va108xx-hal` dependency to v0.6.0
## [v0.4.0] ## [v0.4.0]

View File

@ -1,11 +1,11 @@
[package] [package]
name = "vorago-reb1" name = "vorago-reb1"
version = "0.5.0" version = "0.5.1"
authors = ["Robin Mueller <robin.mueller.m@gmail.com>"] authors = ["Robin Mueller <muellerr@irs.uni-stuttgart.de>"]
edition = "2021" edition = "2021"
description = "Board Support Crate for the Vorago REB1 development board" description = "Board Support Crate for the Vorago REB1 development board"
homepage = "https://egit.irs.uni-stuttgart.de/rust/vorago-reb1" homepage = "https://egit.irs.uni-stuttgart.de/rust/va108xx-rs"
repository = "https://egit.irs.uni-stuttgart.de/rust/vorago-reb1" repository = "https://egit.irs.uni-stuttgart.de/rust/va108xx-rs"
license = "Apache-2.0" license = "Apache-2.0"
keywords = ["no-std", "reb1", "cortex-m", "vorago", "va108xx"] keywords = ["no-std", "reb1", "cortex-m", "vorago", "va108xx"]
categories = ["aerospace", "embedded", "no-std", "hardware-support"] categories = ["aerospace", "embedded", "no-std", "hardware-support"]
@ -14,12 +14,14 @@ categories = ["aerospace", "embedded", "no-std", "hardware-support"]
cortex-m = { version = "0.7", features = ["critical-section-single-core"] } cortex-m = { version = "0.7", features = ["critical-section-single-core"] }
cortex-m-rt = "0.7" cortex-m-rt = "0.7"
embedded-hal = "1" embedded-hal = "1"
nb = "1"
bitfield = "0.17"
[dependencies.max116xx-10bit] [dependencies.max116xx-10bit]
version = "0.3" version = "0.3"
[dependencies.va108xx-hal] [dependencies.va108xx-hal]
version = "0.6" version = ">=0.7, <0.8"
path = "../va108xx-hal" path = "../va108xx-hal"
features = ["rt"] features = ["rt"]
@ -27,16 +29,13 @@ features = ["rt"]
rt = ["va108xx-hal/rt"] rt = ["va108xx-hal/rt"]
[dev-dependencies] [dev-dependencies]
cortex-m-rtic = "1.1"
panic-halt = "0.2" panic-halt = "0.2"
nb = "1" nb = "1"
rtt-target = "0.5"
[dev-dependencies.rtt-target] panic-rtt-target = "0.1"
version = "0.5" embedded-hal-bus = "0.2"
dummy-pin = "1"
[dev-dependencies.panic-rtt-target]
version = "0.1"
[package.metadata.docs.rs] [package.metadata.docs.rs]
all-features = true all-features = true
rustdoc-args = ["--cfg", "docs_rs", "--generate-link-to-definition"] rustdoc-args = ["--generate-link-to-definition"]

View File

@ -6,5 +6,5 @@
This is the Rust **B**oard **S**upport **P**ackage crate for the Vorago REB1 development board. This is the Rust **B**oard **S**upport **P**ackage crate for the Vorago REB1 development board.
Its aim is to provide drivers for the board features of the REB1 board Its aim is to provide drivers for the board features of the REB1 board
The BSP builds on top of the [HAL crate for VA108xx devices](https://egit.irs.uni-stuttgart.de/rust/va108xx-hal). The BSP builds on top of the [HAL crate for VA108xx devices](https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/src/branch/main/va108xx-hal).
The example folder contains some example applications using the on-board peripherals. The example folder contains some example applications using the on-board peripherals.

View File

@ -13,7 +13,7 @@ fn main() -> ! {
rprintln!("-- Vorago Temperature Sensor and I2C Example --"); rprintln!("-- Vorago Temperature Sensor and I2C Example --");
let mut dp = pac::Peripherals::take().unwrap(); let mut dp = pac::Peripherals::take().unwrap();
let mut delay = set_up_ms_delay_provider(&mut dp.sysconfig, 50.MHz(), dp.tim0); let mut delay = set_up_ms_delay_provider(&mut dp.sysconfig, 50.MHz(), dp.tim0);
let mut temp_sensor = Adt75TempSensor::new(dp.i2ca, 50.MHz(), Some(&mut dp.sysconfig)) let mut temp_sensor = Adt75TempSensor::new(&mut dp.sysconfig, 50.MHz(), dp.i2ca)
.expect("Creating temperature sensor struct failed"); .expect("Creating temperature sensor struct failed");
loop { loop {
let temp = temp_sensor let temp = temp_sensor

View File

@ -5,15 +5,16 @@
#![no_main] #![no_main]
#![no_std] #![no_std]
use cortex_m_rt::entry; use cortex_m_rt::entry;
use embedded_hal::spi::SpiBus; use embedded_hal::spi::{SpiBus, MODE_3};
use embedded_hal::{delay::DelayNs, digital::OutputPin}; use embedded_hal::{delay::DelayNs, digital::OutputPin};
use panic_rtt_target as _; use panic_rtt_target as _;
use rtt_target::{rprintln, rtt_init_print}; use rtt_target::{rprintln, rtt_init_print};
use va108xx_hal::spi::SpiClkConfig;
use va108xx_hal::{ use va108xx_hal::{
gpio::PinsA, gpio::PinsA,
pac, pac,
prelude::*, prelude::*,
spi::{Spi, SpiConfig, TransferConfig}, spi::{Spi, SpiConfig},
timer::set_up_ms_delay_provider, timer::set_up_ms_delay_provider,
}; };
@ -31,7 +32,6 @@ fn main() -> ! {
let mut dp = pac::Peripherals::take().unwrap(); let mut dp = pac::Peripherals::take().unwrap();
let mut delay = set_up_ms_delay_provider(&mut dp.sysconfig, 50.MHz(), dp.tim0); let mut delay = set_up_ms_delay_provider(&mut dp.sysconfig, 50.MHz(), dp.tim0);
let pinsa = PinsA::new(&mut dp.sysconfig, None, dp.porta); let pinsa = PinsA::new(&mut dp.sysconfig, None, dp.porta);
let spi_cfg = SpiConfig::default();
let (sck, mosi, miso) = ( let (sck, mosi, miso) = (
pinsa.pa20.into_funsel_2(), pinsa.pa20.into_funsel_2(),
pinsa.pa19.into_funsel_2(), pinsa.pa19.into_funsel_2(),
@ -45,21 +45,20 @@ fn main() -> ! {
.set_high() .set_high()
.expect("Setting ADC chip select high failed"); .expect("Setting ADC chip select high failed");
let transfer_cfg = TransferConfig::new( let spi_cfg = SpiConfig::default()
1.MHz(), .clk_cfg(
embedded_hal::spi::MODE_3, SpiClkConfig::from_clk(50.MHz(), 1.MHz()).expect("creating SPI clock config failed"),
Some(cs_pin), )
false, .mode(MODE_3)
true, .slave_output_disable(true);
); let mut spi = Spi::new(
let mut spi = Spi::spib( &mut dp.sysconfig,
50.MHz(),
dp.spib, dp.spib,
(sck, miso, mosi), (sck, miso, mosi),
50.MHz(),
spi_cfg, spi_cfg,
Some(&mut dp.sysconfig),
Some(&transfer_cfg.downgrade()),
); );
spi.cfg_hw_cs_with_pin(&cs_pin);
let mut tx_rx_buf: [u8; 3] = [0; 3]; let mut tx_rx_buf: [u8; 3] = [0; 3];
tx_rx_buf[0] = READ_MASK | DEVID_REG; tx_rx_buf[0] = READ_MASK | DEVID_REG;

View File

@ -10,7 +10,7 @@ use cortex_m_rt::entry;
use embedded_hal::delay::DelayNs; use embedded_hal::delay::DelayNs;
use embedded_hal::digital::{OutputPin, StatefulOutputPin}; use embedded_hal::digital::{OutputPin, StatefulOutputPin};
use panic_halt as _; use panic_halt as _;
use va108xx_hal::{gpio::pins::PinsA, pac, prelude::*, timer::set_up_ms_delay_provider}; use va108xx_hal::{gpio::PinsA, pac, prelude::*, timer::set_up_ms_delay_provider};
use vorago_reb1::leds::Leds; use vorago_reb1::leds::Leds;
// REB LED pin definitions. All on port A // REB LED pin definitions. All on port A

View File

@ -1,4 +1,7 @@
//! MAX11619 ADC example applikcation //! MAX11619 ADC example application.
//!
//! You can turn the potentiometer knob of the REB1 board to measure
//! different ADC values.
#![no_main] #![no_main]
#![no_std] #![no_std]
@ -6,19 +9,19 @@ use core::convert::Infallible;
use cortex_m_rt::entry; use cortex_m_rt::entry;
use embedded_hal::digital::OutputPin; use embedded_hal::digital::OutputPin;
use embedded_hal::spi::{SpiBus, SpiDevice}; use embedded_hal::spi::{SpiBus, SpiDevice, MODE_0};
use embedded_hal::{delay::DelayNs, spi}; use embedded_hal::{delay::DelayNs, spi};
use max116xx_10bit::VoltageRefMode; use max116xx_10bit::VoltageRefMode;
use max116xx_10bit::{AveragingConversions, AveragingResults}; use max116xx_10bit::{AveragingConversions, AveragingResults};
use panic_rtt_target as _; use panic_rtt_target as _;
use rtt_target::{rprintln, rtt_init_print}; use rtt_target::{rprintln, rtt_init_print};
use va108xx_hal::spi::{NoneT, OptionalHwCs}; use va108xx_hal::spi::{OptionalHwCs, SpiClkConfig};
use va108xx_hal::timer::CountDownTimer; use va108xx_hal::timer::CountDownTimer;
use va108xx_hal::{ use va108xx_hal::{
gpio::PinsA, gpio::PinsA,
pac::{self, interrupt}, pac::{self, interrupt},
prelude::*, prelude::*,
spi::{Spi, SpiBase, SpiConfig, TransferConfig}, spi::{Spi, SpiBase, SpiConfig},
timer::{default_ms_irq_handler, set_up_ms_tick, DelayMs, IrqCfg}, timer::{default_ms_irq_handler, set_up_ms_tick, DelayMs, IrqCfg},
}; };
use va108xx_hal::{port_mux, FunSel, PortSel}; use va108xx_hal::{port_mux, FunSel, PortSel};
@ -100,6 +103,8 @@ impl<Delay: DelayNs, HwCs: OptionalHwCs<pac::Spib>> SpiDevice for SpiWithHwCs<De
} }
} }
const SYS_CLK: Hertz = Hertz::from_raw(50_000_000);
#[entry] #[entry]
fn main() -> ! { fn main() -> ! {
rtt_init_print!(); rtt_init_print!();
@ -110,7 +115,7 @@ fn main() -> ! {
IrqCfg::new(pac::Interrupt::OC0, true, true), IrqCfg::new(pac::Interrupt::OC0, true, true),
&mut dp.sysconfig, &mut dp.sysconfig,
Some(&mut dp.irqsel), Some(&mut dp.irqsel),
50.MHz(), SYS_CLK,
dp.tim0, dp.tim0,
); );
let delay = DelayMs::new(tim0).unwrap(); let delay = DelayMs::new(tim0).unwrap();
@ -119,7 +124,10 @@ fn main() -> ! {
} }
let pinsa = PinsA::new(&mut dp.sysconfig, None, dp.porta); let pinsa = PinsA::new(&mut dp.sysconfig, None, dp.porta);
let spi_cfg = SpiConfig::default(); let spi_cfg = SpiConfig::default()
.clk_cfg(SpiClkConfig::from_clk(SYS_CLK, 3.MHz()).unwrap())
.mode(MODE_0)
.blockmode(true);
let (sck, mosi, miso) = ( let (sck, mosi, miso) = (
pinsa.pa20.into_funsel_2(), pinsa.pa20.into_funsel_2(),
pinsa.pa19.into_funsel_2(), pinsa.pa19.into_funsel_2(),
@ -138,14 +146,12 @@ fn main() -> ! {
.set_high() .set_high()
.expect("Setting accelerometer chip select high failed"); .expect("Setting accelerometer chip select high failed");
let transfer_cfg = TransferConfig::<NoneT>::new(3.MHz(), spi::MODE_0, None, true, false); let spi = Spi::new(
let spi = Spi::spib( &mut dp.sysconfig,
50.MHz(),
dp.spib, dp.spib,
(sck, miso, mosi), (sck, miso, mosi),
50.MHz(),
spi_cfg, spi_cfg,
Some(&mut dp.sysconfig),
Some(&transfer_cfg.downgrade()),
) )
.downgrade(); .downgrade();
let delay_provider = CountDownTimer::new(&mut dp.sysconfig, 50.MHz(), dp.tim1); let delay_provider = CountDownTimer::new(&mut dp.sysconfig, 50.MHz(), dp.tim1);

View File

@ -0,0 +1,64 @@
//! Example application which interfaces with the boot EEPROM.
#![no_main]
#![no_std]
use cortex_m_rt::entry;
use embedded_hal::delay::DelayNs;
use panic_rtt_target as _;
use rtt_target::{rprintln, rtt_init_print};
use va108xx_hal::{pac, pwm::CountDownTimer, time::Hertz};
use vorago_reb1::m95m01::M95M01;
const CLOCK_FREQ: Hertz = Hertz::from_raw(50_000_000);
#[entry]
fn main() -> ! {
rtt_init_print!();
rprintln!("-- VA108XX REB1 NVM example --");
let mut dp = pac::Peripherals::take().unwrap();
let mut timer = CountDownTimer::new(&mut dp.sysconfig, CLOCK_FREQ, dp.tim0);
let mut nvm = M95M01::new(&mut dp.sysconfig, CLOCK_FREQ, dp.spic);
let status_reg = nvm.read_status_reg().expect("reading status reg failed");
if status_reg.zero_segment() == 0b111 {
panic!("status register unexpected values");
}
let mut orig_content: [u8; 16] = [0; 16];
let mut read_buf: [u8; 16] = [0; 16];
let write_buf: [u8; 16] = [0; 16];
for (idx, val) in read_buf.iter_mut().enumerate() {
*val = idx as u8;
}
nvm.read(0x4000, &mut orig_content).unwrap();
// One byte write and read.
nvm.write(0x4000, &write_buf[0..1]).unwrap();
nvm.read(0x4000, &mut read_buf[0..1]).unwrap();
assert_eq!(write_buf[0], read_buf[0]);
read_buf.fill(0);
// Four bytes write and read.
nvm.write(0x4000, &write_buf[0..4]).unwrap();
nvm.read(0x4000, &mut read_buf[0..4]).unwrap();
assert_eq!(&read_buf[0..4], &write_buf[0..4]);
read_buf.fill(0);
// Full sixteen bytes
nvm.write(0x4000, &write_buf).unwrap();
nvm.read(0x4000, &mut read_buf).unwrap();
assert_eq!(&read_buf, &write_buf);
read_buf.fill(0);
// 3 bytes
nvm.write(0x4000, &write_buf[0..3]).unwrap();
nvm.read(0x4000, &mut read_buf[0..3]).unwrap();
assert_eq!(&read_buf[0..3], &write_buf[0..3]);
// Write back original content.
nvm.write(0x4000, &orig_content).unwrap();
loop {
timer.delay_ms(500);
}
}

View File

@ -2,8 +2,8 @@
//! //!
//! ## Examples //! ## Examples
//! //!
//! - [Button Blinky with IRQs](https://egit.irs.uni-stuttgart.de/rust/vorago-reb1/src/branch/main/examples/blinky-button-irq.rs) //! - [Button Blinky with IRQs](https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/src/branch/main/vorago-reb1/examples/blinky-button-irq.rs)
//! - [Button Blinky with IRQs and RTIC](https://egit.irs.uni-stuttgart.de/rust/vorago-reb1/src/branch/main/examples/blinky-button-rtic.rs) //! - [Button Blinky with IRQs and RTIC](https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/src/branch/main/vorago-reb1/examples/blinky-button-rtic.rs)
use embedded_hal::digital::InputPin; use embedded_hal::digital::InputPin;
use va108xx_hal::{ use va108xx_hal::{
gpio::{FilterClkSel, FilterType, InputFloating, InterruptEdge, InterruptLevel, Pin, PA11}, gpio::{FilterClkSel, FilterType, InputFloating, InterruptEdge, InterruptLevel, Pin, PA11},

View File

@ -2,13 +2,13 @@
//! //!
//! ## Examples //! ## Examples
//! //!
//! - [LED example](https://egit.irs.uni-stuttgart.de/rust/vorago-reb1/src/branch/main/examples/blinky-leds.rs) //! - [LED example](https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/src/branch/main/vorago-reb1/examples/blinky-leds.rs)
//! - [Button Blinky using IRQs](https://egit.irs.uni-stuttgart.de/rust/vorago-reb1/src/branch/main/examples/blinky-button-irq.rs) //! - [Button Blinky using IRQs](https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/src/branch/main/vorago-reb1/examples/blinky-button-irq.rs)
//! - [Button Blinky using IRQs and RTIC](https://egit.irs.uni-stuttgart.de/rust/vorago-reb1/src/branch/main/examples/blinky-button-rtic.rs) //! - [Button Blinky using IRQs and RTIC](https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/src/branch/main/vorago-reb1/examples/blinky-button-rtic.rs)
use embedded_hal::digital::OutputPin; use embedded_hal::digital::OutputPin;
use va108xx_hal::{ use va108xx_hal::{
gpio::dynpins::DynPin, gpio::dynpin::DynPin,
gpio::pins::{Pin, PushPullOutput, PA10, PA6, PA7}, gpio::pin::{Pin, PushPullOutput, PA10, PA6, PA7},
}; };
pub type LD2 = Pin<PA10, PushPullOutput>; pub type LD2 = Pin<PA10, PushPullOutput>;

View File

@ -1,7 +1,8 @@
#![no_std] #![no_std]
#![cfg_attr(docs_rs, feature(doc_auto_cfg))] #![cfg_attr(docsrs, feature(doc_auto_cfg))]
pub mod button; pub mod button;
pub mod leds; pub mod leds;
pub mod m95m01;
pub mod max11619; pub mod max11619;
pub mod temp_sensor; pub mod temp_sensor;

172
vorago-reb1/src/m95m01.rs Normal file
View File

@ -0,0 +1,172 @@
//! Basic driver for the ST M95M01 EEPROM memory.
//!
//! This driver is used by the provided bootloader application for the REB1
//! board. It provides a convenient wrapper around the HAL SPI to interface
//! with the EEPROM memory of the REB1 board.
//!
//! # Example
//!
//! - [REB1 EEPROM example](https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/src/branch/main/vorago-reb1/examples/nvm.rs)
use core::convert::Infallible;
use embedded_hal::spi::SpiBus;
bitfield::bitfield! {
pub struct StatusReg(u8);
impl Debug;
u8;
pub status_register_write_protect, _: 7;
pub zero_segment, _: 6, 4;
pub block_protection_bits, set_block_protection_bits: 3, 2;
pub write_enable_latch, _: 1;
pub write_in_progress, _: 0;
}
// Registers.
pub mod regs {
/// Write status register command.
pub const WRSR: u8 = 0x01;
// Write command.
pub const WRITE: u8 = 0x02;
// Read command.
pub const READ: u8 = 0x03;
/// Write disable command.
pub const WRDI: u8 = 0x04;
/// Read status register command.
pub const RDSR: u8 = 0x05;
/// Write enable command.
pub const WREN: u8 = 0x06;
}
use regs::*;
use va108xx_hal::{
pac,
prelude::*,
spi::{RomMiso, RomMosi, RomSck, Spi, SpiConfig, BMSTART_BMSTOP_MASK},
};
pub type RomSpi = Spi<pac::Spic, (RomSck, RomMiso, RomMosi), u8>;
/// Driver for the ST device M95M01 EEPROM memory.
///
/// Specialized for the requirements of the VA108XX MCUs.
pub struct M95M01 {
pub spi: RomSpi,
}
impl M95M01 {
pub fn new(syscfg: &mut pac::Sysconfig, sys_clk: impl Into<Hertz>, spi: pac::Spic) -> Self {
let spi = RomSpi::new(
syscfg,
sys_clk,
spi,
(RomSck, RomMiso, RomMosi),
SpiConfig::default(),
);
let mut spi_dev = Self { spi };
spi_dev.clear_block_protection().unwrap();
spi_dev
}
pub fn release(mut self) -> pac::Spic {
self.set_block_protection().unwrap();
self.spi.release().0
}
// Wait until the write-in-progress state is cleared. This exposes a [nb] API, so this function
// will return [nb::Error::WouldBlock] if the EEPROM is still busy.
pub fn writes_are_done(&mut self) -> nb::Result<(), Infallible> {
let rdsr = self.read_status_reg()?;
if rdsr.write_in_progress() {
return Err(nb::Error::WouldBlock);
}
Ok(())
}
pub fn read_status_reg(&mut self) -> Result<StatusReg, Infallible> {
let mut write_read: [u8; 2] = [regs::RDSR, 0x00];
self.spi.transfer_in_place(&mut write_read)?;
Ok(StatusReg(write_read[1]))
}
pub fn write_enable(&mut self) -> Result<(), Infallible> {
self.spi.write(&[regs::WREN])
}
pub fn clear_block_protection(&mut self) -> Result<(), Infallible> {
// Has to be written separately.
self.write_enable()?;
self.spi.write(&[WRSR, 0x00])
}
pub fn set_block_protection(&mut self) -> Result<(), Infallible> {
let mut reg = StatusReg(0);
reg.set_block_protection_bits(0b11);
self.write_enable()?;
self.spi.write(&[WRSR, reg.0])
}
fn common_init_write_and_read(&mut self, address: u32, reg: u8) -> Result<(), Infallible> {
nb::block!(self.writes_are_done())?;
self.spi.flush()?;
if reg == WRITE {
self.write_enable()?;
self.spi.write_fifo_unchecked(WRITE as u32);
} else {
self.spi.write_fifo_unchecked(READ as u32);
}
self.spi.write_fifo_unchecked((address >> 16) & 0xff);
self.spi.write_fifo_unchecked((address >> 8) & 0xff);
self.spi.write_fifo_unchecked(address & 0xff);
Ok(())
}
fn common_read(&mut self, address: u32) -> Result<(), Infallible> {
self.common_init_write_and_read(address, READ)?;
for _ in 0..4 {
// Pump the FIFO.
self.spi.write_fifo_unchecked(0);
// Ignore the first 4 bytes.
self.spi.read_fifo_unchecked();
}
Ok(())
}
pub fn write(&mut self, address: u32, data: &[u8]) -> Result<(), Infallible> {
self.common_init_write_and_read(address, WRITE)?;
for val in data.iter().take(data.len() - 1) {
nb::block!(self.spi.write_fifo(*val as u32))?;
self.spi.read_fifo_unchecked();
}
nb::block!(self
.spi
.write_fifo(*data.last().unwrap() as u32 | BMSTART_BMSTOP_MASK))?;
self.spi.flush()?;
nb::block!(self.writes_are_done())?;
Ok(())
}
pub fn read(&mut self, address: u32, buf: &mut [u8]) -> Result<(), Infallible> {
self.common_read(address)?;
for val in buf.iter_mut() {
nb::block!(self.spi.write_fifo(0))?;
*val = (nb::block!(self.spi.read_fifo()).unwrap() & 0xff) as u8;
}
nb::block!(self.spi.write_fifo(BMSTART_BMSTOP_MASK))?;
self.spi.flush()?;
Ok(())
}
pub fn verify(&mut self, address: u32, data: &[u8]) -> Result<bool, Infallible> {
self.common_read(address)?;
for val in data.iter() {
nb::block!(self.spi.write_fifo(0))?;
let read_val = (nb::block!(self.spi.read_fifo()).unwrap() & 0xff) as u8;
if read_val != *val {
return Ok(false);
}
}
nb::block!(self.spi.write_fifo(BMSTART_BMSTOP_MASK))?;
self.spi.flush()?;
Ok(true)
}
}

View File

@ -2,7 +2,7 @@
//! //!
//! ## Examples //! ## Examples
//! //!
//! - [ADC example](https://egit.irs.uni-stuttgart.de/rust/vorago-reb1/src/branch/main/examples/max11619-adc.rs) //! - [ADC example](https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/src/branch/main/vorago-reb1/examples/max11619-adc.rs)
use core::convert::Infallible; use core::convert::Infallible;
use embedded_hal::spi::SpiDevice; use embedded_hal::spi::SpiDevice;
use max116xx_10bit::{ use max116xx_10bit::{

View File

@ -4,10 +4,10 @@
//! //!
//! ## Examples //! ## Examples
//! //!
//! - [Temperature Sensor example](https://egit.irs.uni-stuttgart.de/rust/vorago-reb1/src/branch/main/examples/adt75-temp-sensor.rs) //! - [Temperature Sensor example](https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/src/branch/main/vorago-reb1/examples/adt75-temp-sensor.rs)
use embedded_hal::i2c::{I2c, SevenBitAddress}; use embedded_hal::i2c::{I2c, SevenBitAddress};
use va108xx_hal::{ use va108xx_hal::{
i2c::{Error, I2cMaster, I2cSpeed, MasterConfig}, i2c::{Error, I2cMaster, I2cSpeed, InitError, MasterConfig},
pac, pac,
time::Hertz, time::Hertz,
}; };
@ -29,20 +29,40 @@ pub enum RegAddresses {
OneShot = 0x04, OneShot = 0x04,
} }
#[derive(Debug)]
pub enum AdtInitError {
Init(InitError),
I2c(Error),
}
impl From<InitError> for AdtInitError {
fn from(value: InitError) -> Self {
Self::Init(value)
}
}
impl From<Error> for AdtInitError {
fn from(value: Error) -> Self {
Self::I2c(value)
}
}
impl Adt75TempSensor { impl Adt75TempSensor {
pub fn new( pub fn new(
i2ca: pac::I2ca, sys_cfg: &mut pac::Sysconfig,
sys_clk: impl Into<Hertz> + Copy, sys_clk: impl Into<Hertz> + Copy,
sys_cfg: Option<&mut pac::Sysconfig>, i2ca: pac::I2ca,
) -> Result<Self, Error> { ) -> Result<Self, Error> {
let mut sensor = Adt75TempSensor { let mut sensor = Adt75TempSensor {
sensor_if: I2cMaster::i2ca( // The master construction can not fail for regular I2C speed.
sensor_if: I2cMaster::new(
sys_cfg,
sys_clk,
i2ca, i2ca,
MasterConfig::default(), MasterConfig::default(),
sys_clk,
I2cSpeed::Regular100khz, I2cSpeed::Regular100khz,
sys_cfg, )
), .unwrap(),
cmd_buf: [RegAddresses::Temperature as u8], cmd_buf: [RegAddresses::Temperature as u8],
current_reg: RegAddresses::Temperature, current_reg: RegAddresses::Temperature,
}; };

View File

@ -18,6 +18,17 @@
"executable": "${workspaceFolder}/target/thumbv6m-none-eabi/debug/examples/blinky-leds", "executable": "${workspaceFolder}/target/thumbv6m-none-eabi/debug/examples/blinky-leds",
"interface": "jtag", "interface": "jtag",
"runToEntryPoint": "main", "runToEntryPoint": "main",
"rttConfig": {
"enabled": true,
"address": "auto",
"decoders": [
{
"port": 0,
"timestamp": true,
"type": "console"
}
]
}
}, },
{ {
"type": "cortex-debug", "type": "cortex-debug",
@ -28,9 +39,20 @@
"device": "Cortex-M0", "device": "Cortex-M0",
"svdFile": "./va108xx/svd/va108xx.svd.patched", "svdFile": "./va108xx/svd/va108xx.svd.patched",
"preLaunchTask": "rust: cargo build hal tests", "preLaunchTask": "rust: cargo build hal tests",
"executable": "${workspaceFolder}/target/thumbv6m-none-eabi/debug/examples/tests", "executable": "${workspaceFolder}/target/thumbv6m-none-eabi/debug/board-tests",
"interface": "jtag", "interface": "jtag",
"runToEntryPoint": "main", "runToEntryPoint": "main",
"rttConfig": {
"enabled": true,
"address": "auto",
"decoders": [
{
"port": 0,
"timestamp": true,
"type": "console"
}
]
}
}, },
{ {
"type": "cortex-debug", "type": "cortex-debug",
@ -44,6 +66,17 @@
"executable": "${workspaceFolder}/target/thumbv6m-none-eabi/debug/examples/rtt-log", "executable": "${workspaceFolder}/target/thumbv6m-none-eabi/debug/examples/rtt-log",
"interface": "jtag", "interface": "jtag",
"runToEntryPoint": "main", "runToEntryPoint": "main",
"rttConfig": {
"enabled": true,
"address": "auto",
"decoders": [
{
"port": 0,
"timestamp": true,
"type": "console"
}
]
}
}, },
{ {
"type": "cortex-debug", "type": "cortex-debug",
@ -57,6 +90,17 @@
"executable": "${workspaceFolder}/target/thumbv6m-none-eabi/debug/examples/blinky-button-irq", "executable": "${workspaceFolder}/target/thumbv6m-none-eabi/debug/examples/blinky-button-irq",
"interface": "jtag", "interface": "jtag",
"runToEntryPoint": "main", "runToEntryPoint": "main",
"rttConfig": {
"enabled": true,
"address": "auto",
"decoders": [
{
"port": 0,
"timestamp": true,
"type": "console"
}
]
}
}, },
{ {
"type": "cortex-debug", "type": "cortex-debug",
@ -70,11 +114,22 @@
"executable": "${workspaceFolder}/target/thumbv6m-none-eabi/debug/examples/timer-ticks", "executable": "${workspaceFolder}/target/thumbv6m-none-eabi/debug/examples/timer-ticks",
"interface": "jtag", "interface": "jtag",
"runToEntryPoint": "main", "runToEntryPoint": "main",
"rttConfig": {
"enabled": true,
"address": "auto",
"decoders": [
{
"port": 0,
"timestamp": true,
"type": "console"
}
]
}
}, },
{ {
"type": "cortex-debug", "type": "cortex-debug",
"request": "launch", "request": "launch",
"name": "Debug UART", "name": "UART Example",
"servertype": "jlink", "servertype": "jlink",
"cwd": "${workspaceRoot}", "cwd": "${workspaceRoot}",
"device": "Cortex-M0", "device": "Cortex-M0",
@ -83,6 +138,17 @@
"executable": "${workspaceFolder}/target/thumbv6m-none-eabi/debug/examples/uart", "executable": "${workspaceFolder}/target/thumbv6m-none-eabi/debug/examples/uart",
"interface": "jtag", "interface": "jtag",
"runToEntryPoint": "main", "runToEntryPoint": "main",
"rttConfig": {
"enabled": true,
"address": "auto",
"decoders": [
{
"port": 0,
"timestamp": true,
"type": "console"
}
]
}
}, },
{ {
"type": "cortex-debug", "type": "cortex-debug",
@ -96,6 +162,17 @@
"executable": "${workspaceFolder}/target/thumbv6m-none-eabi/debug/examples/spi", "executable": "${workspaceFolder}/target/thumbv6m-none-eabi/debug/examples/spi",
"interface": "jtag", "interface": "jtag",
"runToEntryPoint": "main", "runToEntryPoint": "main",
"rttConfig": {
"enabled": true,
"address": "auto",
"decoders": [
{
"port": 0,
"timestamp": true,
"type": "console"
}
]
}
}, },
{ {
"type": "cortex-debug", "type": "cortex-debug",
@ -109,11 +186,22 @@
"executable": "${workspaceFolder}/target/thumbv6m-none-eabi/debug/examples/adt75-temp-sensor", "executable": "${workspaceFolder}/target/thumbv6m-none-eabi/debug/examples/adt75-temp-sensor",
"interface": "jtag", "interface": "jtag",
"runToEntryPoint": "main", "runToEntryPoint": "main",
"rttConfig": {
"enabled": true,
"address": "auto",
"decoders": [
{
"port": 0,
"timestamp": true,
"type": "console"
}
]
}
}, },
{ {
"type": "cortex-debug", "type": "cortex-debug",
"request": "launch", "request": "launch",
"name": "Debug Button Blinky RTIC", "name": "Button Blinky RTIC Example",
"servertype": "jlink", "servertype": "jlink",
"cwd": "${workspaceRoot}", "cwd": "${workspaceRoot}",
"device": "Cortex-M0", "device": "Cortex-M0",
@ -122,11 +210,22 @@
"executable": "${workspaceFolder}/target/thumbv6m-none-eabi/debug/examples/blinky-button-rtic", "executable": "${workspaceFolder}/target/thumbv6m-none-eabi/debug/examples/blinky-button-rtic",
"interface": "jtag", "interface": "jtag",
"runToEntryPoint": "main", "runToEntryPoint": "main",
"rttConfig": {
"enabled": true,
"address": "auto",
"decoders": [
{
"port": 0,
"timestamp": true,
"type": "console"
}
]
}
}, },
{ {
"type": "cortex-debug", "type": "cortex-debug",
"request": "launch", "request": "launch",
"name": "Debug PWM", "name": "PWM Example",
"servertype": "jlink", "servertype": "jlink",
"cwd": "${workspaceRoot}", "cwd": "${workspaceRoot}",
"device": "Cortex-M0", "device": "Cortex-M0",
@ -135,11 +234,22 @@
"executable": "${workspaceFolder}/target/thumbv6m-none-eabi/debug/examples/pwm", "executable": "${workspaceFolder}/target/thumbv6m-none-eabi/debug/examples/pwm",
"interface": "jtag", "interface": "jtag",
"runToEntryPoint": "main", "runToEntryPoint": "main",
"rttConfig": {
"enabled": true,
"address": "auto",
"decoders": [
{
"port": 0,
"timestamp": true,
"type": "console"
}
]
}
}, },
{ {
"type": "cortex-debug", "type": "cortex-debug",
"request": "launch", "request": "launch",
"name": "Debug Cascade", "name": "Cascade Example",
"servertype": "jlink", "servertype": "jlink",
"cwd": "${workspaceRoot}", "cwd": "${workspaceRoot}",
"device": "Cortex-M0", "device": "Cortex-M0",
@ -148,11 +258,22 @@
"executable": "${workspaceFolder}/target/thumbv6m-none-eabi/debug/examples/cascade", "executable": "${workspaceFolder}/target/thumbv6m-none-eabi/debug/examples/cascade",
"interface": "jtag", "interface": "jtag",
"runToEntryPoint": "main", "runToEntryPoint": "main",
"rttConfig": {
"enabled": true,
"address": "0x10000000",
"decoders": [
{
"port": 0,
"timestamp": true,
"type": "console"
}
]
}
}, },
{ {
"type": "cortex-debug", "type": "cortex-debug",
"request": "launch", "request": "launch",
"name": "Debug Accelerometer", "name": "Accelerometer Example",
"servertype": "jlink", "servertype": "jlink",
"cwd": "${workspaceRoot}", "cwd": "${workspaceRoot}",
"device": "Cortex-M0", "device": "Cortex-M0",
@ -161,6 +282,17 @@
"executable": "${workspaceFolder}/target/thumbv6m-none-eabi/debug/examples/adxl343-accelerometer", "executable": "${workspaceFolder}/target/thumbv6m-none-eabi/debug/examples/adxl343-accelerometer",
"interface": "jtag", "interface": "jtag",
"runToEntryPoint": "main", "runToEntryPoint": "main",
"rttConfig": {
"enabled": true,
"address": "auto",
"decoders": [
{
"port": 0,
"timestamp": true,
"type": "console"
}
]
}
}, },
{ {
"type": "cortex-debug", "type": "cortex-debug",
@ -178,7 +310,7 @@
{ {
"type": "cortex-debug", "type": "cortex-debug",
"request": "launch", "request": "launch",
"name": "Debug ADC", "name": "ADC Example",
"servertype": "jlink", "servertype": "jlink",
"cwd": "${workspaceRoot}", "cwd": "${workspaceRoot}",
"device": "Cortex-M0", "device": "Cortex-M0",
@ -187,19 +319,113 @@
"executable": "${workspaceFolder}/target/thumbv6m-none-eabi/debug/examples/max11619-adc", "executable": "${workspaceFolder}/target/thumbv6m-none-eabi/debug/examples/max11619-adc",
"interface": "jtag", "interface": "jtag",
"runToEntryPoint": "main", "runToEntryPoint": "main",
"rttConfig": {
"enabled": true,
"address": "auto",
"decoders": [
{
"port": 0,
"timestamp": true,
"type": "console"
}
]
}
}, },
{ {
"type": "cortex-debug", "type": "cortex-debug",
"request": "launch", "request": "launch",
"name": "Debug UART IRQ", "name": "UART IRQ Example",
"servertype": "jlink", "servertype": "jlink",
"cwd": "${workspaceRoot}", "cwd": "${workspaceRoot}",
"device": "Cortex-M0", "device": "Cortex-M0",
"svdFile": "./va108xx/svd/va108xx.svd.patched", "svdFile": "./va108xx/svd/va108xx.svd.patched",
"preLaunchTask": "rust: cargo build uart irq", "preLaunchTask": "rust: cargo build uart irq",
"executable": "${workspaceFolder}/target/thumbv6m-none-eabi/debug/examples/uart-irq-rtic", "executable": "${workspaceFolder}/target/thumbv6m-none-eabi/debug/uart-rtic",
"interface": "jtag", "interface": "jtag",
"runToEntryPoint": "main", "runToEntryPoint": "main",
"rttConfig": {
"enabled": true,
"address": "auto",
"decoders": [
{
"port": 0,
"timestamp": true,
"type": "console"
}
]
}
},
{
"type": "cortex-debug",
"request": "launch",
"name": "REB1 NVM Example",
"servertype": "jlink",
"cwd": "${workspaceRoot}",
"device": "Cortex-M0",
"svdFile": "./va108xx/svd/va108xx.svd.patched",
"preLaunchTask": "reb1-nvm",
"executable": "${workspaceFolder}/target/thumbv6m-none-eabi/debug/examples/nvm",
"interface": "jtag",
"runToEntryPoint": "main",
"rttConfig": {
"enabled": true,
"address": "auto",
"decoders": [
{
"port": 0,
"timestamp": true,
"type": "console"
}
]
}
},
{
"type": "cortex-debug",
"request": "launch",
"name": "RTIC Example",
"servertype": "jlink",
"cwd": "${workspaceRoot}",
"device": "Cortex-M0",
"svdFile": "./va108xx/svd/va108xx.svd.patched",
"preLaunchTask": "rtic-example",
"executable": "${workspaceFolder}/target/thumbv6m-none-eabi/debug/rtic-example",
"interface": "jtag",
"runToEntryPoint": "main",
"rttConfig": {
"enabled": true,
"address": "auto",
"decoders": [
{
"port": 0,
"timestamp": true,
"type": "console"
}
]
}
},
{
"type": "cortex-debug",
"request": "launch",
"name": "Embassy Example",
"servertype": "jlink",
"cwd": "${workspaceRoot}",
"device": "Cortex-M0",
"svdFile": "./va108xx/svd/va108xx.svd.patched",
"preLaunchTask": "embassy-example",
"executable": "${workspaceFolder}/target/thumbv6m-none-eabi/debug/embassy-example",
"interface": "jtag",
"runToEntryPoint": "main",
"rttConfig": {
"enabled": true,
"address": "auto",
"decoders": [
{
"port": 0,
"timestamp": true,
"type": "console"
}
]
}
}, },
] ]
} }

View File

@ -21,10 +21,8 @@
"command": "~/.cargo/bin/cargo", // note: full path to the cargo "command": "~/.cargo/bin/cargo", // note: full path to the cargo
"args": [ "args": [
"build", "build",
"-p",
"va108xx-hal",
"--bin", "--bin",
"tests", "board-tests",
"--features", "--features",
"rt" "rt"
], ],
@ -39,8 +37,6 @@
"command": "~/.cargo/bin/cargo", // note: full path to the cargo "command": "~/.cargo/bin/cargo", // note: full path to the cargo
"args": [ "args": [
"build", "build",
"-p",
"va108xx-hal",
"--example", "--example",
"rtt-log", "rtt-log",
], ],
@ -55,8 +51,6 @@
"command": "~/.cargo/bin/cargo", // note: full path to the cargo "command": "~/.cargo/bin/cargo", // note: full path to the cargo
"args": [ "args": [
"build", "build",
"-p",
"va108xx-hal",
"--example", "--example",
"timer-ticks", "timer-ticks",
"--features", "--features",
@ -73,8 +67,6 @@
"command": "~/.cargo/bin/cargo", // note: full path to the cargo "command": "~/.cargo/bin/cargo", // note: full path to the cargo
"args": [ "args": [
"build", "build",
"-p",
"va108xx-hal",
"--example", "--example",
"uart", "uart",
], ],
@ -89,8 +81,6 @@
"command": "~/.cargo/bin/cargo", // note: full path to the cargo "command": "~/.cargo/bin/cargo", // note: full path to the cargo
"args": [ "args": [
"build", "build",
"-p",
"va108xx-hal",
"--example", "--example",
"spi", "spi",
], ],
@ -105,8 +95,6 @@
"command": "~/.cargo/bin/cargo", // note: full path to the cargo "command": "~/.cargo/bin/cargo", // note: full path to the cargo
"args": [ "args": [
"build", "build",
"-p",
"va108xx-hal",
"--example", "--example",
"pwm", "pwm",
"--features", "--features",
@ -123,8 +111,6 @@
"command": "~/.cargo/bin/cargo", // note: full path to the cargo "command": "~/.cargo/bin/cargo", // note: full path to the cargo
"args": [ "args": [
"build", "build",
"-p",
"va108xx-hal",
"--example", "--example",
"cascade", "cascade",
"--features", "--features",
@ -141,12 +127,8 @@
"command": "~/.cargo/bin/cargo", // note: full path to the cargo "command": "~/.cargo/bin/cargo", // note: full path to the cargo
"args": [ "args": [
"build", "build",
"-p", "--bin",
"va108xx-hal", "uart-rtic",
"--example",
"uart-irq-rtic",
"--features",
"rt"
], ],
"group": { "group": {
"kind": "build", "kind": "build",
@ -172,8 +154,6 @@
"command": "~/.cargo/bin/cargo", // note: full path to the cargo "command": "~/.cargo/bin/cargo", // note: full path to the cargo
"args": [ "args": [
"build", "build",
"-p",
"vorago-reb1",
"--example", "--example",
"blinky-leds", "blinky-leds",
], ],
@ -188,8 +168,6 @@
"command": "~/.cargo/bin/cargo", // note: full path to the cargo "command": "~/.cargo/bin/cargo", // note: full path to the cargo
"args": [ "args": [
"build", "build",
"-p",
"vorago-reb1",
"--example", "--example",
"blinky-button-irq", "blinky-button-irq",
], ],
@ -204,8 +182,6 @@
"command": "~/.cargo/bin/cargo", // note: full path to the cargo "command": "~/.cargo/bin/cargo", // note: full path to the cargo
"args": [ "args": [
"build", "build",
"-p",
"vorago-reb1",
"--example", "--example",
"adt75-temp-sensor", "adt75-temp-sensor",
], ],
@ -220,8 +196,6 @@
"command": "~/.cargo/bin/cargo", // note: full path to the cargo "command": "~/.cargo/bin/cargo", // note: full path to the cargo
"args": [ "args": [
"build", "build",
"-p",
"vorago-reb1",
"--example", "--example",
"blinky-button-rtic", "blinky-button-rtic",
], ],
@ -236,8 +210,6 @@
"command": "~/.cargo/bin/cargo", // note: full path to the cargo "command": "~/.cargo/bin/cargo", // note: full path to the cargo
"args": [ "args": [
"build", "build",
"-p",
"vorago-reb1",
"--example", "--example",
"adxl343-accelerometer" "adxl343-accelerometer"
], ],
@ -252,8 +224,6 @@
"command": "~/.cargo/bin/cargo", // note: full path to the cargo "command": "~/.cargo/bin/cargo", // note: full path to the cargo
"args": [ "args": [
"build", "build",
"-p",
"vorago-reb1",
"--example", "--example",
"max11619-adc", "max11619-adc",
], ],
@ -262,5 +232,39 @@
"isDefault": true "isDefault": true
} }
}, },
{
"label": "reb1-nvm",
"type": "shell",
"command": "~/.cargo/bin/cargo", // note: full path to the cargo
"args": [
"build",
"--example",
"nvm",
],
"group": {
"kind": "build",
"isDefault": true
}
},
{
"label": "rtic-example",
"type": "shell",
"command": "~/.cargo/bin/cargo", // note: full path to the cargo
"args": [
"build",
"--bin",
"rtic-example",
],
},
{
"label": "embassy-example",
"type": "shell",
"command": "~/.cargo/bin/cargo", // note: full path to the cargo
"args": [
"build",
"--bin",
"embassy-example",
],
},
] ]
} }