21 Commits

Author SHA1 Message Date
6cd2e809d7 DMA experimentation
Some checks failed
Rust/va416xx-rs/pipeline/head There was a failure building this commit
2024-07-03 11:21:48 +02:00
16d2856fb2 static DMA ctrl block placement
All checks were successful
Rust/va416xx-rs/pipeline/head This commit looks good
2024-07-03 00:01:33 +02:00
01341edc91 some more improvements 2024-07-02 23:32:41 +02:00
d3deb8a467 DMA example working 2024-07-02 23:28:07 +02:00
988d6adcdc come on dma, do something..
All checks were successful
Rust/va416xx-rs/pipeline/head This commit looks good
2024-07-02 20:09:21 +02:00
c78c90b60d start adding DMA support
All checks were successful
Rust/va416xx-rs/pipeline/head This commit looks good
2024-07-02 16:03:54 +02:00
a2b43bff06 Merge pull request 'use released version of the PAC' (#17) from prep-hal-release into main
Some checks failed
Rust/va416xx-rs/pipeline/head There was a failure building this commit
Reviewed-on: #17
2024-07-01 15:03:13 +02:00
081d978360 Merge branch 'prep-hal-release' of egit.irs.uni-stuttgart.de:rust/va416xx-rs into prep-hal-release
Some checks are pending
Rust/va416xx-rs/pipeline/pr-main Build queued...
2024-07-01 15:00:25 +02:00
a2bb6b9227 add badges for HAL 2024-07-01 14:59:51 +02:00
5575e04b91 Merge branch 'main' into prep-hal-release
Some checks are pending
Rust/va416xx-rs/pipeline/pr-main Build queued...
2024-07-01 14:58:43 +02:00
6a83d2e507 use releases version of the PAC
Some checks are pending
Rust/va416xx-rs/pipeline/head Build queued...
2024-07-01 14:58:18 +02:00
aee7e8aa12 Merge pull request 'prepare HAL release' (#16) from prep-hal-release into main
All checks were successful
Rust/va416xx-rs/pipeline/head This commit looks good
Reviewed-on: #16
2024-07-01 14:55:56 +02:00
1bd39624ff prepare HAL release
Some checks are pending
Rust/va416xx-rs/pipeline/head Build queued...
2024-07-01 14:54:47 +02:00
3517fb6ec2 Merge pull request 'finished basic ADC and DAC HAL' (#15) from adc-and-dac into main
All checks were successful
Rust/va416xx-rs/pipeline/head This commit looks good
Reviewed-on: #15
2024-07-01 14:50:05 +02:00
e1011e3600 delete commented code
Some checks are pending
Rust/va416xx-rs/pipeline/pr-main Build queued...
2024-07-01 14:49:34 +02:00
5f50892d8a finished basic ADC and DAC HAL
Some checks are pending
Rust/va416xx-rs/pipeline/head Build started...
2024-07-01 14:47:08 +02:00
78e6c52835 Merge pull request 'adapt dev variant' (#13) from adapt-dev-release into main
All checks were successful
Rust/va416xx-rs/pipeline/head This commit looks good
Reviewed-on: #13
2024-07-01 10:17:16 +02:00
d458926e29 Merge branch 'main' into adapt-dev-release 2024-07-01 10:17:05 +02:00
8fa8137d79 Merge pull request 'remove reset in jlink.gdb script, add Embed.toml' (#14) from flashing-update into main
Reviewed-on: #14
2024-07-01 10:16:58 +02:00
2b6c45dbb7 adapt dev variant
Some checks are pending
Rust/va416xx-rs/pipeline/head Build started...
2024-07-01 10:15:01 +02:00
118341cabe remove reset in jlink.gdb script, add Embed.toml
Some checks are pending
Rust/va416xx-rs/pipeline/head Build queued...
2024-06-30 18:17:00 +02:00
27 changed files with 1713 additions and 270 deletions

View File

@ -12,7 +12,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

5
Embed.toml Normal file
View File

@ -0,0 +1,5 @@
[default.general]
chip = "VA416xx"
[default.rtt]
enabled = true

View File

@ -0,0 +1,70 @@
//! Simple ADC example.
#![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 simple_examples::peb1;
use va416xx_hal::{
adc::{Adc, ChannelSelect, ChannelValue, MultiChannelSelect},
pac,
prelude::*,
timer::CountdownTimer,
};
// Quite spammy and disabled by default.
const ENABLE_BUF_PRINTOUT: bool = false;
#[entry]
fn main() -> ! {
rtt_init_print!();
rprintln!("VA416xx ADC example");
let mut dp = pac::Peripherals::take().unwrap();
// Use the external clock connected to XTAL_N.
let clocks = dp
.clkgen
.constrain()
.xtal_n_clk_with_src_freq(peb1::EXTCLK_FREQ)
.freeze(&mut dp.sysconfig)
.unwrap();
let adc = Adc::new_with_channel_tag(&mut dp.sysconfig, dp.adc, &clocks);
let mut delay_provider = CountdownTimer::new(&mut dp.sysconfig, dp.tim0, &clocks);
let mut read_buf: [ChannelValue; 8] = [ChannelValue::default(); 8];
loop {
let single_value = adc
.trigger_and_read_single_channel(va416xx_hal::adc::ChannelSelect::AnIn0)
.expect("reading single channel value failed");
rprintln!("Read single ADC value on channel 0: {:?}", single_value);
let read_num = adc
.sweep_and_read_range(0, 7, &mut read_buf)
.expect("ADC range read failed");
if ENABLE_BUF_PRINTOUT {
rprintln!("ADC Range Read (0-8) read {} values", read_num);
rprintln!("ADC Range Read (0-8): {:?}", read_buf);
}
assert_eq!(read_num, 8);
for (idx, ch_val) in read_buf.iter().enumerate() {
assert_eq!(
ch_val.channel(),
ChannelSelect::try_from(idx as u8).unwrap()
);
}
adc.sweep_and_read_multiselect(
MultiChannelSelect::AnIn0 | MultiChannelSelect::AnIn2 | MultiChannelSelect::TempSensor,
&mut read_buf[0..3],
)
.expect("ADC multiselect read failed");
if ENABLE_BUF_PRINTOUT {
rprintln!("ADC Multiselect Read(0, 2 and 10): {:?}", &read_buf[0..3]);
}
assert_eq!(read_buf[0].channel(), ChannelSelect::AnIn0);
assert_eq!(read_buf[1].channel(), ChannelSelect::AnIn2);
assert_eq!(read_buf[2].channel(), ChannelSelect::TempSensor);
delay_provider.delay_ms(500);
}
}

View File

@ -16,7 +16,6 @@ fn main() -> ! {
let mut dp = pac::Peripherals::take().unwrap();
let portg = PinsG::new(&mut dp.sysconfig, dp.portg);
let mut led = portg.pg5.into_readable_push_pull_output();
//let mut delay = CountDownTimer::new(&mut dp.SYSCONFIG, 50.mhz(), dp.TIM0);
loop {
cortex_m::asm::delay(2_000_000);
led.toggle().ok();

View File

@ -0,0 +1,78 @@
//! Simple DAC-ADC example.
#![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 simple_examples::peb1;
use va416xx_hal::{adc::Adc, dac::Dac, pac, prelude::*, timer::CountdownTimer};
const DAC_INCREMENT: u16 = 256;
#[derive(Debug, PartialEq, Eq)]
pub enum AppMode {
// Measurements on AIN0.
AdcOnly,
// AOUT0. You can use a multi-meter to measure the changing voltage on the pin.
DacOnly,
/// AOUT0 needs to be wired to AIN0.
DacAndAdc,
}
const APP_MODE: AppMode = AppMode::DacAndAdc;
#[entry]
fn main() -> ! {
rtt_init_print!();
rprintln!("VA416xx DAC/ADC example");
let mut dp = pac::Peripherals::take().unwrap();
// Use the external clock connected to XTAL_N.
let clocks = dp
.clkgen
.constrain()
.xtal_n_clk_with_src_freq(peb1::EXTCLK_FREQ)
.freeze(&mut dp.sysconfig)
.unwrap();
let mut dac = None;
if APP_MODE == AppMode::DacOnly || APP_MODE == AppMode::DacAndAdc {
dac = Some(Dac::new(
&mut dp.sysconfig,
dp.dac0,
va416xx_hal::dac::DacSettling::Apb2Times100,
&clocks,
));
}
let mut adc = None;
if APP_MODE == AppMode::AdcOnly || APP_MODE == AppMode::DacAndAdc {
adc = Some(Adc::new(&mut dp.sysconfig, dp.adc, &clocks));
}
let mut delay_provider = CountdownTimer::new(&mut dp.sysconfig, dp.tim0, &clocks);
let mut current_val = 0;
loop {
if let Some(dac) = &dac {
rprintln!("loading DAC with value {}", current_val);
dac.load_and_trigger_manually(current_val)
.expect("loading DAC value failed");
if current_val + DAC_INCREMENT >= 4096 {
current_val = 0;
} else {
current_val += DAC_INCREMENT;
}
}
if let Some(dac) = &dac {
// This should never block.
nb::block!(dac.is_settled()).unwrap();
}
if let Some(adc) = &adc {
let ch_value = adc
.trigger_and_read_single_channel(va416xx_hal::adc::ChannelSelect::AnIn0)
.expect("reading ADC channel 0 failed");
rprintln!("Received channel value {:?}", ch_value);
}
delay_provider.delay_ms(500);
}
}

View File

@ -0,0 +1,269 @@
//! Simple DMA example
#![no_main]
#![no_std]
use core::cell::Cell;
use cortex_m::interrupt::Mutex;
use cortex_m_rt::entry;
use embedded_hal::delay::DelayNs;
use panic_rtt_target as _;
use rtt_target::{rprintln, rtt_init_print};
use simple_examples::peb1;
use va416xx_hal::dma::{Dma, DmaCfg, DmaChannel, DmaCtrlBlock};
use va416xx_hal::pwm::CountdownTimer;
use va416xx_hal::{
pac::{self, interrupt},
prelude::*,
};
static DMA_DONE_FLAG: Mutex<Cell<bool>> = Mutex::new(Cell::new(false));
static DMA_ACTIVE_FLAG: Mutex<Cell<bool>> = Mutex::new(Cell::new(false));
// Place the DMA control block into SRAM1 statically. This section needs to be defined in
// memory.x
#[link_section = ".sram1"]
static mut DMA_CTRL_BLOCK: DmaCtrlBlock = DmaCtrlBlock::new();
#[entry]
fn main() -> ! {
rtt_init_print!();
rprintln!("VA416xx DMA example");
let mut dp = pac::Peripherals::take().unwrap();
// Use the external clock connected to XTAL_N.
let clocks = dp
.clkgen
.constrain()
.xtal_n_clk_with_src_freq(peb1::EXTCLK_FREQ)
.freeze(&mut dp.sysconfig)
.unwrap();
// Safety: The DMA control block has an alignment rule of 128 and we constructed it directly
// statically.
let dma = Dma::new(&mut dp.sysconfig, dp.dma, DmaCfg::default(), unsafe {
core::ptr::addr_of_mut!(DMA_CTRL_BLOCK)
})
.expect("error creating DMA");
let (mut dma0, _, _, _) = dma.split();
let mut delay_ms = CountdownTimer::new(&mut dp.sysconfig, dp.tim0, &clocks);
let mut src_buf_8_bit: [u8; 65] = [0; 65];
let mut dest_buf_8_bit: [u8; 65] = [0; 65];
let mut src_buf_16_bit: [u16; 33] = [0; 33];
let mut dest_buf_16_bit: [u16; 33] = [0; 33];
let mut src_buf_32_bit: [u32; 17] = [0; 17];
let mut dest_buf_32_bit: [u32; 17] = [0; 17];
loop {
transfer_example_8_bit(
&mut src_buf_8_bit,
&mut dest_buf_8_bit,
&mut dma0,
&mut delay_ms,
);
delay_ms.delay_ms(500);
transfer_example_16_bit(
&mut src_buf_16_bit,
&mut dest_buf_16_bit,
&mut dma0,
&mut delay_ms,
);
delay_ms.delay_ms(500);
transfer_example_32_bit(
&mut src_buf_32_bit,
&mut dest_buf_32_bit,
&mut dma0,
&mut delay_ms,
);
delay_ms.delay_ms(500);
}
}
fn transfer_example_8_bit(
src_buf: &mut [u8; 65],
dest_buf: &mut [u8; 65],
dma0: &mut DmaChannel,
delay_ms: &mut CountdownTimer<pac::Tim0>,
) {
(0..64).for_each(|i| {
src_buf[i] = i as u8;
});
cortex_m::interrupt::free(|cs| {
DMA_DONE_FLAG.borrow(cs).set(false);
});
cortex_m::interrupt::free(|cs| {
DMA_ACTIVE_FLAG.borrow(cs).set(false);
});
let dma_transfer = dma0
.prepare_mem_to_mem_transfer_8_bit(src_buf, dest_buf)
.expect("error preparing transfer");
// Enable all interrupts.
// Safety: Not using mask based critical sections.
unsafe {
dma0.enable_done_interrupt();
dma0.enable_active_interrupt();
};
// Enable the individual channel.
dma0.enable();
// We still need to manually trigger the DMA request.
dma0.trigger_with_sw_request();
let dest_buf;
// Use polling for completion status.
loop {
let mut dma_done = false;
cortex_m::interrupt::free(|cs| {
if DMA_ACTIVE_FLAG.borrow(cs).get() {
rprintln!("DMA0 is active with 8 bit transfer");
DMA_ACTIVE_FLAG.borrow(cs).set(false);
}
if DMA_DONE_FLAG.borrow(cs).get() {
dma_done = true;
}
});
if dma_done {
rprintln!("8-bit transfer done");
// Safety: We checked for transfer completion.
dest_buf = unsafe { dma_transfer.release() };
break;
}
delay_ms.delay_ms(1);
}
(0..64).for_each(|i| {
assert_eq!(dest_buf[i], i as u8);
});
// Sentinel value, should be 0.
assert_eq!(dest_buf[64], 0);
dest_buf.fill(0);
}
fn transfer_example_16_bit(
src_buf: &mut [u16; 33],
dest_buf: &mut [u16; 33],
dma0: &mut DmaChannel,
delay_ms: &mut CountdownTimer<pac::Tim0>,
) {
// Set values scaled from 0 to 65535 to verify this is really a 16-bit transfer.
(0..32).for_each(|i| {
src_buf[i] = (i as u32 * u16::MAX as u32 / (src_buf.len() - 1) as u32) as u16;
});
cortex_m::interrupt::free(|cs| {
DMA_DONE_FLAG.borrow(cs).set(false);
});
cortex_m::interrupt::free(|cs| {
DMA_ACTIVE_FLAG.borrow(cs).set(false);
});
let dma_transfer = dma0
.prepare_mem_to_mem_transfer_16_bit(src_buf, dest_buf)
.expect("error preparing transfer");
dest_buf[5] = 2;
// Enable all interrupts.
// Safety: Not using mask based critical sections.
unsafe {
dma0.enable_done_interrupt();
dma0.enable_active_interrupt();
};
// Enable the individual channel.
dma0.enable();
// We still need to manually trigger the DMA request.
dma0.trigger_with_sw_request();
// Use polling for completion status.
loop {
let mut dma_done = false;
cortex_m::interrupt::free(|cs| {
if DMA_ACTIVE_FLAG.borrow(cs).get() {
rprintln!("DMA0 is active with 16-bit transfer");
DMA_ACTIVE_FLAG.borrow(cs).set(false);
}
if DMA_DONE_FLAG.borrow(cs).get() {
dma_done = true;
}
});
if dma_done {
rprintln!("16-bit transfer done");
break;
}
delay_ms.delay_ms(1);
}
(0..32).for_each(|i| {
assert_eq!(
dest_buf[i],
(i as u32 * u16::MAX as u32 / (src_buf.len() - 1) as u32) as u16
);
});
// Sentinel value, should be 0.
assert_eq!(dest_buf[32], 0);
dest_buf.fill(0);
}
fn transfer_example_32_bit(
src_buf: &mut [u32; 17],
dest_buf: &mut [u32; 17],
dma0: &mut DmaChannel,
delay_ms: &mut CountdownTimer<pac::Tim0>,
) {
// Set values scaled from 0 to 65535 to verify this is really a 16-bit transfer.
(0..16).for_each(|i| {
src_buf[i] = (i as u64 * u32::MAX as u64 / (src_buf.len() - 1) as u64) as u32;
});
cortex_m::interrupt::free(|cs| {
DMA_DONE_FLAG.borrow(cs).set(false);
});
cortex_m::interrupt::free(|cs| {
DMA_ACTIVE_FLAG.borrow(cs).set(false);
});
dma0.prepare_mem_to_mem_transfer_32_bit(src_buf, dest_buf)
.expect("error preparing transfer");
// Enable all interrupts.
// Safety: Not using mask based critical sections.
unsafe {
dma0.enable_done_interrupt();
dma0.enable_active_interrupt();
};
// Enable the individual channel.
dma0.enable();
// We still need to manually trigger the DMA request.
dma0.trigger_with_sw_request();
// Use polling for completion status.
loop {
let mut dma_done = false;
cortex_m::interrupt::free(|cs| {
if DMA_ACTIVE_FLAG.borrow(cs).get() {
rprintln!("DMA0 is active with 32-bit transfer");
DMA_ACTIVE_FLAG.borrow(cs).set(false);
}
if DMA_DONE_FLAG.borrow(cs).get() {
dma_done = true;
}
});
if dma_done {
rprintln!("32-bit transfer done");
break;
}
delay_ms.delay_ms(1);
}
(0..16).for_each(|i| {
assert_eq!(
dest_buf[i],
(i as u64 * u32::MAX as u64 / (src_buf.len() - 1) as u64) as u32
);
});
// Sentinel value, should be 0.
assert_eq!(dest_buf[16], 0);
dest_buf.fill(0);
}
#[interrupt]
#[allow(non_snake_case)]
fn DMA_DONE0() {
// Notify the main loop that the DMA transfer is finished.
cortex_m::interrupt::free(|cs| {
DMA_DONE_FLAG.borrow(cs).set(true);
});
}
#[interrupt]
#[allow(non_snake_case)]
fn DMA_ACTIVE0() {
// Notify the main loop that the DMA 0 is active now.
cortex_m::interrupt::free(|cs| {
DMA_ACTIVE_FLAG.borrow(cs).set(true);
});
}

View File

@ -1,8 +1,6 @@
target remote localhost:2331
monitor halt
# Reset is problematic on RevA, okay for RevB
monitor reset
# *try* to stop at the user entry point (it might be gone due to inlining)
break main

View File

@ -22,7 +22,8 @@ variants:
range:
start: 0x0
end: 0x40000
is_boot_memory: true
access:
boot: true
cores:
- main
- !Generic

View File

@ -1,38 +0,0 @@
[target.'cfg(all(target_arch = "arm", target_os = "none"))']
# uncomment ONE of these three option to make `cargo run` start a GDB session
# which option to pick depends on your system
# If the RevA board is used, replace jlink.gdb with jlink-reva.gdb
# runner = "arm-none-eabi-gdb -q -x jlink.gdb"
# runner = "gdb-multiarch -q -x jlink.gdb"
# runner = "gdb -q -x openocd.gdb"
# runner = "gdb-multiarch -q -x jlink.gdb"
rustflags = [
# This is needed if your flash or ram addresses are not aligned to 0x10000 in memory.x
# See https://github.com/rust-embedded/cortex-m-quickstart/pull/95
"-C", "link-arg=--nmagic",
# LLD (shipped with the Rust toolchain) is used as the default linker
"-C", "link-arg=-Tlink.x",
# if you run into problems with LLD switch to the GNU linker by commenting out
# this line
# "-C", "linker=arm-none-eabi-ld",
# if you need to link to pre-compiled C libraries provided by a C toolchain
# use GCC as the linker by commenting out both lines above and then
# uncommenting the three lines below
# "-C", "linker=arm-none-eabi-gcc",
# "-C", "link-arg=-Wl,-Tlink.x",
# "-C", "link-arg=-nostartfiles",
]
[build]
# Pick ONE of these compilation targets
# target = "thumbv6m-none-eabi" # Cortex-M0 and Cortex-M0+
# target = "thumbv7m-none-eabi" # Cortex-M3
# target = "thumbv7em-none-eabi" # Cortex-M4 and Cortex-M7 (no FPU)
target = "thumbv7em-none-eabihf" # Cortex-M4F and Cortex-M7F (with FPU)
# target = "thumbv8m.base-none-eabi" # Cortex-M23
# target = "thumbv8m.main-none-eabi" # Cortex-M33 (no FPU)
# target = "thumbv8m.main-none-eabihf" # Cortex-M33 (with FPU)

View File

@ -1,39 +0,0 @@
[target.'cfg(all(target_arch = "arm", target_os = "none"))']
# uncomment ONE of these three option to make `cargo run` start a GDB session
# which option to pick depends on your system
# If the RevA board is used, replace jlink.gdb with jlink-reva.gdb
# runner = "arm-none-eabi-gdb -q -x jlink/jlink.gdb"
# runner = "gdb-multiarch -q -x jlink/jlink.gdb"
# runner = "arm-none-eabi-gdb -q -x jlink/jlink-reva.gdb"
# runner = "gdb-multiarch -q -x jlink/jlink-reva.gdb"
rustflags = [
# This is needed if your flash or ram addresses are not aligned to 0x10000 in memory.x
# See https://github.com/rust-embedded/cortex-m-quickstart/pull/95
"-C", "link-arg=--nmagic",
# LLD (shipped with the Rust toolchain) is used as the default linker
"-C", "link-arg=-Tlink.x",
# if you run into problems with LLD switch to the GNU linker by commenting out
# this line
# "-C", "linker=arm-none-eabi-ld",
# if you need to link to pre-compiled C libraries provided by a C toolchain
# use GCC as the linker by commenting out both lines above and then
# uncommenting the three lines below
# "-C", "linker=arm-none-eabi-gcc",
# "-C", "link-arg=-Wl,-Tlink.x",
# "-C", "link-arg=-nostartfiles",
]
[build]
# Pick ONE of these compilation targets
# target = "thumbv6m-none-eabi" # Cortex-M0 and Cortex-M0+
# target = "thumbv7m-none-eabi" # Cortex-M3
# target = "thumbv7em-none-eabi" # Cortex-M4 and Cortex-M7 (no FPU)
target = "thumbv7em-none-eabihf" # Cortex-M4F and Cortex-M7F (with FPU)
# target = "thumbv8m.base-none-eabi" # Cortex-M23
# target = "thumbv8m.main-none-eabi" # Cortex-M33 (no FPU)
# target = "thumbv8m.main-none-eabihf" # Cortex-M33 (with FPU)

View File

@ -4,8 +4,8 @@ version = "0.1.0"
authors = ["Robin Mueller <muellerr@irs.uni-stuttgart.de>"]
edition = "2021"
description = "HAL for the Vorago VA416xx family of MCUs"
homepage = "https://egit.irs.uni-stuttgart.de/rust/va416xx-hal"
repository = "https://egit.irs.uni-stuttgart.de/rust/va416xx-hal"
homepage = "https://egit.irs.uni-stuttgart.de/rust/va416xx-rs"
repository = "https://egit.irs.uni-stuttgart.de/rust/va416xx-rs"
license = "Apache-2.0"
keywords = ["no-std", "hal", "cortex-m", "vorago", "va416xx"]
categories = ["embedded", "no-std", "hardware-support"]
@ -17,7 +17,11 @@ paste = "1"
embedded-hal-nb = "1"
embedded-hal = "1"
embedded-io = "0.6"
embedded-dma = "0.2"
num_enum = { version = "0.7", default-features = false }
typenum = "1"
bitflags = "2"
bitfield = "0.15"
defmt = { version = "0.3", optional = true }
fugit = "0.3"
delegate = "0.12"
@ -27,9 +31,8 @@ version = "1"
default-features = false
[dependencies.va416xx]
path = "../va416xx"
default-features = false
version = "0.2.0"
version = "0.2"
features = ["critical-section"]
[features]

View File

@ -1,3 +1,6 @@
[![Crates.io](https://img.shields.io/crates/v/va416xx-hal)](https://crates.io/crates/va416xx-hal)
[![docs.rs](https://img.shields.io/docsrs/va416xx-hal)](https://docs.rs/va416xx-hal)
# HAL for the Vorago VA416xx MCU family
This repository contains the **H**ardware **A**bstraction **L**ayer (HAL), which is an additional
@ -8,12 +11,6 @@ raw PAC. This crate also implements traits specified by the
[embedded-hal](https://github.com/rust-embedded/embedded-hal) project, making it compatible with
various drivers in the embedded rust ecosystem.
## Supported Boards
The first way to use this HAL will probably be with the
[PEB1 development board](https://www.voragotech.com/products/peb1va416x0-development-kit).
The BSP provided for this board also contains instructions how to flash the board.
## Building
Building an application requires the `thumbv7em-none-eabihf` cross-compiler toolchain.
@ -44,7 +41,8 @@ is contained within the
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`
3. To ensure that `cargo build` cross-compiles, it is recommended to create a `.cargo/config.toml`
file. A sample `.cargo/config.toml` file is provided in this repository as well
file. You can use [this](https://egit.irs.uni-stuttgart.de/rust/va416xx-rs/src/branch/main/.cargo/def-config.toml)
sample file as a starting point.
4. Copy the `memory.x` file into your project. This file contains information required by the linker.
5. Copy the `blinky.rs` file to the `src/main.rs` file in your binary crate
6. You need to add some dependencies to your `Cargo.toml` file

View File

@ -1,14 +0,0 @@
MEMORY
{
FLASH : ORIGIN = 0x00000000, LENGTH = 256K
/* RAM is a mandatory region. This RAM refers to the SRAM_0 */
RAM : ORIGIN = 0x1FFF8000, LENGTH = 32K
SRAM_1 : ORIGIN = 0x20000000, LENGTH = 32K
}
/* This is where the call stack will be allocated. */
/* The stack is of the full descending type. */
/* NOTE Do NOT modify `_stack_start` unless you know what you are doing */
/* SRAM_0 can be used for all busses: Instruction, Data and System */
/* SRAM_1 only supports the system bus */
_stack_start = ORIGIN(RAM) + LENGTH(RAM) - 4;

402
va416xx-hal/src/adc.rs Normal file
View File

@ -0,0 +1,402 @@
use core::marker::PhantomData;
use crate::clock::Clocks;
use crate::pac;
use crate::prelude::*;
use crate::time::Hertz;
use num_enum::{IntoPrimitive, TryFromPrimitive};
pub const ADC_MIN_CLK: Hertz = Hertz::from_raw(2_000_000);
pub const ADC_MAX_CLK: Hertz = Hertz::from_raw(12_500_000);
#[derive(Debug, PartialEq, Eq, Copy, Clone, TryFromPrimitive, IntoPrimitive)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[repr(u8)]
pub enum ChannelSelect {
/// Analogue Input 0 external channel
AnIn0 = 0,
/// Analogue Input 1 external channel
AnIn1 = 1,
/// Analogue Input 2 external channel
AnIn2 = 2,
/// Analogue Input 3 external channel
AnIn3 = 3,
/// Analogue Input 4 external channel
AnIn4 = 4,
/// Analogue Input 5 external channel
AnIn5 = 5,
/// Analogue Input 6 external channel
AnIn6 = 6,
/// Analogue Input 7 external channel
AnIn7 = 7,
/// DAC 0 internal channel
Dac0 = 8,
/// DAC 1 internal channel
Dac1 = 9,
/// Internal temperature sensor
TempSensor = 10,
/// Internal bandgap 1 V reference
Bandgap1V = 11,
/// Internal bandgap 1.5 V reference
Bandgap1_5V = 12,
Avdd1_5 = 13,
Dvdd1_5 = 14,
/// Internally generated Voltage equal to VREFH / 2
Vrefp5 = 15,
}
bitflags::bitflags! {
pub struct MultiChannelSelect: u16 {
const AnIn0 = 1;
const AnIn1 = 1 << 1;
const AnIn2 = 1 << 2;
const AnIn3 = 1 << 3;
const AnIn4 = 1 << 4;
const AnIn5 = 1 << 5;
const AnIn6 = 1 << 6;
const AnIn7 = 1 << 7;
const Dac0 = 1 << 8;
const Dac1 = 1 << 9;
const TempSensor = 1 << 10;
const Bandgap1V = 1 << 11;
const Bandgap1_5V = 1 << 12;
const Avdd1_5 = 1 << 13;
const Dvdd1_5 = 1 << 14;
const Vrefp5 = 1 << 15;
}
}
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct AdcEmptyError;
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct InvalidChannelRangeError;
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct BufferTooSmallError;
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum AdcRangeReadError {
InvalidChannelRange(InvalidChannelRangeError),
BufferTooSmall(BufferTooSmallError),
}
impl From<InvalidChannelRangeError> for AdcRangeReadError {
fn from(value: InvalidChannelRangeError) -> Self {
AdcRangeReadError::InvalidChannelRange(value)
}
}
impl From<BufferTooSmallError> for AdcRangeReadError {
fn from(value: BufferTooSmallError) -> Self {
AdcRangeReadError::BufferTooSmall(value)
}
}
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct ChannelValue {
/// If the channel tag is enabled, this field will contain the determined channel tag.
channel: ChannelSelect,
/// Raw value.
value: u16,
}
impl Default for ChannelValue {
fn default() -> Self {
Self {
channel: ChannelSelect::AnIn0,
value: Default::default(),
}
}
}
impl ChannelValue {
#[inline]
pub fn value(&self) -> u16 {
self.value
}
#[inline]
pub fn channel(&self) -> ChannelSelect {
self.channel
}
}
pub enum ChannelTagEnabled {}
pub enum ChannelTagDisabled {}
pub struct Adc<TagEnabled = ChannelTagDisabled> {
adc: pac::Adc,
phantom: PhantomData<TagEnabled>,
}
impl Adc<ChannelTagEnabled> {}
impl Adc<ChannelTagDisabled> {
pub fn new(syscfg: &mut pac::Sysconfig, adc: pac::Adc, clocks: &Clocks) -> Self {
Self::generic_new(syscfg, adc, clocks)
}
pub fn trigger_and_read_single_channel(&self, ch: ChannelSelect) -> Result<u16, AdcEmptyError> {
self.generic_trigger_and_read_single_channel(ch)
.map(|v| v & 0xfff)
}
/// Perform a sweep for a specified range of ADC channels.
///
/// Returns the number of read values which were written to the passed RX buffer.
pub fn sweep_and_read_range(
&self,
lower_bound_idx: u8,
upper_bound_idx: u8,
rx_buf: &mut [u16],
) -> Result<(), AdcRangeReadError> {
self.generic_prepare_range_sweep_and_wait_until_ready(
lower_bound_idx,
upper_bound_idx,
rx_buf.len(),
)?;
for i in 0..self.adc.status().read().fifo_entry_cnt().bits() {
rx_buf[i as usize] = self.adc.fifo_data().read().bits() as u16 & 0xfff;
}
Ok(())
}
pub fn sweep_and_read_multiselect(
&self,
ch_select: MultiChannelSelect,
rx_buf: &mut [u16],
) -> Result<(), BufferTooSmallError> {
self.generic_prepare_multiselect_sweep_and_wait_until_ready(ch_select, rx_buf.len())?;
for i in 0..self.adc.status().read().fifo_entry_cnt().bits() {
rx_buf[i as usize] = self.adc.fifo_data().read().bits() as u16 & 0xfff;
}
Ok(())
}
pub fn try_read_single_value(&self) -> nb::Result<Option<u16>, ()> {
self.generic_try_read_single_value()
.map(|v| v.map(|v| v & 0xfff))
}
}
impl Adc<ChannelTagEnabled> {
pub fn new_with_channel_tag(
syscfg: &mut pac::Sysconfig,
adc: pac::Adc,
clocks: &Clocks,
) -> Self {
let mut adc = Self::generic_new(syscfg, adc, clocks);
adc.enable_channel_tag();
adc
}
pub fn trigger_and_read_single_channel(
&self,
ch: ChannelSelect,
) -> Result<ChannelValue, AdcEmptyError> {
self.generic_trigger_and_read_single_channel(ch)
.map(|v| self.create_channel_value(v))
}
pub fn try_read_single_value(&self) -> nb::Result<Option<ChannelValue>, ()> {
self.generic_try_read_single_value()
.map(|v| v.map(|v| self.create_channel_value(v)))
}
/// Perform a sweep for a specified range of ADC channels.
///
/// Returns the number of read values which were written to the passed RX buffer.
pub fn sweep_and_read_range(
&self,
lower_bound_idx: u8,
upper_bound_idx: u8,
rx_buf: &mut [ChannelValue],
) -> Result<usize, AdcRangeReadError> {
self.generic_prepare_range_sweep_and_wait_until_ready(
lower_bound_idx,
upper_bound_idx,
rx_buf.len(),
)?;
let fifo_entry_count = self.adc.status().read().fifo_entry_cnt().bits();
for i in 0..core::cmp::min(fifo_entry_count, rx_buf.len() as u8) {
rx_buf[i as usize] =
self.create_channel_value(self.adc.fifo_data().read().bits() as u16);
}
Ok(fifo_entry_count as usize)
}
pub fn sweep_and_read_multiselect(
&self,
ch_select: MultiChannelSelect,
rx_buf: &mut [ChannelValue],
) -> Result<(), BufferTooSmallError> {
self.generic_prepare_multiselect_sweep_and_wait_until_ready(ch_select, rx_buf.len())?;
for i in 0..self.adc.status().read().fifo_entry_cnt().bits() {
rx_buf[i as usize] =
self.create_channel_value(self.adc.fifo_data().read().bits() as u16);
}
Ok(())
}
#[inline]
pub fn create_channel_value(&self, raw_value: u16) -> ChannelValue {
ChannelValue {
value: raw_value & 0xfff,
channel: ChannelSelect::try_from(((raw_value >> 12) & 0xf) as u8).unwrap(),
}
}
}
impl<TagEnabled> Adc<TagEnabled> {
fn generic_new(syscfg: &mut pac::Sysconfig, adc: pac::Adc, _clocks: &Clocks) -> Self {
syscfg.enable_peripheral_clock(crate::clock::PeripheralSelect::Adc);
adc.ctrl().write(|w| unsafe { w.bits(0) });
let adc = Self {
adc,
phantom: PhantomData,
};
adc.clear_fifo();
adc
}
#[inline(always)]
fn enable_channel_tag(&mut self) {
self.adc.ctrl().modify(|_, w| w.chan_tag_en().set_bit());
}
#[inline(always)]
fn disable_channel_tag(&mut self) {
self.adc.ctrl().modify(|_, w| w.chan_tag_en().clear_bit());
}
#[inline(always)]
pub fn channel_tag_enabled(&self) -> bool {
self.adc.ctrl().read().chan_tag_en().bit_is_set()
}
#[inline(always)]
pub fn clear_fifo(&self) {
self.adc.fifo_clr().write(|w| unsafe { w.bits(1) });
}
pub fn generic_try_read_single_value(&self) -> nb::Result<Option<u16>, ()> {
if self.adc.status().read().adc_busy().bit_is_set() {
return Err(nb::Error::WouldBlock);
}
if self.adc.status().read().fifo_entry_cnt().bits() == 0 {
return Ok(None);
}
Ok(Some(self.adc.fifo_data().read().bits() as u16))
}
fn generic_trigger_single_channel(&self, ch: ChannelSelect) {
self.adc.ctrl().modify(|_, w| {
w.ext_trig_en().clear_bit();
unsafe {
// N + 1 conversions, so set set 0 here.
w.conv_cnt().bits(0);
w.chan_en().bits(1 << ch as u8)
}
});
self.clear_fifo();
self.adc.ctrl().modify(|_, w| w.manual_trig().set_bit());
}
fn generic_prepare_range_sweep_and_wait_until_ready(
&self,
lower_bound_idx: u8,
upper_bound_idx: u8,
buf_len: usize,
) -> Result<(), AdcRangeReadError> {
if (lower_bound_idx > 15 || upper_bound_idx > 15) || lower_bound_idx > upper_bound_idx {
return Err(InvalidChannelRangeError.into());
}
let ch_count = upper_bound_idx - lower_bound_idx + 1;
if buf_len < ch_count as usize {
return Err(BufferTooSmallError.into());
}
let mut ch_select = 0;
for i in lower_bound_idx..upper_bound_idx + 1 {
ch_select |= 1 << i;
}
self.generic_trigger_sweep(ch_select);
cortex_m::asm::nop();
cortex_m::asm::nop();
while self.adc.status().read().adc_busy().bit_is_set() {
cortex_m::asm::nop();
}
Ok(())
}
fn generic_prepare_multiselect_sweep_and_wait_until_ready(
&self,
ch_select: MultiChannelSelect,
buf_len: usize,
) -> Result<(), BufferTooSmallError> {
let ch_select = ch_select.bits();
let ch_count = ch_select.count_ones();
if buf_len < ch_count as usize {
return Err(BufferTooSmallError);
}
self.generic_trigger_sweep(ch_select);
while self.adc.status().read().adc_busy().bit_is_set() {
cortex_m::asm::nop();
}
Ok(())
}
fn generic_trigger_sweep(&self, ch_select: u16) {
let ch_num = ch_select.count_ones() as u8;
assert!(ch_num > 0);
self.adc.ctrl().modify(|_, w| {
w.ext_trig_en().clear_bit();
unsafe {
// N + 1 conversions.
w.conv_cnt().bits(0);
w.chan_en().bits(ch_select);
w.sweep_en().set_bit()
}
});
self.clear_fifo();
self.adc.ctrl().modify(|_, w| w.manual_trig().set_bit());
}
fn generic_trigger_and_read_single_channel(
&self,
ch: ChannelSelect,
) -> Result<u16, AdcEmptyError> {
self.generic_trigger_single_channel(ch);
nb::block!(self.generic_try_read_single_value())
.unwrap()
.ok_or(AdcEmptyError)
}
}
impl From<Adc<ChannelTagDisabled>> for Adc<ChannelTagEnabled> {
fn from(value: Adc<ChannelTagDisabled>) -> Self {
let mut adc = Self {
adc: value.adc,
phantom: PhantomData,
};
adc.enable_channel_tag();
adc
}
}
impl From<Adc<ChannelTagEnabled>> for Adc<ChannelTagDisabled> {
fn from(value: Adc<ChannelTagEnabled>) -> Self {
let mut adc = Self {
adc: value.adc,
phantom: PhantomData,
};
adc.disable_channel_tag();
adc
}
}

View File

@ -10,6 +10,7 @@
//! # Examples
//!
//! - [UART example on the PEB1 board](https://egit.irs.uni-stuttgart.de/rust/va416xx-rs/src/branch/main/examples/simple/examples/uart.rs)
use crate::adc::ADC_MAX_CLK;
use crate::pac;
use crate::time::Hertz;
@ -52,7 +53,7 @@ pub enum PeripheralSelect {
PortG = 30,
}
pub type PeripheralClocks = PeripheralSelect;
pub type PeripheralClock = PeripheralSelect;
#[derive(Debug, PartialEq, Eq)]
pub enum FilterClkSel {
@ -94,24 +95,34 @@ pub fn deassert_periph_reset(syscfg: &mut pac::Sysconfig, periph: PeripheralSele
.modify(|r, w| unsafe { w.bits(r.bits() | (1 << periph as u8)) });
}
#[inline(always)]
fn assert_periph_reset_for_two_cycles(syscfg: &mut pac::Sysconfig, periph: PeripheralSelect) {
assert_periph_reset(syscfg, periph);
cortex_m::asm::nop();
cortex_m::asm::nop();
deassert_periph_reset(syscfg, periph);
}
pub trait SyscfgExt {
fn enable_peripheral_clock(&mut self, clock: PeripheralClocks);
fn enable_peripheral_clock(&mut self, clock: PeripheralClock);
fn disable_peripheral_clock(&mut self, clock: PeripheralClocks);
fn disable_peripheral_clock(&mut self, clock: PeripheralClock);
fn assert_periph_reset(&mut self, clock: PeripheralSelect);
fn assert_periph_reset(&mut self, periph: PeripheralSelect);
fn deassert_periph_reset(&mut self, clock: PeripheralSelect);
fn deassert_periph_reset(&mut self, periph: PeripheralSelect);
fn assert_periph_reset_for_two_cycles(&mut self, periph: PeripheralSelect);
}
impl SyscfgExt for pac::Sysconfig {
#[inline(always)]
fn enable_peripheral_clock(&mut self, clock: PeripheralClocks) {
fn enable_peripheral_clock(&mut self, clock: PeripheralClock) {
enable_peripheral_clock(self, clock)
}
#[inline(always)]
fn disable_peripheral_clock(&mut self, clock: PeripheralClocks) {
fn disable_peripheral_clock(&mut self, clock: PeripheralClock) {
disable_peripheral_clock(self, clock)
}
@ -124,6 +135,11 @@ impl SyscfgExt for pac::Sysconfig {
fn deassert_periph_reset(&mut self, clock: PeripheralSelect) {
deassert_periph_reset(self, clock)
}
#[inline(always)]
fn assert_periph_reset_for_two_cycles(&mut self, periph: PeripheralSelect) {
assert_periph_reset_for_two_cycles(self, periph)
}
}
/// Refer to chapter 8 (p.57) of the programmers guide for detailed information.
@ -435,20 +451,23 @@ impl ClkgenCfgr {
// ADC clock (must be 2-12.5 MHz)
// NOTE: Not using divide by 1 or /2 ratio in REVA silicon because of triggering issue
// For this reason, keep SYSCLK above 8MHz to have the ADC /4 ratio in range)
if final_sysclk.raw() <= 50_000_000 {
let adc_clk = if final_sysclk.raw() <= ADC_MAX_CLK.raw() * 4 {
self.clkgen
.ctrl1()
.modify(|_, w| unsafe { w.adc_clk_div_sel().bits(AdcClkDivSel::Div4 as u8) });
final_sysclk / 4
} else {
self.clkgen
.ctrl1()
.modify(|_, w| unsafe { w.adc_clk_div_sel().bits(AdcClkDivSel::Div8 as u8) });
}
final_sysclk / 8
};
Ok(Clocks {
sysclk: final_sysclk,
apb1: final_sysclk / 2,
apb2: final_sysclk / 4,
adc_clk,
})
}
}
@ -464,6 +483,7 @@ pub struct Clocks {
sysclk: Hertz,
apb1: Hertz,
apb2: Hertz,
adc_clk: Hertz,
}
impl Clocks {
@ -491,6 +511,11 @@ impl Clocks {
pub fn sysclk(&self) -> Hertz {
self.sysclk
}
/// Returns the ADC clock frequency which has a separate divider.
pub fn adc_clk(&self) -> Hertz {
self.adc_clk
}
}
pub fn rearm_sysclk_lost() {

157
va416xx-hal/src/dac.rs Normal file
View File

@ -0,0 +1,157 @@
use core::ops::Deref;
use crate::{
clock::{Clocks, PeripheralSelect, SyscfgExt},
pac,
};
pub type DacRegisterBlock = pac::dac0::RegisterBlock;
/// Common trait implemented by all PAC peripheral access structures. The register block
/// format is the same for all DAC blocks.
pub trait Instance: Deref<Target = DacRegisterBlock> {
const IDX: u8;
fn ptr() -> *const DacRegisterBlock;
}
impl Instance for pac::Dac0 {
const IDX: u8 = 0;
#[inline(always)]
fn ptr() -> *const DacRegisterBlock {
Self::ptr()
}
}
impl Instance for pac::Dac1 {
const IDX: u8 = 1;
#[inline(always)]
fn ptr() -> *const DacRegisterBlock {
Self::ptr()
}
}
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum DacSettling {
NoSettling = 0,
Apb2Times25 = 1,
Apb2Times50 = 2,
Apb2Times75 = 3,
Apb2Times100 = 4,
Apb2Times125 = 5,
Apb2Times150 = 6,
}
pub struct Dac<DacInstance> {
dac: DacInstance,
}
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct ValueTooLarge;
impl<DacInstance: Instance> Dac<DacInstance> {
/// Create a new [Dac] driver instance.
///
/// The [Clocks] structure is expected here as well to ensure the clock was set up properly.
pub fn new(
syscfg: &mut pac::Sysconfig,
dac: DacInstance,
dac_settling: DacSettling,
_clocks: &Clocks,
) -> Self {
syscfg.enable_peripheral_clock(PeripheralSelect::Dac);
dac.ctrl1().write(|w| {
w.dac_en().set_bit();
// SAFETY: Enum values are valid values only.
unsafe { w.dac_settling().bits(dac_settling as u8) }
});
let dac = Self { dac };
dac.clear_fifo();
dac.clear_irqs();
dac
}
#[inline(always)]
pub fn clear_irqs(&self) {
self.dac.irq_clr().write(|w| {
w.fifo_oflow().set_bit();
w.fifo_uflow().set_bit();
w.dac_done().set_bit();
w.trig_error().set_bit()
});
}
#[inline(always)]
pub fn clear_fifo(&self) {
self.dac.fifo_clr().write(|w| unsafe { w.bits(1) });
}
/// Load next value into the FIFO.
///
/// Uses the [nb] API to allow blocking and non-blocking usage.
#[inline(always)]
pub fn load_value(&self, val: u16) -> nb::Result<(), ValueTooLarge> {
if val > 2_u16.pow(12) - 1 {
return Err(nb::Error::Other(ValueTooLarge));
}
if self.dac.status().read().fifo_entry_cnt().bits() >= 32_u8 {
return Err(nb::Error::WouldBlock);
}
self.dac
.fifo_data()
.write(|w| unsafe { w.bits(val.into()) });
Ok(())
}
/// This loads and triggers the next value immediately. It also clears the FIFO before
/// loading the passed value.
#[inline(always)]
pub fn load_and_trigger_manually(&self, val: u16) -> Result<(), ValueTooLarge> {
if val > 2_u16.pow(12) - 1 {
return Err(ValueTooLarge);
}
self.clear_fifo();
// This should never block, the FIFO was cleared. We checked the value as well, so unwrap
// is okay here.
nb::block!(self.load_value(val)).unwrap();
self.trigger_manually();
Ok(())
}
/// Manually trigger the DAC. This will de-queue the next value inside the FIFO
/// to be processed by the DAC.
#[inline(always)]
pub fn trigger_manually(&self) {
self.dac.ctrl0().write(|w| w.man_trig_en().set_bit());
}
#[inline(always)]
pub fn enable_external_trigger(&self) {
self.dac.ctrl0().write(|w| w.ext_trig_en().set_bit());
}
pub fn is_settled(&self) -> nb::Result<(), ()> {
if self.dac.status().read().dac_busy().bit_is_set() {
return Err(nb::Error::WouldBlock);
}
Ok(())
}
#[inline(always)]
pub fn reset(&mut self, syscfg: &mut pac::Sysconfig) {
syscfg.enable_peripheral_clock(PeripheralSelect::Dac);
syscfg.assert_periph_reset_for_two_cycles(PeripheralSelect::Dac);
}
/// Relases the DAC, which also disables its peripheral clock.
#[inline(always)]
pub fn release(self, syscfg: &mut pac::Sysconfig) -> DacInstance {
syscfg.disable_peripheral_clock(PeripheralSelect::Dac);
self.dac
}
}

515
va416xx-hal/src/dma.rs Normal file
View File

@ -0,0 +1,515 @@
//! API for the DMA peripheral
//!
//! ## Examples
//!
//! - [Simple DMA example](https://egit.irs.uni-stuttgart.de/rust/va416xx-rs/src/branch/main/examples/simple/examples/dma.rs)
use embedded_dma::WriteBuffer;
use crate::{
clock::{PeripheralClock, PeripheralSelect},
enable_interrupt, pac,
prelude::*,
};
const MAX_DMA_TRANSFERS_PER_CYCLE: usize = 1024;
const BASE_PTR_ADDR_MASK: u32 = 0b1111111;
/// DMA cycle control values.
///
/// Refer to chapter 6.3.1 and 6.6.3 of the datasheet for more details.
#[repr(u8)]
#[derive(Debug, Clone, Copy)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum CycleControl {
/// Indicates that the data structure is invalid.
Stop = 0b000,
/// The controller must receive a new request prior to entering the arbitration
/// process, to enable the DMA cycle to complete. This means that the DMA will only
/// continue to do transfers as long as a trigger signal is still active. Therefore,
/// this should not be used for momentary triggers like a timer.
Basic = 0b001,
/// The controller automatically inserts a request for the appropriate channel during the
/// arbitration process. This means that the initial request is sufficient to enable the
/// DMA cycle to complete.
Auto = 0b010,
/// This is used to support continuous data flow. Both primary and alternate data structure
/// are used. The primary data structure is used first. When the first transfer is complete, an
/// interrupt can be generated, and the DMA switches to the alternate data structure. When the
/// second transfer is complete, the primary data structure is used. This pattern continues
/// until software disables the channel.
PingPong = 0b011,
MemScatterGatherPrimary = 0b100,
MemScatterGatherAlternate = 0b101,
PeriphScatterGatherPrimary = 0b110,
PeriphScatterGatherAlternate = 0b111,
}
#[derive(Debug, Clone, Copy)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum AddrIncrement {
Byte = 0b00,
Halfword = 0b01,
Word = 0b10,
None = 0b11,
}
#[derive(Debug, Clone, Copy)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum DataSize {
Byte = 0b00,
Halfword = 0b01,
Word = 0b10,
}
/// This configuration controls how many DMA transfers can occur before the controller arbitrates.
#[derive(Debug, Clone, Copy)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum RPower {
EachTransfer = 0b0000,
Every2 = 0b0001,
Every4 = 0b0010,
Every8 = 0b0011,
Every16 = 0b0100,
Every32 = 0b0101,
Every64 = 0b0110,
Every128 = 0b0111,
Every256 = 0b1000,
Every512 = 0b1001,
Every1024Min = 0b1010,
Every1024 = 0b1111,
}
#[derive(Debug, PartialEq, Eq)]
pub struct InvalidCtrlBlockAddr;
bitfield::bitfield! {
#[repr(transparent)]
#[derive(Clone, Copy)]
pub struct ChannelConfig(u32);
impl Debug;
u32;
pub raw, set_raw: 31,0;
u8;
pub dst_inc, set_dst_inc: 31, 30;
u8;
pub dst_size, set_dst_size: 29, 28;
u8;
pub src_inc, set_src_inc: 27, 26;
u8;
pub src_size, set_src_size: 25, 24;
u8;
pub dest_prot_ctrl, set_dest_prot_ctrl: 23, 21;
u8;
pub src_prot_ctrl, set_src_prot_ctrl: 20, 18;
u8;
pub r_power, set_r_power: 17, 14;
u16;
pub n_minus_1, set_n_minus_1: 13, 4;
bool;
pub next_useburst, set_next_useburst: 3;
u8;
pub cycle_ctrl, set_cycle_ctr: 2, 0;
}
#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub struct DmaChannelControl {
pub src_end_ptr: u32,
pub dest_end_ptr: u32,
pub cfg: ChannelConfig,
padding: u32,
}
impl DmaChannelControl {
const fn new() -> Self {
Self {
src_end_ptr: 0,
dest_end_ptr: 0,
cfg: ChannelConfig(0),
padding: 0,
}
}
}
impl Default for DmaChannelControl {
fn default() -> Self {
Self::new()
}
}
#[repr(C)]
#[repr(align(128))]
pub struct DmaCtrlBlock {
pub pri: [DmaChannelControl; 4],
pub alt: [DmaChannelControl; 4],
}
impl DmaCtrlBlock {
pub const fn new() -> Self {
Self {
pri: [DmaChannelControl::new(); 4],
alt: [DmaChannelControl::new(); 4],
}
}
}
impl Default for DmaCtrlBlock {
fn default() -> Self {
Self::new()
}
}
impl DmaCtrlBlock {
/// This function creates a DMA control block at the specified memory address.
///
/// The passed address must be 128-byte aligned. The user must also take care of specifying
/// a valid memory address for the DMA control block which is accessible by the system as well.
/// For example, the control block can be placed in the SRAM1.
pub fn new_at_addr(addr: u32) -> Result<*mut DmaCtrlBlock, InvalidCtrlBlockAddr> {
if addr & BASE_PTR_ADDR_MASK > 0 {
return Err(InvalidCtrlBlockAddr);
}
let ctrl_block_ptr = addr as *mut DmaCtrlBlock;
unsafe { core::ptr::write(ctrl_block_ptr, DmaCtrlBlock::default()) }
Ok(ctrl_block_ptr)
}
}
pub struct Dma {
dma: pac::Dma,
ctrl_block: *mut DmaCtrlBlock,
}
#[derive(Debug, Clone, Copy)]
pub enum DmaTransferInitError {
SourceDestLenMissmatch {
src_len: usize,
dest_len: usize,
},
/// Overflow when calculating the source or destination end address.
AddrOverflow,
/// Transfer size larger than 1024 units.
TransferSizeTooLarge(usize),
}
#[derive(Debug, Clone, Copy, Default)]
pub struct DmaCfg {
pub bufferable: bool,
pub cacheable: bool,
pub privileged: bool,
}
pub struct DmaChannel {
channel: u8,
done_interrupt: pac::Interrupt,
active_interrupt: pac::Interrupt,
pub dma: pac::Dma,
pub ch_ctrl_pri: &'static mut DmaChannelControl,
pub ch_ctrl_alt: &'static mut DmaChannelControl,
}
/// This transfer structure takes ownership of the mutable destination slice.
///
/// This avoids accidental violation of the ownership rules because the DMA now has mutable
/// access to that slice as well. The mutable slice can be retrieved after DMA transfer completion
/// by using the [Self::release] method.
pub struct DmaTransfer<W> {
buf: W,
//ch: DmaChannel
}
impl<W: WriteBuffer> DmaTransfer<W> {
/// Retrieve the mutable destination slice once the DMA transfer has completed.
///
/// # Safety
///
/// - The user MUST ensure that the DMA transfer has completed, for example by polling a
/// completion flag set by the DMA_DONE ISR.
pub unsafe fn release(self) -> W {
self.buf
}
}
impl DmaChannel {
#[inline(always)]
pub fn channel(&self) -> u8 {
self.channel
}
#[inline(always)]
pub fn enable(&mut self) {
self.dma
.chnl_enable_set()
.write(|w| unsafe { w.bits(1 << self.channel) });
}
#[inline(always)]
pub fn is_enabled(&mut self) -> bool {
((self.dma.chnl_enable_set().read().bits() >> self.channel) & 0b1) != 0
}
#[inline(always)]
pub fn disable(&mut self) {
self.dma
.chnl_enable_clr()
.write(|w| unsafe { w.bits(1 << self.channel) });
}
#[inline(always)]
pub fn trigger_with_sw_request(&mut self) {
self.dma
.chnl_sw_request()
.write(|w| unsafe { w.bits(1 << self.channel) });
}
#[inline(always)]
pub fn state_raw(&self) -> u8 {
self.dma.status().read().state().bits()
}
#[inline(always)]
pub fn select_primary_structure(&self) {
self.dma
.chnl_pri_alt_clr()
.write(|w| unsafe { w.bits(1 << self.channel) });
}
#[inline(always)]
pub fn select_alternate_structure(&self) {
self.dma
.chnl_pri_alt_set()
.write(|w| unsafe { w.bits(1 << self.channel) });
}
/// Enables the DMA_DONE interrupt for the DMA channel.
///
/// # Safety
///
/// This function is `unsafe` because it can break mask-based critical sections.
pub unsafe fn enable_done_interrupt(&mut self) {
enable_interrupt(self.done_interrupt);
}
/// Enables the DMA_ACTIVE interrupt for the DMA channel.
///
/// # Safety
///
/// This function is `unsafe` because it can break mask-based critical sections.
pub unsafe fn enable_active_interrupt(&mut self) {
enable_interrupt(self.active_interrupt);
}
/// Prepares a 8-bit DMA transfer from memory to memory.
///
/// This function does not enable the DMA channel and interrupts and only prepares
/// the DMA control block parameters for the transfer. It configures the primary channel control
/// structure to perform the transfer.
///
/// You can use [Self::enable], [Self::enable_done_interrupt], [Self::enable_active_interrupt]
/// to finish the transfer preparation and then use [Self::trigger_with_sw_request] to
/// start the DMA transfer.
pub fn prepare_mem_to_mem_transfer_8_bit<W: WriteBuffer<Word = u8>>(
&mut self,
source: &[u8],
mut dest: W,
) -> Result<DmaTransfer<W>, DmaTransferInitError> {
let (write_ptr, len) = unsafe { dest.write_buffer() };
let len = Self::common_mem_transfer_checks(source.len(), len)?;
self.generic_mem_to_mem_transfer_init(
len,
(source.as_ptr() as u32)
.checked_add(len as u32)
.ok_or(DmaTransferInitError::AddrOverflow)?,
(write_ptr as u32)
.checked_add(len as u32)
.ok_or(DmaTransferInitError::AddrOverflow)?,
DataSize::Byte,
AddrIncrement::Byte,
);
Ok(DmaTransfer { buf: dest })
}
/// Prepares a 16-bit DMA transfer from memory to memory.
///
/// This function does not enable the DMA channel and interrupts and only prepares
/// the DMA control block parameters for the transfer. It configures the primary channel control
/// structure to perform the transfer.
///
/// You can use [Self::enable], [Self::enable_done_interrupt], [Self::enable_active_interrupt]
/// to finish the transfer preparation and then use [Self::trigger_with_sw_request] to
/// start the DMA transfer.
pub fn prepare_mem_to_mem_transfer_16_bit<'dest>(
&mut self,
source: &[u16],
dest: &'dest mut [u16],
) -> Result<(), DmaTransferInitError> {
let len = Self::common_mem_transfer_checks(source.len(), dest.len())?;
self.generic_mem_to_mem_transfer_init(
len,
(source.as_ptr() as u32)
.checked_add(len as u32 * core::mem::size_of::<u16>() as u32)
.ok_or(DmaTransferInitError::AddrOverflow)?,
(dest.as_ptr() as u32)
.checked_add(len as u32 * core::mem::size_of::<u16>() as u32)
.ok_or(DmaTransferInitError::AddrOverflow)?,
DataSize::Halfword,
AddrIncrement::Halfword,
);
Ok(())
}
/// Prepares a 32-bit DMA transfer from memory to memory.
///
/// This function does not enable the DMA channel and interrupts and only prepares
/// the DMA control block parameters for the transfer. It configures the primary channel control
/// structure to perform the transfer.
///
/// You can use [Self::enable], [Self::enable_done_interrupt], [Self::enable_active_interrupt]
/// to finish the transfer preparation and then use [Self::trigger_with_sw_request] to
/// start the DMA transfer.
pub fn prepare_mem_to_mem_transfer_32_bit<'dest>(
&mut self,
source: &[u32],
dest: &'dest mut [u32],
) -> Result<(), DmaTransferInitError> {
let len = Self::common_mem_transfer_checks(source.len(), dest.len())?;
self.generic_mem_to_mem_transfer_init(
len,
(source.as_ptr() as u32)
.checked_add(len as u32 * core::mem::size_of::<u32>() as u32)
.ok_or(DmaTransferInitError::AddrOverflow)?,
(dest.as_ptr() as u32)
.checked_add(len as u32 * core::mem::size_of::<u32>() as u32)
.ok_or(DmaTransferInitError::AddrOverflow)?,
DataSize::Word,
AddrIncrement::Word,
);
Ok(())
}
// This function performs common checks and returns the source length minus one which is
// relevant for further configuration of the DMA. This is because the DMA API expects N minus
// 1 and the source and end pointer need to point to the last transfer address.
fn common_mem_transfer_checks(
src_len: usize,
dest_len: usize,
) -> Result<usize, DmaTransferInitError> {
if src_len != dest_len {
return Err(DmaTransferInitError::SourceDestLenMissmatch { src_len, dest_len });
}
if src_len > MAX_DMA_TRANSFERS_PER_CYCLE {
return Err(DmaTransferInitError::TransferSizeTooLarge(src_len));
}
Ok(src_len - 1)
}
fn generic_mem_to_mem_transfer_init(
&mut self,
n_minus_one: usize,
src_end_ptr: u32,
dest_end_ptr: u32,
data_size: DataSize,
addr_incr: AddrIncrement,
) {
self.ch_ctrl_pri.cfg.set_raw(0);
self.ch_ctrl_pri.src_end_ptr = src_end_ptr;
self.ch_ctrl_pri.dest_end_ptr = dest_end_ptr;
self.ch_ctrl_pri.cfg.set_cycle_ctr(CycleControl::Auto as u8);
self.ch_ctrl_pri.cfg.set_src_size(data_size as u8);
self.ch_ctrl_pri.cfg.set_src_inc(addr_incr as u8);
self.ch_ctrl_pri.cfg.set_dst_size(data_size as u8);
self.ch_ctrl_pri.cfg.set_dst_inc(addr_incr as u8);
self.ch_ctrl_pri.cfg.set_n_minus_1(n_minus_one as u16);
self.ch_ctrl_pri.cfg.set_r_power(RPower::Every4 as u8);
self.select_primary_structure();
}
}
impl Dma {
/// Create a new DMA instance.
///
/// You can also place the [DmaCtrlBlock] statically using a global static mutable
/// instance and the [DmaCtrlBlock::new] const constructor This also allows to place the control
/// block in a memory section using the [link_section](https://doc.rust-lang.org/reference/abi.html#the-link_section-attribute)
/// attribute and then creating a mutable pointer to it using [core::ptr::addr_of_mut].
///
/// Alternatively, the [DmaCtrlBlock::new_at_addr] function can be used to create the DMA
/// control block at a specific address.
pub fn new(
syscfg: &mut pac::Sysconfig,
dma: pac::Dma,
cfg: DmaCfg,
ctrl_block: *mut DmaCtrlBlock,
) -> Result<Self, InvalidCtrlBlockAddr> {
// The conversion to u32 is safe here because we are on a 32-bit system.
let raw_addr = ctrl_block as u32;
if raw_addr & BASE_PTR_ADDR_MASK > 0 {
return Err(InvalidCtrlBlockAddr);
}
syscfg.enable_peripheral_clock(PeripheralClock::Dma);
syscfg.assert_periph_reset_for_two_cycles(PeripheralSelect::Dma);
let dma = Dma { dma, ctrl_block };
dma.dma
.ctrl_base_ptr()
.write(|w| unsafe { w.bits(raw_addr) });
dma.set_protection_bits(&cfg);
dma.enable();
Ok(dma)
}
#[inline(always)]
pub fn enable(&self) {
self.dma.cfg().write(|w| w.master_enable().set_bit());
}
#[inline(always)]
pub fn disable(&self) {
self.dma.cfg().write(|w| w.master_enable().clear_bit());
}
#[inline(always)]
pub fn set_protection_bits(&self, cfg: &DmaCfg) {
self.dma.cfg().write(|w| unsafe {
w.chnl_prot_ctrl().bits(
cfg.privileged as u8 | ((cfg.bufferable as u8) << 1) | ((cfg.cacheable as u8) << 2),
)
});
}
/// Split the DMA instance into four DMA channels which can be used individually. This allows
/// using the inidividual DMA channels in separate tasks.
pub fn split(self) -> (DmaChannel, DmaChannel, DmaChannel, DmaChannel) {
// Safety: The DMA channel API only operates on its respective channels.
(
DmaChannel {
channel: 0,
done_interrupt: pac::Interrupt::DMA_DONE0,
active_interrupt: pac::Interrupt::DMA_ACTIVE0,
dma: unsafe { pac::Dma::steal() },
ch_ctrl_pri: unsafe { &mut (*self.ctrl_block).pri[0] },
ch_ctrl_alt: unsafe { &mut (*self.ctrl_block).alt[0] },
},
DmaChannel {
channel: 1,
done_interrupt: pac::Interrupt::DMA_DONE1,
active_interrupt: pac::Interrupt::DMA_ACTIVE1,
dma: unsafe { pac::Dma::steal() },
ch_ctrl_pri: unsafe { &mut (*self.ctrl_block).pri[1] },
ch_ctrl_alt: unsafe { &mut (*self.ctrl_block).alt[1] },
},
DmaChannel {
channel: 2,
done_interrupt: pac::Interrupt::DMA_DONE2,
active_interrupt: pac::Interrupt::DMA_ACTIVE2,
dma: unsafe { pac::Dma::steal() },
ch_ctrl_pri: unsafe { &mut (*self.ctrl_block).pri[2] },
ch_ctrl_alt: unsafe { &mut (*self.ctrl_block).alt[2] },
},
DmaChannel {
channel: 3,
done_interrupt: pac::Interrupt::DMA_DONE3,
active_interrupt: pac::Interrupt::DMA_ACTIVE3,
dma: unsafe { pac::Dma::steal() },
ch_ctrl_pri: unsafe { &mut (*self.ctrl_block).pri[3] },
ch_ctrl_alt: unsafe { &mut (*self.ctrl_block).alt[3] },
},
)
}
}

View File

@ -4,11 +4,9 @@
//!
//! - [PEB1 accelerometer example](https://egit.irs.uni-stuttgart.de/rust/va416xx-rs/src/branch/main/examples/simple/examples/peb1-accelerometer.rs)
use crate::{
clock::{
assert_periph_reset, deassert_periph_reset, enable_peripheral_clock, Clocks,
PeripheralSelect,
},
clock::{Clocks, PeripheralSelect},
pac,
prelude::SyscfgExt,
time::Hertz,
typelevel::Sealed,
};
@ -125,6 +123,7 @@ impl Instance for pac::I2c0 {
const IDX: u8 = 0;
const PERIPH_SEL: PeripheralSelect = PeripheralSelect::I2c0;
#[inline(always)]
fn ptr() -> *const I2cRegBlock {
Self::ptr()
}
@ -134,6 +133,7 @@ impl Instance for pac::I2c1 {
const IDX: u8 = 1;
const PERIPH_SEL: PeripheralSelect = PeripheralSelect::I2c1;
#[inline(always)]
fn ptr() -> *const I2cRegBlock {
Self::ptr()
}
@ -143,6 +143,7 @@ impl Instance for pac::I2c2 {
const IDX: u8 = 2;
const PERIPH_SEL: PeripheralSelect = PeripheralSelect::I2c2;
#[inline(always)]
fn ptr() -> *const I2cRegBlock {
Self::ptr()
}
@ -312,17 +313,13 @@ impl<I2C> I2cBase<I2C> {
impl<I2c: Instance> I2cBase<I2c> {
pub fn new(
i2c: I2c,
sys_cfg: &mut pac::Sysconfig,
syscfg: &mut pac::Sysconfig,
clocks: &Clocks,
speed_mode: I2cSpeed,
ms_cfg: Option<&MasterConfig>,
sl_cfg: Option<&SlaveConfig>,
) -> Result<Self, ClockTooSlowForFastI2c> {
enable_peripheral_clock(sys_cfg, I2c::PERIPH_SEL);
assert_periph_reset(sys_cfg, I2c::PERIPH_SEL);
cortex_m::asm::nop();
cortex_m::asm::nop();
deassert_periph_reset(sys_cfg, I2c::PERIPH_SEL);
syscfg.enable_peripheral_clock(I2c::PERIPH_SEL);
let mut i2c_base = I2cBase {
i2c,

View File

@ -8,7 +8,10 @@ pub use va416xx as pac;
pub mod prelude;
pub mod adc;
pub mod clock;
pub mod dac;
pub mod dma;
pub mod gpio;
pub mod i2c;
pub mod pwm;

View File

@ -8,7 +8,7 @@ use core::{convert::Infallible, marker::PhantomData, ops::Deref};
use embedded_hal::spi::Mode;
use crate::{
clock::PeripheralSelect,
clock::{PeripheralSelect, SyscfgExt},
gpio::{
AltFunc1, AltFunc2, AltFunc3, Pin, PA0, PA1, PA2, PA3, PA4, PA5, PA6, PA7, PA8, PA9, PB0,
PB1, PB10, PB11, PB12, PB13, PB14, PB15, PB2, PB3, PB4, PB5, PB6, PB7, PB8, PB9, PC0, PC1,
@ -365,6 +365,7 @@ impl Instance for pac::Spi0 {
const IDX: u8 = 0;
const PERIPH_SEL: PeripheralSelect = PeripheralSelect::Spi0;
#[inline(always)]
fn ptr() -> *const SpiRegBlock {
Self::ptr()
}
@ -374,6 +375,7 @@ impl Instance for pac::Spi1 {
const IDX: u8 = 1;
const PERIPH_SEL: PeripheralSelect = PeripheralSelect::Spi1;
#[inline(always)]
fn ptr() -> *const SpiRegBlock {
Self::ptr()
}
@ -383,6 +385,7 @@ impl Instance for pac::Spi2 {
const IDX: u8 = 2;
const PERIPH_SEL: PeripheralSelect = PeripheralSelect::Spi2;
#[inline(always)]
fn ptr() -> *const SpiRegBlock {
Self::ptr()
}
@ -472,6 +475,7 @@ where
self.cfg_hw_cs(HwCs::CS_ID);
}
#[inline]
pub fn cfg_hw_cs_disable(&mut self) {
self.spi.ctrl1().modify(|_, w| {
w.sod().set_bit();
@ -571,99 +575,6 @@ where
}
}
/*
macro_rules! spi_ctor {
($spiI:ident, $PeriphSel: path) => {
/// Create a new SPI struct
///
/// You can delete the pin type information by calling the
/// [`downgrade`](Self::downgrade) function
///
/// ## Arguments
/// * `spi` - SPI bus to use
/// * `pins` - Pins to be used for SPI transactions. These pins are consumed
/// to ensure the pins can not be used for other purposes anymore
/// * `spi_cfg` - Configuration specific to the SPI bus
/// * `transfer_cfg` - Optional initial transfer configuration which includes
/// configuration which can change across individual SPI transfers like SPI mode
/// or SPI clock. If only one device is connected, this configuration only needs
/// to be done once.
/// * `syscfg` - Can be passed optionally to enable the peripheral clock
pub fn $spiI(
spi: SpiI,
pins: (Sck, Miso, Mosi),
clocks: &crate::clock::Clocks,
spi_cfg: SpiConfig,
syscfg: &mut pac::Sysconfig,
transfer_cfg: Option<&ErasedTransferConfig>,
) -> Self {
crate::clock::enable_peripheral_clock(syscfg, $PeriphSel);
let SpiConfig {
ser_clock_rate_div,
ms,
slave_output_disable,
loopback_mode,
master_delayer_capture,
} = spi_cfg;
let mut mode = embedded_hal::spi::MODE_0;
let mut clk_prescale = 0x02;
let mut ss = 0;
let mut init_blockmode = false;
let apb1_clk = clocks.apb1();
if let Some(transfer_cfg) = transfer_cfg {
mode = transfer_cfg.mode;
clk_prescale =
apb1_clk.raw() / (transfer_cfg.spi_clk.raw() * (ser_clock_rate_div as u32 + 1));
if transfer_cfg.hw_cs != HwChipSelectId::Invalid {
ss = transfer_cfg.hw_cs as u8;
}
init_blockmode = transfer_cfg.blockmode;
}
let (cpo_bit, cph_bit) = mode_to_cpo_cph_bit(mode);
spi.ctrl0().write(|w| {
unsafe {
w.size().bits(Word::word_reg());
w.scrdv().bits(ser_clock_rate_div);
// Clear clock phase and polarity. Will be set to correct value for each
// transfer
w.spo().bit(cpo_bit);
w.sph().bit(cph_bit)
}
});
spi.ctrl1().write(|w| {
w.lbm().bit(loopback_mode);
w.sod().bit(slave_output_disable);
w.ms().bit(ms);
w.mdlycap().bit(master_delayer_capture);
w.blockmode().bit(init_blockmode);
unsafe { w.ss().bits(ss) }
});
spi.fifo_clr().write(|w| {
w.rxfifo().set_bit();
w.txfifo().set_bit()
});
spi.clkprescale().write(|w| unsafe { w.bits(clk_prescale) });
// Enable the peripheral as the last step as recommended in the
// programmers guide
spi.ctrl1().modify(|_, w| w.enable().set_bit());
Spi {
inner: SpiBase {
spi,
cfg: spi_cfg,
apb1_clk,
fill_word: Default::default(),
blockmode: init_blockmode,
word: PhantomData,
},
pins,
}
}
};
}
*/
impl<
SpiI: Instance,
Sck: PinSck<SpiI>,
@ -698,6 +609,8 @@ where
transfer_cfg: Option<&ErasedTransferConfig>,
) -> Self {
crate::clock::enable_peripheral_clock(syscfg, SpiI::PERIPH_SEL);
// This is done in the C HAL.
syscfg.assert_periph_reset_for_two_cycles(SpiI::PERIPH_SEL);
let SpiConfig {
ser_clock_rate_div,
ms,

View File

@ -513,6 +513,7 @@ impl<Tim: ValidTim> CountdownTimer<Tim> {
/// Listen for events. Depending on the IRQ configuration, this also activates the IRQ in the
/// IRQSEL peripheral for the provided interrupt and unmasks the interrupt
#[inline]
pub fn listen(&mut self) {
self.listening = true;
self.enable_interrupt();
@ -532,10 +533,12 @@ impl<Tim: ValidTim> CountdownTimer<Tim> {
}
}
#[inline]
pub fn stop(&mut self) {
self.tim.reg().ctrl().write(|w| w.enable().clear_bit());
}
#[inline]
pub fn unlisten(&mut self) {
self.listening = true;
self.disable_interrupt();
@ -552,6 +555,7 @@ impl<Tim: ValidTim> CountdownTimer<Tim> {
self.tim.reg().ctrl().modify(|_, w| w.irq_enb().clear_bit());
}
#[inline]
pub fn release(self, syscfg: &mut pac::Sysconfig) -> Tim {
self.tim.reg().ctrl().write(|w| w.enable().clear_bit());
syscfg

View File

@ -9,7 +9,7 @@ use core::ops::Deref;
use embedded_hal_nb::serial::Read;
use fugit::RateExtU32;
use crate::clock::{Clocks, PeripheralSelect};
use crate::clock::{Clocks, PeripheralSelect, SyscfgExt};
use crate::gpio::{AltFunc1, Pin, PD11, PD12, PE2, PE3, PF11, PF12, PF8, PF9, PG0, PG1};
use crate::time::Hertz;
use crate::{disable_interrupt, enable_interrupt};
@ -520,6 +520,8 @@ impl<UartInstance: Instance, Pins> Uart<UartInstance, Pins> {
clocks: &Clocks,
) -> Self {
crate::clock::enable_peripheral_clock(syscfg, UartInstance::PERIPH_SEL);
// This is done in the C HAL.
syscfg.assert_periph_reset_for_two_cycles(UartInstance::PERIPH_SEL);
Uart {
inner: UartBase {
uart,

View File

@ -51,12 +51,7 @@ impl WdtController {
wdt_freq_ms: u32,
) -> Self {
syscfg.enable_peripheral_clock(PeripheralSelect::Watchdog);
// It's done like that in Vorago examples. Not exactly sure why the reset is necessary
// though..
syscfg.assert_periph_reset(PeripheralSelect::Watchdog);
cortex_m::asm::nop();
cortex_m::asm::nop();
syscfg.deassert_periph_reset(PeripheralSelect::Watchdog);
syscfg.assert_periph_reset_for_two_cycles(PeripheralSelect::Watchdog);
let wdt_clock = clocks.apb2();
let mut wdt_ctrl = Self {

View File

@ -4,8 +4,8 @@ version = "0.2.0"
authors = ["Robin Mueller <muellerr@irs.uni-stuttgart.de>"]
edition = "2021"
description = "PAC for the Vorago VA416xx family of MCUs"
homepage = "https://egit.irs.uni-stuttgart.de/rust/va416xx"
repository = "https://egit.irs.uni-stuttgart.de/rust/va416xx"
homepage = "https://egit.irs.uni-stuttgart.de/rust/va416xx-rs"
repository = "https://egit.irs.uni-stuttgart.de/rust/va416xx-rs"
license = "Apache-2.0"
keywords = ["no-std", "arm", "cortex-m", "vorago", "va416xx"]
categories = ["embedded", "no-std", "hardware-support"]

View File

@ -1,39 +0,0 @@
[target.'cfg(all(target_arch = "arm", target_os = "none"))']
# uncomment ONE of these three option to make `cargo run` start a GDB session
# which option to pick depends on your system
# If the RevA board is used, replace jlink.gdb with jlink-reva.gdb
# runner = "arm-none-eabi-gdb -q -x jlink/jlink.gdb"
# runner = "gdb-multiarch -q -x jlink/jlink.gdb"
# runner = "arm-none-eabi-gdb -q -x jlink/jlink-reva.gdb"
# runner = "gdb-multiarch -q -x jlink/jlink-reva.gdb"
rustflags = [
# This is needed if your flash or ram addresses are not aligned to 0x10000 in memory.x
# See https://github.com/rust-embedded/cortex-m-quickstart/pull/95
"-C", "link-arg=--nmagic",
# LLD (shipped with the Rust toolchain) is used as the default linker
"-C", "link-arg=-Tlink.x",
# if you run into problems with LLD switch to the GNU linker by commenting out
# this line
# "-C", "linker=arm-none-eabi-ld",
# if you need to link to pre-compiled C libraries provided by a C toolchain
# use GCC as the linker by commenting out both lines above and then
# uncommenting the three lines below
# "-C", "linker=arm-none-eabi-gcc",
# "-C", "link-arg=-Wl,-Tlink.x",
# "-C", "link-arg=-nostartfiles",
]
[build]
# Pick ONE of these compilation targets
# target = "thumbv6m-none-eabi" # Cortex-M0 and Cortex-M0+
# target = "thumbv7m-none-eabi" # Cortex-M3
# target = "thumbv7em-none-eabi" # Cortex-M4 and Cortex-M7 (no FPU)
target = "thumbv7em-none-eabihf" # Cortex-M4F and Cortex-M7F (with FPU)
# target = "thumbv8m.base-none-eabi" # Cortex-M23
# target = "thumbv8m.main-none-eabi" # Cortex-M33 (no FPU)
# target = "thumbv8m.main-none-eabihf" # Cortex-M33 (with FPU)

View File

@ -4,6 +4,56 @@
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"preLaunchTask": "blinky-example",
"type": "probe-rs-debug",
"request": "launch",
"name": "probe-rs Debug Blinky",
"flashingConfig": {
"flashingEnabled": true,
"haltAfterReset": true
},
"chip": "VA416xx",
"coreConfigs": [
{
"programBinary": "${workspaceFolder}/target/thumbv7em-none-eabihf/debug/examples/blinky",
"rttEnabled": true,
"svdFile": "${workspaceFolder}/va416xx/svd/va416xx.svd.patched"
}
]
},
{
"preLaunchTask": "rtt-log-example",
"type": "probe-rs-debug",
"request": "launch",
"name": "probe-rs Debug RTT",
"flashingConfig": {
"flashingEnabled": true,
"haltAfterReset": false
},
"chip": "VA416xx",
"coreConfigs": [
{
"programBinary": "${workspaceFolder}/target/thumbv7em-none-eabihf/debug/examples/rtt-log",
"rttEnabled": true,
"svdFile": "${workspaceFolder}/va416xx/svd/va416xx.svd.patched"
}
]
},
{
"preLaunchTask": "rtt-log-example",
"type": "probe-rs-debug",
"request": "attach",
"name": "probe-rs Attach RTT",
"chip": "VA416xx",
"coreConfigs": [
{
"programBinary": "${workspaceFolder}/target/thumbv7em-none-eabihf/debug/examples/rtt-log",
"rttEnabled": true,
"svdFile": "${workspaceFolder}/va416xx/svd/va416xx.svd"
}
]
},
{
"type": "cortex-debug",
"request": "launch",
@ -19,7 +69,7 @@
"monitor reset",
"load",
],
"executable": "${workspaceFolder}/target/thumbv7em-none-eabihf/debug/examples/blinky-hal",
"executable": "${workspaceFolder}/target/thumbv7em-none-eabihf/debug/examples/blinky",
"interface": "swd",
"runToEntryPoint": "main",
"rttConfig": {
@ -185,5 +235,67 @@
]
}
},
{
"type": "cortex-debug",
"request": "launch",
"name": "Debug DAC/ADC Example",
"servertype": "jlink",
"jlinkscript": "${workspaceFolder}/jlink/JLinkSettings.JLinkScript",
"cwd": "${workspaceRoot}",
"device": "Cortex-M4",
"svdFile": "${workspaceFolder}/va416xx/svd/va416xx.svd.patched",
"preLaunchTask": "dac-adc-example",
"overrideLaunchCommands": [
"monitor halt",
"monitor reset",
"load",
],
"executable": "${workspaceFolder}/target/thumbv7em-none-eabihf/debug/examples/dac-adc",
"interface": "swd",
"runToEntryPoint": "main",
"rttConfig": {
"enabled": true,
// Have to use exact address unfortunately. "auto" does not work for some reason..
"address": "0x1fff8000",
"decoders": [
{
"port": 0,
"timestamp": true,
"type": "console"
}
]
}
},
{
"type": "cortex-debug",
"request": "launch",
"name": "Debug ADC Example",
"servertype": "jlink",
"jlinkscript": "${workspaceFolder}/jlink/JLinkSettings.JLinkScript",
"cwd": "${workspaceRoot}",
"device": "Cortex-M4",
"svdFile": "${workspaceFolder}/va416xx/svd/va416xx.svd.patched",
"preLaunchTask": "adc-example",
"overrideLaunchCommands": [
"monitor halt",
"monitor reset",
"load",
],
"executable": "${workspaceFolder}/target/thumbv7em-none-eabihf/debug/examples/adc",
"interface": "swd",
"runToEntryPoint": "main",
"rttConfig": {
"enabled": true,
// Have to use exact address unfortunately. "auto" does not work for some reason..
"address": "0x1fff8000",
"decoders": [
{
"port": 0,
"timestamp": true,
"type": "console"
}
]
}
},
]
}

View File

@ -36,7 +36,7 @@
"args": [
"build",
"--example",
"blinky-hal"
"blinky"
],
"group": {
"kind": "build",
@ -82,5 +82,31 @@
"kind": "build",
}
},
{
"label": "dac-adc-example",
"type": "shell",
"command": "~/.cargo/bin/cargo", // note: full path to the cargo
"args": [
"build",
"--example",
"dac-adc"
],
"group": {
"kind": "build",
}
},
{
"label": "adc-example",
"type": "shell",
"command": "~/.cargo/bin/cargo", // note: full path to the cargo
"args": [
"build",
"--example",
"adc"
],
"group": {
"kind": "build",
}
},
]
}