Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| fa30e233c7 |
@@ -1,7 +0,0 @@
|
|||||||
[package]
|
|
||||||
name = "uart-clock-calc"
|
|
||||||
version = "0.1.0"
|
|
||||||
edition = "2024"
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
arbitrary-int = "2"
|
|
||||||
@@ -1,50 +0,0 @@
|
|||||||
use arbitrary_int::{u6, u18};
|
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone)]
|
|
||||||
pub struct ClockConfig {
|
|
||||||
pub frac: u6,
|
|
||||||
pub int: u18,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone)]
|
|
||||||
pub enum BaudMultiplier {
|
|
||||||
_8 = 8,
|
|
||||||
_16 = 16,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn uart_clock_calc(ref_clk: u32, baudrate: u32, baud_mult: BaudMultiplier) -> ClockConfig {
|
|
||||||
// This is the calculation: (64.0 * (x - integer_part as f32) + 0.5) as u32 without floating
|
|
||||||
// point calculations.
|
|
||||||
let multiplier = baud_mult as u32;
|
|
||||||
let frac = ((ref_clk % (baudrate * multiplier)) * 64 + (baudrate * (multiplier / 2)))
|
|
||||||
/ (baudrate * multiplier);
|
|
||||||
// Calculations here are derived from chapter 4.8.5 (p.79) of the datasheet.
|
|
||||||
let integer_part = ref_clk / (baudrate * multiplier);
|
|
||||||
ClockConfig {
|
|
||||||
frac: u6::new(frac as u8),
|
|
||||||
int: u18::new(integer_part),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const SYS_CLK_50_MHZ: u32 = 50_000_000;
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
println!("UART Clock Configuration App");
|
|
||||||
let clock_config = uart_clock_calc(SYS_CLK_50_MHZ, 38400, BaudMultiplier::_16);
|
|
||||||
println!(
|
|
||||||
"For a reference clock of {} Hz and baud rate of {} bps with multiplier {}, the clock configuration is: {:?}",
|
|
||||||
SYS_CLK_50_MHZ,
|
|
||||||
38400,
|
|
||||||
BaudMultiplier::_16 as u32,
|
|
||||||
clock_config
|
|
||||||
);
|
|
||||||
let clock_config = uart_clock_calc(SYS_CLK_50_MHZ, 38400, BaudMultiplier::_8);
|
|
||||||
println!(
|
|
||||||
"For a reference clock of {} Hz and baud rate of {} bps with multiplier {}, the clock configuration is: {:?}",
|
|
||||||
SYS_CLK_50_MHZ,
|
|
||||||
38400,
|
|
||||||
BaudMultiplier::_8 as u32,
|
|
||||||
clock_config
|
|
||||||
);
|
|
||||||
()
|
|
||||||
}
|
|
||||||
+3
-10
@@ -90,22 +90,15 @@ work yet.
|
|||||||
After installation, you can run the following command
|
After installation, you can run the following command
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
probe-rs run --chip VA108xx_RAM --protocol jtag target/thumbv6m-none-eabi/debug/blinky
|
probe-rs run --chip VA108xx_RAM --protocol jtag target/thumbv6m-none-eabi/debug/examples/blinky
|
||||||
```
|
```
|
||||||
|
|
||||||
to flash and run the blinky program on the RAM. There is also a `VA108xx` chip target
|
to flash and run the blinky program on the RAM. There is also a `VA108xx` chip target
|
||||||
available for persistent flashing (see note below!).
|
available for persistent flashing.
|
||||||
|
|
||||||
Runner configuration is available in the `.cargo/config.toml.template` file to use `probe-rs` for
|
Runner configuration is available in the `.cargo/def-config.toml` file to use `probe-rs` for
|
||||||
convenience. `probe-rs` is also able to process and display `defmt` strings directly.
|
convenience. `probe-rs` is also able to process and display `defmt` strings directly.
|
||||||
|
|
||||||
Special note on the `VA108xx` target: This target allows flashing the NVM, but doing a soft reset
|
|
||||||
with a tool like `probe-rs` can only perform a soft reset where the code already running in RAM
|
|
||||||
is reset. If you want to immediately run the code flashed to the NVM and get `defmt` printouts,
|
|
||||||
use `probe-rs download` to flash to NVM, then flash a binary which issues the system reset
|
|
||||||
(e.g. the `reset` app inside the example folder), and then attach with `probe-rs attach`, passing
|
|
||||||
the image downloaded to NVM to the attach command.
|
|
||||||
|
|
||||||
### Using VS Code
|
### Using VS Code
|
||||||
|
|
||||||
Assuming a working debug connection to your VA108xx board, you can debug using VS Code with
|
Assuming a working debug connection to your VA108xx board, you can debug using VS Code with
|
||||||
|
|||||||
@@ -181,8 +181,7 @@ fn check_own_crc(
|
|||||||
// because the address of the bootloader is 0x0, so the NULL check fails and the functions
|
// because the address of the bootloader is 0x0, so the NULL check fails and the functions
|
||||||
// panics.
|
// panics.
|
||||||
#[allow(clippy::zero_ptr)]
|
#[allow(clippy::zero_ptr)]
|
||||||
let first_four_bytes =
|
let first_four_bytes = unsafe { core::ptr::read_volatile(0x0 as *const u32) }.to_ne_bytes();
|
||||||
unsafe { core::ptr::read_volatile(BOOTLOADER_START_ADDR as *const u32) }.to_ne_bytes();
|
|
||||||
let mut digest = CRC_ALGO.digest();
|
let mut digest = CRC_ALGO.digest();
|
||||||
digest.update(&first_four_bytes);
|
digest.update(&first_four_bytes);
|
||||||
digest.update(unsafe {
|
digest.update(unsafe {
|
||||||
@@ -271,10 +270,14 @@ fn boot_app(
|
|||||||
APP_B_START_ADDR
|
APP_B_START_ADDR
|
||||||
};
|
};
|
||||||
unsafe {
|
unsafe {
|
||||||
let first_four_bytes = core::ptr::read_volatile(base_addr as *const u32);
|
// First 4 bytes done with inline assembly, writing to the physical address 0x0 can not
|
||||||
#[allow(clippy::zero_ptr)]
|
// be done without it. See https://users.rust-lang.org/t/reading-from-physical-address-0x0/117408/2.
|
||||||
core::ptr::write_volatile(BOOTLOADER_START_ADDR as *mut u32, first_four_bytes);
|
let first_four_bytes = core::ptr::read(base_addr as *const u32);
|
||||||
|
core::arch::asm!(
|
||||||
|
"str {0}, [{1}]",
|
||||||
|
in(reg) first_four_bytes, // Input: App vector table.
|
||||||
|
in(reg) BOOTLOADER_START_ADDR as *mut u32, // Input: destination pointer
|
||||||
|
);
|
||||||
core::slice::from_raw_parts_mut(
|
core::slice::from_raw_parts_mut(
|
||||||
(BOOTLOADER_START_ADDR + 4) as *mut u8,
|
(BOOTLOADER_START_ADDR + 4) as *mut u8,
|
||||||
(VECTOR_TABLE_LEN - 4) as usize,
|
(VECTOR_TABLE_LEN - 4) as usize,
|
||||||
|
|||||||
@@ -4,9 +4,8 @@ version = "0.1.0"
|
|||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
cortex-m = { version = "0.7", features = ["critical-section-single-core"] }
|
|
||||||
cortex-m-rt = "0.7"
|
|
||||||
cfg-if = "1"
|
cfg-if = "1"
|
||||||
|
cortex-m-rt = "0.7"
|
||||||
embedded-hal-async = "1"
|
embedded-hal-async = "1"
|
||||||
embedded-io = "0.7"
|
embedded-io = "0.7"
|
||||||
embedded-io-async = "0.7"
|
embedded-io-async = "0.7"
|
||||||
@@ -19,10 +18,10 @@ panic-probe = { version = "1", features = ["print-defmt"] }
|
|||||||
|
|
||||||
critical-section = "1"
|
critical-section = "1"
|
||||||
|
|
||||||
embassy-sync = "0.8"
|
embassy-sync = "0.7"
|
||||||
embassy-time = "0.5"
|
embassy-time = "0.5"
|
||||||
embassy-executor = { version = "0.10", features = [
|
embassy-executor = { version = "0.9", features = [
|
||||||
"platform-cortex-m",
|
"arch-cortex-m",
|
||||||
"executor-thread",
|
"executor-thread",
|
||||||
"executor-interrupt"
|
"executor-interrupt"
|
||||||
]}
|
]}
|
||||||
|
|||||||
@@ -62,9 +62,6 @@ async fn main(spawner: Spawner) {
|
|||||||
|
|
||||||
// Safety: Only called once here.
|
// Safety: Only called once here.
|
||||||
va108xx_embassy::init(dp.tim23, dp.tim22, SYSCLK_FREQ);
|
va108xx_embassy::init(dp.tim23, dp.tim22, SYSCLK_FREQ);
|
||||||
unsafe {
|
|
||||||
cortex_m::interrupt::enable();
|
|
||||||
}
|
|
||||||
|
|
||||||
let porta = PinsA::new(dp.porta);
|
let porta = PinsA::new(dp.porta);
|
||||||
let portb = PinsB::new(dp.portb);
|
let portb = PinsB::new(dp.portb);
|
||||||
@@ -74,35 +71,36 @@ async fn main(spawner: Spawner) {
|
|||||||
let out_pb22 = Output::new(portb.pb22, PinState::Low);
|
let out_pb22 = Output::new(portb.pb22, PinState::Low);
|
||||||
let in_pb23 = Input::new_floating(portb.pb23);
|
let in_pb23 = Input::new_floating(portb.pb23);
|
||||||
|
|
||||||
let mut in_pa1_async = InputPinAsync::new(
|
let mut in_pa1_async = InputPinAsync::new(in_pa1, pac::Interrupt::OC10);
|
||||||
in_pa1,
|
let mut in_pb23_async = InputPinAsync::new(in_pb23, PB22_TO_PB23_IRQ);
|
||||||
va108xx_hal::InterruptConfig::new(pac::Interrupt::OC10, true, true),
|
|
||||||
);
|
|
||||||
let mut in_pb23_async = InputPinAsync::new(
|
|
||||||
in_pb23,
|
|
||||||
va108xx_hal::InterruptConfig::new(PB22_TO_PB23_IRQ, true, true),
|
|
||||||
);
|
|
||||||
|
|
||||||
spawner.spawn(output_task("PA0 to PA1", out_pa0, CHANNEL_PA0_PA1.receiver()).unwrap());
|
spawner
|
||||||
spawner.spawn(output_task("PB22 to PB23", out_pb22, CHANNEL_PB22_TO_PB23.receiver()).unwrap());
|
.spawn(output_task(
|
||||||
|
"PA0 to PA1",
|
||||||
|
out_pa0,
|
||||||
|
CHANNEL_PA0_PA1.receiver(),
|
||||||
|
))
|
||||||
|
.unwrap();
|
||||||
|
spawner
|
||||||
|
.spawn(output_task(
|
||||||
|
"PB22 to PB23",
|
||||||
|
out_pb22,
|
||||||
|
CHANNEL_PB22_TO_PB23.receiver(),
|
||||||
|
))
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
for i in 0..3 {
|
if CHECK_PA0_TO_PA1 {
|
||||||
defmt::info!("Starting async GPIO operations check {}", i);
|
check_pin_to_pin_async_ops("PA0 to PA1", CHANNEL_PA0_PA1.sender(), &mut in_pa1_async).await;
|
||||||
if CHECK_PA0_TO_PA1 {
|
defmt::info!("Example PA0 to PA1 done");
|
||||||
check_pin_to_pin_async_ops("PA0 to PA1", CHANNEL_PA0_PA1.sender(), &mut in_pa1_async)
|
}
|
||||||
.await;
|
if CHECK_PB22_TO_PB23 {
|
||||||
defmt::info!("Example PA0 to PA1 done");
|
check_pin_to_pin_async_ops(
|
||||||
}
|
"PB22 to PB23",
|
||||||
if CHECK_PB22_TO_PB23 {
|
CHANNEL_PB22_TO_PB23.sender(),
|
||||||
check_pin_to_pin_async_ops(
|
&mut in_pb23_async,
|
||||||
"PB22 to PB23",
|
)
|
||||||
CHANNEL_PB22_TO_PB23.sender(),
|
.await;
|
||||||
&mut in_pb23_async,
|
defmt::info!("Example PB22 to PB23 done");
|
||||||
)
|
|
||||||
.await;
|
|
||||||
defmt::info!("Example PB22 to PB23 done");
|
|
||||||
}
|
|
||||||
Timer::after(Duration::from_millis(500)).await;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
defmt::info!("Example done, toggling LED0");
|
defmt::info!("Example done, toggling LED0");
|
||||||
|
|||||||
@@ -65,13 +65,12 @@ async fn main(spawner: Spawner) {
|
|||||||
let tx_uart_a = porta.pa9;
|
let tx_uart_a = porta.pa9;
|
||||||
let rx_uart_a = porta.pa8;
|
let rx_uart_a = porta.pa8;
|
||||||
|
|
||||||
let clock_config = uart::ClockConfig::calculate(50.MHz(), 115200.Hz(), uart::BaudMode::_16);
|
|
||||||
let uart_config = uart::Config::new_with_clock_config(clock_config);
|
|
||||||
let uarta = uart::Uart::new_with_interrupt_uart0(
|
let uarta = uart::Uart::new_with_interrupt_uart0(
|
||||||
dp.uarta,
|
dp.uarta,
|
||||||
tx_uart_a,
|
tx_uart_a,
|
||||||
rx_uart_a,
|
rx_uart_a,
|
||||||
uart_config,
|
50.MHz(),
|
||||||
|
115200.Hz().into(),
|
||||||
InterruptConfig::new(pac::Interrupt::OC2, true, true),
|
InterruptConfig::new(pac::Interrupt::OC2, true, true),
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -82,7 +81,8 @@ async fn main(spawner: Spawner) {
|
|||||||
dp.uartb,
|
dp.uartb,
|
||||||
tx_uart_b,
|
tx_uart_b,
|
||||||
rx_uart_b,
|
rx_uart_b,
|
||||||
uart_config,
|
50.MHz(),
|
||||||
|
115200.Hz().into(),
|
||||||
InterruptConfig::new(pac::Interrupt::OC3, true, true),
|
InterruptConfig::new(pac::Interrupt::OC3, true, true),
|
||||||
);
|
);
|
||||||
let (mut tx_uart_a, rx_uart_a) = uarta.split();
|
let (mut tx_uart_a, rx_uart_a) = uarta.split();
|
||||||
@@ -97,7 +97,9 @@ async fn main(spawner: Spawner) {
|
|||||||
});
|
});
|
||||||
let mut async_rx_uart_a = RxAsync::new(rx_uart_a, cons_uart_a);
|
let mut async_rx_uart_a = RxAsync::new(rx_uart_a, cons_uart_a);
|
||||||
let async_rx_uart_b = RxAsyncOverwriting::new(rx_uart_b, &CONSUMER_UART_B);
|
let async_rx_uart_b = RxAsyncOverwriting::new(rx_uart_b, &CONSUMER_UART_B);
|
||||||
spawner.spawn(uart_b_task(async_rx_uart_b, tx_uart_b).unwrap());
|
spawner
|
||||||
|
.spawn(uart_b_task(async_rx_uart_b, tx_uart_b))
|
||||||
|
.unwrap();
|
||||||
let mut buf = [0u8; 256];
|
let mut buf = [0u8; 256];
|
||||||
loop {
|
loop {
|
||||||
defmt::info!("Current time UART A: {}", Instant::now().as_secs());
|
defmt::info!("Current time UART A: {}", Instant::now().as_secs());
|
||||||
|
|||||||
@@ -52,13 +52,12 @@ async fn main(_spawner: Spawner) {
|
|||||||
let tx = porta.pa9;
|
let tx = porta.pa9;
|
||||||
let rx = porta.pa8;
|
let rx = porta.pa8;
|
||||||
|
|
||||||
let clock_config = uart::ClockConfig::calculate(50.MHz(), 115200.Hz(), uart::BaudMode::_16);
|
|
||||||
let uart_config = uart::Config::new_with_clock_config(clock_config);
|
|
||||||
let uarta = uart::Uart::new_with_interrupt_uart0(
|
let uarta = uart::Uart::new_with_interrupt_uart0(
|
||||||
dp.uarta,
|
dp.uarta,
|
||||||
tx,
|
tx,
|
||||||
rx,
|
rx,
|
||||||
uart_config,
|
50.MHz(),
|
||||||
|
115200.Hz().into(),
|
||||||
InterruptConfig::new(pac::Interrupt::OC2, true, true),
|
InterruptConfig::new(pac::Interrupt::OC2, true, true),
|
||||||
);
|
);
|
||||||
let (tx, _rx) = uarta.split();
|
let (tx, _rx) = uarta.split();
|
||||||
|
|||||||
@@ -53,14 +53,12 @@ mod app {
|
|||||||
let tx = gpioa.pa9;
|
let tx = gpioa.pa9;
|
||||||
let rx = gpioa.pa8;
|
let rx = gpioa.pa8;
|
||||||
|
|
||||||
let clock_config =
|
|
||||||
uart::ClockConfig::calculate(SYSCLK_FREQ, 115200.Hz(), uart::BaudMode::_16);
|
|
||||||
let uart_config = uart::Config::new_with_clock_config(clock_config);
|
|
||||||
let irq_uart = uart::Uart::new_with_interrupt_uart0(
|
let irq_uart = uart::Uart::new_with_interrupt_uart0(
|
||||||
dp.uarta,
|
dp.uarta,
|
||||||
tx,
|
tx,
|
||||||
rx,
|
rx,
|
||||||
uart_config,
|
SYSCLK_FREQ,
|
||||||
|
115200.Hz().into(),
|
||||||
InterruptConfig::new(pac::Interrupt::OC3, true, true),
|
InterruptConfig::new(pac::Interrupt::OC3, true, true),
|
||||||
);
|
);
|
||||||
let (tx, rx) = irq_uart.split();
|
let (tx, rx) = irq_uart.split();
|
||||||
|
|||||||
@@ -87,7 +87,7 @@ fn main() -> ! {
|
|||||||
bmstall: true,
|
bmstall: true,
|
||||||
hw_cs: None,
|
hw_cs: None,
|
||||||
};
|
};
|
||||||
spi.configure_transfer(&transfer_cfg);
|
spi.cfg_transfer(&transfer_cfg);
|
||||||
}
|
}
|
||||||
SpiBusSelect::SpiBPortB => {
|
SpiBusSelect::SpiBPortB => {
|
||||||
let hw_cs_pin = configure_pin_as_hw_cs_pin(pinsb.pb2);
|
let hw_cs_pin = configure_pin_as_hw_cs_pin(pinsb.pb2);
|
||||||
@@ -99,7 +99,7 @@ fn main() -> ! {
|
|||||||
bmstall: true,
|
bmstall: true,
|
||||||
hw_cs: Some(hw_cs_pin),
|
hw_cs: Some(hw_cs_pin),
|
||||||
};
|
};
|
||||||
spi.configure_transfer(&transfer_cfg);
|
spi.cfg_transfer(&transfer_cfg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -28,9 +28,8 @@ fn main() -> ! {
|
|||||||
let gpioa = PinsA::new(dp.porta);
|
let gpioa = PinsA::new(dp.porta);
|
||||||
let tx = gpioa.pa9;
|
let tx = gpioa.pa9;
|
||||||
let rx = gpioa.pa8;
|
let rx = gpioa.pa8;
|
||||||
let clock_config = uart::ClockConfig::calculate(50.MHz(), 115200.Hz(), uart::BaudMode::_16);
|
let uart =
|
||||||
let uart_config = uart::Config::new_with_clock_config(clock_config);
|
uart::Uart::new_without_interrupt_uart0(dp.uarta, tx, rx, 50.MHz(), 115200.Hz().into());
|
||||||
let uart = uart::Uart::new_without_interrupt_uart0(dp.uarta, tx, rx, uart_config);
|
|
||||||
|
|
||||||
let (mut tx, mut rx) = uart.split();
|
let (mut tx, mut rx) = uart.split();
|
||||||
writeln!(tx, "Hello World\r").unwrap();
|
writeln!(tx, "Hello World\r").unwrap();
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
//! Dummy app which does not do anything.
|
|
||||||
#![no_main]
|
|
||||||
#![no_std]
|
|
||||||
|
|
||||||
use cortex_m_rt::entry;
|
|
||||||
use panic_halt as _;
|
|
||||||
use va108xx_hal as _;
|
|
||||||
|
|
||||||
#[entry]
|
|
||||||
fn main() -> ! {
|
|
||||||
cortex_m::peripheral::SCB::sys_reset();
|
|
||||||
}
|
|
||||||
@@ -116,14 +116,12 @@ mod app {
|
|||||||
let tx = gpioa.pa9;
|
let tx = gpioa.pa9;
|
||||||
let rx = gpioa.pa8;
|
let rx = gpioa.pa8;
|
||||||
|
|
||||||
let clock_config =
|
|
||||||
uart::ClockConfig::calculate(SYSCLK_FREQ, UART_BAUDRATE.Hz(), uart::BaudMode::_16);
|
|
||||||
let uart_config = uart::Config::new_with_clock_config(clock_config);
|
|
||||||
let irq_uart = uart::Uart::new_with_interrupt_uart0(
|
let irq_uart = uart::Uart::new_with_interrupt_uart0(
|
||||||
dp.uarta,
|
dp.uarta,
|
||||||
tx,
|
tx,
|
||||||
rx,
|
rx,
|
||||||
uart_config,
|
SYSCLK_FREQ,
|
||||||
|
UART_BAUDRATE.Hz().into(),
|
||||||
InterruptConfig::new(pac::Interrupt::OC0, true, true),
|
InterruptConfig::new(pac::Interrupt::OC0, true, true),
|
||||||
);
|
);
|
||||||
let (tx, rx) = irq_uart.split();
|
let (tx, rx) = irq_uart.split();
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ cortex-m = { version = "0.7", features = ["critical-section-single-core"]}
|
|||||||
vorago-shared-hal = { version = "0.2", path = "../../vorago-shared-hal", features = ["vor1x"] }
|
vorago-shared-hal = { version = "0.2", path = "../../vorago-shared-hal", features = ["vor1x"] }
|
||||||
fugit = "0.3"
|
fugit = "0.3"
|
||||||
thiserror = { version = "2", default-features = false }
|
thiserror = { version = "2", default-features = false }
|
||||||
va108xx = { version = "0.6", path = "../va108xx", default-features = false, features = ["critical-section"] }
|
va108xx = { version = "0.6", path = "../va108xx", default-features = false, features = ["critical-section", "defmt"] }
|
||||||
defmt = { version = "1", optional = true }
|
defmt = { version = "1", optional = true }
|
||||||
|
|
||||||
[target.'cfg(all(target_arch = "arm", target_os = "none"))'.dependencies]
|
[target.'cfg(all(target_arch = "arm", target_os = "none"))'.dependencies]
|
||||||
@@ -26,7 +26,7 @@ portable-atomic = "1"
|
|||||||
[features]
|
[features]
|
||||||
default = ["rt"]
|
default = ["rt"]
|
||||||
rt = ["va108xx/rt"]
|
rt = ["va108xx/rt"]
|
||||||
defmt = ["dep:defmt", "vorago-shared-hal/defmt", "va108xx/defmt"]
|
defmt = ["dep:defmt", "vorago-shared-hal/defmt"]
|
||||||
|
|
||||||
[package.metadata.docs.rs]
|
[package.metadata.docs.rs]
|
||||||
all-features = true
|
all-features = true
|
||||||
|
|||||||
@@ -12,4 +12,9 @@
|
|||||||
//!
|
//!
|
||||||
//! The [crate::pins] module exposes singletons to access the [Pin]s required by this module
|
//! The [crate::pins] module exposes singletons to access the [Pin]s required by this module
|
||||||
//! in a type-safe way.
|
//! in a type-safe way.
|
||||||
|
//!
|
||||||
|
//! ## Examples
|
||||||
|
//!
|
||||||
|
//! - [Blinky example](https://egit.irs.uni-stuttgart.de/rust/vorago-rs/src/branch/main/va108xx/examples/simple/examples/blinky.rs)
|
||||||
|
//! - [Async GPIO example](https://egit.irs.uni-stuttgart.de/rust/vorago-rs/src/branch/main/va108xx/examples/embassy/src/bin/async-gpio.rs)
|
||||||
pub use vorago_shared_hal::gpio::*;
|
pub use vorago_shared_hal::gpio::*;
|
||||||
|
|||||||
@@ -1,2 +1,6 @@
|
|||||||
//! API for the I2C peripheral
|
//! API for the I2C peripheral
|
||||||
|
//!
|
||||||
|
//! ## Examples
|
||||||
|
//!
|
||||||
|
//! - [REB1 I2C temperature sensor example](https://egit.irs.uni-stuttgart.de/rust/vorago-rs/src/branch/main/va108xx/vorago-reb1/examples/adt75-temp-sensor.rs)
|
||||||
pub use vorago_shared_hal::i2c::*;
|
pub use vorago_shared_hal::i2c::*;
|
||||||
|
|||||||
@@ -7,9 +7,6 @@
|
|||||||
//! raw PAC. This crate also implements traits specified by the
|
//! raw PAC. This crate also implements traits specified by the
|
||||||
//! [embedded-hal](https://github.com/rust-embedded/embedded-hal) project, making it compatible with
|
//! [embedded-hal](https://github.com/rust-embedded/embedded-hal) project, making it compatible with
|
||||||
//! various drivers in the embedded rust ecosystem.
|
//! various drivers in the embedded rust ecosystem.
|
||||||
//!
|
|
||||||
//! The [examples folder](https://github.com/us-irs/vorago-rs/tree/main/va108xx/examples) contains
|
|
||||||
//! various example applications using the HAL.
|
|
||||||
#![no_std]
|
#![no_std]
|
||||||
#![cfg_attr(docsrs, feature(doc_cfg))]
|
#![cfg_attr(docsrs, feature(doc_cfg))]
|
||||||
|
|
||||||
|
|||||||
@@ -1,2 +1,8 @@
|
|||||||
//! API for Pulse-Width Modulation (PWM)
|
//! API for Pulse-Width Modulation (PWM)
|
||||||
|
//!
|
||||||
|
//! The Vorago VA108xx devices use the TIM peripherals to perform PWM related tasks
|
||||||
|
//!
|
||||||
|
//! ## Examples
|
||||||
|
//!
|
||||||
|
//! - [PWM example](https://egit.irs.uni-stuttgart.de/rust/vorago-rs/src/branch/main/va108xx/examples/simple/examples/pwm.rs)
|
||||||
pub use vorago_shared_hal::pwm::*;
|
pub use vorago_shared_hal::pwm::*;
|
||||||
|
|||||||
@@ -3,4 +3,10 @@
|
|||||||
//! The main abstraction provided by this module is the [Spi] an structure.
|
//! The main abstraction provided by this module is the [Spi] an structure.
|
||||||
//! It provides the [SpiBus trait](https://docs.rs/embedded-hal/latest/embedded_hal/spi/trait.SpiBus.html),
|
//! It provides the [SpiBus trait](https://docs.rs/embedded-hal/latest/embedded_hal/spi/trait.SpiBus.html),
|
||||||
//! but also offer a low level interface via the [SpiLowLevel] trait.
|
//! but also offer a low level interface via the [SpiLowLevel] trait.
|
||||||
|
//!
|
||||||
|
//! ## Examples
|
||||||
|
//!
|
||||||
|
//! - [Blocking SPI example](https://egit.irs.uni-stuttgart.de/rust/vorago-rs/src/branch/main/va108xx/examples/simple/examples/spi.rs)
|
||||||
|
//! - [REB1 ADC example](https://egit.irs.uni-stuttgart.de/rust/vorago-rs/src/branch/main/va108xx/vorago-reb1/examples/max11519-adc.rs)
|
||||||
|
//! - [REB1 EEPROM library](https://egit.irs.uni-stuttgart.de/rust/vorago-rs/src/branch/main/va108xx/vorago-reb1/src/m95m01.rs)
|
||||||
pub use vorago_shared_hal::spi::*;
|
pub use vorago_shared_hal::spi::*;
|
||||||
|
|||||||
@@ -39,6 +39,5 @@ pub fn disable_ram_scrubbing() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub use vorago_shared_hal::sysconfig::{
|
pub use vorago_shared_hal::sysconfig::{
|
||||||
assert_peripheral_reset, deassert_peripheral_reset, disable_peripheral_clock,
|
assert_peripheral_reset, disable_peripheral_clock, enable_peripheral_clock,
|
||||||
enable_peripheral_clock, reset_peripheral_for_cycles,
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,2 +1,7 @@
|
|||||||
//! API for the TIM peripherals
|
//! API for the TIM peripherals
|
||||||
|
//!
|
||||||
|
//! ## Examples
|
||||||
|
//!
|
||||||
|
//! - [MS and second tick implementation](https://egit.irs.uni-stuttgart.de/rust/vorago-rs/src/branch/main/va108xx/examples/simple/examples/timer-ticks.rs)
|
||||||
|
//! - [Cascade feature example](https://egit.irs.uni-stuttgart.de/rust/vorago-rs/src/branch/main/va108xx/examples/simple/examples/cascade.rs)
|
||||||
pub use vorago_shared_hal::timer::*;
|
pub use vorago_shared_hal::timer::*;
|
||||||
|
|||||||
@@ -6,4 +6,12 @@
|
|||||||
//!
|
//!
|
||||||
//! The [rx_async] and [tx_async] modules provide an asynchronous non-blocking API for the UART
|
//! The [rx_async] and [tx_async] modules provide an asynchronous non-blocking API for the UART
|
||||||
//! peripheral.
|
//! peripheral.
|
||||||
|
//!
|
||||||
|
//! ## Examples
|
||||||
|
//!
|
||||||
|
//! - [UART simple example](https://egit.irs.uni-stuttgart.de/rust/vorago-rs/src/branch/main/va108xx/examples/simple/examples/uart.rs)
|
||||||
|
//! - [UART with IRQ and RTIC](https://egit.irs.uni-stuttgart.de/rust/vorago-rs/src/branch/main/va108xx/examples/rtic/src/bin/uart-echo-rtic.rs)
|
||||||
|
//! - [Flashloader exposing a CCSDS interface via UART](https://egit.irs.uni-stuttgart.de/rust/vorago-rs/src/branch/main/va108xx/flashloader)
|
||||||
|
//! - [Async UART RX example](https://egit.irs.uni-stuttgart.de/rust/vorago-rs/src/branch/main/va108xx/examples/embassy/src/bin/async-uart-rx.rs)
|
||||||
|
//! - [Async UART TX example](https://egit.irs.uni-stuttgart.de/rust/vorago-rs/src/branch/main/va108xx/examples/embassy/src/bin/async-uart-tx.rs)
|
||||||
pub use vorago_shared_hal::uart::*;
|
pub use vorago_shared_hal::uart::*;
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ fn main() -> ! {
|
|||||||
.mode(MODE_3)
|
.mode(MODE_3)
|
||||||
.slave_output_disable(true);
|
.slave_output_disable(true);
|
||||||
let mut spi = Spi::new_for_spi1(dp.spib, (sck, miso, mosi), spi_cfg);
|
let mut spi = Spi::new_for_spi1(dp.spib, (sck, miso, mosi), spi_cfg);
|
||||||
spi.configure_hw_cs(hw_cs_id);
|
spi.cfg_hw_cs(hw_cs_id);
|
||||||
|
|
||||||
let mut tx_rx_buf: [u8; 3] = [0; 3];
|
let mut tx_rx_buf: [u8; 3] = [0; 3];
|
||||||
tx_rx_buf[0] = READ_MASK | DEVID_REG;
|
tx_rx_buf[0] = READ_MASK | DEVID_REG;
|
||||||
|
|||||||
@@ -83,7 +83,7 @@ impl<Delay: DelayNs> SpiDevice for SpiWithHwCs<Delay> {
|
|||||||
) -> Result<(), Self::Error> {
|
) -> Result<(), Self::Error> {
|
||||||
// Only the HW CS is configured here. This is not really necessary, but showcases
|
// Only the HW CS is configured here. This is not really necessary, but showcases
|
||||||
// that we could scale this multiple SPI devices.
|
// that we could scale this multiple SPI devices.
|
||||||
self.inner.configure_hw_cs(self.hw_cs_id);
|
self.inner.cfg_hw_cs(self.hw_cs_id);
|
||||||
for operation in operations {
|
for operation in operations {
|
||||||
match operation {
|
match operation {
|
||||||
spi::Operation::Read(buf) => self.inner.read(buf),
|
spi::Operation::Read(buf) => self.inner.read(buf),
|
||||||
@@ -93,7 +93,7 @@ impl<Delay: DelayNs> SpiDevice for SpiWithHwCs<Delay> {
|
|||||||
spi::Operation::DelayNs(delay) => self.delay_provider.delay_ns(*delay),
|
spi::Operation::DelayNs(delay) => self.delay_provider.delay_ns(*delay),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
self.inner.disable_hw_cs();
|
self.inner.cfg_hw_cs_disable();
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ impl Button {
|
|||||||
irq_cfg: InterruptConfig,
|
irq_cfg: InterruptConfig,
|
||||||
) {
|
) {
|
||||||
self.0.configure_edge_interrupt(edge_type);
|
self.0.configure_edge_interrupt(edge_type);
|
||||||
self.0.enable_interrupt(irq_cfg, true);
|
self.0.enable_interrupt(irq_cfg);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Configures an IRQ on level.
|
/// Configures an IRQ on level.
|
||||||
@@ -46,7 +46,7 @@ impl Button {
|
|||||||
irq_cfg: InterruptConfig,
|
irq_cfg: InterruptConfig,
|
||||||
) {
|
) {
|
||||||
self.0.configure_level_interrupt(level);
|
self.0.configure_level_interrupt(level);
|
||||||
self.0.enable_interrupt(irq_cfg, true);
|
self.0.enable_interrupt(irq_cfg);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Configures a filter on the button. This can be useful for debouncing the switch.
|
/// Configures a filter on the button. This can be useful for debouncing the switch.
|
||||||
|
|||||||
+1
-1
@@ -88,7 +88,7 @@ work yet.
|
|||||||
After installation, you can run the following command
|
After installation, you can run the following command
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
probe-rs run --chip VA416xx_RAM --protocol jtag target/thumbv7em-none-eabihf/debug/blinky
|
probe-rs run --chip VA416xx_RAM --protocol jtag target/thumbv7em-none-eabihf/debug/examples/blinky
|
||||||
```
|
```
|
||||||
|
|
||||||
to flash and run the blinky program on the RAM. There is also a `VA416xx` chip target
|
to flash and run the blinky program on the RAM. There is also a `VA416xx` chip target
|
||||||
|
|||||||
@@ -61,14 +61,8 @@ async fn main(_spawner: Spawner) {
|
|||||||
let portg = PinsG::new(dp.portg);
|
let portg = PinsG::new(dp.portg);
|
||||||
let mut led = Output::new(portg.pg5, PinState::Low);
|
let mut led = Output::new(portg.pg5, PinState::Low);
|
||||||
|
|
||||||
let clock_config = uart::ClockConfig::calculate_with_clocks(
|
let uarta =
|
||||||
uart::Bank::Uart0,
|
uart::Uart::new_for_uart0(dp.uart0, portg.pg0, portg.pg1, &clocks, 115200.Hz().into());
|
||||||
&clocks,
|
|
||||||
115200.Hz(),
|
|
||||||
uart::BaudMode::_16,
|
|
||||||
);
|
|
||||||
let uart_config = uart::Config::new_with_clock_config(clock_config);
|
|
||||||
let uarta = uart::Uart::new_for_uart0(dp.uart0, portg.pg0, portg.pg1, uart_config);
|
|
||||||
|
|
||||||
let (mut tx_uart_a, rx_uart_a) = uarta.split();
|
let (mut tx_uart_a, rx_uart_a) = uarta.split();
|
||||||
let (prod_uart_a, cons_uart_a) = QUEUE_UART_A.take().split();
|
let (prod_uart_a, cons_uart_a) = QUEUE_UART_A.take().split();
|
||||||
|
|||||||
@@ -59,14 +59,8 @@ async fn main(_spawner: Spawner) {
|
|||||||
let pinsg = PinsG::new(dp.portg);
|
let pinsg = PinsG::new(dp.portg);
|
||||||
let mut led = Output::new(pinsg.pg5, PinState::Low);
|
let mut led = Output::new(pinsg.pg5, PinState::Low);
|
||||||
|
|
||||||
let clock_config = uart::ClockConfig::calculate_with_clocks(
|
let uarta =
|
||||||
uart::Bank::Uart0,
|
uart::Uart::new_for_uart0(dp.uart0, pinsg.pg0, pinsg.pg1, &clocks, 115200.Hz().into());
|
||||||
&clocks,
|
|
||||||
115200.Hz(),
|
|
||||||
uart::BaudMode::_16,
|
|
||||||
);
|
|
||||||
let uart_config = uart::Config::new_with_clock_config(clock_config);
|
|
||||||
let uarta = uart::Uart::new_for_uart0(dp.uart0, pinsg.pg0, pinsg.pg1, uart_config);
|
|
||||||
let (tx, _rx) = uarta.split();
|
let (tx, _rx) = uarta.split();
|
||||||
let mut async_tx = TxAsync::new(tx);
|
let mut async_tx = TxAsync::new(tx);
|
||||||
let mut ticker = Ticker::every(Duration::from_secs(1));
|
let mut ticker = Ticker::every(Duration::from_secs(1));
|
||||||
|
|||||||
@@ -67,14 +67,13 @@ async fn main(spawner: Spawner) {
|
|||||||
|
|
||||||
let portg = PinsG::new(dp.portg);
|
let portg = PinsG::new(dp.portg);
|
||||||
|
|
||||||
let clock_config = uart::ClockConfig::calculate_with_clocks(
|
let uart0 = uart::Uart::new_for_uart0(
|
||||||
uart::Bank::Uart0,
|
dp.uart0,
|
||||||
|
portg.pg0,
|
||||||
|
portg.pg1,
|
||||||
&clocks,
|
&clocks,
|
||||||
Hertz::from_raw(BAUDRATE),
|
Hertz::from_raw(BAUDRATE).into(),
|
||||||
uart::BaudMode::_16,
|
|
||||||
);
|
);
|
||||||
let uart_config = uart::Config::new_with_clock_config(clock_config);
|
|
||||||
let uart0 = uart::Uart::new_for_uart0(dp.uart0, portg.pg0, portg.pg1, uart_config);
|
|
||||||
let (mut tx, rx) = uart0.split();
|
let (mut tx, rx) = uart0.split();
|
||||||
let mut rx = rx.into_rx_with_irq();
|
let mut rx = rx.into_rx_with_irq();
|
||||||
rx.start();
|
rx.start();
|
||||||
|
|||||||
@@ -22,9 +22,23 @@ va416xx = { version = "0.5", path = "../../va416xx" }
|
|||||||
|
|
||||||
[dependencies.vorago-peb1]
|
[dependencies.vorago-peb1]
|
||||||
path = "../../vorago-peb1"
|
path = "../../vorago-peb1"
|
||||||
|
optional = true
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["va41630"]
|
default = ["va41630"]
|
||||||
va41630 = ["va416xx-hal/va41630"]
|
va41630 = ["va416xx-hal/va41630", "has-adc-dac"]
|
||||||
va41629 = ["va416xx-hal/va41629"]
|
va41629 = ["va416xx-hal/va41629", "has-adc-dac"]
|
||||||
va41628 = ["va416xx-hal/va41628"]
|
va41628 = ["va416xx-hal/va41628"]
|
||||||
|
has-adc-dac = []
|
||||||
|
|
||||||
|
[[example]]
|
||||||
|
name = "peb1-accelerometer"
|
||||||
|
required-features = ["vorago-peb1"]
|
||||||
|
|
||||||
|
[[example]]
|
||||||
|
name = "dac-adc"
|
||||||
|
required-features = ["has-adc-dac"]
|
||||||
|
|
||||||
|
[[example]]
|
||||||
|
name = "adc"
|
||||||
|
required-features = ["has-adc-dac"]
|
||||||
|
|||||||
+2
-1
@@ -15,6 +15,7 @@ use va416xx_hal::{
|
|||||||
clock::ClockConfigurator,
|
clock::ClockConfigurator,
|
||||||
i2c,
|
i2c,
|
||||||
pac::{self},
|
pac::{self},
|
||||||
|
prelude::*,
|
||||||
timer::CountdownTimer,
|
timer::CountdownTimer,
|
||||||
};
|
};
|
||||||
use vorago_peb1::lis2dh12::{self, detect_i2c_addr, FullScale, Odr};
|
use vorago_peb1::lis2dh12::{self, detect_i2c_addr, FullScale, Odr};
|
||||||
@@ -28,7 +29,7 @@ const DISPLAY_MODE: DisplayMode = DisplayMode::Normalized;
|
|||||||
|
|
||||||
#[entry]
|
#[entry]
|
||||||
fn main() -> ! {
|
fn main() -> ! {
|
||||||
let dp = pac::Peripherals::take().unwrap();
|
let mut dp = pac::Peripherals::take().unwrap();
|
||||||
defmt::println!("-- Vorago PEB1 accelerometer example --");
|
defmt::println!("-- Vorago PEB1 accelerometer example --");
|
||||||
// Use the external clock connected to XTAL_N.
|
// Use the external clock connected to XTAL_N.
|
||||||
let clocks = ClockConfigurator::new(dp.clkgen)
|
let clocks = ClockConfigurator::new(dp.clkgen)
|
||||||
@@ -30,14 +30,13 @@ fn main() -> ! {
|
|||||||
|
|
||||||
let gpiog = PinsG::new(dp.portg);
|
let gpiog = PinsG::new(dp.portg);
|
||||||
|
|
||||||
let clock_config = uart::ClockConfig::calculate_with_clocks(
|
let uart0 = uart::Uart::new_for_uart0(
|
||||||
uart::Bank::Uart0,
|
dp.uart0,
|
||||||
|
gpiog.pg0,
|
||||||
|
gpiog.pg1,
|
||||||
&clocks,
|
&clocks,
|
||||||
Hertz::from_raw(115200),
|
Hertz::from_raw(115200).into(),
|
||||||
uart::BaudMode::_16,
|
|
||||||
);
|
);
|
||||||
let uart_config = uart::Config::new_with_clock_config(clock_config);
|
|
||||||
let uart0 = uart::Uart::new_for_uart0(dp.uart0, gpiog.pg0, gpiog.pg1, uart_config);
|
|
||||||
let (mut tx, mut rx) = uart0.split();
|
let (mut tx, mut rx) = uart0.split();
|
||||||
writeln!(tx, "Hello World\n\r").unwrap();
|
writeln!(tx, "Hello World\n\r").unwrap();
|
||||||
loop {
|
loop {
|
||||||
@@ -49,7 +49,7 @@ fn main() -> ! {
|
|||||||
let mut counter: u32 = 0;
|
let mut counter: u32 = 0;
|
||||||
loop {
|
loop {
|
||||||
counter = counter.wrapping_add(1);
|
counter = counter.wrapping_add(1);
|
||||||
if counter.is_multiple_of(log_divisor) {
|
if counter % log_divisor == 0 {
|
||||||
defmt::info!("wdt example main loop alive");
|
defmt::info!("wdt example main loop alive");
|
||||||
}
|
}
|
||||||
if TEST_MODE != TestMode::AllowReset {
|
if TEST_MODE != TestMode::AllowReset {
|
||||||
@@ -116,7 +116,6 @@ mod app {
|
|||||||
nvm::Nvm,
|
nvm::Nvm,
|
||||||
pac,
|
pac,
|
||||||
pins::PinsG,
|
pins::PinsG,
|
||||||
prelude::*,
|
|
||||||
uart::{self, Uart},
|
uart::{self, Uart},
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -168,15 +167,13 @@ mod app {
|
|||||||
|
|
||||||
let gpiog = PinsG::new(cx.device.portg);
|
let gpiog = PinsG::new(cx.device.portg);
|
||||||
|
|
||||||
let clock_config = uart::ClockConfig::calculate_with_clocks(
|
let uart0 = Uart::new_for_uart0(
|
||||||
uart::Bank::Uart0,
|
cx.device.uart0,
|
||||||
|
gpiog.pg0,
|
||||||
|
gpiog.pg1,
|
||||||
&clocks,
|
&clocks,
|
||||||
UART_BAUDRATE.Hz(),
|
Hertz::from_raw(UART_BAUDRATE).into(),
|
||||||
uart::BaudMode::_16,
|
|
||||||
);
|
);
|
||||||
let uart_config = uart::Config::new_with_clock_config(clock_config);
|
|
||||||
|
|
||||||
let uart0 = Uart::new_for_uart0(cx.device.uart0, gpiog.pg0, gpiog.pg1, uart_config);
|
|
||||||
let (tx, rx) = uart0.split();
|
let (tx, rx) = uart0.split();
|
||||||
|
|
||||||
let verif_reporter = VerificationReportCreator::new(u11::new(0));
|
let verif_reporter = VerificationReportCreator::new(u11::new(0));
|
||||||
|
|||||||
@@ -22,11 +22,11 @@ nb = "1"
|
|||||||
embedded-hal = "1"
|
embedded-hal = "1"
|
||||||
num_enum = { version = "0.7", default-features = false }
|
num_enum = { version = "0.7", default-features = false }
|
||||||
bitflags = "2"
|
bitflags = "2"
|
||||||
bitbybit = "2"
|
bitbybit = "1.3"
|
||||||
arbitrary-int = "2"
|
arbitrary-int = "2"
|
||||||
fugit = "0.3"
|
fugit = "0.3"
|
||||||
embedded-can = "0.4"
|
embedded-can = "0.4"
|
||||||
embassy-sync = "0.8"
|
embassy-sync = "0.7"
|
||||||
thiserror = { version = "2", default-features = false }
|
thiserror = { version = "2", default-features = false }
|
||||||
|
|
||||||
defmt = { version = "1", optional = true }
|
defmt = { version = "1", optional = true }
|
||||||
|
|||||||
@@ -1,4 +1,9 @@
|
|||||||
//! Analog to Digital Converter (ADC) driver.
|
//! Analog to Digital Converter (ADC) driver.
|
||||||
|
//!
|
||||||
|
//! ## Examples
|
||||||
|
//!
|
||||||
|
//! - [ADC and DAC example](https://github.com/us-irs/vorago-rs/blob/main/va416xx/examples/simple/examples/dac-adc.rs)
|
||||||
|
//! - [ADC](https://github.com/us-irs/vorago-rs/blob/main/va416xx/examples/simple/examples/adc.rs)
|
||||||
use core::marker::PhantomData;
|
use core::marker::PhantomData;
|
||||||
|
|
||||||
use crate::clock::Clocks;
|
use crate::clock::Clocks;
|
||||||
|
|||||||
@@ -3,8 +3,6 @@ use core::{
|
|||||||
sync::atomic::{AtomicU8, Ordering},
|
sync::atomic::{AtomicU8, Ordering},
|
||||||
};
|
};
|
||||||
|
|
||||||
use arbitrary_int::u4;
|
|
||||||
|
|
||||||
use crate::can::regs::BufferState;
|
use crate::can::regs::BufferState;
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
@@ -60,7 +58,7 @@ pub enum InterruptResult {
|
|||||||
pub enum InterruptError {
|
pub enum InterruptError {
|
||||||
UnexpectedError,
|
UnexpectedError,
|
||||||
InvalidInterruptId(StatusPending),
|
InvalidInterruptId(StatusPending),
|
||||||
InvalidStatus(u4),
|
InvalidStatus(u8),
|
||||||
UnexpectedState(BufferState),
|
UnexpectedState(BufferState),
|
||||||
CanError(DiagnosticRegister),
|
CanError(DiagnosticRegister),
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -81,7 +81,7 @@ impl CanChannelLowLevel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn read_state(&self) -> Result<BufferState, u4> {
|
pub fn read_state(&self) -> Result<BufferState, u8> {
|
||||||
self.msg_buf.read_stat_ctrl().state()
|
self.msg_buf.read_stat_ctrl().state()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,6 +6,10 @@
|
|||||||
//!
|
//!
|
||||||
//! Calling [ClockConfigurator::freeze] returns the frozen clock configuration inside the [Clocks]
|
//! Calling [ClockConfigurator::freeze] returns the frozen clock configuration inside the [Clocks]
|
||||||
//! structure. This structure can also be used to configure other structures provided by this HAL.
|
//! structure. This structure can also be used to configure other structures provided by this HAL.
|
||||||
|
//!
|
||||||
|
//! # Examples
|
||||||
|
//!
|
||||||
|
//! - [UART example on the PEB1 board](https://egit.irs.uni-stuttgart.de/rust/vorago-rs/src/branch/main/va416xx/examples/simple/examples/uart.rs)
|
||||||
#[cfg(not(feature = "va41628"))]
|
#[cfg(not(feature = "va41628"))]
|
||||||
use crate::adc::ADC_MAX_CLK;
|
use crate::adc::ADC_MAX_CLK;
|
||||||
use crate::pac;
|
use crate::pac;
|
||||||
|
|||||||
@@ -1,4 +1,8 @@
|
|||||||
//! Digital to Analog Converter (DAC) driver.
|
//! Digital to Analog Converter (DAC) driver.
|
||||||
|
//!
|
||||||
|
//! ## Examples
|
||||||
|
//!
|
||||||
|
//! - [ADC and DAC example](https://github.com/us-irs/vorago-rs/blob/main/va416xx/examples/simple/examples/dac-adc.rs)
|
||||||
use core::ops::Deref;
|
use core::ops::Deref;
|
||||||
|
|
||||||
use vorago_shared_hal::{
|
use vorago_shared_hal::{
|
||||||
|
|||||||
@@ -1,4 +1,8 @@
|
|||||||
//! API for the DMA peripheral
|
//! API for the DMA peripheral
|
||||||
|
//!
|
||||||
|
//! ## Examples
|
||||||
|
//!
|
||||||
|
//! - [Simple DMA example](https://egit.irs.uni-stuttgart.de/rust/vorago-rs/src/branch/main/va416xx/examples/simple/examples/dma.rs)
|
||||||
use arbitrary_int::{u10, u3};
|
use arbitrary_int::{u10, u3};
|
||||||
use vorago_shared_hal::{enable_peripheral_clock, reset_peripheral_for_cycles, PeripheralSelect};
|
use vorago_shared_hal::{enable_peripheral_clock, reset_peripheral_for_cycles, PeripheralSelect};
|
||||||
|
|
||||||
|
|||||||
@@ -12,4 +12,9 @@
|
|||||||
//!
|
//!
|
||||||
//! The [crate::pins] module exposes singletons to access the [Pin]s required by this module
|
//! The [crate::pins] module exposes singletons to access the [Pin]s required by this module
|
||||||
//! in a type-safe way.
|
//! in a type-safe way.
|
||||||
|
//!
|
||||||
|
//! # Examples
|
||||||
|
//!
|
||||||
|
//! - [Blinky example](https://egit.irs.uni-stuttgart.de/rust/vorago-rs/src/branch/main/va416xx/examples/simple/examples/blinky.rs)
|
||||||
|
//! - [Async GPIO example](https://egit.irs.uni-stuttgart.de/rust/vorago-rs/src/branch/main/va416xx/examples/embassy/src/bin/async-gpio.rs)
|
||||||
pub use vorago_shared_hal::gpio::*;
|
pub use vorago_shared_hal::gpio::*;
|
||||||
|
|||||||
@@ -1,2 +1,6 @@
|
|||||||
//! API for the I2C peripheral
|
//! API for the I2C peripheral
|
||||||
|
//!
|
||||||
|
//! ## Examples
|
||||||
|
//!
|
||||||
|
//! - [PEB1 accelerometer example](https://egit.irs.uni-stuttgart.de/rust/vorago-rs/src/branch/main/va416xx/examples/simple/examples/peb1-accelerometer.rs)
|
||||||
pub use vorago_shared_hal::i2c::*;
|
pub use vorago_shared_hal::i2c::*;
|
||||||
|
|||||||
@@ -25,9 +25,6 @@
|
|||||||
//! with interrupts, it is strongly recommended to set up the IRQ router with the
|
//! with interrupts, it is strongly recommended to set up the IRQ router with the
|
||||||
//! [crate::irq_router] module at the very least because that peripheral has confusing and/or
|
//! [crate::irq_router] module at the very least because that peripheral has confusing and/or
|
||||||
//! faulty register reset values which might lead to weird bugs and glitches.
|
//! faulty register reset values which might lead to weird bugs and glitches.
|
||||||
//!
|
|
||||||
//! The [examples folder](https://github.com/us-irs/vorago-rs/tree/main/va416xx/examples) contains
|
|
||||||
//! various example applications using the HAL.
|
|
||||||
#![no_std]
|
#![no_std]
|
||||||
#![cfg_attr(docsrs, feature(doc_cfg))]
|
#![cfg_attr(docsrs, feature(doc_cfg))]
|
||||||
#[cfg(feature = "alloc")]
|
#[cfg(feature = "alloc")]
|
||||||
|
|||||||
@@ -1,4 +1,8 @@
|
|||||||
//! API for Pulse-Width Modulation (PWM)
|
//! API for Pulse-Width Modulation (PWM)
|
||||||
//!
|
//!
|
||||||
//! The Vorago devices use the TIM peripherals to perform PWM related tasks
|
//! The Vorago devices use the TIM peripherals to perform PWM related tasks
|
||||||
|
//!
|
||||||
|
//! ## Examples
|
||||||
|
//!
|
||||||
|
//! - [PWM example](https://egit.irs.uni-stuttgart.de/rust/vorago-rs/src/branch/main/va416xx/examples/simple/examples/pwm.rs)
|
||||||
pub use vorago_shared_hal::pwm::*;
|
pub use vorago_shared_hal::pwm::*;
|
||||||
|
|||||||
@@ -3,4 +3,9 @@
|
|||||||
//! The main abstraction provided by this module is the [Spi] an structure.
|
//! The main abstraction provided by this module is the [Spi] an structure.
|
||||||
//! It provides the [SpiBus trait](https://docs.rs/embedded-hal/latest/embedded_hal/spi/trait.SpiBus.html),
|
//! It provides the [SpiBus trait](https://docs.rs/embedded-hal/latest/embedded_hal/spi/trait.SpiBus.html),
|
||||||
//! but also offer a low level interface via the [SpiLowLevel] trait.
|
//! but also offer a low level interface via the [SpiLowLevel] trait.
|
||||||
|
//!
|
||||||
|
//! ## Examples
|
||||||
|
//!
|
||||||
|
//! - [Blocking SPI example](https://egit.irs.uni-stuttgart.de/rust/vorago-rs/src/branch/main/va416xx/examples/simple/examples/spi.rs)
|
||||||
|
//! - [NVM library][crate::nvm]
|
||||||
pub use vorago_shared_hal::spi::*;
|
pub use vorago_shared_hal::spi::*;
|
||||||
|
|||||||
@@ -1,4 +1,9 @@
|
|||||||
//! API for the TIM peripherals
|
//! API for the TIM peripherals
|
||||||
|
//!
|
||||||
|
//! ## Examples
|
||||||
|
//!
|
||||||
|
//! - [MS and second tick implementation](https://egit.irs.uni-stuttgart.de/rust/vorago-rs/src/branch/main/va416xx/examples/simple/examples/timer-ticks.rs)
|
||||||
|
//! - [Cascade feature example](https://egit.irs.uni-stuttgart.de/rust/vorago-rs/src/branch/main/va416xx/examples/simple/examples/cascade.rs)
|
||||||
pub use vorago_shared_hal::timer::*;
|
pub use vorago_shared_hal::timer::*;
|
||||||
|
|
||||||
pub const TIM_IRQ_OFFSET: usize = 48;
|
pub const TIM_IRQ_OFFSET: usize = 48;
|
||||||
|
|||||||
@@ -6,4 +6,12 @@
|
|||||||
//!
|
//!
|
||||||
//! The [rx_async] and [tx_async] modules provide an asynchronous non-blocking API for the UART
|
//! The [rx_async] and [tx_async] modules provide an asynchronous non-blocking API for the UART
|
||||||
//! peripheral.
|
//! peripheral.
|
||||||
|
//!
|
||||||
|
//! ## Examples
|
||||||
|
//!
|
||||||
|
//! - [UART simple example](https://egit.irs.uni-stuttgart.de/rust/vorago-rs/src/branch/main/va416xx/examples/simple/examples/uart.rs)
|
||||||
|
//! - [UART with IRQ and RTIC](https://egit.irs.uni-stuttgart.de/rust/vorago-rs/src/branch/main/va416xx/examples/rtic/src/bin/uart-echo-rtic.rs)
|
||||||
|
//! - [Flashloader exposing a CCSDS interface via UART](https://egit.irs.uni-stuttgart.de/rust/vorago-rs/src/branch/main/va416xx/flashloader)
|
||||||
|
//! - [Async UART RX example](https://egit.irs.uni-stuttgart.de/rust/vorago-rs/src/branch/main/va416xx/examples/embassy/src/bin/async-uart-rx.rs)
|
||||||
|
//! - [Async UART TX example](https://egit.irs.uni-stuttgart.de/rust/vorago-rs/src/branch/main/va416xx/examples/embassy/src/bin/async-uart-tx.rs)
|
||||||
pub use vorago_shared_hal::uart::*;
|
pub use vorago_shared_hal::uart::*;
|
||||||
|
|||||||
@@ -1,4 +1,8 @@
|
|||||||
//! # API for the Watchdog peripheral
|
//! # API for the Watchdog peripheral
|
||||||
|
//!
|
||||||
|
//! ## Examples
|
||||||
|
//!
|
||||||
|
//! - [Watchdog simple example](https://egit.irs.uni-stuttgart.de/rust/vorago-rs/src/branch/main/va416xx/examples/simple/examples/wdt.rs)
|
||||||
use vorago_shared_hal::{enable_peripheral_clock, reset_peripheral_for_cycles, PeripheralSelect};
|
use vorago_shared_hal::{enable_peripheral_clock, reset_peripheral_for_cycles, PeripheralSelect};
|
||||||
|
|
||||||
use crate::time::Hertz;
|
use crate::time::Hertz;
|
||||||
|
|||||||
@@ -12,7 +12,6 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
|
|||||||
|
|
||||||
- Add `is_high` and `is_low` for `InputPinAsync`.
|
- Add `is_high` and `is_low` for `InputPinAsync`.
|
||||||
- Add `InputPin` impl for `InputPinAsync`.
|
- Add `InputPin` impl for `InputPinAsync`.
|
||||||
- `HwCsPin` in SPI module for easer usage of HW CS pins as `Output` CS pins
|
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
@@ -20,18 +19,12 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
|
|||||||
- Renamed UART `Data` register `value` field to `data`
|
- Renamed UART `Data` register `value` field to `data`
|
||||||
- Improved type level support for resource management for SPI, PWM, UART.
|
- Improved type level support for resource management for SPI, PWM, UART.
|
||||||
- Renamed `tx_asynch` and `rx_asynch` module name to `*_async`
|
- Renamed `tx_asynch` and `rx_asynch` module name to `*_async`
|
||||||
- Naming improvements in SPI module: replaced `cfg` by `config*`
|
|
||||||
- UART configuration now expects an explicit clock configuration structure and does not
|
|
||||||
calculate it itself anymore.
|
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
- Removed HW CS pin provider implementation for PA23, PA22 and PA21, which are multi HW CS pins.
|
- Removed HW CS pin provider implementation for PA23, PA22 and PA21, which are multi HW CS pins.
|
||||||
- Added missing `AnyPin` trait impl for Multi HW CS pins.
|
- Added missing `AnyPin` trait impl for Multi HW CS pins.
|
||||||
- Expose inner `Input` pin for `InputPinAsync`.
|
- Expose inner `Input` pin for `InputPinAsync`.
|
||||||
- Bugfix for UART clock calculation with 8x baud mode.
|
|
||||||
- Possible bugfix for Asynch GPIO where the interrupt handler could become stuck in a loop.
|
|
||||||
- Robustness improvements for the Asynch GPIO driver code.
|
|
||||||
|
|
||||||
## [v0.2.0] 2025-09-03
|
## [v0.2.0] 2025-09-03
|
||||||
|
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ fugit = "0.3"
|
|||||||
defmt = { version = "1", optional = true }
|
defmt = { version = "1", optional = true }
|
||||||
va108xx = { version = "0.6", path = "../va108xx/va108xx", default-features = false, optional = true }
|
va108xx = { version = "0.6", path = "../va108xx/va108xx", default-features = false, optional = true }
|
||||||
va416xx = { version = "0.5", path = "../va416xx/va416xx", default-features = false, optional = true }
|
va416xx = { version = "0.5", path = "../va416xx/va416xx", default-features = false, optional = true }
|
||||||
embassy-sync = "0.8"
|
embassy-sync = "0.7"
|
||||||
embassy-time-driver = "0.2"
|
embassy-time-driver = "0.2"
|
||||||
embassy-time-queue-utils = "0.3"
|
embassy-time-queue-utils = "0.3"
|
||||||
once_cell = { version = "1", default-features = false, features = [
|
once_cell = { version = "1", default-features = false, features = [
|
||||||
|
|||||||
@@ -21,6 +21,9 @@ use crate::{InterruptConfig, NUM_PORT_A, NUM_PORT_B};
|
|||||||
#[cfg(feature = "vor4x")]
|
#[cfg(feature = "vor4x")]
|
||||||
use super::ll::PortDoesNotSupportInterrupts;
|
use super::ll::PortDoesNotSupportInterrupts;
|
||||||
|
|
||||||
|
#[cfg(feature = "vor1x")]
|
||||||
|
use va108xx as pac;
|
||||||
|
|
||||||
pub use super::ll::InterruptEdge;
|
pub use super::ll::InterruptEdge;
|
||||||
use super::{
|
use super::{
|
||||||
Input, Port,
|
Input, Port,
|
||||||
@@ -115,7 +118,7 @@ pub fn on_interrupt_for_async_gpio_for_port(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn on_interrupt_for_async_gpio_for_port_generic(port: Port) {
|
fn on_interrupt_for_async_gpio_for_port_generic(port: Port) {
|
||||||
let mut gpio = unsafe { port.steal_regs() };
|
let gpio = unsafe { port.steal_gpio() };
|
||||||
|
|
||||||
let irq_enb = gpio.read_irq_enable();
|
let irq_enb = gpio.read_irq_enable();
|
||||||
let edge_status = gpio.read_edge_status();
|
let edge_status = gpio.read_edge_status();
|
||||||
@@ -131,19 +134,18 @@ fn on_interrupt_for_port(
|
|||||||
wakers: &'static [AtomicWaker],
|
wakers: &'static [AtomicWaker],
|
||||||
edge_detection: &'static [AtomicBool],
|
edge_detection: &'static [AtomicBool],
|
||||||
) {
|
) {
|
||||||
// Check all enabled interrupts.
|
|
||||||
while irq_enb != 0 {
|
while irq_enb != 0 {
|
||||||
// For all enabled interrupts, check whether the corresponding edge detection has
|
|
||||||
// triggered.
|
|
||||||
let bit_pos = irq_enb.trailing_zeros() as usize;
|
let bit_pos = irq_enb.trailing_zeros() as usize;
|
||||||
let bit_mask = 1 << bit_pos;
|
let bit_mask = 1 << bit_pos;
|
||||||
|
|
||||||
|
wakers[bit_pos].wake();
|
||||||
|
|
||||||
if edge_status & bit_mask != 0 {
|
if edge_status & bit_mask != 0 {
|
||||||
edge_detection[bit_pos].store(true, core::sync::atomic::Ordering::Relaxed);
|
edge_detection[bit_pos].store(true, core::sync::atomic::Ordering::Relaxed);
|
||||||
wakers[bit_pos].wake();
|
|
||||||
|
// Clear the processed bit
|
||||||
|
irq_enb &= !bit_mask;
|
||||||
}
|
}
|
||||||
// Clear the processed bit
|
|
||||||
irq_enb &= !bit_mask;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -161,12 +163,13 @@ pub struct InputPinFuture {
|
|||||||
impl InputPinFuture {
|
impl InputPinFuture {
|
||||||
/// Create a new input pin future from mutable reference to an [Input] pin.
|
/// Create a new input pin future from mutable reference to an [Input] pin.
|
||||||
#[cfg(feature = "vor1x")]
|
#[cfg(feature = "vor1x")]
|
||||||
pub fn new_with_input_pin(pin: &mut Input, edge: InterruptEdge) -> Self {
|
pub fn new_with_input_pin(pin: &mut Input, irq: pac::Interrupt, edge: InterruptEdge) -> Self {
|
||||||
let (waker_group, edge_detection_group) =
|
let (waker_group, edge_detection_group) =
|
||||||
pin_group_to_waker_and_edge_detection_group(pin.id().port());
|
pin_group_to_waker_and_edge_detection_group(pin.id().port());
|
||||||
edge_detection_group[pin.id().offset()].store(false, core::sync::atomic::Ordering::Relaxed);
|
edge_detection_group[pin.id().offset()].store(false, core::sync::atomic::Ordering::Relaxed);
|
||||||
pin.configure_edge_interrupt(edge);
|
pin.configure_edge_interrupt(edge);
|
||||||
pin.enable_interrupt_gpio_only();
|
#[cfg(feature = "vor1x")]
|
||||||
|
pin.enable_interrupt(InterruptConfig::new(irq, true, true));
|
||||||
Self {
|
Self {
|
||||||
id: pin.id(),
|
id: pin.id(),
|
||||||
waker_group,
|
waker_group,
|
||||||
@@ -183,7 +186,7 @@ impl InputPinFuture {
|
|||||||
let (waker_group, edge_detection_group) =
|
let (waker_group, edge_detection_group) =
|
||||||
pin_group_to_waker_and_edge_detection_group(pin.id().port());
|
pin_group_to_waker_and_edge_detection_group(pin.id().port());
|
||||||
pin.configure_edge_interrupt(edge);
|
pin.configure_edge_interrupt(edge);
|
||||||
pin.enable_interrupt_gpio_only();
|
pin.enable_interrupt(true)?;
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
id: pin.id(),
|
id: pin.id(),
|
||||||
waker_group,
|
waker_group,
|
||||||
@@ -220,6 +223,8 @@ impl Future for InputPinFuture {
|
|||||||
/// Input pin which has additional asynchronous support.
|
/// Input pin which has additional asynchronous support.
|
||||||
pub struct InputPinAsync {
|
pub struct InputPinAsync {
|
||||||
pin: Input,
|
pin: Input,
|
||||||
|
#[cfg(feature = "vor1x")]
|
||||||
|
irq: va108xx::Interrupt,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl InputPinAsync {
|
impl InputPinAsync {
|
||||||
@@ -230,10 +235,8 @@ impl InputPinAsync {
|
|||||||
/// generic [on_interrupt_for_async_gpio_for_port] function must be called inside that function
|
/// generic [on_interrupt_for_async_gpio_for_port] function must be called inside that function
|
||||||
/// for the asynchronous functionality to work.
|
/// for the asynchronous functionality to work.
|
||||||
#[cfg(feature = "vor1x")]
|
#[cfg(feature = "vor1x")]
|
||||||
pub fn new(mut pin: Input, irq_config: InterruptConfig) -> Self {
|
pub fn new(pin: Input, irq: va108xx::Interrupt) -> Self {
|
||||||
// Do not enable GPIO interrupt bit yet.
|
Self { pin, irq }
|
||||||
pin.enable_interrupt(irq_config, false);
|
|
||||||
Self { pin }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a new asynchronous input pin from an [Input] pin. The interrupt ID to be used must be
|
/// Create a new asynchronous input pin from an [Input] pin. The interrupt ID to be used must be
|
||||||
@@ -243,12 +246,10 @@ impl InputPinAsync {
|
|||||||
/// generic [on_interrupt_for_async_gpio_for_port] function must be called inside that function
|
/// generic [on_interrupt_for_async_gpio_for_port] function must be called inside that function
|
||||||
/// for the asynchronous functionality to work.
|
/// for the asynchronous functionality to work.
|
||||||
#[cfg(feature = "vor4x")]
|
#[cfg(feature = "vor4x")]
|
||||||
pub fn new(mut pin: Input) -> Result<Self, PortDoesNotSupportInterrupts> {
|
pub fn new(pin: Input) -> Result<Self, PortDoesNotSupportInterrupts> {
|
||||||
if pin.id().port() == Port::G {
|
if pin.id().port() == Port::G {
|
||||||
return Err(PortDoesNotSupportInterrupts);
|
return Err(PortDoesNotSupportInterrupts);
|
||||||
}
|
}
|
||||||
// Do not enable GPIO interrupt bit yet.
|
|
||||||
pin.enable_interrupt(true, false)?;
|
|
||||||
Ok(Self { pin })
|
Ok(Self { pin })
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -258,7 +259,8 @@ impl InputPinAsync {
|
|||||||
pub async fn wait_for_high(&mut self) {
|
pub async fn wait_for_high(&mut self) {
|
||||||
// Unwrap okay, checked pin in constructor.
|
// Unwrap okay, checked pin in constructor.
|
||||||
#[cfg(feature = "vor1x")]
|
#[cfg(feature = "vor1x")]
|
||||||
let fut = InputPinFuture::new_with_input_pin(&mut self.pin, InterruptEdge::LowToHigh);
|
let fut =
|
||||||
|
InputPinFuture::new_with_input_pin(&mut self.pin, self.irq, InterruptEdge::LowToHigh);
|
||||||
#[cfg(feature = "vor4x")]
|
#[cfg(feature = "vor4x")]
|
||||||
let fut =
|
let fut =
|
||||||
InputPinFuture::new_with_input_pin(&mut self.pin, InterruptEdge::LowToHigh).unwrap();
|
InputPinFuture::new_with_input_pin(&mut self.pin, InterruptEdge::LowToHigh).unwrap();
|
||||||
@@ -298,7 +300,8 @@ impl InputPinAsync {
|
|||||||
pub async fn wait_for_low(&mut self) {
|
pub async fn wait_for_low(&mut self) {
|
||||||
// Unwrap okay, checked pin in constructor.
|
// Unwrap okay, checked pin in constructor.
|
||||||
#[cfg(feature = "vor1x")]
|
#[cfg(feature = "vor1x")]
|
||||||
let fut = InputPinFuture::new_with_input_pin(&mut self.pin, InterruptEdge::HighToLow);
|
let fut =
|
||||||
|
InputPinFuture::new_with_input_pin(&mut self.pin, self.irq, InterruptEdge::HighToLow);
|
||||||
#[cfg(feature = "vor4x")]
|
#[cfg(feature = "vor4x")]
|
||||||
let fut =
|
let fut =
|
||||||
InputPinFuture::new_with_input_pin(&mut self.pin, InterruptEdge::HighToLow).unwrap();
|
InputPinFuture::new_with_input_pin(&mut self.pin, InterruptEdge::HighToLow).unwrap();
|
||||||
@@ -312,7 +315,7 @@ impl InputPinAsync {
|
|||||||
pub async fn wait_for_falling_edge(&mut self) {
|
pub async fn wait_for_falling_edge(&mut self) {
|
||||||
// Unwrap okay, checked pin in constructor.
|
// Unwrap okay, checked pin in constructor.
|
||||||
#[cfg(feature = "vor1x")]
|
#[cfg(feature = "vor1x")]
|
||||||
InputPinFuture::new_with_input_pin(&mut self.pin, InterruptEdge::HighToLow).await;
|
InputPinFuture::new_with_input_pin(&mut self.pin, self.irq, InterruptEdge::HighToLow).await;
|
||||||
#[cfg(feature = "vor4x")]
|
#[cfg(feature = "vor4x")]
|
||||||
InputPinFuture::new_with_input_pin(&mut self.pin, InterruptEdge::HighToLow)
|
InputPinFuture::new_with_input_pin(&mut self.pin, InterruptEdge::HighToLow)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
@@ -323,14 +326,14 @@ impl InputPinAsync {
|
|||||||
pub async fn wait_for_rising_edge(&mut self) {
|
pub async fn wait_for_rising_edge(&mut self) {
|
||||||
// Unwrap okay, checked pin in constructor.
|
// Unwrap okay, checked pin in constructor.
|
||||||
#[cfg(feature = "vor1x")]
|
#[cfg(feature = "vor1x")]
|
||||||
InputPinFuture::new_with_input_pin(&mut self.pin, InterruptEdge::LowToHigh).await;
|
InputPinFuture::new_with_input_pin(&mut self.pin, self.irq, InterruptEdge::LowToHigh).await;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Asynchronously wait until the pin sees any edge (either rising or falling).
|
/// Asynchronously wait until the pin sees any edge (either rising or falling).
|
||||||
pub async fn wait_for_any_edge(&mut self) {
|
pub async fn wait_for_any_edge(&mut self) {
|
||||||
// Unwrap okay, checked pin in constructor.
|
// Unwrap okay, checked pin in constructor.
|
||||||
#[cfg(feature = "vor1x")]
|
#[cfg(feature = "vor1x")]
|
||||||
InputPinFuture::new_with_input_pin(&mut self.pin, InterruptEdge::BothEdges).await;
|
InputPinFuture::new_with_input_pin(&mut self.pin, self.irq, InterruptEdge::BothEdges).await;
|
||||||
#[cfg(feature = "vor4x")]
|
#[cfg(feature = "vor4x")]
|
||||||
InputPinFuture::new_with_input_pin(&mut self.pin, InterruptEdge::BothEdges)
|
InputPinFuture::new_with_input_pin(&mut self.pin, InterruptEdge::BothEdges)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
|
|||||||
@@ -384,53 +384,32 @@ impl LowLevelGpio {
|
|||||||
self.gpio.write_tog_out(self.mask_32());
|
self.gpio.write_tog_out(self.mask_32());
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Only enabled GPIO peripheral interrupt bit without enabling the interrupt in NVIC
|
|
||||||
/// or routing it in the IRQSEL peripheral for VA108xx devices.
|
|
||||||
#[inline]
|
|
||||||
pub fn enable_interrupt_gpio_only(&mut self) {
|
|
||||||
self.gpio.modify_irq_enable(|mut value| {
|
|
||||||
value |= 1 << self.id.offset;
|
|
||||||
value
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Depending on the configuration parameters, does the following:
|
|
||||||
///
|
|
||||||
/// - Routes the interrupt in the IRQSEL peripheral
|
|
||||||
/// - Enables the interrupt in the NVIC,
|
|
||||||
/// - Enable the GPIO peripheral interrupt bit for this pin if configured.
|
|
||||||
#[cfg(feature = "vor1x")]
|
#[cfg(feature = "vor1x")]
|
||||||
pub fn enable_interrupt(&mut self, irq_cfg: crate::InterruptConfig, gpio: bool) {
|
pub fn enable_interrupt(&mut self, irq_cfg: crate::InterruptConfig) {
|
||||||
if irq_cfg.route {
|
if irq_cfg.route {
|
||||||
self.configure_irqsel(irq_cfg.id);
|
self.configure_irqsel(irq_cfg.id);
|
||||||
}
|
}
|
||||||
if irq_cfg.enable_in_nvic {
|
if irq_cfg.enable_in_nvic {
|
||||||
unsafe { crate::enable_nvic_interrupt(irq_cfg.id) };
|
unsafe { crate::enable_nvic_interrupt(irq_cfg.id) };
|
||||||
}
|
}
|
||||||
if gpio {
|
self.gpio.modify_irq_enable(|mut value| {
|
||||||
self.enable_interrupt_gpio_only();
|
value |= 1 << self.id.offset;
|
||||||
}
|
value
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Depending on the configuration parameters, does the following:
|
|
||||||
///
|
|
||||||
/// - Enables the interrupt in the NVIC,
|
|
||||||
/// - Enable the GPIO peripheral interrupt bit for this pin if configured.
|
|
||||||
#[cfg(feature = "vor4x")]
|
#[cfg(feature = "vor4x")]
|
||||||
pub fn enable_interrupt(
|
pub fn enable_interrupt(
|
||||||
&mut self,
|
&mut self,
|
||||||
enable_in_nvic: bool,
|
enable_in_nvic: bool,
|
||||||
gpio: bool,
|
|
||||||
) -> Result<(), PortDoesNotSupportInterrupts> {
|
) -> Result<(), PortDoesNotSupportInterrupts> {
|
||||||
if self.id().port() == Port::G {
|
|
||||||
return Err(PortDoesNotSupportInterrupts);
|
|
||||||
}
|
|
||||||
if enable_in_nvic {
|
if enable_in_nvic {
|
||||||
unsafe { crate::enable_nvic_interrupt(self.id().irq_unchecked()) };
|
unsafe { crate::enable_nvic_interrupt(self.id().irq_unchecked()) };
|
||||||
}
|
}
|
||||||
if gpio {
|
self.gpio.modify_irq_enable(|mut value| {
|
||||||
self.enable_interrupt_gpio_only();
|
value |= 1 << self.id.offset;
|
||||||
}
|
value
|
||||||
|
});
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -132,34 +132,19 @@ impl Input {
|
|||||||
self.0.id()
|
self.0.id()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn enable_interrupt_gpio_only(&mut self) {
|
|
||||||
self.0.enable_interrupt_gpio_only();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Depending on the configuration parameters, does the following:
|
|
||||||
///
|
|
||||||
/// - Routes the interrupt in the IRQSEL peripheral
|
|
||||||
/// - Enables the interrupt in the NVIC,
|
|
||||||
/// - Enable the GPIO peripheral interrupt bit for this pin if configured.
|
|
||||||
#[cfg(feature = "vor1x")]
|
#[cfg(feature = "vor1x")]
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn enable_interrupt(&mut self, irq_cfg: crate::InterruptConfig, gpio: bool) {
|
pub fn enable_interrupt(&mut self, irq_cfg: crate::InterruptConfig) {
|
||||||
self.0.enable_interrupt(irq_cfg, gpio);
|
self.0.enable_interrupt(irq_cfg);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Depending on the configuration parameters, does the following:
|
|
||||||
///
|
|
||||||
/// - Enables the interrupt in the NVIC,
|
|
||||||
/// - Enable the GPIO peripheral interrupt bit for this pin if configured.
|
|
||||||
#[cfg(feature = "vor4x")]
|
#[cfg(feature = "vor4x")]
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn enable_interrupt(
|
pub fn enable_interrupt(
|
||||||
&mut self,
|
&mut self,
|
||||||
enable_in_nvic: bool,
|
enable_in_nvic: bool,
|
||||||
gpio: bool,
|
|
||||||
) -> Result<(), ll::PortDoesNotSupportInterrupts> {
|
) -> Result<(), ll::PortDoesNotSupportInterrupts> {
|
||||||
self.0.enable_interrupt(enable_in_nvic, gpio)
|
self.0.enable_interrupt(enable_in_nvic)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
|
|||||||
@@ -58,8 +58,7 @@ pub struct Gpio {
|
|||||||
/// Read-only register which shows enabled and active interrupts. Called IRQ_end by Vorago.
|
/// Read-only register which shows enabled and active interrupts. Called IRQ_end by Vorago.
|
||||||
#[mmio(PureRead)]
|
#[mmio(PureRead)]
|
||||||
irq_status: u32,
|
irq_status: u32,
|
||||||
// Reading this register clears it.
|
#[mmio(PureRead)]
|
||||||
#[mmio(Read)]
|
|
||||||
edge_status: u32,
|
edge_status: u32,
|
||||||
|
|
||||||
#[cfg(feature = "vor1x")]
|
#[cfg(feature = "vor1x")]
|
||||||
|
|||||||
@@ -137,7 +137,7 @@ impl Port {
|
|||||||
/// # Safety
|
/// # Safety
|
||||||
///
|
///
|
||||||
/// Circumvents ownership and safety guarantees by the HAL.
|
/// Circumvents ownership and safety guarantees by the HAL.
|
||||||
pub unsafe fn steal_regs(&self) -> gpio::regs::MmioGpio<'static> {
|
pub unsafe fn steal_gpio(&self) -> gpio::regs::MmioGpio<'static> {
|
||||||
gpio::regs::Gpio::new_mmio(*self)
|
gpio::regs::Gpio::new_mmio(*self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -225,11 +225,4 @@ pub(crate) mod shared {
|
|||||||
#[bit(0, w)]
|
#[bit(0, w)]
|
||||||
rx_fifo: bool,
|
rx_fifo: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FifoClear {
|
|
||||||
pub const ALL: Self = Self::builder()
|
|
||||||
.with_tx_fifo(true)
|
|
||||||
.with_rx_fifo(true)
|
|
||||||
.build();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,584 +0,0 @@
|
|||||||
use core::{cell::RefCell, convert::Infallible};
|
|
||||||
|
|
||||||
use arbitrary_int::u5;
|
|
||||||
use critical_section::Mutex;
|
|
||||||
use embassy_sync::waitqueue::AtomicWaker;
|
|
||||||
use portable_atomic::AtomicBool;
|
|
||||||
use raw_slice::{RawBufSlice, RawBufSliceMut};
|
|
||||||
|
|
||||||
use crate::{
|
|
||||||
shared::{FifoClear, TriggerLevel},
|
|
||||||
spi::regs::{Data, InterruptClear, InterruptControl, InterruptStatus},
|
|
||||||
};
|
|
||||||
|
|
||||||
#[cfg(feature = "vor1x")]
|
|
||||||
pub const NUM_SPIS: usize = 3;
|
|
||||||
#[cfg(feature = "vor4x")]
|
|
||||||
pub const NUM_SPIS: usize = 4;
|
|
||||||
|
|
||||||
static WAKERS: [AtomicWaker; NUM_SPIS] = [const { AtomicWaker::new() }; NUM_SPIS];
|
|
||||||
static TRANSFER_CONTEXTS: [Mutex<RefCell<TransferContext>>; NUM_SPIS] =
|
|
||||||
[const { Mutex::new(RefCell::new(TransferContext::new())) }; NUM_SPIS];
|
|
||||||
// Completion flag. Kept outside of the context structure as an atomic to avoid
|
|
||||||
// critical section.
|
|
||||||
static DONE: [AtomicBool; NUM_SPIS] = [const { AtomicBool::new(false) }; NUM_SPIS];
|
|
||||||
|
|
||||||
/// This is a generic interrupt handler to handle asynchronous SPI operations for a given
|
|
||||||
/// SPI peripheral.
|
|
||||||
///
|
|
||||||
/// The user has to call this once in the interrupt handler responsible for the SPI interrupts on
|
|
||||||
/// the given SPI bank.
|
|
||||||
pub fn on_interrupt(peripheral: super::Bank) {
|
|
||||||
let mut spi = unsafe { peripheral.steal_regs() };
|
|
||||||
let idx = peripheral as usize;
|
|
||||||
let interrupt_enabled = spi.read_interrupt_control();
|
|
||||||
let isr = spi.read_interrupt_status();
|
|
||||||
// IRQ is not related.
|
|
||||||
if interrupt_enabled.raw_value() == 0 {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// Prevent spurious interrupts from messing with out logic here.
|
|
||||||
spi.write_interrupt_control(InterruptControl::DISABLE_ALL);
|
|
||||||
spi.write_interrupt_clear(InterruptClear::ALL);
|
|
||||||
let mut context = critical_section::with(|cs| {
|
|
||||||
let context_ref = TRANSFER_CONTEXTS[idx].borrow(cs);
|
|
||||||
*context_ref.borrow()
|
|
||||||
});
|
|
||||||
// No transfer active.
|
|
||||||
if context.transfer_type.is_none() {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
let transfer_type = context.transfer_type.unwrap();
|
|
||||||
match transfer_type {
|
|
||||||
TransferType::Read => on_interrupt_read(idx, &mut context, &mut spi, isr),
|
|
||||||
TransferType::Write => on_interrupt_write(idx, &mut context, &mut spi, isr),
|
|
||||||
TransferType::Transfer => on_interrupt_transfer(idx, &mut context, &mut spi, isr),
|
|
||||||
TransferType::TransferInPlace => {
|
|
||||||
on_interrupt_transfer_in_place(idx, &mut context, &mut spi, isr)
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
fn on_interrupt_read(
|
|
||||||
idx: usize,
|
|
||||||
context: &mut TransferContext,
|
|
||||||
spi: &mut super::regs::MmioSpi<'static>,
|
|
||||||
isr: InterruptStatus,
|
|
||||||
) {
|
|
||||||
let read_slice = unsafe { context.rx_slice.get_mut().unwrap() };
|
|
||||||
let transfer_len = read_slice.len();
|
|
||||||
|
|
||||||
// Read data from RX FIFO first.
|
|
||||||
let read_len = calculate_read_len(spi, isr, transfer_len, context.rx_progress);
|
|
||||||
(0..read_len).for_each(|_| {
|
|
||||||
read_slice[context.rx_progress] = (spi.read_data().data() & 0xFF) as u8;
|
|
||||||
context.rx_progress += 1;
|
|
||||||
});
|
|
||||||
|
|
||||||
// The FIFO still needs to be pumped.
|
|
||||||
while context.tx_progress < read_slice.len() && spi.read_status().tx_not_full() {
|
|
||||||
spi.write_data(Data::new_with_raw_value(0));
|
|
||||||
context.tx_progress += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
isr_finish_handler(idx, spi, context, transfer_len)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn on_interrupt_write(
|
|
||||||
idx: usize,
|
|
||||||
context: &mut TransferContext,
|
|
||||||
spi: &mut super::regs::MmioSpi<'static>,
|
|
||||||
isr: InterruptStatus,
|
|
||||||
) {
|
|
||||||
let write_slice = unsafe { context.tx_slice.get().unwrap() };
|
|
||||||
let transfer_len = write_slice.len();
|
|
||||||
|
|
||||||
// Read data from RX FIFO first.
|
|
||||||
let read_len = calculate_read_len(spi, isr, transfer_len, context.rx_progress);
|
|
||||||
(0..read_len).for_each(|_| {
|
|
||||||
spi.read_data();
|
|
||||||
context.rx_progress += 1;
|
|
||||||
});
|
|
||||||
|
|
||||||
// Data still needs to be sent
|
|
||||||
while context.tx_progress < transfer_len && spi.read_status().tx_not_full() {
|
|
||||||
spi.write_data(Data::new_with_raw_value(
|
|
||||||
write_slice[context.tx_progress] as u32,
|
|
||||||
));
|
|
||||||
context.tx_progress += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
isr_finish_handler(idx, spi, context, transfer_len)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn on_interrupt_transfer(
|
|
||||||
idx: usize,
|
|
||||||
context: &mut TransferContext,
|
|
||||||
spi: &mut super::regs::MmioSpi<'static>,
|
|
||||||
isr: InterruptStatus,
|
|
||||||
) {
|
|
||||||
let read_slice = unsafe { context.rx_slice.get_mut().unwrap() };
|
|
||||||
let read_len = read_slice.len();
|
|
||||||
let write_slice = unsafe { context.tx_slice.get().unwrap() };
|
|
||||||
let write_len = write_slice.len();
|
|
||||||
let transfer_len = core::cmp::max(read_len, write_len);
|
|
||||||
|
|
||||||
// Send data first to avoid overwriting data that still needs to be sent.
|
|
||||||
while context.tx_progress < transfer_len && spi.read_status().tx_not_full() {
|
|
||||||
if context.tx_progress < write_len {
|
|
||||||
spi.write_data(Data::new_with_raw_value(
|
|
||||||
write_slice[context.tx_progress] as u32,
|
|
||||||
));
|
|
||||||
} else {
|
|
||||||
// Dummy write.
|
|
||||||
spi.write_data(Data::new_with_raw_value(0));
|
|
||||||
}
|
|
||||||
context.tx_progress += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read data from RX FIFO.
|
|
||||||
let read_len = calculate_read_len(spi, isr, transfer_len, context.rx_progress);
|
|
||||||
(0..read_len).for_each(|_| {
|
|
||||||
if context.rx_progress < read_len {
|
|
||||||
read_slice[context.rx_progress] = (spi.read_data().data() & 0xFF) as u8;
|
|
||||||
} else {
|
|
||||||
spi.read_data();
|
|
||||||
}
|
|
||||||
context.rx_progress += 1;
|
|
||||||
});
|
|
||||||
|
|
||||||
isr_finish_handler(idx, spi, context, transfer_len)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn on_interrupt_transfer_in_place(
|
|
||||||
idx: usize,
|
|
||||||
context: &mut TransferContext,
|
|
||||||
spi: &mut super::regs::MmioSpi<'static>,
|
|
||||||
isr: InterruptStatus,
|
|
||||||
) {
|
|
||||||
let transfer_slice = unsafe { context.rx_slice.get_mut().unwrap() };
|
|
||||||
let transfer_len = transfer_slice.len();
|
|
||||||
// Send data first to avoid overwriting data that still needs to be sent.
|
|
||||||
while context.tx_progress < transfer_len && spi.read_status().tx_not_full() {
|
|
||||||
spi.write_data(Data::new_with_raw_value(
|
|
||||||
transfer_slice[context.tx_progress] as u32,
|
|
||||||
));
|
|
||||||
context.tx_progress += 1;
|
|
||||||
}
|
|
||||||
// Read data from RX FIFO.
|
|
||||||
let read_len = calculate_read_len(spi, isr, transfer_len, context.rx_progress);
|
|
||||||
(0..read_len).for_each(|_| {
|
|
||||||
transfer_slice[context.rx_progress] = (spi.read_data().data() & 0xFF) as u8;
|
|
||||||
context.rx_progress += 1;
|
|
||||||
});
|
|
||||||
|
|
||||||
isr_finish_handler(idx, spi, context, transfer_len)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn calculate_read_len(
|
|
||||||
spi: &mut super::regs::MmioSpi<'static>,
|
|
||||||
isr: InterruptStatus,
|
|
||||||
total_read_len: usize,
|
|
||||||
rx_progress: usize,
|
|
||||||
) -> usize {
|
|
||||||
if isr.rx() {
|
|
||||||
core::cmp::min(super::FIFO_DEPTH, total_read_len - rx_progress)
|
|
||||||
} else if spi.read_status().rx_not_empty() {
|
|
||||||
let fifo_level = spi.read_state().rx_fifo();
|
|
||||||
core::cmp::min(total_read_len - rx_progress, fifo_level as usize)
|
|
||||||
} else {
|
|
||||||
0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Generic handler after RX FIFO and TX FIFO were handled. Checks and handles finished
|
|
||||||
/// and unfinished conditions.
|
|
||||||
fn isr_finish_handler(
|
|
||||||
idx: usize,
|
|
||||||
spi: &mut super::regs::MmioSpi<'static>,
|
|
||||||
context: &mut TransferContext,
|
|
||||||
transfer_len: usize,
|
|
||||||
) {
|
|
||||||
// Transfer finish condition.
|
|
||||||
if context.rx_progress == context.tx_progress && context.rx_progress == transfer_len {
|
|
||||||
finish_transfer(idx, context, spi);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
unfinished_transfer(spi, transfer_len, context.rx_progress);
|
|
||||||
|
|
||||||
// If the transfer is done, the context structure was already written back.
|
|
||||||
// Write back updated context structure.
|
|
||||||
critical_section::with(|cs| {
|
|
||||||
let context_ref = TRANSFER_CONTEXTS[idx].borrow(cs);
|
|
||||||
*context_ref.borrow_mut() = *context;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
fn finish_transfer(
|
|
||||||
idx: usize,
|
|
||||||
context: &mut TransferContext,
|
|
||||||
spi: &mut super::regs::MmioSpi<'static>,
|
|
||||||
) {
|
|
||||||
// Write back updated context structure.
|
|
||||||
critical_section::with(|cs| {
|
|
||||||
let context_ref = TRANSFER_CONTEXTS[idx].borrow(cs);
|
|
||||||
*context_ref.borrow_mut() = *context;
|
|
||||||
});
|
|
||||||
spi.write_rx_fifo_trigger(TriggerLevel::new(u5::new(0x08)));
|
|
||||||
spi.write_tx_fifo_trigger(TriggerLevel::new(u5::new(0x00)));
|
|
||||||
// Interrupts were already disabled and cleared.
|
|
||||||
DONE[idx].store(true, core::sync::atomic::Ordering::Relaxed);
|
|
||||||
WAKERS[idx].wake();
|
|
||||||
}
|
|
||||||
|
|
||||||
fn unfinished_transfer(
|
|
||||||
spi: &mut super::regs::MmioSpi<'static>,
|
|
||||||
transfer_len: usize,
|
|
||||||
rx_progress: usize,
|
|
||||||
) {
|
|
||||||
let new_trig_level = core::cmp::min(super::FIFO_DEPTH, transfer_len - rx_progress);
|
|
||||||
spi.write_rx_fifo_trigger(TriggerLevel::new(u5::new(new_trig_level as u8)));
|
|
||||||
// Re-enable interrupts with the new RX FIFO trigger level.
|
|
||||||
spi.write_interrupt_control(InterruptControl::ENABLE_ALL);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
|
||||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
|
||||||
pub enum TransferType {
|
|
||||||
Read,
|
|
||||||
Write,
|
|
||||||
Transfer,
|
|
||||||
TransferInPlace,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Default, Debug, Copy, Clone)]
|
|
||||||
pub struct TransferContext {
|
|
||||||
transfer_type: Option<TransferType>,
|
|
||||||
tx_progress: usize,
|
|
||||||
rx_progress: usize,
|
|
||||||
tx_slice: RawBufSlice,
|
|
||||||
rx_slice: RawBufSliceMut,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(clippy::new_without_default)]
|
|
||||||
impl TransferContext {
|
|
||||||
pub const fn new() -> Self {
|
|
||||||
Self {
|
|
||||||
transfer_type: None,
|
|
||||||
tx_progress: 0,
|
|
||||||
rx_progress: 0,
|
|
||||||
tx_slice: RawBufSlice::new_nulled(),
|
|
||||||
rx_slice: RawBufSliceMut::new_nulled(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct SpiFuture<'spi> {
|
|
||||||
bank: super::Bank,
|
|
||||||
spi: &'spi mut super::Spi<u8>,
|
|
||||||
finished_regularly: core::cell::Cell<bool>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'spi> SpiFuture<'spi> {
|
|
||||||
fn new_for_read(spi: &'spi mut super::Spi<u8>, bank: super::Bank, words: &mut [u8]) -> Self {
|
|
||||||
if words.is_empty() {
|
|
||||||
panic!("words length unexpectedly 0");
|
|
||||||
}
|
|
||||||
let idx = bank as usize;
|
|
||||||
DONE[idx].store(false, core::sync::atomic::Ordering::Relaxed);
|
|
||||||
spi.regs
|
|
||||||
.write_interrupt_control(InterruptControl::DISABLE_ALL);
|
|
||||||
spi.regs.write_fifo_clear(FifoClear::ALL);
|
|
||||||
spi.regs.modify_ctrl1(|v| v.with_mtxpause(true));
|
|
||||||
let write_idx = core::cmp::min(super::FIFO_DEPTH, words.len());
|
|
||||||
// Send dummy bytes.
|
|
||||||
(0..write_idx).for_each(|_| {
|
|
||||||
spi.regs.write_data(Data::new_with_raw_value(0));
|
|
||||||
});
|
|
||||||
|
|
||||||
Self::set_triggers(spi, write_idx, words.len());
|
|
||||||
|
|
||||||
critical_section::with(|cs| {
|
|
||||||
let context_ref = TRANSFER_CONTEXTS[idx].borrow(cs);
|
|
||||||
let mut context = context_ref.borrow_mut();
|
|
||||||
context.transfer_type = Some(TransferType::Read);
|
|
||||||
unsafe {
|
|
||||||
context.rx_slice.set(words);
|
|
||||||
}
|
|
||||||
context.tx_slice.set_null();
|
|
||||||
context.tx_progress = write_idx;
|
|
||||||
context.rx_progress = 0;
|
|
||||||
spi.regs.write_interrupt_clear(InterruptClear::ALL);
|
|
||||||
spi.regs
|
|
||||||
.write_interrupt_control(InterruptControl::ENABLE_ALL);
|
|
||||||
spi.regs.modify_ctrl1(|v| v.with_mtxpause(false));
|
|
||||||
});
|
|
||||||
Self {
|
|
||||||
bank,
|
|
||||||
spi,
|
|
||||||
finished_regularly: core::cell::Cell::new(false),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn new_for_write(spi: &'spi mut super::Spi<u8>, bank: super::Bank, words: &[u8]) -> Self {
|
|
||||||
if words.is_empty() {
|
|
||||||
panic!("words length unexpectedly 0");
|
|
||||||
}
|
|
||||||
let (idx, write_idx) = Self::generic_init_transfer(spi, bank, words);
|
|
||||||
critical_section::with(|cs| {
|
|
||||||
let context_ref = TRANSFER_CONTEXTS[idx].borrow(cs);
|
|
||||||
let mut context = context_ref.borrow_mut();
|
|
||||||
context.transfer_type = Some(TransferType::Write);
|
|
||||||
unsafe {
|
|
||||||
context.tx_slice.set(words);
|
|
||||||
}
|
|
||||||
context.rx_slice.set_null();
|
|
||||||
context.tx_progress = write_idx;
|
|
||||||
context.rx_progress = 0;
|
|
||||||
spi.regs.write_interrupt_clear(InterruptClear::ALL);
|
|
||||||
spi.regs
|
|
||||||
.write_interrupt_control(InterruptControl::ENABLE_ALL);
|
|
||||||
spi.regs.modify_ctrl1(|v| v.with_mtxpause(false));
|
|
||||||
});
|
|
||||||
Self {
|
|
||||||
bank,
|
|
||||||
spi,
|
|
||||||
finished_regularly: core::cell::Cell::new(false),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn new_for_transfer(
|
|
||||||
spi: &'spi mut super::Spi<u8>,
|
|
||||||
spi_id: super::Bank,
|
|
||||||
read: &mut [u8],
|
|
||||||
write: &[u8],
|
|
||||||
) -> Self {
|
|
||||||
if read.is_empty() || write.is_empty() {
|
|
||||||
panic!("read or write buffer unexpectedly empty");
|
|
||||||
}
|
|
||||||
let (idx, write_idx) = Self::generic_init_transfer(spi, spi_id, write);
|
|
||||||
critical_section::with(|cs| {
|
|
||||||
let context_ref = TRANSFER_CONTEXTS[idx].borrow(cs);
|
|
||||||
let mut context = context_ref.borrow_mut();
|
|
||||||
context.transfer_type = Some(TransferType::Transfer);
|
|
||||||
unsafe {
|
|
||||||
context.tx_slice.set(write);
|
|
||||||
context.rx_slice.set(read);
|
|
||||||
}
|
|
||||||
context.tx_progress = write_idx;
|
|
||||||
context.rx_progress = 0;
|
|
||||||
spi.regs.write_interrupt_clear(InterruptClear::ALL);
|
|
||||||
spi.regs
|
|
||||||
.write_interrupt_control(InterruptControl::ENABLE_ALL);
|
|
||||||
spi.regs.modify_ctrl1(|v| v.with_mtxpause(false));
|
|
||||||
});
|
|
||||||
Self {
|
|
||||||
bank: spi_id,
|
|
||||||
spi,
|
|
||||||
finished_regularly: core::cell::Cell::new(false),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn new_for_transfer_in_place(
|
|
||||||
spi: &'spi mut super::Spi<u8>,
|
|
||||||
spi_id: super::Bank,
|
|
||||||
words: &mut [u8],
|
|
||||||
) -> Self {
|
|
||||||
if words.is_empty() {
|
|
||||||
panic!("read and write buffer unexpectedly empty");
|
|
||||||
}
|
|
||||||
let (idx, write_idx) = Self::generic_init_transfer(spi, spi_id, words);
|
|
||||||
critical_section::with(|cs| {
|
|
||||||
let context_ref = TRANSFER_CONTEXTS[idx].borrow(cs);
|
|
||||||
let mut context = context_ref.borrow_mut();
|
|
||||||
context.transfer_type = Some(TransferType::TransferInPlace);
|
|
||||||
unsafe {
|
|
||||||
context.rx_slice.set(words);
|
|
||||||
}
|
|
||||||
context.tx_slice.set_null();
|
|
||||||
context.tx_progress = write_idx;
|
|
||||||
context.rx_progress = 0;
|
|
||||||
spi.regs.write_interrupt_clear(InterruptClear::ALL);
|
|
||||||
spi.regs
|
|
||||||
.write_interrupt_control(InterruptControl::ENABLE_ALL);
|
|
||||||
spi.regs.modify_ctrl1(|v| v.with_mtxpause(false));
|
|
||||||
});
|
|
||||||
Self {
|
|
||||||
bank: spi_id,
|
|
||||||
spi,
|
|
||||||
finished_regularly: core::cell::Cell::new(false),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn generic_init_transfer(
|
|
||||||
spi: &mut super::Spi<u8>,
|
|
||||||
bank: super::Bank,
|
|
||||||
write: &[u8],
|
|
||||||
) -> (usize, usize) {
|
|
||||||
let idx = bank as usize;
|
|
||||||
DONE[idx].store(false, core::sync::atomic::Ordering::Relaxed);
|
|
||||||
spi.regs
|
|
||||||
.write_interrupt_control(InterruptControl::DISABLE_ALL);
|
|
||||||
spi.regs.write_fifo_clear(FifoClear::ALL);
|
|
||||||
spi.regs.modify_ctrl1(|v| v.with_mtxpause(true));
|
|
||||||
|
|
||||||
let write_idx = core::cmp::min(super::FIFO_DEPTH, write.len());
|
|
||||||
(0..write_idx).for_each(|idx| {
|
|
||||||
spi.regs
|
|
||||||
.write_data(Data::new_with_raw_value(write[idx] as u32));
|
|
||||||
});
|
|
||||||
|
|
||||||
Self::set_triggers(spi, write_idx, write.len());
|
|
||||||
(idx, write_idx)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn set_triggers(spi: &mut super::Spi<u8>, write_idx: usize, write_len: usize) {
|
|
||||||
// This should never fail because it is never larger than the FIFO depth.
|
|
||||||
spi.regs
|
|
||||||
.write_rx_fifo_trigger(TriggerLevel::new(u5::new(write_idx as u8)));
|
|
||||||
// We want to re-fill the TX FIFO before it is completely empty if the full transfer size
|
|
||||||
// is larger than the FIFO depth. I am not sure whether the default value of 1 ensures
|
|
||||||
// this because the PG says that this interrupt is triggered when the FIFO has less than
|
|
||||||
// threshold entries.
|
|
||||||
if write_len > super::FIFO_DEPTH {
|
|
||||||
spi.regs
|
|
||||||
.write_tx_fifo_trigger(TriggerLevel::new(u5::new(2)));
|
|
||||||
} else {
|
|
||||||
spi.regs
|
|
||||||
.write_tx_fifo_trigger(TriggerLevel::new(u5::new(0)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'spi> Future for SpiFuture<'spi> {
|
|
||||||
type Output = ();
|
|
||||||
|
|
||||||
fn poll(
|
|
||||||
self: core::pin::Pin<&mut Self>,
|
|
||||||
cx: &mut core::task::Context<'_>,
|
|
||||||
) -> core::task::Poll<Self::Output> {
|
|
||||||
WAKERS[self.bank as usize].register(cx.waker());
|
|
||||||
if DONE[self.bank as usize].swap(false, core::sync::atomic::Ordering::Relaxed) {
|
|
||||||
critical_section::with(|cs| {
|
|
||||||
let mut ctx = TRANSFER_CONTEXTS[self.bank as usize]
|
|
||||||
.borrow(cs)
|
|
||||||
.borrow_mut();
|
|
||||||
*ctx = TransferContext::default();
|
|
||||||
});
|
|
||||||
self.finished_regularly.set(true);
|
|
||||||
return core::task::Poll::Ready(());
|
|
||||||
}
|
|
||||||
core::task::Poll::Pending
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'spi> Drop for SpiFuture<'spi> {
|
|
||||||
fn drop(&mut self) {
|
|
||||||
if !self.finished_regularly.get() {
|
|
||||||
// It might be sufficient to disable and enable the SPI.. But this definitely
|
|
||||||
// ensures the SPI is fully reset.
|
|
||||||
self.spi.regs.write_interrupt_clear(InterruptClear::ALL);
|
|
||||||
self.spi
|
|
||||||
.regs
|
|
||||||
.write_interrupt_control(InterruptControl::DISABLE_ALL);
|
|
||||||
self.spi.regs.write_fifo_clear(FifoClear::ALL);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Asynchronous SPI driver.
|
|
||||||
///
|
|
||||||
/// This is the primary data structure used to perform non-blocking SPI operations.
|
|
||||||
/// It implements the [embedded_hal_async::spi::SpiBus] as well.
|
|
||||||
pub struct SpiAsync(pub super::Spi<u8>);
|
|
||||||
|
|
||||||
impl SpiAsync {
|
|
||||||
pub fn new(
|
|
||||||
mut spi: super::Spi<u8>,
|
|
||||||
#[cfg(feature = "vor1x")] opt_irq_cfg: Option<crate::InterruptConfig>,
|
|
||||||
) -> Self {
|
|
||||||
#[cfg(feature = "vor1x")]
|
|
||||||
if let Some(irq_cfg) = opt_irq_cfg {
|
|
||||||
spi.regs
|
|
||||||
.write_interrupt_control(InterruptControl::DISABLE_ALL);
|
|
||||||
spi.regs.write_interrupt_clear(InterruptClear::ALL);
|
|
||||||
if irq_cfg.route {
|
|
||||||
crate::enable_peripheral_clock(crate::PeripheralSelect::Irqsel);
|
|
||||||
unsafe { va108xx::Irqsel::steal() }
|
|
||||||
.spi(spi.id as usize)
|
|
||||||
.write(|w| unsafe { w.bits(irq_cfg.id as u32) });
|
|
||||||
}
|
|
||||||
if irq_cfg.enable_in_nvic {
|
|
||||||
// Safety: User has specifically configured this.
|
|
||||||
unsafe { crate::enable_nvic_interrupt(irq_cfg.id) };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Disable blockmode for asynchronous mode.
|
|
||||||
spi.regs
|
|
||||||
.modify_ctrl1(|v| v.with_bm_stall(false).with_blockmode(false));
|
|
||||||
Self(spi)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn read(&mut self, words: &mut [u8]) -> Option<SpiFuture<'_>> {
|
|
||||||
if words.is_empty() {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
let id = self.0.id;
|
|
||||||
Some(SpiFuture::new_for_read(&mut self.0, id, words))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn write(&mut self, words: &[u8]) -> Option<SpiFuture<'_>> {
|
|
||||||
if words.is_empty() {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
let id = self.0.id;
|
|
||||||
Some(SpiFuture::new_for_write(&mut self.0, id, words))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn transfer(&mut self, read: &mut [u8], write: &[u8]) -> Option<SpiFuture<'_>> {
|
|
||||||
if read.is_empty() || write.is_empty() {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
let id = self.0.id;
|
|
||||||
Some(SpiFuture::new_for_transfer(&mut self.0, id, read, write))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn transfer_in_place(&mut self, words: &mut [u8]) -> Option<SpiFuture<'_>> {
|
|
||||||
if words.is_empty() {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
let id = self.0.id;
|
|
||||||
Some(SpiFuture::new_for_transfer_in_place(&mut self.0, id, words))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl embedded_hal_async::spi::ErrorType for SpiAsync {
|
|
||||||
type Error = Infallible;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl embedded_hal_async::spi::SpiBus for SpiAsync {
|
|
||||||
async fn read(&mut self, words: &mut [u8]) -> Result<(), Self::Error> {
|
|
||||||
self.read(words).unwrap().await;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn write(&mut self, words: &[u8]) -> Result<(), Self::Error> {
|
|
||||||
self.write(words).unwrap().await;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn transfer(&mut self, read: &mut [u8], write: &[u8]) -> Result<(), Self::Error> {
|
|
||||||
self.transfer(read, write).unwrap().await;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn transfer_in_place(&mut self, words: &mut [u8]) -> Result<(), Self::Error> {
|
|
||||||
self.transfer_in_place(words).unwrap().await;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn flush(&mut self) -> Result<(), Self::Error> {
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -12,11 +12,8 @@ use va416xx as pac;
|
|||||||
|
|
||||||
pub use regs::{Bank, HwChipSelectId};
|
pub use regs::{Bank, HwChipSelectId};
|
||||||
|
|
||||||
pub mod asynch;
|
|
||||||
pub mod regs;
|
pub mod regs;
|
||||||
|
|
||||||
pub const FIFO_DEPTH: usize = 16;
|
|
||||||
|
|
||||||
pub fn configure_pin_as_hw_cs_pin<P: AnyPin + HwCsProvider>(_pin: P) -> HwChipSelectId {
|
pub fn configure_pin_as_hw_cs_pin<P: AnyPin + HwCsProvider>(_pin: P) -> HwChipSelectId {
|
||||||
IoPeriphPin::new(P::ID, P::FUN_SEL, None);
|
IoPeriphPin::new(P::ID, P::FUN_SEL, None);
|
||||||
P::CS_ID
|
P::CS_ID
|
||||||
@@ -523,6 +520,7 @@ pub fn clk_div_for_target_clock(sys_clk: Hertz, spi_clk: Hertz) -> Option<u16> {
|
|||||||
pub struct Spi<Word = u8> {
|
pub struct Spi<Word = u8> {
|
||||||
id: Bank,
|
id: Bank,
|
||||||
regs: regs::MmioSpi<'static>,
|
regs: regs::MmioSpi<'static>,
|
||||||
|
cfg: SpiConfig,
|
||||||
/// Fill word for read-only SPI transactions.
|
/// Fill word for read-only SPI transactions.
|
||||||
fill_word: Word,
|
fill_word: Word,
|
||||||
blockmode: bool,
|
blockmode: bool,
|
||||||
@@ -655,6 +653,7 @@ where
|
|||||||
Spi {
|
Spi {
|
||||||
id: spi_sel,
|
id: spi_sel,
|
||||||
regs: regs::Spi::new_mmio(spi_sel),
|
regs: regs::Spi::new_mmio(spi_sel),
|
||||||
|
cfg: spi_cfg,
|
||||||
fill_word: Default::default(),
|
fill_word: Default::default(),
|
||||||
bmstall: spi_cfg.bmstall,
|
bmstall: spi_cfg.bmstall,
|
||||||
blockmode: spi_cfg.blockmode,
|
blockmode: spi_cfg.blockmode,
|
||||||
@@ -677,14 +676,14 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn configure_clock_from_div(&mut self, div: u16) -> Result<(), SpiClockConfigError> {
|
pub fn cfg_clock_from_div(&mut self, div: u16) -> Result<(), SpiClockConfigError> {
|
||||||
let val = spi_clk_config_from_div(div)?;
|
let val = spi_clk_config_from_div(div)?;
|
||||||
self.cfg_clock(val);
|
self.cfg_clock(val);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn configure_mode(&mut self, mode: Mode) {
|
pub fn cfg_mode(&mut self, mode: Mode) {
|
||||||
let (cpo_bit, cph_bit) = mode_to_cpo_cph_bit(mode);
|
let (cpo_bit, cph_bit) = mode_to_cpo_cph_bit(mode);
|
||||||
self.regs.modify_ctrl0(|mut value| {
|
self.regs.modify_ctrl0(|mut value| {
|
||||||
value.set_spo(cpo_bit);
|
value.set_spo(cpo_bit);
|
||||||
@@ -719,7 +718,7 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn peripheral_id(&self) -> u32 {
|
pub fn perid(&self) -> u32 {
|
||||||
self.regs.read_perid()
|
self.regs.read_perid()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -729,7 +728,7 @@ where
|
|||||||
/// by using the [configure_pin_as_hw_cs_pin] function which also returns the
|
/// by using the [configure_pin_as_hw_cs_pin] function which also returns the
|
||||||
/// corresponding [HwChipSelectId].
|
/// corresponding [HwChipSelectId].
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn configure_hw_cs(&mut self, hw_cs: HwChipSelectId) {
|
pub fn cfg_hw_cs(&mut self, hw_cs: HwChipSelectId) {
|
||||||
self.regs.modify_ctrl1(|mut value| {
|
self.regs.modify_ctrl1(|mut value| {
|
||||||
value.set_sod(false);
|
value.set_sod(false);
|
||||||
value.set_ss(hw_cs);
|
value.set_ss(hw_cs);
|
||||||
@@ -740,7 +739,7 @@ where
|
|||||||
/// Disables the hardware chip select functionality. This can be used when performing
|
/// Disables the hardware chip select functionality. This can be used when performing
|
||||||
/// external chip select handling, for example with GPIO pins.
|
/// external chip select handling, for example with GPIO pins.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn disable_hw_cs(&mut self) {
|
pub fn cfg_hw_cs_disable(&mut self) {
|
||||||
self.regs.modify_ctrl1(|mut value| {
|
self.regs.modify_ctrl1(|mut value| {
|
||||||
value.set_sod(true);
|
value.set_sod(true);
|
||||||
value
|
value
|
||||||
@@ -750,12 +749,12 @@ where
|
|||||||
/// Utility function to configure all relevant transfer parameters in one go.
|
/// Utility function to configure all relevant transfer parameters in one go.
|
||||||
/// This is useful if multiple devices with different clock and mode configurations
|
/// This is useful if multiple devices with different clock and mode configurations
|
||||||
/// are connected to one bus.
|
/// are connected to one bus.
|
||||||
pub fn configure_transfer(&mut self, transfer_cfg: &TransferConfig) {
|
pub fn cfg_transfer(&mut self, transfer_cfg: &TransferConfig) {
|
||||||
if let Some(trans_clk_div) = transfer_cfg.clk_cfg {
|
if let Some(trans_clk_div) = transfer_cfg.clk_cfg {
|
||||||
self.cfg_clock(trans_clk_div);
|
self.cfg_clock(trans_clk_div);
|
||||||
}
|
}
|
||||||
if let Some(mode) = transfer_cfg.mode {
|
if let Some(mode) = transfer_cfg.mode {
|
||||||
self.configure_mode(mode);
|
self.cfg_mode(mode);
|
||||||
}
|
}
|
||||||
self.blockmode = transfer_cfg.blockmode;
|
self.blockmode = transfer_cfg.blockmode;
|
||||||
self.regs.modify_ctrl1(|mut value| {
|
self.regs.modify_ctrl1(|mut value| {
|
||||||
@@ -1032,6 +1031,7 @@ impl From<Spi<u8>> for Spi<u16> {
|
|||||||
Spi {
|
Spi {
|
||||||
id: old_spi.id,
|
id: old_spi.id,
|
||||||
regs: old_spi.regs,
|
regs: old_spi.regs,
|
||||||
|
cfg: old_spi.cfg,
|
||||||
blockmode: old_spi.blockmode,
|
blockmode: old_spi.blockmode,
|
||||||
fill_word: Default::default(),
|
fill_word: Default::default(),
|
||||||
bmstall: old_spi.bmstall,
|
bmstall: old_spi.bmstall,
|
||||||
@@ -1049,6 +1049,7 @@ impl From<Spi<u16>> for Spi<u8> {
|
|||||||
Spi {
|
Spi {
|
||||||
id: old_spi.id,
|
id: old_spi.id,
|
||||||
regs: old_spi.regs,
|
regs: old_spi.regs,
|
||||||
|
cfg: old_spi.cfg,
|
||||||
blockmode: old_spi.blockmode,
|
blockmode: old_spi.blockmode,
|
||||||
fill_word: Default::default(),
|
fill_word: Default::default(),
|
||||||
bmstall: old_spi.bmstall,
|
bmstall: old_spi.bmstall,
|
||||||
@@ -1056,39 +1057,3 @@ impl From<Spi<u16>> for Spi<u8> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This abstraction which can be used to map a hardware chip select pin
|
|
||||||
/// to [embedded_hal::digital::OutputPin]. This is useful for creating physical chip select
|
|
||||||
/// pins required by the [embedded_hal_bus](https://docs.rs/embedded-hal-bus/latest/embedded_hal_bus/)
|
|
||||||
/// API.
|
|
||||||
pub struct HwCsPin {
|
|
||||||
regs: regs::MmioSpi<'static>,
|
|
||||||
id: HwChipSelectId,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl HwCsPin {
|
|
||||||
pub fn new<P: HwCsProvider + AnyPin>(pin: P) -> Self {
|
|
||||||
configure_pin_as_hw_cs_pin(pin);
|
|
||||||
Self {
|
|
||||||
regs: unsafe { P::SPI_ID.steal_regs() },
|
|
||||||
id: P::CS_ID,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl embedded_hal::digital::ErrorType for HwCsPin {
|
|
||||||
type Error = Infallible;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl embedded_hal::digital::OutputPin for HwCsPin {
|
|
||||||
fn set_low(&mut self) -> Result<(), Self::Error> {
|
|
||||||
self.regs
|
|
||||||
.modify_ctrl1(|value| value.with_sod(false).with_ss(self.id));
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn set_high(&mut self) -> Result<(), Self::Error> {
|
|
||||||
self.regs.modify_ctrl1(|value| value.with_sod(true));
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -25,11 +25,11 @@ cfg_if::cfg_if! {
|
|||||||
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
||||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
pub enum Bank {
|
pub enum Bank {
|
||||||
Spi0 = 0,
|
Spi0,
|
||||||
Spi1 = 1,
|
Spi1,
|
||||||
Spi2 = 2,
|
Spi2,
|
||||||
#[cfg(feature = "vor4x")]
|
#[cfg(feature = "vor4x")]
|
||||||
Spi3 = 3,
|
Spi3,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Bank {
|
impl Bank {
|
||||||
@@ -157,7 +157,7 @@ impl ClockPrescaler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[bitbybit::bitfield(u32, debug, default = 0x0, defmt_bitfields(feature = "defmt"))]
|
#[bitbybit::bitfield(u32, debug, defmt_bitfields(feature = "defmt"))]
|
||||||
pub struct InterruptControl {
|
pub struct InterruptControl {
|
||||||
/// TX FIFO count <= TX FIFO trigger level.
|
/// TX FIFO count <= TX FIFO trigger level.
|
||||||
#[bit(3, rw)]
|
#[bit(3, rw)]
|
||||||
@@ -174,19 +174,9 @@ pub struct InterruptControl {
|
|||||||
rx_overrun: bool,
|
rx_overrun: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl InterruptControl {
|
|
||||||
pub const DISABLE_ALL: Self = Self::ZERO;
|
|
||||||
pub const ENABLE_ALL: Self = Self::builder()
|
|
||||||
.with_tx(true)
|
|
||||||
.with_rx(true)
|
|
||||||
.with_rx_timeout(true)
|
|
||||||
.with_rx_overrun(true)
|
|
||||||
.build();
|
|
||||||
}
|
|
||||||
|
|
||||||
#[bitbybit::bitfield(u32, debug, defmt_bitfields(feature = "defmt"))]
|
#[bitbybit::bitfield(u32, debug, defmt_bitfields(feature = "defmt"))]
|
||||||
pub struct InterruptStatus {
|
pub struct InterruptStatus {
|
||||||
/// TX FIFO count < TX FIFO trigger level.
|
/// TX FIFO count <= TX FIFO trigger level.
|
||||||
#[bit(3, r)]
|
#[bit(3, r)]
|
||||||
tx: bool,
|
tx: bool,
|
||||||
/// RX FIFO count >= RX FIFO trigger level.
|
/// RX FIFO count >= RX FIFO trigger level.
|
||||||
@@ -201,7 +191,7 @@ pub struct InterruptStatus {
|
|||||||
rx_overrun: bool,
|
rx_overrun: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[bitbybit::bitfield(u32, default = 0x0)]
|
#[bitbybit::bitfield(u32)]
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct InterruptClear {
|
pub struct InterruptClear {
|
||||||
/// Clearing the RX interrupt or reading data from the FIFO resets the timeout counter.
|
/// Clearing the RX interrupt or reading data from the FIFO resets the timeout counter.
|
||||||
@@ -211,13 +201,6 @@ pub struct InterruptClear {
|
|||||||
rx_overrun: bool,
|
rx_overrun: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl InterruptClear {
|
|
||||||
pub const ALL: Self = Self::builder()
|
|
||||||
.with_rx_timeout(true)
|
|
||||||
.with_rx_overrun(true)
|
|
||||||
.build();
|
|
||||||
}
|
|
||||||
|
|
||||||
#[bitbybit::bitfield(u32, debug, defmt_bitfields(feature = "defmt"))]
|
#[bitbybit::bitfield(u32, debug, defmt_bitfields(feature = "defmt"))]
|
||||||
pub struct State {
|
pub struct State {
|
||||||
#[bits(0..=7, r)]
|
#[bits(0..=7, r)]
|
||||||
@@ -238,21 +221,21 @@ pub struct Spi {
|
|||||||
#[mmio(PureRead)]
|
#[mmio(PureRead)]
|
||||||
status: Status,
|
status: Status,
|
||||||
clkprescale: ClockPrescaler,
|
clkprescale: ClockPrescaler,
|
||||||
interrupt_control: InterruptControl,
|
irq_enb: InterruptControl,
|
||||||
/// Raw interrupt status.
|
/// Raw interrupt status.
|
||||||
#[mmio(PureRead)]
|
#[mmio(PureRead)]
|
||||||
interrupt_status_raw: InterruptStatus,
|
irq_raw: InterruptStatus,
|
||||||
/// Enabled interrupt status.
|
/// Enabled interrupt status.
|
||||||
#[mmio(PureRead)]
|
#[mmio(PureRead)]
|
||||||
interrupt_status: InterruptStatus,
|
irq_status: InterruptStatus,
|
||||||
#[mmio(Write)]
|
#[mmio(Write)]
|
||||||
interrupt_clear: InterruptClear,
|
irq_clear: InterruptClear,
|
||||||
rx_fifo_trigger: TriggerLevel,
|
rx_fifo_trigger: TriggerLevel,
|
||||||
tx_fifo_trigger: TriggerLevel,
|
tx_fifo_trigger: TriggerLevel,
|
||||||
#[mmio(Write)]
|
#[mmio(Write)]
|
||||||
fifo_clear: FifoClear,
|
fifo_clear: FifoClear,
|
||||||
#[mmio(PureRead)]
|
#[mmio(PureRead)]
|
||||||
state: State,
|
state: u32,
|
||||||
#[cfg(feature = "vor1x")]
|
#[cfg(feature = "vor1x")]
|
||||||
_reserved: [u32; 0x3F2],
|
_reserved: [u32; 0x3F2],
|
||||||
#[cfg(feature = "vor4x")]
|
#[cfg(feature = "vor4x")]
|
||||||
|
|||||||
+105
-100
@@ -23,6 +23,7 @@ use crate::{
|
|||||||
sealed::Sealed,
|
sealed::Sealed,
|
||||||
};
|
};
|
||||||
use arbitrary_int::{prelude::*, u6, u18};
|
use arbitrary_int::{prelude::*, u6, u18};
|
||||||
|
use fugit::RateExtU32;
|
||||||
use regs::{ClockScale, Control, Data, Enable, FifoClear, InterruptClear, MmioUart};
|
use regs::{ClockScale, Control, Data, Enable, FifoClear, InterruptClear, MmioUart};
|
||||||
|
|
||||||
use crate::{PeripheralSelect, enable_nvic_interrupt, enable_peripheral_clock, time::Hertz};
|
use crate::{PeripheralSelect, enable_nvic_interrupt, enable_peripheral_clock, time::Hertz};
|
||||||
@@ -119,25 +120,6 @@ pub enum Event {
|
|||||||
TxCts,
|
TxCts,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Default, Copy, Clone, PartialEq, Eq)]
|
|
||||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
|
||||||
pub enum BaudMode {
|
|
||||||
/// Default 16x baud clock.
|
|
||||||
#[default]
|
|
||||||
_16 = 0,
|
|
||||||
/// Slower 8x baud clock.
|
|
||||||
_8 = 1,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl BaudMode {
|
|
||||||
pub const fn multiplier(&self) -> u32 {
|
|
||||||
match self {
|
|
||||||
BaudMode::_16 => 16,
|
|
||||||
BaudMode::_8 => 8,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
pub enum Parity {
|
pub enum Parity {
|
||||||
@@ -146,100 +128,74 @@ pub enum Parity {
|
|||||||
Even,
|
Even,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
|
||||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
|
||||||
pub struct ClockConfig {
|
|
||||||
/// Integer divisor.
|
|
||||||
pub div: u18,
|
|
||||||
/// Fractional divide value in 1/64 units.
|
|
||||||
pub frac: u6,
|
|
||||||
pub baud_mode: BaudMode,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ClockConfig {
|
|
||||||
pub const fn calculate(ref_clk: Hertz, baudrate: Hertz, baud_mode: BaudMode) -> Self {
|
|
||||||
// This is the calculation: (64.0 * (x - integer_part as f32) + 0.5) as u32 without floating
|
|
||||||
// point calculations.
|
|
||||||
let multiplier = baud_mode.multiplier();
|
|
||||||
let frac = ((ref_clk.raw() % (baudrate.raw() * multiplier)) * 64
|
|
||||||
+ (baudrate.raw() * (multiplier / 2)))
|
|
||||||
/ (baudrate.raw() * multiplier);
|
|
||||||
// Calculations here are derived from chapter 4.8.5 (p.79) of the datasheet.
|
|
||||||
let integer_div = ref_clk.raw() / (baudrate.raw() * multiplier);
|
|
||||||
Self {
|
|
||||||
frac: u6::new(frac as u8),
|
|
||||||
div: u18::new(integer_div),
|
|
||||||
baud_mode,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "vor4x")]
|
|
||||||
pub fn calculate_with_clocks(
|
|
||||||
uart_id: Bank,
|
|
||||||
clks: &Clocks,
|
|
||||||
baudrate: Hertz,
|
|
||||||
baud_mode: BaudMode,
|
|
||||||
) -> Self {
|
|
||||||
let clk = if uart_id == Bank::Uart2 {
|
|
||||||
clks.apb1()
|
|
||||||
} else {
|
|
||||||
clks.apb2()
|
|
||||||
};
|
|
||||||
Self::calculate(clk, baudrate, baud_mode)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
pub struct Config {
|
pub struct Config {
|
||||||
pub clock_config: ClockConfig,
|
pub baudrate: Hertz,
|
||||||
pub parity: Parity,
|
pub parity: Parity,
|
||||||
pub stopbits: Stopbits,
|
pub stopbits: Stopbits,
|
||||||
|
// When false, use standard 16x baud clock, other 8x baud clock
|
||||||
|
pub baud8: bool,
|
||||||
pub wordsize: WordSize,
|
pub wordsize: WordSize,
|
||||||
pub enable_tx: bool,
|
pub enable_tx: bool,
|
||||||
pub enable_rx: bool,
|
pub enable_rx: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Config {
|
impl Config {
|
||||||
pub fn new_with_clock_config(clock_config: ClockConfig) -> Self {
|
pub fn baudrate(mut self, baudrate: Hertz) -> Self {
|
||||||
|
self.baudrate = baudrate;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn parity_none(mut self) -> Self {
|
||||||
|
self.parity = Parity::None;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn parity_even(mut self) -> Self {
|
||||||
|
self.parity = Parity::Even;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn parity_odd(mut self) -> Self {
|
||||||
|
self.parity = Parity::Odd;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn stopbits(mut self, stopbits: Stopbits) -> Self {
|
||||||
|
self.stopbits = stopbits;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn wordsize(mut self, wordsize: WordSize) -> Self {
|
||||||
|
self.wordsize = wordsize;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn baud8(mut self, baud: bool) -> Self {
|
||||||
|
self.baud8 = baud;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for Config {
|
||||||
|
fn default() -> Config {
|
||||||
|
let baudrate = 115_200_u32.Hz();
|
||||||
Config {
|
Config {
|
||||||
clock_config,
|
baudrate,
|
||||||
parity: Parity::None,
|
parity: Parity::None,
|
||||||
stopbits: Stopbits::One,
|
stopbits: Stopbits::One,
|
||||||
|
baud8: false,
|
||||||
wordsize: WordSize::Eight,
|
wordsize: WordSize::Eight,
|
||||||
enable_tx: true,
|
enable_tx: true,
|
||||||
enable_rx: true,
|
enable_rx: true,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn with_clock_config(mut self, clock_config: ClockConfig) -> Self {
|
impl From<Hertz> for Config {
|
||||||
self.clock_config = clock_config;
|
fn from(baud: Hertz) -> Self {
|
||||||
self
|
Config::default().baudrate(baud)
|
||||||
}
|
|
||||||
|
|
||||||
pub fn with_parity_none(mut self) -> Self {
|
|
||||||
self.parity = Parity::None;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn with_parity_even(mut self) -> Self {
|
|
||||||
self.parity = Parity::Even;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn with_parity_odd(mut self) -> Self {
|
|
||||||
self.parity = Parity::Odd;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn with_stopbits(mut self, stopbits: Stopbits) -> Self {
|
|
||||||
self.stopbits = stopbits;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn with_wordsize(mut self, wordsize: WordSize) -> Self {
|
|
||||||
self.wordsize = wordsize;
|
|
||||||
self
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -452,10 +408,11 @@ impl Uart {
|
|||||||
uart: Uart,
|
uart: Uart,
|
||||||
tx_pin: Tx,
|
tx_pin: Tx,
|
||||||
rx_pin: Rx,
|
rx_pin: Rx,
|
||||||
|
sys_clk: Hertz,
|
||||||
config: Config,
|
config: Config,
|
||||||
irq_cfg: InterruptConfig,
|
irq_cfg: InterruptConfig,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self::new_for_uart0(uart, tx_pin, rx_pin, config, Some(irq_cfg))
|
Self::new_for_uart0(uart, tx_pin, rx_pin, sys_clk, config, Some(irq_cfg))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Calls [Self::new_for_uart1] with the interrupt configuration to some valid value.
|
/// Calls [Self::new_for_uart1] with the interrupt configuration to some valid value.
|
||||||
@@ -463,10 +420,11 @@ impl Uart {
|
|||||||
uart: Uart,
|
uart: Uart,
|
||||||
tx_pin: Tx,
|
tx_pin: Tx,
|
||||||
rx_pin: Rx,
|
rx_pin: Rx,
|
||||||
|
sys_clk: Hertz,
|
||||||
config: Config,
|
config: Config,
|
||||||
irq_cfg: InterruptConfig,
|
irq_cfg: InterruptConfig,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self::new_for_uart1(uart, tx_pin, rx_pin, config, Some(irq_cfg))
|
Self::new_for_uart1(uart, tx_pin, rx_pin, sys_clk, config, Some(irq_cfg))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Calls [Self::new_for_uart0] with the interrupt configuration to [None].
|
/// Calls [Self::new_for_uart0] with the interrupt configuration to [None].
|
||||||
@@ -474,9 +432,10 @@ impl Uart {
|
|||||||
uart: Uart,
|
uart: Uart,
|
||||||
tx_pin: Tx,
|
tx_pin: Tx,
|
||||||
rx_pin: Rx,
|
rx_pin: Rx,
|
||||||
|
sys_clk: Hertz,
|
||||||
config: Config,
|
config: Config,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self::new_for_uart0(uart, tx_pin, rx_pin, config, None)
|
Self::new_for_uart0(uart, tx_pin, rx_pin, sys_clk, config, None)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Calls [Self::new_for_uart1] with the interrupt configuration to [None].
|
/// Calls [Self::new_for_uart1] with the interrupt configuration to [None].
|
||||||
@@ -484,9 +443,10 @@ impl Uart {
|
|||||||
uart: Uart,
|
uart: Uart,
|
||||||
tx_pin: Tx,
|
tx_pin: Tx,
|
||||||
rx_pin: Rx,
|
rx_pin: Rx,
|
||||||
|
sys_clk: Hertz,
|
||||||
config: Config,
|
config: Config,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self::new_for_uart1(uart, tx_pin, rx_pin, config, None)
|
Self::new_for_uart1(uart, tx_pin, rx_pin, sys_clk, config, None)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a new UART peripheral driver with an interrupt configuration.
|
/// Create a new UART peripheral driver with an interrupt configuration.
|
||||||
@@ -505,6 +465,7 @@ impl Uart {
|
|||||||
_uart: Uart,
|
_uart: Uart,
|
||||||
_tx_pin: Tx,
|
_tx_pin: Tx,
|
||||||
_rx_pin: Rx,
|
_rx_pin: Rx,
|
||||||
|
sys_clk: Hertz,
|
||||||
config: Config,
|
config: Config,
|
||||||
opt_irq_cfg: Option<InterruptConfig>,
|
opt_irq_cfg: Option<InterruptConfig>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
@@ -515,6 +476,7 @@ impl Uart {
|
|||||||
Tx::FUNC_SEL,
|
Tx::FUNC_SEL,
|
||||||
Rx::ID,
|
Rx::ID,
|
||||||
Rx::FUNC_SEL,
|
Rx::FUNC_SEL,
|
||||||
|
sys_clk,
|
||||||
config,
|
config,
|
||||||
opt_irq_cfg
|
opt_irq_cfg
|
||||||
)
|
)
|
||||||
@@ -536,6 +498,7 @@ impl Uart {
|
|||||||
_uart: Uart,
|
_uart: Uart,
|
||||||
_tx_pin: Tx,
|
_tx_pin: Tx,
|
||||||
_rx_pin: Rx,
|
_rx_pin: Rx,
|
||||||
|
sys_clk: Hertz,
|
||||||
config: Config,
|
config: Config,
|
||||||
opt_irq_cfg: Option<InterruptConfig>,
|
opt_irq_cfg: Option<InterruptConfig>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
@@ -546,6 +509,7 @@ impl Uart {
|
|||||||
Tx::FUNC_SEL,
|
Tx::FUNC_SEL,
|
||||||
Rx::ID,
|
Rx::ID,
|
||||||
Rx::FUNC_SEL,
|
Rx::FUNC_SEL,
|
||||||
|
sys_clk,
|
||||||
config,
|
config,
|
||||||
opt_irq_cfg
|
opt_irq_cfg
|
||||||
)
|
)
|
||||||
@@ -563,8 +527,14 @@ impl Uart {
|
|||||||
_uart: Uart,
|
_uart: Uart,
|
||||||
_tx_pin: Tx,
|
_tx_pin: Tx,
|
||||||
_rx_pin: Rx,
|
_rx_pin: Rx,
|
||||||
|
clks: &Clocks,
|
||||||
config: Config,
|
config: Config,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
|
let clk = if Uart::ID == Bank::Uart2 {
|
||||||
|
clks.apb1()
|
||||||
|
} else {
|
||||||
|
clks.apb2()
|
||||||
|
};
|
||||||
Self::new_internal(
|
Self::new_internal(
|
||||||
Uart::PERIPH_SEL,
|
Uart::PERIPH_SEL,
|
||||||
Uart::ID,
|
Uart::ID,
|
||||||
@@ -572,6 +542,7 @@ impl Uart {
|
|||||||
Tx::FUNC_SEL,
|
Tx::FUNC_SEL,
|
||||||
Rx::ID,
|
Rx::ID,
|
||||||
Rx::FUNC_SEL,
|
Rx::FUNC_SEL,
|
||||||
|
clk,
|
||||||
config
|
config
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -588,8 +559,14 @@ impl Uart {
|
|||||||
_uart: Uart,
|
_uart: Uart,
|
||||||
_tx_pin: Tx,
|
_tx_pin: Tx,
|
||||||
_rx_pin: Rx,
|
_rx_pin: Rx,
|
||||||
|
clks: &Clocks,
|
||||||
config: Config,
|
config: Config,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
|
let clk = if Uart::ID == Bank::Uart2 {
|
||||||
|
clks.apb1()
|
||||||
|
} else {
|
||||||
|
clks.apb2()
|
||||||
|
};
|
||||||
Self::new_internal(
|
Self::new_internal(
|
||||||
Uart::PERIPH_SEL,
|
Uart::PERIPH_SEL,
|
||||||
Uart::ID,
|
Uart::ID,
|
||||||
@@ -597,6 +574,7 @@ impl Uart {
|
|||||||
Tx::FUNC_SEL,
|
Tx::FUNC_SEL,
|
||||||
Rx::ID,
|
Rx::ID,
|
||||||
Rx::FUNC_SEL,
|
Rx::FUNC_SEL,
|
||||||
|
clk,
|
||||||
config
|
config
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -613,8 +591,14 @@ impl Uart {
|
|||||||
_uart: Uart,
|
_uart: Uart,
|
||||||
_tx_pin: Tx,
|
_tx_pin: Tx,
|
||||||
_rx_pin: Rx,
|
_rx_pin: Rx,
|
||||||
|
clks: &Clocks,
|
||||||
config: Config,
|
config: Config,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
|
let clk = if Uart::ID == Bank::Uart2 {
|
||||||
|
clks.apb1()
|
||||||
|
} else {
|
||||||
|
clks.apb2()
|
||||||
|
};
|
||||||
Self::new_internal(
|
Self::new_internal(
|
||||||
Uart::PERIPH_SEL,
|
Uart::PERIPH_SEL,
|
||||||
Uart::ID,
|
Uart::ID,
|
||||||
@@ -622,6 +606,7 @@ impl Uart {
|
|||||||
Tx::FUNC_SEL,
|
Tx::FUNC_SEL,
|
||||||
Rx::ID,
|
Rx::ID,
|
||||||
Rx::FUNC_SEL,
|
Rx::FUNC_SEL,
|
||||||
|
clk,
|
||||||
config
|
config
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -638,6 +623,7 @@ impl Uart {
|
|||||||
_uart: Uart,
|
_uart: Uart,
|
||||||
_tx_pin: Tx,
|
_tx_pin: Tx,
|
||||||
_rx_pin: Rx,
|
_rx_pin: Rx,
|
||||||
|
ref_clk: Hertz,
|
||||||
config: Config,
|
config: Config,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self::new_internal(
|
Self::new_internal(
|
||||||
@@ -647,6 +633,7 @@ impl Uart {
|
|||||||
Tx::FUNC_SEL,
|
Tx::FUNC_SEL,
|
||||||
Rx::ID,
|
Rx::ID,
|
||||||
Rx::FUNC_SEL,
|
Rx::FUNC_SEL,
|
||||||
|
ref_clk,
|
||||||
config
|
config
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -663,6 +650,7 @@ impl Uart {
|
|||||||
_uart: Uart,
|
_uart: Uart,
|
||||||
_tx_pin: Tx,
|
_tx_pin: Tx,
|
||||||
_rx_pin: Rx,
|
_rx_pin: Rx,
|
||||||
|
ref_clk: Hertz,
|
||||||
config: Config,
|
config: Config,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self::new_internal(
|
Self::new_internal(
|
||||||
@@ -672,6 +660,7 @@ impl Uart {
|
|||||||
Tx::FUNC_SEL,
|
Tx::FUNC_SEL,
|
||||||
Rx::ID,
|
Rx::ID,
|
||||||
Rx::FUNC_SEL,
|
Rx::FUNC_SEL,
|
||||||
|
ref_clk,
|
||||||
config
|
config
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -688,6 +677,7 @@ impl Uart {
|
|||||||
_uart: Uart,
|
_uart: Uart,
|
||||||
_tx_pin: Tx,
|
_tx_pin: Tx,
|
||||||
_rx_pin: Rx,
|
_rx_pin: Rx,
|
||||||
|
ref_clk: Hertz,
|
||||||
config: Config,
|
config: Config,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self::new_internal(
|
Self::new_internal(
|
||||||
@@ -697,6 +687,7 @@ impl Uart {
|
|||||||
Tx::FUNC_SEL,
|
Tx::FUNC_SEL,
|
||||||
Rx::ID,
|
Rx::ID,
|
||||||
Rx::FUNC_SEL,
|
Rx::FUNC_SEL,
|
||||||
|
ref_clk,
|
||||||
config
|
config
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -711,6 +702,7 @@ impl Uart {
|
|||||||
tx_func_sel: FunctionSelect,
|
tx_func_sel: FunctionSelect,
|
||||||
rx_pin_id: DynPinId,
|
rx_pin_id: DynPinId,
|
||||||
rx_func_sel: FunctionSelect,
|
rx_func_sel: FunctionSelect,
|
||||||
|
ref_clk: Hertz,
|
||||||
config: Config,
|
config: Config,
|
||||||
#[cfg(feature = "vor1x")] opt_irq_cfg: Option<InterruptConfig>,
|
#[cfg(feature = "vor1x")] opt_irq_cfg: Option<InterruptConfig>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
@@ -719,10 +711,23 @@ impl Uart {
|
|||||||
enable_peripheral_clock(periph_sel);
|
enable_peripheral_clock(periph_sel);
|
||||||
|
|
||||||
let mut reg_block = regs::Uart::new_mmio(uart_bank);
|
let mut reg_block = regs::Uart::new_mmio(uart_bank);
|
||||||
|
let baud_multiplier = match config.baud8 {
|
||||||
|
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 = ((ref_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 = ref_clk.raw() as f32 / (config.baudrate.raw() * baud_multiplier) as f32;
|
||||||
|
let integer_part = x as u32;
|
||||||
reg_block.write_clkscale(
|
reg_block.write_clkscale(
|
||||||
ClockScale::builder()
|
ClockScale::builder()
|
||||||
.with_int(config.clock_config.div)
|
.with_int(u18::new(integer_part))
|
||||||
.with_frac(config.clock_config.frac)
|
.with_frac(u6::new(frac as u8))
|
||||||
.build(),
|
.build(),
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -733,7 +738,7 @@ impl Uart {
|
|||||||
};
|
};
|
||||||
reg_block.write_ctrl(
|
reg_block.write_ctrl(
|
||||||
Control::builder()
|
Control::builder()
|
||||||
.with_baud8(config.clock_config.baud_mode == BaudMode::_8)
|
.with_baud8(config.baud8)
|
||||||
.with_auto_rts(false)
|
.with_auto_rts(false)
|
||||||
.with_def_rts(false)
|
.with_def_rts(false)
|
||||||
.with_auto_cts(false)
|
.with_auto_cts(false)
|
||||||
|
|||||||
Reference in New Issue
Block a user