Compare commits
41 Commits
Author | SHA1 | Date | |
---|---|---|---|
2db345af9f | |||
09fd0d2aad | |||
b776bd2823 | |||
6131458a13 | |||
2b8a8dc7c8 | |||
e9f1294572 | |||
1476f4eebe | |||
0ee53c70d5 | |||
833567037c | |||
38b9625773 | |||
49b72d683f | |||
21c44e6327 | |||
147c57defb | |||
5cbbb53094 | |||
491ef3ce09 | |||
e3cdd21b41 | |||
482a725ef7 | |||
9f5a31c5b6 | |||
5cbec366bc | |||
9a5c9ac53c | |||
a8b484d66f | |||
d458a81635 | |||
d5b12c8343 | |||
f376a43f41 | |||
dc2426a905 | |||
de607b1950 | |||
c954fd185d | |||
732f3e3bc8 | |||
bdbe666a2c | |||
b9d4d7214c | |||
0bc7e0f341 | |||
59463fbaba | |||
439e1d43e7 | |||
2abf35bb6e | |||
a1c0fb90e0 | |||
87b0180e6f | |||
f39863e59f | |||
fb158caf6e | |||
3fc8ce519a | |||
2ad405d325 | |||
063a7a56e5 |
8
.github/workflows/ci.yml
vendored
8
.github/workflows/ci.yml
vendored
@ -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() }}
|
||||
|
52
CHANGELOG.md
52
CHANGELOG.md
@ -6,7 +6,57 @@ All notable changes to this project will be documented in this file.
|
||||
The format is based on [Keep a Changelog](http://keepachangelog.com/)
|
||||
and this project adheres to [Semantic Versioning](http://semver.org/).
|
||||
|
||||
## [unreleased]
|
||||
## [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]
|
||||
|
||||
|
46
Cargo.toml
46
Cargo.toml
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "va108xx-hal"
|
||||
version = "0.4.0"
|
||||
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"]
|
||||
|
@ -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).
|
||||
|
@ -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
|
||||
|
37
automation/Jenkinsfile
vendored
37
automation/Jenkinsfile
vendored
@ -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
47
examples/blinky-pac.rs
Normal 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);
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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
29
examples/rtic-empty.rs
Normal 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 {}
|
||||
}
|
||||
}
|
@ -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();
|
||||
|
@ -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
126
examples/uart-irq-rtic.rs
Normal 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");
|
||||
});
|
||||
}
|
||||
}
|
@ -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
|
||||
|
@ -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),
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||