New VA108xx Rust workspace structure + dependency updates
- The workspace is now a monorepo without submodules. The HAL, PAC and BSP are integrated directly - Update all dependencies: embedded-hal v1 and RTIC v2
This commit is contained in:
37
examples/simple/Cargo.toml
Normal file
37
examples/simple/Cargo.toml
Normal file
@ -0,0 +1,37 @@
|
||||
[package]
|
||||
name = "simple-examples"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
panic-halt = "0.2"
|
||||
cortex-m = {version = "0.7", features = ["critical-section-single-core"]}
|
||||
panic-rtt-target = "0.1"
|
||||
cortex-m-rt = "0.7"
|
||||
rtt-target = "0.5"
|
||||
rtic-sync = { version = "1.3", features = ["defmt-03"] }
|
||||
embedded-hal = "1"
|
||||
embedded-hal-nb = "1"
|
||||
embedded-io = "0.6"
|
||||
cortex-m-semihosting = "0.5.0"
|
||||
# I'd really like to use those, but it is tricky without probe-rs..
|
||||
# defmt = "0.3"
|
||||
# defmt-brtt = { version = "0.1", default-features = false, features = ["rtt"] }
|
||||
# panic-probe = { version = "0.3", features = ["print-defmt"] }
|
||||
|
||||
[dependencies.rtic]
|
||||
version = "2"
|
||||
features = ["thumbv6-backend"]
|
||||
|
||||
[dependencies.rtic-monotonics]
|
||||
version = "1"
|
||||
features = ["cortex-m-systick"]
|
||||
|
||||
[dependencies.va108xx-hal]
|
||||
version = "0.6"
|
||||
path = "../../va108xx-hal"
|
||||
features = ["rt", "defmt"]
|
||||
|
||||
[dependencies.va108xx]
|
||||
version = "0.3"
|
||||
path = "../../va108xx"
|
47
examples/simple/examples/blinky-pac.rs
Normal file
47
examples/simple/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);
|
||||
}
|
||||
}
|
64
examples/simple/examples/blinky.rs
Normal file
64
examples/simple/examples/blinky.rs
Normal file
@ -0,0 +1,64 @@
|
||||
//! Simple blinky example
|
||||
//!
|
||||
//! Additional note on LEDs when using the REB1 development board:
|
||||
//! Be not afraid: Pulling the GPIOs low makes the LEDs blink. See REB1
|
||||
//! schematic for more details.
|
||||
#![no_main]
|
||||
#![no_std]
|
||||
|
||||
use cortex_m_rt::entry;
|
||||
use embedded_hal::{
|
||||
delay::DelayNs,
|
||||
digital::{OutputPin, StatefulOutputPin},
|
||||
};
|
||||
use panic_halt as _;
|
||||
use va108xx_hal::{
|
||||
gpio::PinsA,
|
||||
pac::{self, interrupt},
|
||||
prelude::*,
|
||||
pwm::{default_ms_irq_handler, set_up_ms_tick, CountDownTimer},
|
||||
timer::DelayMs,
|
||||
IrqCfg,
|
||||
};
|
||||
|
||||
#[entry]
|
||||
fn main() -> ! {
|
||||
let mut dp = pac::Peripherals::take().unwrap();
|
||||
let mut delay_ms = DelayMs::new(set_up_ms_tick(
|
||||
IrqCfg::new(interrupt::OC0, true, true),
|
||||
&mut dp.sysconfig,
|
||||
Some(&mut dp.irqsel),
|
||||
50.MHz(),
|
||||
dp.tim0,
|
||||
))
|
||||
.unwrap();
|
||||
let mut delay_tim1 = CountDownTimer::new(&mut dp.sysconfig, 50.MHz(), dp.tim1);
|
||||
let porta = PinsA::new(&mut dp.sysconfig, Some(dp.ioconfig), dp.porta);
|
||||
let mut led1 = porta.pa10.into_readable_push_pull_output();
|
||||
let mut led2 = porta.pa7.into_readable_push_pull_output();
|
||||
let mut led3 = porta.pa6.into_readable_push_pull_output();
|
||||
for _ in 0..10 {
|
||||
led1.set_low().ok();
|
||||
led2.set_low().ok();
|
||||
led3.set_low().ok();
|
||||
delay_ms.delay_ms(200);
|
||||
led1.set_high().ok();
|
||||
led2.set_high().ok();
|
||||
led3.set_high().ok();
|
||||
delay_tim1.delay_ms(200);
|
||||
}
|
||||
loop {
|
||||
led1.toggle().ok();
|
||||
delay_ms.delay_ms(200);
|
||||
led2.toggle().ok();
|
||||
delay_tim1.delay_ms(200);
|
||||
led3.toggle().ok();
|
||||
delay_ms.delay_ms(200);
|
||||
}
|
||||
}
|
||||
|
||||
#[interrupt]
|
||||
#[allow(non_snake_case)]
|
||||
fn OC0() {
|
||||
default_ms_irq_handler()
|
||||
}
|
142
examples/simple/examples/cascade.rs
Normal file
142
examples/simple/examples/cascade.rs
Normal file
@ -0,0 +1,142 @@
|
||||
//! Simple Cascade example
|
||||
//!
|
||||
//! A timer will be periodically started which starts another timer via the cascade feature.
|
||||
//! This timer will then start another timer with the cascade feature as well.
|
||||
#![no_main]
|
||||
#![no_std]
|
||||
#![allow(non_snake_case)]
|
||||
|
||||
use core::cell::RefCell;
|
||||
use cortex_m::interrupt::Mutex;
|
||||
use cortex_m_rt::entry;
|
||||
use embedded_hal::delay::DelayNs;
|
||||
use panic_rtt_target as _;
|
||||
use rtt_target::{rprintln, rtt_init_print};
|
||||
use va108xx_hal::{
|
||||
pac::{self, interrupt},
|
||||
prelude::*,
|
||||
timer::{
|
||||
default_ms_irq_handler, set_up_ms_delay_provider, CascadeCtrl, CascadeSource,
|
||||
CountDownTimer, Event, IrqCfg,
|
||||
},
|
||||
};
|
||||
|
||||
static CSD_TGT_1: Mutex<RefCell<Option<CountDownTimer<pac::Tim4>>>> =
|
||||
Mutex::new(RefCell::new(None));
|
||||
static CSD_TGT_2: Mutex<RefCell<Option<CountDownTimer<pac::Tim5>>>> =
|
||||
Mutex::new(RefCell::new(None));
|
||||
|
||||
#[entry]
|
||||
fn main() -> ! {
|
||||
rtt_init_print!();
|
||||
rprintln!("-- VA108xx Cascade example application--");
|
||||
|
||||
let mut dp = pac::Peripherals::take().unwrap();
|
||||
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,
|
||||
IrqCfg::new(va108xx::Interrupt::OC1, true, false),
|
||||
Some(&mut dp.irqsel),
|
||||
Some(&mut dp.sysconfig),
|
||||
);
|
||||
|
||||
// First target for cascade
|
||||
let mut cascade_target_1 =
|
||||
CountDownTimer::new(&mut dp.sysconfig, 50.MHz(), dp.tim4).auto_deactivate(true);
|
||||
cascade_target_1
|
||||
.cascade_0_source(CascadeSource::TimBase, Some(3))
|
||||
.expect("Configuring cascade source for TIM4 failed");
|
||||
let mut csd_cfg = CascadeCtrl {
|
||||
enb_start_src_csd0: true,
|
||||
..Default::default()
|
||||
};
|
||||
// Use trigger mode here
|
||||
csd_cfg.trg_csd0 = true;
|
||||
cascade_target_1.cascade_control(csd_cfg);
|
||||
// Normally it should already be sufficient to activate IRQ in the CTRL
|
||||
// register but a full interrupt is use here to display print output when
|
||||
// the timer expires
|
||||
cascade_target_1.listen(
|
||||
Event::TimeOut,
|
||||
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
|
||||
cascade_target_1.start(1.Hz());
|
||||
|
||||
// Activated by first cascade target
|
||||
let mut cascade_target_2 =
|
||||
CountDownTimer::new(&mut dp.sysconfig, 50.MHz(), dp.tim5).auto_deactivate(true);
|
||||
// Set TIM4 as cascade source
|
||||
cascade_target_2
|
||||
.cascade_1_source(CascadeSource::TimBase, Some(4))
|
||||
.expect("Configuring cascade source for TIM5 failed");
|
||||
|
||||
csd_cfg = CascadeCtrl::default();
|
||||
csd_cfg.enb_start_src_csd1 = true;
|
||||
// Use trigger mode here
|
||||
csd_cfg.trg_csd1 = true;
|
||||
cascade_target_2.cascade_control(csd_cfg);
|
||||
// Normally it should already be sufficient to activate IRQ in the CTRL
|
||||
// register but a full interrupt is use here to display print output when
|
||||
// the timer expires
|
||||
cascade_target_2.listen(
|
||||
Event::TimeOut,
|
||||
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
|
||||
cascade_target_2.start(1.Hz());
|
||||
|
||||
// Unpend all IRQs
|
||||
unsafe {
|
||||
cortex_m::peripheral::NVIC::unmask(pac::Interrupt::OC0);
|
||||
cortex_m::peripheral::NVIC::unmask(pac::Interrupt::OC1);
|
||||
cortex_m::peripheral::NVIC::unmask(pac::Interrupt::OC2);
|
||||
cortex_m::peripheral::NVIC::unmask(pac::Interrupt::OC3);
|
||||
}
|
||||
// Make both cascade targets accessible from the IRQ handler with the Mutex dance
|
||||
cortex_m::interrupt::free(|cs| {
|
||||
CSD_TGT_1.borrow(cs).replace(Some(cascade_target_1));
|
||||
CSD_TGT_2.borrow(cs).replace(Some(cascade_target_2));
|
||||
});
|
||||
loop {
|
||||
rprintln!("-- Triggering cascade in 0.5 seconds --");
|
||||
cascade_triggerer.start(2.Hz());
|
||||
delay.delay_ms(5000);
|
||||
}
|
||||
}
|
||||
|
||||
#[interrupt]
|
||||
fn OC0() {
|
||||
default_ms_irq_handler()
|
||||
}
|
||||
|
||||
#[interrupt]
|
||||
fn OC1() {
|
||||
static mut IDX: u32 = 0;
|
||||
rprintln!("{}: Cascade triggered timed out", &IDX);
|
||||
*IDX += 1;
|
||||
}
|
||||
|
||||
#[interrupt]
|
||||
fn OC2() {
|
||||
static mut IDX: u32 = 0;
|
||||
rprintln!("{}: First cascade target timed out", &IDX);
|
||||
*IDX += 1;
|
||||
}
|
||||
|
||||
#[interrupt]
|
||||
fn OC3() {
|
||||
static mut IDX: u32 = 0;
|
||||
rprintln!("{}: Second cascade target timed out", &IDX);
|
||||
*IDX += 1;
|
||||
}
|
72
examples/simple/examples/pwm.rs
Normal file
72
examples/simple/examples/pwm.rs
Normal file
@ -0,0 +1,72 @@
|
||||
//! Simple PWM example
|
||||
#![no_main]
|
||||
#![no_std]
|
||||
|
||||
use cortex_m_rt::entry;
|
||||
use embedded_hal::{delay::DelayNs, pwm::SetDutyCycle};
|
||||
use panic_rtt_target as _;
|
||||
use rtt_target::{rprintln, rtt_init_print};
|
||||
use va108xx_hal::{
|
||||
gpio::PinsA,
|
||||
pac,
|
||||
prelude::*,
|
||||
pwm::{self, get_duty_from_percent, PwmA, PwmB, ReducedPwmPin},
|
||||
timer::set_up_ms_delay_provider,
|
||||
};
|
||||
|
||||
#[entry]
|
||||
fn main() -> ! {
|
||||
rtt_init_print!();
|
||||
rprintln!("-- VA108xx PWM example application--");
|
||||
let mut dp = pac::Peripherals::take().unwrap();
|
||||
let pinsa = PinsA::new(&mut dp.sysconfig, None, dp.porta);
|
||||
let mut pwm = pwm::PwmPin::new(
|
||||
(pinsa.pa3.into_funsel_1(), dp.tim3),
|
||||
50.MHz(),
|
||||
&mut dp.sysconfig,
|
||||
10.Hz(),
|
||||
);
|
||||
let mut delay = set_up_ms_delay_provider(&mut dp.sysconfig, 50.MHz(), dp.tim0);
|
||||
let mut current_duty_cycle = 0.0;
|
||||
pwm.set_duty_cycle(get_duty_from_percent(current_duty_cycle))
|
||||
.unwrap();
|
||||
pwm.enable();
|
||||
|
||||
// Delete type information, increased code readibility for the rest of the code
|
||||
let mut reduced_pin = ReducedPwmPin::from(pwm);
|
||||
loop {
|
||||
let mut counter = 0;
|
||||
// Increase duty cycle continuously
|
||||
while current_duty_cycle < 1.0 {
|
||||
delay.delay_ms(400);
|
||||
current_duty_cycle += 0.02;
|
||||
counter += 1;
|
||||
if counter % 10 == 0 {
|
||||
rprintln!("current duty cycle: {}", current_duty_cycle);
|
||||
}
|
||||
|
||||
reduced_pin
|
||||
.set_duty_cycle(get_duty_from_percent(current_duty_cycle))
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
// Switch to PWMB and decrease the window with a high signal from 100 % to 0 %
|
||||
// continously
|
||||
current_duty_cycle = 0.0;
|
||||
let mut upper_limit = 1.0;
|
||||
let mut lower_limit = 0.0;
|
||||
let mut pwmb: ReducedPwmPin<PwmB> = ReducedPwmPin::from(reduced_pin);
|
||||
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(400);
|
||||
lower_limit += 0.01;
|
||||
upper_limit -= 0.01;
|
||||
pwmb.set_pwmb_lower_limit(get_duty_from_percent(lower_limit));
|
||||
pwmb.set_pwmb_upper_limit(get_duty_from_percent(upper_limit));
|
||||
rprintln!("Lower limit: {}", pwmb.pwmb_lower_limit());
|
||||
rprintln!("Upper limit: {}", pwmb.pwmb_upper_limit());
|
||||
}
|
||||
reduced_pin = ReducedPwmPin::<PwmA>::from(pwmb);
|
||||
}
|
||||
}
|
30
examples/simple/examples/rtic-empty.rs
Normal file
30
examples/simple/examples/rtic-empty.rs
Normal file
@ -0,0 +1,30 @@
|
||||
//! 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) {
|
||||
rtt_init_default!();
|
||||
rprintln!("-- Vorago RTIC template --");
|
||||
(Shared {}, Local {})
|
||||
}
|
||||
|
||||
// `shared` cannot be accessed from this context
|
||||
#[idle]
|
||||
fn idle(_cx: idle::Context) -> ! {
|
||||
#[allow(clippy::empty_loop)]
|
||||
loop {}
|
||||
}
|
||||
}
|
19
examples/simple/examples/rtt-log.rs
Normal file
19
examples/simple/examples/rtt-log.rs
Normal file
@ -0,0 +1,19 @@
|
||||
//! Code to test RTT logger functionality
|
||||
#![no_main]
|
||||
#![no_std]
|
||||
|
||||
use cortex_m_rt::entry;
|
||||
use panic_halt as _;
|
||||
use rtt_target::{rprintln, rtt_init_print};
|
||||
use va108xx as _;
|
||||
|
||||
#[entry]
|
||||
fn main() -> ! {
|
||||
rtt_init_print!();
|
||||
let mut counter = 0;
|
||||
loop {
|
||||
rprintln!("{}: Hello, world!", counter);
|
||||
counter += 1;
|
||||
cortex_m::asm::delay(25_000_000);
|
||||
}
|
||||
}
|
244
examples/simple/examples/spi.rs
Normal file
244
examples/simple/examples/spi.rs
Normal file
@ -0,0 +1,244 @@
|
||||
//! SPI example application
|
||||
#![no_main]
|
||||
#![no_std]
|
||||
|
||||
use core::cell::RefCell;
|
||||
|
||||
use cortex_m_rt::entry;
|
||||
use embedded_hal::{
|
||||
delay::DelayNs,
|
||||
spi::{Mode, SpiBus, MODE_0},
|
||||
};
|
||||
use panic_rtt_target as _;
|
||||
use rtt_target::{rprintln, rtt_init_print};
|
||||
use va108xx_hal::{
|
||||
gpio::{PinsA, PinsB},
|
||||
pac::{self, interrupt},
|
||||
prelude::*,
|
||||
pwm::{default_ms_irq_handler, set_up_ms_tick},
|
||||
spi::{self, Spi, SpiBase, TransferConfig},
|
||||
IrqCfg,
|
||||
};
|
||||
|
||||
#[derive(PartialEq, Debug)]
|
||||
pub enum ExampleSelect {
|
||||
// Enter loopback mode. It is not necessary to tie MOSI/MISO together for this
|
||||
Loopback,
|
||||
// Send a test buffer and print everything received
|
||||
TestBuffer,
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Debug)]
|
||||
pub enum SpiBusSelect {
|
||||
SpiAPortA,
|
||||
SpiAPortB,
|
||||
SpiBPortB,
|
||||
}
|
||||
|
||||
const EXAMPLE_SEL: ExampleSelect = ExampleSelect::Loopback;
|
||||
const SPI_BUS_SEL: SpiBusSelect = SpiBusSelect::SpiBPortB;
|
||||
const SPI_SPEED_KHZ: u32 = 1000;
|
||||
const SPI_MODE: Mode = MODE_0;
|
||||
const BLOCKMODE: bool = true;
|
||||
const FILL_WORD: u8 = 0x0f;
|
||||
|
||||
#[entry]
|
||||
fn main() -> ! {
|
||||
rtt_init_print!();
|
||||
rprintln!("-- VA108xx SPI example application--");
|
||||
let mut dp = pac::Peripherals::take().unwrap();
|
||||
let mut delay = set_up_ms_tick(
|
||||
IrqCfg::new(interrupt::OC0, true, true),
|
||||
&mut dp.sysconfig,
|
||||
Some(&mut dp.irqsel),
|
||||
50.MHz(),
|
||||
dp.tim0,
|
||||
);
|
||||
|
||||
let spia_ref: RefCell<Option<SpiBase<pac::Spia, u8>>> = RefCell::new(None);
|
||||
let spib_ref: RefCell<Option<SpiBase<pac::Spib, u8>>> = RefCell::new(None);
|
||||
let pinsa = PinsA::new(&mut dp.sysconfig, None, dp.porta);
|
||||
let pinsb = PinsB::new(&mut dp.sysconfig, Some(dp.ioconfig), dp.portb);
|
||||
|
||||
let mut spi_cfg = spi::SpiConfig::default();
|
||||
if EXAMPLE_SEL == ExampleSelect::Loopback {
|
||||
spi_cfg = spi_cfg.loopback(true)
|
||||
}
|
||||
|
||||
// Set up the SPI peripheral
|
||||
match SPI_BUS_SEL {
|
||||
SpiBusSelect::SpiAPortA => {
|
||||
let (sck, mosi, miso) = (
|
||||
pinsa.pa31.into_funsel_1(),
|
||||
pinsa.pa30.into_funsel_1(),
|
||||
pinsa.pa29.into_funsel_1(),
|
||||
);
|
||||
let mut spia = Spi::spia(
|
||||
dp.spia,
|
||||
(sck, miso, mosi),
|
||||
50.MHz(),
|
||||
spi_cfg,
|
||||
Some(&mut dp.sysconfig),
|
||||
None,
|
||||
);
|
||||
spia.set_fill_word(FILL_WORD);
|
||||
spia_ref.borrow_mut().replace(spia.downgrade());
|
||||
}
|
||||
SpiBusSelect::SpiAPortB => {
|
||||
let (sck, mosi, miso) = (
|
||||
pinsb.pb9.into_funsel_2(),
|
||||
pinsb.pb8.into_funsel_2(),
|
||||
pinsb.pb7.into_funsel_2(),
|
||||
);
|
||||
let mut spia = Spi::spia(
|
||||
dp.spia,
|
||||
(sck, miso, mosi),
|
||||
50.MHz(),
|
||||
spi_cfg,
|
||||
Some(&mut dp.sysconfig),
|
||||
None,
|
||||
);
|
||||
spia.set_fill_word(FILL_WORD);
|
||||
spia_ref.borrow_mut().replace(spia.downgrade());
|
||||
}
|
||||
SpiBusSelect::SpiBPortB => {
|
||||
let (sck, mosi, miso) = (
|
||||
pinsb.pb5.into_funsel_1(),
|
||||
pinsb.pb4.into_funsel_1(),
|
||||
pinsb.pb3.into_funsel_1(),
|
||||
);
|
||||
let mut spib = Spi::spib(
|
||||
dp.spib,
|
||||
(sck, miso, mosi),
|
||||
50.MHz(),
|
||||
spi_cfg,
|
||||
Some(&mut dp.sysconfig),
|
||||
None,
|
||||
);
|
||||
spib.set_fill_word(FILL_WORD);
|
||||
spib_ref.borrow_mut().replace(spib.downgrade());
|
||||
}
|
||||
}
|
||||
// Configure transfer specific properties here
|
||||
match SPI_BUS_SEL {
|
||||
SpiBusSelect::SpiAPortA | SpiBusSelect::SpiAPortB => {
|
||||
if let Some(ref mut spi) = *spia_ref.borrow_mut() {
|
||||
let transfer_cfg =
|
||||
TransferConfig::new_no_hw_cs(SPI_SPEED_KHZ.kHz(), SPI_MODE, BLOCKMODE, false);
|
||||
spi.cfg_transfer(&transfer_cfg);
|
||||
}
|
||||
}
|
||||
SpiBusSelect::SpiBPortB => {
|
||||
if let Some(ref mut spi) = *spib_ref.borrow_mut() {
|
||||
let hw_cs_pin = pinsb.pb2.into_funsel_1();
|
||||
let transfer_cfg = TransferConfig::new(
|
||||
SPI_SPEED_KHZ.kHz(),
|
||||
SPI_MODE,
|
||||
Some(hw_cs_pin),
|
||||
BLOCKMODE,
|
||||
false,
|
||||
);
|
||||
spi.cfg_transfer(&transfer_cfg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Application logic
|
||||
loop {
|
||||
let mut reply_buf: [u8; 8] = [0; 8];
|
||||
match SPI_BUS_SEL {
|
||||
SpiBusSelect::SpiAPortA | SpiBusSelect::SpiAPortB => {
|
||||
if let Some(ref mut spi) = *spia_ref.borrow_mut() {
|
||||
if EXAMPLE_SEL == ExampleSelect::Loopback {
|
||||
// Can't really verify correct reply here.
|
||||
spi.write(&[0x42]).expect("write failed");
|
||||
// Because of the loopback mode, we should get back the fill word here.
|
||||
spi.read(&mut reply_buf[0..1]).unwrap();
|
||||
assert_eq!(reply_buf[0], FILL_WORD);
|
||||
delay.delay_ms(500_u32);
|
||||
|
||||
let tx_buf: [u8; 3] = [0x01, 0x02, 0x03];
|
||||
spi.transfer(&mut reply_buf[0..3], &tx_buf).unwrap();
|
||||
assert_eq!(tx_buf, reply_buf[0..3]);
|
||||
rprintln!(
|
||||
"Received reply: {}, {}, {}",
|
||||
reply_buf[0],
|
||||
reply_buf[1],
|
||||
reply_buf[2]
|
||||
);
|
||||
delay.delay_ms(500_u32);
|
||||
|
||||
let mut tx_rx_buf: [u8; 3] = [0x03, 0x02, 0x01];
|
||||
spi.transfer_in_place(&mut tx_rx_buf).unwrap();
|
||||
rprintln!(
|
||||
"Received reply: {}, {}, {}",
|
||||
tx_rx_buf[0],
|
||||
tx_rx_buf[1],
|
||||
tx_rx_buf[2]
|
||||
);
|
||||
assert_eq!(&tx_rx_buf[0..3], &[0x03, 0x02, 0x01]);
|
||||
} else {
|
||||
let send_buf: [u8; 3] = [0x01, 0x02, 0x03];
|
||||
spi.transfer(&mut reply_buf[0..3], &send_buf).unwrap();
|
||||
rprintln!(
|
||||
"Received reply: {}, {}, {}",
|
||||
reply_buf[0],
|
||||
reply_buf[1],
|
||||
reply_buf[2]
|
||||
);
|
||||
delay.delay_ms(1000_u32);
|
||||
}
|
||||
}
|
||||
}
|
||||
SpiBusSelect::SpiBPortB => {
|
||||
if let Some(ref mut spi) = *spib_ref.borrow_mut() {
|
||||
if EXAMPLE_SEL == ExampleSelect::Loopback {
|
||||
// Can't really verify correct reply here.
|
||||
spi.write(&[0x42]).expect("write failed");
|
||||
// Because of the loopback mode, we should get back the fill word here.
|
||||
spi.read(&mut reply_buf[0..1]).unwrap();
|
||||
assert_eq!(reply_buf[0], FILL_WORD);
|
||||
delay.delay_ms(500_u32);
|
||||
|
||||
let tx_buf: [u8; 3] = [0x01, 0x02, 0x03];
|
||||
spi.transfer(&mut reply_buf[0..3], &tx_buf).unwrap();
|
||||
assert_eq!(tx_buf, reply_buf[0..3]);
|
||||
rprintln!(
|
||||
"Received reply: {}, {}, {}",
|
||||
reply_buf[0],
|
||||
reply_buf[1],
|
||||
reply_buf[2]
|
||||
);
|
||||
delay.delay_ms(500_u32);
|
||||
|
||||
let mut tx_rx_buf: [u8; 3] = [0x03, 0x02, 0x01];
|
||||
spi.transfer_in_place(&mut tx_rx_buf).unwrap();
|
||||
rprintln!(
|
||||
"Received reply: {}, {}, {}",
|
||||
tx_rx_buf[0],
|
||||
tx_rx_buf[1],
|
||||
tx_rx_buf[2]
|
||||
);
|
||||
assert_eq!(&tx_rx_buf[0..3], &[0x03, 0x02, 0x01]);
|
||||
} else {
|
||||
let send_buf: [u8; 3] = [0x01, 0x02, 0x03];
|
||||
spi.transfer(&mut reply_buf[0..3], &send_buf).unwrap();
|
||||
rprintln!(
|
||||
"Received reply: {}, {}, {}",
|
||||
reply_buf[0],
|
||||
reply_buf[1],
|
||||
reply_buf[2]
|
||||
);
|
||||
delay.delay_ms(1000_u32);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[interrupt]
|
||||
#[allow(non_snake_case)]
|
||||
fn OC0() {
|
||||
default_ms_irq_handler()
|
||||
}
|
118
examples/simple/examples/timer-ticks.rs
Normal file
118
examples/simple/examples/timer-ticks.rs
Normal file
@ -0,0 +1,118 @@
|
||||
//! MS and Second counter implemented using the TIM0 and TIM1 peripheral
|
||||
#![no_main]
|
||||
#![no_std]
|
||||
|
||||
use core::cell::Cell;
|
||||
use cortex_m::interrupt::Mutex;
|
||||
use cortex_m_rt::entry;
|
||||
use panic_rtt_target as _;
|
||||
use rtt_target::{rprintln, rtt_init_print};
|
||||
use va108xx_hal::{
|
||||
clock::{get_sys_clock, set_sys_clock},
|
||||
pac::{self, interrupt},
|
||||
prelude::*,
|
||||
time::Hertz,
|
||||
timer::{default_ms_irq_handler, set_up_ms_tick, CountDownTimer, Event, IrqCfg, MS_COUNTER},
|
||||
};
|
||||
|
||||
#[allow(dead_code)]
|
||||
enum LibType {
|
||||
Pac,
|
||||
Hal,
|
||||
}
|
||||
|
||||
static SEC_COUNTER: Mutex<Cell<u32>> = Mutex::new(Cell::new(0));
|
||||
|
||||
#[entry]
|
||||
fn main() -> ! {
|
||||
rtt_init_print!();
|
||||
let mut dp = pac::Peripherals::take().unwrap();
|
||||
let mut last_ms = 0;
|
||||
rprintln!("-- Vorago system ticks using timers --");
|
||||
set_sys_clock(50.MHz());
|
||||
let lib_type = LibType::Hal;
|
||||
match lib_type {
|
||||
LibType::Pac => {
|
||||
unsafe {
|
||||
dp.sysconfig
|
||||
.peripheral_clk_enable()
|
||||
.modify(|_, w| w.irqsel().set_bit());
|
||||
dp.sysconfig
|
||||
.tim_clk_enable()
|
||||
.modify(|r, w| w.bits(r.bits() | (1 << 0) | (1 << 1)));
|
||||
dp.irqsel.tim0(0).write(|w| w.bits(0x00));
|
||||
dp.irqsel.tim0(1).write(|w| w.bits(0x01));
|
||||
}
|
||||
|
||||
let sys_clk: Hertz = 50.MHz();
|
||||
let cnt_ms = sys_clk.raw() / 1000 - 1;
|
||||
let cnt_sec = sys_clk.raw() - 1;
|
||||
unsafe {
|
||||
dp.tim0.cnt_value().write(|w| w.bits(cnt_ms));
|
||||
dp.tim0.rst_value().write(|w| w.bits(cnt_ms));
|
||||
dp.tim0.ctrl().write(|w| {
|
||||
w.enable().set_bit();
|
||||
w.irq_enb().set_bit()
|
||||
});
|
||||
dp.tim1.cnt_value().write(|w| w.bits(cnt_sec));
|
||||
dp.tim1.rst_value().write(|w| w.bits(cnt_sec));
|
||||
dp.tim1.ctrl().write(|w| {
|
||||
w.enable().set_bit();
|
||||
w.irq_enb().set_bit()
|
||||
});
|
||||
unmask_irqs();
|
||||
}
|
||||
}
|
||||
LibType::Hal => {
|
||||
set_up_ms_tick(
|
||||
IrqCfg::new(interrupt::OC0, true, true),
|
||||
&mut dp.sysconfig,
|
||||
Some(&mut dp.irqsel),
|
||||
50.MHz(),
|
||||
dp.tim0,
|
||||
);
|
||||
let mut second_timer =
|
||||
CountDownTimer::new(&mut dp.sysconfig, get_sys_clock().unwrap(), dp.tim1);
|
||||
second_timer.listen(
|
||||
Event::TimeOut,
|
||||
IrqCfg::new(interrupt::OC1, true, true),
|
||||
Some(&mut dp.irqsel),
|
||||
Some(&mut dp.sysconfig),
|
||||
);
|
||||
second_timer.start(1.Hz());
|
||||
}
|
||||
}
|
||||
loop {
|
||||
let current_ms = cortex_m::interrupt::free(|cs| MS_COUNTER.borrow(cs).get());
|
||||
if current_ms - last_ms >= 1000 {
|
||||
last_ms = current_ms;
|
||||
rprintln!("MS counter: {}", current_ms);
|
||||
let second = cortex_m::interrupt::free(|cs| SEC_COUNTER.borrow(cs).get());
|
||||
rprintln!("Second counter: {}", second);
|
||||
}
|
||||
cortex_m::asm::delay(10000);
|
||||
}
|
||||
}
|
||||
|
||||
fn unmask_irqs() {
|
||||
unsafe {
|
||||
cortex_m::peripheral::NVIC::unmask(pac::Interrupt::OC0);
|
||||
cortex_m::peripheral::NVIC::unmask(pac::Interrupt::OC1);
|
||||
}
|
||||
}
|
||||
|
||||
#[interrupt]
|
||||
#[allow(non_snake_case)]
|
||||
fn OC0() {
|
||||
default_ms_irq_handler()
|
||||
}
|
||||
|
||||
#[interrupt]
|
||||
#[allow(non_snake_case)]
|
||||
fn OC1() {
|
||||
cortex_m::interrupt::free(|cs| {
|
||||
let mut sec = SEC_COUNTER.borrow(cs).get();
|
||||
sec += 1;
|
||||
SEC_COUNTER.borrow(cs).set(sec);
|
||||
});
|
||||
}
|
168
examples/simple/examples/uart-irq-rtic.rs
Normal file
168
examples/simple/examples/uart-irq-rtic.rs
Normal file
@ -0,0 +1,168 @@
|
||||
//! 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 embedded_io::Write;
|
||||
use rtic_monotonics::systick::Systick;
|
||||
use rtic_sync::make_channel;
|
||||
use panic_rtt_target as _;
|
||||
use rtt_target::{rprintln, rtt_init_print};
|
||||
use va108xx_hal::{
|
||||
time::Hertz,
|
||||
gpio::PinsB,
|
||||
pac,
|
||||
prelude::*,
|
||||
uart::{self, IrqCfg, IrqResult, UartWithIrqBase},
|
||||
};
|
||||
|
||||
#[local]
|
||||
struct Local {
|
||||
rx_info_tx: rtic_sync::channel::Sender<'static, RxInfo, 3>,
|
||||
rx_info_rx: rtic_sync::channel::Receiver<'static, RxInfo, 3>,
|
||||
}
|
||||
|
||||
#[shared]
|
||||
struct Shared {
|
||||
irq_uart: UartWithIrqBase<pac::Uartb>,
|
||||
rx_buf: [u8; 64],
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
struct RxInfo {
|
||||
pub bytes_read: usize,
|
||||
pub end_idx: usize,
|
||||
pub timeout: bool,
|
||||
}
|
||||
|
||||
#[init]
|
||||
fn init(cx: init::Context) -> (Shared, Local) {
|
||||
rtt_init_print!();
|
||||
//set_print_channel(channels.up.0);
|
||||
rprintln!("-- VA108xx UART IRQ example application--");
|
||||
|
||||
// Initialize the systick interrupt & obtain the token to prove that we did
|
||||
let systick_mono_token = rtic_monotonics::create_systick_token!();
|
||||
Systick::start(cx.core.SYST, Hertz::from(50.MHz()).raw(), systick_mono_token);
|
||||
|
||||
let mut dp = cx.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.Hz(), &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_info_tx, rx_info_rx) = make_channel!(RxInfo, 3);
|
||||
let rx_buf: [u8; 64] = [0; 64];
|
||||
//reply_handler::spawn().expect("spawning reply handler failed");
|
||||
(
|
||||
Shared { irq_uart, rx_buf },
|
||||
Local {
|
||||
rx_info_tx,
|
||||
rx_info_rx,
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
// `shared` cannot be accessed from this context
|
||||
#[idle]
|
||||
fn idle(_cx: idle::Context) -> ! {
|
||||
loop {
|
||||
cortex_m::asm::nop();
|
||||
}
|
||||
}
|
||||
|
||||
#[task(
|
||||
binds = OC3,
|
||||
shared = [irq_uart, rx_buf],
|
||||
local = [cnt: u32 = 0, result: IrqResult = IrqResult::new(), rx_info_tx],
|
||||
)]
|
||||
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);
|
||||
cx.local
|
||||
.rx_info_tx
|
||||
.try_send(RxInfo {
|
||||
bytes_read: result.bytes_read,
|
||||
end_idx,
|
||||
timeout: result.timeout(),
|
||||
})
|
||||
.expect("RX queue full");
|
||||
}
|
||||
*cnt += 1;
|
||||
}
|
||||
|
||||
#[task(shared = [irq_uart, rx_buf], local = [rx_info_rx], priority=1)]
|
||||
async fn reply_handler(cx: reply_handler::Context) {
|
||||
let mut irq_uart = cx.shared.irq_uart;
|
||||
let mut rx_buf = cx.shared.rx_buf;
|
||||
loop {
|
||||
match cx.local.rx_info_rx.recv().await {
|
||||
Ok(rx_info) => {
|
||||
rprintln!("reception success, {} bytes read", rx_info.bytes_read);
|
||||
if rx_info.timeout {
|
||||
rprintln!("timeout occurred");
|
||||
}
|
||||
rx_buf.lock(|rx_buf| {
|
||||
let string = core::str::from_utf8(&rx_buf[0..rx_info.end_idx])
|
||||
.expect("Invalid string format");
|
||||
rprintln!("read string: {}", string);
|
||||
irq_uart.lock(|uart| {
|
||||
writeln!(uart.uart, "{}", string).expect("Sending reply failed");
|
||||
});
|
||||
});
|
||||
}
|
||||
Err(e) => {
|
||||
rprintln!("error receiving RX info: {:?}", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
47
examples/simple/examples/uart.rs
Normal file
47
examples/simple/examples/uart.rs
Normal file
@ -0,0 +1,47 @@
|
||||
//! UART example application. Sends a test string over a UART and then enters
|
||||
//! echo mode.
|
||||
//!
|
||||
//! Instructions:
|
||||
//!
|
||||
//! 1. Tie a USB to UART converter with RX to PA9 and TX to PA8.
|
||||
//! 2. Connect to the serial interface by using an application like Putty or picocom.
|
||||
//! You should set a "Hello World" print when the application starts. After that, everything
|
||||
//! typed on the console should be printed back by the echo application.
|
||||
#![no_main]
|
||||
#![no_std]
|
||||
|
||||
use cortex_m_rt::entry;
|
||||
use embedded_hal_nb::{nb, serial::Read};
|
||||
use embedded_io::Write as _;
|
||||
use panic_rtt_target as _;
|
||||
use rtt_target::{rprintln, rtt_init_print};
|
||||
use va108xx_hal::{gpio::PinsA, pac, prelude::*, uart};
|
||||
|
||||
#[entry]
|
||||
fn main() -> ! {
|
||||
rtt_init_print!();
|
||||
rprintln!("-- VA108xx UART example application--");
|
||||
|
||||
let mut dp = pac::Peripherals::take().unwrap();
|
||||
|
||||
let gpioa = PinsA::new(&mut dp.sysconfig, Some(dp.ioconfig), dp.porta);
|
||||
let tx = gpioa.pa9.into_funsel_2();
|
||||
let rx = gpioa.pa8.into_funsel_2();
|
||||
|
||||
let uarta = uart::Uart::uarta(dp.uarta, (tx, rx), 115200.Hz(), &mut dp.sysconfig, 50.MHz());
|
||||
let (mut tx, mut rx) = uarta.split();
|
||||
writeln!(tx, "Hello World\r").unwrap();
|
||||
loop {
|
||||
// Echo what is received on the serial link.
|
||||
match rx.read() {
|
||||
Ok(recv) => {
|
||||
nb::block!(embedded_hal_nb::serial::Write::write(&mut tx, recv))
|
||||
.expect("TX send error");
|
||||
}
|
||||
Err(nb::Error::WouldBlock) => (),
|
||||
Err(nb::Error::Other(uart_error)) => {
|
||||
rprintln!("UART receive error {:?}", uart_error);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
13
examples/simple/src/main.rs
Normal file
13
examples/simple/src/main.rs
Normal file
@ -0,0 +1,13 @@
|
||||
//! Dummy app which does not do anything.
|
||||
#![no_main]
|
||||
#![no_std]
|
||||
|
||||
use cortex_m_rt::entry;
|
||||
use panic_rtt_target as _;
|
||||
|
||||
#[entry]
|
||||
fn main() -> ! {
|
||||
loop {
|
||||
cortex_m::asm::nop();
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user