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
This commit is contained in:
Robin Müller 2021-12-20 23:49:07 +01:00
commit 482a725ef7
22 changed files with 1088 additions and 150 deletions

View File

@ -6,7 +6,18 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](http://keepachangelog.com/) The format is based on [Keep a Changelog](http://keepachangelog.com/)
and this project adheres to [Semantic Versioning](http://semver.org/). and this project adheres to [Semantic Versioning](http://semver.org/).
## [unreleased] ## [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] ## [v0.4.3]

View File

@ -1,6 +1,6 @@
[package] [package]
name = "va108xx-hal" name = "va108xx-hal"
version = "0.4.3" version = "0.5.0"
authors = ["Robin Mueller <muellerr@irs.uni-stuttgart.de>"] authors = ["Robin Mueller <muellerr@irs.uni-stuttgart.de>"]
edition = "2021" edition = "2021"
description = "HAL for the Vorago VA108xx family of microcontrollers" description = "HAL for the Vorago VA108xx family of microcontrollers"
@ -27,6 +27,7 @@ version = "0.2.4"
rt = ["va108xx/rt"] rt = ["va108xx/rt"]
[dev-dependencies] [dev-dependencies]
cortex-m-rtic = "0.6.0-rc.4"
panic-rtt-target = { version = "0.1", features = ["cortex-m"] } panic-rtt-target = { version = "0.1", features = ["cortex-m"] }
rtt-target = { version = "0.3", features = ["cortex-m"] } rtt-target = { version = "0.3", features = ["cortex-m"] }
panic-halt = "0.2" panic-halt = "0.2"
@ -55,9 +56,9 @@ name = "tests"
required-features = ["rt"] required-features = ["rt"]
[[example]] [[example]]
name = "pwm" name = "cascade"
required-features = ["rt"] required-features = ["rt"]
[[example]] [[example]]
name = "cascade" name = "uart-irq-rtic"
required-features = ["rt"] required-features = ["rt"]

View File

@ -9,7 +9,7 @@
use cortex_m_rt::entry; use cortex_m_rt::entry;
use embedded_hal::digital::v2::ToggleableOutputPin; use embedded_hal::digital::v2::ToggleableOutputPin;
use panic_halt as _; use panic_halt as _;
use va108xx_hal::{gpio::PinsA, pac, prelude::*, timer::set_up_ms_timer}; use va108xx_hal::{gpio::PinsA, pac, prelude::*, timer::CountDownTimer};
#[entry] #[entry]
fn main() -> ! { fn main() -> ! {
@ -18,13 +18,7 @@ fn main() -> ! {
let mut led1 = porta.pa10.into_push_pull_output(); let mut led1 = porta.pa10.into_push_pull_output();
let mut led2 = porta.pa7.into_push_pull_output(); let mut led2 = porta.pa7.into_push_pull_output();
let mut led3 = porta.pa6.into_push_pull_output(); let mut led3 = porta.pa6.into_push_pull_output();
let mut delay = set_up_ms_timer( let mut delay = CountDownTimer::new(&mut dp.SYSCONFIG, 50.mhz(), dp.TIM0);
&mut dp.SYSCONFIG,
&mut dp.IRQSEL,
50.mhz().into(),
dp.TIM0,
pac::Interrupt::OC0,
);
for _ in 0..10 { for _ in 0..10 {
led1.set_low().ok(); led1.set_low().ok();
led2.set_low().ok(); led2.set_low().ok();

View File

@ -14,8 +14,8 @@ use va108xx_hal::{
pac::{self, interrupt, TIM4, TIM5}, pac::{self, interrupt, TIM4, TIM5},
prelude::*, prelude::*,
timer::{ timer::{
default_ms_irq_handler, set_up_ms_timer, CascadeCtrl, CascadeSource, CountDownTimer, Delay, default_ms_irq_handler, set_up_ms_delay_provider, CascadeCtrl, CascadeSource,
Event, CountDownTimer, Event, IrqCfg,
}, },
}; };
@ -28,23 +28,16 @@ fn main() -> ! {
rprintln!("-- VA108xx Cascade example application--"); rprintln!("-- VA108xx Cascade example application--");
let mut dp = pac::Peripherals::take().unwrap(); let mut dp = pac::Peripherals::take().unwrap();
let timer = set_up_ms_timer( let mut delay = set_up_ms_delay_provider(&mut dp.SYSCONFIG, 50.mhz(), dp.TIM0);
&mut dp.SYSCONFIG,
&mut dp.IRQSEL,
50.mhz().into(),
dp.TIM0,
pac::Interrupt::OC0,
);
let mut delay = Delay::new(timer);
// Will be started periodically to trigger a cascade // Will be started periodically to trigger a cascade
let mut cascade_triggerer = let mut cascade_triggerer =
CountDownTimer::new(&mut dp.SYSCONFIG, 50.mhz(), dp.TIM3).auto_disable(true); CountDownTimer::new(&mut dp.SYSCONFIG, 50.mhz(), dp.TIM3).auto_disable(true);
cascade_triggerer.listen( cascade_triggerer.listen(
Event::TimeOut, Event::TimeOut,
&mut dp.SYSCONFIG, IrqCfg::new(va108xx::Interrupt::OC1, true, false),
&mut dp.IRQSEL, Some(&mut dp.IRQSEL),
va108xx::Interrupt::OC1, Some(&mut dp.SYSCONFIG),
); );
// First target for cascade // First target for cascade
@ -63,9 +56,9 @@ fn main() -> ! {
// the timer expires // the timer expires
cascade_target_1.listen( cascade_target_1.listen(
Event::TimeOut, Event::TimeOut,
&mut dp.SYSCONFIG, IrqCfg::new(va108xx::Interrupt::OC2, true, false),
&mut dp.IRQSEL, Some(&mut dp.IRQSEL),
va108xx::Interrupt::OC2, Some(&mut dp.SYSCONFIG),
); );
// The counter will only activate when the cascade signal is coming in so // 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 // it is okay to call start here to set the reset value
@ -89,9 +82,9 @@ fn main() -> ! {
// the timer expires // the timer expires
cascade_target_2.listen( cascade_target_2.listen(
Event::TimeOut, Event::TimeOut,
&mut dp.SYSCONFIG, IrqCfg::new(va108xx::Interrupt::OC3, true, false),
&mut dp.IRQSEL, Some(&mut dp.IRQSEL),
va108xx::Interrupt::OC3, Some(&mut dp.SYSCONFIG),
); );
// The counter will only activate when the cascade signal is coming in so // 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 // it is okay to call start here to set the reset value
@ -112,7 +105,7 @@ fn main() -> ! {
loop { loop {
rprintln!("-- Triggering cascade in 0.5 seconds --"); rprintln!("-- Triggering cascade in 0.5 seconds --");
cascade_triggerer.start(2.hz()); 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 rtt_target::{rprintln, rtt_init_print};
use va108xx_hal::{ use va108xx_hal::{
gpio::PinsA, gpio::PinsA,
pac::{self, interrupt}, pac,
prelude::*, prelude::*,
pwm::{self, get_duty_from_percent, ReducedPwmPin, PWMA, PWMB}, 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] #[entry]
@ -26,17 +26,7 @@ fn main() -> ! {
&mut dp.SYSCONFIG, &mut dp.SYSCONFIG,
10.hz(), 10.hz(),
); );
let timer = set_up_ms_timer( let mut delay = set_up_ms_delay_provider(&mut dp.SYSCONFIG, 50.mhz(), dp.TIM0);
&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 current_duty_cycle = 0.0; let mut current_duty_cycle = 0.0;
PwmPin::set_duty(&mut pwm, get_duty_from_percent(current_duty_cycle)); PwmPin::set_duty(&mut pwm, get_duty_from_percent(current_duty_cycle));
PwmPin::enable(&mut pwm); PwmPin::enable(&mut pwm);
@ -46,7 +36,7 @@ fn main() -> ! {
loop { loop {
// Increase duty cycle continuously // Increase duty cycle continuously
while current_duty_cycle < 1.0 { while current_duty_cycle < 1.0 {
delay.delay_ms(200); delay.delay_ms(200_u16);
current_duty_cycle += 0.02; current_duty_cycle += 0.02;
PwmPin::set_duty(&mut reduced_pin, get_duty_from_percent(current_duty_cycle)); 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_lower_limit(get_duty_from_percent(lower_limit));
pwmb.set_pwmb_upper_limit(get_duty_from_percent(upper_limit)); pwmb.set_pwmb_upper_limit(get_duty_from_percent(upper_limit));
while lower_limit < 0.5 { while lower_limit < 0.5 {
delay.delay_ms(200); delay.delay_ms(200_u16);
lower_limit += 0.01; lower_limit += 0.01;
upper_limit -= 0.01; upper_limit -= 0.01;
pwmb.set_pwmb_lower_limit(get_duty_from_percent(lower_limit)); pwmb.set_pwmb_lower_limit(get_duty_from_percent(lower_limit));
@ -71,8 +61,3 @@ fn main() -> ! {
reduced_pin = ReducedPwmPin::<PWMA>::from(pwmb); 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

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

View File

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

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

@ -0,0 +1,119 @@
//! UART example application. Sends a test string over a UART and then enters
//! echo mode
#![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

@ -66,8 +66,8 @@ use super::{
}; };
use crate::{ use crate::{
clock::FilterClkSel, clock::FilterClkSel,
pac::{self, IRQSEL, SYSCONFIG}, pac::{IRQSEL, SYSCONFIG},
utility::Funsel, utility::{Funsel, IrqCfg},
}; };
use embedded_hal::digital::v2::{InputPin, OutputPin, ToggleableOutputPin}; use embedded_hal::digital::v2::{InputPin, OutputPin, ToggleableOutputPin};
use paste::paste; use paste::paste;
@ -344,14 +344,14 @@ impl DynPin {
pub fn interrupt_edge( pub fn interrupt_edge(
mut self, mut self,
edge_type: InterruptEdge, edge_type: InterruptEdge,
irq_cfg: IrqCfg,
syscfg: Option<&mut SYSCONFIG>, syscfg: Option<&mut SYSCONFIG>,
irqsel: &mut IRQSEL, irqsel: Option<&mut IRQSEL>,
interrupt: pac::Interrupt,
) -> Result<Self, PinError> { ) -> Result<Self, PinError> {
match self.mode { match self.mode {
DynPinMode::Input(_) | DynPinMode::Output(_) => { DynPinMode::Input(_) | DynPinMode::Output(_) => {
self._irq_enb(syscfg, irqsel, interrupt);
self.regs.interrupt_edge(edge_type); self.regs.interrupt_edge(edge_type);
self.irq_enb(irq_cfg, syscfg, irqsel);
Ok(self) Ok(self)
} }
_ => Err(PinError::InvalidPinType), _ => Err(PinError::InvalidPinType),
@ -361,14 +361,14 @@ impl DynPin {
pub fn interrupt_level( pub fn interrupt_level(
mut self, mut self,
level_type: InterruptLevel, level_type: InterruptLevel,
irq_cfg: IrqCfg,
syscfg: Option<&mut SYSCONFIG>, syscfg: Option<&mut SYSCONFIG>,
irqsel: &mut IRQSEL, irqsel: Option<&mut IRQSEL>,
interrupt: crate::pac::Interrupt,
) -> Result<Self, PinError> { ) -> Result<Self, PinError> {
match self.mode { match self.mode {
DynPinMode::Input(_) | DynPinMode::Output(_) => { DynPinMode::Input(_) | DynPinMode::Output(_) => {
self._irq_enb(syscfg, irqsel, interrupt);
self.regs.interrupt_level(level_type); self.regs.interrupt_level(level_type);
self.irq_enb(irq_cfg, syscfg, irqsel);
Ok(self) Ok(self)
} }
_ => Err(PinError::InvalidPinType), _ => Err(PinError::InvalidPinType),

View File

@ -92,8 +92,9 @@
use super::dynpins::{DynAlternate, DynGroup, DynInput, DynOutput, DynPinId, DynPinMode}; use super::dynpins::{DynAlternate, DynGroup, DynInput, DynOutput, DynPinId, DynPinMode};
use super::reg::RegisterInterface; use super::reg::RegisterInterface;
use crate::{ use crate::{
pac::{self, IOCONFIG, IRQSEL, PORTA, PORTB, SYSCONFIG}, pac::{IOCONFIG, IRQSEL, PORTA, PORTB, SYSCONFIG},
typelevel::Is, typelevel::Is,
utility::IrqCfg,
Sealed, Sealed,
}; };
use core::convert::Infallible; use core::convert::Infallible;
@ -397,11 +398,11 @@ macro_rules! common_reg_if_functions {
self.regs.write_pin_masked(false) self.regs.write_pin_masked(false)
} }
fn _irq_enb( fn irq_enb(
&mut self, &mut self,
irq_cfg: crate::utility::IrqCfg,
syscfg: Option<&mut va108xx::SYSCONFIG>, syscfg: Option<&mut va108xx::SYSCONFIG>,
irqsel: &mut va108xx::IRQSEL, irqsel: Option<&mut va108xx::IRQSEL>,
interrupt: va108xx::Interrupt,
) { ) {
if syscfg.is_some() { if syscfg.is_some() {
crate::clock::enable_peripheral_clock( crate::clock::enable_peripheral_clock(
@ -410,15 +411,19 @@ macro_rules! common_reg_if_functions {
); );
} }
self.regs.enable_irq(); self.regs.enable_irq();
match self.regs.id().group { if let Some(irqsel) = irqsel {
// Set the correct interrupt number in the IRQSEL register if irq_cfg.route {
DynGroup::A => { match self.regs.id().group {
irqsel.porta[self.regs.id().num as usize] // Set the correct interrupt number in the IRQSEL register
.write(|w| unsafe { w.bits(interrupt as u32) }); DynGroup::A => {
} irqsel.porta[self.regs.id().num as usize]
DynGroup::B => { .write(|w| unsafe { w.bits(irq_cfg.irq as u32) });
irqsel.portb[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(irq_cfg.irq as u32) });
}
}
} }
} }
} }
@ -577,24 +582,24 @@ impl<I: PinId, C: InputConfig> Pin<I, Input<C>> {
pub fn interrupt_edge( pub fn interrupt_edge(
mut self, mut self,
edge_type: InterruptEdge, edge_type: InterruptEdge,
irq_cfg: IrqCfg,
syscfg: Option<&mut SYSCONFIG>, syscfg: Option<&mut SYSCONFIG>,
irqsel: &mut IRQSEL, irqsel: Option<&mut IRQSEL>,
interrupt: pac::Interrupt,
) -> Self { ) -> Self {
self._irq_enb(syscfg, irqsel, interrupt);
self.regs.interrupt_edge(edge_type); self.regs.interrupt_edge(edge_type);
self.irq_enb(irq_cfg, syscfg, irqsel);
self self
} }
pub fn interrupt_level( pub fn interrupt_level(
mut self, mut self,
level_type: InterruptLevel, level_type: InterruptLevel,
irq_cfg: IrqCfg,
syscfg: Option<&mut SYSCONFIG>, syscfg: Option<&mut SYSCONFIG>,
irqsel: &mut IRQSEL, irqsel: Option<&mut IRQSEL>,
interrupt: pac::Interrupt,
) -> Self { ) -> Self {
self._irq_enb(syscfg, irqsel, interrupt);
self.regs.interrupt_level(level_type); self.regs.interrupt_level(level_type);
self.irq_enb(irq_cfg, syscfg, irqsel);
self self
} }
} }
@ -622,24 +627,24 @@ impl<I: PinId, C: OutputConfig> Pin<I, Output<C>> {
pub fn interrupt_edge( pub fn interrupt_edge(
mut self, mut self,
edge_type: InterruptEdge, edge_type: InterruptEdge,
irq_cfg: IrqCfg,
syscfg: Option<&mut SYSCONFIG>, syscfg: Option<&mut SYSCONFIG>,
irqsel: &mut IRQSEL, irqsel: Option<&mut IRQSEL>,
interrupt: pac::Interrupt,
) -> Self { ) -> Self {
self._irq_enb(syscfg, irqsel, interrupt);
self.regs.interrupt_edge(edge_type); self.regs.interrupt_edge(edge_type);
self.irq_enb(irq_cfg, syscfg, irqsel);
self self
} }
pub fn interrupt_level( pub fn interrupt_level(
mut self, mut self,
level_type: InterruptLevel, level_type: InterruptLevel,
irq_cfg: IrqCfg,
syscfg: Option<&mut SYSCONFIG>, syscfg: Option<&mut SYSCONFIG>,
irqsel: &mut IRQSEL, irqsel: Option<&mut IRQSEL>,
interrupt: pac::Interrupt,
) -> Self { ) -> Self {
self._irq_enb(syscfg, irqsel, interrupt);
self.regs.interrupt_level(level_type); self.regs.interrupt_level(level_type);
self.irq_enb(irq_cfg, syscfg, irqsel);
self self
} }
} }

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) //! - [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) //! - [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::{ use crate::{
clock::{enable_peripheral_clock, PeripheralClocks}, clock::{enable_peripheral_clock, PeripheralClocks},
gpio::{ gpio::{
@ -20,6 +21,7 @@ use crate::{
private::Sealed, private::Sealed,
time::Hertz, time::Hertz,
timer, timer,
utility::unmask_irq,
}; };
use core::cell::Cell; use core::cell::Cell;
use cortex_m::interrupt::Mutex; use cortex_m::interrupt::Mutex;
@ -27,7 +29,7 @@ use embedded_hal::{
blocking::delay, blocking::delay,
timer::{Cancel, CountDown, Periodic}, timer::{Cancel, CountDown, Periodic},
}; };
use va108xx::{Interrupt, IRQSEL, SYSCONFIG}; use va108xx::{IRQSEL, SYSCONFIG};
use void::Void; use void::Void;
const IRQ_DST_NONE: u32 = 0xffffffff; const IRQ_DST_NONE: u32 = 0xffffffff;
@ -386,6 +388,7 @@ unsafe impl TimPinInterface for TimDynRegister {
pub struct CountDownTimer<TIM: ValidTim> { pub struct CountDownTimer<TIM: ValidTim> {
tim: TimRegister<TIM>, tim: TimRegister<TIM>,
curr_freq: Hertz, curr_freq: Hertz,
irq_cfg: Option<IrqCfg>,
sys_clk: Hertz, sys_clk: Hertz,
rst_val: u32, rst_val: u32,
last_cnt: u32, last_cnt: u32,
@ -482,6 +485,7 @@ impl<TIM: ValidTim> CountDownTimer<TIM> {
let cd_timer = CountDownTimer { let cd_timer = CountDownTimer {
tim: unsafe { TimRegister::new(tim) }, tim: unsafe { TimRegister::new(tim) },
sys_clk: sys_clk.into(), sys_clk: sys_clk.into(),
irq_cfg: None,
rst_val: 0, rst_val: 0,
curr_freq: 0.hz(), curr_freq: 0.hz(),
listening: false, listening: false,
@ -491,21 +495,28 @@ impl<TIM: ValidTim> CountDownTimer<TIM> {
cd_timer cd_timer
} }
/// Listen for events. This also actives the IRQ in the IRQSEL register /// Listen for events. Depending on the IRQ configuration, this also activates the IRQ in the
/// for the provided interrupt. It also actives the peripheral clock for /// IRQSEL peripheral for the provided interrupt and unmasks the interrupt
/// IRQSEL
pub fn listen( pub fn listen(
&mut self, &mut self,
event: Event, event: Event,
syscfg: &mut SYSCONFIG, irq_cfg: IrqCfg,
irqsel: &mut IRQSEL, irq_sel: Option<&mut IRQSEL>,
interrupt: Interrupt, sys_cfg: Option<&mut SYSCONFIG>,
) { ) {
match event { match event {
Event::TimeOut => { Event::TimeOut => {
enable_peripheral_clock(syscfg, PeripheralClocks::Irqsel); cortex_m::peripheral::NVIC::mask(irq_cfg.irq);
irqsel.tim[TIM::TIM_ID as usize].write(|w| unsafe { w.bits(interrupt as u32) }); self.irq_cfg = Some(irq_cfg);
self.enable_interrupt(); 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; self.listening = true;
} }
} }
@ -554,6 +565,12 @@ impl<TIM: ValidTim> CountDownTimer<TIM> {
#[inline(always)] #[inline(always)]
pub fn enable(&mut self) { pub fn enable(&mut self) {
self.tim.reg().ctrl.modify(|_, w| w.enable().set_bit()); 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)] #[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 // Set up a millisecond timer on TIM0. Please note that you still need to unmask the related IRQ
// and provide an IRQ handler yourself // and provide an IRQ handler yourself
pub fn set_up_ms_timer( pub fn set_up_ms_timer<TIM: ValidTim>(
syscfg: &mut pac::SYSCONFIG, irq_cfg: IrqCfg,
irqsel: &mut pac::IRQSEL, sys_cfg: &mut pac::SYSCONFIG,
sys_clk: Hertz, irq_sel: Option<&mut pac::IRQSEL>,
tim0: TIM0, sys_clk: impl Into<Hertz>,
irq: pac::Interrupt, tim0: TIM,
) -> CountDownTimer<TIM0> { ) -> CountDownTimer<TIM> {
let mut ms_timer = CountDownTimer::new(syscfg, sys_clk, tim0); let mut ms_timer = CountDownTimer::new(sys_cfg, sys_clk, tim0);
ms_timer.listen(timer::Event::TimeOut, syscfg, irqsel, irq); ms_timer.listen(timer::Event::TimeOut, irq_cfg, irq_sel, Some(sys_cfg));
ms_timer.start(1000.hz()); ms_timer.start(1000.hz());
ms_timer 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 /// This function can be called in a specified interrupt handler to increment
/// the MS counter /// the MS counter
pub fn default_ms_irq_handler() { 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 /// 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 { impl embedded_hal::blocking::delay::DelayMs<u32> for Delay {
fn delay_ms(&mut self, ms: u32) { fn delay_ms(&mut self, ms: u32) {
if self.cd_tim.curr_freq() != 1000.hz() || !self.cd_tim.listening() { if self.cd_tim.curr_freq() != 1000.hz() || !self.cd_tim.listening() {

View File

@ -7,20 +7,25 @@ use core::{convert::Infallible, ptr};
use core::{marker::PhantomData, ops::Deref}; use core::{marker::PhantomData, ops::Deref};
use libm::floorf; use libm::floorf;
use crate::clock::enable_peripheral_clock; pub use crate::utility::IrqCfg;
use crate::{ use crate::{
clock, clock::{self, enable_peripheral_clock, PeripheralClocks},
gpio::pins::{ gpio::pins::{
AltFunc1, AltFunc2, AltFunc3, Pin, PA16, PA17, PA18, PA19, PA2, PA26, PA27, PA3, PA30, 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, 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::*, prelude::*,
time::{Bps, Hertz}, time::{Bps, Hertz},
utility::unmask_irq,
}; };
use embedded_hal::{blocking, serial}; use embedded_hal::{blocking, serial};
//==================================================================================================
// Type-Level support
//==================================================================================================
pub trait Pins<UART> {} pub trait Pins<UART> {}
impl Pins<UARTA> for (Pin<PA9, AltFunc2>, Pin<PA8, AltFunc2>) {} impl Pins<UARTA> for (Pin<PA9, AltFunc2>, Pin<PA8, AltFunc2>) {}
@ -38,12 +43,20 @@ 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<PB19, AltFunc2>, Pin<PB18, AltFunc2>) {}
impl Pins<UARTB> for (Pin<PB21, AltFunc1>, Pin<PB20, AltFunc1>) {} impl Pins<UARTB> for (Pin<PB21, AltFunc1>, Pin<PB20, AltFunc1>) {}
//==================================================================================================
// Regular Definitions
//==================================================================================================
#[derive(Debug)] #[derive(Debug)]
pub enum Error { pub enum Error {
Overrun, Overrun,
FramingError, FramingError,
ParityError, ParityError,
BreakCondition, BreakCondition,
TransferPending,
BufferTooShort,
/// Can be a combination of overrun, framing or parity error
IrqError,
} }
#[derive(Debug, PartialEq, Copy, Clone)] #[derive(Debug, PartialEq, Copy, Clone)]
@ -160,13 +173,139 @@ impl From<Bps> for Config {
} }
} }
/// Serial abstraction //==================================================================================================
pub struct Uart<UART, PINS> { // IRQ Definitions
//==================================================================================================
pub 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,
}
#[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)]
pub enum IrqReceptionMode {
Idle,
FixedLen,
VarLen,
}
//==================================================================================================
// UART implementation
//==================================================================================================
pub struct UartBase<UART> {
uart: UART, uart: UART,
pins: PINS,
tx: Tx<UART>, tx: Tx<UART>,
rx: Rx<UART>, rx: Rx<UART>,
} }
/// Serial abstraction
pub struct Uart<UART, PINS> {
uart_base: UartBase<UART>,
pins: PINS,
}
pub struct UartWithIrq<UART, PINS> {
irq_base: UartWithIrqBase<UART>,
pins: PINS,
}
pub struct UartWithIrqBase<UART> {
pub uart: UartBase<UART>,
irq_info: IrqInfo,
}
/// Serial receiver /// Serial receiver
pub struct Rx<UART> { pub struct Rx<UART> {
@ -196,12 +335,10 @@ impl<UART> Tx<UART> {
pub trait Instance: Deref<Target = uart_base::RegisterBlock> { pub trait Instance: Deref<Target = uart_base::RegisterBlock> {
fn ptr() -> *const uart_base::RegisterBlock; fn ptr() -> *const uart_base::RegisterBlock;
const IDX: u8;
} }
impl<UART, PINS> Uart<UART, PINS> impl<UART: Instance> UartBase<UART> {
where
UART: Instance,
{
/// This function assumes that the peripheral clock was alredy enabled /// This function assumes that the peripheral clock was alredy enabled
/// in the SYSCONFIG register /// in the SYSCONFIG register
fn init(self, config: Config, sys_clk: Hertz) -> Self { fn init(self, config: Config, sys_clk: Hertz) -> Self {
@ -247,7 +384,47 @@ where
self 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 { self.uart.irq_enb.modify(|_, w| match event {
Event::RxError => w.irq_rx_status().set_bit(), Event::RxError => w.irq_rx_status().set_bit(),
Event::RxFifoHalfFull => w.irq_rx().set_bit(), Event::RxFifoHalfFull => w.irq_rx().set_bit(),
@ -257,10 +434,9 @@ where
Event::TxFifoHalfFull => w.irq_tx().set_bit(), Event::TxFifoHalfFull => w.irq_tx().set_bit(),
Event::TxCts => w.irq_tx_cts().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 { self.uart.irq_enb.modify(|_, w| match event {
Event::RxError => w.irq_rx_status().clear_bit(), Event::RxError => w.irq_rx_status().clear_bit(),
Event::RxFifoHalfFull => w.irq_rx().clear_bit(), Event::RxFifoHalfFull => w.irq_rx().clear_bit(),
@ -270,10 +446,9 @@ where
Event::TxFifoHalfFull => w.irq_tx().clear_bit(), Event::TxFifoHalfFull => w.irq_tx().clear_bit(),
Event::TxCts => w.irq_tx_cts().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 // Clear the FIFO
self.uart.fifo_clr.write(|w| { self.uart.fifo_clr.write(|w| {
w.rxfifo().set_bit(); w.rxfifo().set_bit();
@ -283,7 +458,7 @@ where
w.rxenable().clear_bit(); w.rxenable().clear_bit();
w.txenable().clear_bit() w.txenable().clear_bit()
}); });
(self.uart, self.pins) self.uart
} }
pub fn split(self) -> (Tx<UART>, Rx<UART>) { pub fn split(self) -> (Tx<UART>, Rx<UART>) {
@ -291,14 +466,130 @@ 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
}
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 { macro_rules! uart_impl {
($($UARTX:ident: ($uartx:ident, $clk_enb_enum:path),)+) => { ($($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> { impl<PINS: Pins<$UARTX>> Uart<$UARTX, PINS> {
pub fn $uartx( pub fn $uartx(
@ -310,15 +601,240 @@ macro_rules! uart_impl {
) -> Self ) -> Self
{ {
enable_peripheral_clock(syscfg, $clk_enb_enum); enable_peripheral_clock(syscfg, $clk_enb_enum);
Uart { uart, pins, tx: Tx::new(), rx: Rx::new() }.init( Uart {
config.into(), sys_clk.into() 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
}
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.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;
}
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.rx_idx = 0;
self.irq_info.rx_len = 0;
}
pub fn release(self) -> UART {
self.uart.release()
}
}
impl<UART: Instance, PINS> UartWithIrq<UART, PINS> {
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()
}
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! { uart_impl! {
UARTA: (uarta, clock::PeripheralClocks::Uart0), UARTA: (uarta, clock::PeripheralClocks::Uart0),
UARTB: (uartb, clock::PeripheralClocks::Uart1), UARTB: (uartb, clock::PeripheralClocks::Uart1),
@ -326,10 +842,7 @@ uart_impl! {
impl<UART> Tx<UART> where UART: Instance {} impl<UART> Tx<UART> where UART: Instance {}
impl<UART, PINS> serial::Write<u8> for Uart<UART, PINS> impl<UART: Instance> serial::Write<u8> for UartBase<UART> {
where
UART: Instance,
{
type Error = Infallible; type Error = Infallible;
fn write(&mut self, word: u8) -> nb::Result<(), Self::Error> { fn write(&mut self, word: u8) -> nb::Result<(), Self::Error> {
self.tx.write(word) self.tx.write(word)
@ -339,6 +852,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, PINS> blocking::serial::write::Default<u8> for Uart<UART, PINS> {}
impl<UART: Instance> serial::Write<u8> for Tx<UART> { impl<UART: Instance> serial::Write<u8> for Tx<UART> {
@ -370,7 +896,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; type Error = Error;
fn read(&mut self) -> nb::Result<u8, Error> { fn read(&mut self) -> nb::Result<u8, Error> {
@ -378,6 +904,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> { impl<UART: Instance> serial::Read<u8> for Rx<UART> {
type Error = Error; type Error = Error;
@ -409,7 +943,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 where
Tx<UART>: embedded_hal::serial::Write<u8>, Tx<UART>: embedded_hal::serial::Write<u8>,
{ {
@ -420,3 +954,12 @@ where
.map_err(|_| core::fmt::Error) .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,6 +3,7 @@
//! Some more information about the recommended scrub rates can be found on the //! Some more information about the recommended scrub rates can be found on the
//! [Vorago White Paper website](https://www.voragotech.com/resources) in the //! [Vorago White Paper website](https://www.voragotech.com/resources) in the
//! application note AN1212 //! application note AN1212
use crate::pac;
use va108xx::{IOCONFIG, SYSCONFIG}; use va108xx::{IOCONFIG, SYSCONFIG};
#[derive(PartialEq, Debug)] #[derive(PartialEq, Debug)]
@ -41,6 +42,26 @@ pub enum PeripheralSelect {
Gpio = 24, 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, 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 /// Enable scrubbing for the ROM
/// ///
/// Returns [`UtilityError::InvalidCounterResetVal`] if the scrub rate is 0 /// Returns [`UtilityError::InvalidCounterResetVal`] if the scrub rate is 0
@ -111,3 +132,13 @@ pub fn port_mux(
} }
} }
} }
/// 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