Compare commits
30 Commits
va108xx-ha
...
main
Author | SHA1 | Date | |
---|---|---|---|
e2a55e7309 | |||
e971e8dc0d | |||
501d1c973e | |||
acb8b67ae7 | |||
405cc089c3 | |||
f48ee8231a | |||
4fb19fe234 | |||
652af5cb3c | |||
6e231e2553 | |||
79b7d7b4c2 | |||
3196d74a34 | |||
d7c27446e0 | |||
3d4e8477c1 | |||
e2e3cc7020 | |||
4f15cd7a31 | |||
3c8c455c6f | |||
abb78c2682 | |||
51f21fee43 | |||
6fb3b0544f | |||
e3996d9166 | |||
deee5f6f46 | |||
5d6c7ebf5e | |||
6b3cdf74cc | |||
f4f378ba4f | |||
4224b14545 | |||
e3cdb19a35 | |||
95e72cfea6 | |||
6854703c5d | |||
edc03d9e6e | |||
e434f2e301 |
@ -24,8 +24,7 @@ rustflags = [
|
||||
# "-C", "link-arg=-Tdefmt.x",
|
||||
|
||||
# Can be useful for debugging.
|
||||
"-Clink-args=-Map=app.map"
|
||||
|
||||
# "-Clink-args=-Map=app.map"
|
||||
]
|
||||
|
||||
[build]
|
||||
|
4
.github/workflows/ci.yml
vendored
4
.github/workflows/ci.yml
vendored
@ -39,7 +39,9 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- 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:
|
||||
name: Clippy
|
||||
|
15
Cargo.toml
15
Cargo.toml
@ -5,7 +5,10 @@ members = [
|
||||
"va108xx",
|
||||
"va108xx-hal",
|
||||
"examples/simple",
|
||||
"examples/rtic",
|
||||
"examples/embassy",
|
||||
"board-tests",
|
||||
"bootloader",
|
||||
]
|
||||
|
||||
exclude = [
|
||||
@ -17,7 +20,8 @@ codegen-units = 1
|
||||
debug = 2
|
||||
debug-assertions = true # <-
|
||||
incremental = false
|
||||
opt-level = 'z' # <-
|
||||
# This is problematic for stepping..
|
||||
# opt-level = 'z' # <-
|
||||
overflow-checks = true # <-
|
||||
|
||||
# cargo build/run --release
|
||||
@ -29,3 +33,12 @@ incremental = false
|
||||
lto = 'fat'
|
||||
opt-level = 3 # <-
|
||||
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.
|
||||
|
16
README.md
16
README.md
@ -19,9 +19,13 @@ This workspace contains the following released crates:
|
||||
|
||||
It also contains the following helper crates:
|
||||
|
||||
- The `board-tests` 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 [`board-tests`](https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/src/branch/main/board-tests)
|
||||
contains an application which can be used to test the libraries on the board.
|
||||
- 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
|
||||
|
||||
@ -94,6 +98,8 @@ example.
|
||||
|
||||
Assuming a working debug connection to your VA108xx board, you can debug using VS Code with
|
||||
the [`Cortex-Debug` plugin](https://marketplace.visualstudio.com/items?itemName=marus25.cortex-debug).
|
||||
Please make sure that [`objdump-multiarch` and `nm-multiarch`](https://forums.raspberrypi.com/viewtopic.php?t=333146)
|
||||
are installed as well.
|
||||
|
||||
Some sample configuration files for VS code were provided and can be used by running
|
||||
`cp -rT vscode .vscode` like specified above. After that, you can use `Run and Debug`
|
||||
@ -106,3 +112,7 @@ configuration variables in your `settings.json`:
|
||||
- `"cortex-debug.gdbPath.linux"`
|
||||
- `"cortex-debug.gdbPath.windows"`
|
||||
- `"cortex-debug.gdbPath.osx"`
|
||||
|
||||
The provided VS Code configurations also provide an integrated RTT logger, which you can access
|
||||
via the terminal at `RTT Ch:0 console`. In order for the RTT block address detection to
|
||||
work properly, `objdump-multiarch` and `nm-multiarch` need to be installed.
|
||||
|
4
automation/Jenkinsfile
vendored
4
automation/Jenkinsfile
vendored
@ -25,7 +25,9 @@ pipeline {
|
||||
stage('Docs') {
|
||||
steps {
|
||||
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
|
||||
"""
|
||||
}
|
||||
}
|
||||
|
@ -4,10 +4,9 @@ version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
cortex-m-rtic = "1"
|
||||
panic-halt = "0.2"
|
||||
cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] }
|
||||
cortex-m-rt = "0.7"
|
||||
panic-halt = "0.2"
|
||||
rtt-target = "0.5"
|
||||
panic-rtt-target = "0.1.3"
|
||||
embedded-hal = "1"
|
||||
@ -15,7 +14,7 @@ embedded-hal-nb = "1"
|
||||
embedded-io = "0.6"
|
||||
|
||||
[dependencies.va108xx-hal]
|
||||
version = "0.6"
|
||||
version = "0.7"
|
||||
path = "../va108xx-hal"
|
||||
features = ["rt"]
|
||||
|
||||
|
25
bootloader/Cargo.toml
Normal file
25
bootloader/Cargo.toml
Normal 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
10
bootloader/src/lib.rs
Normal 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
296
bootloader/src/main.rs
Normal 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
25
examples/README.md
Normal 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
|
||||
```
|
40
examples/embassy/Cargo.toml
Normal file
40
examples/embassy/Cargo.toml
Normal 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"]
|
4
examples/embassy/src/lib.rs
Normal file
4
examples/embassy/src/lib.rs
Normal file
@ -0,0 +1,4 @@
|
||||
#![no_std]
|
||||
pub mod time_driver;
|
||||
|
||||
pub use time_driver::init;
|
43
examples/embassy/src/main.rs
Normal file
43
examples/embassy/src/main.rs
Normal 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();
|
||||
}
|
||||
}
|
333
examples/embassy/src/time_driver.rs
Normal file
333
examples/embassy/src/time_driver.rs
Normal file
@ -0,0 +1,333 @@
|
||||
//! 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,
|
||||
) {
|
||||
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
33
examples/rtic/Cargo.toml
Normal 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"]
|
@ -5,6 +5,7 @@
|
||||
#[rtic::app(device = pac)]
|
||||
mod app {
|
||||
use panic_rtt_target as _;
|
||||
use rtic_example::SYSCLK_FREQ;
|
||||
use rtt_target::{rprintln, rtt_init_default, set_print_channel};
|
||||
use va108xx_hal::{
|
||||
clock::{set_clk_div_register, FilterClkSel},
|
||||
@ -16,6 +17,8 @@ mod app {
|
||||
use vorago_reb1::button::Button;
|
||||
use vorago_reb1::leds::Leds;
|
||||
|
||||
rtic_monotonics::systick_monotonic!(Mono, 1_000);
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum PressMode {
|
||||
Toggle,
|
||||
@ -43,10 +46,12 @@ mod app {
|
||||
struct Shared {}
|
||||
|
||||
#[init]
|
||||
fn init(ctx: init::Context) -> (Shared, Local, init::Monotonics) {
|
||||
fn init(cx: init::Context) -> (Shared, Local) {
|
||||
let channels = rtt_init_default!();
|
||||
set_print_channel(channels.up.0);
|
||||
rprintln!("-- Vorago Button IRQ Example --");
|
||||
Mono::start(cx.core.SYST, SYSCLK_FREQ.raw());
|
||||
|
||||
let mode = match CFG_MODE {
|
||||
// Ask mode from user via RTT
|
||||
CfgMode::Prompt => prompt_mode(channels.down.0),
|
||||
@ -55,7 +60,7 @@ mod app {
|
||||
};
|
||||
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 edge_irq = match mode {
|
||||
PressMode::Toggle => InterruptEdge::HighToLow,
|
||||
@ -90,7 +95,7 @@ mod app {
|
||||
50.MHz(),
|
||||
dp.tim0,
|
||||
);
|
||||
(Shared {}, Local { leds, button, mode }, init::Monotonics())
|
||||
(Shared {}, Local { leds, button, mode })
|
||||
}
|
||||
|
||||
// `shared` cannot be accessed from this context
|
||||
@ -108,12 +113,10 @@ mod app {
|
||||
let mode = cx.local.mode;
|
||||
if *mode == PressMode::Toggle {
|
||||
leds[0].toggle();
|
||||
} else if button.released() {
|
||||
leds[0].off();
|
||||
} else {
|
||||
if button.released() {
|
||||
leds[0].off();
|
||||
} else {
|
||||
leds[0].on();
|
||||
}
|
||||
leds[0].on();
|
||||
}
|
||||
}
|
||||
|
||||
@ -129,14 +132,11 @@ mod app {
|
||||
let mut read;
|
||||
loop {
|
||||
read = down_channel.read(&mut read_buf);
|
||||
for i in 0..read {
|
||||
let val = read_buf[i] as char;
|
||||
if val == '0' || val == '1' {
|
||||
return if val == '0' {
|
||||
PressMode::Toggle
|
||||
} else {
|
||||
PressMode::Keep
|
||||
};
|
||||
for &byte in &read_buf[..read] {
|
||||
match byte as char {
|
||||
'0' => return PressMode::Toggle,
|
||||
'1' => return PressMode::Keep,
|
||||
_ => continue, // Ignore other characters
|
||||
}
|
||||
}
|
||||
}
|
@ -14,14 +14,13 @@
|
||||
mod app {
|
||||
use embedded_io::Write;
|
||||
use panic_rtt_target as _;
|
||||
use rtic_monotonics::systick::Systick;
|
||||
use rtic_example::SYSCLK_FREQ;
|
||||
use rtic_sync::make_channel;
|
||||
use rtt_target::{rprintln, rtt_init_print};
|
||||
use va108xx_hal::{
|
||||
gpio::PinsB,
|
||||
pac,
|
||||
prelude::*,
|
||||
time::Hertz,
|
||||
uart::{self, IrqCfg, IrqResult, UartWithIrqBase},
|
||||
};
|
||||
|
||||
@ -44,19 +43,14 @@ mod app {
|
||||
pub timeout: bool,
|
||||
}
|
||||
|
||||
rtic_monotonics::systick_monotonic!(Mono, 1_000);
|
||||
|
||||
#[init]
|
||||
fn init(cx: init::Context) -> (Shared, Local) {
|
||||
rtt_init_print!();
|
||||
//set_print_channel(channels.up.0);
|
||||
rprintln!("-- VA108xx UART IRQ example application--");
|
||||
|
||||
// Initialize the systick interrupt & obtain the token to prove that we did
|
||||
let systick_mono_token = rtic_monotonics::create_systick_token!();
|
||||
Systick::start(
|
||||
cx.core.SYST,
|
||||
Hertz::from(50.MHz()).raw(),
|
||||
systick_mono_token,
|
||||
);
|
||||
Mono::start(cx.core.SYST, SYSCLK_FREQ.raw());
|
||||
|
||||
let mut dp = cx.device;
|
||||
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 (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))
|
||||
.downgrade();
|
||||
irq_uart
|
||||
@ -74,7 +68,6 @@ mod app {
|
||||
|
||||
let (rx_info_tx, rx_info_rx) = make_channel!(RxInfo, 3);
|
||||
let rx_buf: [u8; 64] = [0; 64];
|
||||
//reply_handler::spawn().expect("spawning reply handler failed");
|
||||
(
|
||||
Shared { irq_uart, rx_buf },
|
||||
Local {
|
||||
@ -112,8 +105,8 @@ mod app {
|
||||
.expect("Read operation init failed");
|
||||
|
||||
let mut end_idx = 0;
|
||||
for idx in 0..rx_buf.len() {
|
||||
if (rx_buf[idx] as char) == '\n' {
|
||||
for (idx, val) in rx_buf.iter().enumerate() {
|
||||
if (*val as char) == '\n' {
|
||||
end_idx = idx;
|
||||
break;
|
||||
}
|
4
examples/rtic/src/lib.rs
Normal file
4
examples/rtic/src/lib.rs
Normal 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
71
examples/rtic/src/main.rs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
@ -4,30 +4,20 @@ version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
panic-halt = "0.2"
|
||||
cortex-m = {version = "0.7", features = ["critical-section-single-core"]}
|
||||
panic-rtt-target = "0.1"
|
||||
cortex-m-rt = "0.7"
|
||||
panic-halt = "0.2"
|
||||
panic-rtt-target = "0.1"
|
||||
critical-section = "1"
|
||||
rtt-target = "0.5"
|
||||
rtic-sync = { version = "1.3", features = ["defmt-03"] }
|
||||
embedded-hal = "1"
|
||||
embedded-hal-nb = "1"
|
||||
embedded-io = "0.6"
|
||||
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]
|
||||
version = "0.6"
|
||||
path = "../../va108xx-hal"
|
||||
features = ["rt", "defmt"]
|
||||
|
||||
[dependencies.vorago-reb1]
|
||||
path = "../../vorago-reb1"
|
||||
|
@ -48,7 +48,7 @@ fn main() -> ! {
|
||||
let mut cascade_target_1 =
|
||||
CountDownTimer::new(&mut dp.sysconfig, 50.MHz(), dp.tim4).auto_deactivate(true);
|
||||
cascade_target_1
|
||||
.cascade_0_source(CascadeSource::TimBase, Some(3))
|
||||
.cascade_0_source(CascadeSource::Tim(3))
|
||||
.expect("Configuring cascade source for TIM4 failed");
|
||||
let mut csd_cfg = CascadeCtrl {
|
||||
enb_start_src_csd0: true,
|
||||
@ -75,7 +75,7 @@ fn main() -> ! {
|
||||
CountDownTimer::new(&mut dp.sysconfig, 50.MHz(), dp.tim5).auto_deactivate(true);
|
||||
// Set TIM4 as cascade source
|
||||
cascade_target_2
|
||||
.cascade_1_source(CascadeSource::TimBase, Some(4))
|
||||
.cascade_1_source(CascadeSource::Tim(4))
|
||||
.expect("Configuring cascade source for TIM5 failed");
|
||||
|
||||
csd_cfg = CascadeCtrl::default();
|
||||
|
@ -21,9 +21,9 @@ fn main() -> ! {
|
||||
let mut dp = pac::Peripherals::take().unwrap();
|
||||
let pinsa = PinsA::new(&mut dp.sysconfig, None, dp.porta);
|
||||
let mut pwm = pwm::PwmPin::new(
|
||||
(pinsa.pa3.into_funsel_1(), dp.tim3),
|
||||
50.MHz(),
|
||||
&mut dp.sysconfig,
|
||||
50.MHz(),
|
||||
(pinsa.pa3.into_funsel_1(), dp.tim3),
|
||||
10.Hz(),
|
||||
);
|
||||
let mut delay = set_up_ms_delay_provider(&mut dp.sysconfig, 50.MHz(), dp.tim0);
|
||||
|
@ -3,12 +3,14 @@
|
||||
#![no_std]
|
||||
|
||||
use cortex_m_rt::entry;
|
||||
use panic_halt as _;
|
||||
use panic_rtt_target as _;
|
||||
use rtt_target::{rprintln, rtt_init_print};
|
||||
use va108xx_hal as _;
|
||||
|
||||
#[entry]
|
||||
fn main() -> ! {
|
||||
rtt_init_print!();
|
||||
rprintln!("-- VA108XX RTT example --");
|
||||
let mut counter = 0;
|
||||
loop {
|
||||
rprintln!("{}: Hello, world!", counter);
|
||||
|
@ -16,7 +16,7 @@ use va108xx_hal::{
|
||||
pac::{self, interrupt},
|
||||
prelude::*,
|
||||
pwm::{default_ms_irq_handler, set_up_ms_tick},
|
||||
spi::{self, Spi, SpiBase, TransferConfig},
|
||||
spi::{self, Spi, SpiBase, SpiClkConfig, TransferConfigWithHwcs},
|
||||
IrqCfg,
|
||||
};
|
||||
|
||||
@ -24,8 +24,7 @@ use va108xx_hal::{
|
||||
pub enum ExampleSelect {
|
||||
// Enter loopback mode. It is not necessary to tie MOSI/MISO together for this
|
||||
Loopback,
|
||||
// Send a test buffer and print everything received
|
||||
TestBuffer,
|
||||
MosiMisoTiedTogetherManually,
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Debug)]
|
||||
@ -55,6 +54,8 @@ fn main() -> ! {
|
||||
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 spib_ref: RefCell<Option<SpiBase<pac::Spib, u8>>> = RefCell::new(None);
|
||||
let pinsa = PinsA::new(&mut dp.sysconfig, None, dp.porta);
|
||||
@ -73,13 +74,12 @@ fn main() -> ! {
|
||||
pinsa.pa30.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,
|
||||
(sck, miso, mosi),
|
||||
50.MHz(),
|
||||
spi_cfg,
|
||||
Some(&mut dp.sysconfig),
|
||||
None,
|
||||
);
|
||||
spia.set_fill_word(FILL_WORD);
|
||||
spia_ref.borrow_mut().replace(spia.downgrade());
|
||||
@ -90,13 +90,12 @@ fn main() -> ! {
|
||||
pinsb.pb8.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,
|
||||
(sck, miso, mosi),
|
||||
50.MHz(),
|
||||
spi_cfg,
|
||||
Some(&mut dp.sysconfig),
|
||||
None,
|
||||
);
|
||||
spia.set_fill_word(FILL_WORD);
|
||||
spia_ref.borrow_mut().replace(spia.downgrade());
|
||||
@ -107,13 +106,12 @@ fn main() -> ! {
|
||||
pinsb.pb4.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,
|
||||
(sck, miso, mosi),
|
||||
50.MHz(),
|
||||
spi_cfg,
|
||||
Some(&mut dp.sysconfig),
|
||||
None,
|
||||
);
|
||||
spib.set_fill_word(FILL_WORD);
|
||||
spib_ref.borrow_mut().replace(spib.downgrade());
|
||||
@ -123,19 +121,25 @@ fn main() -> ! {
|
||||
match SPI_BUS_SEL {
|
||||
SpiBusSelect::SpiAPortA | SpiBusSelect::SpiAPortB => {
|
||||
if let Some(ref mut spi) = *spia_ref.borrow_mut() {
|
||||
let transfer_cfg =
|
||||
TransferConfig::new_no_hw_cs(SPI_SPEED_KHZ.kHz(), SPI_MODE, BLOCKMODE, false);
|
||||
let transfer_cfg = TransferConfigWithHwcs::new_no_hw_cs(
|
||||
Some(spi_clk_cfg),
|
||||
Some(SPI_MODE),
|
||||
BLOCKMODE,
|
||||
true,
|
||||
false,
|
||||
);
|
||||
spi.cfg_transfer(&transfer_cfg);
|
||||
}
|
||||
}
|
||||
SpiBusSelect::SpiBPortB => {
|
||||
if let Some(ref mut spi) = *spib_ref.borrow_mut() {
|
||||
let hw_cs_pin = pinsb.pb2.into_funsel_1();
|
||||
let transfer_cfg = TransferConfig::new(
|
||||
SPI_SPEED_KHZ.kHz(),
|
||||
SPI_MODE,
|
||||
let transfer_cfg = TransferConfigWithHwcs::new(
|
||||
Some(spi_clk_cfg),
|
||||
Some(SPI_MODE),
|
||||
Some(hw_cs_pin),
|
||||
BLOCKMODE,
|
||||
true,
|
||||
false,
|
||||
);
|
||||
spi.cfg_transfer(&transfer_cfg);
|
||||
@ -149,88 +153,64 @@ fn main() -> ! {
|
||||
match SPI_BUS_SEL {
|
||||
SpiBusSelect::SpiAPortA | SpiBusSelect::SpiAPortB => {
|
||||
if let Some(ref mut spi) = *spia_ref.borrow_mut() {
|
||||
if EXAMPLE_SEL == ExampleSelect::Loopback {
|
||||
// Can't really verify correct reply here.
|
||||
spi.write(&[0x42]).expect("write failed");
|
||||
// Because of the loopback mode, we should get back the fill word here.
|
||||
spi.read(&mut reply_buf[0..1]).unwrap();
|
||||
assert_eq!(reply_buf[0], FILL_WORD);
|
||||
delay.delay_ms(500_u32);
|
||||
// Can't really verify correct reply here.
|
||||
spi.write(&[0x42]).expect("write failed");
|
||||
// Because of the loopback mode, we should get back the fill word here.
|
||||
spi.read(&mut reply_buf[0..1]).unwrap();
|
||||
assert_eq!(reply_buf[0], FILL_WORD);
|
||||
delay.delay_ms(500_u32);
|
||||
|
||||
let tx_buf: [u8; 3] = [0x01, 0x02, 0x03];
|
||||
spi.transfer(&mut reply_buf[0..3], &tx_buf).unwrap();
|
||||
assert_eq!(tx_buf, reply_buf[0..3]);
|
||||
rprintln!(
|
||||
"Received reply: {}, {}, {}",
|
||||
reply_buf[0],
|
||||
reply_buf[1],
|
||||
reply_buf[2]
|
||||
);
|
||||
delay.delay_ms(500_u32);
|
||||
let tx_buf: [u8; 3] = [0x01, 0x02, 0x03];
|
||||
spi.transfer(&mut reply_buf[0..3], &tx_buf).unwrap();
|
||||
assert_eq!(tx_buf, reply_buf[0..3]);
|
||||
rprintln!(
|
||||
"Received reply: {}, {}, {}",
|
||||
reply_buf[0],
|
||||
reply_buf[1],
|
||||
reply_buf[2]
|
||||
);
|
||||
delay.delay_ms(500_u32);
|
||||
|
||||
let mut tx_rx_buf: [u8; 3] = [0x03, 0x02, 0x01];
|
||||
spi.transfer_in_place(&mut tx_rx_buf).unwrap();
|
||||
rprintln!(
|
||||
"Received reply: {}, {}, {}",
|
||||
tx_rx_buf[0],
|
||||
tx_rx_buf[1],
|
||||
tx_rx_buf[2]
|
||||
);
|
||||
assert_eq!(&tx_rx_buf[0..3], &[0x03, 0x02, 0x01]);
|
||||
} else {
|
||||
let send_buf: [u8; 3] = [0x01, 0x02, 0x03];
|
||||
spi.transfer(&mut reply_buf[0..3], &send_buf).unwrap();
|
||||
rprintln!(
|
||||
"Received reply: {}, {}, {}",
|
||||
reply_buf[0],
|
||||
reply_buf[1],
|
||||
reply_buf[2]
|
||||
);
|
||||
delay.delay_ms(1000_u32);
|
||||
}
|
||||
let mut tx_rx_buf: [u8; 3] = [0x03, 0x02, 0x01];
|
||||
spi.transfer_in_place(&mut tx_rx_buf).unwrap();
|
||||
rprintln!(
|
||||
"Received reply: {}, {}, {}",
|
||||
tx_rx_buf[0],
|
||||
tx_rx_buf[1],
|
||||
tx_rx_buf[2]
|
||||
);
|
||||
assert_eq!(&tx_rx_buf[0..3], &[0x03, 0x02, 0x01]);
|
||||
}
|
||||
}
|
||||
SpiBusSelect::SpiBPortB => {
|
||||
if let Some(ref mut spi) = *spib_ref.borrow_mut() {
|
||||
if EXAMPLE_SEL == ExampleSelect::Loopback {
|
||||
// Can't really verify correct reply here.
|
||||
spi.write(&[0x42]).expect("write failed");
|
||||
// Because of the loopback mode, we should get back the fill word here.
|
||||
spi.read(&mut reply_buf[0..1]).unwrap();
|
||||
assert_eq!(reply_buf[0], FILL_WORD);
|
||||
delay.delay_ms(500_u32);
|
||||
// Can't really verify correct reply here.
|
||||
spi.write(&[0x42]).expect("write failed");
|
||||
// Because of the loopback mode, we should get back the fill word here.
|
||||
spi.read(&mut reply_buf[0..1]).unwrap();
|
||||
assert_eq!(reply_buf[0], FILL_WORD);
|
||||
delay.delay_ms(500_u32);
|
||||
|
||||
let tx_buf: [u8; 3] = [0x01, 0x02, 0x03];
|
||||
spi.transfer(&mut reply_buf[0..3], &tx_buf).unwrap();
|
||||
assert_eq!(tx_buf, reply_buf[0..3]);
|
||||
rprintln!(
|
||||
"Received reply: {}, {}, {}",
|
||||
reply_buf[0],
|
||||
reply_buf[1],
|
||||
reply_buf[2]
|
||||
);
|
||||
delay.delay_ms(500_u32);
|
||||
let tx_buf: [u8; 3] = [0x01, 0x02, 0x03];
|
||||
spi.transfer(&mut reply_buf[0..3], &tx_buf).unwrap();
|
||||
assert_eq!(tx_buf, reply_buf[0..3]);
|
||||
rprintln!(
|
||||
"Received reply: {}, {}, {}",
|
||||
reply_buf[0],
|
||||
reply_buf[1],
|
||||
reply_buf[2]
|
||||
);
|
||||
delay.delay_ms(500_u32);
|
||||
|
||||
let mut tx_rx_buf: [u8; 3] = [0x03, 0x02, 0x01];
|
||||
spi.transfer_in_place(&mut tx_rx_buf).unwrap();
|
||||
rprintln!(
|
||||
"Received reply: {}, {}, {}",
|
||||
tx_rx_buf[0],
|
||||
tx_rx_buf[1],
|
||||
tx_rx_buf[2]
|
||||
);
|
||||
assert_eq!(&tx_rx_buf[0..3], &[0x03, 0x02, 0x01]);
|
||||
} else {
|
||||
let send_buf: [u8; 3] = [0x01, 0x02, 0x03];
|
||||
spi.transfer(&mut reply_buf[0..3], &send_buf).unwrap();
|
||||
rprintln!(
|
||||
"Received reply: {}, {}, {}",
|
||||
reply_buf[0],
|
||||
reply_buf[1],
|
||||
reply_buf[2]
|
||||
);
|
||||
delay.delay_ms(1000_u32);
|
||||
}
|
||||
let mut tx_rx_buf: [u8; 3] = [0x03, 0x02, 0x01];
|
||||
spi.transfer_in_place(&mut tx_rx_buf).unwrap();
|
||||
rprintln!(
|
||||
"Received reply: {}, {}, {}",
|
||||
tx_rx_buf[0],
|
||||
tx_rx_buf[1],
|
||||
tx_rx_buf[2]
|
||||
);
|
||||
assert_eq!(&tx_rx_buf[0..3], &[0x03, 0x02, 0x01]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3,8 +3,8 @@
|
||||
#![no_std]
|
||||
|
||||
use core::cell::Cell;
|
||||
use cortex_m::interrupt::Mutex;
|
||||
use cortex_m_rt::entry;
|
||||
use critical_section::Mutex;
|
||||
use panic_rtt_target as _;
|
||||
use rtt_target::{rprintln, rtt_init_print};
|
||||
use va108xx_hal::{
|
||||
@ -83,11 +83,12 @@ fn main() -> ! {
|
||||
}
|
||||
}
|
||||
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 {
|
||||
last_ms = current_ms;
|
||||
// To prevent drift.
|
||||
last_ms += 1000;
|
||||
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);
|
||||
}
|
||||
cortex_m::asm::delay(10000);
|
||||
@ -110,7 +111,7 @@ fn OC0() {
|
||||
#[interrupt]
|
||||
#[allow(non_snake_case)]
|
||||
fn OC1() {
|
||||
cortex_m::interrupt::free(|cs| {
|
||||
critical_section::with(|cs| {
|
||||
let mut sec = SEC_COUNTER.borrow(cs).get();
|
||||
sec += 1;
|
||||
SEC_COUNTER.borrow(cs).set(sec);
|
||||
|
@ -28,7 +28,7 @@ fn main() -> ! {
|
||||
let tx = gpioa.pa9.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();
|
||||
writeln!(tx, "Hello World\r").unwrap();
|
||||
loop {
|
||||
|
@ -11,14 +11,14 @@ variants:
|
||||
psel: 0x0
|
||||
memory_map:
|
||||
- !Ram
|
||||
name: IRAM1
|
||||
name: DRAM
|
||||
range:
|
||||
start: 0x10000000
|
||||
end: 0x10008000
|
||||
cores:
|
||||
- main
|
||||
- !Nvm
|
||||
name: IROM1
|
||||
name: NVM
|
||||
range:
|
||||
start: 0x0
|
||||
end: 0x20000
|
||||
@ -30,6 +30,29 @@ variants:
|
||||
- va108xx_m95m01_128kb_prog
|
||||
- va108xx_mr25h10_1mb_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:
|
||||
- name: va108xx_fm25v20a_fram_128kb_prog
|
||||
description: VA108_FM25V20A_FRAM_128KB
|
||||
|
@ -6,6 +6,29 @@ All notable changes to this project will be documented in this file.
|
||||
The format is based on [Keep a Changelog](http://keepachangelog.com/)
|
||||
and this project adheres to [Semantic Versioning](http://semver.org/).
|
||||
|
||||
## [unreleased]
|
||||
|
||||
## Changed
|
||||
|
||||
- Improves `CascardSource` handling and general API when chosing cascade sources.
|
||||
- Replaced `utility::unmask_irq` by `enable_interrupt` and `disable_interrupt` API.
|
||||
- Improve and fix SPI abstractions. Add new low level interface. The primary SPI constructor now
|
||||
only expects a configuration structure and the transfer configuration needs to be applied in a
|
||||
separate step.
|
||||
|
||||
## Fixes
|
||||
|
||||
- Fixes for SPI peripheral: Flush implementation was incorrect and should now flush properly.
|
||||
|
||||
## [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
|
||||
|
||||
- Updated `embedded-hal` to v1
|
||||
|
@ -1,11 +1,11 @@
|
||||
[package]
|
||||
name = "va108xx-hal"
|
||||
version = "0.6.0"
|
||||
version = "0.7.0"
|
||||
authors = ["Robin Mueller <muellerr@irs.uni-stuttgart.de>"]
|
||||
edition = "2021"
|
||||
description = "HAL for the Vorago VA108xx family of microcontrollers"
|
||||
homepage = "https://egit.irs.uni-stuttgart.de/rust/va108xx-hal"
|
||||
repository = "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-rs"
|
||||
license = "Apache-2.0"
|
||||
keywords = ["no-std", "hal", "cortex-m", "vorago", "va108xx"]
|
||||
categories = ["aerospace", "embedded", "no-std", "hardware-support"]
|
||||
@ -16,11 +16,10 @@ cortex-m-rt = "0.7"
|
||||
nb = "1"
|
||||
paste = "1"
|
||||
embedded-hal-nb = "1"
|
||||
libm = "0.2"
|
||||
embedded-io = "0.6"
|
||||
fugit = "0.3"
|
||||
typenum = "1"
|
||||
defmt = { version = "0.3", optional = true }
|
||||
critical-section = "1"
|
||||
delegate = "0.12"
|
||||
|
||||
[dependencies.va108xx]
|
||||
@ -39,10 +38,15 @@ default-features = false
|
||||
version = "1.14"
|
||||
default-features = false
|
||||
|
||||
[dependencies.defmt]
|
||||
version = "0.3"
|
||||
optional = true
|
||||
|
||||
[features]
|
||||
default = ["rt"]
|
||||
rt = ["va108xx/rt"]
|
||||
defmt = ["dep:defmt", "fugit/defmt"]
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
all-features = true
|
||||
rustdoc-args = ["--cfg", "docs_rs", "--generate-link-to-definition"]
|
||||
rustdoc-args = ["--generate-link-to-definition"]
|
||||
|
@ -4,7 +4,7 @@
|
||||
# HAL for the Vorago VA108xx MCU family
|
||||
|
||||
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
|
||||
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
|
||||
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)
|
||||
2. Create a new binary crate with `cargo init`
|
||||
|
@ -36,8 +36,7 @@
|
||||
//! Users may try to convert value-level pins back to their type-level
|
||||
//! equivalents. However, this option is fallible, because the compiler cannot
|
||||
//! guarantee the pin has the correct ID or is in the correct mode at
|
||||
//! compile-time. Use [`TryFrom`](core::convert::TryFrom)/
|
||||
//! [`TryInto`](core::convert::TryInto) for this conversion.
|
||||
//! compile-time. Use [TryFrom]/[TryInto] for this conversion.
|
||||
//!
|
||||
//! ```
|
||||
//! // Convert to a `DynPin`
|
||||
@ -55,10 +54,10 @@
|
||||
//! `Error = core::convert::Infallible`, the value-level API can return a real
|
||||
//! error. If the [`DynPin`] is not in the correct [`DynPinMode`] for the
|
||||
//! operation, the trait functions will return
|
||||
//! [`InvalidPinType`](PinError::InvalidPinType).
|
||||
//! [InvalidPinTypeError].
|
||||
|
||||
use super::{
|
||||
pins::{FilterType, InterruptEdge, InterruptLevel, Pin, PinId, PinMode, PinState},
|
||||
pin::{FilterType, InterruptEdge, InterruptLevel, Pin, PinId, PinMode, PinState},
|
||||
reg::RegisterInterface,
|
||||
};
|
||||
use crate::{clock::FilterClkSel, pac, FunSel, IrqCfg};
|
@ -3,10 +3,10 @@
|
||||
//! 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).
|
||||
//!
|
||||
//! This API provides two different submodules, [`mod@pins`] and [`dynpins`],
|
||||
//! representing two different ways to handle GPIO pins. The default, [`mod@pins`],
|
||||
//! This API provides two different submodules, [pin] and [dynpin],
|
||||
//! 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
|
||||
//! 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.
|
||||
//!
|
||||
//! 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
|
||||
//! 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,
|
||||
//! and each pin has a non-zero memory footprint.
|
||||
//!
|
||||
//! ## 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)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
@ -102,10 +101,10 @@ macro_rules! common_reg_if_functions {
|
||||
};
|
||||
}
|
||||
|
||||
pub mod dynpins;
|
||||
pub use dynpins::*;
|
||||
pub mod dynpin;
|
||||
pub use dynpin::*;
|
||||
|
||||
pub mod pins;
|
||||
pub use pins::*;
|
||||
pub mod pin;
|
||||
pub use pin::*;
|
||||
|
||||
mod reg;
|
||||
|
@ -29,73 +29,48 @@
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! A `PinId` identifies a pin by it's group (A, B, C or D) and pin number. Each
|
||||
//! `PinId` instance is named according to its datasheet identifier, e.g.
|
||||
//! [`PA02`].
|
||||
//! 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.
|
||||
//! [PA2].
|
||||
//!
|
||||
//! A `PinMode` represents the various pin modes. The available `PinMode`
|
||||
//! variants are [`Disabled`], [`Input`], [`Interrupt`], [`Output`] and
|
||||
//! [`Alternate`], each with its own corresponding configurations.
|
||||
//! A [PinMode] represents the various pin modes. The available [PinMode]
|
||||
//! variants are [`Input`], [`Output`] and [`Alternate`], each with its own
|
||||
//! corresponding configurations.
|
||||
//!
|
||||
//! 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.
|
||||
//!
|
||||
//! To create the [`Pins`] struct, users must supply the PAC
|
||||
//! [`PORT`](crate::pac::PORT) peripheral. The [`Pins`] struct takes
|
||||
//! ownership of the [`PORT`] and provides the corresponding pins. Each [`Pin`]
|
||||
//! within the [`Pins`] struct can be moved out and used individually.
|
||||
//! Example for the pins of PORT A:
|
||||
//!
|
||||
//! To create the [PinsA] struct, users must supply the PAC
|
||||
//! [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 pins = Pins::new(peripherals.PORT);
|
||||
//! let pinsa = PinsA::new(peripherals.PORT);
|
||||
//! ```
|
||||
//!
|
||||
//! Pins can be converted between modes using several different methods.
|
||||
//!
|
||||
//! ```
|
||||
//! ```no_run
|
||||
//! // 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
|
||||
//! let pa27 = pins.pa27.into_mode::<FloatingInput>();
|
||||
//! let pa0 = pinsa.pa0.into_mode::<FloatingInput>();
|
||||
//! // 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
|
||||
//!
|
||||
//! This module implements all of the embedded HAL GPIO traits for each [`Pin`]
|
||||
//! in the corresponding [`PinMode`]s, namely: [`InputPin`], [`OutputPin`],
|
||||
//! [`ToggleableOutputPin`] and [`StatefulOutputPin`].
|
||||
//!
|
||||
//! 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};
|
||||
//! and [`StatefulOutputPin`].
|
||||
use super::dynpin::{DynAlternate, DynGroup, DynInput, DynOutput, DynPinId, DynPinMode};
|
||||
use super::reg::RegisterInterface;
|
||||
use crate::{
|
||||
pac::{Irqsel, Porta, Portb, Sysconfig},
|
||||
@ -137,9 +112,9 @@ pub enum PinState {
|
||||
|
||||
/// 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 {
|
||||
/// Corresponding [`DynInput`](super::DynInput)
|
||||
/// Corresponding [DynInput]
|
||||
const DYN: DynInput;
|
||||
}
|
||||
|
||||
@ -297,9 +272,9 @@ pub type Reset = InputFloating;
|
||||
|
||||
/// 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 {
|
||||
/// Corresponding [`DynPinMode`](super::DynPinMode)
|
||||
/// Corresponding [DynPinMode]
|
||||
const DYN: DynPinMode;
|
||||
}
|
||||
|
||||
@ -319,7 +294,7 @@ impl<C: AlternateConfig> PinMode for Alternate<C> {
|
||||
|
||||
/// Type-level enum for pin IDs
|
||||
pub trait PinId: Sealed {
|
||||
/// Corresponding [`DynPinId`](super::DynPinId)
|
||||
/// Corresponding [DynPinId]
|
||||
const DYN: DynPinId;
|
||||
}
|
||||
|
||||
@ -344,7 +319,7 @@ macro_rules! pin_id {
|
||||
// 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(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> {
|
||||
/// Create a new [`Pin`]
|
||||
/// Create a new [Pin]
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// Each [`Pin`] must be a singleton. For a given [`PinId`], there must be
|
||||
/// at most one corresponding [`Pin`] in existence at any given time.
|
||||
/// Each [Pin] must be a singleton. For a given [PinId], there must be
|
||||
/// at most one corresponding [Pin] in existence at any given time.
|
||||
/// Violating this requirement is `unsafe`.
|
||||
#[inline]
|
||||
pub(crate) unsafe fn new() -> Pin<I, M> {
|
@ -1,5 +1,5 @@
|
||||
use super::dynpins::{self, DynGroup, DynPinId, DynPinMode};
|
||||
use super::pins::{FilterType, InterruptEdge, InterruptLevel, PinState};
|
||||
use super::dynpin::{self, DynGroup, DynPinId, DynPinMode};
|
||||
use super::pin::{FilterType, InterruptEdge, InterruptLevel, PinState};
|
||||
use super::IsMaskedError;
|
||||
use crate::clock::FilterClkSel;
|
||||
use va108xx::{ioconfig, porta};
|
||||
@ -30,7 +30,7 @@ impl From<DynPinMode> for ModeFields {
|
||||
use DynPinMode::*;
|
||||
match mode {
|
||||
Input(config) => {
|
||||
use dynpins::DynInput::*;
|
||||
use dynpin::DynInput::*;
|
||||
fields.dir = false;
|
||||
match config {
|
||||
Floating => (),
|
||||
@ -44,7 +44,7 @@ impl From<DynPinMode> for ModeFields {
|
||||
}
|
||||
}
|
||||
Output(config) => {
|
||||
use dynpins::DynOutput::*;
|
||||
use dynpin::DynOutput::*;
|
||||
fields.dir = true;
|
||||
match config {
|
||||
PushPull => (),
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,5 +1,5 @@
|
||||
#![no_std]
|
||||
#![cfg_attr(docs_rs, feature(doc_auto_cfg))]
|
||||
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
|
||||
|
||||
pub use va108xx;
|
||||
pub use va108xx as pac;
|
||||
@ -15,7 +15,6 @@ pub mod time;
|
||||
pub mod timer;
|
||||
pub mod typelevel;
|
||||
pub mod uart;
|
||||
pub mod utility;
|
||||
|
||||
#[derive(Debug, Eq, Copy, Clone, PartialEq)]
|
||||
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);
|
||||
}
|
||||
|
@ -1,3 +1,5 @@
|
||||
//! Prelude
|
||||
pub use fugit::ExtU32 as _;
|
||||
pub use fugit::RateExtU32 as _;
|
||||
|
||||
pub use crate::time::*;
|
||||
|
@ -4,7 +4,7 @@
|
||||
//!
|
||||
//! ## 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::marker::PhantomData;
|
||||
|
||||
@ -158,9 +158,9 @@ where
|
||||
{
|
||||
/// Create a new stronlgy typed PWM pin
|
||||
pub fn new(
|
||||
vtp: (Pin, Tim),
|
||||
sys_clk: impl Into<Hertz> + Copy,
|
||||
sys_cfg: &mut pac::Sysconfig,
|
||||
sys_clk: impl Into<Hertz> + Copy,
|
||||
tim_and_pin: (Pin, Tim),
|
||||
initial_period: impl Into<Hertz> + Copy,
|
||||
) -> Self {
|
||||
let mut pin = PwmPin {
|
||||
@ -171,7 +171,7 @@ where
|
||||
current_rst_val: 0,
|
||||
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,
|
||||
};
|
||||
enable_peripheral_clock(sys_cfg, crate::clock::PeripheralClocks::Gpio);
|
||||
@ -225,12 +225,13 @@ where
|
||||
(Pin, Tim): ValidTimAndPin<Pin, Tim>,
|
||||
{
|
||||
pub fn pwma(
|
||||
vtp: (Pin, Tim),
|
||||
sys_clk: impl Into<Hertz> + Copy,
|
||||
sys_cfg: &mut pac::Sysconfig,
|
||||
sys_clk: impl Into<Hertz> + Copy,
|
||||
pin_and_tim: (Pin, Tim),
|
||||
initial_period: impl Into<Hertz> + Copy,
|
||||
) -> 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
|
||||
}
|
||||
@ -241,12 +242,13 @@ where
|
||||
(Pin, Tim): ValidTimAndPin<Pin, Tim>,
|
||||
{
|
||||
pub fn pwmb(
|
||||
vtp: (Pin, Tim),
|
||||
sys_clk: impl Into<Hertz> + Copy,
|
||||
sys_cfg: &mut pac::Sysconfig,
|
||||
sys_clk: impl Into<Hertz> + Copy,
|
||||
pin_and_tim: (Pin, Tim),
|
||||
initial_period: impl Into<Hertz> + Copy,
|
||||
) -> 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
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,18 +1,19 @@
|
||||
use crate::{pac, PeripheralSelect};
|
||||
|
||||
#[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
|
||||
///
|
||||
/// 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
|
||||
pub fn enable_rom_scrubbing(
|
||||
syscfg: &mut pac::Sysconfig,
|
||||
scrub_rate: u32,
|
||||
) -> Result<(), InvalidounterResetVal> {
|
||||
) -> Result<(), InvalidCounterResetVal> {
|
||||
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) });
|
||||
Ok(())
|
||||
@ -24,14 +25,14 @@ pub fn disable_rom_scrubbing(syscfg: &mut pac::Sysconfig) {
|
||||
|
||||
/// 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
|
||||
pub fn enable_ram_scrubbing(
|
||||
syscfg: &mut pac::Sysconfig,
|
||||
scrub_rate: u32,
|
||||
) -> Result<(), InvalidounterResetVal> {
|
||||
) -> Result<(), InvalidCounterResetVal> {
|
||||
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) });
|
||||
Ok(())
|
||||
|
@ -2,11 +2,12 @@
|
||||
//!
|
||||
//! ## Examples
|
||||
//!
|
||||
//! - [MS and second tick implementation](https://egit.irs.uni-stuttgart.de/rust/va108xx-hal/src/branch/main/examples/timer-ticks.rs)
|
||||
//! - [Cascade feature example](https://egit.irs.uni-stuttgart.de/rust/va108xx-hal/src/branch/main/examples/cascade.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-rs/src/branch/main/examples/simple/examples/cascade.rs)
|
||||
pub use crate::IrqCfg;
|
||||
use crate::{
|
||||
clock::{enable_peripheral_clock, PeripheralClocks},
|
||||
enable_interrupt,
|
||||
gpio::{
|
||||
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,
|
||||
@ -17,10 +18,9 @@ use crate::{
|
||||
time::Hertz,
|
||||
timer,
|
||||
typelevel::Sealed,
|
||||
utility::unmask_irq,
|
||||
};
|
||||
use core::cell::Cell;
|
||||
use cortex_m::interrupt::Mutex;
|
||||
use critical_section::Mutex;
|
||||
use fugit::RateExtU32;
|
||||
|
||||
const IRQ_DST_NONE: u32 = 0xffffffff;
|
||||
@ -72,25 +72,46 @@ pub enum CascadeSel {
|
||||
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
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
#[repr(u8)]
|
||||
pub enum CascadeSource {
|
||||
PortABase = 0,
|
||||
PortBBase = 32,
|
||||
TimBase = 64,
|
||||
PortA(u8),
|
||||
PortB(u8),
|
||||
Tim(u8),
|
||||
RamSbe = 96,
|
||||
RamMbe = 97,
|
||||
RomSbe = 98,
|
||||
RomMbe = 99,
|
||||
Txev = 100,
|
||||
ClockDividerBase = 120,
|
||||
ClockDivider(u8),
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub enum TimerErrors {
|
||||
Canceled,
|
||||
/// Invalid input for Cascade source
|
||||
InvalidCsdSourceInput,
|
||||
impl CascadeSource {
|
||||
fn id(&self) -> Result<u8, InvalidCascadeSourceId> {
|
||||
let port_check = |base: u8, id: u8, len: u8| {
|
||||
if id > len - 1 {
|
||||
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,
|
||||
}
|
||||
|
||||
fn enable_tim_clk(syscfg: &mut pac::Sysconfig, idx: u8) {
|
||||
#[inline(always)]
|
||||
pub fn enable_tim_clk(syscfg: &mut pac::Sysconfig, idx: u8) {
|
||||
syscfg
|
||||
.tim_clk_enable()
|
||||
.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> {
|
||||
fn tim_id(&self) -> u8 {
|
||||
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> {
|
||||
/// 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 {
|
||||
@ -554,18 +512,18 @@ impl<TIM: ValidTim> CountDownTimer<TIM> {
|
||||
|
||||
#[inline(always)]
|
||||
pub fn enable(&mut self) {
|
||||
self.tim.reg().ctrl().modify(|_, w| w.enable().set_bit());
|
||||
if let Some(irq_cfg) = self.irq_cfg {
|
||||
self.enable_interrupt();
|
||||
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)]
|
||||
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
|
||||
@ -619,9 +577,32 @@ impl<TIM: ValidTim> CountDownTimer<TIM> {
|
||||
});
|
||||
}
|
||||
|
||||
csd_sel!(cascade_0_source, cascade0);
|
||||
csd_sel!(cascade_1_source, cascade1);
|
||||
csd_sel!(cascade_2_source, cascade2);
|
||||
pub fn cascade_0_source(&mut self, src: CascadeSource) -> Result<(), InvalidCascadeSourceId> {
|
||||
let id = src.id()?;
|
||||
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 {
|
||||
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() {
|
||||
return Err(TimerErrors::Canceled);
|
||||
return false;
|
||||
}
|
||||
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
|
||||
/// the MS counter
|
||||
pub fn default_ms_irq_handler() {
|
||||
cortex_m::interrupt::free(|cs| {
|
||||
critical_section::with(|cs| {
|
||||
let mut ms = MS_COUNTER.borrow(cs).get();
|
||||
ms += 1;
|
||||
MS_COUNTER.borrow(cs).set(ms);
|
||||
@ -756,7 +738,7 @@ pub fn default_ms_irq_handler() {
|
||||
|
||||
/// Get the current MS tick count
|
||||
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())
|
||||
}
|
||||
|
||||
//==================================================================================================
|
||||
|
@ -2,23 +2,23 @@
|
||||
//!
|
||||
//! ## Examples
|
||||
//!
|
||||
//! - [UART simple example](https://egit.irs.uni-stuttgart.de/rust/va108xx-hal/src/branch/main/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 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-rs/src/branch/va108xx-update-package/examples/rtic/src/bin/uart-rtic.rs)
|
||||
use core::{marker::PhantomData, ops::Deref};
|
||||
use embedded_hal_nb::serial::Read;
|
||||
use fugit::RateExtU32;
|
||||
use libm::floorf;
|
||||
|
||||
pub use crate::IrqCfg;
|
||||
use crate::{
|
||||
clock::{self, enable_peripheral_clock, PeripheralClocks},
|
||||
gpio::pins::{
|
||||
clock::{enable_peripheral_clock, PeripheralClocks},
|
||||
enable_interrupt,
|
||||
gpio::pin::{
|
||||
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,
|
||||
},
|
||||
pac::{self, uarta as uart_base},
|
||||
time::Hertz,
|
||||
utility::unmask_irq,
|
||||
PeripheralSelect,
|
||||
};
|
||||
|
||||
//==================================================================================================
|
||||
@ -306,7 +306,7 @@ pub struct UartBase<Uart> {
|
||||
}
|
||||
/// Serial abstraction. Entry point to create a new UART
|
||||
pub struct Uart<Uart, Pins> {
|
||||
uart_base: UartBase<Uart>,
|
||||
inner: UartBase<Uart>,
|
||||
pins: Pins,
|
||||
}
|
||||
|
||||
@ -353,6 +353,7 @@ impl<UART> Tx<UART> {
|
||||
pub trait Instance: Deref<Target = uart_base::RegisterBlock> {
|
||||
fn ptr() -> *const uart_base::RegisterBlock;
|
||||
const IDX: u8;
|
||||
const PERIPH_SEL: PeripheralSelect;
|
||||
}
|
||||
|
||||
impl<UART: Instance> UartBase<UART> {
|
||||
@ -363,12 +364,19 @@ impl<UART: Instance> UartBase<UART> {
|
||||
false => 16,
|
||||
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 integer_part = floorf(x) as u32;
|
||||
let frac = floorf(64.0 * (x - integer_part as f32) + 0.5) as u32;
|
||||
self.uart
|
||||
.clkscale()
|
||||
.write(|w| unsafe { w.bits(integer_part * 64 + frac) });
|
||||
let integer_part = x as u32;
|
||||
self.uart.clkscale().write(|w| unsafe {
|
||||
w.frac().bits(frac as u8);
|
||||
w.int().bits(integer_part)
|
||||
});
|
||||
|
||||
let (paren, pareven) = match config.parity {
|
||||
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
|
||||
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
|
||||
/// in the SYSCONFIG register
|
||||
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
|
||||
}
|
||||
|
||||
@ -501,7 +529,7 @@ where
|
||||
irq_cfg: IrqCfg,
|
||||
sys_cfg: Option<&mut pac::Sysconfig>,
|
||||
irq_sel: Option<&mut pac::Irqsel>,
|
||||
) -> UartWithIrq<UART, PINS> {
|
||||
) -> UartWithIrq<UartInstance, PinsInstance> {
|
||||
let (uart, pins) = self.downgrade_internal();
|
||||
UartWithIrq {
|
||||
pins,
|
||||
@ -520,75 +548,75 @@ where
|
||||
|
||||
#[inline]
|
||||
pub fn enable_rx(&mut self) {
|
||||
self.uart_base.enable_rx();
|
||||
self.inner.enable_rx();
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn disable_rx(&mut self) {
|
||||
self.uart_base.enable_rx();
|
||||
self.inner.enable_rx();
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn enable_tx(&mut self) {
|
||||
self.uart_base.enable_tx();
|
||||
self.inner.enable_tx();
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn disable_tx(&mut self) {
|
||||
self.uart_base.disable_tx();
|
||||
self.inner.disable_tx();
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn clear_rx_fifo(&mut self) {
|
||||
self.uart_base.clear_rx_fifo();
|
||||
self.inner.clear_rx_fifo();
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn clear_tx_fifo(&mut self) {
|
||||
self.uart_base.clear_tx_fifo();
|
||||
self.inner.clear_tx_fifo();
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn clear_rx_status(&mut self) {
|
||||
self.uart_base.clear_rx_status();
|
||||
self.inner.clear_rx_status();
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn clear_tx_status(&mut self) {
|
||||
self.uart_base.clear_tx_status();
|
||||
self.inner.clear_tx_status();
|
||||
}
|
||||
|
||||
pub fn listen(&self, event: Event) {
|
||||
self.uart_base.listen(event);
|
||||
self.inner.listen(event);
|
||||
}
|
||||
|
||||
pub fn unlisten(&self, event: Event) {
|
||||
self.uart_base.unlisten(event);
|
||||
self.inner.unlisten(event);
|
||||
}
|
||||
|
||||
pub fn release(self) -> (UART, PINS) {
|
||||
(self.uart_base.release(), self.pins)
|
||||
pub fn release(self) -> (UartInstance, PinsInstance) {
|
||||
(self.inner.release(), self.pins)
|
||||
}
|
||||
|
||||
fn downgrade_internal(self) -> (UartBase<UART>, PINS) {
|
||||
fn downgrade_internal(self) -> (UartBase<UartInstance>, PinsInstance) {
|
||||
let base = UartBase {
|
||||
uart: self.uart_base.uart,
|
||||
tx: self.uart_base.tx,
|
||||
rx: self.uart_base.rx,
|
||||
uart: self.inner.uart,
|
||||
tx: self.inner.tx,
|
||||
rx: self.inner.rx,
|
||||
};
|
||||
(base, self.pins)
|
||||
}
|
||||
|
||||
pub fn downgrade(self) -> UartBase<UART> {
|
||||
pub fn downgrade(self) -> UartBase<UartInstance> {
|
||||
UartBase {
|
||||
uart: self.uart_base.uart,
|
||||
tx: self.uart_base.tx,
|
||||
rx: self.uart_base.rx,
|
||||
uart: self.inner.uart,
|
||||
tx: self.inner.tx,
|
||||
rx: self.inner.rx,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn split(self) -> (Tx<UART>, Rx<UART>) {
|
||||
self.uart_base.split()
|
||||
pub fn split(self) -> (Tx<UartInstance>, Rx<UartInstance>) {
|
||||
self.inner.split()
|
||||
}
|
||||
}
|
||||
|
||||
@ -597,6 +625,8 @@ impl Instance for pac::Uarta {
|
||||
pac::Uarta::ptr() as *const _
|
||||
}
|
||||
const IDX: u8 = 0;
|
||||
|
||||
const PERIPH_SEL: PeripheralSelect = PeripheralSelect::Uart0;
|
||||
}
|
||||
|
||||
impl Instance for pac::Uartb {
|
||||
@ -604,39 +634,11 @@ impl Instance for pac::Uartb {
|
||||
pac::Uartb::ptr() as *const _
|
||||
}
|
||||
const IDX: u8 = 1;
|
||||
|
||||
const PERIPH_SEL: PeripheralSelect = PeripheralSelect::Uart1;
|
||||
}
|
||||
|
||||
macro_rules! uart_impl {
|
||||
($($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> {
|
||||
impl<Uart: Instance> UartWithIrqBase<Uart> {
|
||||
fn init(self, sys_cfg: Option<&mut pac::Sysconfig>, irq_sel: Option<&mut pac::Irqsel>) -> Self {
|
||||
if let Some(sys_cfg) = sys_cfg {
|
||||
enable_peripheral_clock(sys_cfg, PeripheralClocks::Irqsel)
|
||||
@ -644,7 +646,7 @@ impl<UART: Instance> UartWithIrqBase<UART> {
|
||||
if let Some(irq_sel) = irq_sel {
|
||||
if self.irq_info.irq_cfg.route {
|
||||
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) });
|
||||
}
|
||||
}
|
||||
@ -674,7 +676,9 @@ impl<UART: Instance> UartWithIrqBase<UART> {
|
||||
self.uart.enable_tx();
|
||||
self.enable_rx_irq_sources(enb_timeout_irq);
|
||||
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(())
|
||||
}
|
||||
@ -837,7 +841,7 @@ impl<UART: Instance> UartWithIrqBase<UART> {
|
||||
self.irq_info.rx_len = 0;
|
||||
}
|
||||
|
||||
pub fn release(self) -> UART {
|
||||
pub fn release(self) -> Uart {
|
||||
self.uart.release()
|
||||
}
|
||||
}
|
||||
@ -871,10 +875,12 @@ impl<UART: Instance, PINS> UartWithIrq<UART, PINS> {
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
uart_impl! {
|
||||
pac::Uarta: (uarta, clock::PeripheralClocks::Uart0),
|
||||
pac::Uartb: (uartb, clock::PeripheralClocks::Uart1),
|
||||
}
|
||||
*/
|
||||
|
||||
impl<Uart> Tx<Uart> where Uart: Instance {}
|
||||
|
||||
|
@ -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) };
|
||||
}
|
@ -4,8 +4,8 @@ version = "0.3.0"
|
||||
authors = ["Robin Mueller <muellerr@irs.uni-stuttgart.de>"]
|
||||
edition = "2021"
|
||||
description = "PAC for the Vorago VA108xx family of microcontrollers"
|
||||
homepage = "https://egit.irs.uni-stuttgart.de/rust/va108xx"
|
||||
repository = "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-rs"
|
||||
license = "Apache-2.0"
|
||||
keywords = ["no-std", "arm", "cortex-m", "vorago", "va108xx"]
|
||||
categories = ["embedded", "no-std", "hardware-support"]
|
||||
@ -24,4 +24,4 @@ rt = ["cortex-m-rt/device"]
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
all-features = true
|
||||
rustdoc-args = ["--cfg", "docs_rs", "--generate-link-to-definition"]
|
||||
rustdoc-args = ["--generate-link-to-definition"]
|
||||
|
@ -4,7 +4,7 @@ svd2rust release can be generated by cloning the svd2rust [repository], checking
|
||||
#![allow(non_snake_case)]
|
||||
#![no_std]
|
||||
// Manually inserted.
|
||||
#![cfg_attr(docs_rs, feature(doc_auto_cfg))]
|
||||
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
|
||||
use core::marker::PhantomData;
|
||||
use core::ops::Deref;
|
||||
#[doc = r"Number available in the NVIC for configuring priority"]
|
||||
|
@ -8,9 +8,15 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
|
||||
|
||||
## [unreleased]
|
||||
|
||||
- Added M95M01 EEPROM module/API
|
||||
|
||||
## [v0.5.1] 2024-07-04
|
||||
|
||||
- Update `va108xx-hal` dependency to v0.7.0
|
||||
|
||||
## [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]
|
||||
|
||||
|
@ -1,11 +1,11 @@
|
||||
[package]
|
||||
name = "vorago-reb1"
|
||||
version = "0.5.0"
|
||||
authors = ["Robin Mueller <robin.mueller.m@gmail.com>"]
|
||||
version = "0.5.1"
|
||||
authors = ["Robin Mueller <muellerr@irs.uni-stuttgart.de>"]
|
||||
edition = "2021"
|
||||
description = "Board Support Crate for the Vorago REB1 development board"
|
||||
homepage = "https://egit.irs.uni-stuttgart.de/rust/vorago-reb1"
|
||||
repository = "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/va108xx-rs"
|
||||
license = "Apache-2.0"
|
||||
keywords = ["no-std", "reb1", "cortex-m", "vorago", "va108xx"]
|
||||
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-rt = "0.7"
|
||||
embedded-hal = "1"
|
||||
nb = "1"
|
||||
bitfield = "0.17"
|
||||
|
||||
[dependencies.max116xx-10bit]
|
||||
version = "0.3"
|
||||
|
||||
[dependencies.va108xx-hal]
|
||||
version = "0.6"
|
||||
version = ">=0.7, <0.8"
|
||||
path = "../va108xx-hal"
|
||||
features = ["rt"]
|
||||
|
||||
@ -27,16 +29,13 @@ features = ["rt"]
|
||||
rt = ["va108xx-hal/rt"]
|
||||
|
||||
[dev-dependencies]
|
||||
cortex-m-rtic = "1.1"
|
||||
panic-halt = "0.2"
|
||||
nb = "1"
|
||||
|
||||
[dev-dependencies.rtt-target]
|
||||
version = "0.5"
|
||||
|
||||
[dev-dependencies.panic-rtt-target]
|
||||
version = "0.1"
|
||||
rtt-target = "0.5"
|
||||
panic-rtt-target = "0.1"
|
||||
embedded-hal-bus = "0.2"
|
||||
dummy-pin = "1"
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
all-features = true
|
||||
rustdoc-args = ["--cfg", "docs_rs", "--generate-link-to-definition"]
|
||||
rustdoc-args = ["--generate-link-to-definition"]
|
||||
|
@ -6,5 +6,5 @@
|
||||
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
|
||||
|
||||
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.
|
||||
|
@ -13,7 +13,7 @@ fn main() -> ! {
|
||||
rprintln!("-- Vorago Temperature Sensor and I2C Example --");
|
||||
let mut dp = pac::Peripherals::take().unwrap();
|
||||
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");
|
||||
loop {
|
||||
let temp = temp_sensor
|
||||
|
@ -5,15 +5,16 @@
|
||||
#![no_main]
|
||||
#![no_std]
|
||||
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 panic_rtt_target as _;
|
||||
use rtt_target::{rprintln, rtt_init_print};
|
||||
use va108xx_hal::spi::SpiClkConfig;
|
||||
use va108xx_hal::{
|
||||
gpio::PinsA,
|
||||
pac,
|
||||
prelude::*,
|
||||
spi::{Spi, SpiConfig, TransferConfig},
|
||||
spi::{Spi, SpiConfig},
|
||||
timer::set_up_ms_delay_provider,
|
||||
};
|
||||
|
||||
@ -31,7 +32,6 @@ fn main() -> ! {
|
||||
let mut dp = pac::Peripherals::take().unwrap();
|
||||
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 spi_cfg = SpiConfig::default();
|
||||
let (sck, mosi, miso) = (
|
||||
pinsa.pa20.into_funsel_2(),
|
||||
pinsa.pa19.into_funsel_2(),
|
||||
@ -45,21 +45,20 @@ fn main() -> ! {
|
||||
.set_high()
|
||||
.expect("Setting ADC chip select high failed");
|
||||
|
||||
let transfer_cfg = TransferConfig::new(
|
||||
1.MHz(),
|
||||
embedded_hal::spi::MODE_3,
|
||||
Some(cs_pin),
|
||||
false,
|
||||
true,
|
||||
);
|
||||
let mut spi = Spi::spib(
|
||||
let spi_cfg = SpiConfig::default()
|
||||
.clk_cfg(
|
||||
SpiClkConfig::from_clk(50.MHz(), 1.MHz()).expect("creating SPI clock config failed"),
|
||||
)
|
||||
.mode(MODE_3)
|
||||
.slave_output_disable(true);
|
||||
let mut spi = Spi::new(
|
||||
&mut dp.sysconfig,
|
||||
50.MHz(),
|
||||
dp.spib,
|
||||
(sck, miso, mosi),
|
||||
50.MHz(),
|
||||
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];
|
||||
tx_rx_buf[0] = READ_MASK | DEVID_REG;
|
||||
|
@ -10,7 +10,7 @@ use cortex_m_rt::entry;
|
||||
use embedded_hal::delay::DelayNs;
|
||||
use embedded_hal::digital::{OutputPin, StatefulOutputPin};
|
||||
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;
|
||||
|
||||
// REB LED pin definitions. All on port A
|
||||
|
@ -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_std]
|
||||
|
||||
@ -6,19 +9,19 @@ use core::convert::Infallible;
|
||||
|
||||
use cortex_m_rt::entry;
|
||||
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 max116xx_10bit::VoltageRefMode;
|
||||
use max116xx_10bit::{AveragingConversions, AveragingResults};
|
||||
use panic_rtt_target as _;
|
||||
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::{
|
||||
gpio::PinsA,
|
||||
pac::{self, interrupt},
|
||||
prelude::*,
|
||||
spi::{Spi, SpiBase, SpiConfig, TransferConfig},
|
||||
spi::{Spi, SpiBase, SpiConfig},
|
||||
timer::{default_ms_irq_handler, set_up_ms_tick, DelayMs, IrqCfg},
|
||||
};
|
||||
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]
|
||||
fn main() -> ! {
|
||||
rtt_init_print!();
|
||||
@ -110,7 +115,7 @@ fn main() -> ! {
|
||||
IrqCfg::new(pac::Interrupt::OC0, true, true),
|
||||
&mut dp.sysconfig,
|
||||
Some(&mut dp.irqsel),
|
||||
50.MHz(),
|
||||
SYS_CLK,
|
||||
dp.tim0,
|
||||
);
|
||||
let delay = DelayMs::new(tim0).unwrap();
|
||||
@ -119,7 +124,10 @@ fn main() -> ! {
|
||||
}
|
||||
|
||||
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) = (
|
||||
pinsa.pa20.into_funsel_2(),
|
||||
pinsa.pa19.into_funsel_2(),
|
||||
@ -138,14 +146,12 @@ fn main() -> ! {
|
||||
.set_high()
|
||||
.expect("Setting accelerometer chip select high failed");
|
||||
|
||||
let transfer_cfg = TransferConfig::<NoneT>::new(3.MHz(), spi::MODE_0, None, true, false);
|
||||
let spi = Spi::spib(
|
||||
let spi = Spi::new(
|
||||
&mut dp.sysconfig,
|
||||
50.MHz(),
|
||||
dp.spib,
|
||||
(sck, miso, mosi),
|
||||
50.MHz(),
|
||||
spi_cfg,
|
||||
Some(&mut dp.sysconfig),
|
||||
Some(&transfer_cfg.downgrade()),
|
||||
)
|
||||
.downgrade();
|
||||
let delay_provider = CountDownTimer::new(&mut dp.sysconfig, 50.MHz(), dp.tim1);
|
||||
|
64
vorago-reb1/examples/nvm.rs
Normal file
64
vorago-reb1/examples/nvm.rs
Normal 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);
|
||||
}
|
||||
}
|
@ -2,8 +2,8 @@
|
||||
//!
|
||||
//! ## 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 and RTIC](https://egit.irs.uni-stuttgart.de/rust/vorago-reb1/src/branch/main/examples/blinky-button-rtic.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/va108xx-rs/src/branch/main/vorago-reb1/examples/blinky-button-rtic.rs)
|
||||
use embedded_hal::digital::InputPin;
|
||||
use va108xx_hal::{
|
||||
gpio::{FilterClkSel, FilterType, InputFloating, InterruptEdge, InterruptLevel, Pin, PA11},
|
||||
|
@ -2,13 +2,13 @@
|
||||
//!
|
||||
//! ## Examples
|
||||
//!
|
||||
//! - [LED example](https://egit.irs.uni-stuttgart.de/rust/vorago-reb1/src/branch/main/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 and RTIC](https://egit.irs.uni-stuttgart.de/rust/vorago-reb1/src/branch/main/examples/blinky-button-rtic.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/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/va108xx-rs/src/branch/main/vorago-reb1/examples/blinky-button-rtic.rs)
|
||||
use embedded_hal::digital::OutputPin;
|
||||
use va108xx_hal::{
|
||||
gpio::dynpins::DynPin,
|
||||
gpio::pins::{Pin, PushPullOutput, PA10, PA6, PA7},
|
||||
gpio::dynpin::DynPin,
|
||||
gpio::pin::{Pin, PushPullOutput, PA10, PA6, PA7},
|
||||
};
|
||||
|
||||
pub type LD2 = Pin<PA10, PushPullOutput>;
|
||||
|
@ -1,7 +1,8 @@
|
||||
#![no_std]
|
||||
#![cfg_attr(docs_rs, feature(doc_auto_cfg))]
|
||||
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
|
||||
|
||||
pub mod button;
|
||||
pub mod leds;
|
||||
pub mod m95m01;
|
||||
pub mod max11619;
|
||||
pub mod temp_sensor;
|
||||
|
172
vorago-reb1/src/m95m01.rs
Normal file
172
vorago-reb1/src/m95m01.rs
Normal 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)
|
||||
}
|
||||
}
|
@ -2,7 +2,7 @@
|
||||
//!
|
||||
//! ## 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 embedded_hal::spi::SpiDevice;
|
||||
use max116xx_10bit::{
|
||||
|
@ -4,10 +4,10 @@
|
||||
//!
|
||||
//! ## 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 va108xx_hal::{
|
||||
i2c::{Error, I2cMaster, I2cSpeed, MasterConfig},
|
||||
i2c::{Error, I2cMaster, I2cSpeed, InitError, MasterConfig},
|
||||
pac,
|
||||
time::Hertz,
|
||||
};
|
||||
@ -29,20 +29,40 @@ pub enum RegAddresses {
|
||||
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 {
|
||||
pub fn new(
|
||||
i2ca: pac::I2ca,
|
||||
sys_cfg: &mut pac::Sysconfig,
|
||||
sys_clk: impl Into<Hertz> + Copy,
|
||||
sys_cfg: Option<&mut pac::Sysconfig>,
|
||||
i2ca: pac::I2ca,
|
||||
) -> Result<Self, Error> {
|
||||
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,
|
||||
MasterConfig::default(),
|
||||
sys_clk,
|
||||
I2cSpeed::Regular100khz,
|
||||
sys_cfg,
|
||||
),
|
||||
)
|
||||
.unwrap(),
|
||||
cmd_buf: [RegAddresses::Temperature as u8],
|
||||
current_reg: RegAddresses::Temperature,
|
||||
};
|
||||
|
@ -18,6 +18,17 @@
|
||||
"executable": "${workspaceFolder}/target/thumbv6m-none-eabi/debug/examples/blinky-leds",
|
||||
"interface": "jtag",
|
||||
"runToEntryPoint": "main",
|
||||
"rttConfig": {
|
||||
"enabled": true,
|
||||
"address": "auto",
|
||||
"decoders": [
|
||||
{
|
||||
"port": 0,
|
||||
"timestamp": true,
|
||||
"type": "console"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "cortex-debug",
|
||||
@ -28,9 +39,20 @@
|
||||
"device": "Cortex-M0",
|
||||
"svdFile": "./va108xx/svd/va108xx.svd.patched",
|
||||
"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",
|
||||
"runToEntryPoint": "main",
|
||||
"rttConfig": {
|
||||
"enabled": true,
|
||||
"address": "auto",
|
||||
"decoders": [
|
||||
{
|
||||
"port": 0,
|
||||
"timestamp": true,
|
||||
"type": "console"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "cortex-debug",
|
||||
@ -44,6 +66,17 @@
|
||||
"executable": "${workspaceFolder}/target/thumbv6m-none-eabi/debug/examples/rtt-log",
|
||||
"interface": "jtag",
|
||||
"runToEntryPoint": "main",
|
||||
"rttConfig": {
|
||||
"enabled": true,
|
||||
"address": "auto",
|
||||
"decoders": [
|
||||
{
|
||||
"port": 0,
|
||||
"timestamp": true,
|
||||
"type": "console"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "cortex-debug",
|
||||
@ -57,6 +90,17 @@
|
||||
"executable": "${workspaceFolder}/target/thumbv6m-none-eabi/debug/examples/blinky-button-irq",
|
||||
"interface": "jtag",
|
||||
"runToEntryPoint": "main",
|
||||
"rttConfig": {
|
||||
"enabled": true,
|
||||
"address": "auto",
|
||||
"decoders": [
|
||||
{
|
||||
"port": 0,
|
||||
"timestamp": true,
|
||||
"type": "console"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "cortex-debug",
|
||||
@ -70,11 +114,22 @@
|
||||
"executable": "${workspaceFolder}/target/thumbv6m-none-eabi/debug/examples/timer-ticks",
|
||||
"interface": "jtag",
|
||||
"runToEntryPoint": "main",
|
||||
"rttConfig": {
|
||||
"enabled": true,
|
||||
"address": "auto",
|
||||
"decoders": [
|
||||
{
|
||||
"port": 0,
|
||||
"timestamp": true,
|
||||
"type": "console"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "cortex-debug",
|
||||
"request": "launch",
|
||||
"name": "Debug UART",
|
||||
"name": "UART Example",
|
||||
"servertype": "jlink",
|
||||
"cwd": "${workspaceRoot}",
|
||||
"device": "Cortex-M0",
|
||||
@ -83,6 +138,17 @@
|
||||
"executable": "${workspaceFolder}/target/thumbv6m-none-eabi/debug/examples/uart",
|
||||
"interface": "jtag",
|
||||
"runToEntryPoint": "main",
|
||||
"rttConfig": {
|
||||
"enabled": true,
|
||||
"address": "auto",
|
||||
"decoders": [
|
||||
{
|
||||
"port": 0,
|
||||
"timestamp": true,
|
||||
"type": "console"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "cortex-debug",
|
||||
@ -96,6 +162,17 @@
|
||||
"executable": "${workspaceFolder}/target/thumbv6m-none-eabi/debug/examples/spi",
|
||||
"interface": "jtag",
|
||||
"runToEntryPoint": "main",
|
||||
"rttConfig": {
|
||||
"enabled": true,
|
||||
"address": "auto",
|
||||
"decoders": [
|
||||
{
|
||||
"port": 0,
|
||||
"timestamp": true,
|
||||
"type": "console"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "cortex-debug",
|
||||
@ -109,11 +186,22 @@
|
||||
"executable": "${workspaceFolder}/target/thumbv6m-none-eabi/debug/examples/adt75-temp-sensor",
|
||||
"interface": "jtag",
|
||||
"runToEntryPoint": "main",
|
||||
"rttConfig": {
|
||||
"enabled": true,
|
||||
"address": "auto",
|
||||
"decoders": [
|
||||
{
|
||||
"port": 0,
|
||||
"timestamp": true,
|
||||
"type": "console"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "cortex-debug",
|
||||
"request": "launch",
|
||||
"name": "Debug Button Blinky RTIC",
|
||||
"name": "Button Blinky RTIC Example",
|
||||
"servertype": "jlink",
|
||||
"cwd": "${workspaceRoot}",
|
||||
"device": "Cortex-M0",
|
||||
@ -122,11 +210,22 @@
|
||||
"executable": "${workspaceFolder}/target/thumbv6m-none-eabi/debug/examples/blinky-button-rtic",
|
||||
"interface": "jtag",
|
||||
"runToEntryPoint": "main",
|
||||
"rttConfig": {
|
||||
"enabled": true,
|
||||
"address": "auto",
|
||||
"decoders": [
|
||||
{
|
||||
"port": 0,
|
||||
"timestamp": true,
|
||||
"type": "console"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "cortex-debug",
|
||||
"request": "launch",
|
||||
"name": "Debug PWM",
|
||||
"name": "PWM Example",
|
||||
"servertype": "jlink",
|
||||
"cwd": "${workspaceRoot}",
|
||||
"device": "Cortex-M0",
|
||||
@ -135,11 +234,22 @@
|
||||
"executable": "${workspaceFolder}/target/thumbv6m-none-eabi/debug/examples/pwm",
|
||||
"interface": "jtag",
|
||||
"runToEntryPoint": "main",
|
||||
"rttConfig": {
|
||||
"enabled": true,
|
||||
"address": "auto",
|
||||
"decoders": [
|
||||
{
|
||||
"port": 0,
|
||||
"timestamp": true,
|
||||
"type": "console"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "cortex-debug",
|
||||
"request": "launch",
|
||||
"name": "Debug Cascade",
|
||||
"name": "Cascade Example",
|
||||
"servertype": "jlink",
|
||||
"cwd": "${workspaceRoot}",
|
||||
"device": "Cortex-M0",
|
||||
@ -148,11 +258,22 @@
|
||||
"executable": "${workspaceFolder}/target/thumbv6m-none-eabi/debug/examples/cascade",
|
||||
"interface": "jtag",
|
||||
"runToEntryPoint": "main",
|
||||
"rttConfig": {
|
||||
"enabled": true,
|
||||
"address": "0x10000000",
|
||||
"decoders": [
|
||||
{
|
||||
"port": 0,
|
||||
"timestamp": true,
|
||||
"type": "console"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "cortex-debug",
|
||||
"request": "launch",
|
||||
"name": "Debug Accelerometer",
|
||||
"name": "Accelerometer Example",
|
||||
"servertype": "jlink",
|
||||
"cwd": "${workspaceRoot}",
|
||||
"device": "Cortex-M0",
|
||||
@ -161,6 +282,17 @@
|
||||
"executable": "${workspaceFolder}/target/thumbv6m-none-eabi/debug/examples/adxl343-accelerometer",
|
||||
"interface": "jtag",
|
||||
"runToEntryPoint": "main",
|
||||
"rttConfig": {
|
||||
"enabled": true,
|
||||
"address": "auto",
|
||||
"decoders": [
|
||||
{
|
||||
"port": 0,
|
||||
"timestamp": true,
|
||||
"type": "console"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "cortex-debug",
|
||||
@ -178,7 +310,7 @@
|
||||
{
|
||||
"type": "cortex-debug",
|
||||
"request": "launch",
|
||||
"name": "Debug ADC",
|
||||
"name": "ADC Example",
|
||||
"servertype": "jlink",
|
||||
"cwd": "${workspaceRoot}",
|
||||
"device": "Cortex-M0",
|
||||
@ -187,19 +319,113 @@
|
||||
"executable": "${workspaceFolder}/target/thumbv6m-none-eabi/debug/examples/max11619-adc",
|
||||
"interface": "jtag",
|
||||
"runToEntryPoint": "main",
|
||||
"rttConfig": {
|
||||
"enabled": true,
|
||||
"address": "auto",
|
||||
"decoders": [
|
||||
{
|
||||
"port": 0,
|
||||
"timestamp": true,
|
||||
"type": "console"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "cortex-debug",
|
||||
"request": "launch",
|
||||
"name": "Debug UART IRQ",
|
||||
"name": "UART IRQ Example",
|
||||
"servertype": "jlink",
|
||||
"cwd": "${workspaceRoot}",
|
||||
"device": "Cortex-M0",
|
||||
"svdFile": "./va108xx/svd/va108xx.svd.patched",
|
||||
"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",
|
||||
"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"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
]
|
||||
}
|
@ -21,10 +21,8 @@
|
||||
"command": "~/.cargo/bin/cargo", // note: full path to the cargo
|
||||
"args": [
|
||||
"build",
|
||||
"-p",
|
||||
"va108xx-hal",
|
||||
"--bin",
|
||||
"tests",
|
||||
"board-tests",
|
||||
"--features",
|
||||
"rt"
|
||||
],
|
||||
@ -39,8 +37,6 @@
|
||||
"command": "~/.cargo/bin/cargo", // note: full path to the cargo
|
||||
"args": [
|
||||
"build",
|
||||
"-p",
|
||||
"va108xx-hal",
|
||||
"--example",
|
||||
"rtt-log",
|
||||
],
|
||||
@ -55,8 +51,6 @@
|
||||
"command": "~/.cargo/bin/cargo", // note: full path to the cargo
|
||||
"args": [
|
||||
"build",
|
||||
"-p",
|
||||
"va108xx-hal",
|
||||
"--example",
|
||||
"timer-ticks",
|
||||
"--features",
|
||||
@ -73,8 +67,6 @@
|
||||
"command": "~/.cargo/bin/cargo", // note: full path to the cargo
|
||||
"args": [
|
||||
"build",
|
||||
"-p",
|
||||
"va108xx-hal",
|
||||
"--example",
|
||||
"uart",
|
||||
],
|
||||
@ -89,8 +81,6 @@
|
||||
"command": "~/.cargo/bin/cargo", // note: full path to the cargo
|
||||
"args": [
|
||||
"build",
|
||||
"-p",
|
||||
"va108xx-hal",
|
||||
"--example",
|
||||
"spi",
|
||||
],
|
||||
@ -105,8 +95,6 @@
|
||||
"command": "~/.cargo/bin/cargo", // note: full path to the cargo
|
||||
"args": [
|
||||
"build",
|
||||
"-p",
|
||||
"va108xx-hal",
|
||||
"--example",
|
||||
"pwm",
|
||||
"--features",
|
||||
@ -123,8 +111,6 @@
|
||||
"command": "~/.cargo/bin/cargo", // note: full path to the cargo
|
||||
"args": [
|
||||
"build",
|
||||
"-p",
|
||||
"va108xx-hal",
|
||||
"--example",
|
||||
"cascade",
|
||||
"--features",
|
||||
@ -141,12 +127,8 @@
|
||||
"command": "~/.cargo/bin/cargo", // note: full path to the cargo
|
||||
"args": [
|
||||
"build",
|
||||
"-p",
|
||||
"va108xx-hal",
|
||||
"--example",
|
||||
"uart-irq-rtic",
|
||||
"--features",
|
||||
"rt"
|
||||
"--bin",
|
||||
"uart-rtic",
|
||||
],
|
||||
"group": {
|
||||
"kind": "build",
|
||||
@ -172,8 +154,6 @@
|
||||
"command": "~/.cargo/bin/cargo", // note: full path to the cargo
|
||||
"args": [
|
||||
"build",
|
||||
"-p",
|
||||
"vorago-reb1",
|
||||
"--example",
|
||||
"blinky-leds",
|
||||
],
|
||||
@ -188,8 +168,6 @@
|
||||
"command": "~/.cargo/bin/cargo", // note: full path to the cargo
|
||||
"args": [
|
||||
"build",
|
||||
"-p",
|
||||
"vorago-reb1",
|
||||
"--example",
|
||||
"blinky-button-irq",
|
||||
],
|
||||
@ -204,8 +182,6 @@
|
||||
"command": "~/.cargo/bin/cargo", // note: full path to the cargo
|
||||
"args": [
|
||||
"build",
|
||||
"-p",
|
||||
"vorago-reb1",
|
||||
"--example",
|
||||
"adt75-temp-sensor",
|
||||
],
|
||||
@ -220,8 +196,6 @@
|
||||
"command": "~/.cargo/bin/cargo", // note: full path to the cargo
|
||||
"args": [
|
||||
"build",
|
||||
"-p",
|
||||
"vorago-reb1",
|
||||
"--example",
|
||||
"blinky-button-rtic",
|
||||
],
|
||||
@ -236,8 +210,6 @@
|
||||
"command": "~/.cargo/bin/cargo", // note: full path to the cargo
|
||||
"args": [
|
||||
"build",
|
||||
"-p",
|
||||
"vorago-reb1",
|
||||
"--example",
|
||||
"adxl343-accelerometer"
|
||||
],
|
||||
@ -252,8 +224,6 @@
|
||||
"command": "~/.cargo/bin/cargo", // note: full path to the cargo
|
||||
"args": [
|
||||
"build",
|
||||
"-p",
|
||||
"vorago-reb1",
|
||||
"--example",
|
||||
"max11619-adc",
|
||||
],
|
||||
@ -262,5 +232,39 @@
|
||||
"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",
|
||||
],
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user