Compare commits

...

43 Commits
v0.3.1 ... main

Author SHA1 Message Date
2db345af9f prep patch v0.5.2
All checks were successful
Rust/va108xx-hal/pipeline/head This commit looks good
2024-06-16 16:08:15 +02:00
09fd0d2aad
that should do the job
All checks were successful
Rust/va108xx-hal/pipeline/head This commit looks good
2023-01-11 23:15:48 +01:00
b776bd2823
clippy: remove unnecessary casts
Some checks failed
Rust/va108xx-hal/pipeline/head There was a failure building this commit
2023-01-11 00:41:07 +01:00
6131458a13 Merge branch 'main' of https://egit.irs.uni-stuttgart.de/rust/va108xx-hal
Some checks failed
Rust/va108xx-hal/pipeline/head There was a failure building this commit
2023-01-11 00:39:24 +01:00
2b8a8dc7c8
update Jenkins CI, add docs build 2023-01-11 00:39:12 +01:00
e9f1294572
clippy did not find this..
All checks were successful
Rust/va108xx-hal/pipeline/head This commit looks good
2022-09-14 11:19:19 +02:00
1476f4eebe
bump some dependencies
All checks were successful
Rust/va108xx-hal/pipeline/head This commit looks good
2022-09-13 10:57:07 +02:00
0ee53c70d5
add Eq auto-derives
All checks were successful
Rust/va108xx-hal/pipeline/head This commit looks good
2022-09-13 10:47:11 +02:00
833567037c
typo fix
All checks were successful
Rust/va108xx-hal/pipeline/head This commit looks good
2022-06-18 22:22:56 +02:00
38b9625773
update badge link
All checks were successful
Rust/va108xx-hal/pipeline/head This commit looks good
2022-06-18 22:21:48 +02:00
49b72d683f
update changelog and manifest file for v0.5.1
All checks were successful
Rust/va108xx-hal/pipeline/head This commit looks good
2022-06-18 22:12:58 +02:00
21c44e6327
update dependencies and add category 2022-06-18 22:06:49 +02:00
147c57defb
run cargo fmt
All checks were successful
Rust/va108xx-hal/pipeline/head This commit looks good
2022-05-02 16:52:31 +02:00
5cbbb53094 some minor improvements
All checks were successful
Rust/va108xx-hal/pipeline/head This commit looks good
- Docs updated, internal architecture improvements
2021-12-21 00:30:28 +01:00
491ef3ce09
update example documentation
All checks were successful
Rust/va108xx-hal/pipeline/head This commit looks good
2021-12-21 00:06:50 +01:00
e3cdd21b41
added link to new example
All checks were successful
Rust/va108xx-hal/pipeline/head This commit looks good
2021-12-20 23:51:37 +01:00
482a725ef7 Merge pull request 'IRQ Handling Update and UART IRQ Handler' (#5) from irq-handling-uart-update into main
All checks were successful
Rust/va108xx-hal/pipeline/head This commit looks good
Reviewed-on: #5
2021-12-20 23:49:07 +01:00
9f5a31c5b6
small changelog update
Some checks are pending
Rust/va108xx-hal/pipeline/head Build started...
Rust/va108xx-hal/pipeline/pr-main Build started...
2021-12-20 23:48:50 +01:00
5cbec366bc added Arduino Due test script
All checks were successful
Rust/va108xx-hal/pipeline/pr-main This commit looks good
Rust/va108xx-hal/pipeline/head This commit looks good
2021-12-20 23:40:15 +01:00
9a5c9ac53c update changelog and manifest
All checks were successful
Rust/va108xx-hal/pipeline/head This commit looks good
Rust/va108xx-hal/pipeline/pr-main This commit looks good
- Clippy fixes
2021-12-20 11:39:30 +01:00
a8b484d66f UART reception and echo reply now working
All checks were successful
Rust/va108xx-hal/pipeline/head This commit looks good
- Important bugfix in UART: use `modify` instead of `write` when enabling
  or disabling TX or RX
- Extend RTIC example application. Reply handling is dispatched to lower
  priority interrupt
2021-12-20 11:25:00 +01:00
d458a81635
RTIC additions
All checks were successful
Rust/va108xx-hal/pipeline/head This commit looks good
- Use RTIC in UART IRQ example
- Add RTIC template
2021-12-19 14:38:11 +01:00
d5b12c8343
some fixes for manifest file
All checks were successful
Rust/va108xx-hal/pipeline/head This commit looks good
2021-12-19 14:25:15 +01:00
f376a43f41
Major refactoring
Some checks failed
Rust/va108xx-hal/pipeline/head There was a failure building this commit
- Improved IRQ handling, which makes most unsafe unmask operations
  in user code absolete
- Add first UART RX handlers which use an IRQ
2021-12-19 14:18:10 +01:00
dc2426a905
make clock enable function inline
All checks were successful
Rust/va108xx-hal/pipeline/head This commit looks good
2021-12-18 15:48:14 +01:00
de607b1950
small fix for CI/CD
All checks were successful
Rust/va108xx-hal/pipeline/head This commit looks good
2021-12-18 14:41:56 +01:00
c954fd185d
update changelog, bump revision
Some checks failed
Rust/va108xx-hal/pipeline/head There was a failure building this commit
2021-12-18 14:34:37 +01:00
732f3e3bc8
use HAL delay instead of bare metal delay 2021-12-18 14:31:44 +01:00
bdbe666a2c
applied cargo fmt
Some checks failed
Rust/va108xx-hal/pipeline/head There was a failure building this commit
2021-12-17 10:36:05 +01:00
b9d4d7214c Merge branch 'main' of https://egit.irs.uni-stuttgart.de/rust/va108xx-hal
Some checks failed
Rust/va108xx-hal/pipeline/head There was a failure building this commit
2021-12-16 11:26:53 +01:00
0bc7e0f341
new blinky example, cargo.toml update 2021-12-16 11:26:26 +01:00
59463fbaba
try without use-cross in CI
All checks were successful
Rust/va108xx-hal/pipeline/head This commit looks good
2021-12-13 00:04:09 +01:00
439e1d43e7
update link 2021-12-12 23:51:47 +01:00
2abf35bb6e
simplified ci files
All checks were successful
Rust/va108xx-hal/pipeline/head This commit looks good
2021-12-12 23:49:44 +01:00
a1c0fb90e0
update example link
All checks were successful
Rust/va108xx-hal/pipeline/head This commit looks good
2021-12-12 23:04:21 +01:00
87b0180e6f Merge pull request 'HAL update' (#4) from mueller/hal-update into main
All checks were successful
Rust/va108xx-hal/pipeline/head This commit looks good
Reviewed-on: #4
2021-12-12 14:39:13 +01:00
f39863e59f
bump version
All checks were successful
Rust/va108xx-hal/pipeline/head This commit looks good
Rust/va108xx-hal/pipeline/pr-main This commit looks good
2021-12-12 14:35:15 +01:00
fb158caf6e
HAL update
All checks were successful
Rust/va108xx-hal/pipeline/head This commit looks good
Rust/va108xx-hal/pipeline/pr-main This commit looks good
- SPI: Clear TX and RX FIFO for transfers
- Added `port_mux` function to manually select function for pins
2021-12-12 14:21:49 +01:00
3fc8ce519a Merge pull request 'SPI Bugfix' (#3) from mueller/spi-bugfix into main
All checks were successful
Rust/va108xx-hal/pipeline/head This commit looks good
Reviewed-on: #3
2021-12-11 17:53:11 +01:00
2ad405d325
update changelog, bump to v0.4.1
Some checks are pending
Rust/va108xx-hal/pipeline/pr-main This commit looks good
Rust/va108xx-hal/pipeline/head Build started...
2021-12-11 17:46:05 +01:00
063a7a56e5
init blockmode was not set 2021-12-11 17:45:06 +01:00
1db363fe1a Merge pull request 'Replaced Hertz by impl Into<Hertz> completely' (#2) from mueller/impl-into-hertz into main
All checks were successful
Rust/va108xx-hal/pipeline/head This commit looks good
Reviewed-on: #2
2021-12-09 23:28:58 +01:00
659b7e8f27
Replaced Hertz by impl Into<Hertz> completely
Some checks are pending
Rust/va108xx-hal/pipeline/head This commit looks good
Rust/va108xx-hal/pipeline/pr-main Build started...
2021-12-09 23:19:21 +01:00
34 changed files with 1385 additions and 274 deletions

View File

@ -16,14 +16,11 @@ jobs:
override: true
- uses: actions-rs/cargo@v1
with:
use-cross: true
command: check
args: --target thumbv6m-none-eabi
- uses: actions-rs/cargo@v1
with:
use-cross: true
command: check
args: --examples --target thumbv6m-none-eabi
args: --examples
fmt:
name: Rustfmt
@ -55,9 +52,8 @@ jobs:
- run: rustup component add clippy
- uses: actions-rs/cargo@v1
with:
use-cross: true
command: clippy
args: --target thumbv6m-none-eabi -- -D warnings
args: -- -D warnings
ci:
if: ${{ success() }}

View File

@ -6,6 +6,65 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](http://keepachangelog.com/)
and this project adheres to [Semantic Versioning](http://semver.org/).
## [v0.5.2] 2024-06-16
## Fixed
- Replaced usage to `ptr::write_volatile` in UART module which is denied on more recent Rust
compilers.
## [v0.5.1]
### Changes
- Updated dependencies:
- `cortex-m-rtic` (dev-depencency) to 1.1.2
- `once_cell` to 1.12.0
- Other dependencies: Only revision has changed
## [v0.5.0]
### Added
- Reactored IRQ handling, so that `unmask` operations can be moved to HAL
- Added UART IRQ handler. Right now, can only perform reception, TX still needs to be done in
a blocking manner
- Added RTIC template and RTIC UART IRQ application
### Fixed
- Bugfix in UART code where RX and TX could not be enabled or disabled independently
## [v0.4.3]
- Various smaller fixes for READMEs, update of links in documentation
- Simplified CI for github, do not use `cross`
- New `blinky-pac` example
- Use HAL delay in `blinky` example
## [v0.4.2]
### Added
- `port_mux` function to set pin function select manually
### Changed
- Clear TX and RX FIFO in SPI transfer function
## [v0.4.1]
### Fixed
- Initial blockmode setting was not set in SPI constructor
## [v0.4.0]
### Changed
- Replaced `Hertz` by `impl Into<Hertz>` completely and removed
`+ Copy` where not necessary
## [v0.3.1]
- Updated all links to point to new repository

View File

@ -1,6 +1,6 @@
[package]
name = "va108xx-hal"
version = "0.3.1"
version = "0.5.2"
authors = ["Robin Mueller <muellerr@irs.uni-stuttgart.de>"]
edition = "2021"
description = "HAL for the Vorago VA108xx family of microcontrollers"
@ -8,38 +8,58 @@ homepage = "https://egit.irs.uni-stuttgart.de/rust/va108xx-hal"
repository = "https://egit.irs.uni-stuttgart.de/rust/va108xx-hal"
license = "Apache-2.0"
keywords = ["no-std", "hal", "cortex-m", "vorago", "va108xx"]
categories = ["embedded", "no-std", "hardware-support"]
categories = ["aerospace", "embedded", "no-std", "hardware-support"]
[dependencies]
va108xx = "0.2.4"
cortex-m = "0.7"
cortex-m-rt = "0.7"
nb = "1"
paste = "1.0"
embedded-hal = { features = ["unproven"], version = "0.2.6" }
void = { version = "1.0", default-features = false }
once_cell = { version = "1.8.0", default-features = false }
libm = "0.2.1"
libm = "0.2"
[dependencies.va108xx]
version = "0.2.4"
[dependencies.embedded-hal]
version = "0.2.7"
features = ["unproven"]
[dependencies.void]
version = "1.0"
default-features = false
[dependencies.once_cell]
version = "1.14"
default-features = false
[features]
rt = ["va108xx/rt"]
[dev-dependencies]
panic-rtt-target = { version = "0.1", features = ["cortex-m"] }
rtt-target = { version = "0.3", features = ["cortex-m"] }
cortex-m-rtic = "1.1.2"
panic-halt = "0.2"
[dev-dependencies.rtt-target]
version = "0.3"
features = ["cortex-m"]
[dev-dependencies.panic-rtt-target]
version = "0.1"
features = ["cortex-m"]
[profile.dev]
debug = true
lto = false
[profile.release]
lto = true
# Problematic because RTT won't work
lto = false
debug = true
opt-level = "s"
# Commented until named-profiles feature is stabilized
# [profile.release-lto]
# inherits = "release"
# lto = true
[[example]]
name = "timer-ticks"
required-features = ["rt"]
@ -49,9 +69,9 @@ name = "tests"
required-features = ["rt"]
[[example]]
name = "pwm"
name = "cascade"
required-features = ["rt"]
[[example]]
name = "cascade"
name = "uart-irq-rtic"
required-features = ["rt"]

View File

@ -1,5 +1,5 @@
[![Crates.io](https://img.shields.io/crates/v/va108xx-hal)](https://crates.io/crates/va108xx-hal)
[![ci](https://github.com/robamu-org/va108xx-hal-rs/actions/workflows/ci.yml/badge.svg)](https://github.com/robamu-org/va108xx-hal-rs/actions/workflows/ci.yml)
[![ci](https://github.com/us-irs/va108xx-hal-rs/actions/workflows/ci.yml/badge.svg)](https://github.com/us-irs/va108xx-hal-rs/actions/workflows/ci.yml)
[![docs.rs](https://img.shields.io/docsrs/va108xx-hal)](https://docs.rs/va108xx-hal)
# HAL for the Vorago VA108xx MCU family
@ -63,7 +63,7 @@ is contained within the
1. Set up your Rust cross-compiler if you have not done so yet. See more in the [build chapter](#Building)
2. Create a new binary crate with `cargo init`
3. To ensure that `cargo build` cross-compiles, it is recommended to create a `cargo/config.toml`
3. To ensure that `cargo build` cross-compiles, it is recommended to create a `.cargo/config.toml`
file. A sample `.cargo/config.toml` file is provided in this repository as well
4. Copy the `memory.x` file into your project. This file contains information required by the linker.
5. Copy the `blinky.rs` file to the `src/main.rs` file in your binary crate
@ -85,4 +85,4 @@ is contained within the
7. Flashing the board might work differently for different boards and there is usually
more than one way. You can find example instructions for the REB1 development board
[here](https://github.com/robamu-org/vorago-reb1-rs).
[here](https://egit.irs.uni-stuttgart.de/rust/vorago-reb1).

View File

@ -7,5 +7,7 @@ RUN apt-get --yes upgrade
# tzdata is a dependency, won't install otherwise
ARG DEBIAN_FRONTEND=noninteractive
RUN rustup target add thumbv6m-none-eabi && \
RUN rustup install nightly && \
rustup target add thumbv6m-none-eabi && \
rustup +nightly target add thumbv6m-none-eabi && \
rustup component add rustfmt clippy

View File

@ -1,47 +1,34 @@
pipeline {
agent any
agent {
dockerfile {
dir 'automation'
reuseNode true
}
}
stages {
stage('Clippy') {
agent {
dockerfile {
dir 'automation'
reuseNode true
}
}
steps {
sh 'cargo clippy'
}
}
stage('Rustfmt') {
agent {
dockerfile {
dir 'automation'
reuseNode true
}
}
steps {
sh 'cargo fmt'
}
}
stage('Check') {
agent {
dockerfile {
dir 'automation'
reuseNode true
}
stage('Docs') {
steps {
sh 'cargo +nightly doc'
}
}
stage('Check') {
steps {
sh 'cargo check --target thumbv6m-none-eabi'
}
}
stage('Check Examples') {
agent {
dockerfile {
dir 'automation'
reuseNode true
}
}
steps {
sh 'cargo check --target thumbv6m-none-eabi --examples'
}

47
examples/blinky-pac.rs Normal file
View File

@ -0,0 +1,47 @@
//! Blinky examples using only the PAC
//!
//! Additional note on LEDs:
//! Pulling the GPIOs low makes the LEDs blink. See REB1
//! schematic for more details.
#![no_main]
#![no_std]
use cortex_m_rt::entry;
use panic_halt as _;
use va108xx as pac;
// REB LED pin definitions. All on port A
const LED_D2: u32 = 1 << 10;
const LED_D3: u32 = 1 << 7;
const LED_D4: u32 = 1 << 6;
#[entry]
fn main() -> ! {
let dp = pac::Peripherals::take().unwrap();
// Enable all peripheral clocks
dp.SYSCONFIG
.peripheral_clk_enable
.modify(|_, w| unsafe { w.bits(0xffffffff) });
dp.PORTA
.dir()
.modify(|_, w| unsafe { w.bits(LED_D2 | LED_D3 | LED_D4) });
dp.PORTA
.datamask()
.modify(|_, w| unsafe { w.bits(LED_D2 | LED_D3 | LED_D4) });
for _ in 0..10 {
dp.PORTA
.clrout()
.write(|w| unsafe { w.bits(LED_D2 | LED_D3 | LED_D4) });
cortex_m::asm::delay(5_000_000);
dp.PORTA
.setout()
.write(|w| unsafe { w.bits(LED_D2 | LED_D3 | LED_D4) });
cortex_m::asm::delay(5_000_000);
}
loop {
dp.PORTA
.togout()
.write(|w| unsafe { w.bits(LED_D2 | LED_D3 | LED_D4) });
cortex_m::asm::delay(25_000_000);
}
}

View File

@ -9,7 +9,7 @@
use cortex_m_rt::entry;
use embedded_hal::digital::v2::ToggleableOutputPin;
use panic_halt as _;
use va108xx_hal::{gpio::PinsA, pac, prelude::*};
use va108xx_hal::{gpio::PinsA, pac, prelude::*, timer::CountDownTimer};
#[entry]
fn main() -> ! {
@ -18,22 +18,23 @@ fn main() -> ! {
let mut led1 = porta.pa10.into_push_pull_output();
let mut led2 = porta.pa7.into_push_pull_output();
let mut led3 = porta.pa6.into_push_pull_output();
let mut delay = CountDownTimer::new(&mut dp.SYSCONFIG, 50.mhz(), dp.TIM0);
for _ in 0..10 {
led1.set_low().ok();
led2.set_low().ok();
led3.set_low().ok();
cortex_m::asm::delay(5_000_000);
delay.delay_ms(200_u16);
led1.set_high().ok();
led2.set_high().ok();
led3.set_high().ok();
cortex_m::asm::delay(5_000_000);
delay.delay_ms(200_u16);
}
loop {
led1.toggle().ok();
cortex_m::asm::delay(5_000_000);
delay.delay_ms(200_u16);
led2.toggle().ok();
cortex_m::asm::delay(5_000_000);
delay.delay_ms(200_u16);
led3.toggle().ok();
cortex_m::asm::delay(5_000_000);
delay.delay_ms(200_u16);
}
}

View File

@ -14,8 +14,8 @@ use va108xx_hal::{
pac::{self, interrupt, TIM4, TIM5},
prelude::*,
timer::{
default_ms_irq_handler, set_up_ms_timer, CascadeCtrl, CascadeSource, CountDownTimer, Delay,
Event,
default_ms_irq_handler, set_up_ms_delay_provider, CascadeCtrl, CascadeSource,
CountDownTimer, Event, IrqCfg,
},
};
@ -28,23 +28,16 @@ fn main() -> ! {
rprintln!("-- VA108xx Cascade example application--");
let mut dp = pac::Peripherals::take().unwrap();
let timer = set_up_ms_timer(
&mut dp.SYSCONFIG,
&mut dp.IRQSEL,
50.mhz().into(),
dp.TIM0,
pac::Interrupt::OC0,
);
let mut delay = Delay::new(timer);
let mut delay = set_up_ms_delay_provider(&mut dp.SYSCONFIG, 50.mhz(), dp.TIM0);
// Will be started periodically to trigger a cascade
let mut cascade_triggerer =
CountDownTimer::new(&mut dp.SYSCONFIG, 50.mhz(), dp.TIM3).auto_disable(true);
cascade_triggerer.listen(
Event::TimeOut,
&mut dp.SYSCONFIG,
&mut dp.IRQSEL,
va108xx::Interrupt::OC1,
IrqCfg::new(va108xx::Interrupt::OC1, true, false),
Some(&mut dp.IRQSEL),
Some(&mut dp.SYSCONFIG),
);
// First target for cascade
@ -63,9 +56,9 @@ fn main() -> ! {
// the timer expires
cascade_target_1.listen(
Event::TimeOut,
&mut dp.SYSCONFIG,
&mut dp.IRQSEL,
va108xx::Interrupt::OC2,
IrqCfg::new(va108xx::Interrupt::OC2, true, false),
Some(&mut dp.IRQSEL),
Some(&mut dp.SYSCONFIG),
);
// The counter will only activate when the cascade signal is coming in so
// it is okay to call start here to set the reset value
@ -89,9 +82,9 @@ fn main() -> ! {
// the timer expires
cascade_target_2.listen(
Event::TimeOut,
&mut dp.SYSCONFIG,
&mut dp.IRQSEL,
va108xx::Interrupt::OC3,
IrqCfg::new(va108xx::Interrupt::OC3, true, false),
Some(&mut dp.IRQSEL),
Some(&mut dp.SYSCONFIG),
);
// The counter will only activate when the cascade signal is coming in so
// it is okay to call start here to set the reset value
@ -112,7 +105,7 @@ fn main() -> ! {
loop {
rprintln!("-- Triggering cascade in 0.5 seconds --");
cascade_triggerer.start(2.hz());
delay.delay_ms(5000);
delay.delay_ms(5000_u16);
}
}

View File

@ -8,10 +8,10 @@ use panic_rtt_target as _;
use rtt_target::{rprintln, rtt_init_print};
use va108xx_hal::{
gpio::PinsA,
pac::{self, interrupt},
pac,
prelude::*,
pwm::{self, get_duty_from_percent, ReducedPwmPin, PWMA, PWMB},
timer::{default_ms_irq_handler, set_up_ms_timer, Delay},
timer::set_up_ms_delay_provider,
};
#[entry]
@ -26,17 +26,7 @@ fn main() -> ! {
&mut dp.SYSCONFIG,
10.hz(),
);
let timer = set_up_ms_timer(
&mut dp.SYSCONFIG,
&mut dp.IRQSEL,
50.mhz().into(),
dp.TIM0,
pac::Interrupt::OC0,
);
let mut delay = Delay::new(timer);
unsafe {
cortex_m::peripheral::NVIC::unmask(pac::Interrupt::OC0);
}
let mut delay = set_up_ms_delay_provider(&mut dp.SYSCONFIG, 50.mhz(), dp.TIM0);
let mut current_duty_cycle = 0.0;
PwmPin::set_duty(&mut pwm, get_duty_from_percent(current_duty_cycle));
PwmPin::enable(&mut pwm);
@ -46,7 +36,7 @@ fn main() -> ! {
loop {
// Increase duty cycle continuously
while current_duty_cycle < 1.0 {
delay.delay_ms(200);
delay.delay_ms(200_u16);
current_duty_cycle += 0.02;
PwmPin::set_duty(&mut reduced_pin, get_duty_from_percent(current_duty_cycle));
}
@ -60,7 +50,7 @@ fn main() -> ! {
pwmb.set_pwmb_lower_limit(get_duty_from_percent(lower_limit));
pwmb.set_pwmb_upper_limit(get_duty_from_percent(upper_limit));
while lower_limit < 0.5 {
delay.delay_ms(200);
delay.delay_ms(200_u16);
lower_limit += 0.01;
upper_limit -= 0.01;
pwmb.set_pwmb_lower_limit(get_duty_from_percent(lower_limit));
@ -71,8 +61,3 @@ fn main() -> ! {
reduced_pin = ReducedPwmPin::<PWMA>::from(pwmb);
}
}
#[interrupt]
fn OC0() {
default_ms_irq_handler()
}

29
examples/rtic-empty.rs Normal file
View File

@ -0,0 +1,29 @@
//! Empty RTIC project template
#![no_main]
#![no_std]
#[rtic::app(device = pac)]
mod app {
use panic_rtt_target as _;
use rtt_target::{rprintln, rtt_init_default};
use va108xx_hal::pac;
#[local]
struct Local {}
#[shared]
struct Shared {}
#[init]
fn init(_ctx: init::Context) -> (Shared, Local, init::Monotonics) {
rtt_init_default!();
rprintln!("-- Vorago RTIC template --");
(Shared {}, Local {}, init::Monotonics())
}
// `shared` cannot be accessed from this context
#[idle]
fn idle(_cx: idle::Context) -> ! {
loop {}
}
}

View File

@ -114,12 +114,8 @@ fn main() -> ! {
match SPI_BUS_SEL {
SpiBusSelect::SpiAPortA | SpiBusSelect::SpiAPortB => {
if let Some(ref mut spi) = *spia_ref.borrow_mut() {
let transfer_cfg = TransferConfig::new_no_hw_cs(
SPI_SPEED_KHZ.khz().into(),
SPI_MODE,
BLOCKMODE,
false,
);
let transfer_cfg =
TransferConfig::new_no_hw_cs(SPI_SPEED_KHZ.khz(), SPI_MODE, BLOCKMODE, false);
spi.cfg_transfer(&transfer_cfg);
}
}
@ -127,7 +123,7 @@ fn main() -> ! {
if let Some(ref mut spi) = *spib_ref.borrow_mut() {
let hw_cs_pin = pinsb.pb2.into_funsel_1();
let transfer_cfg = TransferConfig::new(
SPI_SPEED_KHZ.khz().into(),
SPI_SPEED_KHZ.khz(),
SPI_MODE,
Some(hw_cs_pin),
BLOCKMODE,

View File

@ -14,7 +14,7 @@ use va108xx_hal::{
pac::{self, interrupt},
prelude::*,
time::Hertz,
timer::{default_ms_irq_handler, set_up_ms_timer, CountDownTimer, Delay},
timer::{default_ms_irq_handler, set_up_ms_timer, CountDownTimer, Delay, IrqCfg},
};
#[allow(dead_code)]
@ -146,15 +146,12 @@ fn main() -> ! {
}
TestCase::DelayMs => {
let ms_timer = set_up_ms_timer(
IrqCfg::new(pac::Interrupt::OC0, true, true),
&mut dp.SYSCONFIG,
&mut dp.IRQSEL,
50.mhz().into(),
Some(&mut dp.IRQSEL),
50.mhz(),
dp.TIM0,
pac::Interrupt::OC0,
);
unsafe {
cortex_m::peripheral::NVIC::unmask(pac::Interrupt::OC0);
}
let mut delay = Delay::new(ms_timer);
for _ in 0..5 {
led1.toggle().ok();
@ -163,7 +160,7 @@ fn main() -> ! {
delay.delay_ms(500);
}
let mut delay_timer = CountDownTimer::new(&mut dp.SYSCONFIG, 50.mhz().into(), dp.TIM1);
let mut delay_timer = CountDownTimer::new(&mut dp.SYSCONFIG, 50.mhz(), dp.TIM1);
let mut pa0 = pinsa.pa0.into_push_pull_output();
for _ in 0..5 {
led1.toggle().ok();

View File

@ -12,7 +12,7 @@ use va108xx_hal::{
pac::{self, interrupt},
prelude::*,
time::Hertz,
timer::{default_ms_irq_handler, set_up_ms_timer, CountDownTimer, Event, MS_COUNTER},
timer::{default_ms_irq_handler, set_up_ms_timer, CountDownTimer, Event, IrqCfg, MS_COUNTER},
};
#[allow(dead_code)]
@ -65,22 +65,21 @@ fn main() -> ! {
}
LibType::Hal => {
set_up_ms_timer(
IrqCfg::new(interrupt::OC0, true, true),
&mut dp.SYSCONFIG,
&mut dp.IRQSEL,
50.mhz().into(),
Some(&mut dp.IRQSEL),
50.mhz(),
dp.TIM0,
interrupt::OC0,
);
let mut second_timer =
CountDownTimer::new(&mut dp.SYSCONFIG, get_sys_clock().unwrap(), dp.TIM1);
second_timer.listen(
Event::TimeOut,
&mut dp.SYSCONFIG,
&mut dp.IRQSEL,
interrupt::OC1,
IrqCfg::new(interrupt::OC1, true, true),
Some(&mut dp.IRQSEL),
Some(&mut dp.SYSCONFIG),
);
second_timer.start(1.hz());
unmask_irqs();
}
}
loop {

126
examples/uart-irq-rtic.rs Normal file
View File

@ -0,0 +1,126 @@
//! More complex UART application
//!
//! Uses the IRQ capabilities of the VA10820 peripheral and the RTIC framework to poll the UART in
//! a non-blocking way. You can send variably sized strings to the VA10820 which will be echoed
//! back to the sender.
//!
//! This script was tested with an Arduino Due. You can find the test script in the
//! [`/test/DueSerialTest`](https://egit.irs.uni-stuttgart.de/rust/va108xx-hal/src/branch/main/test/DueSerialTest)
//! folder.
#![no_main]
#![no_std]
#[rtic::app(device = pac, dispatchers = [OC4])]
mod app {
use core::fmt::Write;
use panic_rtt_target as _;
use rtt_target::{rprintln, rtt_init_default, set_print_channel};
use va108xx_hal::{
gpio::PinsB,
pac,
prelude::*,
uart::{self, IrqCfg, IrqResult, UartWithIrqBase},
};
#[local]
struct Local {}
#[shared]
struct Shared {
irq_uart: UartWithIrqBase<pac::UARTB>,
rx_buf: [u8; 64],
}
#[init]
fn init(ctx: init::Context) -> (Shared, Local, init::Monotonics) {
let channels = rtt_init_default!();
set_print_channel(channels.up.0);
rprintln!("-- VA108xx UART IRQ example application--");
let mut dp = ctx.device;
let gpiob = PinsB::new(&mut dp.SYSCONFIG, Some(dp.IOCONFIG), dp.PORTB);
let tx = gpiob.pb21.into_funsel_1();
let rx = gpiob.pb20.into_funsel_1();
let irq_cfg = IrqCfg::new(pac::interrupt::OC3, true, true);
let (mut irq_uart, _) = uart::Uart::uartb(
dp.UARTB,
(tx, rx),
115200.bps(),
&mut dp.SYSCONFIG,
50.mhz(),
)
.into_uart_with_irq(irq_cfg, Some(&mut dp.SYSCONFIG), Some(&mut dp.IRQSEL))
.downgrade();
irq_uart
.read_fixed_len_using_irq(64, true)
.expect("Read initialization failed");
let rx_buf: [u8; 64] = [0; 64];
(Shared { irq_uart, rx_buf }, Local {}, init::Monotonics())
}
// `shared` cannot be accessed from this context
#[idle]
fn idle(_cx: idle::Context) -> ! {
loop {}
}
#[task(
binds = OC3,
shared = [irq_uart, rx_buf],
local = [cnt: u32 = 0, result: IrqResult = IrqResult::new()],
priority = 4
)]
fn reception_task(cx: reception_task::Context) {
let result = cx.local.result;
let cnt: &mut u32 = cx.local.cnt;
let irq_uart = cx.shared.irq_uart;
let rx_buf = cx.shared.rx_buf;
let (completed, end_idx) = (irq_uart, rx_buf).lock(|irq_uart, rx_buf| {
match irq_uart.irq_handler(result, rx_buf) {
Ok(_) => {
if result.complete() {
// Initiate next transfer immediately
irq_uart
.read_fixed_len_using_irq(64, true)
.expect("Read operation init failed");
let mut end_idx = 0;
for idx in 0..rx_buf.len() {
if (rx_buf[idx] as char) == '\n' {
end_idx = idx;
break;
}
}
(true, end_idx)
} else {
(false, 0)
}
}
Err(e) => {
rprintln!("Reception Error {:?}", e);
(false, 0)
}
}
});
if completed {
rprintln!("Counter: {}", cnt);
reply_handler::spawn(result.bytes_read, end_idx, result.timeout()).unwrap();
}
*cnt += 1;
}
#[task(shared = [irq_uart, rx_buf], priority = 3)]
fn reply_handler(cx: reply_handler::Context, bytes_read: usize, end_idx: usize, timeout: bool) {
let irq_uart = cx.shared.irq_uart;
let rx_buf = cx.shared.rx_buf;
(irq_uart, rx_buf).lock(|irq_uart, rx_buf| {
rprintln!("Reception success, {} bytes read", bytes_read);
if timeout {
rprintln!("Timeout occured");
}
let string = core::str::from_utf8(&rx_buf[0..end_idx]).expect("Invalid string format");
rprintln!("Read string: {}", string);
writeln!(irq_uart.uart, "{}", string).expect("Sending reply failed");
});
}
}

View File

@ -25,7 +25,7 @@ fn main() -> ! {
(tx, rx),
115200.bps(),
&mut dp.SYSCONFIG,
50.mhz().into(),
50.mhz(),
);
let (mut tx, mut rx) = uartb.split();
writeln!(tx, "Hello World\r").unwrap();

View File

@ -11,7 +11,7 @@ static SYS_CLOCK: Mutex<OnceCell<Hertz>> = Mutex::new(OnceCell::new());
pub type PeripheralClocks = PeripheralSelect;
#[derive(Debug, PartialEq)]
#[derive(Debug, PartialEq, Eq)]
pub enum FilterClkSel {
SysClk = 0,
Clk1 = 1,
@ -50,12 +50,14 @@ pub fn set_clk_div_register(syscfg: &mut SYSCONFIG, clk_sel: FilterClkSel, div:
}
}
#[inline]
pub fn enable_peripheral_clock(syscfg: &mut SYSCONFIG, clock: PeripheralClocks) {
syscfg
.peripheral_clk_enable
.modify(|r, w| unsafe { w.bits(r.bits() | (1 << clock as u8)) });
}
#[inline]
pub fn disable_peripheral_clock(syscfg: &mut SYSCONFIG, clock: PeripheralClocks) {
syscfg
.peripheral_clk_enable

View File

@ -57,14 +57,17 @@
//! operation, the trait functions will return
//! [`InvalidPinType`](PinError::InvalidPinType).
use super::pins::{
common_reg_if_functions, FilterType, InterruptEdge, InterruptLevel, Pin, PinError, PinId,
PinMode, PinState,
use super::{
pins::{
common_reg_if_functions, FilterType, InterruptEdge, InterruptLevel, Pin, PinError, PinId,
PinMode, PinState,
},
reg::RegisterInterface,
};
use super::reg::RegisterInterface;
use crate::{
clock::FilterClkSel,
pac::{self, IRQSEL, SYSCONFIG},
pac::{IRQSEL, SYSCONFIG},
utility::{Funsel, IrqCfg},
};
use embedded_hal::digital::v2::{InputPin, OutputPin, ToggleableOutputPin};
use paste::paste;
@ -98,13 +101,7 @@ pub enum DynOutput {
ReadableOpenDrain,
}
/// Value-level `enum` for alternate peripheral function configurations
#[derive(PartialEq, Eq, Clone, Copy)]
pub enum DynAlternate {
Funsel1,
Funsel2,
Funsel3,
}
pub type DynAlternate = Funsel;
//==================================================================================================
// DynPinMode
@ -146,14 +143,14 @@ pub const DYN_ALT_FUNC_3: DynPinMode = DynPinMode::Alternate(DynAlternate::Funse
//==================================================================================================
/// Value-level `enum` for pin groups
#[derive(PartialEq, Clone, Copy)]
#[derive(PartialEq, Eq, Clone, Copy)]
pub enum DynGroup {
A,
B,
}
/// Value-level `struct` representing pin IDs
#[derive(PartialEq, Clone, Copy)]
#[derive(PartialEq, Eq, Clone, Copy)]
pub struct DynPinId {
pub group: DynGroup,
pub num: u8,
@ -347,14 +344,14 @@ impl DynPin {
pub fn interrupt_edge(
mut self,
edge_type: InterruptEdge,
irq_cfg: IrqCfg,
syscfg: Option<&mut SYSCONFIG>,
irqsel: &mut IRQSEL,
interrupt: pac::Interrupt,
irqsel: Option<&mut IRQSEL>,
) -> Result<Self, PinError> {
match self.mode {
DynPinMode::Input(_) | DynPinMode::Output(_) => {
self._irq_enb(syscfg, irqsel, interrupt);
self.regs.interrupt_edge(edge_type);
self.irq_enb(irq_cfg, syscfg, irqsel);
Ok(self)
}
_ => Err(PinError::InvalidPinType),
@ -364,14 +361,14 @@ impl DynPin {
pub fn interrupt_level(
mut self,
level_type: InterruptLevel,
irq_cfg: IrqCfg,
syscfg: Option<&mut SYSCONFIG>,
irqsel: &mut IRQSEL,
interrupt: crate::pac::Interrupt,
irqsel: Option<&mut IRQSEL>,
) -> Result<Self, PinError> {
match self.mode {
DynPinMode::Input(_) | DynPinMode::Output(_) => {
self._irq_enb(syscfg, irqsel, interrupt);
self.regs.interrupt_level(level_type);
self.irq_enb(irq_cfg, syscfg, irqsel);
Ok(self)
}
_ => Err(PinError::InvalidPinType),

View File

@ -92,8 +92,9 @@
use super::dynpins::{DynAlternate, DynGroup, DynInput, DynOutput, DynPinId, DynPinMode};
use super::reg::RegisterInterface;
use crate::{
pac::{self, IOCONFIG, IRQSEL, PORTA, PORTB, SYSCONFIG},
pac::{IOCONFIG, IRQSEL, PORTA, PORTB, SYSCONFIG},
typelevel::Is,
utility::IrqCfg,
Sealed,
};
use core::convert::Infallible;
@ -105,27 +106,27 @@ use paste::paste;
// Errors and Definitions
//==================================================================================================
#[derive(Debug, PartialEq)]
#[derive(Debug, PartialEq, Eq)]
pub enum InterruptEdge {
HighToLow,
LowToHigh,
BothEdges,
}
#[derive(Debug, PartialEq)]
#[derive(Debug, PartialEq, Eq)]
pub enum InterruptLevel {
Low = 0,
High = 1,
}
#[derive(Debug, PartialEq)]
#[derive(Debug, PartialEq, Eq)]
pub enum PinState {
Low = 0,
High = 1,
}
/// GPIO error type
#[derive(Debug, PartialEq)]
#[derive(Debug, PartialEq, Eq)]
pub enum PinError {
/// The pin did not have the correct ID or mode for the requested operation.
/// [`DynPin`](crate::gpio::DynPin)s are not tracked and verified at compile-time, so run-time
@ -181,7 +182,7 @@ pub struct Input<C: InputConfig> {
impl<C: InputConfig> Sealed for Input<C> {}
#[derive(Debug, PartialEq)]
#[derive(Debug, PartialEq, Eq)]
pub enum FilterType {
SystemClock = 0,
DirectInputWithSynchronization = 1,
@ -397,11 +398,11 @@ macro_rules! common_reg_if_functions {
self.regs.write_pin_masked(false)
}
fn _irq_enb(
fn irq_enb(
&mut self,
irq_cfg: crate::utility::IrqCfg,
syscfg: Option<&mut va108xx::SYSCONFIG>,
irqsel: &mut va108xx::IRQSEL,
interrupt: va108xx::Interrupt,
irqsel: Option<&mut va108xx::IRQSEL>,
) {
if syscfg.is_some() {
crate::clock::enable_peripheral_clock(
@ -410,15 +411,19 @@ macro_rules! common_reg_if_functions {
);
}
self.regs.enable_irq();
match self.regs.id().group {
// Set the correct interrupt number in the IRQSEL register
DynGroup::A => {
irqsel.porta[self.regs.id().num as usize]
.write(|w| unsafe { w.bits(interrupt as u32) });
}
DynGroup::B => {
irqsel.portb[self.regs.id().num as usize]
.write(|w| unsafe { w.bits(interrupt as u32) });
if let Some(irqsel) = irqsel {
if irq_cfg.route {
match self.regs.id().group {
// Set the correct interrupt number in the IRQSEL register
DynGroup::A => {
irqsel.porta[self.regs.id().num as usize]
.write(|w| unsafe { w.bits(irq_cfg.irq as u32) });
}
DynGroup::B => {
irqsel.portb[self.regs.id().num as usize]
.write(|w| unsafe { w.bits(irq_cfg.irq as u32) });
}
}
}
}
}
@ -577,24 +582,24 @@ impl<I: PinId, C: InputConfig> Pin<I, Input<C>> {
pub fn interrupt_edge(
mut self,
edge_type: InterruptEdge,
irq_cfg: IrqCfg,
syscfg: Option<&mut SYSCONFIG>,
irqsel: &mut IRQSEL,
interrupt: pac::Interrupt,
irqsel: Option<&mut IRQSEL>,
) -> Self {
self._irq_enb(syscfg, irqsel, interrupt);
self.regs.interrupt_edge(edge_type);
self.irq_enb(irq_cfg, syscfg, irqsel);
self
}
pub fn interrupt_level(
mut self,
level_type: InterruptLevel,
irq_cfg: IrqCfg,
syscfg: Option<&mut SYSCONFIG>,
irqsel: &mut IRQSEL,
interrupt: pac::Interrupt,
irqsel: Option<&mut IRQSEL>,
) -> Self {
self._irq_enb(syscfg, irqsel, interrupt);
self.regs.interrupt_level(level_type);
self.irq_enb(irq_cfg, syscfg, irqsel);
self
}
}
@ -622,24 +627,24 @@ impl<I: PinId, C: OutputConfig> Pin<I, Output<C>> {
pub fn interrupt_edge(
mut self,
edge_type: InterruptEdge,
irq_cfg: IrqCfg,
syscfg: Option<&mut SYSCONFIG>,
irqsel: &mut IRQSEL,
interrupt: pac::Interrupt,
irqsel: Option<&mut IRQSEL>,
) -> Self {
self._irq_enb(syscfg, irqsel, interrupt);
self.regs.interrupt_edge(edge_type);
self.irq_enb(irq_cfg, syscfg, irqsel);
self
}
pub fn interrupt_level(
mut self,
level_type: InterruptLevel,
irq_cfg: IrqCfg,
syscfg: Option<&mut SYSCONFIG>,
irqsel: &mut IRQSEL,
interrupt: pac::Interrupt,
irqsel: Option<&mut IRQSEL>,
) -> Self {
self._irq_enb(syscfg, irqsel, interrupt);
self.regs.interrupt_level(level_type);
self.irq_enb(irq_cfg, syscfg, irqsel);
self
}
}

View File

@ -60,18 +60,7 @@ impl From<DynPinMode> for ModeFields {
}
}
Alternate(config) => {
use dynpins::DynAlternate::*;
match config {
Funsel1 => {
fields.funsel = 1;
}
Funsel2 => {
fields.funsel = 2;
}
Funsel3 => {
fields.funsel = 3;
}
}
fields.funsel = config as u8;
}
}
fields

View File

@ -2,7 +2,7 @@
//!
//! ## Examples
//!
//! - [REB1 I2C temperature sensor example](https://github.com/robamu-org/vorago-reb1-rs/blob/main/examples/temp-sensor.rs)
//! - [REB1 I2C temperature sensor example](https://egit.irs.uni-stuttgart.de/rust/vorago-reb1/src/branch/main/examples/adt75-temp-sensor.rs)
use crate::{
clock::{enable_peripheral_clock, PeripheralClocks},
pac::{I2CA, I2CB, SYSCONFIG},
@ -18,13 +18,13 @@ pub use embedded_hal::blocking::i2c::{SevenBitAddress, TenBitAddress};
// Defintions
//==================================================================================================
#[derive(Debug, PartialEq, Copy, Clone)]
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
pub enum FifoEmptyMode {
Stall = 0,
EndTransaction = 1,
}
#[derive(Debug, PartialEq)]
#[derive(Debug, PartialEq, Eq)]
pub enum Error {
InvalidTimingParams,
ArbitrationLost,
@ -46,19 +46,19 @@ enum I2cCmd {
Cancel = 0b100,
}
#[derive(Debug, PartialEq, Copy, Clone)]
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
pub enum I2cSpeed {
Regular100khz = 0,
Fast400khz = 1,
}
#[derive(Debug, PartialEq)]
#[derive(Debug, PartialEq, Eq)]
pub enum I2cDirection {
Send = 0,
Read = 1,
}
#[derive(Debug, PartialEq, Copy, Clone)]
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
pub enum I2cAddress {
Regular(u8),
TenBit(u16),
@ -119,14 +119,14 @@ impl TimingCfg {
}
pub fn reg(&self) -> u32 {
((self.tbuf as u32) << 28
(self.tbuf as u32) << 28
| (self.thd_sta as u32) << 24
| (self.tsu_sta as u32) << 20
| (self.tsu_sto as u32) << 16
| (self.tlow as u32) << 12
| (self.thigh as u32) << 8
| (self.tf as u32) << 4
| (self.tr as u32)) as u32
| (self.tr as u32)
}
}
@ -231,7 +231,7 @@ macro_rules! i2c_base {
impl I2cBase<$I2CX> {
pub fn $i2cx(
i2c: $I2CX,
sys_clk: impl Into<Hertz> + Copy,
sys_clk: impl Into<Hertz>,
speed_mode: I2cSpeed,
ms_cfg: Option<&MasterConfig>,
sl_cfg: Option<&SlaveConfig>,
@ -740,7 +740,7 @@ macro_rules! i2c_slave {
fn $i2cx_slave(
i2c: $I2CX,
cfg: SlaveConfig,
sys_clk: impl Into<Hertz> + Copy,
sys_clk: impl Into<Hertz>,
speed_mode: I2cSpeed,
sys_cfg: Option<&mut SYSCONFIG>,
) -> Self {
@ -897,7 +897,7 @@ macro_rules! i2c_slave {
pub fn i2ca(
i2c: $I2CX,
cfg: SlaveConfig,
sys_clk: impl Into<Hertz> + Copy,
sys_clk: impl Into<Hertz>,
speed_mode: I2cSpeed,
sys_cfg: Option<&mut SYSCONFIG>,
) -> Result<Self, Error> {
@ -912,7 +912,7 @@ macro_rules! i2c_slave {
pub fn $i2cx(
i2c: $I2CX,
cfg: SlaveConfig,
sys_clk: impl Into<Hertz> + Copy,
sys_clk: impl Into<Hertz>,
speed_mode: I2cSpeed,
sys_cfg: Option<&mut SYSCONFIG>,
) -> Self {

View File

@ -25,7 +25,7 @@ use embedded_hal::{
// Defintions
//==================================================================================================
#[derive(Debug, PartialEq, Copy, Clone)]
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
pub enum HwChipSelectId {
Id0 = 0,
Id1 = 1,
@ -38,7 +38,7 @@ pub enum HwChipSelectId {
Invalid = 0xff,
}
#[derive(Debug, PartialEq, Copy, Clone)]
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
pub enum WordSize {
OneBit = 0x00,
FourBits = 0x03,
@ -218,9 +218,9 @@ pub struct ReducedTransferConfig {
}
impl TransferConfig<NoneT> {
pub fn new_no_hw_cs(spi_clk: Hertz, mode: Mode, blockmode: bool, sod: bool) -> Self {
pub fn new_no_hw_cs(spi_clk: impl Into<Hertz>, mode: Mode, blockmode: bool, sod: bool) -> Self {
TransferConfig {
spi_clk,
spi_clk: spi_clk.into(),
mode,
hw_cs: None,
sod,
@ -231,14 +231,14 @@ impl TransferConfig<NoneT> {
impl<HWCS: HwCs> TransferConfig<HWCS> {
pub fn new(
spi_clk: Hertz,
spi_clk: impl Into<Hertz>,
mode: Mode,
hw_cs: Option<HWCS>,
blockmode: bool,
sod: bool,
) -> Self {
TransferConfig {
spi_clk,
spi_clk: spi_clk.into(),
mode,
hw_cs,
sod,
@ -428,6 +428,7 @@ macro_rules! spi {
w.sod().bit(sod);
w.ms().bit(ms);
w.mdlycap().bit(mdlycap);
w.blockmode().bit(init_blockmode);
unsafe { w.ss().bits(ss) }
});
@ -452,7 +453,7 @@ macro_rules! spi {
}
#[inline]
pub fn cfg_clock(&mut self, spi_clk: Hertz) {
pub fn cfg_clock(&mut self, spi_clk: impl Into<Hertz>) {
self.spi_base.cfg_clock(spi_clk);
}
@ -482,8 +483,8 @@ macro_rules! spi {
impl<WORD: Word> SpiBase<$SPIX, WORD> {
#[inline]
pub fn cfg_clock(&mut self, spi_clk: Hertz) {
let clk_prescale = self.sys_clk.0 / (spi_clk.0 * (self.cfg.scrdv as u32 + 1));
pub fn cfg_clock(&mut self, spi_clk: impl Into<Hertz>) {
let clk_prescale = self.sys_clk.0 / (spi_clk.into().0 * (self.cfg.scrdv as u32 + 1));
self.spi
.clkprescale
.write(|w| unsafe { w.bits(clk_prescale) });
@ -503,6 +504,16 @@ macro_rules! spi {
});
}
#[inline]
pub fn clear_tx_fifo(&self) {
self.spi.fifo_clr.write(|w| w.txfifo().set_bit());
}
#[inline]
pub fn clear_rx_fifo(&self) {
self.spi.fifo_clr.write(|w| w.rxfifo().set_bit());
}
#[inline]
pub fn perid(&self) -> u32 {
self.spi.perid.read().bits()
@ -639,6 +650,9 @@ macro_rules! spi {
// FIFO has a depth of 16.
const FILL_DEPTH: usize = 12;
self.clear_tx_fifo();
self.clear_rx_fifo();
if self.blockmode {
self.spi.ctrl1.modify(|_, w| {
w.mtxpause().set_bit()

View File

@ -6,7 +6,7 @@
//! allowing it to be converted into frequencies.
/// Bits per second
#[derive(Clone, Copy, PartialEq, PartialOrd, Debug)]
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Debug)]
pub struct Bps(pub u32);
/// Hertz
@ -25,7 +25,7 @@ pub struct Bps(pub u32);
///
/// let freq = 60.hz();
/// ```
#[derive(Clone, Copy, PartialEq, PartialOrd, Debug)]
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Debug)]
pub struct Hertz(pub u32);
/// Kilohertz
@ -47,7 +47,7 @@ pub struct Hertz(pub u32);
///
/// let freq = 100.khz();
/// ```
#[derive(Clone, Copy, PartialEq, PartialOrd, Debug)]
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Debug)]
pub struct KiloHertz(pub u32);
/// Megahertz
@ -68,14 +68,14 @@ pub struct KiloHertz(pub u32);
///
/// let freq = 8.mhz();
/// ```
#[derive(Clone, Copy, PartialEq, PartialOrd, Debug)]
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Debug)]
pub struct MegaHertz(pub u32);
/// Time unit
#[derive(PartialEq, PartialOrd, Clone, Copy)]
#[derive(PartialEq, Eq, PartialOrd, Clone, Copy)]
pub struct MilliSeconds(pub u32);
#[derive(PartialEq, PartialOrd, Clone, Copy)]
#[derive(PartialEq, Eq, PartialOrd, Clone, Copy)]
pub struct MicroSeconds(pub u32);
/// Extension trait that adds convenience methods to the `u32` type

View File

@ -4,6 +4,7 @@
//!
//! - [MS and second tick implementation](https://egit.irs.uni-stuttgart.de/rust/va108xx-hal/src/branch/main/examples/timer-ticks.rs)
//! - [Cascade feature example](https://egit.irs.uni-stuttgart.de/rust/va108xx-hal/src/branch/main/examples/cascade.rs)
pub use crate::utility::IrqCfg;
use crate::{
clock::{enable_peripheral_clock, PeripheralClocks},
gpio::{
@ -20,6 +21,7 @@ use crate::{
private::Sealed,
time::Hertz,
timer,
utility::unmask_irq,
};
use core::cell::Cell;
use cortex_m::interrupt::Mutex;
@ -27,7 +29,7 @@ use embedded_hal::{
blocking::delay,
timer::{Cancel, CountDown, Periodic},
};
use va108xx::{Interrupt, IRQSEL, SYSCONFIG};
use va108xx::{IRQSEL, SYSCONFIG};
use void::Void;
const IRQ_DST_NONE: u32 = 0xffffffff;
@ -43,7 +45,7 @@ pub enum Event {
TimeOut,
}
#[derive(Default, Debug, PartialEq, Copy, Clone)]
#[derive(Default, Debug, PartialEq, Eq, Copy, Clone)]
pub struct CascadeCtrl {
/// Enable Cascade 0 signal active as a requirement for counting
pub enb_start_src_csd0: bool,
@ -72,7 +74,7 @@ pub struct CascadeCtrl {
pub trg_csd2: bool,
}
#[derive(Debug, PartialEq)]
#[derive(Debug, PartialEq, Eq)]
pub enum CascadeSel {
Csd0 = 0,
Csd1 = 1,
@ -80,7 +82,7 @@ pub enum CascadeSel {
}
/// The numbers are the base numbers for bundles like PORTA, PORTB or TIM
#[derive(Debug, PartialEq)]
#[derive(Debug, PartialEq, Eq)]
pub enum CascadeSource {
PortABase = 0,
PortBBase = 32,
@ -93,7 +95,7 @@ pub enum CascadeSource {
ClockDividerBase = 120,
}
#[derive(Debug, PartialEq)]
#[derive(Debug, PartialEq, Eq)]
pub enum TimerErrors {
Canceled,
/// Invalid input for Cascade source
@ -386,6 +388,7 @@ unsafe impl TimPinInterface for TimDynRegister {
pub struct CountDownTimer<TIM: ValidTim> {
tim: TimRegister<TIM>,
curr_freq: Hertz,
irq_cfg: Option<IrqCfg>,
sys_clk: Hertz,
rst_val: u32,
last_cnt: u32,
@ -482,6 +485,7 @@ impl<TIM: ValidTim> CountDownTimer<TIM> {
let cd_timer = CountDownTimer {
tim: unsafe { TimRegister::new(tim) },
sys_clk: sys_clk.into(),
irq_cfg: None,
rst_val: 0,
curr_freq: 0.hz(),
listening: false,
@ -491,21 +495,28 @@ impl<TIM: ValidTim> CountDownTimer<TIM> {
cd_timer
}
/// Listen for events. This also actives the IRQ in the IRQSEL register
/// for the provided interrupt. It also actives the peripheral clock for
/// IRQSEL
/// Listen for events. Depending on the IRQ configuration, this also activates the IRQ in the
/// IRQSEL peripheral for the provided interrupt and unmasks the interrupt
pub fn listen(
&mut self,
event: Event,
syscfg: &mut SYSCONFIG,
irqsel: &mut IRQSEL,
interrupt: Interrupt,
irq_cfg: IrqCfg,
irq_sel: Option<&mut IRQSEL>,
sys_cfg: Option<&mut SYSCONFIG>,
) {
match event {
Event::TimeOut => {
enable_peripheral_clock(syscfg, PeripheralClocks::Irqsel);
irqsel.tim[TIM::TIM_ID as usize].write(|w| unsafe { w.bits(interrupt as u32) });
self.enable_interrupt();
cortex_m::peripheral::NVIC::mask(irq_cfg.irq);
self.irq_cfg = Some(irq_cfg);
if irq_cfg.route {
if let Some(sys_cfg) = sys_cfg {
enable_peripheral_clock(sys_cfg, PeripheralClocks::Irqsel);
}
if let Some(irq_sel) = irq_sel {
irq_sel.tim[TIM::TIM_ID as usize]
.write(|w| unsafe { w.bits(irq_cfg.irq as u32) });
}
}
self.listening = true;
}
}
@ -554,6 +565,12 @@ impl<TIM: ValidTim> CountDownTimer<TIM> {
#[inline(always)]
pub fn enable(&mut self) {
self.tim.reg().ctrl.modify(|_, w| w.enable().set_bit());
if let Some(irq_cfg) = self.irq_cfg {
self.enable_interrupt();
if irq_cfg.enable {
unmask_irq(irq_cfg.irq);
}
}
}
#[inline(always)]
@ -720,19 +737,29 @@ impl<TIM: ValidTim> embedded_hal::blocking::delay::DelayMs<u8> for CountDownTime
// Set up a millisecond timer on TIM0. Please note that you still need to unmask the related IRQ
// and provide an IRQ handler yourself
pub fn set_up_ms_timer(
syscfg: &mut pac::SYSCONFIG,
irqsel: &mut pac::IRQSEL,
sys_clk: Hertz,
tim0: TIM0,
irq: pac::Interrupt,
) -> CountDownTimer<TIM0> {
let mut ms_timer = CountDownTimer::new(syscfg, sys_clk, tim0);
ms_timer.listen(timer::Event::TimeOut, syscfg, irqsel, irq);
pub fn set_up_ms_timer<TIM: ValidTim>(
irq_cfg: IrqCfg,
sys_cfg: &mut pac::SYSCONFIG,
irq_sel: Option<&mut pac::IRQSEL>,
sys_clk: impl Into<Hertz>,
tim0: TIM,
) -> CountDownTimer<TIM> {
let mut ms_timer = CountDownTimer::new(sys_cfg, sys_clk, tim0);
ms_timer.listen(timer::Event::TimeOut, irq_cfg, irq_sel, Some(sys_cfg));
ms_timer.start(1000.hz());
ms_timer
}
pub fn set_up_ms_delay_provider<TIM: ValidTim>(
sys_cfg: &mut pac::SYSCONFIG,
sys_clk: impl Into<Hertz>,
tim: TIM,
) -> CountDownTimer<TIM> {
let mut provider = CountDownTimer::new(sys_cfg, sys_clk, tim);
provider.start(1000.hz());
provider
}
/// This function can be called in a specified interrupt handler to increment
/// the MS counter
pub fn default_ms_irq_handler() {
@ -763,6 +790,7 @@ impl Delay {
}
/// This assumes that the user has already set up a MS tick timer in TIM0 as a system tick
/// with [`set_up_ms_delay_provider`]
impl embedded_hal::blocking::delay::DelayMs<u32> for Delay {
fn delay_ms(&mut self, ms: u32) {
if self.cd_tim.curr_freq() != 1000.hz() || !self.cd_tim.listening() {

View File

@ -2,25 +2,31 @@
//!
//! ## Examples
//!
//! - [UART example](https://egit.irs.uni-stuttgart.de/rust/va108xx-hal/src/branch/main/examples/uart.rs)
//! - [UART simple example](https://egit.irs.uni-stuttgart.de/rust/va108xx-hal/src/branch/main/examples/uart.rs)
//! - [UART with IRQ and RTIC](https://egit.irs.uni-stuttgart.de/rust/va108xx-hal/src/branch/main/examples/uart-irq-rtic.rs)
use core::{convert::Infallible, ptr};
use core::{marker::PhantomData, ops::Deref};
use libm::floorf;
use crate::clock::enable_peripheral_clock;
pub use crate::utility::IrqCfg;
use crate::{
clock,
clock::{self, enable_peripheral_clock, PeripheralClocks},
gpio::pins::{
AltFunc1, AltFunc2, AltFunc3, Pin, PA16, PA17, PA18, PA19, PA2, PA26, PA27, PA3, PA30,
PA31, PA8, PA9, PB18, PB19, PB20, PB21, PB22, PB23, PB6, PB7, PB8, PB9,
},
pac::{uarta as uart_base, SYSCONFIG, UARTA, UARTB},
pac::{uarta as uart_base, IRQSEL, SYSCONFIG, UARTA, UARTB},
prelude::*,
time::{Bps, Hertz},
utility::unmask_irq,
};
use embedded_hal::{blocking, serial};
//==================================================================================================
// Type-Level support
//==================================================================================================
pub trait Pins<UART> {}
impl Pins<UARTA> for (Pin<PA9, AltFunc2>, Pin<PA8, AltFunc2>) {}
@ -38,15 +44,23 @@ impl Pins<UARTB> for (Pin<PB7, AltFunc1>, Pin<PB6, AltFunc1>) {}
impl Pins<UARTB> for (Pin<PB19, AltFunc2>, Pin<PB18, AltFunc2>) {}
impl Pins<UARTB> for (Pin<PB21, AltFunc1>, Pin<PB20, AltFunc1>) {}
//==================================================================================================
// Regular Definitions
//==================================================================================================
#[derive(Debug)]
pub enum Error {
Overrun,
FramingError,
ParityError,
BreakCondition,
TransferPending,
BufferTooShort,
/// Can be a combination of overrun, framing or parity error
IrqError,
}
#[derive(Debug, PartialEq, Copy, Clone)]
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
pub enum Event {
// Receiver FIFO interrupt enable. Generates interrupt
// when FIFO is at least half full. Half full is defined as FIFO
@ -70,20 +84,20 @@ pub enum Event {
TxCts,
}
#[derive(Copy, Clone, PartialEq)]
#[derive(Copy, Clone, PartialEq, Eq)]
pub enum Parity {
None,
Odd,
Even,
}
#[derive(Copy, Clone, PartialEq)]
#[derive(Copy, Clone, PartialEq, Eq)]
pub enum StopBits {
One = 0,
Two = 1,
}
#[derive(Copy, Clone, PartialEq)]
#[derive(Copy, Clone, PartialEq, Eq)]
pub enum WordSize {
Five = 0,
Six = 1,
@ -160,13 +174,144 @@ impl From<Bps> for Config {
}
}
/// Serial abstraction
pub struct Uart<UART, PINS> {
//==================================================================================================
// IRQ Definitions
//==================================================================================================
struct IrqInfo {
rx_len: usize,
rx_idx: usize,
irq_cfg: IrqCfg,
mode: IrqReceptionMode,
}
pub enum IrqResultMask {
Complete = 0,
Overflow = 1,
FramingError = 2,
ParityError = 3,
Break = 4,
Timeout = 5,
Addr9 = 6,
/// Should not happen
Unknown = 7,
}
/// This struct is used to return the default IRQ handler result to the user
#[derive(Debug, Default)]
pub struct IrqResult {
raw_res: u32,
pub bytes_read: usize,
}
impl IrqResult {
pub const fn new() -> Self {
IrqResult {
raw_res: 0,
bytes_read: 0,
}
}
}
impl IrqResult {
#[inline]
pub fn raw_result(&self) -> u32 {
self.raw_res
}
#[inline]
pub(crate) fn clear_result(&mut self) {
self.raw_res = 0;
}
#[inline]
pub(crate) fn set_result(&mut self, flag: IrqResultMask) {
self.raw_res |= 1 << flag as u32;
}
#[inline]
pub fn complete(&self) -> bool {
if ((self.raw_res >> IrqResultMask::Complete as u32) & 0x01) == 0x01 {
return true;
}
false
}
#[inline]
pub fn error(&self) -> bool {
if self.overflow_error() || self.framing_error() || self.parity_error() {
return true;
}
false
}
#[inline]
pub fn overflow_error(&self) -> bool {
if ((self.raw_res >> IrqResultMask::Overflow as u32) & 0x01) == 0x01 {
return true;
}
false
}
#[inline]
pub fn framing_error(&self) -> bool {
if ((self.raw_res >> IrqResultMask::FramingError as u32) & 0x01) == 0x01 {
return true;
}
false
}
#[inline]
pub fn parity_error(&self) -> bool {
if ((self.raw_res >> IrqResultMask::ParityError as u32) & 0x01) == 0x01 {
return true;
}
false
}
#[inline]
pub fn timeout(&self) -> bool {
if ((self.raw_res >> IrqResultMask::Timeout as u32) & 0x01) == 0x01 {
return true;
}
false
}
}
#[derive(Debug, PartialEq)]
enum IrqReceptionMode {
Idle,
Pending,
}
//==================================================================================================
// UART implementation
//==================================================================================================
/// Type erased variant of a UART. Can be created with the [`Uart::downgrade`] function.
pub struct UartBase<UART> {
uart: UART,
pins: PINS,
tx: Tx<UART>,
rx: Rx<UART>,
}
/// Serial abstraction. Entry point to create a new UART
pub struct Uart<UART, PINS> {
uart_base: UartBase<UART>,
pins: PINS,
}
/// UART using the IRQ capabilities of the peripheral. Can be created with the
/// [`Uart::into_uart_with_irq`] function.
pub struct UartWithIrq<UART, PINS> {
irq_base: UartWithIrqBase<UART>,
pins: PINS,
}
/// Type-erased UART using the IRQ capabilities of the peripheral. Can be created with the
/// [`UartWithIrq::downgrade`] function.
pub struct UartWithIrqBase<UART> {
pub uart: UartBase<UART>,
irq_info: IrqInfo,
}
/// Serial receiver
pub struct Rx<UART> {
@ -196,12 +341,10 @@ impl<UART> Tx<UART> {
pub trait Instance: Deref<Target = uart_base::RegisterBlock> {
fn ptr() -> *const uart_base::RegisterBlock;
const IDX: u8;
}
impl<UART, PINS> Uart<UART, PINS>
where
UART: Instance,
{
impl<UART: Instance> UartBase<UART> {
/// This function assumes that the peripheral clock was alredy enabled
/// in the SYSCONFIG register
fn init(self, config: Config, sys_clk: Hertz) -> Self {
@ -211,7 +354,7 @@ where
};
let x = sys_clk.0 as f32 / (config.baudrate.0 * baud_multiplier) as f32;
let integer_part = floorf(x) as u32;
let frac = floorf((64.0 * (x - integer_part as f32) + 0.5) as f32) as u32;
let frac = floorf(64.0 * (x - integer_part as f32) + 0.5) as u32;
self.uart
.clkscale
.write(|w| unsafe { w.bits(integer_part * 64 + frac) });
@ -247,7 +390,47 @@ where
self
}
pub fn listen(self, event: Event) -> Self {
#[inline]
pub fn enable_rx(&mut self) {
self.uart.enable.modify(|_, w| w.rxenable().set_bit());
}
#[inline]
pub fn disable_rx(&mut self) {
self.uart.enable.modify(|_, w| w.rxenable().clear_bit());
}
#[inline]
pub fn enable_tx(&mut self) {
self.uart.enable.modify(|_, w| w.txenable().set_bit());
}
#[inline]
pub fn disable_tx(&mut self) {
self.uart.enable.modify(|_, w| w.txenable().clear_bit());
}
#[inline]
pub fn clear_rx_fifo(&mut self) {
self.uart.fifo_clr.write(|w| w.rxfifo().set_bit());
}
#[inline]
pub fn clear_tx_fifo(&mut self) {
self.uart.fifo_clr.write(|w| w.txfifo().set_bit());
}
#[inline]
pub fn clear_rx_status(&mut self) {
self.uart.fifo_clr.write(|w| w.rxsts().set_bit());
}
#[inline]
pub fn clear_tx_status(&mut self) {
self.uart.fifo_clr.write(|w| w.txsts().set_bit());
}
pub fn listen(&self, event: Event) {
self.uart.irq_enb.modify(|_, w| match event {
Event::RxError => w.irq_rx_status().set_bit(),
Event::RxFifoHalfFull => w.irq_rx().set_bit(),
@ -257,10 +440,9 @@ where
Event::TxFifoHalfFull => w.irq_tx().set_bit(),
Event::TxCts => w.irq_tx_cts().set_bit(),
});
self
}
pub fn unlisten(self, event: Event) -> Self {
pub fn unlisten(&self, event: Event) {
self.uart.irq_enb.modify(|_, w| match event {
Event::RxError => w.irq_rx_status().clear_bit(),
Event::RxFifoHalfFull => w.irq_rx().clear_bit(),
@ -270,10 +452,9 @@ where
Event::TxFifoHalfFull => w.irq_tx().clear_bit(),
Event::TxCts => w.irq_tx_cts().clear_bit(),
});
self
}
pub fn release(self) -> (UART, PINS) {
pub fn release(self) -> UART {
// Clear the FIFO
self.uart.fifo_clr.write(|w| {
w.rxfifo().set_bit();
@ -283,7 +464,7 @@ where
w.rxenable().clear_bit();
w.txenable().clear_bit()
});
(self.uart, self.pins)
self.uart
}
pub fn split(self) -> (Tx<UART>, Rx<UART>) {
@ -291,14 +472,132 @@ where
}
}
impl<UART, PINS> Uart<UART, PINS>
where
UART: Instance,
{
/// This function assumes that the peripheral clock was alredy enabled
/// in the SYSCONFIG register
fn init(mut self, config: Config, sys_clk: Hertz) -> Self {
self.uart_base = self.uart_base.init(config, sys_clk);
self
}
/// If the IRQ capabilities of the peripheral are used, the UART needs to be converted
/// with this function
pub fn into_uart_with_irq(
self,
irq_cfg: IrqCfg,
sys_cfg: Option<&mut SYSCONFIG>,
irq_sel: Option<&mut IRQSEL>,
) -> UartWithIrq<UART, PINS> {
let (uart, pins) = self.downgrade_internal();
UartWithIrq {
pins,
irq_base: UartWithIrqBase {
uart,
irq_info: IrqInfo {
rx_len: 0,
rx_idx: 0,
irq_cfg,
mode: IrqReceptionMode::Idle,
},
}
.init(sys_cfg, irq_sel),
}
}
#[inline]
pub fn enable_rx(&mut self) {
self.uart_base.enable_rx();
}
#[inline]
pub fn disable_rx(&mut self) {
self.uart_base.enable_rx();
}
#[inline]
pub fn enable_tx(&mut self) {
self.uart_base.enable_tx();
}
#[inline]
pub fn disable_tx(&mut self) {
self.uart_base.disable_tx();
}
#[inline]
pub fn clear_rx_fifo(&mut self) {
self.uart_base.clear_rx_fifo();
}
#[inline]
pub fn clear_tx_fifo(&mut self) {
self.uart_base.clear_tx_fifo();
}
#[inline]
pub fn clear_rx_status(&mut self) {
self.uart_base.clear_rx_status();
}
#[inline]
pub fn clear_tx_status(&mut self) {
self.uart_base.clear_tx_status();
}
pub fn listen(&self, event: Event) {
self.uart_base.listen(event);
}
pub fn unlisten(&self, event: Event) {
self.uart_base.unlisten(event);
}
pub fn release(self) -> (UART, PINS) {
(self.uart_base.release(), self.pins)
}
fn downgrade_internal(self) -> (UartBase<UART>, PINS) {
let base = UartBase {
uart: self.uart_base.uart,
tx: self.uart_base.tx,
rx: self.uart_base.rx,
};
(base, self.pins)
}
pub fn downgrade(self) -> UartBase<UART> {
UartBase {
uart: self.uart_base.uart,
tx: self.uart_base.tx,
rx: self.uart_base.rx,
}
}
pub fn split(self) -> (Tx<UART>, Rx<UART>) {
self.uart_base.split()
}
}
impl Instance for UARTA {
fn ptr() -> *const uart_base::RegisterBlock {
UARTA::ptr() as *const _
}
const IDX: u8 = 0;
}
impl Instance for UARTB {
fn ptr() -> *const uart_base::RegisterBlock {
UARTB::ptr() as *const _
}
const IDX: u8 = 1;
}
macro_rules! uart_impl {
($($UARTX:ident: ($uartx:ident, $clk_enb_enum:path),)+) => {
$(
impl Instance for $UARTX {
fn ptr() -> *const uart_base::RegisterBlock {
$UARTX::ptr() as *const _
}
}
impl<PINS: Pins<$UARTX>> Uart<$UARTX, PINS> {
pub fn $uartx(
@ -306,19 +605,260 @@ macro_rules! uart_impl {
pins: PINS,
config: impl Into<Config>,
syscfg: &mut SYSCONFIG,
sys_clk: Hertz
sys_clk: impl Into<Hertz>
) -> Self
{
enable_peripheral_clock(syscfg, $clk_enb_enum);
Uart { uart, pins, tx: Tx::new(), rx: Rx::new() }.init(
config.into(), sys_clk
)
Uart {
uart_base: UartBase {
uart,
tx: Tx::new(),
rx: Rx::new(),
},
pins,
}
.init(config.into(), sys_clk.into())
}
}
)+
}
}
impl<UART: Instance> UartWithIrqBase<UART> {
fn init(self, sys_cfg: Option<&mut SYSCONFIG>, irq_sel: Option<&mut IRQSEL>) -> Self {
if let Some(sys_cfg) = sys_cfg {
enable_peripheral_clock(sys_cfg, PeripheralClocks::Irqsel)
}
if let Some(irq_sel) = irq_sel {
if self.irq_info.irq_cfg.route {
irq_sel.uart[UART::IDX as usize]
.write(|w| unsafe { w.bits(self.irq_info.irq_cfg.irq as u32) });
}
}
self
}
/// This initializes a non-blocking read transfer using the IRQ capabilities of the UART
/// peripheral.
///
/// The only required information is the maximum length for variable sized reception
/// or the expected length for fixed length reception. If variable sized packets are expected,
/// the timeout functionality of the IRQ should be enabled as well. After calling this function,
/// the [`irq_handler`](Self::irq_handler) function should be called in the user interrupt
/// handler to read the received packets and reinitiate another transfer if desired.
pub fn read_fixed_len_using_irq(
&mut self,
max_len: usize,
enb_timeout_irq: bool,
) -> Result<(), Error> {
if self.irq_info.mode != IrqReceptionMode::Idle {
return Err(Error::TransferPending);
}
self.irq_info.mode = IrqReceptionMode::Pending;
self.irq_info.rx_idx = 0;
self.irq_info.rx_len = max_len;
self.uart.enable_rx();
self.uart.enable_tx();
self.enable_rx_irq_sources(enb_timeout_irq);
if self.irq_info.irq_cfg.enable {
unmask_irq(self.irq_info.irq_cfg.irq);
}
Ok(())
}
#[inline]
fn enable_rx_irq_sources(&mut self, timeout: bool) {
self.uart.uart.irq_enb.modify(|_, w| {
if timeout {
w.irq_rx_to().set_bit();
}
w.irq_rx_status().set_bit();
w.irq_rx().set_bit()
});
}
#[inline]
fn disable_rx_irq_sources(&mut self) {
self.uart.uart.irq_enb.modify(|_, w| {
w.irq_rx_to().clear_bit();
w.irq_rx_status().clear_bit();
w.irq_rx().clear_bit()
});
}
#[inline]
pub fn enable_tx(&mut self) {
self.uart.enable_tx()
}
#[inline]
pub fn disable_tx(&mut self) {
self.uart.disable_tx()
}
pub fn cancel_transfer(&mut self) {
// Disable IRQ
cortex_m::peripheral::NVIC::mask(self.irq_info.irq_cfg.irq);
self.disable_rx_irq_sources();
self.uart.clear_tx_fifo();
self.irq_info.rx_idx = 0;
self.irq_info.rx_len = 0;
}
/// Default IRQ handler which can be used to read the packets arriving on the UART peripheral.
///
/// If passed buffer is equal to or larger than the specified maximum length, an
/// [`Error::BufferTooShort`] will be returned
pub fn irq_handler(&mut self, res: &mut IrqResult, buf: &mut [u8]) -> Result<(), Error> {
if buf.len() < self.irq_info.rx_len {
return Err(Error::BufferTooShort);
}
let irq_end = self.uart.uart.irq_end.read();
let enb_status = self.uart.uart.enable.read();
let rx_enabled = enb_status.rxenable().bit_is_set();
let _tx_enabled = enb_status.txenable().bit_is_set();
let read_handler =
|res: &mut IrqResult, read_res: nb::Result<u8, Error>| -> Result<Option<u8>, Error> {
match read_res {
Ok(byte) => Ok(Some(byte)),
Err(nb::Error::WouldBlock) => Ok(None),
Err(nb::Error::Other(e)) => match e {
Error::Overrun => {
res.set_result(IrqResultMask::Overflow);
Err(Error::IrqError)
}
Error::FramingError => {
res.set_result(IrqResultMask::FramingError);
Err(Error::IrqError)
}
Error::ParityError => {
res.set_result(IrqResultMask::ParityError);
Err(Error::IrqError)
}
_ => {
res.set_result(IrqResultMask::Unknown);
Err(Error::IrqError)
}
},
}
};
if irq_end.irq_rx().bit_is_set() {
// If this interrupt bit is set, the trigger level is available at the very least.
// Read everything as fast as possible
for _ in 0..core::cmp::min(
self.uart.uart.rxfifoirqtrg.read().bits() as usize,
self.irq_info.rx_len,
) {
buf[self.irq_info.rx_idx] = (self.uart.uart.data.read().bits() & 0xff) as u8;
self.irq_info.rx_idx += 1;
}
// While there is data in the FIFO, write it into the reception buffer
loop {
if self.irq_info.rx_idx == self.irq_info.rx_len {
self.irq_completion_handler(res);
return Ok(());
}
if let Some(byte) = read_handler(res, self.uart.read())? {
buf[self.irq_info.rx_idx] = byte;
self.irq_info.rx_idx += 1;
} else {
break;
}
}
}
// RX transfer not complete, check for RX errors
if (self.irq_info.rx_idx < self.irq_info.rx_len) && rx_enabled {
// Read status register again, might have changed since reading received data
let rx_status = self.uart.uart.rxstatus.read();
res.clear_result();
if rx_status.rxovr().bit_is_set() {
res.set_result(IrqResultMask::Overflow);
}
if rx_status.rxfrm().bit_is_set() {
res.set_result(IrqResultMask::FramingError);
}
if rx_status.rxpar().bit_is_set() {
res.set_result(IrqResultMask::ParityError);
}
if rx_status.rxbrk().bit_is_set() {
res.set_result(IrqResultMask::Break);
}
if rx_status.rxto().bit_is_set() {
// A timeout has occured but there might be some leftover data in the FIFO,
// so read that data as well
while let Some(byte) = read_handler(res, self.uart.read())? {
buf[self.irq_info.rx_idx] = byte;
self.irq_info.rx_idx += 1;
}
self.irq_completion_handler(res);
res.set_result(IrqResultMask::Timeout);
return Ok(());
}
// If it is not a timeout, it's an error
if res.raw_res != 0 {
self.disable_rx_irq_sources();
return Err(Error::IrqError);
}
}
// Clear the interrupt status bits
self.uart
.uart
.irq_clr
.write(|w| unsafe { w.bits(irq_end.bits()) });
Ok(())
}
fn irq_completion_handler(&mut self, res: &mut IrqResult) {
self.disable_rx_irq_sources();
self.uart.disable_rx();
res.bytes_read = self.irq_info.rx_idx;
res.clear_result();
res.set_result(IrqResultMask::Complete);
self.irq_info.mode = IrqReceptionMode::Idle;
self.irq_info.rx_idx = 0;
self.irq_info.rx_len = 0;
}
pub fn release(self) -> UART {
self.uart.release()
}
}
impl<UART: Instance, PINS> UartWithIrq<UART, PINS> {
/// See [`UartWithIrqBase::read_fixed_len_using_irq`] doc
pub fn read_fixed_len_using_irq(
&mut self,
max_len: usize,
enb_timeout_irq: bool,
) -> Result<(), Error> {
self.irq_base
.read_fixed_len_using_irq(max_len, enb_timeout_irq)
}
pub fn cancel_transfer(&mut self) {
self.irq_base.cancel_transfer()
}
/// See [`UartWithIrqBase::irq_handler`] doc
pub fn irq_handler(&mut self, res: &mut IrqResult, buf: &mut [u8]) -> Result<(), Error> {
self.irq_base.irq_handler(res, buf)
}
pub fn release(self) -> (UART, PINS) {
(self.irq_base.release(), self.pins)
}
pub fn downgrade(self) -> (UartWithIrqBase<UART>, PINS) {
(self.irq_base, self.pins)
}
}
uart_impl! {
UARTA: (uarta, clock::PeripheralClocks::Uart0),
UARTB: (uartb, clock::PeripheralClocks::Uart1),
@ -326,10 +866,7 @@ uart_impl! {
impl<UART> Tx<UART> where UART: Instance {}
impl<UART, PINS> serial::Write<u8> for Uart<UART, PINS>
where
UART: Instance,
{
impl<UART: Instance> serial::Write<u8> for UartBase<UART> {
type Error = Infallible;
fn write(&mut self, word: u8) -> nb::Result<(), Self::Error> {
self.tx.write(word)
@ -339,6 +876,19 @@ where
}
}
impl<UART, PINS> serial::Write<u8> for Uart<UART, PINS>
where
UART: Instance,
{
type Error = Infallible;
fn write(&mut self, word: u8) -> nb::Result<(), Self::Error> {
self.uart_base.write(word)
}
fn flush(&mut self) -> nb::Result<(), Self::Error> {
self.uart_base.flush()
}
}
impl<UART: Instance, PINS> blocking::serial::write::Default<u8> for Uart<UART, PINS> {}
impl<UART: Instance> serial::Write<u8> for Tx<UART> {
@ -350,11 +900,10 @@ impl<UART: Instance> serial::Write<u8> for Tx<UART> {
return Err(nb::Error::WouldBlock);
} else {
// DPARITY bit not supported yet
// NOTE(unsafe) atomic write to data register
unsafe {
// NOTE(unsafe) atomic write to data register
// NOTE(write_volatile) 8-bit write that's not
// possible through the svd2rust API
ptr::write_volatile(&(*UART::ptr()).data as *const _ as *mut _, word);
(*UART::ptr()).data.write(|w| w.bits(word as u32));
}
}
Ok(())
@ -370,7 +919,7 @@ impl<UART: Instance> serial::Write<u8> for Tx<UART> {
}
}
impl<UART: Instance, PINS> serial::Read<u8> for Uart<UART, PINS> {
impl<UART: Instance> serial::Read<u8> for UartBase<UART> {
type Error = Error;
fn read(&mut self) -> nb::Result<u8, Error> {
@ -378,6 +927,14 @@ impl<UART: Instance, PINS> serial::Read<u8> for Uart<UART, PINS> {
}
}
impl<UART: Instance, PINS> serial::Read<u8> for Uart<UART, PINS> {
type Error = Error;
fn read(&mut self) -> nb::Result<u8, Error> {
self.uart_base.read()
}
}
impl<UART: Instance> serial::Read<u8> for Rx<UART> {
type Error = Error;
@ -409,7 +966,7 @@ impl<UART: Instance> serial::Read<u8> for Rx<UART> {
}
}
impl<UART> core::fmt::Write for Tx<UART>
impl<UART: Instance> core::fmt::Write for Tx<UART>
where
Tx<UART>: embedded_hal::serial::Write<u8>,
{
@ -420,3 +977,12 @@ where
.map_err(|_| core::fmt::Error)
}
}
impl<UART: Instance> core::fmt::Write for UartBase<UART>
where
UartBase<UART>: embedded_hal::serial::Write<u8>,
{
fn write_str(&mut self, s: &str) -> core::fmt::Result {
self.tx.write_str(s)
}
}

View File

@ -3,14 +3,29 @@
//! Some more information about the recommended scrub rates can be found on the
//! [Vorago White Paper website](https://www.voragotech.com/resources) in the
//! application note AN1212
use va108xx::SYSCONFIG;
use crate::pac;
use va108xx::{IOCONFIG, SYSCONFIG};
#[derive(PartialEq, Debug)]
#[derive(PartialEq, Eq, Debug)]
pub enum UtilityError {
InvalidCounterResetVal,
InvalidPin,
}
#[derive(Copy, Clone, PartialEq)]
#[derive(Debug, Eq, Copy, Clone, PartialEq)]
pub enum Funsel {
Funsel1 = 0b01,
Funsel2 = 0b10,
Funsel3 = 0b11,
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum PortSel {
PortA,
PortB,
}
#[derive(Copy, Clone, PartialEq, Eq)]
pub enum PeripheralSelect {
PortA = 0,
PortB = 1,
@ -27,6 +42,26 @@ pub enum PeripheralSelect {
Gpio = 24,
}
/// Generic IRQ config which can be used to specify whether the HAL driver will
/// use the IRQSEL register to route an interrupt, and whether the IRQ will be unmasked in the
/// Cortex-M0 NVIC. Both are generally necessary for IRQs to work, but the user might perform
/// this steps themselves
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub struct IrqCfg {
/// Interrupt target vector. Should always be set, might be required for disabling IRQs
pub irq: pac::Interrupt,
/// Specfiy whether IRQ should be routed to an IRQ vector using the IRQSEL peripheral
pub route: bool,
/// Specify whether the IRQ is unmasked in the Cortex-M NVIC
pub enable: bool,
}
impl IrqCfg {
pub fn new(irq: pac::Interrupt, route: bool, enable: bool) -> Self {
IrqCfg { irq, route, enable }
}
}
/// Enable scrubbing for the ROM
///
/// Returns [`UtilityError::InvalidCounterResetVal`] if the scrub rate is 0
@ -72,3 +107,38 @@ pub fn set_reset_bit(syscfg: &mut SYSCONFIG, periph_sel: PeripheralSelect) {
.peripheral_reset
.modify(|r, w| unsafe { w.bits(r.bits() | (1 << periph_sel as u8)) });
}
/// Can be used to manually manipulate the function select of port pins
pub fn port_mux(
ioconfig: &mut IOCONFIG,
port: PortSel,
pin: u8,
funsel: Funsel,
) -> Result<(), UtilityError> {
match port {
PortSel::PortA => {
if pin > 31 {
return Err(UtilityError::InvalidPin);
}
ioconfig.porta[pin as usize].modify(|_, w| unsafe { w.funsel().bits(funsel as u8) });
Ok(())
}
PortSel::PortB => {
if pin > 23 {
return Err(UtilityError::InvalidPin);
}
ioconfig.portb[pin as usize].modify(|_, w| unsafe { w.funsel().bits(funsel as u8) });
Ok(())
}
}
}
/// Unmask and enable an IRQ with the given interrupt number
///
/// ## Safety
///
/// The unmask function can break mask-based critical sections
#[inline]
pub(crate) fn unmask_irq(irq: pac::Interrupt) {
unsafe { cortex_m::peripheral::NVIC::unmask(irq) };
}

5
test/DueSerialTest/.gitignore vendored Normal file
View File

@ -0,0 +1,5 @@
.pio
.vscode/.browse.c_cpp.db*
.vscode/c_cpp_properties.json
.vscode/launch.json
.vscode/ipch

View File

@ -0,0 +1,7 @@
{
// See http://go.microsoft.com/fwlink/?LinkId=827846
// for the documentation about the extensions.json format
"recommendations": [
"platformio.platformio-ide"
]
}

View File

@ -0,0 +1,2 @@
This is a Platform IO test script for the Arduino Due which can be used to sent different kind
of strings via the serial interface (RX1 and TX1) to the Vorago board.

View File

@ -0,0 +1,39 @@
This directory is intended for project header files.
A header file is a file containing C declarations and macro definitions
to be shared between several project source files. You request the use of a
header file in your project source file (C, C++, etc) located in `src` folder
by including it, with the C preprocessing directive `#include'.
```src/main.c
#include "header.h"
int main (void)
{
...
}
```
Including a header file produces the same results as copying the header file
into each source file that needs it. Such copying would be time-consuming
and error-prone. With a header file, the related declarations appear
in only one place. If they need to be changed, they can be changed in one
place, and programs that include the header file will automatically use the
new version when next recompiled. The header file eliminates the labor of
finding and changing all the copies as well as the risk that a failure to
find one copy will result in inconsistencies within a program.
In C, the usual convention is to give header files names that end with `.h'.
It is most portable to use only letters, digits, dashes, and underscores in
header file names, and at most one dot.
Read more about using header files in official GCC documentation:
* Include Syntax
* Include Operation
* Once-Only Headers
* Computed Includes
https://gcc.gnu.org/onlinedocs/cpp/Header-Files.html

View File

@ -0,0 +1,46 @@
This directory is intended for project specific (private) libraries.
PlatformIO will compile them to static libraries and link into executable file.
The source code of each library should be placed in a an own separate directory
("lib/your_library_name/[here are source files]").
For example, see a structure of the following two libraries `Foo` and `Bar`:
|--lib
| |
| |--Bar
| | |--docs
| | |--examples
| | |--src
| | |- Bar.c
| | |- Bar.h
| | |- library.json (optional, custom build options, etc) https://docs.platformio.org/page/librarymanager/config.html
| |
| |--Foo
| | |- Foo.c
| | |- Foo.h
| |
| |- README --> THIS FILE
|
|- platformio.ini
|--src
|- main.c
and a contents of `src/main.c`:
```
#include <Foo.h>
#include <Bar.h>
int main (void)
{
...
}
```
PlatformIO Library Dependency Finder will find automatically dependent
libraries scanning project source files.
More information about PlatformIO Library Dependency Finder
- https://docs.platformio.org/page/librarymanager/ldf.html

View File

@ -0,0 +1,15 @@
; PlatformIO Project Configuration File
;
; Build options: build flags, source filter
; Upload options: custom upload port, speed and extra flags
; Library options: dependencies, extra library storages
; Advanced options: extra scripting
;
; Please visit documentation for the other options and examples
; https://docs.platformio.org/page/projectconf.html
[env:due]
platform = atmelsam
board = due
framework = arduino
monitor_speed = 115200

View File

@ -0,0 +1,78 @@
#include <Arduino.h>
enum SendModes {
ECHO,
ONLY_WRITE,
ONLY_READ,
WRITE_READ
};
enum StringModes {
FIXED,
VARIABLE
};
// Configure the test application here
SendModes SEND_MODE = SendModes::WRITE_READ;
StringModes STRING_MODE = StringModes::VARIABLE;
uint8_t STRING_IDX = 0;
String STRINGS[4] = {
"$Hi\n",
"$Hello\n",
"$Hello World\n",
"$Hello and Merry Christmas to all of you!\n"
};
void setup() {
// put your setup code here, to run once:
Serial.begin(115200);
Serial.println("Starting Arduino Serial Test script..");
Serial1.begin(115200);
if(STRING_MODE == StringModes::VARIABLE) {
STRING_IDX = 0;
}
pinMode(LED_BUILTIN, OUTPUT);
}
void loop() {
static byte ICOMING_BYTE = 0;
static uint32_t GLOBAL_IDX = 0;
digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN));
// put your main code here, to run repeatedly:
// send data only when you receive data:
if (SEND_MODE == SendModes::ONLY_WRITE or SEND_MODE == SendModes::WRITE_READ) {
Serial.println("Sending string..");
Serial1.write(STRINGS[STRING_IDX].c_str());
if(STRING_MODE == StringModes::VARIABLE) {
STRING_IDX += 1;
if(STRING_IDX > 3) {
STRING_IDX = 0;
}
}
}
if(
SEND_MODE == SendModes::WRITE_READ or
SEND_MODE == SendModes::ONLY_READ or
SEND_MODE == SendModes::ECHO
) {
if (Serial1.available() > 0) {
// read the incoming byte:
String readString = Serial1.readStringUntil('\n');
Serial.print(GLOBAL_IDX);
Serial.print(" - ");
GLOBAL_IDX++;
// say what you got:
Serial.print("I received: ");
Serial.println(readString);
if(SEND_MODE == SendModes::ECHO) {
delay(200);
Serial.println("Sending back echo message");
String sendBack = readString + '\n';
Serial1.write(sendBack.c_str());
}
}
}
delay(3000);
}

View File

@ -0,0 +1,11 @@
This directory is intended for PlatformIO Unit Testing and project tests.
Unit Testing is a software testing method by which individual units of
source code, sets of one or more MCU program modules together with associated
control data, usage procedures, and operating procedures, are tested to
determine whether they are fit for use. Unit testing finds problems early
in the development cycle.
More information about PlatformIO Unit Testing:
- https://docs.platformio.org/page/plus/unit-testing.html