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
All checks were successful
Rust/va108xx-hal/pipeline/head This commit looks good
Reviewed-on: #5
This commit is contained in:
commit
482a725ef7
13
CHANGELOG.md
13
CHANGELOG.md
@ -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/)
|
||||
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]
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "va108xx-hal"
|
||||
version = "0.4.3"
|
||||
version = "0.5.0"
|
||||
authors = ["Robin Mueller <muellerr@irs.uni-stuttgart.de>"]
|
||||
edition = "2021"
|
||||
description = "HAL for the Vorago VA108xx family of microcontrollers"
|
||||
@ -27,6 +27,7 @@ version = "0.2.4"
|
||||
rt = ["va108xx/rt"]
|
||||
|
||||
[dev-dependencies]
|
||||
cortex-m-rtic = "0.6.0-rc.4"
|
||||
panic-rtt-target = { version = "0.1", features = ["cortex-m"] }
|
||||
rtt-target = { version = "0.3", features = ["cortex-m"] }
|
||||
panic-halt = "0.2"
|
||||
@ -55,9 +56,9 @@ name = "tests"
|
||||
required-features = ["rt"]
|
||||
|
||||
[[example]]
|
||||
name = "pwm"
|
||||
name = "cascade"
|
||||
required-features = ["rt"]
|
||||
|
||||
[[example]]
|
||||
name = "cascade"
|
||||
name = "uart-irq-rtic"
|
||||
required-features = ["rt"]
|
||||
|
@ -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::*, timer::set_up_ms_timer};
|
||||
use va108xx_hal::{gpio::PinsA, pac, prelude::*, timer::CountDownTimer};
|
||||
|
||||
#[entry]
|
||||
fn main() -> ! {
|
||||
@ -18,13 +18,7 @@ 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 = set_up_ms_timer(
|
||||
&mut dp.SYSCONFIG,
|
||||
&mut dp.IRQSEL,
|
||||
50.mhz().into(),
|
||||
dp.TIM0,
|
||||
pac::Interrupt::OC0,
|
||||
);
|
||||
let mut delay = CountDownTimer::new(&mut dp.SYSCONFIG, 50.mhz(), dp.TIM0);
|
||||
for _ in 0..10 {
|
||||
led1.set_low().ok();
|
||||
led2.set_low().ok();
|
||||
|
@ -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 {
|
||||
|
119
examples/uart-irq-rtic.rs
Normal file
119
examples/uart-irq-rtic.rs
Normal 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");
|
||||
});
|
||||
}
|
||||
}
|
@ -66,8 +66,8 @@ use super::{
|
||||
};
|
||||
use crate::{
|
||||
clock::FilterClkSel,
|
||||
pac::{self, IRQSEL, SYSCONFIG},
|
||||
utility::Funsel,
|
||||
pac::{IRQSEL, SYSCONFIG},
|
||||
utility::{Funsel, IrqCfg},
|
||||
};
|
||||
use embedded_hal::digital::v2::{InputPin, OutputPin, ToggleableOutputPin};
|
||||
use paste::paste;
|
||||
@ -344,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),
|
||||
@ -361,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;
|
||||
@ -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
|
||||
}
|
||||
}
|
||||
|
66
src/timer.rs
66
src/timer.rs
@ -4,6 +4,7 @@
|
||||
//!
|
||||
//! - [MS and second tick implementation](https://egit.irs.uni-stuttgart.de/rust/va108xx-hal/src/branch/main/examples/timer-ticks.rs)
|
||||
//! - [Cascade feature example](https://egit.irs.uni-stuttgart.de/rust/va108xx-hal/src/branch/main/examples/cascade.rs)
|
||||
pub use crate::utility::IrqCfg;
|
||||
use crate::{
|
||||
clock::{enable_peripheral_clock, PeripheralClocks},
|
||||
gpio::{
|
||||
@ -20,6 +21,7 @@ use crate::{
|
||||
private::Sealed,
|
||||
time::Hertz,
|
||||
timer,
|
||||
utility::unmask_irq,
|
||||
};
|
||||
use core::cell::Cell;
|
||||
use cortex_m::interrupt::Mutex;
|
||||
@ -27,7 +29,7 @@ use embedded_hal::{
|
||||
blocking::delay,
|
||||
timer::{Cancel, CountDown, Periodic},
|
||||
};
|
||||
use va108xx::{Interrupt, IRQSEL, SYSCONFIG};
|
||||
use va108xx::{IRQSEL, SYSCONFIG};
|
||||
use void::Void;
|
||||
|
||||
const IRQ_DST_NONE: u32 = 0xffffffff;
|
||||
@ -386,6 +388,7 @@ unsafe impl TimPinInterface for TimDynRegister {
|
||||
pub struct CountDownTimer<TIM: ValidTim> {
|
||||
tim: TimRegister<TIM>,
|
||||
curr_freq: Hertz,
|
||||
irq_cfg: Option<IrqCfg>,
|
||||
sys_clk: Hertz,
|
||||
rst_val: u32,
|
||||
last_cnt: u32,
|
||||
@ -482,6 +485,7 @@ impl<TIM: ValidTim> CountDownTimer<TIM> {
|
||||
let cd_timer = CountDownTimer {
|
||||
tim: unsafe { TimRegister::new(tim) },
|
||||
sys_clk: sys_clk.into(),
|
||||
irq_cfg: None,
|
||||
rst_val: 0,
|
||||
curr_freq: 0.hz(),
|
||||
listening: false,
|
||||
@ -491,21 +495,28 @@ impl<TIM: ValidTim> CountDownTimer<TIM> {
|
||||
cd_timer
|
||||
}
|
||||
|
||||
/// Listen for events. This also actives the IRQ in the IRQSEL register
|
||||
/// for the provided interrupt. It also actives the peripheral clock for
|
||||
/// IRQSEL
|
||||
/// Listen for events. Depending on the IRQ configuration, this also activates the IRQ in the
|
||||
/// IRQSEL peripheral for the provided interrupt and unmasks the interrupt
|
||||
pub fn listen(
|
||||
&mut self,
|
||||
event: Event,
|
||||
syscfg: &mut SYSCONFIG,
|
||||
irqsel: &mut IRQSEL,
|
||||
interrupt: Interrupt,
|
||||
irq_cfg: IrqCfg,
|
||||
irq_sel: Option<&mut IRQSEL>,
|
||||
sys_cfg: Option<&mut SYSCONFIG>,
|
||||
) {
|
||||
match event {
|
||||
Event::TimeOut => {
|
||||
enable_peripheral_clock(syscfg, PeripheralClocks::Irqsel);
|
||||
irqsel.tim[TIM::TIM_ID as usize].write(|w| unsafe { w.bits(interrupt as u32) });
|
||||
self.enable_interrupt();
|
||||
cortex_m::peripheral::NVIC::mask(irq_cfg.irq);
|
||||
self.irq_cfg = Some(irq_cfg);
|
||||
if irq_cfg.route {
|
||||
if let Some(sys_cfg) = sys_cfg {
|
||||
enable_peripheral_clock(sys_cfg, PeripheralClocks::Irqsel);
|
||||
}
|
||||
if let Some(irq_sel) = irq_sel {
|
||||
irq_sel.tim[TIM::TIM_ID as usize]
|
||||
.write(|w| unsafe { w.bits(irq_cfg.irq as u32) });
|
||||
}
|
||||
}
|
||||
self.listening = true;
|
||||
}
|
||||
}
|
||||
@ -554,6 +565,12 @@ impl<TIM: ValidTim> CountDownTimer<TIM> {
|
||||
#[inline(always)]
|
||||
pub fn enable(&mut self) {
|
||||
self.tim.reg().ctrl.modify(|_, w| w.enable().set_bit());
|
||||
if let Some(irq_cfg) = self.irq_cfg {
|
||||
self.enable_interrupt();
|
||||
if irq_cfg.enable {
|
||||
unmask_irq(irq_cfg.irq);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
@ -720,19 +737,29 @@ impl<TIM: ValidTim> embedded_hal::blocking::delay::DelayMs<u8> for CountDownTime
|
||||
|
||||
// Set up a millisecond timer on TIM0. Please note that you still need to unmask the related IRQ
|
||||
// and provide an IRQ handler yourself
|
||||
pub fn set_up_ms_timer(
|
||||
syscfg: &mut pac::SYSCONFIG,
|
||||
irqsel: &mut pac::IRQSEL,
|
||||
sys_clk: Hertz,
|
||||
tim0: TIM0,
|
||||
irq: pac::Interrupt,
|
||||
) -> CountDownTimer<TIM0> {
|
||||
let mut ms_timer = CountDownTimer::new(syscfg, sys_clk, tim0);
|
||||
ms_timer.listen(timer::Event::TimeOut, syscfg, irqsel, irq);
|
||||
pub fn set_up_ms_timer<TIM: ValidTim>(
|
||||
irq_cfg: IrqCfg,
|
||||
sys_cfg: &mut pac::SYSCONFIG,
|
||||
irq_sel: Option<&mut pac::IRQSEL>,
|
||||
sys_clk: impl Into<Hertz>,
|
||||
tim0: TIM,
|
||||
) -> CountDownTimer<TIM> {
|
||||
let mut ms_timer = CountDownTimer::new(sys_cfg, sys_clk, tim0);
|
||||
ms_timer.listen(timer::Event::TimeOut, irq_cfg, irq_sel, Some(sys_cfg));
|
||||
ms_timer.start(1000.hz());
|
||||
ms_timer
|
||||
}
|
||||
|
||||
pub fn set_up_ms_delay_provider<TIM: ValidTim>(
|
||||
sys_cfg: &mut pac::SYSCONFIG,
|
||||
sys_clk: impl Into<Hertz>,
|
||||
tim: TIM,
|
||||
) -> CountDownTimer<TIM> {
|
||||
let mut provider = CountDownTimer::new(sys_cfg, sys_clk, tim);
|
||||
provider.start(1000.hz());
|
||||
provider
|
||||
}
|
||||
|
||||
/// This function can be called in a specified interrupt handler to increment
|
||||
/// the MS counter
|
||||
pub fn default_ms_irq_handler() {
|
||||
@ -763,6 +790,7 @@ impl Delay {
|
||||
}
|
||||
|
||||
/// This assumes that the user has already set up a MS tick timer in TIM0 as a system tick
|
||||
/// with [`set_up_ms_delay_provider`]
|
||||
impl embedded_hal::blocking::delay::DelayMs<u32> for Delay {
|
||||
fn delay_ms(&mut self, ms: u32) {
|
||||
if self.cd_tim.curr_freq() != 1000.hz() || !self.cd_tim.listening() {
|
||||
|
603
src/uart.rs
603
src/uart.rs
@ -7,20 +7,25 @@ use core::{convert::Infallible, ptr};
|
||||
use core::{marker::PhantomData, ops::Deref};
|
||||
use libm::floorf;
|
||||
|
||||
use crate::clock::enable_peripheral_clock;
|
||||
pub use crate::utility::IrqCfg;
|
||||
use crate::{
|
||||
clock,
|
||||
clock::{self, enable_peripheral_clock, PeripheralClocks},
|
||||
gpio::pins::{
|
||||
AltFunc1, AltFunc2, AltFunc3, Pin, PA16, PA17, PA18, PA19, PA2, PA26, PA27, PA3, PA30,
|
||||
PA31, PA8, PA9, PB18, PB19, PB20, PB21, PB22, PB23, PB6, PB7, PB8, PB9,
|
||||
},
|
||||
pac::{uarta as uart_base, SYSCONFIG, UARTA, UARTB},
|
||||
pac::{uarta as uart_base, IRQSEL, SYSCONFIG, UARTA, UARTB},
|
||||
prelude::*,
|
||||
time::{Bps, Hertz},
|
||||
utility::unmask_irq,
|
||||
};
|
||||
|
||||
use embedded_hal::{blocking, serial};
|
||||
|
||||
//==================================================================================================
|
||||
// Type-Level support
|
||||
//==================================================================================================
|
||||
|
||||
pub trait Pins<UART> {}
|
||||
|
||||
impl Pins<UARTA> for (Pin<PA9, AltFunc2>, Pin<PA8, AltFunc2>) {}
|
||||
@ -38,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<PB21, AltFunc1>, Pin<PB20, AltFunc1>) {}
|
||||
|
||||
//==================================================================================================
|
||||
// Regular Definitions
|
||||
//==================================================================================================
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Error {
|
||||
Overrun,
|
||||
FramingError,
|
||||
ParityError,
|
||||
BreakCondition,
|
||||
TransferPending,
|
||||
BufferTooShort,
|
||||
/// Can be a combination of overrun, framing or parity error
|
||||
IrqError,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Copy, Clone)]
|
||||
@ -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,
|
||||
pins: PINS,
|
||||
tx: Tx<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
|
||||
pub struct Rx<UART> {
|
||||
@ -196,12 +335,10 @@ impl<UART> Tx<UART> {
|
||||
|
||||
pub trait Instance: Deref<Target = uart_base::RegisterBlock> {
|
||||
fn ptr() -> *const uart_base::RegisterBlock;
|
||||
const IDX: u8;
|
||||
}
|
||||
|
||||
impl<UART, PINS> Uart<UART, PINS>
|
||||
where
|
||||
UART: Instance,
|
||||
{
|
||||
impl<UART: Instance> UartBase<UART> {
|
||||
/// This function assumes that the peripheral clock was alredy enabled
|
||||
/// in the SYSCONFIG register
|
||||
fn init(self, config: Config, sys_clk: Hertz) -> Self {
|
||||
@ -247,7 +384,47 @@ where
|
||||
self
|
||||
}
|
||||
|
||||
pub fn listen(self, event: Event) -> Self {
|
||||
#[inline]
|
||||
pub fn enable_rx(&mut self) {
|
||||
self.uart.enable.modify(|_, w| w.rxenable().set_bit());
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn disable_rx(&mut self) {
|
||||
self.uart.enable.modify(|_, w| w.rxenable().clear_bit());
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn enable_tx(&mut self) {
|
||||
self.uart.enable.modify(|_, w| w.txenable().set_bit());
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn disable_tx(&mut self) {
|
||||
self.uart.enable.modify(|_, w| w.txenable().clear_bit());
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn clear_rx_fifo(&mut self) {
|
||||
self.uart.fifo_clr.write(|w| w.rxfifo().set_bit());
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn clear_tx_fifo(&mut self) {
|
||||
self.uart.fifo_clr.write(|w| w.txfifo().set_bit());
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn clear_rx_status(&mut self) {
|
||||
self.uart.fifo_clr.write(|w| w.rxsts().set_bit());
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn clear_tx_status(&mut self) {
|
||||
self.uart.fifo_clr.write(|w| w.txsts().set_bit());
|
||||
}
|
||||
|
||||
pub fn listen(&self, event: Event) {
|
||||
self.uart.irq_enb.modify(|_, w| match event {
|
||||
Event::RxError => w.irq_rx_status().set_bit(),
|
||||
Event::RxFifoHalfFull => w.irq_rx().set_bit(),
|
||||
@ -257,10 +434,9 @@ where
|
||||
Event::TxFifoHalfFull => w.irq_tx().set_bit(),
|
||||
Event::TxCts => w.irq_tx_cts().set_bit(),
|
||||
});
|
||||
self
|
||||
}
|
||||
|
||||
pub fn unlisten(self, event: Event) -> Self {
|
||||
pub fn unlisten(&self, event: Event) {
|
||||
self.uart.irq_enb.modify(|_, w| match event {
|
||||
Event::RxError => w.irq_rx_status().clear_bit(),
|
||||
Event::RxFifoHalfFull => w.irq_rx().clear_bit(),
|
||||
@ -270,10 +446,9 @@ where
|
||||
Event::TxFifoHalfFull => w.irq_tx().clear_bit(),
|
||||
Event::TxCts => w.irq_tx_cts().clear_bit(),
|
||||
});
|
||||
self
|
||||
}
|
||||
|
||||
pub fn release(self) -> (UART, PINS) {
|
||||
pub fn release(self) -> UART {
|
||||
// Clear the FIFO
|
||||
self.uart.fifo_clr.write(|w| {
|
||||
w.rxfifo().set_bit();
|
||||
@ -283,7 +458,7 @@ where
|
||||
w.rxenable().clear_bit();
|
||||
w.txenable().clear_bit()
|
||||
});
|
||||
(self.uart, self.pins)
|
||||
self.uart
|
||||
}
|
||||
|
||||
pub fn split(self) -> (Tx<UART>, Rx<UART>) {
|
||||
@ -291,14 +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 {
|
||||
($($UARTX:ident: ($uartx:ident, $clk_enb_enum:path),)+) => {
|
||||
$(
|
||||
impl Instance for $UARTX {
|
||||
fn ptr() -> *const uart_base::RegisterBlock {
|
||||
$UARTX::ptr() as *const _
|
||||
}
|
||||
}
|
||||
|
||||
impl<PINS: Pins<$UARTX>> Uart<$UARTX, PINS> {
|
||||
pub fn $uartx(
|
||||
@ -310,15 +601,240 @@ macro_rules! uart_impl {
|
||||
) -> Self
|
||||
{
|
||||
enable_peripheral_clock(syscfg, $clk_enb_enum);
|
||||
Uart { uart, pins, tx: Tx::new(), rx: Rx::new() }.init(
|
||||
config.into(), sys_clk.into()
|
||||
)
|
||||
Uart {
|
||||
uart_base: UartBase {
|
||||
uart,
|
||||
tx: Tx::new(),
|
||||
rx: Rx::new(),
|
||||
},
|
||||
pins,
|
||||
}
|
||||
.init(config.into(), sys_clk.into())
|
||||
}
|
||||
}
|
||||
|
||||
)+
|
||||
}
|
||||
}
|
||||
|
||||
impl<UART: Instance> UartWithIrqBase<UART> {
|
||||
fn init(self, sys_cfg: Option<&mut SYSCONFIG>, irq_sel: Option<&mut IRQSEL>) -> Self {
|
||||
if let Some(sys_cfg) = sys_cfg {
|
||||
enable_peripheral_clock(sys_cfg, PeripheralClocks::Irqsel)
|
||||
}
|
||||
if let Some(irq_sel) = irq_sel {
|
||||
if self.irq_info.irq_cfg.route {
|
||||
irq_sel.uart[UART::IDX as usize]
|
||||
.write(|w| unsafe { w.bits(self.irq_info.irq_cfg.irq as u32) });
|
||||
}
|
||||
}
|
||||
self
|
||||
}
|
||||
|
||||
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! {
|
||||
UARTA: (uarta, clock::PeripheralClocks::Uart0),
|
||||
UARTB: (uartb, clock::PeripheralClocks::Uart1),
|
||||
@ -326,10 +842,7 @@ uart_impl! {
|
||||
|
||||
impl<UART> Tx<UART> where UART: Instance {}
|
||||
|
||||
impl<UART, PINS> serial::Write<u8> for Uart<UART, PINS>
|
||||
where
|
||||
UART: Instance,
|
||||
{
|
||||
impl<UART: Instance> serial::Write<u8> for UartBase<UART> {
|
||||
type Error = Infallible;
|
||||
fn write(&mut self, word: u8) -> nb::Result<(), Self::Error> {
|
||||
self.tx.write(word)
|
||||
@ -339,6 +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> 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;
|
||||
|
||||
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> {
|
||||
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
|
||||
Tx<UART>: embedded_hal::serial::Write<u8>,
|
||||
{
|
||||
@ -420,3 +954,12 @@ where
|
||||
.map_err(|_| core::fmt::Error)
|
||||
}
|
||||
}
|
||||
|
||||
impl<UART: Instance> core::fmt::Write for UartBase<UART>
|
||||
where
|
||||
UartBase<UART>: embedded_hal::serial::Write<u8>,
|
||||
{
|
||||
fn write_str(&mut self, s: &str) -> core::fmt::Result {
|
||||
self.tx.write_str(s)
|
||||
}
|
||||
}
|
||||
|
@ -3,6 +3,7 @@
|
||||
//! Some more information about the recommended scrub rates can be found on the
|
||||
//! [Vorago White Paper website](https://www.voragotech.com/resources) in the
|
||||
//! application note AN1212
|
||||
use crate::pac;
|
||||
use va108xx::{IOCONFIG, SYSCONFIG};
|
||||
|
||||
#[derive(PartialEq, Debug)]
|
||||
@ -41,6 +42,26 @@ pub enum PeripheralSelect {
|
||||
Gpio = 24,
|
||||
}
|
||||
|
||||
/// Generic IRQ config which can be used to specify whether the HAL driver will
|
||||
/// use the IRQSEL register to route an interrupt, and whether the IRQ will be unmasked in the
|
||||
/// Cortex-M0 NVIC. Both are generally necessary for IRQs to work, but the user might perform
|
||||
/// this steps themselves
|
||||
#[derive(Debug, PartialEq, Clone, Copy)]
|
||||
pub struct IrqCfg {
|
||||
/// Interrupt target vector. Should always be set, might be required for disabling IRQs
|
||||
pub irq: pac::Interrupt,
|
||||
/// Specfiy whether IRQ should be routed to an IRQ vector using the IRQSEL peripheral
|
||||
pub route: bool,
|
||||
/// Specify whether the IRQ is unmasked in the Cortex-M NVIC
|
||||
pub enable: bool,
|
||||
}
|
||||
|
||||
impl IrqCfg {
|
||||
pub fn new(irq: pac::Interrupt, route: bool, enable: bool) -> Self {
|
||||
IrqCfg { irq, route, enable }
|
||||
}
|
||||
}
|
||||
|
||||
/// Enable scrubbing for the ROM
|
||||
///
|
||||
/// Returns [`UtilityError::InvalidCounterResetVal`] if the scrub rate is 0
|
||||
@ -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
5
test/DueSerialTest/.gitignore
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
.pio
|
||||
.vscode/.browse.c_cpp.db*
|
||||
.vscode/c_cpp_properties.json
|
||||
.vscode/launch.json
|
||||
.vscode/ipch
|
7
test/DueSerialTest/.vscode/extensions.json
vendored
Normal file
7
test/DueSerialTest/.vscode/extensions.json
vendored
Normal 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"
|
||||
]
|
||||
}
|
2
test/DueSerialTest/README.md
Normal file
2
test/DueSerialTest/README.md
Normal 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.
|
39
test/DueSerialTest/include/README
Normal file
39
test/DueSerialTest/include/README
Normal 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
|
46
test/DueSerialTest/lib/README
Normal file
46
test/DueSerialTest/lib/README
Normal 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
|
15
test/DueSerialTest/platformio.ini
Normal file
15
test/DueSerialTest/platformio.ini
Normal 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
|
78
test/DueSerialTest/src/main.cpp
Normal file
78
test/DueSerialTest/src/main.cpp
Normal 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);
|
||||
}
|
11
test/DueSerialTest/test/README
Normal file
11
test/DueSerialTest/test/README
Normal 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
|
Reference in New Issue
Block a user