Compare commits

..

No commits in common. "main" and "v0.4.0" have entirely different histories.
main ... v0.4.0

32 changed files with 250 additions and 1350 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,29 +0,0 @@
//! 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},
prelude::*,
time::Hertz,
timer::{default_ms_irq_handler, set_up_ms_timer, CountDownTimer, Delay, IrqCfg},
timer::{default_ms_irq_handler, set_up_ms_timer, CountDownTimer, Delay},
};
#[allow(dead_code)]
@ -146,12 +146,15 @@ fn main() -> ! {
}
TestCase::DelayMs => {
let ms_timer = set_up_ms_timer(
IrqCfg::new(pac::Interrupt::OC0, true, true),
&mut dp.SYSCONFIG,
Some(&mut dp.IRQSEL),
50.mhz(),
&mut dp.IRQSEL,
50.mhz().into(),
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();
@ -160,7 +163,7 @@ fn main() -> ! {
delay.delay_ms(500);
}
let mut delay_timer = CountDownTimer::new(&mut dp.SYSCONFIG, 50.mhz(), dp.TIM1);
let mut delay_timer = CountDownTimer::new(&mut dp.SYSCONFIG, 50.mhz().into(), dp.TIM1);
let mut pa0 = pinsa.pa0.into_push_pull_output();
for _ in 0..5 {
led1.toggle().ok();

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -25,7 +25,7 @@ use embedded_hal::{
// Defintions
//==================================================================================================
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
#[derive(Debug, PartialEq, Copy, Clone)]
pub enum HwChipSelectId {
Id0 = 0,
Id1 = 1,
@ -38,7 +38,7 @@ pub enum HwChipSelectId {
Invalid = 0xff,
}
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
#[derive(Debug, PartialEq, Copy, Clone)]
pub enum WordSize {
OneBit = 0x00,
FourBits = 0x03,
@ -428,7 +428,6 @@ macro_rules! spi {
w.sod().bit(sod);
w.ms().bit(ms);
w.mdlycap().bit(mdlycap);
w.blockmode().bit(init_blockmode);
unsafe { w.ss().bits(ss) }
});
@ -504,16 +503,6 @@ macro_rules! spi {
});
}
#[inline]
pub fn clear_tx_fifo(&self) {
self.spi.fifo_clr.write(|w| w.txfifo().set_bit());
}
#[inline]
pub fn clear_rx_fifo(&self) {
self.spi.fifo_clr.write(|w| w.rxfifo().set_bit());
}
#[inline]
pub fn perid(&self) -> u32 {
self.spi.perid.read().bits()
@ -650,9 +639,6 @@ macro_rules! spi {
// FIFO has a depth of 16.
const FILL_DEPTH: usize = 12;
self.clear_tx_fifo();
self.clear_rx_fifo();
if self.blockmode {
self.spi.ctrl1.modify(|_, w| {
w.mtxpause().set_bit()

View File

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

View File

@ -4,7 +4,6 @@
//!
//! - [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::{
@ -21,7 +20,6 @@ use crate::{
private::Sealed,
time::Hertz,
timer,
utility::unmask_irq,
};
use core::cell::Cell;
use cortex_m::interrupt::Mutex;
@ -29,7 +27,7 @@ use embedded_hal::{
blocking::delay,
timer::{Cancel, CountDown, Periodic},
};
use va108xx::{IRQSEL, SYSCONFIG};
use va108xx::{Interrupt, IRQSEL, SYSCONFIG};
use void::Void;
const IRQ_DST_NONE: u32 = 0xffffffff;
@ -45,7 +43,7 @@ pub enum Event {
TimeOut,
}
#[derive(Default, Debug, PartialEq, Eq, Copy, Clone)]
#[derive(Default, Debug, PartialEq, Copy, Clone)]
pub struct CascadeCtrl {
/// Enable Cascade 0 signal active as a requirement for counting
pub enb_start_src_csd0: bool,
@ -74,7 +72,7 @@ pub struct CascadeCtrl {
pub trg_csd2: bool,
}
#[derive(Debug, PartialEq, Eq)]
#[derive(Debug, PartialEq)]
pub enum CascadeSel {
Csd0 = 0,
Csd1 = 1,
@ -82,7 +80,7 @@ pub enum CascadeSel {
}
/// The numbers are the base numbers for bundles like PORTA, PORTB or TIM
#[derive(Debug, PartialEq, Eq)]
#[derive(Debug, PartialEq)]
pub enum CascadeSource {
PortABase = 0,
PortBBase = 32,
@ -95,7 +93,7 @@ pub enum CascadeSource {
ClockDividerBase = 120,
}
#[derive(Debug, PartialEq, Eq)]
#[derive(Debug, PartialEq)]
pub enum TimerErrors {
Canceled,
/// Invalid input for Cascade source
@ -388,7 +386,6 @@ 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,
@ -485,7 +482,6 @@ 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,
@ -495,28 +491,21 @@ impl<TIM: ValidTim> CountDownTimer<TIM> {
cd_timer
}
/// 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
/// Listen for events. This also actives the IRQ in the IRQSEL register
/// for the provided interrupt. It also actives the peripheral clock for
/// IRQSEL
pub fn listen(
&mut self,
event: Event,
irq_cfg: IrqCfg,
irq_sel: Option<&mut IRQSEL>,
sys_cfg: Option<&mut SYSCONFIG>,
syscfg: &mut SYSCONFIG,
irqsel: &mut IRQSEL,
interrupt: Interrupt,
) {
match event {
Event::TimeOut => {
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) });
}
}
enable_peripheral_clock(syscfg, PeripheralClocks::Irqsel);
irqsel.tim[TIM::TIM_ID as usize].write(|w| unsafe { w.bits(interrupt as u32) });
self.enable_interrupt();
self.listening = true;
}
}
@ -565,12 +554,6 @@ 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)]
@ -737,29 +720,19 @@ 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<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));
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);
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() {
@ -790,7 +763,6 @@ impl Delay {
}
/// This assumes that the user has already set up a MS tick timer in TIM0 as a system tick
/// with [`set_up_ms_delay_provider`]
impl embedded_hal::blocking::delay::DelayMs<u32> for Delay {
fn delay_ms(&mut self, ms: u32) {
if self.cd_tim.curr_freq() != 1000.hz() || !self.cd_tim.listening() {

View File

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

View File

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

View File

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

View File

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

View File

@ -1,2 +0,0 @@
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

@ -1,39 +0,0 @@
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

@ -1,46 +0,0 @@
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

@ -1,15 +0,0 @@
; 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

@ -1,78 +0,0 @@
#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

@ -1,11 +0,0 @@
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