timer, PWM and I2C module
This commit is contained in:
parent
6bd6d24f04
commit
5c745a09ac
@ -8,9 +8,11 @@ cortex-m-rt = "0.7"
|
|||||||
va416xx-hal = { path = "../../va416xx-hal" }
|
va416xx-hal = { path = "../../va416xx-hal" }
|
||||||
panic-rtt-target = { version = "0.1.3" }
|
panic-rtt-target = { version = "0.1.3" }
|
||||||
rtt-target = { version = "0.5" }
|
rtt-target = { version = "0.5" }
|
||||||
cortex-m = { version = "0.7", features = ["critical-section-single-core"]}
|
cortex-m = { version = "0.7", features = ["critical-section-single-core"] }
|
||||||
embedded-hal = "1"
|
embedded-hal = "1"
|
||||||
embedded-hal-nb = "1"
|
embedded-hal-nb = "1"
|
||||||
nb = "1"
|
nb = "1"
|
||||||
embedded-io = "0.6"
|
embedded-io = "0.6"
|
||||||
panic-halt = "0.2"
|
panic-halt = "0.2"
|
||||||
|
vorago-peb1 = { path = "../../vorago-peb1" }
|
||||||
|
accelerometer = "0.12"
|
||||||
|
88
examples/simple/examples/peb1-accelerometer.rs
Normal file
88
examples/simple/examples/peb1-accelerometer.rs
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
//! Example code for the PEB1 development board accelerometer.
|
||||||
|
#![no_main]
|
||||||
|
#![no_std]
|
||||||
|
|
||||||
|
use accelerometer::{Accelerometer, RawAccelerometer};
|
||||||
|
use cortex_m_rt::entry;
|
||||||
|
use embedded_hal::delay::DelayNs;
|
||||||
|
use panic_rtt_target as _;
|
||||||
|
use rtt_target::{rprintln, rtt_init_print};
|
||||||
|
use simple_examples::peb1;
|
||||||
|
use va416xx_hal::{
|
||||||
|
i2c,
|
||||||
|
pac::{self},
|
||||||
|
prelude::*,
|
||||||
|
pwm::CountdownTimer,
|
||||||
|
};
|
||||||
|
use vorago_peb1::lis2dh12::{self, detect_i2c_addr, FullScale, Odr};
|
||||||
|
|
||||||
|
pub enum DisplayMode {
|
||||||
|
Raw,
|
||||||
|
Normalized,
|
||||||
|
}
|
||||||
|
|
||||||
|
const DISPLAY_MODE: DisplayMode = DisplayMode::Normalized;
|
||||||
|
|
||||||
|
#[entry]
|
||||||
|
fn main() -> ! {
|
||||||
|
rtt_init_print!();
|
||||||
|
let mut dp = pac::Peripherals::take().unwrap();
|
||||||
|
rprintln!("-- Vorago PEB1 accelerometer example --");
|
||||||
|
// Use the external clock connected to XTAL_N.
|
||||||
|
let clocks = dp
|
||||||
|
.clkgen
|
||||||
|
.constrain()
|
||||||
|
.xtal_n_clk_with_src_freq(peb1::EXTCLK_FREQ)
|
||||||
|
.freeze(&mut dp.sysconfig)
|
||||||
|
.unwrap();
|
||||||
|
let mut i2c_master = i2c::I2cMaster::new(
|
||||||
|
dp.i2c0,
|
||||||
|
&mut dp.sysconfig,
|
||||||
|
i2c::MasterConfig::default(),
|
||||||
|
&clocks,
|
||||||
|
i2c::I2cSpeed::Regular100khz,
|
||||||
|
)
|
||||||
|
.expect("creating I2C master failed");
|
||||||
|
let mut delay_provider = CountdownTimer::new(&mut dp.sysconfig, dp.tim1, &clocks);
|
||||||
|
// Detect the I2C address of the accelerometer by scanning all possible values.
|
||||||
|
let slave_addr = detect_i2c_addr(&mut i2c_master).expect("detecting I2C address failed");
|
||||||
|
// Create the accelerometer driver using the PEB1 BSP.
|
||||||
|
let mut accelerometer = vorago_peb1::accelerometer::new_with_i2cm(i2c_master, slave_addr)
|
||||||
|
.expect("creating accelerometer driver failed");
|
||||||
|
let device_id = accelerometer.get_device_id().unwrap();
|
||||||
|
accelerometer
|
||||||
|
.set_mode(lis2dh12::reg::Mode::Normal)
|
||||||
|
.expect("setting mode failed");
|
||||||
|
accelerometer
|
||||||
|
.set_odr(Odr::Hz100)
|
||||||
|
.expect("setting ODR failed");
|
||||||
|
accelerometer
|
||||||
|
.set_fs(FullScale::G4)
|
||||||
|
.expect("setting full scale failed");
|
||||||
|
// This function also enabled BDU.
|
||||||
|
accelerometer
|
||||||
|
.enable_temp(true)
|
||||||
|
.expect("enabling temperature sensor failed");
|
||||||
|
rprintln!("Device ID: 0x{:02X}", device_id);
|
||||||
|
// Start reading the accelerometer periodically.
|
||||||
|
loop {
|
||||||
|
let temperature = accelerometer
|
||||||
|
.get_temp_outf()
|
||||||
|
.expect("reading temperature failed");
|
||||||
|
match DISPLAY_MODE {
|
||||||
|
DisplayMode::Normalized => {
|
||||||
|
let value = accelerometer
|
||||||
|
.accel_norm()
|
||||||
|
.expect("reading normalized accelerometer data failed");
|
||||||
|
rprintln!("Accel Norm F32x3: {:.06?} | Temp {} °C", value, temperature);
|
||||||
|
}
|
||||||
|
DisplayMode::Raw => {
|
||||||
|
let value_raw = accelerometer
|
||||||
|
.accel_raw()
|
||||||
|
.expect("reading raw accelerometer data failed");
|
||||||
|
rprintln!("Accel Raw F32x3: {:?} | Temp {} °C", value_raw, temperature);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
delay_provider.delay_ms(100);
|
||||||
|
}
|
||||||
|
}
|
81
examples/simple/examples/pwm.rs
Normal file
81
examples/simple/examples/pwm.rs
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
//! Simple PWM example
|
||||||
|
#![no_main]
|
||||||
|
#![no_std]
|
||||||
|
|
||||||
|
use cortex_m_rt::entry;
|
||||||
|
use embedded_hal::{delay::DelayNs, pwm::SetDutyCycle};
|
||||||
|
use panic_rtt_target as _;
|
||||||
|
use rtt_target::{rprintln, rtt_init_print};
|
||||||
|
use simple_examples::peb1;
|
||||||
|
use va416xx_hal::{
|
||||||
|
gpio::PinsA,
|
||||||
|
pac,
|
||||||
|
prelude::*,
|
||||||
|
pwm::{self, get_duty_from_percent, CountdownTimer, PwmA, PwmB, ReducedPwmPin},
|
||||||
|
};
|
||||||
|
|
||||||
|
#[entry]
|
||||||
|
fn main() -> ! {
|
||||||
|
rtt_init_print!();
|
||||||
|
rprintln!("-- VA108xx PWM example application--");
|
||||||
|
let mut dp = pac::Peripherals::take().unwrap();
|
||||||
|
|
||||||
|
// Use the external clock connected to XTAL_N.
|
||||||
|
let clocks = dp
|
||||||
|
.clkgen
|
||||||
|
.constrain()
|
||||||
|
.xtal_n_clk_with_src_freq(peb1::EXTCLK_FREQ)
|
||||||
|
.freeze(&mut dp.sysconfig)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let pinsa = PinsA::new(&mut dp.sysconfig, dp.porta);
|
||||||
|
let mut pwm = pwm::PwmPin::new(
|
||||||
|
(pinsa.pa3.into_funsel_1(), dp.tim3),
|
||||||
|
&mut dp.sysconfig,
|
||||||
|
&clocks,
|
||||||
|
10.Hz(),
|
||||||
|
);
|
||||||
|
let mut delay_timer = CountdownTimer::new(&mut dp.sysconfig, dp.tim0, &clocks);
|
||||||
|
let mut current_duty_cycle = 0.0;
|
||||||
|
pwm.set_duty_cycle(get_duty_from_percent(current_duty_cycle))
|
||||||
|
.unwrap();
|
||||||
|
pwm.enable();
|
||||||
|
|
||||||
|
// Delete type information, increased code readibility for the rest of the code
|
||||||
|
let mut reduced_pin = ReducedPwmPin::from(pwm);
|
||||||
|
loop {
|
||||||
|
let mut counter = 0;
|
||||||
|
// Increase duty cycle continuously
|
||||||
|
while current_duty_cycle < 1.0 {
|
||||||
|
delay_timer.delay_ms(400);
|
||||||
|
current_duty_cycle += 0.02;
|
||||||
|
counter += 1;
|
||||||
|
if counter % 10 == 0 {
|
||||||
|
rprintln!("current duty cycle: {}", current_duty_cycle);
|
||||||
|
}
|
||||||
|
|
||||||
|
reduced_pin
|
||||||
|
.set_duty_cycle(get_duty_from_percent(current_duty_cycle))
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Switch to PWMB and decrease the window with a high signal from 100 % to 0 %
|
||||||
|
// continously
|
||||||
|
current_duty_cycle = 0.0;
|
||||||
|
let mut upper_limit = 1.0;
|
||||||
|
let mut lower_limit = 0.0;
|
||||||
|
let mut pwmb: ReducedPwmPin<PwmB> = ReducedPwmPin::from(reduced_pin);
|
||||||
|
pwmb.set_pwmb_lower_limit(get_duty_from_percent(lower_limit));
|
||||||
|
pwmb.set_pwmb_upper_limit(get_duty_from_percent(upper_limit));
|
||||||
|
while lower_limit < 0.5 {
|
||||||
|
delay_timer.delay_ms(400);
|
||||||
|
lower_limit += 0.01;
|
||||||
|
upper_limit -= 0.01;
|
||||||
|
pwmb.set_pwmb_lower_limit(get_duty_from_percent(lower_limit));
|
||||||
|
pwmb.set_pwmb_upper_limit(get_duty_from_percent(upper_limit));
|
||||||
|
rprintln!("Lower limit: {}", pwmb.pwmb_lower_limit());
|
||||||
|
rprintln!("Upper limit: {}", pwmb.pwmb_upper_limit());
|
||||||
|
}
|
||||||
|
reduced_pin = ReducedPwmPin::<PwmA>::from(pwmb);
|
||||||
|
}
|
||||||
|
}
|
@ -63,7 +63,7 @@ fn main() -> ! {
|
|||||||
let transfer_cfg =
|
let transfer_cfg =
|
||||||
TransferConfig::new_no_hw_cs(SPI_SPEED_KHZ.kHz(), SPI_MODE, BLOCKMODE, false);
|
TransferConfig::new_no_hw_cs(SPI_SPEED_KHZ.kHz(), SPI_MODE, BLOCKMODE, false);
|
||||||
// Create SPI peripheral.
|
// Create SPI peripheral.
|
||||||
let mut spi0 = Spi::spi0(
|
let mut spi0 = Spi::new(
|
||||||
dp.spi0,
|
dp.spi0,
|
||||||
(sck, miso, mosi),
|
(sck, miso, mosi),
|
||||||
&clocks,
|
&clocks,
|
||||||
|
68
examples/simple/examples/timer-ticks.rs
Normal file
68
examples/simple/examples/timer-ticks.rs
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
//! MS and Second counter implemented using the TIM0 and TIM1 peripheral
|
||||||
|
#![no_main]
|
||||||
|
#![no_std]
|
||||||
|
|
||||||
|
use core::cell::Cell;
|
||||||
|
use cortex_m::interrupt::Mutex;
|
||||||
|
use cortex_m_rt::entry;
|
||||||
|
use panic_rtt_target as _;
|
||||||
|
use rtt_target::{rprintln, rtt_init_print};
|
||||||
|
use simple_examples::peb1;
|
||||||
|
use va416xx_hal::{
|
||||||
|
pac::{self, interrupt},
|
||||||
|
prelude::*,
|
||||||
|
timer::{default_ms_irq_handler, set_up_ms_tick, CountdownTimer, MS_COUNTER},
|
||||||
|
};
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
enum LibType {
|
||||||
|
Pac,
|
||||||
|
Hal,
|
||||||
|
}
|
||||||
|
|
||||||
|
static SEC_COUNTER: Mutex<Cell<u32>> = Mutex::new(Cell::new(0));
|
||||||
|
|
||||||
|
#[entry]
|
||||||
|
fn main() -> ! {
|
||||||
|
rtt_init_print!();
|
||||||
|
let mut dp = pac::Peripherals::take().unwrap();
|
||||||
|
let mut last_ms = 0;
|
||||||
|
rprintln!("-- Vorago system ticks using timers --");
|
||||||
|
// Use the external clock connected to XTAL_N.
|
||||||
|
let clocks = dp
|
||||||
|
.clkgen
|
||||||
|
.constrain()
|
||||||
|
.xtal_n_clk_with_src_freq(peb1::EXTCLK_FREQ)
|
||||||
|
.freeze(&mut dp.sysconfig)
|
||||||
|
.unwrap();
|
||||||
|
let _ = set_up_ms_tick(&mut dp.sysconfig, dp.tim0, &clocks);
|
||||||
|
let mut second_timer = CountdownTimer::new(&mut dp.sysconfig, dp.tim1, &clocks);
|
||||||
|
second_timer.start(1.Hz());
|
||||||
|
second_timer.listen();
|
||||||
|
loop {
|
||||||
|
let current_ms = cortex_m::interrupt::free(|cs| MS_COUNTER.borrow(cs).get());
|
||||||
|
if current_ms - last_ms >= 1000 {
|
||||||
|
last_ms = current_ms;
|
||||||
|
rprintln!("MS counter: {}", current_ms);
|
||||||
|
let second = cortex_m::interrupt::free(|cs| SEC_COUNTER.borrow(cs).get());
|
||||||
|
rprintln!("Second counter: {}", second);
|
||||||
|
}
|
||||||
|
cortex_m::asm::delay(10000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[interrupt]
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
fn TIM0() {
|
||||||
|
default_ms_irq_handler()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[interrupt]
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
fn TIM1() {
|
||||||
|
cortex_m::interrupt::free(|cs| {
|
||||||
|
let mut sec = SEC_COUNTER.borrow(cs).get();
|
||||||
|
sec += 1;
|
||||||
|
SEC_COUNTER.borrow(cs).set(sec);
|
||||||
|
});
|
||||||
|
}
|
@ -32,7 +32,7 @@ fn main() -> ! {
|
|||||||
let tx = gpiob.pg0.into_funsel_1();
|
let tx = gpiob.pg0.into_funsel_1();
|
||||||
let rx = gpiob.pg1.into_funsel_1();
|
let rx = gpiob.pg1.into_funsel_1();
|
||||||
|
|
||||||
let uart0 = uart::Uart::uart0(
|
let uart0 = uart::Uart::new(
|
||||||
dp.uart0,
|
dp.uart0,
|
||||||
(tx, rx),
|
(tx, rx),
|
||||||
Hertz::from_raw(115200),
|
Hertz::from_raw(115200),
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
#![no_std]
|
#![no_std]
|
||||||
|
|
||||||
|
/// PEB1 board specific configuration.
|
||||||
pub mod peb1 {
|
pub mod peb1 {
|
||||||
use va416xx_hal::time::Hertz;
|
use va416xx_hal::time::Hertz;
|
||||||
|
|
||||||
|
@ -22,6 +22,10 @@ defmt = { version = "0.3", optional = true }
|
|||||||
fugit = "0.3"
|
fugit = "0.3"
|
||||||
delegate = "0.12"
|
delegate = "0.12"
|
||||||
|
|
||||||
|
[dependencies.void]
|
||||||
|
version = "1"
|
||||||
|
default-features = false
|
||||||
|
|
||||||
[dependencies.va416xx]
|
[dependencies.va416xx]
|
||||||
path = "../va416xx"
|
path = "../va416xx"
|
||||||
default-features = false
|
default-features = false
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
//! This also includes functionality to enable the peripheral clocks
|
//! This also includes functionality to enable the peripheral clocks
|
||||||
//use va416xx::{, Sysconfig};
|
|
||||||
use crate::pac;
|
use crate::pac;
|
||||||
|
|
||||||
use crate::time::Hertz;
|
use crate::time::Hertz;
|
||||||
|
904
va416xx-hal/src/i2c.rs
Normal file
904
va416xx-hal/src/i2c.rs
Normal file
@ -0,0 +1,904 @@
|
|||||||
|
//! API for the I2C peripheral
|
||||||
|
//!
|
||||||
|
//! ## Examples
|
||||||
|
//!
|
||||||
|
//! - [PEB1 accelerometer example](https://egit.irs.uni-stuttgart.de/rust/va416xx-rs/src/branch/main/examples/simple/examples/peb1-accelerometer.rs)
|
||||||
|
use crate::{
|
||||||
|
clock::{
|
||||||
|
assert_periph_reset, deassert_periph_reset, enable_peripheral_clock, Clocks,
|
||||||
|
PeripheralSelect,
|
||||||
|
},
|
||||||
|
pac,
|
||||||
|
time::Hertz,
|
||||||
|
typelevel::Sealed,
|
||||||
|
};
|
||||||
|
use core::{marker::PhantomData, ops::Deref};
|
||||||
|
use embedded_hal::i2c::{self, Operation, SevenBitAddress, TenBitAddress};
|
||||||
|
|
||||||
|
//==================================================================================================
|
||||||
|
// Defintions
|
||||||
|
//==================================================================================================
|
||||||
|
|
||||||
|
const CLK_100K: Hertz = Hertz::from_raw(100_000);
|
||||||
|
const CLK_400K: Hertz = Hertz::from_raw(400_000);
|
||||||
|
const MIN_CLK_400K: Hertz = Hertz::from_raw(10_000_000);
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
|
||||||
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
|
pub enum FifoEmptyMode {
|
||||||
|
Stall = 0,
|
||||||
|
EndTransaction = 1,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
|
pub struct ClockTooSlowForFastI2c;
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
|
pub enum Error {
|
||||||
|
InvalidTimingParams,
|
||||||
|
ArbitrationLost,
|
||||||
|
NackAddr,
|
||||||
|
/// Data not acknowledged in write operation
|
||||||
|
NackData,
|
||||||
|
/// Not enough data received in read operation
|
||||||
|
InsufficientDataReceived,
|
||||||
|
/// Number of bytes in transfer too large (larger than 0x7fe)
|
||||||
|
DataTooLarge,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
|
pub enum InitError {
|
||||||
|
/// Wrong address used in constructor
|
||||||
|
WrongAddrMode,
|
||||||
|
/// APB1 clock is too slow for fast I2C mode.
|
||||||
|
ClkTooSlow(ClockTooSlowForFastI2c),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<ClockTooSlowForFastI2c> for InitError {
|
||||||
|
fn from(value: ClockTooSlowForFastI2c) -> Self {
|
||||||
|
Self::ClkTooSlow(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl embedded_hal::i2c::Error for Error {
|
||||||
|
fn kind(&self) -> embedded_hal::i2c::ErrorKind {
|
||||||
|
match self {
|
||||||
|
Error::ArbitrationLost => embedded_hal::i2c::ErrorKind::ArbitrationLoss,
|
||||||
|
Error::NackAddr => {
|
||||||
|
embedded_hal::i2c::ErrorKind::NoAcknowledge(i2c::NoAcknowledgeSource::Address)
|
||||||
|
}
|
||||||
|
Error::NackData => {
|
||||||
|
embedded_hal::i2c::ErrorKind::NoAcknowledge(i2c::NoAcknowledgeSource::Data)
|
||||||
|
}
|
||||||
|
Error::DataTooLarge | Error::InsufficientDataReceived | Error::InvalidTimingParams => {
|
||||||
|
embedded_hal::i2c::ErrorKind::Other
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Copy, Clone)]
|
||||||
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
|
enum I2cCmd {
|
||||||
|
Start = 0b00,
|
||||||
|
Stop = 0b10,
|
||||||
|
StartWithStop = 0b11,
|
||||||
|
Cancel = 0b100,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
|
||||||
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
|
pub enum I2cSpeed {
|
||||||
|
Regular100khz = 0,
|
||||||
|
Fast400khz = 1,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
|
pub enum I2cDirection {
|
||||||
|
Send = 0,
|
||||||
|
Read = 1,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
|
||||||
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
|
pub enum I2cAddress {
|
||||||
|
Regular(u8),
|
||||||
|
TenBit(u16),
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type I2cRegBlock = pac::i2c0::RegisterBlock;
|
||||||
|
|
||||||
|
/// Common trait implemented by all PAC peripheral access structures. The register block
|
||||||
|
/// format is the same for all SPI blocks.
|
||||||
|
pub trait Instance: Deref<Target = I2cRegBlock> {
|
||||||
|
const IDX: u8;
|
||||||
|
const PERIPH_SEL: PeripheralSelect;
|
||||||
|
|
||||||
|
fn ptr() -> *const I2cRegBlock;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Instance for pac::I2c0 {
|
||||||
|
const IDX: u8 = 0;
|
||||||
|
const PERIPH_SEL: PeripheralSelect = PeripheralSelect::I2c0;
|
||||||
|
|
||||||
|
fn ptr() -> *const I2cRegBlock {
|
||||||
|
Self::ptr()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Instance for pac::I2c1 {
|
||||||
|
const IDX: u8 = 1;
|
||||||
|
const PERIPH_SEL: PeripheralSelect = PeripheralSelect::I2c1;
|
||||||
|
|
||||||
|
fn ptr() -> *const I2cRegBlock {
|
||||||
|
Self::ptr()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Instance for pac::I2c2 {
|
||||||
|
const IDX: u8 = 2;
|
||||||
|
const PERIPH_SEL: PeripheralSelect = PeripheralSelect::I2c2;
|
||||||
|
|
||||||
|
fn ptr() -> *const I2cRegBlock {
|
||||||
|
Self::ptr()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//==================================================================================================
|
||||||
|
// Config
|
||||||
|
//==================================================================================================
|
||||||
|
|
||||||
|
pub struct TrTfThighTlow(u8, u8, u8, u8);
|
||||||
|
pub struct TsuStoTsuStaThdStaTBuf(u8, u8, u8, u8);
|
||||||
|
|
||||||
|
pub struct TimingCfg {
|
||||||
|
// 4 bit max width
|
||||||
|
tr: u8,
|
||||||
|
// 4 bit max width
|
||||||
|
tf: u8,
|
||||||
|
// 4 bit max width
|
||||||
|
thigh: u8,
|
||||||
|
// 4 bit max width
|
||||||
|
tlow: u8,
|
||||||
|
// 4 bit max width
|
||||||
|
tsu_sto: u8,
|
||||||
|
// 4 bit max width
|
||||||
|
tsu_sta: u8,
|
||||||
|
// 4 bit max width
|
||||||
|
thd_sta: u8,
|
||||||
|
// 4 bit max width
|
||||||
|
tbuf: u8,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TimingCfg {
|
||||||
|
pub fn new(
|
||||||
|
first_16_bits: TrTfThighTlow,
|
||||||
|
second_16_bits: TsuStoTsuStaThdStaTBuf,
|
||||||
|
) -> Result<Self, Error> {
|
||||||
|
if first_16_bits.0 > 0xf
|
||||||
|
|| first_16_bits.1 > 0xf
|
||||||
|
|| first_16_bits.2 > 0xf
|
||||||
|
|| first_16_bits.3 > 0xf
|
||||||
|
|| second_16_bits.0 > 0xf
|
||||||
|
|| second_16_bits.1 > 0xf
|
||||||
|
|| second_16_bits.2 > 0xf
|
||||||
|
|| second_16_bits.3 > 0xf
|
||||||
|
{
|
||||||
|
return Err(Error::InvalidTimingParams);
|
||||||
|
}
|
||||||
|
Ok(TimingCfg {
|
||||||
|
tr: first_16_bits.0,
|
||||||
|
tf: first_16_bits.1,
|
||||||
|
thigh: first_16_bits.2,
|
||||||
|
tlow: first_16_bits.3,
|
||||||
|
tsu_sto: second_16_bits.0,
|
||||||
|
tsu_sta: second_16_bits.1,
|
||||||
|
thd_sta: second_16_bits.2,
|
||||||
|
tbuf: second_16_bits.3,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn reg(&self) -> u32 {
|
||||||
|
(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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for TimingCfg {
|
||||||
|
fn default() -> Self {
|
||||||
|
TimingCfg {
|
||||||
|
tr: 0x02,
|
||||||
|
tf: 0x01,
|
||||||
|
thigh: 0x08,
|
||||||
|
tlow: 0x09,
|
||||||
|
tsu_sto: 0x8,
|
||||||
|
tsu_sta: 0x0a,
|
||||||
|
thd_sta: 0x8,
|
||||||
|
tbuf: 0xa,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct MasterConfig {
|
||||||
|
pub tx_fe_mode: FifoEmptyMode,
|
||||||
|
pub rx_fe_mode: FifoEmptyMode,
|
||||||
|
/// Enable the analog delay glitch filter
|
||||||
|
pub alg_filt: bool,
|
||||||
|
/// Enable the digital glitch filter
|
||||||
|
pub dlg_filt: bool,
|
||||||
|
pub tm_cfg: Option<TimingCfg>,
|
||||||
|
// Loopback mode
|
||||||
|
// lbm: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for MasterConfig {
|
||||||
|
fn default() -> Self {
|
||||||
|
MasterConfig {
|
||||||
|
tx_fe_mode: FifoEmptyMode::Stall,
|
||||||
|
rx_fe_mode: FifoEmptyMode::Stall,
|
||||||
|
alg_filt: false,
|
||||||
|
dlg_filt: false,
|
||||||
|
tm_cfg: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Sealed for MasterConfig {}
|
||||||
|
|
||||||
|
pub struct SlaveConfig {
|
||||||
|
pub tx_fe_mode: FifoEmptyMode,
|
||||||
|
pub rx_fe_mode: FifoEmptyMode,
|
||||||
|
/// Maximum number of words before issuing a negative acknowledge.
|
||||||
|
/// Range should be 0 to 0x7fe. Setting the value to 0x7ff has the same effect as not setting
|
||||||
|
/// the enable bit since RXCOUNT stops counting at 0x7fe.
|
||||||
|
pub max_words: Option<usize>,
|
||||||
|
/// A received address is compared to the ADDRESS register (addr) using the address mask
|
||||||
|
/// (addr_mask). Those bits with a 1 in the address mask must match for there to be an address
|
||||||
|
/// match
|
||||||
|
pub addr: I2cAddress,
|
||||||
|
/// The default address mask will be 0x3ff to only allow full matches
|
||||||
|
pub addr_mask: Option<u16>,
|
||||||
|
/// Optionally specify a second I2C address the slave interface responds to
|
||||||
|
pub addr_b: Option<I2cAddress>,
|
||||||
|
pub addr_b_mask: Option<u16>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SlaveConfig {
|
||||||
|
/// Build a default slave config given a specified slave address to respond to
|
||||||
|
pub fn new(addr: I2cAddress) -> Self {
|
||||||
|
SlaveConfig {
|
||||||
|
tx_fe_mode: FifoEmptyMode::Stall,
|
||||||
|
rx_fe_mode: FifoEmptyMode::Stall,
|
||||||
|
max_words: None,
|
||||||
|
addr,
|
||||||
|
addr_mask: None,
|
||||||
|
addr_b: None,
|
||||||
|
addr_b_mask: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Sealed for SlaveConfig {}
|
||||||
|
|
||||||
|
//==================================================================================================
|
||||||
|
// I2C Base
|
||||||
|
//==================================================================================================
|
||||||
|
|
||||||
|
pub struct I2cBase<I2c> {
|
||||||
|
i2c: I2c,
|
||||||
|
clock: Hertz,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<I2C> I2cBase<I2C> {
|
||||||
|
#[inline]
|
||||||
|
fn unwrap_addr(addr: I2cAddress) -> (u16, u32) {
|
||||||
|
match addr {
|
||||||
|
I2cAddress::Regular(addr) => (addr as u16, 0 << 15),
|
||||||
|
I2cAddress::TenBit(addr) => (addr, 1 << 15),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<I2c: Instance> I2cBase<I2c> {
|
||||||
|
pub fn new(
|
||||||
|
i2c: I2c,
|
||||||
|
sys_cfg: &mut pac::Sysconfig,
|
||||||
|
clocks: &Clocks,
|
||||||
|
speed_mode: I2cSpeed,
|
||||||
|
ms_cfg: Option<&MasterConfig>,
|
||||||
|
sl_cfg: Option<&SlaveConfig>,
|
||||||
|
) -> Result<Self, ClockTooSlowForFastI2c> {
|
||||||
|
enable_peripheral_clock(sys_cfg, I2c::PERIPH_SEL);
|
||||||
|
assert_periph_reset(sys_cfg, I2c::PERIPH_SEL);
|
||||||
|
cortex_m::asm::nop();
|
||||||
|
cortex_m::asm::nop();
|
||||||
|
deassert_periph_reset(sys_cfg, I2c::PERIPH_SEL);
|
||||||
|
|
||||||
|
let mut i2c_base = I2cBase {
|
||||||
|
i2c,
|
||||||
|
clock: clocks.apb1(),
|
||||||
|
};
|
||||||
|
if let Some(ms_cfg) = ms_cfg {
|
||||||
|
i2c_base.cfg_master(ms_cfg);
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(sl_cfg) = sl_cfg {
|
||||||
|
i2c_base.cfg_slave(sl_cfg);
|
||||||
|
}
|
||||||
|
i2c_base.cfg_clk_scale(speed_mode)?;
|
||||||
|
Ok(i2c_base)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn cfg_master(&mut self, ms_cfg: &MasterConfig) {
|
||||||
|
let (txfemd, rxfemd) = match (ms_cfg.tx_fe_mode, ms_cfg.rx_fe_mode) {
|
||||||
|
(FifoEmptyMode::Stall, FifoEmptyMode::Stall) => (false, false),
|
||||||
|
(FifoEmptyMode::Stall, FifoEmptyMode::EndTransaction) => (false, true),
|
||||||
|
(FifoEmptyMode::EndTransaction, FifoEmptyMode::Stall) => (true, false),
|
||||||
|
(FifoEmptyMode::EndTransaction, FifoEmptyMode::EndTransaction) => (true, true),
|
||||||
|
};
|
||||||
|
self.i2c.ctrl().modify(|_, w| {
|
||||||
|
w.txfemd().bit(txfemd);
|
||||||
|
w.rxffmd().bit(rxfemd);
|
||||||
|
w.dlgfilter().bit(ms_cfg.dlg_filt);
|
||||||
|
w.algfilter().bit(ms_cfg.alg_filt)
|
||||||
|
});
|
||||||
|
if let Some(ref tm_cfg) = ms_cfg.tm_cfg {
|
||||||
|
self.i2c
|
||||||
|
.tmconfig()
|
||||||
|
.write(|w| unsafe { w.bits(tm_cfg.reg()) });
|
||||||
|
}
|
||||||
|
self.i2c.fifo_clr().write(|w| {
|
||||||
|
w.rxfifo().set_bit();
|
||||||
|
w.txfifo().set_bit()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fn cfg_slave(&mut self, sl_cfg: &SlaveConfig) {
|
||||||
|
let (txfemd, rxfemd) = match (sl_cfg.tx_fe_mode, sl_cfg.rx_fe_mode) {
|
||||||
|
(FifoEmptyMode::Stall, FifoEmptyMode::Stall) => (false, false),
|
||||||
|
(FifoEmptyMode::Stall, FifoEmptyMode::EndTransaction) => (false, true),
|
||||||
|
(FifoEmptyMode::EndTransaction, FifoEmptyMode::Stall) => (true, false),
|
||||||
|
(FifoEmptyMode::EndTransaction, FifoEmptyMode::EndTransaction) => (true, true),
|
||||||
|
};
|
||||||
|
self.i2c.s0_ctrl().modify(|_, w| {
|
||||||
|
w.txfemd().bit(txfemd);
|
||||||
|
w.rxffmd().bit(rxfemd)
|
||||||
|
});
|
||||||
|
self.i2c.s0_fifo_clr().write(|w| {
|
||||||
|
w.rxfifo().set_bit();
|
||||||
|
w.txfifo().set_bit()
|
||||||
|
});
|
||||||
|
let max_words = sl_cfg.max_words;
|
||||||
|
if let Some(max_words) = max_words {
|
||||||
|
self.i2c
|
||||||
|
.s0_maxwords()
|
||||||
|
.write(|w| unsafe { w.bits(1 << 31 | max_words as u32) });
|
||||||
|
}
|
||||||
|
let (addr, addr_mode_mask) = Self::unwrap_addr(sl_cfg.addr);
|
||||||
|
// The first bit is the read/write value. Normally, both read and write are matched
|
||||||
|
// using the RWMASK bit of the address mask register
|
||||||
|
self.i2c
|
||||||
|
.s0_address()
|
||||||
|
.write(|w| unsafe { w.bits((addr << 1) as u32 | addr_mode_mask) });
|
||||||
|
if let Some(addr_mask) = sl_cfg.addr_mask {
|
||||||
|
self.i2c
|
||||||
|
.s0_addressmask()
|
||||||
|
.write(|w| unsafe { w.bits((addr_mask << 1) as u32) });
|
||||||
|
}
|
||||||
|
if let Some(addr_b) = sl_cfg.addr_b {
|
||||||
|
let (addr, addr_mode_mask) = Self::unwrap_addr(addr_b);
|
||||||
|
self.i2c
|
||||||
|
.s0_addressb()
|
||||||
|
.write(|w| unsafe { w.bits((addr << 1) as u32 | addr_mode_mask) })
|
||||||
|
}
|
||||||
|
if let Some(addr_b_mask) = sl_cfg.addr_b_mask {
|
||||||
|
self.i2c
|
||||||
|
.s0_addressmaskb()
|
||||||
|
.write(|w| unsafe { w.bits((addr_b_mask << 1) as u32) })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn filters(&mut self, digital_filt: bool, analog_filt: bool) {
|
||||||
|
self.i2c.ctrl().modify(|_, w| {
|
||||||
|
w.dlgfilter().bit(digital_filt);
|
||||||
|
w.algfilter().bit(analog_filt)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn fifo_empty_mode(&mut self, rx: FifoEmptyMode, tx: FifoEmptyMode) {
|
||||||
|
self.i2c.ctrl().modify(|_, w| {
|
||||||
|
w.txfemd().bit(tx as u8 != 0);
|
||||||
|
w.rxffmd().bit(rx as u8 != 0)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fn calc_clk_div(&self, speed_mode: I2cSpeed) -> Result<u8, ClockTooSlowForFastI2c> {
|
||||||
|
if speed_mode == I2cSpeed::Regular100khz {
|
||||||
|
Ok(((self.clock.raw() / CLK_100K.raw() / 20) - 1) as u8)
|
||||||
|
} else {
|
||||||
|
if self.clock.raw() < MIN_CLK_400K.raw() {
|
||||||
|
return Err(ClockTooSlowForFastI2c);
|
||||||
|
}
|
||||||
|
Ok(((self.clock.raw() / CLK_400K.raw() / 25) - 1) as u8)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Configures the clock scale for a given speed mode setting
|
||||||
|
pub fn cfg_clk_scale(&mut self, speed_mode: I2cSpeed) -> Result<(), ClockTooSlowForFastI2c> {
|
||||||
|
let clk_div = self.calc_clk_div(speed_mode)?;
|
||||||
|
self.i2c
|
||||||
|
.clkscale()
|
||||||
|
.write(|w| unsafe { w.bits((speed_mode as u32) << 31 | clk_div as u32) });
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn load_address(&mut self, addr: u16) {
|
||||||
|
// Load address
|
||||||
|
self.i2c
|
||||||
|
.address()
|
||||||
|
.write(|w| unsafe { w.bits((addr << 1) as u32) });
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn stop_cmd(&mut self) {
|
||||||
|
self.i2c
|
||||||
|
.cmd()
|
||||||
|
.write(|w| unsafe { w.bits(I2cCmd::Stop as u32) });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//==================================================================================================
|
||||||
|
// I2C Master
|
||||||
|
//==================================================================================================
|
||||||
|
|
||||||
|
pub struct I2cMaster<I2c, Addr = SevenBitAddress> {
|
||||||
|
i2c_base: I2cBase<I2c>,
|
||||||
|
addr: PhantomData<Addr>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<I2c: Instance, Addr> I2cMaster<I2c, Addr> {
|
||||||
|
pub fn new(
|
||||||
|
i2c: I2c,
|
||||||
|
sys_cfg: &mut pac::Sysconfig,
|
||||||
|
cfg: MasterConfig,
|
||||||
|
clocks: &Clocks,
|
||||||
|
speed_mode: I2cSpeed,
|
||||||
|
) -> Result<Self, ClockTooSlowForFastI2c> {
|
||||||
|
Ok(I2cMaster {
|
||||||
|
i2c_base: I2cBase::new(i2c, sys_cfg, clocks, speed_mode, Some(&cfg), None)?,
|
||||||
|
addr: PhantomData,
|
||||||
|
}
|
||||||
|
.enable_master())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn cancel_transfer(&self) {
|
||||||
|
self.i2c_base
|
||||||
|
.i2c
|
||||||
|
.cmd()
|
||||||
|
.write(|w| unsafe { w.bits(I2cCmd::Cancel as u32) });
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn clear_tx_fifo(&self) {
|
||||||
|
self.i2c_base.i2c.fifo_clr().write(|w| w.txfifo().set_bit());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn clear_rx_fifo(&self) {
|
||||||
|
self.i2c_base.i2c.fifo_clr().write(|w| w.rxfifo().set_bit());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn enable_master(self) -> Self {
|
||||||
|
self.i2c_base.i2c.ctrl().modify(|_, w| w.enable().set_bit());
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn disable_master(self) -> Self {
|
||||||
|
self.i2c_base
|
||||||
|
.i2c
|
||||||
|
.ctrl()
|
||||||
|
.modify(|_, w| w.enable().clear_bit());
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn load_fifo(&self, word: u8) {
|
||||||
|
self.i2c_base
|
||||||
|
.i2c
|
||||||
|
.data()
|
||||||
|
.write(|w| unsafe { w.bits(word as u32) });
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn read_fifo(&self) -> u8 {
|
||||||
|
self.i2c_base.i2c.data().read().bits() as u8
|
||||||
|
}
|
||||||
|
|
||||||
|
fn error_handler_write(&mut self, init_cmd: &I2cCmd) {
|
||||||
|
self.clear_tx_fifo();
|
||||||
|
if *init_cmd == I2cCmd::Start {
|
||||||
|
self.i2c_base.stop_cmd()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_base(
|
||||||
|
&mut self,
|
||||||
|
addr: I2cAddress,
|
||||||
|
init_cmd: I2cCmd,
|
||||||
|
bytes: impl IntoIterator<Item = u8>,
|
||||||
|
) -> Result<(), Error> {
|
||||||
|
let mut iter = bytes.into_iter();
|
||||||
|
// Load address
|
||||||
|
let (addr, addr_mode_bit) = I2cBase::<I2c>::unwrap_addr(addr);
|
||||||
|
self.i2c_base.i2c.address().write(|w| unsafe {
|
||||||
|
w.bits(I2cDirection::Send as u32 | (addr << 1) as u32 | addr_mode_bit)
|
||||||
|
});
|
||||||
|
|
||||||
|
self.i2c_base
|
||||||
|
.i2c
|
||||||
|
.cmd()
|
||||||
|
.write(|w| unsafe { w.bits(init_cmd as u32) });
|
||||||
|
let mut load_if_next_available = || {
|
||||||
|
if let Some(next_byte) = iter.next() {
|
||||||
|
self.load_fifo(next_byte);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
loop {
|
||||||
|
let status_reader = self.i2c_base.i2c.status().read();
|
||||||
|
if status_reader.arblost().bit_is_set() {
|
||||||
|
self.error_handler_write(&init_cmd);
|
||||||
|
return Err(Error::ArbitrationLost);
|
||||||
|
} else if status_reader.nackaddr().bit_is_set() {
|
||||||
|
self.error_handler_write(&init_cmd);
|
||||||
|
return Err(Error::NackAddr);
|
||||||
|
} else if status_reader.nackdata().bit_is_set() {
|
||||||
|
self.error_handler_write(&init_cmd);
|
||||||
|
return Err(Error::NackData);
|
||||||
|
} else if status_reader.idle().bit_is_set() {
|
||||||
|
return Ok(());
|
||||||
|
} else {
|
||||||
|
while !status_reader.txnfull().bit_is_set() {
|
||||||
|
load_if_next_available();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_from_buffer(
|
||||||
|
&mut self,
|
||||||
|
init_cmd: I2cCmd,
|
||||||
|
addr: I2cAddress,
|
||||||
|
output: &[u8],
|
||||||
|
) -> Result<(), Error> {
|
||||||
|
let len = output.len();
|
||||||
|
// It should theoretically possible to transfer larger data sizes by tracking
|
||||||
|
// the number of sent words and setting it to 0x7fe as soon as only that many
|
||||||
|
// bytes are remaining. However, large transfer like this are not common. This
|
||||||
|
// feature will therefore not be supported for now.
|
||||||
|
if len > 0x7fe {
|
||||||
|
return Err(Error::DataTooLarge);
|
||||||
|
}
|
||||||
|
// Load number of words
|
||||||
|
self.i2c_base
|
||||||
|
.i2c
|
||||||
|
.words()
|
||||||
|
.write(|w| unsafe { w.bits(len as u32) });
|
||||||
|
let mut bytes = output.iter();
|
||||||
|
// FIFO has a depth of 16. We load slightly above the trigger level
|
||||||
|
// but not all of it because the transaction might fail immediately
|
||||||
|
const FILL_DEPTH: usize = 12;
|
||||||
|
|
||||||
|
// load the FIFO
|
||||||
|
for _ in 0..core::cmp::min(FILL_DEPTH, len) {
|
||||||
|
self.load_fifo(*bytes.next().unwrap());
|
||||||
|
}
|
||||||
|
|
||||||
|
self.write_base(addr, init_cmd, output.iter().cloned())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_internal(&mut self, addr: I2cAddress, buffer: &mut [u8]) -> Result<(), Error> {
|
||||||
|
let len = buffer.len();
|
||||||
|
// It should theoretically possible to transfer larger data sizes by tracking
|
||||||
|
// the number of sent words and setting it to 0x7fe as soon as only that many
|
||||||
|
// bytes are remaining. However, large transfer like this are not common. This
|
||||||
|
// feature will therefore not be supported for now.
|
||||||
|
if len > 0x7fe {
|
||||||
|
return Err(Error::DataTooLarge);
|
||||||
|
}
|
||||||
|
// Clear the receive FIFO
|
||||||
|
self.clear_rx_fifo();
|
||||||
|
|
||||||
|
// Load number of words
|
||||||
|
self.i2c_base
|
||||||
|
.i2c
|
||||||
|
.words()
|
||||||
|
.write(|w| unsafe { w.bits(len as u32) });
|
||||||
|
let (addr, addr_mode_bit) = match addr {
|
||||||
|
I2cAddress::Regular(addr) => (addr as u16, 0 << 15),
|
||||||
|
I2cAddress::TenBit(addr) => (addr, 1 << 15),
|
||||||
|
};
|
||||||
|
// Load address
|
||||||
|
self.i2c_base.i2c.address().write(|w| unsafe {
|
||||||
|
w.bits(I2cDirection::Read as u32 | (addr << 1) as u32 | addr_mode_bit)
|
||||||
|
});
|
||||||
|
|
||||||
|
let mut buf_iter = buffer.iter_mut();
|
||||||
|
let mut read_bytes = 0;
|
||||||
|
// Start receive transfer
|
||||||
|
self.i2c_base
|
||||||
|
.i2c
|
||||||
|
.cmd()
|
||||||
|
.write(|w| unsafe { w.bits(I2cCmd::StartWithStop as u32) });
|
||||||
|
let mut read_if_next_available = || {
|
||||||
|
if let Some(next_byte) = buf_iter.next() {
|
||||||
|
*next_byte = self.read_fifo();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
loop {
|
||||||
|
let status_reader = self.i2c_base.i2c.status().read();
|
||||||
|
if status_reader.arblost().bit_is_set() {
|
||||||
|
self.clear_rx_fifo();
|
||||||
|
return Err(Error::ArbitrationLost);
|
||||||
|
} else if status_reader.nackaddr().bit_is_set() {
|
||||||
|
self.clear_rx_fifo();
|
||||||
|
return Err(Error::NackAddr);
|
||||||
|
} else if status_reader.idle().bit_is_set() {
|
||||||
|
if read_bytes != len {
|
||||||
|
return Err(Error::InsufficientDataReceived);
|
||||||
|
}
|
||||||
|
return Ok(());
|
||||||
|
} else if status_reader.rxnempty().bit_is_set() {
|
||||||
|
read_if_next_available();
|
||||||
|
read_bytes += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//======================================================================================
|
||||||
|
// Embedded HAL I2C implementations
|
||||||
|
//======================================================================================
|
||||||
|
|
||||||
|
impl<I2c> embedded_hal::i2c::ErrorType for I2cMaster<I2c, SevenBitAddress> {
|
||||||
|
type Error = Error;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<I2c: Instance> embedded_hal::i2c::I2c for I2cMaster<I2c, SevenBitAddress> {
|
||||||
|
fn transaction(
|
||||||
|
&mut self,
|
||||||
|
address: SevenBitAddress,
|
||||||
|
operations: &mut [Operation<'_>],
|
||||||
|
) -> Result<(), Self::Error> {
|
||||||
|
for operation in operations {
|
||||||
|
match operation {
|
||||||
|
Operation::Read(buf) => self.read_internal(I2cAddress::Regular(address), buf)?,
|
||||||
|
Operation::Write(buf) => self.write_from_buffer(
|
||||||
|
I2cCmd::StartWithStop,
|
||||||
|
I2cAddress::Regular(address),
|
||||||
|
buf,
|
||||||
|
)?,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<I2c> embedded_hal::i2c::ErrorType for I2cMaster<I2c, TenBitAddress> {
|
||||||
|
type Error = Error;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<I2c: Instance> embedded_hal::i2c::I2c<TenBitAddress> for I2cMaster<I2c, TenBitAddress> {
|
||||||
|
fn transaction(
|
||||||
|
&mut self,
|
||||||
|
address: TenBitAddress,
|
||||||
|
operations: &mut [Operation<'_>],
|
||||||
|
) -> Result<(), Self::Error> {
|
||||||
|
for operation in operations {
|
||||||
|
match operation {
|
||||||
|
Operation::Read(buf) => self.read_internal(I2cAddress::TenBit(address), buf)?,
|
||||||
|
Operation::Write(buf) => {
|
||||||
|
self.write_from_buffer(I2cCmd::StartWithStop, I2cAddress::TenBit(address), buf)?
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//==================================================================================================
|
||||||
|
// I2C Slave
|
||||||
|
//==================================================================================================
|
||||||
|
|
||||||
|
pub struct I2cSlave<I2c, Addr = SevenBitAddress> {
|
||||||
|
i2c_base: I2cBase<I2c>,
|
||||||
|
addr: PhantomData<Addr>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<I2c: Instance, Addr> I2cSlave<I2c, Addr> {
|
||||||
|
fn new_generic(
|
||||||
|
i2c: I2c,
|
||||||
|
sys_cfg: &mut pac::Sysconfig,
|
||||||
|
cfg: SlaveConfig,
|
||||||
|
clocks: &Clocks,
|
||||||
|
speed_mode: I2cSpeed,
|
||||||
|
) -> Result<Self, ClockTooSlowForFastI2c> {
|
||||||
|
Ok(I2cSlave {
|
||||||
|
i2c_base: I2cBase::new(i2c, sys_cfg, clocks, speed_mode, None, Some(&cfg))?,
|
||||||
|
addr: PhantomData,
|
||||||
|
}
|
||||||
|
.enable_slave())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn enable_slave(self) -> Self {
|
||||||
|
self.i2c_base
|
||||||
|
.i2c
|
||||||
|
.s0_ctrl()
|
||||||
|
.modify(|_, w| w.enable().set_bit());
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn disable_slave(self) -> Self {
|
||||||
|
self.i2c_base
|
||||||
|
.i2c
|
||||||
|
.s0_ctrl()
|
||||||
|
.modify(|_, w| w.enable().clear_bit());
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn load_fifo(&self, word: u8) {
|
||||||
|
self.i2c_base
|
||||||
|
.i2c
|
||||||
|
.s0_data()
|
||||||
|
.write(|w| unsafe { w.bits(word as u32) });
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn read_fifo(&self) -> u8 {
|
||||||
|
self.i2c_base.i2c.s0_data().read().bits() as u8
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn clear_tx_fifo(&self) {
|
||||||
|
self.i2c_base
|
||||||
|
.i2c
|
||||||
|
.s0_fifo_clr()
|
||||||
|
.write(|w| w.txfifo().set_bit());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn clear_rx_fifo(&self) {
|
||||||
|
self.i2c_base
|
||||||
|
.i2c
|
||||||
|
.s0_fifo_clr()
|
||||||
|
.write(|w| w.rxfifo().set_bit());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the last address that was matched by the slave control and the corresponding
|
||||||
|
/// master direction
|
||||||
|
pub fn last_address(&self) -> (I2cDirection, u32) {
|
||||||
|
let bits = self.i2c_base.i2c.s0_lastaddress().read().bits();
|
||||||
|
match bits & 0x01 {
|
||||||
|
0 => (I2cDirection::Send, bits >> 1),
|
||||||
|
1 => (I2cDirection::Read, bits >> 1),
|
||||||
|
_ => (I2cDirection::Send, bits >> 1),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn write(&mut self, output: &[u8]) -> Result<(), Error> {
|
||||||
|
let len = output.len();
|
||||||
|
// It should theoretically possible to transfer larger data sizes by tracking
|
||||||
|
// the number of sent words and setting it to 0x7fe as soon as only that many
|
||||||
|
// bytes are remaining. However, large transfer like this are not common. This
|
||||||
|
// feature will therefore not be supported for now.
|
||||||
|
if len > 0x7fe {
|
||||||
|
return Err(Error::DataTooLarge);
|
||||||
|
}
|
||||||
|
let mut bytes = output.iter();
|
||||||
|
// FIFO has a depth of 16. We load slightly above the trigger level
|
||||||
|
// but not all of it because the transaction might fail immediately
|
||||||
|
const FILL_DEPTH: usize = 12;
|
||||||
|
|
||||||
|
// load the FIFO
|
||||||
|
for _ in 0..core::cmp::min(FILL_DEPTH, len) {
|
||||||
|
self.load_fifo(*bytes.next().unwrap());
|
||||||
|
}
|
||||||
|
|
||||||
|
let status_reader = self.i2c_base.i2c.s0_status().read();
|
||||||
|
let mut load_if_next_available = || {
|
||||||
|
if let Some(next_byte) = bytes.next() {
|
||||||
|
self.load_fifo(*next_byte);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
loop {
|
||||||
|
if status_reader.nackdata().bit_is_set() {
|
||||||
|
self.clear_tx_fifo();
|
||||||
|
return Err(Error::NackData);
|
||||||
|
} else if status_reader.idle().bit_is_set() {
|
||||||
|
return Ok(());
|
||||||
|
} else {
|
||||||
|
while !status_reader.txnfull().bit_is_set() {
|
||||||
|
load_if_next_available();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn read(&mut self, buffer: &mut [u8]) -> Result<(), Error> {
|
||||||
|
let len = buffer.len();
|
||||||
|
// It should theoretically possible to transfer larger data sizes by tracking
|
||||||
|
// the number of sent words and setting it to 0x7fe as soon as only that many
|
||||||
|
// bytes are remaining. However, large transfer like this are not common. This
|
||||||
|
// feature will therefore not be supported for now.
|
||||||
|
if len > 0x7fe {
|
||||||
|
return Err(Error::DataTooLarge);
|
||||||
|
}
|
||||||
|
// Clear the receive FIFO
|
||||||
|
self.clear_rx_fifo();
|
||||||
|
|
||||||
|
let mut buf_iter = buffer.iter_mut();
|
||||||
|
let mut read_bytes = 0;
|
||||||
|
let mut read_if_next_available = || {
|
||||||
|
if let Some(next_byte) = buf_iter.next() {
|
||||||
|
*next_byte = self.read_fifo();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
loop {
|
||||||
|
let status_reader = self.i2c_base.i2c.s0_status().read();
|
||||||
|
if status_reader.idle().bit_is_set() {
|
||||||
|
if read_bytes != len {
|
||||||
|
return Err(Error::InsufficientDataReceived);
|
||||||
|
}
|
||||||
|
return Ok(());
|
||||||
|
} else if status_reader.rxnempty().bit_is_set() {
|
||||||
|
read_bytes += 1;
|
||||||
|
read_if_next_available();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<I2c: Instance> I2cSlave<I2c, SevenBitAddress> {
|
||||||
|
/// Create a new I2C slave for seven bit addresses
|
||||||
|
pub fn new(
|
||||||
|
i2c: I2c,
|
||||||
|
sys_cfg: &mut pac::Sysconfig,
|
||||||
|
cfg: SlaveConfig,
|
||||||
|
clocks: &Clocks,
|
||||||
|
speed_mode: I2cSpeed,
|
||||||
|
) -> Result<Self, InitError> {
|
||||||
|
if let I2cAddress::TenBit(_) = cfg.addr {
|
||||||
|
return Err(InitError::WrongAddrMode);
|
||||||
|
}
|
||||||
|
Ok(Self::new_generic(i2c, sys_cfg, cfg, clocks, speed_mode)?)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<I2c: Instance> I2cSlave<I2c, TenBitAddress> {
|
||||||
|
pub fn new_ten_bit_addr(
|
||||||
|
i2c: I2c,
|
||||||
|
sys_cfg: &mut pac::Sysconfig,
|
||||||
|
cfg: SlaveConfig,
|
||||||
|
clocks: &Clocks,
|
||||||
|
speed_mode: I2cSpeed,
|
||||||
|
) -> Result<Self, ClockTooSlowForFastI2c> {
|
||||||
|
Self::new_generic(i2c, sys_cfg, cfg, clocks, speed_mode)
|
||||||
|
}
|
||||||
|
}
|
@ -9,8 +9,11 @@ pub mod prelude;
|
|||||||
|
|
||||||
pub mod clock;
|
pub mod clock;
|
||||||
pub mod gpio;
|
pub mod gpio;
|
||||||
|
pub mod i2c;
|
||||||
|
pub mod pwm;
|
||||||
pub mod spi;
|
pub mod spi;
|
||||||
pub mod time;
|
pub mod time;
|
||||||
|
pub mod timer;
|
||||||
pub mod typelevel;
|
pub mod typelevel;
|
||||||
pub mod uart;
|
pub mod uart;
|
||||||
pub mod wdt;
|
pub mod wdt;
|
||||||
@ -23,22 +26,20 @@ pub enum FunSel {
|
|||||||
Sel3 = 0b11,
|
Sel3 = 0b11,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Generic IRQ config which can be used to specify whether the HAL driver will
|
/// Enable a specific interrupt using the NVIC peripheral.
|
||||||
/// 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
|
/// # Safety
|
||||||
/// this steps themselves
|
///
|
||||||
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
/// This function is `unsafe` because it can break mask-based critical sections.
|
||||||
pub struct IrqCfg {
|
#[inline]
|
||||||
/// Interrupt target vector. Should always be set, might be required for disabling IRQs
|
pub unsafe fn enable_interrupt(irq: pac::Interrupt) {
|
||||||
pub irq: pac::Interrupt,
|
unsafe {
|
||||||
/// Specfiy whether IRQ should be routed to an IRQ vector using the IRQSEL peripheral
|
cortex_m::peripheral::NVIC::unmask(irq);
|
||||||
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 }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Disable a specific interrupt using the NVIC peripheral.
|
||||||
|
#[inline]
|
||||||
|
pub fn disable_interrupt(irq: pac::Interrupt) {
|
||||||
|
cortex_m::peripheral::NVIC::mask(irq);
|
||||||
|
}
|
||||||
|
388
va416xx-hal/src/pwm.rs
Normal file
388
va416xx-hal/src/pwm.rs
Normal file
@ -0,0 +1,388 @@
|
|||||||
|
//! API for Pulse-Width Modulation (PWM)
|
||||||
|
//!
|
||||||
|
//! The Vorago VA416xx devices use the TIM peripherals to perform PWM related tasks.
|
||||||
|
//!
|
||||||
|
//! ## Examples
|
||||||
|
//!
|
||||||
|
//! - [PWM example](https://egit.irs.uni-stuttgart.de/rust/va416xx-rs/src/branch/main/examples/simple/examples/pwm.rs)
|
||||||
|
use core::convert::Infallible;
|
||||||
|
use core::marker::PhantomData;
|
||||||
|
|
||||||
|
use crate::pac;
|
||||||
|
use crate::{clock::Clocks, gpio::DynPinId};
|
||||||
|
pub use crate::{gpio::PinId, time::Hertz, timer::*};
|
||||||
|
|
||||||
|
const DUTY_MAX: u16 = u16::MAX;
|
||||||
|
|
||||||
|
pub struct PwmBase {
|
||||||
|
clock: Hertz,
|
||||||
|
/// For PWMB, this is the upper limit
|
||||||
|
current_duty: u16,
|
||||||
|
/// For PWMA, this value will not be used
|
||||||
|
current_lower_limit: u16,
|
||||||
|
current_period: Hertz,
|
||||||
|
current_rst_val: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
enum StatusSelPwm {
|
||||||
|
PwmA = 3,
|
||||||
|
PwmB = 4,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct PwmA {}
|
||||||
|
pub struct PwmB {}
|
||||||
|
|
||||||
|
//==================================================================================================
|
||||||
|
// Common
|
||||||
|
//==================================================================================================
|
||||||
|
|
||||||
|
macro_rules! pwm_common_func {
|
||||||
|
() => {
|
||||||
|
#[inline]
|
||||||
|
fn enable_pwm_a(&mut self) {
|
||||||
|
self.reg
|
||||||
|
.reg()
|
||||||
|
.ctrl()
|
||||||
|
.modify(|_, w| unsafe { w.status_sel().bits(StatusSelPwm::PwmA as u8) });
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn enable_pwm_b(&mut self) {
|
||||||
|
self.reg
|
||||||
|
.reg()
|
||||||
|
.ctrl()
|
||||||
|
.modify(|_, w| unsafe { w.status_sel().bits(StatusSelPwm::PwmB as u8) });
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn get_period(&self) -> Hertz {
|
||||||
|
self.pwm_base.current_period
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn set_period(&mut self, period: impl Into<Hertz>) {
|
||||||
|
self.pwm_base.current_period = period.into();
|
||||||
|
// Avoid division by 0
|
||||||
|
if self.pwm_base.current_period.raw() == 0 {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
self.pwm_base.current_rst_val =
|
||||||
|
self.pwm_base.clock.raw() / self.pwm_base.current_period.raw();
|
||||||
|
self.reg
|
||||||
|
.reg()
|
||||||
|
.rst_value()
|
||||||
|
.write(|w| unsafe { w.bits(self.pwm_base.current_rst_val) });
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn disable(&mut self) {
|
||||||
|
self.reg.reg().ctrl().modify(|_, w| w.enable().clear_bit());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn enable(&mut self) {
|
||||||
|
self.reg.reg().ctrl().modify(|_, w| w.enable().set_bit());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn period(&self) -> Hertz {
|
||||||
|
self.pwm_base.current_period
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn duty(&self) -> u16 {
|
||||||
|
self.pwm_base.current_duty
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! pwmb_func {
|
||||||
|
() => {
|
||||||
|
pub fn pwmb_lower_limit(&self) -> u16 {
|
||||||
|
self.pwm_base.current_lower_limit
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn pwmb_upper_limit(&self) -> u16 {
|
||||||
|
self.pwm_base.current_duty
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set the lower limit for PWMB
|
||||||
|
///
|
||||||
|
/// The PWM signal will be 1 as long as the current RST counter is larger than
|
||||||
|
/// the lower limit. For example, with a lower limit of 0.5 and and an upper limit
|
||||||
|
/// of 0.7, Only a fixed period between 0.5 * period and 0.7 * period will be in a high
|
||||||
|
/// state
|
||||||
|
pub fn set_pwmb_lower_limit(&mut self, duty: u16) {
|
||||||
|
self.pwm_base.current_lower_limit = duty;
|
||||||
|
let pwmb_val: u64 = (self.pwm_base.current_rst_val as u64
|
||||||
|
* self.pwm_base.current_lower_limit as u64)
|
||||||
|
/ DUTY_MAX as u64;
|
||||||
|
self.reg
|
||||||
|
.reg()
|
||||||
|
.pwmb_value()
|
||||||
|
.write(|w| unsafe { w.bits(pwmb_val as u32) });
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set the higher limit for PWMB
|
||||||
|
///
|
||||||
|
/// The PWM signal will be 1 as long as the current RST counter is smaller than
|
||||||
|
/// the higher limit. For example, with a lower limit of 0.5 and and an upper limit
|
||||||
|
/// of 0.7, Only a fixed period between 0.5 * period and 0.7 * period will be in a high
|
||||||
|
/// state
|
||||||
|
pub fn set_pwmb_upper_limit(&mut self, duty: u16) {
|
||||||
|
self.pwm_base.current_duty = duty;
|
||||||
|
let pwma_val: u64 = (self.pwm_base.current_rst_val as u64
|
||||||
|
* self.pwm_base.current_duty as u64)
|
||||||
|
/ DUTY_MAX as u64;
|
||||||
|
self.reg
|
||||||
|
.reg()
|
||||||
|
.pwma_value()
|
||||||
|
.write(|w| unsafe { w.bits(pwma_val as u32) });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
//==================================================================================================
|
||||||
|
// Strongly typed PWM pin
|
||||||
|
//==================================================================================================
|
||||||
|
|
||||||
|
pub struct PwmPin<Pin: TimPin, Tim: ValidTim, Mode = PwmA> {
|
||||||
|
reg: TimAndPinRegister<Pin, Tim>,
|
||||||
|
pwm_base: PwmBase,
|
||||||
|
mode: PhantomData<Mode>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Pin: TimPin, Tim: ValidTim, Mode> PwmPin<Pin, Tim, Mode>
|
||||||
|
where
|
||||||
|
(Pin, Tim): ValidTimAndPin<Pin, Tim>,
|
||||||
|
{
|
||||||
|
/// Create a new stronlgy typed PWM pin
|
||||||
|
pub fn new(
|
||||||
|
pin_and_tim: (Pin, Tim),
|
||||||
|
sys_cfg: &mut pac::Sysconfig,
|
||||||
|
clocks: &Clocks,
|
||||||
|
initial_period: impl Into<Hertz> + Copy,
|
||||||
|
) -> Self {
|
||||||
|
let mut pin = PwmPin {
|
||||||
|
pwm_base: PwmBase {
|
||||||
|
current_duty: 0,
|
||||||
|
current_lower_limit: 0,
|
||||||
|
current_period: initial_period.into(),
|
||||||
|
current_rst_val: 0,
|
||||||
|
clock: Tim::clock(clocks),
|
||||||
|
},
|
||||||
|
reg: unsafe { TimAndPinRegister::new(pin_and_tim.0, pin_and_tim.1) },
|
||||||
|
mode: PhantomData,
|
||||||
|
};
|
||||||
|
sys_cfg
|
||||||
|
.tim_clk_enable()
|
||||||
|
.modify(|r, w| unsafe { w.bits(r.bits() | pin.reg.mask_32()) });
|
||||||
|
pin.enable_pwm_a();
|
||||||
|
pin.set_period(initial_period);
|
||||||
|
pin
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn release(self) -> (Pin, Tim) {
|
||||||
|
self.reg.release()
|
||||||
|
}
|
||||||
|
|
||||||
|
pwm_common_func!();
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Pin: TimPin, Tim: ValidTim> From<PwmPin<Pin, Tim, PwmA>> for PwmPin<Pin, Tim, PwmB>
|
||||||
|
where
|
||||||
|
(Pin, Tim): ValidTimAndPin<Pin, Tim>,
|
||||||
|
{
|
||||||
|
fn from(other: PwmPin<Pin, Tim, PwmA>) -> Self {
|
||||||
|
let mut pwmb = Self {
|
||||||
|
reg: other.reg,
|
||||||
|
pwm_base: other.pwm_base,
|
||||||
|
mode: PhantomData,
|
||||||
|
};
|
||||||
|
pwmb.enable_pwm_b();
|
||||||
|
pwmb
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<PIN: TimPin, TIM: ValidTim> From<PwmPin<PIN, TIM, PwmB>> for PwmPin<PIN, TIM, PwmA>
|
||||||
|
where
|
||||||
|
(PIN, TIM): ValidTimAndPin<PIN, TIM>,
|
||||||
|
{
|
||||||
|
fn from(other: PwmPin<PIN, TIM, PwmB>) -> Self {
|
||||||
|
let mut pwmb = Self {
|
||||||
|
reg: other.reg,
|
||||||
|
pwm_base: other.pwm_base,
|
||||||
|
mode: PhantomData,
|
||||||
|
};
|
||||||
|
pwmb.enable_pwm_a();
|
||||||
|
pwmb
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Pin: TimPin, Tim: ValidTim> PwmPin<Pin, Tim, PwmA>
|
||||||
|
where
|
||||||
|
(Pin, Tim): ValidTimAndPin<Pin, Tim>,
|
||||||
|
{
|
||||||
|
pub fn pwma(
|
||||||
|
tim_and_pin: (Pin, Tim),
|
||||||
|
sys_cfg: &mut pac::Sysconfig,
|
||||||
|
clocks: &Clocks,
|
||||||
|
initial_period: impl Into<Hertz> + Copy,
|
||||||
|
) -> Self {
|
||||||
|
let mut pin: PwmPin<Pin, Tim, PwmA> =
|
||||||
|
Self::new(tim_and_pin, sys_cfg, clocks, initial_period);
|
||||||
|
pin.enable_pwm_a();
|
||||||
|
pin
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Pin: TimPin, Tim: ValidTim> PwmPin<Pin, Tim, PwmB>
|
||||||
|
where
|
||||||
|
(Pin, Tim): ValidTimAndPin<Pin, Tim>,
|
||||||
|
{
|
||||||
|
pub fn pwmb(
|
||||||
|
tim_and_pin: (Pin, Tim),
|
||||||
|
sys_cfg: &mut pac::Sysconfig,
|
||||||
|
clocks: &Clocks,
|
||||||
|
initial_period: impl Into<Hertz> + Copy,
|
||||||
|
) -> Self {
|
||||||
|
let mut pin: PwmPin<Pin, Tim, PwmB> =
|
||||||
|
Self::new(tim_and_pin, sys_cfg, clocks, initial_period);
|
||||||
|
pin.enable_pwm_b();
|
||||||
|
pin
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//==================================================================================================
|
||||||
|
// Reduced PWM pin
|
||||||
|
//==================================================================================================
|
||||||
|
|
||||||
|
/// Reduced version where type information is deleted
|
||||||
|
pub struct ReducedPwmPin<Mode = PwmA> {
|
||||||
|
reg: TimDynRegister,
|
||||||
|
pwm_base: PwmBase,
|
||||||
|
pin_id: DynPinId,
|
||||||
|
mode: PhantomData<Mode>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<PIN: TimPin, TIM: ValidTim> From<PwmPin<PIN, TIM>> for ReducedPwmPin<PwmA> {
|
||||||
|
fn from(pwm_pin: PwmPin<PIN, TIM>) -> Self {
|
||||||
|
ReducedPwmPin {
|
||||||
|
reg: TimDynRegister::from(pwm_pin.reg),
|
||||||
|
pwm_base: pwm_pin.pwm_base,
|
||||||
|
pin_id: PIN::DYN,
|
||||||
|
mode: PhantomData,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<MODE> ReducedPwmPin<MODE> {
|
||||||
|
pwm_common_func!();
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<ReducedPwmPin<PwmA>> for ReducedPwmPin<PwmB> {
|
||||||
|
fn from(other: ReducedPwmPin<PwmA>) -> Self {
|
||||||
|
let mut pwmb = Self {
|
||||||
|
reg: other.reg,
|
||||||
|
pwm_base: other.pwm_base,
|
||||||
|
pin_id: other.pin_id,
|
||||||
|
mode: PhantomData,
|
||||||
|
};
|
||||||
|
pwmb.enable_pwm_b();
|
||||||
|
pwmb
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<ReducedPwmPin<PwmB>> for ReducedPwmPin<PwmA> {
|
||||||
|
fn from(other: ReducedPwmPin<PwmB>) -> Self {
|
||||||
|
let mut pwmb = Self {
|
||||||
|
reg: other.reg,
|
||||||
|
pwm_base: other.pwm_base,
|
||||||
|
pin_id: other.pin_id,
|
||||||
|
mode: PhantomData,
|
||||||
|
};
|
||||||
|
pwmb.enable_pwm_a();
|
||||||
|
pwmb
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//==================================================================================================
|
||||||
|
// PWMB implementations
|
||||||
|
//==================================================================================================
|
||||||
|
|
||||||
|
impl<PIN: TimPin, TIM: ValidTim> PwmPin<PIN, TIM, PwmB>
|
||||||
|
where
|
||||||
|
(PIN, TIM): ValidTimAndPin<PIN, TIM>,
|
||||||
|
{
|
||||||
|
pwmb_func!();
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ReducedPwmPin<PwmB> {
|
||||||
|
pwmb_func!();
|
||||||
|
}
|
||||||
|
|
||||||
|
//==================================================================================================
|
||||||
|
// Embedded HAL implementation: PWMA only
|
||||||
|
//==================================================================================================
|
||||||
|
|
||||||
|
impl<Pin: TimPin, Tim: ValidTim> embedded_hal::pwm::ErrorType for PwmPin<Pin, Tim> {
|
||||||
|
type Error = Infallible;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl embedded_hal::pwm::ErrorType for ReducedPwmPin {
|
||||||
|
type Error = Infallible;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl embedded_hal::pwm::SetDutyCycle for ReducedPwmPin {
|
||||||
|
#[inline]
|
||||||
|
fn max_duty_cycle(&self) -> u16 {
|
||||||
|
DUTY_MAX
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn set_duty_cycle(&mut self, duty: u16) -> Result<(), Self::Error> {
|
||||||
|
self.pwm_base.current_duty = duty;
|
||||||
|
let pwma_val: u64 = (self.pwm_base.current_rst_val as u64
|
||||||
|
* (DUTY_MAX as u64 - self.pwm_base.current_duty as u64))
|
||||||
|
/ DUTY_MAX as u64;
|
||||||
|
self.reg
|
||||||
|
.reg()
|
||||||
|
.pwma_value()
|
||||||
|
.write(|w| unsafe { w.bits(pwma_val as u32) });
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Pin: TimPin, Tim: ValidTim> embedded_hal::pwm::SetDutyCycle for PwmPin<Pin, Tim> {
|
||||||
|
#[inline]
|
||||||
|
fn max_duty_cycle(&self) -> u16 {
|
||||||
|
DUTY_MAX
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn set_duty_cycle(&mut self, duty: u16) -> Result<(), Self::Error> {
|
||||||
|
self.pwm_base.current_duty = duty;
|
||||||
|
let pwma_val: u64 = (self.pwm_base.current_rst_val as u64
|
||||||
|
* (DUTY_MAX as u64 - self.pwm_base.current_duty as u64))
|
||||||
|
/ DUTY_MAX as u64;
|
||||||
|
self.reg
|
||||||
|
.reg()
|
||||||
|
.pwma_value()
|
||||||
|
.write(|w| unsafe { w.bits(pwma_val as u32) });
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the corresponding u16 duty cycle from a percent value ranging between 0.0 and 1.0.
|
||||||
|
///
|
||||||
|
/// Please note that this might load a lot of floating point code because this processor does not
|
||||||
|
/// have a FPU
|
||||||
|
pub fn get_duty_from_percent(percent: f32) -> u16 {
|
||||||
|
if percent > 1.0 {
|
||||||
|
DUTY_MAX
|
||||||
|
} else if percent <= 0.0 {
|
||||||
|
0
|
||||||
|
} else {
|
||||||
|
(percent * DUTY_MAX as f32) as u16
|
||||||
|
}
|
||||||
|
}
|
@ -1,3 +1,8 @@
|
|||||||
|
//! API for the SPI peripheral
|
||||||
|
//!
|
||||||
|
//! ## Examples
|
||||||
|
//!
|
||||||
|
//! - [Blocking SPI example](https://egit.irs.uni-stuttgart.de/rust/va416xx-rs/src/branch/main/examples/simple/examples/spi.rs)
|
||||||
use core::{convert::Infallible, marker::PhantomData, ops::Deref};
|
use core::{convert::Infallible, marker::PhantomData, ops::Deref};
|
||||||
|
|
||||||
use embedded_hal::spi::Mode;
|
use embedded_hal::spi::Mode;
|
||||||
@ -351,12 +356,14 @@ pub type SpiRegBlock = pac::spi0::RegisterBlock;
|
|||||||
/// format is the same for all SPI blocks.
|
/// format is the same for all SPI blocks.
|
||||||
pub trait Instance: Deref<Target = SpiRegBlock> {
|
pub trait Instance: Deref<Target = SpiRegBlock> {
|
||||||
const IDX: u8;
|
const IDX: u8;
|
||||||
|
const PERIPH_SEL: PeripheralSelect;
|
||||||
|
|
||||||
fn ptr() -> *const SpiRegBlock;
|
fn ptr() -> *const SpiRegBlock;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Instance for pac::Spi0 {
|
impl Instance for pac::Spi0 {
|
||||||
const IDX: u8 = 0;
|
const IDX: u8 = 0;
|
||||||
|
const PERIPH_SEL: PeripheralSelect = PeripheralSelect::Spi0;
|
||||||
|
|
||||||
fn ptr() -> *const SpiRegBlock {
|
fn ptr() -> *const SpiRegBlock {
|
||||||
Self::ptr()
|
Self::ptr()
|
||||||
@ -365,6 +372,7 @@ impl Instance for pac::Spi0 {
|
|||||||
|
|
||||||
impl Instance for pac::Spi1 {
|
impl Instance for pac::Spi1 {
|
||||||
const IDX: u8 = 1;
|
const IDX: u8 = 1;
|
||||||
|
const PERIPH_SEL: PeripheralSelect = PeripheralSelect::Spi1;
|
||||||
|
|
||||||
fn ptr() -> *const SpiRegBlock {
|
fn ptr() -> *const SpiRegBlock {
|
||||||
Self::ptr()
|
Self::ptr()
|
||||||
@ -373,6 +381,7 @@ impl Instance for pac::Spi1 {
|
|||||||
|
|
||||||
impl Instance for pac::Spi2 {
|
impl Instance for pac::Spi2 {
|
||||||
const IDX: u8 = 2;
|
const IDX: u8 = 2;
|
||||||
|
const PERIPH_SEL: PeripheralSelect = PeripheralSelect::Spi2;
|
||||||
|
|
||||||
fn ptr() -> *const SpiRegBlock {
|
fn ptr() -> *const SpiRegBlock {
|
||||||
Self::ptr()
|
Self::ptr()
|
||||||
@ -562,6 +571,7 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
macro_rules! spi_ctor {
|
macro_rules! spi_ctor {
|
||||||
($spiI:ident, $PeriphSel: path) => {
|
($spiI:ident, $PeriphSel: path) => {
|
||||||
/// Create a new SPI struct
|
/// Create a new SPI struct
|
||||||
@ -652,6 +662,7 @@ macro_rules! spi_ctor {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
impl<
|
impl<
|
||||||
SpiI: Instance,
|
SpiI: Instance,
|
||||||
@ -663,10 +674,92 @@ impl<
|
|||||||
where
|
where
|
||||||
<Word as TryFrom<u32>>::Error: core::fmt::Debug,
|
<Word as TryFrom<u32>>::Error: core::fmt::Debug,
|
||||||
{
|
{
|
||||||
spi_ctor!(spi0, PeripheralSelect::Spi0);
|
/// Create a new SPI struct
|
||||||
spi_ctor!(spi1, PeripheralSelect::Spi1);
|
///
|
||||||
spi_ctor!(spi2, PeripheralSelect::Spi2);
|
/// You can delete the pin type information by calling the
|
||||||
spi_ctor!(spi3, PeripheralSelect::Spi3);
|
/// [`downgrade`](Self::downgrade) function
|
||||||
|
///
|
||||||
|
/// ## Arguments
|
||||||
|
/// * `spi` - SPI bus to use
|
||||||
|
/// * `pins` - Pins to be used for SPI transactions. These pins are consumed
|
||||||
|
/// to ensure the pins can not be used for other purposes anymore
|
||||||
|
/// * `spi_cfg` - Configuration specific to the SPI bus
|
||||||
|
/// * `transfer_cfg` - Optional initial transfer configuration which includes
|
||||||
|
/// configuration which can change across individual SPI transfers like SPI mode
|
||||||
|
/// or SPI clock. If only one device is connected, this configuration only needs
|
||||||
|
/// to be done once.
|
||||||
|
/// * `syscfg` - Can be passed optionally to enable the peripheral clock
|
||||||
|
pub fn new(
|
||||||
|
spi: SpiI,
|
||||||
|
pins: (Sck, Miso, Mosi),
|
||||||
|
clocks: &crate::clock::Clocks,
|
||||||
|
spi_cfg: SpiConfig,
|
||||||
|
syscfg: &mut pac::Sysconfig,
|
||||||
|
transfer_cfg: Option<&ErasedTransferConfig>,
|
||||||
|
) -> Self {
|
||||||
|
crate::clock::enable_peripheral_clock(syscfg, SpiI::PERIPH_SEL);
|
||||||
|
let SpiConfig {
|
||||||
|
ser_clock_rate_div,
|
||||||
|
ms,
|
||||||
|
slave_output_disable,
|
||||||
|
loopback_mode,
|
||||||
|
master_delayer_capture,
|
||||||
|
} = spi_cfg;
|
||||||
|
let mut mode = embedded_hal::spi::MODE_0;
|
||||||
|
let mut clk_prescale = 0x02;
|
||||||
|
let mut ss = 0;
|
||||||
|
let mut init_blockmode = false;
|
||||||
|
let apb1_clk = clocks.apb1();
|
||||||
|
if let Some(transfer_cfg) = transfer_cfg {
|
||||||
|
mode = transfer_cfg.mode;
|
||||||
|
clk_prescale =
|
||||||
|
apb1_clk.raw() / (transfer_cfg.spi_clk.raw() * (ser_clock_rate_div as u32 + 1));
|
||||||
|
if transfer_cfg.hw_cs != HwChipSelectId::Invalid {
|
||||||
|
ss = transfer_cfg.hw_cs as u8;
|
||||||
|
}
|
||||||
|
init_blockmode = transfer_cfg.blockmode;
|
||||||
|
}
|
||||||
|
|
||||||
|
let (cpo_bit, cph_bit) = mode_to_cpo_cph_bit(mode);
|
||||||
|
spi.ctrl0().write(|w| {
|
||||||
|
unsafe {
|
||||||
|
w.size().bits(Word::word_reg());
|
||||||
|
w.scrdv().bits(ser_clock_rate_div);
|
||||||
|
// Clear clock phase and polarity. Will be set to correct value for each
|
||||||
|
// transfer
|
||||||
|
w.spo().bit(cpo_bit);
|
||||||
|
w.sph().bit(cph_bit)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
spi.ctrl1().write(|w| {
|
||||||
|
w.lbm().bit(loopback_mode);
|
||||||
|
w.sod().bit(slave_output_disable);
|
||||||
|
w.ms().bit(ms);
|
||||||
|
w.mdlycap().bit(master_delayer_capture);
|
||||||
|
w.blockmode().bit(init_blockmode);
|
||||||
|
unsafe { w.ss().bits(ss) }
|
||||||
|
});
|
||||||
|
|
||||||
|
spi.fifo_clr().write(|w| {
|
||||||
|
w.rxfifo().set_bit();
|
||||||
|
w.txfifo().set_bit()
|
||||||
|
});
|
||||||
|
spi.clkprescale().write(|w| unsafe { w.bits(clk_prescale) });
|
||||||
|
// Enable the peripheral as the last step as recommended in the
|
||||||
|
// programmers guide
|
||||||
|
spi.ctrl1().modify(|_, w| w.enable().set_bit());
|
||||||
|
Spi {
|
||||||
|
inner: SpiBase {
|
||||||
|
spi,
|
||||||
|
cfg: spi_cfg,
|
||||||
|
apb1_clk,
|
||||||
|
fill_word: Default::default(),
|
||||||
|
blockmode: init_blockmode,
|
||||||
|
word: PhantomData,
|
||||||
|
},
|
||||||
|
pins,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn cfg_clock(&mut self, spi_clk: impl Into<Hertz>) {
|
pub fn cfg_clock(&mut self, spi_clk: impl Into<Hertz>) {
|
||||||
|
802
va416xx-hal/src/timer.rs
Normal file
802
va416xx-hal/src/timer.rs
Normal file
@ -0,0 +1,802 @@
|
|||||||
|
//! API for the TIM peripherals
|
||||||
|
//!
|
||||||
|
//! ## Examples
|
||||||
|
//!
|
||||||
|
//! TODO.
|
||||||
|
use core::cell::Cell;
|
||||||
|
|
||||||
|
use cortex_m::interrupt::Mutex;
|
||||||
|
|
||||||
|
use crate::clock::Clocks;
|
||||||
|
use crate::gpio::{
|
||||||
|
AltFunc1, AltFunc2, AltFunc3, DynPinId, Pin, PinId, PA0, PA1, PA10, PA11, PA12, PA13, PA14,
|
||||||
|
PA15, PA2, PA3, PA4, PA5, PA6, PA7, PB0, PB1, PB10, PB11, PB12, PB13, PB14, PB15, PB2, PB3,
|
||||||
|
PB4, PB5, PB6, PB7, PB8, PB9, PC0, PC1, PD0, PD1, PD10, PD11, PD12, PD13, PD14, PD15, PD2, PD3,
|
||||||
|
PD4, PD5, PD6, PD7, PD8, PD9, PE0, PE1, PE10, PE11, PE12, PE13, PE14, PE15, PE2, PE3, PE4, PE5,
|
||||||
|
PE6, PE7, PE8, PE9, PF0, PF1, PF10, PF11, PF12, PF13, PF14, PF15, PF2, PF3, PF4, PF5, PF6, PF7,
|
||||||
|
PF8, PF9, PG0, PG1, PG2, PG3, PG6,
|
||||||
|
};
|
||||||
|
use crate::time::Hertz;
|
||||||
|
use crate::typelevel::Sealed;
|
||||||
|
use crate::{disable_interrupt, prelude::*};
|
||||||
|
use crate::{enable_interrupt, pac};
|
||||||
|
|
||||||
|
pub static MS_COUNTER: Mutex<Cell<u32>> = Mutex::new(Cell::new(0));
|
||||||
|
|
||||||
|
//==================================================================================================
|
||||||
|
// Defintions
|
||||||
|
//==================================================================================================
|
||||||
|
|
||||||
|
/// Interrupt events
|
||||||
|
//pub enum Event {
|
||||||
|
/// Timer timed out / count down ended
|
||||||
|
//TimeOut,
|
||||||
|
//}
|
||||||
|
|
||||||
|
#[derive(Default, Debug, PartialEq, Eq, Copy, Clone)]
|
||||||
|
pub struct CascadeCtrl {
|
||||||
|
/// Enable Cascade 0 signal active as a requirement for counting
|
||||||
|
pub enb_start_src_csd0: bool,
|
||||||
|
/// Invert Cascade 0, making it active low
|
||||||
|
pub inv_csd0: bool,
|
||||||
|
/// Enable Cascade 1 signal active as a requirement for counting
|
||||||
|
pub enb_start_src_csd1: bool,
|
||||||
|
/// Invert Cascade 1, making it active low
|
||||||
|
pub inv_csd1: bool,
|
||||||
|
/// Specify required operation if both Cascade 0 and Cascade 1 are active.
|
||||||
|
/// 0 is a logical AND of both cascade signals, 1 is a logical OR
|
||||||
|
pub dual_csd_op: bool,
|
||||||
|
/// Enable trigger mode for Cascade 0. In trigger mode, couting will start with the selected
|
||||||
|
/// cascade signal active, but once the counter is active, cascade control will be ignored
|
||||||
|
pub trg_csd0: bool,
|
||||||
|
/// Trigger mode, identical to [`trg_csd0`](CascadeCtrl) but for Cascade 1
|
||||||
|
pub trg_csd1: bool,
|
||||||
|
/// Enable Cascade 2 signal active as a requirement to stop counting. This mode is similar
|
||||||
|
/// to the REQ_STOP control bit, but signalled by a Cascade source
|
||||||
|
pub enb_stop_src_csd2: bool,
|
||||||
|
/// Invert Cascade 2, making it active low
|
||||||
|
pub inv_csd2: bool,
|
||||||
|
/// The counter is automatically disabled if the corresponding Cascade 2 level-sensitive input
|
||||||
|
/// souce is active when the count reaches 0. If the counter is not 0, the cascade control is
|
||||||
|
/// ignored
|
||||||
|
pub trg_csd2: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
|
pub enum CascadeSel {
|
||||||
|
Sel0 = 0,
|
||||||
|
Sel1 = 1,
|
||||||
|
Sel2 = 2,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
|
pub struct InvalidCascadeSourceId;
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
|
pub enum CascadeSource {
|
||||||
|
PortA(u8),
|
||||||
|
PortB(u8),
|
||||||
|
PortC(u8),
|
||||||
|
PortD(u8),
|
||||||
|
PortE(u8),
|
||||||
|
Tim(u8),
|
||||||
|
TxEv,
|
||||||
|
AdcIrq,
|
||||||
|
RomSbe,
|
||||||
|
RomMbe,
|
||||||
|
Ram0Sbe,
|
||||||
|
Ram0Mbe,
|
||||||
|
Ram1Sbe,
|
||||||
|
Ram2Mbe,
|
||||||
|
WdogIrq,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CascadeSource {
|
||||||
|
fn id(&self) -> Result<u8, InvalidCascadeSourceId> {
|
||||||
|
let port_check = |base: u8, id: u8| {
|
||||||
|
if id > 15 {
|
||||||
|
return Err(InvalidCascadeSourceId);
|
||||||
|
}
|
||||||
|
Ok(base + id)
|
||||||
|
};
|
||||||
|
match self {
|
||||||
|
CascadeSource::PortA(id) => port_check(0, *id),
|
||||||
|
CascadeSource::PortB(id) => port_check(16, *id),
|
||||||
|
CascadeSource::PortC(id) => port_check(32, *id),
|
||||||
|
CascadeSource::PortD(id) => port_check(48, *id),
|
||||||
|
CascadeSource::PortE(id) => port_check(65, *id),
|
||||||
|
CascadeSource::Tim(id) => {
|
||||||
|
if *id > 23 {
|
||||||
|
return Err(InvalidCascadeSourceId);
|
||||||
|
}
|
||||||
|
Ok(80 + id)
|
||||||
|
}
|
||||||
|
CascadeSource::TxEv => Ok(104),
|
||||||
|
CascadeSource::AdcIrq => Ok(105),
|
||||||
|
CascadeSource::RomSbe => Ok(106),
|
||||||
|
CascadeSource::RomMbe => Ok(106),
|
||||||
|
CascadeSource::Ram0Sbe => Ok(108),
|
||||||
|
CascadeSource::Ram0Mbe => Ok(109),
|
||||||
|
CascadeSource::Ram1Sbe => Ok(110),
|
||||||
|
CascadeSource::Ram2Mbe => Ok(111),
|
||||||
|
CascadeSource::WdogIrq => Ok(112),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//==================================================================================================
|
||||||
|
// Valid TIM and PIN combinations
|
||||||
|
//==================================================================================================
|
||||||
|
|
||||||
|
pub trait TimPin {
|
||||||
|
const DYN: DynPinId;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait ValidTim {
|
||||||
|
// TIM ID ranging from 0 to 23 for 24 TIM peripherals
|
||||||
|
const TIM_ID: u8;
|
||||||
|
const IRQ: pac::Interrupt;
|
||||||
|
|
||||||
|
fn clock(clocks: &Clocks) -> Hertz {
|
||||||
|
if Self::TIM_ID <= 15 {
|
||||||
|
clocks.apb1()
|
||||||
|
} else {
|
||||||
|
clocks.apb2()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! tim_markers {
|
||||||
|
(
|
||||||
|
$(
|
||||||
|
($TimX:path, $id:expr, $Irq:path),
|
||||||
|
)+
|
||||||
|
) => {
|
||||||
|
$(
|
||||||
|
impl ValidTim for $TimX {
|
||||||
|
const TIM_ID: u8 = $id;
|
||||||
|
const IRQ: pac::Interrupt = $Irq;
|
||||||
|
}
|
||||||
|
)+
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
tim_markers!(
|
||||||
|
(pac::Tim0, 0, pac::Interrupt::TIM0),
|
||||||
|
(pac::Tim1, 1, pac::Interrupt::TIM1),
|
||||||
|
(pac::Tim2, 2, pac::Interrupt::TIM2),
|
||||||
|
(pac::Tim3, 3, pac::Interrupt::TIM3),
|
||||||
|
(pac::Tim4, 4, pac::Interrupt::TIM4),
|
||||||
|
(pac::Tim5, 5, pac::Interrupt::TIM5),
|
||||||
|
(pac::Tim6, 6, pac::Interrupt::TIM6),
|
||||||
|
(pac::Tim7, 7, pac::Interrupt::TIM7),
|
||||||
|
(pac::Tim8, 8, pac::Interrupt::TIM8),
|
||||||
|
(pac::Tim9, 9, pac::Interrupt::TIM9),
|
||||||
|
(pac::Tim10, 10, pac::Interrupt::TIM10),
|
||||||
|
(pac::Tim11, 11, pac::Interrupt::TIM11),
|
||||||
|
(pac::Tim12, 12, pac::Interrupt::TIM12),
|
||||||
|
(pac::Tim13, 13, pac::Interrupt::TIM13),
|
||||||
|
(pac::Tim14, 14, pac::Interrupt::TIM14),
|
||||||
|
(pac::Tim15, 15, pac::Interrupt::TIM15),
|
||||||
|
(pac::Tim16, 16, pac::Interrupt::TIM16),
|
||||||
|
(pac::Tim17, 17, pac::Interrupt::TIM17),
|
||||||
|
(pac::Tim18, 18, pac::Interrupt::TIM18),
|
||||||
|
(pac::Tim19, 19, pac::Interrupt::TIM19),
|
||||||
|
(pac::Tim20, 20, pac::Interrupt::TIM20),
|
||||||
|
(pac::Tim21, 21, pac::Interrupt::TIM21),
|
||||||
|
(pac::Tim22, 22, pac::Interrupt::TIM22),
|
||||||
|
(pac::Tim23, 23, pac::Interrupt::TIM23),
|
||||||
|
);
|
||||||
|
|
||||||
|
pub trait ValidTimAndPin<Pin: TimPin, Tim: ValidTim>: Sealed {}
|
||||||
|
|
||||||
|
macro_rules! valid_pin_and_tims {
|
||||||
|
(
|
||||||
|
$(
|
||||||
|
($PinX:ident, $AltFunc:ident, $TimX:path),
|
||||||
|
)+
|
||||||
|
) => {
|
||||||
|
$(
|
||||||
|
impl TimPin for Pin<$PinX, $AltFunc>
|
||||||
|
where
|
||||||
|
$PinX: PinId,
|
||||||
|
{
|
||||||
|
const DYN: DynPinId = $PinX::DYN;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<
|
||||||
|
PinInstance: TimPin,
|
||||||
|
Tim: ValidTim
|
||||||
|
> ValidTimAndPin<PinInstance, Tim> for (Pin<$PinX, $AltFunc>, $TimX)
|
||||||
|
where
|
||||||
|
Pin<$PinX, $AltFunc>: TimPin,
|
||||||
|
$PinX: PinId,
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Sealed for (Pin<$PinX, $AltFunc>, $TimX) {}
|
||||||
|
)+
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
valid_pin_and_tims!(
|
||||||
|
(PA0, AltFunc1, pac::Tim0),
|
||||||
|
(PA1, AltFunc1, pac::Tim1),
|
||||||
|
(PA2, AltFunc1, pac::Tim2),
|
||||||
|
(PA3, AltFunc1, pac::Tim3),
|
||||||
|
(PA4, AltFunc1, pac::Tim4),
|
||||||
|
(PA5, AltFunc1, pac::Tim5),
|
||||||
|
(PA6, AltFunc1, pac::Tim6),
|
||||||
|
(PA7, AltFunc1, pac::Tim7),
|
||||||
|
(PA10, AltFunc2, pac::Tim23),
|
||||||
|
(PA11, AltFunc2, pac::Tim22),
|
||||||
|
(PA12, AltFunc2, pac::Tim21),
|
||||||
|
(PA13, AltFunc2, pac::Tim20),
|
||||||
|
(PA14, AltFunc2, pac::Tim19),
|
||||||
|
(PA15, AltFunc2, pac::Tim18),
|
||||||
|
(PB0, AltFunc2, pac::Tim17),
|
||||||
|
(PB1, AltFunc2, pac::Tim16),
|
||||||
|
(PB2, AltFunc2, pac::Tim15),
|
||||||
|
(PB3, AltFunc2, pac::Tim14),
|
||||||
|
(PB4, AltFunc2, pac::Tim13),
|
||||||
|
(PB5, AltFunc2, pac::Tim12),
|
||||||
|
(PB6, AltFunc2, pac::Tim11),
|
||||||
|
(PB7, AltFunc2, pac::Tim10),
|
||||||
|
(PB8, AltFunc2, pac::Tim9),
|
||||||
|
(PB9, AltFunc2, pac::Tim8),
|
||||||
|
(PB10, AltFunc2, pac::Tim7),
|
||||||
|
(PB11, AltFunc2, pac::Tim6),
|
||||||
|
(PB12, AltFunc2, pac::Tim5),
|
||||||
|
(PB13, AltFunc2, pac::Tim4),
|
||||||
|
(PB14, AltFunc2, pac::Tim3),
|
||||||
|
(PB15, AltFunc2, pac::Tim2),
|
||||||
|
(PC0, AltFunc2, pac::Tim1),
|
||||||
|
(PC1, AltFunc2, pac::Tim0),
|
||||||
|
(PD0, AltFunc2, pac::Tim0),
|
||||||
|
(PD1, AltFunc2, pac::Tim1),
|
||||||
|
(PD2, AltFunc2, pac::Tim2),
|
||||||
|
(PD3, AltFunc2, pac::Tim3),
|
||||||
|
(PD4, AltFunc2, pac::Tim4),
|
||||||
|
(PD5, AltFunc2, pac::Tim5),
|
||||||
|
(PD6, AltFunc2, pac::Tim6),
|
||||||
|
(PD7, AltFunc2, pac::Tim7),
|
||||||
|
(PD8, AltFunc2, pac::Tim8),
|
||||||
|
(PD9, AltFunc2, pac::Tim9),
|
||||||
|
(PD10, AltFunc2, pac::Tim10),
|
||||||
|
(PD11, AltFunc2, pac::Tim11),
|
||||||
|
(PD12, AltFunc2, pac::Tim12),
|
||||||
|
(PD13, AltFunc2, pac::Tim13),
|
||||||
|
(PD14, AltFunc2, pac::Tim14),
|
||||||
|
(PD15, AltFunc2, pac::Tim15),
|
||||||
|
(PE0, AltFunc2, pac::Tim16),
|
||||||
|
(PE1, AltFunc2, pac::Tim17),
|
||||||
|
(PE2, AltFunc2, pac::Tim18),
|
||||||
|
(PE3, AltFunc2, pac::Tim19),
|
||||||
|
(PE4, AltFunc2, pac::Tim20),
|
||||||
|
(PE5, AltFunc2, pac::Tim21),
|
||||||
|
(PE6, AltFunc2, pac::Tim22),
|
||||||
|
(PE7, AltFunc2, pac::Tim23),
|
||||||
|
(PE8, AltFunc3, pac::Tim16),
|
||||||
|
(PE9, AltFunc3, pac::Tim17),
|
||||||
|
(PE10, AltFunc3, pac::Tim18),
|
||||||
|
(PE11, AltFunc3, pac::Tim19),
|
||||||
|
(PE12, AltFunc3, pac::Tim20),
|
||||||
|
(PE13, AltFunc3, pac::Tim21),
|
||||||
|
(PE14, AltFunc3, pac::Tim22),
|
||||||
|
(PE15, AltFunc3, pac::Tim23),
|
||||||
|
(PF0, AltFunc3, pac::Tim0),
|
||||||
|
(PF1, AltFunc3, pac::Tim1),
|
||||||
|
(PF2, AltFunc3, pac::Tim2),
|
||||||
|
(PF3, AltFunc3, pac::Tim3),
|
||||||
|
(PF4, AltFunc3, pac::Tim4),
|
||||||
|
(PF5, AltFunc3, pac::Tim5),
|
||||||
|
(PF6, AltFunc3, pac::Tim6),
|
||||||
|
(PF7, AltFunc3, pac::Tim7),
|
||||||
|
(PF8, AltFunc3, pac::Tim8),
|
||||||
|
(PF9, AltFunc3, pac::Tim9),
|
||||||
|
(PF10, AltFunc3, pac::Tim10),
|
||||||
|
(PF11, AltFunc3, pac::Tim11),
|
||||||
|
(PF12, AltFunc3, pac::Tim12),
|
||||||
|
(PF13, AltFunc2, pac::Tim19),
|
||||||
|
(PF14, AltFunc2, pac::Tim20),
|
||||||
|
(PF15, AltFunc2, pac::Tim21),
|
||||||
|
(PG0, AltFunc2, pac::Tim22),
|
||||||
|
(PG1, AltFunc2, pac::Tim23),
|
||||||
|
(PG2, AltFunc1, pac::Tim9),
|
||||||
|
(PG3, AltFunc1, pac::Tim10),
|
||||||
|
(PG6, AltFunc1, pac::Tim12),
|
||||||
|
);
|
||||||
|
|
||||||
|
//==================================================================================================
|
||||||
|
// Register Interface for TIM registers and TIM pins
|
||||||
|
//==================================================================================================
|
||||||
|
|
||||||
|
/// Clear the reset bit of the TIM, holding it in reset
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// Only the bit related to the corresponding TIM peripheral is modified
|
||||||
|
#[inline]
|
||||||
|
fn assert_tim_reset(syscfg: &mut pac::Sysconfig, tim_id: u8) {
|
||||||
|
syscfg
|
||||||
|
.tim_reset()
|
||||||
|
.modify(|r, w| unsafe { w.bits(r.bits() & !(1 << tim_id as u32)) })
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn deassert_tim_reset(syscfg: &mut pac::Sysconfig, tim_id: u8) {
|
||||||
|
syscfg
|
||||||
|
.tim_reset()
|
||||||
|
.modify(|r, w| unsafe { w.bits(r.bits() | (1 << tim_id as u32)) })
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type TimRegBlock = pac::tim0::RegisterBlock;
|
||||||
|
|
||||||
|
/// Register interface.
|
||||||
|
///
|
||||||
|
/// This interface provides valid TIM pins a way to access their corresponding TIM
|
||||||
|
/// registers
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// Users should only implement the [`tim_id`] function. No default function
|
||||||
|
/// implementations should be overridden. The implementing type must also have
|
||||||
|
/// "control" over the corresponding pin ID, i.e. it must guarantee that a each
|
||||||
|
/// pin ID is a singleton.
|
||||||
|
pub(super) unsafe trait TimRegInterface {
|
||||||
|
fn tim_id(&self) -> u8;
|
||||||
|
|
||||||
|
const PORT_BASE: *const pac::tim0::RegisterBlock = pac::Tim0::ptr() as *const _;
|
||||||
|
|
||||||
|
/// All 24 TIM blocks are identical. This helper functions returns the correct
|
||||||
|
/// memory mapped peripheral depending on the TIM ID.
|
||||||
|
#[inline(always)]
|
||||||
|
fn reg(&self) -> &TimRegBlock {
|
||||||
|
unsafe { &*Self::PORT_BASE.offset(self.tim_id() as isize) }
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn mask_32(&self) -> u32 {
|
||||||
|
1 << self.tim_id()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Clear the reset bit of the TIM, holding it in reset
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// Only the bit related to the corresponding TIM peripheral is modified
|
||||||
|
#[inline]
|
||||||
|
#[allow(dead_code)]
|
||||||
|
fn assert_tim_reset(&self, syscfg: &mut pac::Sysconfig) {
|
||||||
|
assert_tim_reset(syscfg, self.tim_id());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
#[allow(dead_code)]
|
||||||
|
fn deassert_time_reset(&self, syscfg: &mut pac::Sysconfig) {
|
||||||
|
deassert_tim_reset(syscfg, self.tim_id());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Provide a safe register interface for [`ValidTimAndPin`]s
|
||||||
|
///
|
||||||
|
/// This `struct` takes ownership of a [`ValidTimAndPin`] and provides an API to
|
||||||
|
/// access the corresponding registers.
|
||||||
|
pub(super) struct TimAndPinRegister<Pin: TimPin, Tim: ValidTim> {
|
||||||
|
pin: Pin,
|
||||||
|
tim: Tim,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) struct TimRegister<TIM: ValidTim> {
|
||||||
|
tim: TIM,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<TIM: ValidTim> TimRegister<TIM> {
|
||||||
|
#[inline]
|
||||||
|
pub(super) unsafe fn new(tim: TIM) -> Self {
|
||||||
|
TimRegister { tim }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn release(self) -> TIM {
|
||||||
|
self.tim
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe impl<Tim: ValidTim> TimRegInterface for TimRegister<Tim> {
|
||||||
|
#[inline(always)]
|
||||||
|
fn tim_id(&self) -> u8 {
|
||||||
|
Tim::TIM_ID
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Pin: TimPin, Tim: ValidTim> TimAndPinRegister<Pin, Tim>
|
||||||
|
where
|
||||||
|
(Pin, Tim): ValidTimAndPin<Pin, Tim>,
|
||||||
|
{
|
||||||
|
#[inline]
|
||||||
|
pub(super) unsafe fn new(pin: Pin, tim: Tim) -> Self {
|
||||||
|
TimAndPinRegister { pin, tim }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn release(self) -> (Pin, Tim) {
|
||||||
|
(self.pin, self.tim)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe impl<Pin: TimPin, Tim: ValidTim> TimRegInterface for TimAndPinRegister<Pin, Tim> {
|
||||||
|
#[inline(always)]
|
||||||
|
fn tim_id(&self) -> u8 {
|
||||||
|
Tim::TIM_ID
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) struct TimDynRegister {
|
||||||
|
tim_id: u8,
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pin_id: DynPinId,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Pin: TimPin, Tim: ValidTim> From<TimAndPinRegister<Pin, Tim>> for TimDynRegister {
|
||||||
|
fn from(_reg: TimAndPinRegister<Pin, Tim>) -> Self {
|
||||||
|
Self {
|
||||||
|
tim_id: Tim::TIM_ID,
|
||||||
|
pin_id: Pin::DYN,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe impl TimRegInterface for TimDynRegister {
|
||||||
|
#[inline(always)]
|
||||||
|
fn tim_id(&self) -> u8 {
|
||||||
|
self.tim_id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//==================================================================================================
|
||||||
|
// Timers
|
||||||
|
//==================================================================================================
|
||||||
|
|
||||||
|
/// Hardware timers
|
||||||
|
pub struct CountdownTimer<TIM: ValidTim> {
|
||||||
|
tim: TimRegister<TIM>,
|
||||||
|
curr_freq: Hertz,
|
||||||
|
clock: Hertz,
|
||||||
|
rst_val: u32,
|
||||||
|
last_cnt: u32,
|
||||||
|
listening: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn enable_tim_clk(syscfg: &mut pac::Sysconfig, idx: u8) {
|
||||||
|
syscfg
|
||||||
|
.tim_clk_enable()
|
||||||
|
.modify(|r, w| unsafe { w.bits(r.bits() | (1 << idx)) });
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe impl<TIM: ValidTim> TimRegInterface for CountdownTimer<TIM> {
|
||||||
|
#[inline]
|
||||||
|
fn tim_id(&self) -> u8 {
|
||||||
|
TIM::TIM_ID
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Tim: ValidTim> CountdownTimer<Tim> {
|
||||||
|
/// Create a new countdown timer, but does not start it.
|
||||||
|
///
|
||||||
|
/// You can use [Self::start] to start the countdown timer, and you may optionally call
|
||||||
|
/// [Self::listen] to enable interrupts for the TIM peripheral as well.
|
||||||
|
pub fn new(syscfg: &mut pac::Sysconfig, tim: Tim, clocks: &Clocks) -> Self {
|
||||||
|
enable_tim_clk(syscfg, Tim::TIM_ID);
|
||||||
|
assert_tim_reset(syscfg, Tim::TIM_ID);
|
||||||
|
cortex_m::asm::nop();
|
||||||
|
cortex_m::asm::nop();
|
||||||
|
deassert_tim_reset(syscfg, Tim::TIM_ID);
|
||||||
|
|
||||||
|
CountdownTimer {
|
||||||
|
tim: unsafe { TimRegister::new(tim) },
|
||||||
|
clock: Tim::clock(clocks),
|
||||||
|
rst_val: 0,
|
||||||
|
curr_freq: 0_u32.Hz(),
|
||||||
|
listening: false,
|
||||||
|
last_cnt: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn start(&mut self, timeout: impl Into<Hertz>) {
|
||||||
|
self.load(timeout);
|
||||||
|
self.enable();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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) {
|
||||||
|
self.listening = true;
|
||||||
|
self.enable_interrupt();
|
||||||
|
unsafe { enable_interrupt(Tim::IRQ) }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return `Ok` if the timer has wrapped. Peripheral will automatically clear the
|
||||||
|
/// flag and restart the time if configured correctly
|
||||||
|
pub fn wait(&mut self) -> nb::Result<(), void::Void> {
|
||||||
|
let cnt = self.tim.reg().cnt_value().read().bits();
|
||||||
|
if (cnt > self.last_cnt) || cnt == 0 {
|
||||||
|
self.last_cnt = self.rst_val;
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
self.last_cnt = cnt;
|
||||||
|
Err(nb::Error::WouldBlock)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn stop(&mut self) {
|
||||||
|
self.tim.reg().ctrl().write(|w| w.enable().clear_bit());
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn unlisten(&mut self) {
|
||||||
|
self.listening = true;
|
||||||
|
self.disable_interrupt();
|
||||||
|
disable_interrupt(Tim::IRQ);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn enable_interrupt(&mut self) {
|
||||||
|
self.tim.reg().ctrl().modify(|_, w| w.irq_enb().set_bit());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn disable_interrupt(&mut self) {
|
||||||
|
self.tim.reg().ctrl().modify(|_, w| w.irq_enb().clear_bit());
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn release(self, syscfg: &mut pac::Sysconfig) -> Tim {
|
||||||
|
self.tim.reg().ctrl().write(|w| w.enable().clear_bit());
|
||||||
|
syscfg
|
||||||
|
.tim_clk_enable()
|
||||||
|
.modify(|r, w| unsafe { w.bits(r.bits() & !(1 << Tim::TIM_ID)) });
|
||||||
|
self.tim.release()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Load the count down timer with a timeout but do not start it.
|
||||||
|
pub fn load(&mut self, timeout: impl Into<Hertz>) {
|
||||||
|
self.tim.reg().ctrl().modify(|_, w| w.enable().clear_bit());
|
||||||
|
self.curr_freq = timeout.into();
|
||||||
|
self.rst_val = self.clock.raw() / self.curr_freq.raw();
|
||||||
|
self.set_reload(self.rst_val);
|
||||||
|
self.set_count(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn set_reload(&mut self, val: u32) {
|
||||||
|
self.tim.reg().rst_value().write(|w| unsafe { w.bits(val) });
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn set_count(&mut self, val: u32) {
|
||||||
|
self.tim.reg().cnt_value().write(|w| unsafe { w.bits(val) });
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn count(&self) -> u32 {
|
||||||
|
self.tim.reg().cnt_value().read().bits()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn enable(&mut self) {
|
||||||
|
self.tim.reg().ctrl().modify(|_, w| w.enable().set_bit());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn disable(&mut self) {
|
||||||
|
self.tim.reg().ctrl().modify(|_, w| w.enable().clear_bit());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Disable the counter, setting both enable and active bit to 0
|
||||||
|
#[inline]
|
||||||
|
pub fn auto_disable(self, enable: bool) -> Self {
|
||||||
|
if enable {
|
||||||
|
self.tim
|
||||||
|
.reg()
|
||||||
|
.ctrl()
|
||||||
|
.modify(|_, w| w.auto_disable().set_bit());
|
||||||
|
} else {
|
||||||
|
self.tim
|
||||||
|
.reg()
|
||||||
|
.ctrl()
|
||||||
|
.modify(|_, w| w.auto_disable().clear_bit());
|
||||||
|
}
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// This option only applies when the Auto-Disable functionality is 0.
|
||||||
|
///
|
||||||
|
/// The active bit is changed to 0 when count reaches 0, but the counter stays
|
||||||
|
/// enabled. When Auto-Disable is 1, Auto-Deactivate is implied
|
||||||
|
#[inline]
|
||||||
|
pub fn auto_deactivate(self, enable: bool) -> Self {
|
||||||
|
if enable {
|
||||||
|
self.tim
|
||||||
|
.reg()
|
||||||
|
.ctrl()
|
||||||
|
.modify(|_, w| w.auto_deactivate().set_bit());
|
||||||
|
} else {
|
||||||
|
self.tim
|
||||||
|
.reg()
|
||||||
|
.ctrl()
|
||||||
|
.modify(|_, w| w.auto_deactivate().clear_bit());
|
||||||
|
}
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Configure the cascade parameters
|
||||||
|
#[inline]
|
||||||
|
pub fn cascade_control(&mut self, ctrl: CascadeCtrl) {
|
||||||
|
self.tim.reg().csd_ctrl().write(|w| {
|
||||||
|
w.csden0().bit(ctrl.enb_start_src_csd0);
|
||||||
|
w.csdinv0().bit(ctrl.inv_csd0);
|
||||||
|
w.csden1().bit(ctrl.enb_start_src_csd1);
|
||||||
|
w.csdinv1().bit(ctrl.inv_csd1);
|
||||||
|
w.dcasop().bit(ctrl.dual_csd_op);
|
||||||
|
w.csdtrg0().bit(ctrl.trg_csd0);
|
||||||
|
w.csdtrg1().bit(ctrl.trg_csd1);
|
||||||
|
w.csden2().bit(ctrl.enb_stop_src_csd2);
|
||||||
|
w.csdinv2().bit(ctrl.inv_csd2);
|
||||||
|
w.csdtrg2().bit(ctrl.trg_csd2)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn cascade_0_source(&mut self, src: CascadeSource) -> Result<(), InvalidCascadeSourceId> {
|
||||||
|
let id = src.id()?;
|
||||||
|
self.tim
|
||||||
|
.reg()
|
||||||
|
.cascade0()
|
||||||
|
.write(|w| unsafe { w.cassel().bits(id) });
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn cascade_1_source(&mut self, src: CascadeSource) -> Result<(), InvalidCascadeSourceId> {
|
||||||
|
let id = src.id()?;
|
||||||
|
self.tim
|
||||||
|
.reg()
|
||||||
|
.cascade1()
|
||||||
|
.write(|w| unsafe { w.cassel().bits(id) });
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn cascade_2_source(&mut self, src: CascadeSource) -> Result<(), InvalidCascadeSourceId> {
|
||||||
|
let id = src.id()?;
|
||||||
|
self.tim
|
||||||
|
.reg()
|
||||||
|
.cascade2()
|
||||||
|
.write(|w| unsafe { w.cassel().bits(id) });
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn curr_freq(&self) -> Hertz {
|
||||||
|
self.curr_freq
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn listening(&self) -> bool {
|
||||||
|
self.listening
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Tim: ValidTim> embedded_hal::delay::DelayNs for CountdownTimer<Tim> {
|
||||||
|
fn delay_ns(&mut self, ns: u32) {
|
||||||
|
let ticks = (u64::from(ns)) * (u64::from(self.clock.raw())) / 1_000_000_000;
|
||||||
|
|
||||||
|
let full_cycles = ticks >> 32;
|
||||||
|
let mut last_count;
|
||||||
|
let mut new_count;
|
||||||
|
if full_cycles > 0 {
|
||||||
|
self.set_reload(u32::MAX);
|
||||||
|
self.set_count(u32::MAX);
|
||||||
|
self.enable();
|
||||||
|
|
||||||
|
for _ in 0..full_cycles {
|
||||||
|
// Always ensure that both values are the same at the start.
|
||||||
|
new_count = self.count();
|
||||||
|
last_count = new_count;
|
||||||
|
loop {
|
||||||
|
new_count = self.count();
|
||||||
|
if new_count == 0 {
|
||||||
|
// Wait till timer has wrapped.
|
||||||
|
while self.count() == 0 {
|
||||||
|
cortex_m::asm::nop()
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// Timer has definitely wrapped.
|
||||||
|
if new_count > last_count {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
last_count = new_count;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let ticks = (ticks & u32::MAX as u64) as u32;
|
||||||
|
self.disable();
|
||||||
|
if ticks > 1 {
|
||||||
|
self.set_reload(ticks);
|
||||||
|
self.set_count(ticks);
|
||||||
|
self.enable();
|
||||||
|
last_count = ticks;
|
||||||
|
|
||||||
|
loop {
|
||||||
|
new_count = self.count();
|
||||||
|
if new_count == 0 || (new_count > last_count) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
last_count = new_count;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.disable();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//==================================================================================================
|
||||||
|
// MS tick implementations
|
||||||
|
//==================================================================================================
|
||||||
|
|
||||||
|
// Set up a millisecond timer on TIM0. Please note that the user still has to provide an IRQ handler
|
||||||
|
// which should call [default_ms_irq_handler].
|
||||||
|
pub fn set_up_ms_tick<Tim: ValidTim>(
|
||||||
|
sys_cfg: &mut pac::Sysconfig,
|
||||||
|
tim: Tim,
|
||||||
|
clocks: &Clocks,
|
||||||
|
) -> CountdownTimer<Tim> {
|
||||||
|
let mut ms_timer = CountdownTimer::new(sys_cfg, tim, clocks);
|
||||||
|
ms_timer.listen();
|
||||||
|
ms_timer.start(1000.Hz());
|
||||||
|
ms_timer
|
||||||
|
}
|
||||||
|
|
||||||
|
/// This function can be called in a specified interrupt handler to increment
|
||||||
|
/// the MS counter
|
||||||
|
pub fn default_ms_irq_handler() {
|
||||||
|
cortex_m::interrupt::free(|cs| {
|
||||||
|
let mut ms = MS_COUNTER.borrow(cs).get();
|
||||||
|
ms += 1;
|
||||||
|
MS_COUNTER.borrow(cs).set(ms);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the current MS tick count
|
||||||
|
pub fn get_ms_ticks() -> u32 {
|
||||||
|
cortex_m::interrupt::free(|cs| MS_COUNTER.borrow(cs).get())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct DelayMs<Tim: ValidTim = pac::Tim0>(CountdownTimer<Tim>);
|
||||||
|
|
||||||
|
impl<Tim: ValidTim> DelayMs<Tim> {
|
||||||
|
pub fn new(timer: CountdownTimer<Tim>) -> Option<Self> {
|
||||||
|
if timer.curr_freq() != Hertz::from_raw(1000) || !timer.listening() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
Some(Self(timer))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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::delay::DelayNs for DelayMs {
|
||||||
|
fn delay_ns(&mut self, ns: u32) {
|
||||||
|
let ns_as_ms = ns / 1_000_000;
|
||||||
|
if self.0.curr_freq() != Hertz::from_raw(1000) || !self.0.listening() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let start_time = get_ms_ticks();
|
||||||
|
while get_ms_ticks() - start_time < ns_as_ms {
|
||||||
|
cortex_m::asm::nop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,15 +1,21 @@
|
|||||||
|
//! # API for the UART peripheral
|
||||||
|
//!
|
||||||
|
//! ## Examples
|
||||||
|
//!
|
||||||
|
//! - [UART simple example](https://egit.irs.uni-stuttgart.de/rust/va416xx-rs/src/branch/main/examples/simple/examples/uart.rs)
|
||||||
use core::marker::PhantomData;
|
use core::marker::PhantomData;
|
||||||
use core::ops::Deref;
|
use core::ops::Deref;
|
||||||
|
|
||||||
use embedded_hal_nb::serial::Read;
|
use embedded_hal_nb::serial::Read;
|
||||||
use fugit::RateExtU32;
|
use fugit::RateExtU32;
|
||||||
|
|
||||||
use crate::clock::{self, Clocks};
|
use crate::clock::{Clocks, PeripheralSelect};
|
||||||
use crate::gpio::{AltFunc1, Pin, PD11, PD12, PE2, PE3, PF11, PF12, PF8, PF9, PG0, PG1};
|
use crate::gpio::{AltFunc1, Pin, PD11, PD12, PE2, PE3, PF11, PF12, PF8, PF9, PG0, PG1};
|
||||||
use crate::time::Hertz;
|
use crate::time::Hertz;
|
||||||
|
use crate::{disable_interrupt, enable_interrupt};
|
||||||
use crate::{
|
use crate::{
|
||||||
gpio::{AltFunc2, AltFunc3, PA2, PA3, PB14, PB15, PC14, PC15, PC4, PC5},
|
gpio::{AltFunc2, AltFunc3, PA2, PA3, PB14, PB15, PC14, PC15, PC4, PC5},
|
||||||
pac::{uart0 as uart_base, Uart0, Uart1, Uart2},
|
pac::{self, uart0 as uart_base, Uart0, Uart1, Uart2},
|
||||||
};
|
};
|
||||||
|
|
||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
@ -332,10 +338,46 @@ impl<Uart> Tx<Uart> {
|
|||||||
|
|
||||||
pub trait Instance: Deref<Target = uart_base::RegisterBlock> {
|
pub trait Instance: Deref<Target = uart_base::RegisterBlock> {
|
||||||
const IDX: u8;
|
const IDX: u8;
|
||||||
|
const PERIPH_SEL: PeripheralSelect;
|
||||||
|
const IRQ_RX: pac::Interrupt;
|
||||||
|
const IRQ_TX: pac::Interrupt;
|
||||||
|
|
||||||
fn ptr() -> *const uart_base::RegisterBlock;
|
fn ptr() -> *const uart_base::RegisterBlock;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Instance for Uart0 {
|
||||||
|
const IDX: u8 = 0;
|
||||||
|
const PERIPH_SEL: PeripheralSelect = PeripheralSelect::Uart0;
|
||||||
|
const IRQ_RX: pac::Interrupt = pac::Interrupt::UART0_RX;
|
||||||
|
const IRQ_TX: pac::Interrupt = pac::Interrupt::UART0_TX;
|
||||||
|
|
||||||
|
fn ptr() -> *const uart_base::RegisterBlock {
|
||||||
|
Uart0::ptr() as *const _
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Instance for Uart1 {
|
||||||
|
const IDX: u8 = 1;
|
||||||
|
const PERIPH_SEL: PeripheralSelect = PeripheralSelect::Uart1;
|
||||||
|
const IRQ_RX: pac::Interrupt = pac::Interrupt::UART1_RX;
|
||||||
|
const IRQ_TX: pac::Interrupt = pac::Interrupt::UART1_TX;
|
||||||
|
|
||||||
|
fn ptr() -> *const uart_base::RegisterBlock {
|
||||||
|
Uart1::ptr() as *const _
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Instance for Uart2 {
|
||||||
|
const IDX: u8 = 2;
|
||||||
|
const PERIPH_SEL: PeripheralSelect = PeripheralSelect::Uart2;
|
||||||
|
const IRQ_RX: pac::Interrupt = pac::Interrupt::UART2_RX;
|
||||||
|
const IRQ_TX: pac::Interrupt = pac::Interrupt::UART2_TX;
|
||||||
|
|
||||||
|
fn ptr() -> *const uart_base::RegisterBlock {
|
||||||
|
Uart2::ptr() as *const _
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<Uart: Instance> UartBase<Uart> {
|
impl<Uart: Instance> UartBase<Uart> {
|
||||||
fn init(self, config: Config, clocks: &Clocks) -> Self {
|
fn init(self, config: Config, clocks: &Clocks) -> Self {
|
||||||
if Uart::IDX == 2 {
|
if Uart::IDX == 2 {
|
||||||
@ -460,6 +502,7 @@ impl<Uart: Instance> UartBase<Uart> {
|
|||||||
w.rxenable().clear_bit();
|
w.rxenable().clear_bit();
|
||||||
w.txenable().clear_bit()
|
w.txenable().clear_bit()
|
||||||
});
|
});
|
||||||
|
disable_interrupt(Uart::IRQ_RX);
|
||||||
self.uart
|
self.uart
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -468,10 +511,45 @@ impl<Uart: Instance> UartBase<Uart> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<UartInstance, Pins> Uart<UartInstance, Pins>
|
impl<UartInstance: Instance, Pins> Uart<UartInstance, Pins> {
|
||||||
where
|
pub fn new(
|
||||||
UartInstance: Instance,
|
uart: UartInstance,
|
||||||
{
|
pins: Pins,
|
||||||
|
config: impl Into<Config>,
|
||||||
|
syscfg: &mut va416xx::Sysconfig,
|
||||||
|
clocks: &Clocks,
|
||||||
|
) -> Self {
|
||||||
|
crate::clock::enable_peripheral_clock(syscfg, UartInstance::PERIPH_SEL);
|
||||||
|
Uart {
|
||||||
|
inner: UartBase {
|
||||||
|
uart,
|
||||||
|
tx: Tx::new(),
|
||||||
|
rx: Rx::new(),
|
||||||
|
},
|
||||||
|
pins,
|
||||||
|
}
|
||||||
|
.init(config.into(), clocks)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new_with_clock_freq(
|
||||||
|
uart: UartInstance,
|
||||||
|
pins: Pins,
|
||||||
|
config: impl Into<Config>,
|
||||||
|
syscfg: &mut va416xx::Sysconfig,
|
||||||
|
clock: impl Into<Hertz>,
|
||||||
|
) -> Self {
|
||||||
|
crate::clock::enable_peripheral_clock(syscfg, UartInstance::PERIPH_SEL);
|
||||||
|
Uart {
|
||||||
|
inner: UartBase {
|
||||||
|
uart,
|
||||||
|
tx: Tx::new(),
|
||||||
|
rx: Rx::new(),
|
||||||
|
},
|
||||||
|
pins,
|
||||||
|
}
|
||||||
|
.init_with_clock_freq(config.into(), clock.into())
|
||||||
|
}
|
||||||
|
|
||||||
fn init(mut self, config: Config, clocks: &Clocks) -> Self {
|
fn init(mut self, config: Config, clocks: &Clocks) -> Self {
|
||||||
self.inner = self.inner.init(config, clocks);
|
self.inner = self.inner.init(config, clocks);
|
||||||
self
|
self
|
||||||
@ -584,6 +662,7 @@ impl<Uart: Instance> UartWithIrqBase<Uart> {
|
|||||||
self.inner.enable_rx();
|
self.inner.enable_rx();
|
||||||
self.inner.enable_tx();
|
self.inner.enable_tx();
|
||||||
self.enable_rx_irq_sources(enb_timeout_irq);
|
self.enable_rx_irq_sources(enb_timeout_irq);
|
||||||
|
unsafe { enable_interrupt(Uart::IRQ_RX) };
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -783,87 +862,6 @@ impl<Uart: Instance, Pins> UartWithIrq<Uart, Pins> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Instance for Uart0 {
|
|
||||||
const IDX: u8 = 0;
|
|
||||||
|
|
||||||
fn ptr() -> *const uart_base::RegisterBlock {
|
|
||||||
Uart0::ptr() as *const _
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Instance for Uart1 {
|
|
||||||
const IDX: u8 = 1;
|
|
||||||
|
|
||||||
fn ptr() -> *const uart_base::RegisterBlock {
|
|
||||||
Uart1::ptr() as *const _
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Instance for Uart2 {
|
|
||||||
const IDX: u8 = 2;
|
|
||||||
|
|
||||||
fn ptr() -> *const uart_base::RegisterBlock {
|
|
||||||
Uart2::ptr() as *const _
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
macro_rules! uart_impl {
|
|
||||||
($($Uartx:ident: ($uartx:ident, $clk_enb_enum:path),)+) => {
|
|
||||||
$(
|
|
||||||
|
|
||||||
impl<Pins: TxRxPins<$Uartx>> Uart<$Uartx, Pins> {
|
|
||||||
pub fn $uartx(
|
|
||||||
uart: $Uartx,
|
|
||||||
pins: Pins,
|
|
||||||
config: impl Into<Config>,
|
|
||||||
syscfg: &mut va416xx::Sysconfig,
|
|
||||||
clocks: &Clocks
|
|
||||||
) -> Self
|
|
||||||
{
|
|
||||||
crate::clock::enable_peripheral_clock(syscfg, $clk_enb_enum);
|
|
||||||
Uart {
|
|
||||||
inner: UartBase {
|
|
||||||
uart,
|
|
||||||
tx: Tx::new(),
|
|
||||||
rx: Rx::new(),
|
|
||||||
},
|
|
||||||
pins,
|
|
||||||
}.init(config.into(), clocks)
|
|
||||||
}
|
|
||||||
|
|
||||||
paste::paste! {
|
|
||||||
pub fn [ <$uartx _with_clock_freq> ](
|
|
||||||
uart: $Uartx,
|
|
||||||
pins: Pins,
|
|
||||||
config: impl Into<Config>,
|
|
||||||
syscfg: &mut va416xx::Sysconfig,
|
|
||||||
clock: impl Into<Hertz>,
|
|
||||||
) -> Self
|
|
||||||
{
|
|
||||||
crate::clock::enable_peripheral_clock(syscfg, $clk_enb_enum);
|
|
||||||
Uart {
|
|
||||||
inner: UartBase {
|
|
||||||
uart,
|
|
||||||
tx: Tx::new(),
|
|
||||||
rx: Rx::new(),
|
|
||||||
},
|
|
||||||
pins,
|
|
||||||
}.init_with_clock_freq(config.into(), clock.into())
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
)+
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
uart_impl! {
|
|
||||||
Uart0: (uart0, clock::PeripheralClocks::Uart0),
|
|
||||||
Uart1: (uart1, clock::PeripheralClocks::Uart1),
|
|
||||||
Uart2: (uart2, clock::PeripheralClocks::Uart2),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl embedded_io::Error for Error {
|
impl embedded_io::Error for Error {
|
||||||
fn kind(&self) -> embedded_io::ErrorKind {
|
fn kind(&self) -> embedded_io::ErrorKind {
|
||||||
embedded_io::ErrorKind::Other
|
embedded_io::ErrorKind::Other
|
||||||
|
@ -1,9 +1,15 @@
|
|||||||
|
//! # API for the Watchdog peripheral
|
||||||
|
//!
|
||||||
|
//! ## Examples
|
||||||
|
//!
|
||||||
|
//! - [Watchdog simple example](https://egit.irs.uni-stuttgart.de/rust/va416xx-rs/src/branch/main/examples/simple/examples/wdt.rs)
|
||||||
use crate::time::Hertz;
|
use crate::time::Hertz;
|
||||||
use crate::{
|
use crate::{
|
||||||
clock::{Clocks, PeripheralSelect},
|
clock::{Clocks, PeripheralSelect},
|
||||||
pac,
|
pac,
|
||||||
prelude::SyscfgExt,
|
prelude::SyscfgExt,
|
||||||
};
|
};
|
||||||
|
use crate::{disable_interrupt, enable_interrupt};
|
||||||
|
|
||||||
pub const WDT_UNLOCK_VALUE: u32 = 0x1ACC_E551;
|
pub const WDT_UNLOCK_VALUE: u32 = 0x1ACC_E551;
|
||||||
|
|
||||||
@ -19,14 +25,12 @@ pub struct WdtController {
|
|||||||
/// This function is `unsafe` because it can break mask-based critical sections.
|
/// This function is `unsafe` because it can break mask-based critical sections.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub unsafe fn enable_wdt_interrupts() {
|
pub unsafe fn enable_wdt_interrupts() {
|
||||||
unsafe {
|
enable_interrupt(pac::Interrupt::WATCHDOG)
|
||||||
cortex_m::peripheral::NVIC::unmask(pac::Interrupt::WATCHDOG);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn disable_wdt_interrupts() {
|
pub fn disable_wdt_interrupts() {
|
||||||
cortex_m::peripheral::NVIC::mask(pac::Interrupt::WATCHDOG);
|
disable_interrupt(pac::Interrupt::WATCHDOG)
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WdtController {
|
impl WdtController {
|
||||||
|
@ -19,11 +19,12 @@ embedded-hal = "1"
|
|||||||
path = "../va416xx-hal"
|
path = "../va416xx-hal"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
|
|
||||||
|
[dependencies.lis2dh12]
|
||||||
|
git = "https://github.com/us-irs/lis2dh12.git"
|
||||||
|
# path = "../../lis2dh12"
|
||||||
|
branch = "all-features"
|
||||||
|
version = "0.7"
|
||||||
|
features = ["out_f32"]
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
rt = ["va416xx-hal/rt"]
|
rt = ["va416xx-hal/rt"]
|
||||||
|
|
||||||
[dev-dependencies]
|
|
||||||
panic-rtt-target = { version = "0.1.3" }
|
|
||||||
rtt-target = { version = "0.5" }
|
|
||||||
panic-halt = "0.2"
|
|
||||||
cortex-m = { version = "0.7.6", features = ["critical-section-single-core"]}
|
|
||||||
|
@ -1 +1,50 @@
|
|||||||
|
//! Board support crate for the VORAGO PEB1 board.
|
||||||
#![no_std]
|
#![no_std]
|
||||||
|
|
||||||
|
pub use lis2dh12;
|
||||||
|
|
||||||
|
/// Support for the LIS2DH12 accelerometer on the GPIO board.
|
||||||
|
pub mod accelerometer {
|
||||||
|
|
||||||
|
use lis2dh12::{self, detect_i2c_addr, AddrDetectionError, Lis2dh12};
|
||||||
|
use va416xx_hal::{
|
||||||
|
clock::Clocks,
|
||||||
|
i2c::{self, ClockTooSlowForFastI2c, I2cMaster, I2cSpeed, MasterConfig},
|
||||||
|
pac,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Accelerometer located on the GPIO board.
|
||||||
|
pub type Accelerometer = Lis2dh12<I2cMaster<pac::I2c0>>;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum ConstructorError {
|
||||||
|
ClkError(ClockTooSlowForFastI2c),
|
||||||
|
AddrDetectionError(AddrDetectionError<i2c::Error>),
|
||||||
|
AccelerometerError(lis2dh12::Error<i2c::Error>),
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new_with_addr_detection(
|
||||||
|
i2c: pac::I2c0,
|
||||||
|
sysconfig: &mut pac::Sysconfig,
|
||||||
|
clocks: &Clocks,
|
||||||
|
) -> Result<Accelerometer, ConstructorError> {
|
||||||
|
let mut i2c_master = I2cMaster::new(
|
||||||
|
i2c,
|
||||||
|
sysconfig,
|
||||||
|
MasterConfig::default(),
|
||||||
|
clocks,
|
||||||
|
I2cSpeed::Regular100khz,
|
||||||
|
)
|
||||||
|
.map_err(ConstructorError::ClkError)?;
|
||||||
|
let slave_addr =
|
||||||
|
detect_i2c_addr(&mut i2c_master).map_err(ConstructorError::AddrDetectionError)?;
|
||||||
|
Lis2dh12::new(i2c_master, slave_addr).map_err(ConstructorError::AccelerometerError)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new_with_i2cm(
|
||||||
|
i2c: I2cMaster<pac::I2c0>,
|
||||||
|
addr: lis2dh12::SlaveAddr,
|
||||||
|
) -> Result<Accelerometer, lis2dh12::Error<i2c::Error>> {
|
||||||
|
Lis2dh12::new(i2c, addr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -24,6 +24,7 @@
|
|||||||
"runToEntryPoint": "main",
|
"runToEntryPoint": "main",
|
||||||
"rttConfig": {
|
"rttConfig": {
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
|
// Have to use exact address unfortunately. "auto" does not work for some reason..
|
||||||
"address": "0x1fff8000",
|
"address": "0x1fff8000",
|
||||||
"decoders": [
|
"decoders": [
|
||||||
{
|
{
|
||||||
@ -53,6 +54,7 @@
|
|||||||
"runToEntryPoint": "main",
|
"runToEntryPoint": "main",
|
||||||
"rttConfig": {
|
"rttConfig": {
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
|
// Have to use exact address unfortunately. "auto" does not work for some reason..
|
||||||
"address": "0x1fff8000",
|
"address": "0x1fff8000",
|
||||||
"decoders": [
|
"decoders": [
|
||||||
{
|
{
|
||||||
@ -82,6 +84,7 @@
|
|||||||
"runToEntryPoint": "main",
|
"runToEntryPoint": "main",
|
||||||
"rttConfig": {
|
"rttConfig": {
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
|
// Have to use exact address unfortunately. "auto" does not work for some reason..
|
||||||
"address": "0x1fff8000",
|
"address": "0x1fff8000",
|
||||||
"decoders": [
|
"decoders": [
|
||||||
{
|
{
|
||||||
@ -111,6 +114,7 @@
|
|||||||
"runToEntryPoint": "main",
|
"runToEntryPoint": "main",
|
||||||
"rttConfig": {
|
"rttConfig": {
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
|
// Have to use exact address unfortunately. "auto" does not work for some reason..
|
||||||
"address": "0x1fff8000",
|
"address": "0x1fff8000",
|
||||||
"decoders": [
|
"decoders": [
|
||||||
{
|
{
|
||||||
@ -140,6 +144,7 @@
|
|||||||
"runToEntryPoint": "main",
|
"runToEntryPoint": "main",
|
||||||
"rttConfig": {
|
"rttConfig": {
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
|
// Have to use exact address unfortunately. "auto" does not work for some reason..
|
||||||
"address": "0x1fff8000",
|
"address": "0x1fff8000",
|
||||||
"decoders": [
|
"decoders": [
|
||||||
{
|
{
|
||||||
@ -149,5 +154,36 @@
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"type": "cortex-debug",
|
||||||
|
"request": "launch",
|
||||||
|
"name": "Debug Timer/Ticks Example",
|
||||||
|
"servertype": "jlink",
|
||||||
|
"jlinkscript": "${workspaceFolder}/jlink/JLinkSettings.JLinkScript",
|
||||||
|
"cwd": "${workspaceRoot}",
|
||||||
|
"device": "Cortex-M4",
|
||||||
|
"svdFile": "${workspaceFolder}/va416xx/svd/va416xx.svd.patched",
|
||||||
|
"preLaunchTask": "timer-ticks-example",
|
||||||
|
"overrideLaunchCommands": [
|
||||||
|
"monitor halt",
|
||||||
|
"monitor reset",
|
||||||
|
"load",
|
||||||
|
],
|
||||||
|
"executable": "${workspaceFolder}/target/thumbv7em-none-eabihf/debug/examples/timer-ticks",
|
||||||
|
"interface": "swd",
|
||||||
|
"runToEntryPoint": "main",
|
||||||
|
"rttConfig": {
|
||||||
|
"enabled": true,
|
||||||
|
// Have to use exact address unfortunately. "auto" does not work for some reason..
|
||||||
|
"address": "0x1fff8000",
|
||||||
|
"decoders": [
|
||||||
|
{
|
||||||
|
"port": 0,
|
||||||
|
"timestamp": true,
|
||||||
|
"type": "console"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
]
|
]
|
||||||
}
|
}
|
Loading…
Reference in New Issue
Block a user