Compare commits
8 Commits
main
...
can-suppor
Author | SHA1 | Date | |
---|---|---|---|
be6cd2d609 | |||
5e9526a2d1 | |||
125619e485 | |||
a6c9a6fcdc | |||
e8e7ea9b1c | |||
7d6f69d808 | |||
0baf8d7b32 | |||
512de17719 |
2
scripts/unittest.sh
Executable file
2
scripts/unittest.sh
Executable file
@ -0,0 +1,2 @@
|
||||
#!/bin/bash
|
||||
cargo +stable test --target $(rustc -vV | grep host | cut -d ' ' -f2) -p va416xx-hal --features alloc
|
@ -15,15 +15,19 @@ cortex-m = { version = "0.7", features = ["critical-section-single-core"] }
|
||||
critical-section = "1"
|
||||
nb = "1"
|
||||
paste = "1"
|
||||
libm = "0.2"
|
||||
embedded-hal-nb = "1"
|
||||
embedded-hal-async = "1"
|
||||
embedded-hal = "1"
|
||||
embedded-io = "0.6"
|
||||
embedded-io-async = "0.6"
|
||||
embedded-can = "0.4"
|
||||
num_enum = { version = "0.7", default-features = false }
|
||||
typenum = "1"
|
||||
bitflags = "2"
|
||||
bitfield = { version = ">=0.17, <=0.18"}
|
||||
bitbybit = "1.3"
|
||||
arbitrary-int = "1.3"
|
||||
derive-mmio = "0.4"
|
||||
fugit = "0.3"
|
||||
delegate = ">=0.12, <=0.13"
|
||||
heapless = "0.8"
|
||||
@ -33,11 +37,12 @@ portable-atomic = "1"
|
||||
embassy-sync = "0.6"
|
||||
va416xx = { version = "0.4", features = ["critical-section"], default-features = false }
|
||||
|
||||
defmt = { version = "0.3", optional = true }
|
||||
defmt = { version = "1", optional = true }
|
||||
|
||||
[features]
|
||||
default = ["rt", "revb"]
|
||||
rt = ["va416xx/rt"]
|
||||
alloc = []
|
||||
defmt = ["dep:defmt", "fugit/defmt", "embedded-hal/defmt-03"]
|
||||
|
||||
va41630 = ["device-selected"]
|
||||
|
@ -287,7 +287,7 @@ impl Adc<ChannelTagEnabled> {
|
||||
|
||||
impl<TagEnabled> Adc<TagEnabled> {
|
||||
fn generic_new(syscfg: &mut pac::Sysconfig, adc: pac::Adc, _clocks: &Clocks) -> Self {
|
||||
syscfg.enable_peripheral_clock(crate::clock::PeripheralSelect::Adc);
|
||||
syscfg.enable_peripheral_clock(crate::PeripheralSelect::Adc);
|
||||
adc.ctrl().write(|w| unsafe { w.bits(0) });
|
||||
let adc = Self {
|
||||
adc,
|
||||
|
117
va416xx-hal/src/can/frame.rs
Normal file
117
va416xx-hal/src/can/frame.rs
Normal file
@ -0,0 +1,117 @@
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
#[error("invalid data size error {0}")]
|
||||
pub struct InvalidDataSizeError(usize);
|
||||
|
||||
pub struct CanFrameNormal {
|
||||
id: embedded_can::Id,
|
||||
size: usize,
|
||||
data: [u8; 8],
|
||||
}
|
||||
|
||||
impl CanFrameNormal {
|
||||
pub fn new(id: embedded_can::Id, data: &[u8]) -> Self {
|
||||
let size = data.len();
|
||||
let mut data_array = [0; 8];
|
||||
data_array[..size].copy_from_slice(data);
|
||||
Self {
|
||||
id,
|
||||
size,
|
||||
data: data_array,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn id(&self) -> embedded_can::Id {
|
||||
self.id
|
||||
}
|
||||
|
||||
pub fn data(&self) -> &[u8] {
|
||||
&self.data[..self.size]
|
||||
}
|
||||
|
||||
pub fn dlc(&self) -> usize {
|
||||
self.size
|
||||
}
|
||||
}
|
||||
|
||||
pub struct CanFrameRtr {
|
||||
id: embedded_can::Id,
|
||||
dlc: usize,
|
||||
}
|
||||
|
||||
impl CanFrameRtr {
|
||||
pub fn new(id: embedded_can::Id, dlc: usize) -> Self {
|
||||
Self { id, dlc }
|
||||
}
|
||||
|
||||
pub fn id(&self) -> embedded_can::Id {
|
||||
self.id
|
||||
}
|
||||
|
||||
pub fn dlc(&self) -> usize {
|
||||
self.dlc
|
||||
}
|
||||
}
|
||||
|
||||
pub enum CanFrame {
|
||||
Normal(CanFrameNormal),
|
||||
Rtr(CanFrameRtr),
|
||||
}
|
||||
|
||||
impl From<CanFrameNormal> for CanFrame {
|
||||
fn from(value: CanFrameNormal) -> Self {
|
||||
Self::Normal(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<CanFrameRtr> for CanFrame {
|
||||
fn from(value: CanFrameRtr) -> Self {
|
||||
Self::Rtr(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl embedded_can::Frame for CanFrame {
|
||||
fn new(id: impl Into<embedded_can::Id>, data: &[u8]) -> Option<Self> {
|
||||
let id: embedded_can::Id = id.into();
|
||||
Some(Self::Normal(CanFrameNormal::new(id, data)))
|
||||
}
|
||||
|
||||
fn new_remote(id: impl Into<embedded_can::Id>, dlc: usize) -> Option<Self> {
|
||||
let id: embedded_can::Id = id.into();
|
||||
Some(Self::Rtr(CanFrameRtr::new(id, dlc)))
|
||||
}
|
||||
|
||||
fn is_extended(&self) -> bool {
|
||||
match self.id() {
|
||||
embedded_can::Id::Extended(_) => true,
|
||||
embedded_can::Id::Standard(_) => false,
|
||||
}
|
||||
}
|
||||
|
||||
fn is_remote_frame(&self) -> bool {
|
||||
match self {
|
||||
CanFrame::Normal(_) => false,
|
||||
CanFrame::Rtr(_) => true,
|
||||
}
|
||||
}
|
||||
|
||||
fn id(&self) -> embedded_can::Id {
|
||||
match self {
|
||||
CanFrame::Normal(can_frame_normal) => can_frame_normal.id(),
|
||||
CanFrame::Rtr(can_frame_rtr) => can_frame_rtr.id(),
|
||||
}
|
||||
}
|
||||
|
||||
fn dlc(&self) -> usize {
|
||||
match self {
|
||||
CanFrame::Normal(can_frame_normal) => can_frame_normal.dlc(),
|
||||
CanFrame::Rtr(can_frame_rtr) => can_frame_rtr.dlc(),
|
||||
}
|
||||
}
|
||||
|
||||
fn data(&self) -> &[u8] {
|
||||
match self {
|
||||
CanFrame::Normal(can_frame_normal) => can_frame_normal.data(),
|
||||
CanFrame::Rtr(_) => &[],
|
||||
}
|
||||
}
|
||||
}
|
606
va416xx-hal/src/can/mod.rs
Normal file
606
va416xx-hal/src/can/mod.rs
Normal file
@ -0,0 +1,606 @@
|
||||
//! CAN driver.
|
||||
//!
|
||||
//! The VA416xx CAN module is based on the CP3UB26 module.
|
||||
use arbitrary_int::{u11, u15, u2, u3, u4, u7, Number};
|
||||
use embedded_can::Frame;
|
||||
use regs::{
|
||||
BaseId, BufStatusAndControl, Control, DataDirection, ExtendedId, MmioCan, TimingConfig,
|
||||
};
|
||||
|
||||
use crate::{clock::Clocks, enable_peripheral_clock, time::Hertz, PeripheralSelect};
|
||||
use libm::roundf;
|
||||
|
||||
pub mod frame;
|
||||
pub mod regs;
|
||||
pub use frame::*;
|
||||
|
||||
pub const PRESCALER_MIN: u8 = 2;
|
||||
pub const PRESCALER_MAX: u8 = 128;
|
||||
/// 1 is the minimum value, but not recommended by Vorago.
|
||||
pub const TSEG1_MIN: u8 = 1;
|
||||
pub const TSEG1_MAX: u8 = 16;
|
||||
pub const TSEG2_MAX: u8 = 8;
|
||||
/// In addition, SJW may not be larger than TSEG2.
|
||||
pub const SJW_MAX: u8 = 4;
|
||||
|
||||
pub const MIN_SAMPLE_POINT: f32 = 0.5;
|
||||
pub const MAX_BITRATE_DEVIATION: f32 = 0.005;
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
||||
pub enum CanId {
|
||||
Can0 = 0,
|
||||
Can1 = 1,
|
||||
}
|
||||
|
||||
impl CanId {
|
||||
/// Steal the register block for the CAN ID.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// See safety of the [regs::Can::new_mmio_fixed_0].
|
||||
pub unsafe fn steal_regs(&self) -> regs::MmioCan<'static> {
|
||||
match self {
|
||||
CanId::Can0 => unsafe { regs::Can::new_mmio_fixed_0() },
|
||||
CanId::Can1 => unsafe { regs::Can::new_mmio_fixed_1() },
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Sample point between 0 and 1.0 for the given time segments.
|
||||
pub const fn calculate_sample_point(tseg1: u8, tseg2: u8) -> f32 {
|
||||
let tseg1_val = tseg1 as f32;
|
||||
(tseg1_val + 1.0) / (1.0 + tseg1_val + tseg2 as f32)
|
||||
}
|
||||
|
||||
/// Calculate all viable clock configurations for the given input clock, the target bitrate and
|
||||
/// for a sample point between 0.5 and 1.0.
|
||||
///
|
||||
/// There are various recommendations for the sample point when using the CAN bus. The value
|
||||
/// depends on different parameters like the bus length and propagation time, as well as
|
||||
/// the information processing time of the nodes. It should always be at least 50 %.
|
||||
/// In doubt, select a value like 0.75.
|
||||
///
|
||||
/// - The [Python CAN library](https://python-can.readthedocs.io/en/stable/bit_timing.html)
|
||||
/// assumes a default value of 69 % as the sample point if none is specified.
|
||||
/// - CiA-301 recommends 87.5 %
|
||||
/// - For simpler setups like laboratory setups, smaller values should work as well.
|
||||
///
|
||||
/// A clock configuration is consideres viable when
|
||||
///
|
||||
/// - The sample point deviation is less than 5 %.
|
||||
/// - The bitrate error is less than +-0.5 %.
|
||||
///
|
||||
/// SJW will be set to either TSEG2 or 4, whichever is smaller.
|
||||
#[cfg(feature = "alloc")]
|
||||
pub fn calculate_all_viable_clock_configs(
|
||||
apb1_clock: Hertz,
|
||||
bitrate: Hertz,
|
||||
sample_point: f32,
|
||||
) -> Result<alloc::vec::Vec<ClockConfig>, InvalidSamplePointError> {
|
||||
if sample_point < 0.5 || sample_point > 1.0 {
|
||||
return Err(InvalidSamplePointError { sample_point });
|
||||
}
|
||||
let mut configs = alloc::vec::Vec::new();
|
||||
for prescaler in PRESCALER_MIN..PRESCALER_MAX {
|
||||
let nom_bit_time = apb1_clock / (bitrate * prescaler as u32);
|
||||
// This is taken from the Python CAN library. NBT should not be too small.
|
||||
if nom_bit_time < 8 {
|
||||
break;
|
||||
}
|
||||
let actual_bitrate = apb1_clock / (prescaler as u32 * nom_bit_time);
|
||||
let bitrate_deviation = ((actual_bitrate.raw() as i32 - bitrate.raw() as i32).abs() as f32)
|
||||
/ bitrate.raw() as f32;
|
||||
if bitrate_deviation > 0.05 {
|
||||
continue;
|
||||
}
|
||||
let tseg1 = roundf(sample_point * nom_bit_time as f32) as u32 - 1;
|
||||
if tseg1 > TSEG1_MAX as u32 || tseg1 < TSEG1_MIN as u32 {
|
||||
continue;
|
||||
}
|
||||
// limit tseg1, so tseg2 is at least 1 TQ
|
||||
let tseg1 = core::cmp::min(tseg1, nom_bit_time - 2) as u8;
|
||||
let tseg2 = nom_bit_time - tseg1 as u32 - 1;
|
||||
if tseg2 > TSEG2_MAX as u32 {
|
||||
continue;
|
||||
}
|
||||
let tseg2 = tseg2 as u8;
|
||||
let sjw = core::cmp::min(tseg2, 4) as u8;
|
||||
// Use percent to have a higher resolution for the sample point deviation.
|
||||
let sample_point_actual = roundf(calculate_sample_point(tseg1, tseg2) * 100.0) as u32;
|
||||
let sample_point = roundf(sample_point * 100.0) as u32;
|
||||
let deviation = (sample_point_actual as i32 - sample_point as i32).abs();
|
||||
if deviation > 5 {
|
||||
continue;
|
||||
}
|
||||
configs.push(ClockConfig {
|
||||
prescaler,
|
||||
tseg1,
|
||||
tseg2,
|
||||
sjw,
|
||||
});
|
||||
}
|
||||
Ok(configs)
|
||||
}
|
||||
|
||||
pub trait Instance {
|
||||
const ID: CanId;
|
||||
const PERIPH_SEL: PeripheralSelect;
|
||||
}
|
||||
|
||||
impl Instance for va416xx::Can0 {
|
||||
const ID: CanId = CanId::Can0;
|
||||
const PERIPH_SEL: PeripheralSelect = PeripheralSelect::Can0;
|
||||
}
|
||||
|
||||
impl Instance for va416xx::Can1 {
|
||||
const ID: CanId = CanId::Can1;
|
||||
const PERIPH_SEL: PeripheralSelect = PeripheralSelect::Can1;
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct ClockConfig {
|
||||
prescaler: u8,
|
||||
tseg1: u8,
|
||||
tseg2: u8,
|
||||
sjw: u8,
|
||||
}
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
#[error("invalid buffer index {0}")]
|
||||
pub struct InvalidBufferIndexError(usize);
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
#[error("sjw must be less than or equal to the smaller tseg value")]
|
||||
pub struct InvalidSjwError(u8);
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
#[error("invalid sample point {sample_point}")]
|
||||
pub struct InvalidSamplePointError {
|
||||
/// Sample point, should be larger than 0.5 (50 %) but was not.
|
||||
sample_point: f32,
|
||||
}
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum ClockConfigError {
|
||||
#[error("invalid sjw: {0}")]
|
||||
InvalidSjw(#[from] InvalidSjwError),
|
||||
#[error("TSEG is zero which is not allowed")]
|
||||
TsegIsZero,
|
||||
#[error("TSEG1 is larger than 16")]
|
||||
InvalidTseg1,
|
||||
#[error("TSEG1 is larger than 8")]
|
||||
InvalidTseg2,
|
||||
#[error("invalid sample point: {0}")]
|
||||
InvalidSamplePoint(#[from] InvalidSamplePointError),
|
||||
#[error("bitrate is zero")]
|
||||
BitrateIsZero,
|
||||
#[error("bitrate error larger than +-0.5 %")]
|
||||
BitrateErrorTooLarge,
|
||||
#[error("maximum or minimum allowed prescaler is not sufficient for target bitrate clock")]
|
||||
CanNotFindPrescaler,
|
||||
}
|
||||
|
||||
impl ClockConfig {
|
||||
/// New clock configuration from the raw configuration values.
|
||||
///
|
||||
/// The values specified here are not the register values, but the actual numerical values
|
||||
/// relevant for calculations.
|
||||
///
|
||||
/// The values have the following requirements:
|
||||
///
|
||||
/// - Prescaler must be between 2 and 128.
|
||||
/// - TSEG1 must be smaller than 16 and should be larger than 1.
|
||||
/// - TSEG2 must be smaller than 8 and small enough so that the calculated sample point
|
||||
/// is larger than 0.5 (50 %).
|
||||
/// - SJW (Synchronization Jump Width) must be smaller than the smaller of the time segment
|
||||
/// configuration values and smaller than 4.
|
||||
pub fn new(prescaler: u8, tseg1: u8, tseg2: u8, sjw: u8) -> Result<Self, ClockConfigError> {
|
||||
if !(PRESCALER_MIN..=PRESCALER_MAX).contains(&prescaler.value()) {
|
||||
return Err(ClockConfigError::CanNotFindPrescaler);
|
||||
}
|
||||
if tseg1 == 0 || tseg2 == 0 {
|
||||
return Err(ClockConfigError::TsegIsZero);
|
||||
}
|
||||
if tseg1 > TSEG1_MAX {
|
||||
return Err(ClockConfigError::InvalidTseg1);
|
||||
}
|
||||
if tseg2 > TSEG2_MAX {
|
||||
return Err(ClockConfigError::InvalidTseg2);
|
||||
}
|
||||
let smaller_tseg = core::cmp::min(tseg1.value(), tseg2.value());
|
||||
if sjw.value() > smaller_tseg || sjw > SJW_MAX {
|
||||
return Err(InvalidSjwError(sjw).into());
|
||||
}
|
||||
let sample_point = calculate_sample_point(tseg1, tseg2);
|
||||
if sample_point < MIN_SAMPLE_POINT {
|
||||
return Err(InvalidSamplePointError { sample_point }.into());
|
||||
}
|
||||
Ok(Self {
|
||||
prescaler,
|
||||
tseg1,
|
||||
tseg2,
|
||||
sjw,
|
||||
})
|
||||
}
|
||||
|
||||
/// Calculate the clock configuration for the given input clock, the target bitrate and for a
|
||||
/// set of timing parameters.
|
||||
///
|
||||
/// This function basically calculates the necessary prescaler to achieve the given timing
|
||||
/// parameters. It also performs sanity and validity checks for the calculated prescaler:
|
||||
/// The bitrate error for the given prescaler needs to be smaller than 0.5 %.
|
||||
pub fn from_bitrate_and_segments(
|
||||
clocks: &Clocks,
|
||||
bitrate: Hertz,
|
||||
tseg1: u8,
|
||||
tseg2: u8,
|
||||
sjw: u8,
|
||||
) -> Result<ClockConfig, ClockConfigError> {
|
||||
if bitrate.raw() == 0 {
|
||||
return Err(ClockConfigError::BitrateIsZero);
|
||||
}
|
||||
let prescaler = roundf(
|
||||
clocks.apb1().raw() as f32
|
||||
/ (bitrate.raw() as f32 * (1.0 + tseg1.as_u32() as f32 + tseg2.as_u32() as f32)),
|
||||
) as u32;
|
||||
if !(PRESCALER_MIN as u32..=PRESCALER_MAX as u32).contains(&prescaler) {
|
||||
return Err(ClockConfigError::CanNotFindPrescaler);
|
||||
}
|
||||
|
||||
let actual_bitrate = clocks.apb1() / (prescaler * (1 + tseg1.as_u32() + tseg2.as_u32()));
|
||||
let bitrate_deviation = ((actual_bitrate.raw() as i32 - bitrate.raw() as i32).abs() as f32)
|
||||
/ bitrate.raw() as f32;
|
||||
if bitrate_deviation > MAX_BITRATE_DEVIATION {
|
||||
return Err(ClockConfigError::BitrateErrorTooLarge);
|
||||
}
|
||||
// The subtractions are fine because we made checks to avoid underflows.
|
||||
Self::new(prescaler as u8, tseg1, tseg2, sjw)
|
||||
}
|
||||
|
||||
pub fn sjw_reg_value(&self) -> u2 {
|
||||
u2::new(self.sjw.value() - 1)
|
||||
}
|
||||
|
||||
pub fn tseg1_reg_value(&self) -> u4 {
|
||||
u4::new(self.tseg1.value() - 1)
|
||||
}
|
||||
|
||||
pub fn tseg2_reg_value(&self) -> u3 {
|
||||
u3::new(self.tseg2.value() - 1)
|
||||
}
|
||||
|
||||
pub fn prescaler_reg_value(&self) -> u7 {
|
||||
u7::new(self.prescaler.value() - 2)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Can {
|
||||
regs: regs::MmioCan<'static>,
|
||||
id: CanId,
|
||||
}
|
||||
|
||||
impl Can {
|
||||
pub fn new<CanI: Instance>(_can: CanI, clk_config: ClockConfig) -> Self {
|
||||
enable_peripheral_clock(CanI::PERIPH_SEL);
|
||||
let id = CanI::ID;
|
||||
let mut regs = if id == CanId::Can0 {
|
||||
unsafe { regs::Can::new_mmio_fixed_0() }
|
||||
} else {
|
||||
unsafe { regs::Can::new_mmio_fixed_1() }
|
||||
};
|
||||
// Disable the CAN bus before configuring it.
|
||||
regs.write_control(Control::new_with_raw_value(0));
|
||||
for i in 0..15 {
|
||||
regs.msg_buf_block_mut(i).reset();
|
||||
}
|
||||
regs.write_timing(
|
||||
TimingConfig::builder()
|
||||
.with_tseg2(clk_config.tseg2_reg_value())
|
||||
.with_tseg1(clk_config.tseg1_reg_value())
|
||||
.with_sync_jump_width(clk_config.sjw_reg_value())
|
||||
.with_prescaler(clk_config.prescaler_reg_value())
|
||||
.build(),
|
||||
);
|
||||
Self { regs, id }
|
||||
}
|
||||
|
||||
/// This configures the global mask so that acceptance is only determined by an exact match
|
||||
/// with the ID in the receive message buffers. This is the default reset configuration for
|
||||
/// the global mask as well.
|
||||
pub fn set_global_mask_for_exact_id_match(&mut self) {
|
||||
self.regs.write_gmskx(ExtendedId::new_with_raw_value(0));
|
||||
self.regs.write_gmskb(BaseId::new_with_raw_value(0));
|
||||
}
|
||||
|
||||
/// Similar to [Self::set_global_mask_for_exact_id_match] but masks the XRTR and RTR/SRR bits.
|
||||
///
|
||||
/// This is useful for when transmitting remote frames with the RTR bit set. The hardware
|
||||
/// will automatically go into the [regs::BufferState::RxReady] state after the transmission,
|
||||
/// but the XRTR and RTR/SRR bits need to be masked for the response frame to be accepted
|
||||
/// on that buffer.
|
||||
pub fn set_global_mask_for_exact_id_match_with_rtr_masked(&mut self) {
|
||||
self.regs.write_gmskx(
|
||||
ExtendedId::builder()
|
||||
.with_mask_14_0(u15::new(0))
|
||||
.with_xrtr(true)
|
||||
.build(),
|
||||
);
|
||||
self.regs.write_gmskb(
|
||||
BaseId::builder()
|
||||
.with_mask_28_18(u11::new(0))
|
||||
.with_rtr_or_srr(true)
|
||||
.with_ide(false)
|
||||
.with_mask_17_15(u3::new(0))
|
||||
.build(),
|
||||
);
|
||||
}
|
||||
|
||||
/// This configures the base mask for buffer 14 so that acceptance is only determined by an
|
||||
/// exact match with the ID in the receive message buffers. This is the default reset
|
||||
/// configuration for the global mask as well.
|
||||
pub fn set_base_mask_for_exact_id_match(&mut self) {
|
||||
self.regs.write_bmskx(ExtendedId::new_with_raw_value(0));
|
||||
self.regs.write_bmskb(BaseId::new_with_raw_value(0));
|
||||
}
|
||||
|
||||
/// This configures the base mask so that all CAN frames which are not handled by any other
|
||||
/// buffers are accepted by the base buffer 14.
|
||||
pub fn set_base_mask_for_all_match(&mut self) {
|
||||
self.regs
|
||||
.write_bmskx(ExtendedId::new_with_raw_value(0xffff));
|
||||
self.regs.write_bmskb(BaseId::new_with_raw_value(0xffff));
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn regs(&mut self) -> &mut MmioCan<'static> {
|
||||
&mut self.regs
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn id(&self) -> CanId {
|
||||
self.id
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn write_ctrl_reg(&mut self, ctrl: Control) {
|
||||
self.regs.write_control(ctrl);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn enable_bufflock(&mut self) {
|
||||
self.regs.modify_control(|mut ctrl| {
|
||||
ctrl.set_bufflock(true);
|
||||
ctrl
|
||||
});
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn enable(&mut self) {
|
||||
self.regs.modify_control(|mut ctrl| {
|
||||
ctrl.set_enable(true);
|
||||
ctrl
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum ChannelState {
|
||||
Idle,
|
||||
Receiving,
|
||||
Transmitting,
|
||||
AwaitingRtrReply,
|
||||
}
|
||||
|
||||
pub struct CanChannel {
|
||||
can_id: CanId,
|
||||
idx: usize,
|
||||
regs: regs::MmioCanMsgBuf<'static>,
|
||||
mode: ChannelState,
|
||||
}
|
||||
|
||||
impl core::fmt::Debug for CanChannel {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||
f.debug_struct("CanChannel")
|
||||
.field("can_id", &self.can_id)
|
||||
.field("idx", &self.idx)
|
||||
.field("mode", &self.mode)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl CanChannel {
|
||||
pub fn configure_for_reception_with_standard_id(
|
||||
&mut self,
|
||||
standard_id: embedded_can::StandardId,
|
||||
set_rtr: bool,
|
||||
) -> Result<(), InvalidBufferIndexError> {
|
||||
let mut id1_reg = standard_id.as_raw() << 5;
|
||||
if set_rtr {
|
||||
id1_reg |= 1 << 4;
|
||||
}
|
||||
self.regs
|
||||
.write_id1(BaseId::new_with_raw_value(id1_reg as u32));
|
||||
|
||||
self.regs.write_stat_ctrl(
|
||||
BufStatusAndControl::builder()
|
||||
.with_dlc(u4::new(0))
|
||||
.with_priority(u4::new(0))
|
||||
.with_status(regs::BufferState::RxReady)
|
||||
.build(),
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn configure_for_reception_with_extended_id(
|
||||
&mut self,
|
||||
extended_id: embedded_can::ExtendedId,
|
||||
set_rtr: bool,
|
||||
) -> Result<(), InvalidBufferIndexError> {
|
||||
let mut regs = unsafe { self.can_id.steal_regs() };
|
||||
let mut cmb_block = regs.msg_buf_block_mut(self.idx);
|
||||
let id_raw = extended_id.as_raw();
|
||||
let id1_reg = (((id_raw >> 18) & 0x7FF) << 4) as u16 | ((id_raw >> 15) & 0b111) as u16;
|
||||
cmb_block.write_id1(BaseId::new_with_raw_value(id1_reg as u32));
|
||||
let id0_reg = ((id_raw & 0x7FFF) << 1) as u16 | set_rtr as u16;
|
||||
cmb_block.write_id0(ExtendedId::new_with_raw_value(id0_reg as u32));
|
||||
cmb_block.write_stat_ctrl(
|
||||
BufStatusAndControl::builder()
|
||||
.with_dlc(u4::new(0))
|
||||
.with_priority(u4::new(0))
|
||||
.with_status(regs::BufferState::RxReady)
|
||||
.build(),
|
||||
);
|
||||
self.mode = ChannelState::Receiving;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn configure_for_transmission(
|
||||
&mut self,
|
||||
tx_priority: u4,
|
||||
) -> Result<(), InvalidBufferIndexError> {
|
||||
let mut regs = unsafe { self.can_id.steal_regs() };
|
||||
let mut cmb_block = regs.msg_buf_block_mut(self.idx);
|
||||
cmb_block.write_stat_ctrl(
|
||||
BufStatusAndControl::builder()
|
||||
.with_dlc(u4::new(0))
|
||||
.with_priority(tx_priority)
|
||||
.with_status(regs::BufferState::TxNotActive)
|
||||
.build(),
|
||||
);
|
||||
self.mode = ChannelState::Receiving;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Reads a received CAN frame from the message buffer.
|
||||
///
|
||||
/// This function does not check whether the pre-requisites for reading a CAN frame were
|
||||
/// met and assumes this was already checked by the user.
|
||||
pub fn read_frame_unchecked(&self) -> CanFrame {
|
||||
let id0 = self.regs.read_id0();
|
||||
let id1 = self.regs.read_id1();
|
||||
let data0 = self.regs.read_data0();
|
||||
let data1 = self.regs.read_data1();
|
||||
let data2 = self.regs.read_data2();
|
||||
let data3 = self.regs.read_data3();
|
||||
let mut data: [u8; 8] = [0; 8];
|
||||
let mut read_data = |dlc: u4| {
|
||||
(0..dlc.as_usize()).for_each(|i| match i {
|
||||
0 => data[i] = data3.data_upper_byte().as_u8(),
|
||||
1 => data[i] = data3.data_lower_byte().as_u8(),
|
||||
2 => data[i] = data2.data_upper_byte().as_u8(),
|
||||
3 => data[i] = data2.data_lower_byte().as_u8(),
|
||||
4 => data[i] = data1.data_upper_byte().as_u8(),
|
||||
5 => data[i] = data1.data_lower_byte().as_u8(),
|
||||
6 => data[i] = data0.data_upper_byte().as_u8(),
|
||||
7 => data[i] = data0.data_lower_byte().as_u8(),
|
||||
_ => unreachable!(),
|
||||
});
|
||||
};
|
||||
let (id, rtr) = if !id1.ide() {
|
||||
let id = embedded_can::Id::Standard(
|
||||
embedded_can::StandardId::new(id1.mask_28_18().as_u16()).unwrap(),
|
||||
);
|
||||
if id1.rtr_or_srr() {
|
||||
(id, true)
|
||||
} else {
|
||||
(id, false)
|
||||
}
|
||||
} else {
|
||||
let id_raw = (id1.mask_28_18().as_u32() << 18)
|
||||
| (id1.mask_17_15().as_u32() << 15)
|
||||
| id0.mask_14_0().as_u32();
|
||||
let id = embedded_can::Id::Extended(embedded_can::ExtendedId::new(id_raw).unwrap());
|
||||
if id0.xrtr() {
|
||||
(id, true)
|
||||
} else {
|
||||
(id, false)
|
||||
}
|
||||
};
|
||||
if rtr {
|
||||
CanFrameRtr::new(id, self.regs.read_stat_ctrl().dlc().as_usize()).into()
|
||||
} else {
|
||||
let dlc = self.regs.read_stat_ctrl().dlc();
|
||||
read_data(dlc);
|
||||
CanFrameNormal::new(id, &data[0..dlc.as_usize()]).into()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn transmit_frame_unchecked(&mut self, frame: CanFrame) {
|
||||
let is_remote = frame.is_remote_frame();
|
||||
self.write_id(frame.id(), is_remote);
|
||||
let dlc = frame.dlc();
|
||||
self.regs.modify_stat_ctrl(|mut ctrl| {
|
||||
ctrl.set_status(regs::BufferState::TxOnce);
|
||||
ctrl
|
||||
});
|
||||
}
|
||||
|
||||
fn write_id(&mut self, id: embedded_can::Id, is_remote: bool) {
|
||||
match id {
|
||||
embedded_can::Id::Standard(standard_id) => {
|
||||
self.regs.write_id1(
|
||||
BaseId::builder()
|
||||
.with_mask_28_18(u11::new(standard_id.as_raw()))
|
||||
.with_rtr_or_srr(is_remote)
|
||||
.with_ide(false)
|
||||
.with_mask_17_15(u3::new(0))
|
||||
.build(),
|
||||
);
|
||||
self.regs.write_id0(ExtendedId::new_with_raw_value(0));
|
||||
}
|
||||
embedded_can::Id::Extended(extended_id) => {
|
||||
let id_raw = extended_id.as_raw();
|
||||
self.regs.write_id1(
|
||||
BaseId::builder()
|
||||
.with_mask_28_18(u11::new(((id_raw >> 18) & 0x7FF) as u16))
|
||||
.with_rtr_or_srr(true)
|
||||
.with_ide(true)
|
||||
.with_mask_17_15(u3::new(((id_raw >> 15) & 0b111) as u8))
|
||||
.build(),
|
||||
);
|
||||
self.regs.write_id0(
|
||||
ExtendedId::builder()
|
||||
.with_mask_14_0(u15::new((id_raw & 0x7FFF) as u16))
|
||||
.with_xrtr(is_remote)
|
||||
.build(),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct CanWorker {
|
||||
can: Can,
|
||||
channels: [CanChannel; 15],
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
#[cfg(feature = "alloc")]
|
||||
use std::println;
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
#[test]
|
||||
pub fn test_clock_calculator_example_1() {
|
||||
let configs = super::calculate_all_viable_clock_configs(
|
||||
crate::time::Hertz::from_raw(50_000_000),
|
||||
crate::time::Hertz::from_raw(25_000),
|
||||
0.75,
|
||||
)
|
||||
.expect("clock calculation failed");
|
||||
// Bitrate: 25278.05 Hz. Sample point: 0.7391
|
||||
assert_eq!(configs[0].prescaler, 84);
|
||||
assert_eq!(configs[0].tseg1, 16);
|
||||
assert_eq!(configs[0].tseg2, 6);
|
||||
assert_eq!(configs[0].sjw, 4);
|
||||
// Vorago sample value.
|
||||
let sample_cfg = configs
|
||||
.iter()
|
||||
.find(|c| c.prescaler == 100)
|
||||
.expect("clock config not found");
|
||||
// Slightly different distribution because we use a different sample point, but
|
||||
// the sum of TSEG1 and TSEG2 is the same as the Vorago example 1.
|
||||
assert_eq!(sample_cfg.tseg1, 14);
|
||||
assert_eq!(sample_cfg.tseg2, 5);
|
||||
}
|
||||
}
|
349
va416xx-hal/src/can/regs.rs
Normal file
349
va416xx-hal/src/can/regs.rs
Normal file
@ -0,0 +1,349 @@
|
||||
//! Custom register definitions for the CAN register block to circumvent PAC API / SVD
|
||||
//! shortcomings.
|
||||
|
||||
use arbitrary_int::{u11, u15, u2, u3, u4, u7};
|
||||
|
||||
pub const CAN_0_BASE: usize = 0x4001_4000;
|
||||
pub const CAN_1_BASE: usize = 0x4001_4400;
|
||||
|
||||
#[derive(Debug)]
|
||||
#[bitbybit::bitenum(u4)]
|
||||
pub enum BufferState {
|
||||
/// Passive channel.
|
||||
RxNotActive = 0b0000,
|
||||
/// This condition indicated that SW wrote RxNotActive to a buffer when a data copy
|
||||
/// process is still active.
|
||||
RxBusy = 0b0001,
|
||||
RxReady = 0b0010,
|
||||
/// Indicated that data is being copied for the first time (RxRead -> RxBusy0).
|
||||
RxBusy0 = 0b0011,
|
||||
RxFull = 0b0100,
|
||||
/// Indicated that data is being copied for the second time (RxFull -> RxBusy2).
|
||||
RxBusy1 = 0b0101,
|
||||
RxOverrun = 0b0110,
|
||||
RxBusy2 = 0b0111,
|
||||
TxNotActive = 0b1000,
|
||||
/// Automatical response to a remote frame.
|
||||
TxRtr = 0b1010,
|
||||
/// Transmit one frame.
|
||||
TxOnce = 0b1100,
|
||||
TxBusy0 = 0b1101,
|
||||
/// Transmit one frame, and changes to TxRtr after that. This can either be written by
|
||||
/// software, or it will be written by the hardware after an auto response of the
|
||||
/// [BufferState::TxRtr] state.
|
||||
TxOnceRtr = 0b1110,
|
||||
TxBusy2 = 0b1111,
|
||||
}
|
||||
|
||||
/// Status control register for individual message buffers.
|
||||
#[bitbybit::bitfield(u32, default = 0x0)]
|
||||
#[derive(Debug)]
|
||||
pub struct BufStatusAndControl {
|
||||
/// Data length code.
|
||||
#[bits(12..=15, rw)]
|
||||
dlc: u4,
|
||||
#[bits(4..=7, rw)]
|
||||
priority: u4,
|
||||
#[bits(0..=3, rw)]
|
||||
status: Option<BufferState>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Timestamp(arbitrary_int::UInt<u32, 16>);
|
||||
|
||||
impl Timestamp {
|
||||
pub fn new(value: u16) -> Self {
|
||||
Self(value.into())
|
||||
}
|
||||
pub fn value(&self) -> u16 {
|
||||
self.0.value() as u16
|
||||
}
|
||||
|
||||
pub fn write(&mut self, value: u16) {
|
||||
self.0 = value.into();
|
||||
}
|
||||
}
|
||||
|
||||
#[bitbybit::bitfield(u32, default = 0x0)]
|
||||
#[derive(Debug)]
|
||||
pub struct TwoBytesData {
|
||||
#[bits(8..=15, rw)]
|
||||
data_upper_byte: u8,
|
||||
#[bits(8..=15, rw)]
|
||||
data_lower_byte: u8,
|
||||
}
|
||||
|
||||
#[derive(derive_mmio::Mmio)]
|
||||
#[repr(C)]
|
||||
pub struct CanMsgBuf {
|
||||
stat_ctrl: BufStatusAndControl,
|
||||
timestamp: Timestamp,
|
||||
data3: TwoBytesData,
|
||||
data2: TwoBytesData,
|
||||
data1: TwoBytesData,
|
||||
data0: TwoBytesData,
|
||||
id0: ExtendedId,
|
||||
id1: BaseId,
|
||||
}
|
||||
|
||||
impl MmioCanMsgBuf<'_> {
|
||||
pub fn reset(&mut self) {
|
||||
self.write_stat_ctrl(BufStatusAndControl::new_with_raw_value(0));
|
||||
self.write_timestamp(Timestamp::new(0));
|
||||
self.write_data3(TwoBytesData::new_with_raw_value(0));
|
||||
self.write_data2(TwoBytesData::new_with_raw_value(0));
|
||||
self.write_data1(TwoBytesData::new_with_raw_value(0));
|
||||
self.write_data0(TwoBytesData::new_with_raw_value(0));
|
||||
self.write_id1(BaseId::new_with_raw_value(0));
|
||||
self.write_id0(ExtendedId::new_with_raw_value(0));
|
||||
}
|
||||
}
|
||||
|
||||
#[bitbybit::bitenum(u1, exhaustive = true)]
|
||||
#[derive(Debug)]
|
||||
pub enum PinLogicLevel {
|
||||
DominantIsZero = 0b0,
|
||||
DominantIsOne = 0b1,
|
||||
}
|
||||
|
||||
#[bitbybit::bitenum(u1, exhaustive = true)]
|
||||
#[derive(Debug)]
|
||||
pub enum ErrorInterruptType {
|
||||
/// EIPND bit is set on every error.
|
||||
EveryError = 0b0,
|
||||
/// EIPND bit is only set if error state changes as a result of a receive or transmit
|
||||
/// error counter increment.
|
||||
ErrorOnRxTxCounterChange = 0b1,
|
||||
}
|
||||
|
||||
#[bitbybit::bitenum(u1, exhaustive = true)]
|
||||
#[derive(Debug)]
|
||||
pub enum DataDirection {
|
||||
FirstByteAtHighestAddr = 0b0,
|
||||
LastByteAtHighestAddr = 0b1,
|
||||
}
|
||||
|
||||
#[bitbybit::bitfield(u32)]
|
||||
pub struct Control {
|
||||
#[bit(11, rw)]
|
||||
error_interrupt_type: ErrorInterruptType,
|
||||
/// Enables special diagnostics features of the CAN like LO, IGNACK, LOOPBACK, INTERNAL.
|
||||
#[bit(10, rw)]
|
||||
diag_enable: bool,
|
||||
/// CANTX and CANRX pins are internally connected to each other.
|
||||
#[bit(9, rw)]
|
||||
internal: bool,
|
||||
/// All messages sent by the CAN controller can also be received by a CAN buffer with a
|
||||
/// matching buffer ID.
|
||||
#[bit(8, rw)]
|
||||
loopback: bool,
|
||||
/// IGNACK feature. The CAN does not expect to receive an ACK bit.
|
||||
#[bit(7, rw)]
|
||||
ignore_ack: bool,
|
||||
/// LO feature. The CAN is only configured as a receiver.
|
||||
#[bit(6, rw)]
|
||||
listen_only: bool,
|
||||
#[bit(5, rw)]
|
||||
data_dir: DataDirection,
|
||||
#[bit(4, rw)]
|
||||
timestamp_enable: bool,
|
||||
#[bit(3, rw)]
|
||||
bufflock: bool,
|
||||
#[bit(2, rw)]
|
||||
tx_logic_level: PinLogicLevel,
|
||||
#[bit(1, rw)]
|
||||
rx_logic_level: PinLogicLevel,
|
||||
#[bit(0, rw)]
|
||||
enable: bool,
|
||||
}
|
||||
|
||||
#[bitbybit::bitfield(u32, default = 0x0)]
|
||||
#[derive(Debug)]
|
||||
pub struct TimingConfig {
|
||||
#[bits(0..=2, rw)]
|
||||
tseg2: u3,
|
||||
#[bits(3..=6, rw)]
|
||||
tseg1: u4,
|
||||
#[bits(7..=8, rw)]
|
||||
sync_jump_width: u2,
|
||||
#[bits(9..=15, rw)]
|
||||
prescaler: u7,
|
||||
}
|
||||
|
||||
#[bitbybit::bitfield(u32)]
|
||||
#[derive(Debug)]
|
||||
pub struct InterruptEnable {
|
||||
#[bit(15, rw)]
|
||||
error: bool,
|
||||
#[bit(0, rw)]
|
||||
buffer: [bool; 15],
|
||||
}
|
||||
|
||||
#[bitbybit::bitfield(u32)]
|
||||
#[derive(Debug)]
|
||||
pub struct InterruptClear {
|
||||
#[bit(15, w)]
|
||||
error: bool,
|
||||
#[bit(0, w)]
|
||||
buffer: [bool; 15],
|
||||
}
|
||||
|
||||
#[bitbybit::bitfield(u32)]
|
||||
#[derive(Debug)]
|
||||
pub struct InterruptPending {
|
||||
#[bit(15, r)]
|
||||
error: bool,
|
||||
#[bit(0, r)]
|
||||
buffer: [bool; 15],
|
||||
}
|
||||
|
||||
#[bitbybit::bitfield(u32)]
|
||||
#[derive(Debug)]
|
||||
pub struct ErrorCounter {
|
||||
#[bits(0..=7, r)]
|
||||
transmit: u8,
|
||||
#[bits(8..=15, r)]
|
||||
receive: u8,
|
||||
}
|
||||
|
||||
/// This register is unused for standard frames.
|
||||
#[bitbybit::bitfield(u32, default = 0x0)]
|
||||
#[derive(Debug)]
|
||||
pub struct ExtendedId {
|
||||
/// Mask for ID bits \[14:0\] of extended frames.
|
||||
#[bits(1..=15, rw)]
|
||||
mask_14_0: u15,
|
||||
/// CAN XRTR bit.
|
||||
#[bit(0, rw)]
|
||||
xrtr: bool,
|
||||
}
|
||||
|
||||
#[bitbybit::bitfield(u32, default = 0x0)]
|
||||
#[derive(Debug)]
|
||||
pub struct BaseId {
|
||||
/// This will contain ID\[10:0\] for standard frames and bits [28:18] for extended frames.
|
||||
#[bits(5..=15, rw)]
|
||||
mask_28_18: u11,
|
||||
/// This is the RTR bit for standard frames, and the SRR bit for extended frames.
|
||||
#[bit(4, rw)]
|
||||
rtr_or_srr: bool,
|
||||
/// Identifier extension bit.
|
||||
#[bit(3, rw)]
|
||||
ide: bool,
|
||||
/// Mask for ID bits \[17:15\] of extended frames.
|
||||
#[bits(0..=2, rw)]
|
||||
mask_17_15: u3,
|
||||
}
|
||||
|
||||
#[derive(derive_mmio::Mmio)]
|
||||
#[repr(C)]
|
||||
pub struct Can {
|
||||
#[mmio(inner)]
|
||||
cmb0: CanMsgBuf,
|
||||
#[mmio(inner)]
|
||||
cmb1: CanMsgBuf,
|
||||
#[mmio(inner)]
|
||||
cmb2: CanMsgBuf,
|
||||
#[mmio(inner)]
|
||||
cmb3: CanMsgBuf,
|
||||
#[mmio(inner)]
|
||||
cmb4: CanMsgBuf,
|
||||
#[mmio(inner)]
|
||||
cmb5: CanMsgBuf,
|
||||
#[mmio(inner)]
|
||||
cmb6: CanMsgBuf,
|
||||
#[mmio(inner)]
|
||||
cmb7: CanMsgBuf,
|
||||
#[mmio(inner)]
|
||||
cmb8: CanMsgBuf,
|
||||
#[mmio(inner)]
|
||||
cmb9: CanMsgBuf,
|
||||
#[mmio(inner)]
|
||||
cmb10: CanMsgBuf,
|
||||
#[mmio(inner)]
|
||||
cmb11: CanMsgBuf,
|
||||
#[mmio(inner)]
|
||||
cmb12: CanMsgBuf,
|
||||
#[mmio(inner)]
|
||||
cmb13: CanMsgBuf,
|
||||
// This CAN message buffer has different mask registers.
|
||||
#[mmio(inner)]
|
||||
cmb14: CanMsgBuf,
|
||||
/// Hidden CAN message buffer. Only allowed to be used internally by the peripheral.
|
||||
#[mmio(inner)]
|
||||
_hcmb: CanMsgBuf,
|
||||
control: Control,
|
||||
timing: TimingConfig,
|
||||
/// Global mask extension used for buffers 0 to 13.
|
||||
gmskx: ExtendedId,
|
||||
/// Global mask base used for buffers 0 to 13.
|
||||
gmskb: BaseId,
|
||||
/// Basic mask extension used for buffer 14.
|
||||
bmskx: ExtendedId,
|
||||
/// Basic mask base used for buffer 14.
|
||||
bmskb: BaseId,
|
||||
ien: InterruptEnable,
|
||||
#[mmio(PureRead)]
|
||||
ipnd: InterruptPending,
|
||||
#[mmio(Write)]
|
||||
iclr: InterruptClear,
|
||||
/// Interrupt Code Enable Register.
|
||||
icen: InterruptEnable,
|
||||
#[mmio(PureRead)]
|
||||
status_pending: u32,
|
||||
#[mmio(PureRead)]
|
||||
error_counter: ErrorCounter,
|
||||
#[mmio(PureRead)]
|
||||
diag: u32,
|
||||
#[mmio(PureRead)]
|
||||
timer: u32,
|
||||
}
|
||||
|
||||
impl Can {
|
||||
/// Create a new CAN MMIO instance for peripheral 0.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// This API can be used to potentially create a driver to the same peripheral structure
|
||||
/// from multiple threads. The user must ensure that concurrent accesses are safe and do not
|
||||
/// interfere with each other.
|
||||
pub const unsafe fn new_mmio_fixed_0() -> MmioCan<'static> {
|
||||
Self::new_mmio_at(CAN_0_BASE)
|
||||
}
|
||||
|
||||
/// Create a new CAN MMIO instance for peripheral 1.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// This API can be used to potentially create a driver to the same peripheral structure
|
||||
/// from multiple threads. The user must ensure that concurrent accesses are safe and do not
|
||||
/// interfere with each other.
|
||||
pub const unsafe fn new_mmio_fixed_1() -> MmioCan<'static> {
|
||||
Self::new_mmio_at(CAN_1_BASE)
|
||||
}
|
||||
}
|
||||
|
||||
impl MmioCan<'_> {
|
||||
// TODO: It would be nice if derive-mmio could generate this for us..
|
||||
pub fn msg_buf_block_mut(&mut self, idx: usize) -> MmioCanMsgBuf<'static> {
|
||||
assert!(idx < 15, "invalid index for CAN message buffer");
|
||||
match idx {
|
||||
0 => unsafe { self.steal_cmb0() },
|
||||
1 => unsafe { self.steal_cmb1() },
|
||||
2 => unsafe { self.steal_cmb2() },
|
||||
3 => unsafe { self.steal_cmb3() },
|
||||
4 => unsafe { self.steal_cmb4() },
|
||||
5 => unsafe { self.steal_cmb5() },
|
||||
6 => unsafe { self.steal_cmb6() },
|
||||
7 => unsafe { self.steal_cmb7() },
|
||||
8 => unsafe { self.steal_cmb8() },
|
||||
9 => unsafe { self.steal_cmb9() },
|
||||
10 => unsafe { self.steal_cmb10() },
|
||||
11 => unsafe { self.steal_cmb11() },
|
||||
12 => unsafe { self.steal_cmb12() },
|
||||
13 => unsafe { self.steal_cmb13() },
|
||||
14 => unsafe { self.steal_cmb14() },
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
@ -12,51 +12,13 @@
|
||||
//! - [UART example on the PEB1 board](https://egit.irs.uni-stuttgart.de/rust/va416xx-rs/src/branch/main/examples/simple/examples/uart.rs)
|
||||
#[cfg(not(feature = "va41628"))]
|
||||
use crate::adc::ADC_MAX_CLK;
|
||||
use crate::pac;
|
||||
use crate::{pac, PeripheralSelect, SyscfgExt as _};
|
||||
|
||||
use crate::time::Hertz;
|
||||
|
||||
pub const HBO_FREQ: Hertz = Hertz::from_raw(20_000_000);
|
||||
pub const XTAL_OSC_TSTART_MS: u32 = 15;
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub enum PeripheralSelect {
|
||||
Spi0 = 0,
|
||||
Spi1 = 1,
|
||||
Spi2 = 2,
|
||||
Spi3 = 3,
|
||||
Uart0 = 4,
|
||||
Uart1 = 5,
|
||||
Uart2 = 6,
|
||||
I2c0 = 7,
|
||||
I2c1 = 8,
|
||||
I2c2 = 9,
|
||||
Can0 = 10,
|
||||
Can1 = 11,
|
||||
Rng = 12,
|
||||
Adc = 13,
|
||||
Dac = 14,
|
||||
Dma = 15,
|
||||
Ebi = 16,
|
||||
Eth = 17,
|
||||
Spw = 18,
|
||||
Clkgen = 19,
|
||||
IrqRouter = 20,
|
||||
IoConfig = 21,
|
||||
Utility = 22,
|
||||
Watchdog = 23,
|
||||
PortA = 24,
|
||||
PortB = 25,
|
||||
PortC = 26,
|
||||
PortD = 27,
|
||||
PortE = 28,
|
||||
PortF = 29,
|
||||
PortG = 30,
|
||||
}
|
||||
|
||||
pub type PeripheralClock = PeripheralSelect;
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub enum FilterClkSel {
|
||||
@ -70,81 +32,6 @@ pub enum FilterClkSel {
|
||||
Clk7 = 7,
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn enable_peripheral_clock(syscfg: &mut pac::Sysconfig, clock: PeripheralSelect) {
|
||||
syscfg
|
||||
.peripheral_clk_enable()
|
||||
.modify(|r, w| unsafe { w.bits(r.bits() | (1 << clock as u8)) });
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn disable_peripheral_clock(syscfg: &mut pac::Sysconfig, clock: PeripheralSelect) {
|
||||
syscfg
|
||||
.peripheral_clk_enable()
|
||||
.modify(|r, w| unsafe { w.bits(r.bits() & !(1 << clock as u8)) });
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn assert_periph_reset(syscfg: &mut pac::Sysconfig, periph: PeripheralSelect) {
|
||||
syscfg
|
||||
.peripheral_reset()
|
||||
.modify(|r, w| unsafe { w.bits(r.bits() & !(1 << periph as u8)) });
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn deassert_periph_reset(syscfg: &mut pac::Sysconfig, periph: PeripheralSelect) {
|
||||
syscfg
|
||||
.peripheral_reset()
|
||||
.modify(|r, w| unsafe { w.bits(r.bits() | (1 << periph as u8)) });
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn assert_periph_reset_for_two_cycles(syscfg: &mut pac::Sysconfig, periph: PeripheralSelect) {
|
||||
assert_periph_reset(syscfg, periph);
|
||||
cortex_m::asm::nop();
|
||||
cortex_m::asm::nop();
|
||||
deassert_periph_reset(syscfg, periph);
|
||||
}
|
||||
|
||||
pub trait SyscfgExt {
|
||||
fn enable_peripheral_clock(&mut self, clock: PeripheralClock);
|
||||
|
||||
fn disable_peripheral_clock(&mut self, clock: PeripheralClock);
|
||||
|
||||
fn assert_periph_reset(&mut self, periph: PeripheralSelect);
|
||||
|
||||
fn deassert_periph_reset(&mut self, periph: PeripheralSelect);
|
||||
|
||||
fn assert_periph_reset_for_two_cycles(&mut self, periph: PeripheralSelect);
|
||||
}
|
||||
|
||||
impl SyscfgExt for pac::Sysconfig {
|
||||
#[inline(always)]
|
||||
fn enable_peripheral_clock(&mut self, clock: PeripheralClock) {
|
||||
enable_peripheral_clock(self, clock)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn disable_peripheral_clock(&mut self, clock: PeripheralClock) {
|
||||
disable_peripheral_clock(self, clock)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn assert_periph_reset(&mut self, clock: PeripheralSelect) {
|
||||
assert_periph_reset(self, clock)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn deassert_periph_reset(&mut self, clock: PeripheralSelect) {
|
||||
deassert_periph_reset(self, clock)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn assert_periph_reset_for_two_cycles(&mut self, periph: PeripheralSelect) {
|
||||
assert_periph_reset_for_two_cycles(self, periph)
|
||||
}
|
||||
}
|
||||
|
||||
/// Refer to chapter 8 (p.57) of the programmers guide for detailed information.
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
@ -509,16 +396,45 @@ impl Clocks {
|
||||
}
|
||||
|
||||
/// Returns the frequency of the APB0 which is equal to the system clock.
|
||||
///
|
||||
/// This clock is the reference clock for the following peripherals:
|
||||
///
|
||||
/// - Ethernet
|
||||
/// - SpaceWire
|
||||
/// - IRQ Router
|
||||
/// - DMA
|
||||
/// - Clock Generator
|
||||
pub const fn apb0(&self) -> Hertz {
|
||||
self.sysclk()
|
||||
}
|
||||
|
||||
/// Returns system clock divied by 2.
|
||||
///
|
||||
/// This clock is the reference clock for the following peripherals:
|
||||
///
|
||||
/// - Timer[15:0]
|
||||
/// - UART2
|
||||
/// - SPI
|
||||
/// - I2C
|
||||
/// - CAN
|
||||
/// - GPIO
|
||||
/// - IOCONFIG
|
||||
/// - System Config
|
||||
pub const fn apb1(&self) -> Hertz {
|
||||
self.apb1
|
||||
}
|
||||
|
||||
/// Returns system clock divied by 4.
|
||||
///
|
||||
/// This clock is the reference clock for the following peripherals:
|
||||
///
|
||||
/// - Timer[23:16]
|
||||
/// - TRNG
|
||||
/// - UART[1:0]
|
||||
/// - DAC
|
||||
/// - ADC
|
||||
/// - Watchdog
|
||||
/// - Utility
|
||||
pub const fn apb2(&self) -> Hertz {
|
||||
self.apb2
|
||||
}
|
||||
|
@ -5,10 +5,7 @@
|
||||
//! - [ADC and DAC example](https://github.com/us-irs/va416xx-rs/blob/main/examples/simple/examples/dac-adc.rs)
|
||||
use core::ops::Deref;
|
||||
|
||||
use crate::{
|
||||
clock::{Clocks, PeripheralSelect, SyscfgExt},
|
||||
pac,
|
||||
};
|
||||
use crate::{clock::Clocks, pac, PeripheralSelect, SyscfgExt as _};
|
||||
|
||||
pub type DacRegisterBlock = pac::dac0::RegisterBlock;
|
||||
|
||||
|
@ -3,11 +3,9 @@
|
||||
//! ## Examples
|
||||
//!
|
||||
//! - [Simple DMA example](https://egit.irs.uni-stuttgart.de/rust/va416xx-rs/src/branch/main/examples/simple/examples/dma.rs)
|
||||
use crate::{
|
||||
clock::{PeripheralClock, PeripheralSelect},
|
||||
enable_nvic_interrupt, pac,
|
||||
prelude::*,
|
||||
};
|
||||
use arbitrary_int::{u10, u2, u3, u4};
|
||||
|
||||
use crate::{enable_nvic_interrupt, pac, PeripheralClock, PeripheralSelect, SyscfgExt as _};
|
||||
|
||||
const MAX_DMA_TRANSFERS_PER_CYCLE: usize = 1024;
|
||||
const BASE_PTR_ADDR_MASK: u32 = 0b1111111;
|
||||
@ -82,34 +80,30 @@ pub enum RPower {
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub struct InvalidCtrlBlockAddrError;
|
||||
|
||||
bitfield::bitfield! {
|
||||
#[repr(transparent)]
|
||||
#[derive(Clone, Copy)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub struct ChannelConfig(u32);
|
||||
impl Debug;
|
||||
u32;
|
||||
pub raw, set_raw: 31,0;
|
||||
u8;
|
||||
pub dst_inc, set_dst_inc: 31, 30;
|
||||
u8;
|
||||
pub dst_size, set_dst_size: 29, 28;
|
||||
u8;
|
||||
pub src_inc, set_src_inc: 27, 26;
|
||||
u8;
|
||||
pub src_size, set_src_size: 25, 24;
|
||||
u8;
|
||||
pub dest_prot_ctrl, set_dest_prot_ctrl: 23, 21;
|
||||
u8;
|
||||
pub src_prot_ctrl, set_src_prot_ctrl: 20, 18;
|
||||
u8;
|
||||
pub r_power, set_r_power: 17, 14;
|
||||
u16;
|
||||
pub n_minus_1, set_n_minus_1: 13, 4;
|
||||
bool;
|
||||
pub next_useburst, set_next_useburst: 3;
|
||||
u8;
|
||||
pub cycle_ctrl, set_cycle_ctr: 2, 0;
|
||||
#[bitbybit::bitfield(u32)]
|
||||
#[derive(Debug)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub struct ChannelConfig {
|
||||
#[bits(30..=31, rw)]
|
||||
dest_inc: u2,
|
||||
#[bits(28..=29, rw)]
|
||||
dest_size: u2,
|
||||
#[bits(26..=27, rw)]
|
||||
src_inc: u2,
|
||||
#[bits(24..=25, rw)]
|
||||
src_size: u2,
|
||||
#[bits(21..=23, rw)]
|
||||
dest_prot_ctrl: u3,
|
||||
#[bits(18..=20, rw)]
|
||||
src_prot_ctrl: u3,
|
||||
#[bits(14..=17, rw)]
|
||||
r_power: u4,
|
||||
#[bits(4..=13, rw)]
|
||||
n_minus_1: u10,
|
||||
#[bit(3, rw)]
|
||||
next_useburst: bool,
|
||||
#[bits(0..=2, rw)]
|
||||
cycle_ctrl: u3,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
@ -127,7 +121,7 @@ impl DmaChannelControl {
|
||||
Self {
|
||||
src_end_ptr: 0,
|
||||
dest_end_ptr: 0,
|
||||
cfg: ChannelConfig(0),
|
||||
cfg: ChannelConfig::new_with_raw_value(0),
|
||||
padding: 0,
|
||||
}
|
||||
}
|
||||
@ -428,20 +422,30 @@ impl DmaChannel {
|
||||
return Err(DmaTransferInitError::TransferSizeTooLarge(source.len()));
|
||||
}
|
||||
let len = source.len() - 1;
|
||||
self.ch_ctrl_pri.cfg.set_raw(0);
|
||||
self.ch_ctrl_pri.cfg = ChannelConfig::new_with_raw_value(0);
|
||||
self.ch_ctrl_pri.src_end_ptr = (source.as_ptr() as u32)
|
||||
.checked_add(len as u32)
|
||||
.ok_or(DmaTransferInitError::AddrOverflow)?;
|
||||
self.ch_ctrl_pri.dest_end_ptr = dest as u32;
|
||||
self.ch_ctrl_pri
|
||||
.cfg
|
||||
.set_cycle_ctr(CycleControl::Basic as u8);
|
||||
self.ch_ctrl_pri.cfg.set_src_size(DataSize::Byte as u8);
|
||||
self.ch_ctrl_pri.cfg.set_src_inc(AddrIncrement::Byte as u8);
|
||||
self.ch_ctrl_pri.cfg.set_dst_size(DataSize::Byte as u8);
|
||||
self.ch_ctrl_pri.cfg.set_dst_inc(AddrIncrement::None as u8);
|
||||
self.ch_ctrl_pri.cfg.set_n_minus_1(len as u16);
|
||||
self.ch_ctrl_pri.cfg.set_r_power(RPower::Every8 as u8);
|
||||
.set_cycle_ctrl(u3::new(CycleControl::Basic as u8));
|
||||
self.ch_ctrl_pri
|
||||
.cfg
|
||||
.set_src_size(u2::new(DataSize::Byte as u8));
|
||||
self.ch_ctrl_pri
|
||||
.cfg
|
||||
.set_src_inc(u2::new(AddrIncrement::Byte as u8));
|
||||
self.ch_ctrl_pri
|
||||
.cfg
|
||||
.set_dest_size(u2::new(DataSize::Byte as u8));
|
||||
self.ch_ctrl_pri
|
||||
.cfg
|
||||
.set_dest_inc(u2::new(AddrIncrement::None as u8));
|
||||
self.ch_ctrl_pri.cfg.set_n_minus_1(u10::new(len as u16));
|
||||
self.ch_ctrl_pri
|
||||
.cfg
|
||||
.set_r_power(u4::new(RPower::Every8 as u8));
|
||||
self.select_primary_structure();
|
||||
Ok(())
|
||||
}
|
||||
@ -470,16 +474,22 @@ impl DmaChannel {
|
||||
data_size: DataSize,
|
||||
addr_incr: AddrIncrement,
|
||||
) {
|
||||
self.ch_ctrl_pri.cfg.set_raw(0);
|
||||
self.ch_ctrl_pri.cfg = ChannelConfig::new_with_raw_value(0);
|
||||
self.ch_ctrl_pri.src_end_ptr = src_end_ptr;
|
||||
self.ch_ctrl_pri.dest_end_ptr = dest_end_ptr;
|
||||
self.ch_ctrl_pri.cfg.set_cycle_ctr(CycleControl::Auto as u8);
|
||||
self.ch_ctrl_pri.cfg.set_src_size(data_size as u8);
|
||||
self.ch_ctrl_pri.cfg.set_src_inc(addr_incr as u8);
|
||||
self.ch_ctrl_pri.cfg.set_dst_size(data_size as u8);
|
||||
self.ch_ctrl_pri.cfg.set_dst_inc(addr_incr as u8);
|
||||
self.ch_ctrl_pri.cfg.set_n_minus_1(n_minus_one as u16);
|
||||
self.ch_ctrl_pri.cfg.set_r_power(RPower::Every4 as u8);
|
||||
self.ch_ctrl_pri
|
||||
.cfg
|
||||
.set_cycle_ctrl(u3::new(CycleControl::Auto as u8));
|
||||
self.ch_ctrl_pri.cfg.set_src_size(u2::new(data_size as u8));
|
||||
self.ch_ctrl_pri.cfg.set_src_inc(u2::new(addr_incr as u8));
|
||||
self.ch_ctrl_pri.cfg.set_dest_size(u2::new(data_size as u8));
|
||||
self.ch_ctrl_pri.cfg.set_dest_inc(u2::new(addr_incr as u8));
|
||||
self.ch_ctrl_pri
|
||||
.cfg
|
||||
.set_n_minus_1(u10::new(n_minus_one as u16));
|
||||
self.ch_ctrl_pri
|
||||
.cfg
|
||||
.set_r_power(u4::new(RPower::Every4 as u8));
|
||||
self.select_primary_structure();
|
||||
}
|
||||
}
|
||||
|
@ -14,7 +14,7 @@
|
||||
//! Instances of [`DynPin`] cannot be created directly. Rather, they must be
|
||||
//! created from their type-level equivalents using [`From`]/[`Into`].
|
||||
//!
|
||||
//! ```
|
||||
//! ```ignore
|
||||
//! // Move a pin out of the Pins struct and convert to a DynPin
|
||||
//! let pa0: DynPin = pins.pa0.into();
|
||||
//! ```
|
||||
@ -22,7 +22,7 @@
|
||||
//! Conversions between pin modes use a value-level version of the type-level
|
||||
//! API.
|
||||
//!
|
||||
//! ```
|
||||
//! ```ignore
|
||||
//! // Use one of the literal function names
|
||||
//! pa0.into_floating_input();
|
||||
//! // Use a method and a DynPinMode variant
|
||||
@ -38,7 +38,7 @@
|
||||
//! guarantee the pin has the correct ID or is in the correct mode at
|
||||
//! compile-time. Use [TryFrom]/[TryInto] for this conversion.
|
||||
//!
|
||||
//! ```
|
||||
//! ```ignore
|
||||
//! // Convert to a `DynPin`
|
||||
//! let pa0: DynPin = pins.pa0.into();
|
||||
//! // Change pin mode
|
||||
|
@ -19,7 +19,7 @@
|
||||
//! Type-level [`Pin`]s are parameterized by two type-level enums, [`PinId`] and
|
||||
//! [`PinMode`].
|
||||
//!
|
||||
//! ```
|
||||
//! ```ignore
|
||||
//! pub struct Pin<I, M>
|
||||
//! where
|
||||
//! I: PinId,
|
||||
@ -49,14 +49,14 @@
|
||||
//! within the [PinsA] struct can be moved out and used individually.
|
||||
//!
|
||||
//!
|
||||
//! ```no_run
|
||||
//! ```no_run,ignore
|
||||
//! let mut peripherals = Peripherals::take().unwrap();
|
||||
//! let pinsa = PinsA::new(peripherals.porta);
|
||||
//! ```
|
||||
//!
|
||||
//! Pins can be converted between modes using several different methods.
|
||||
//!
|
||||
//! ```no_run
|
||||
//! ```no_run,ignore
|
||||
//! // Use one of the literal function names
|
||||
//! let pa0 = pinsa.pa0.into_floating_input();
|
||||
//! // Use a generic method and one of the `PinMode` variant types
|
||||
|
@ -4,11 +4,7 @@
|
||||
//!
|
||||
//! - [PEB1 accelerometer example](https://egit.irs.uni-stuttgart.de/rust/va416xx-rs/src/branch/main/examples/simple/examples/peb1-accelerometer.rs)
|
||||
use crate::{
|
||||
clock::{Clocks, PeripheralSelect},
|
||||
pac,
|
||||
prelude::SyscfgExt,
|
||||
time::Hertz,
|
||||
typelevel::Sealed,
|
||||
clock::Clocks, enable_peripheral_clock, pac, time::Hertz, typelevel::Sealed, PeripheralSelect,
|
||||
};
|
||||
use core::{marker::PhantomData, ops::Deref};
|
||||
use embedded_hal::i2c::{self, Operation, SevenBitAddress, TenBitAddress};
|
||||
@ -324,13 +320,12 @@ impl<I2C> I2cBase<I2C> {
|
||||
impl<I2c: Instance> I2cBase<I2c> {
|
||||
pub fn new(
|
||||
i2c: I2c,
|
||||
syscfg: &mut pac::Sysconfig,
|
||||
clocks: &Clocks,
|
||||
speed_mode: I2cSpeed,
|
||||
ms_cfg: Option<&MasterConfig>,
|
||||
sl_cfg: Option<&SlaveConfig>,
|
||||
) -> Result<Self, ClockTooSlowForFastI2cError> {
|
||||
syscfg.enable_peripheral_clock(I2c::PERIPH_SEL);
|
||||
enable_peripheral_clock(I2c::PERIPH_SEL);
|
||||
|
||||
let mut i2c_base = I2cBase {
|
||||
i2c,
|
||||
@ -482,13 +477,12 @@ pub struct I2cMaster<I2c, Addr = SevenBitAddress> {
|
||||
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, ClockTooSlowForFastI2cError> {
|
||||
Ok(I2cMaster {
|
||||
i2c_base: I2cBase::new(i2c, sys_cfg, clocks, speed_mode, Some(&cfg), None)?,
|
||||
i2c_base: I2cBase::new(i2c, clocks, speed_mode, Some(&cfg), None)?,
|
||||
addr: PhantomData,
|
||||
}
|
||||
.enable_master())
|
||||
@ -743,13 +737,12 @@ pub struct I2cSlave<I2c, Addr = SevenBitAddress> {
|
||||
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, ClockTooSlowForFastI2cError> {
|
||||
Ok(I2cSlave {
|
||||
i2c_base: I2cBase::new(i2c, sys_cfg, clocks, speed_mode, None, Some(&cfg))?,
|
||||
i2c_base: I2cBase::new(i2c, clocks, speed_mode, None, Some(&cfg))?,
|
||||
addr: PhantomData,
|
||||
}
|
||||
.enable_slave())
|
||||
@ -890,7 +883,6 @@ 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,
|
||||
@ -898,18 +890,17 @@ impl<I2c: Instance> I2cSlave<I2c, SevenBitAddress> {
|
||||
if let I2cAddress::TenBit(_) = cfg.addr {
|
||||
return Err(InitError::WrongAddrMode);
|
||||
}
|
||||
Ok(Self::new_generic(i2c, sys_cfg, cfg, clocks, speed_mode)?)
|
||||
Ok(Self::new_generic(i2c, 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, ClockTooSlowForFastI2cError> {
|
||||
Self::new_generic(i2c, sys_cfg, cfg, clocks, speed_mode)
|
||||
Self::new_generic(i2c, cfg, clocks, speed_mode)
|
||||
}
|
||||
}
|
||||
|
@ -1,8 +1,5 @@
|
||||
//! IRQ Router peripheral support.
|
||||
use crate::{
|
||||
clock::{PeripheralSelect, SyscfgExt},
|
||||
pac,
|
||||
};
|
||||
use crate::{pac, PeripheralSelect, SyscfgExt as _};
|
||||
|
||||
/// This enables and initiates the peripheral.
|
||||
///
|
||||
|
@ -26,6 +26,8 @@
|
||||
//! faulty register reset values which might lead to weird bugs and glitches.
|
||||
#![no_std]
|
||||
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
|
||||
#[cfg(feature = "alloc")]
|
||||
extern crate alloc;
|
||||
#[cfg(test)]
|
||||
extern crate std;
|
||||
|
||||
@ -35,6 +37,7 @@ pub use va416xx as pac;
|
||||
|
||||
pub mod prelude;
|
||||
|
||||
pub mod can;
|
||||
pub mod clock;
|
||||
pub mod dma;
|
||||
pub mod edac;
|
||||
@ -57,6 +60,84 @@ pub mod adc;
|
||||
#[cfg(not(feature = "va41628"))]
|
||||
pub mod dac;
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub enum PeripheralSelect {
|
||||
Spi0 = 0,
|
||||
Spi1 = 1,
|
||||
Spi2 = 2,
|
||||
Spi3 = 3,
|
||||
Uart0 = 4,
|
||||
Uart1 = 5,
|
||||
Uart2 = 6,
|
||||
I2c0 = 7,
|
||||
I2c1 = 8,
|
||||
I2c2 = 9,
|
||||
Can0 = 10,
|
||||
Can1 = 11,
|
||||
Rng = 12,
|
||||
Adc = 13,
|
||||
Dac = 14,
|
||||
Dma = 15,
|
||||
Ebi = 16,
|
||||
Eth = 17,
|
||||
Spw = 18,
|
||||
Clkgen = 19,
|
||||
IrqRouter = 20,
|
||||
IoConfig = 21,
|
||||
Utility = 22,
|
||||
Watchdog = 23,
|
||||
PortA = 24,
|
||||
PortB = 25,
|
||||
PortC = 26,
|
||||
PortD = 27,
|
||||
PortE = 28,
|
||||
PortF = 29,
|
||||
PortG = 30,
|
||||
}
|
||||
|
||||
pub type PeripheralClock = PeripheralSelect;
|
||||
|
||||
#[inline(always)]
|
||||
pub fn enable_peripheral_clock(clock: PeripheralSelect) {
|
||||
// Safety: Only bit of peripheral is modified.
|
||||
unsafe { pac::Sysconfig::steal() }
|
||||
.peripheral_clk_enable()
|
||||
.modify(|r, w| unsafe { w.bits(r.bits() | (1 << clock as u8)) });
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn disable_peripheral_clock(clock: PeripheralSelect) {
|
||||
// Safety: Only bit of peripheral is modified.
|
||||
unsafe { pac::Sysconfig::steal() }
|
||||
.peripheral_clk_enable()
|
||||
.modify(|r, w| unsafe { w.bits(r.bits() & !(1 << clock as u8)) });
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn assert_periph_reset(periph: PeripheralSelect) {
|
||||
// Safety: Only reset bit of peripheral is modified.
|
||||
unsafe { pac::Sysconfig::steal() }
|
||||
.peripheral_reset()
|
||||
.modify(|r, w| unsafe { w.bits(r.bits() & !(1 << periph as u8)) });
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn deassert_periph_reset(periph: PeripheralSelect) {
|
||||
// Safety: Only rest bit of peripheral is modified.
|
||||
unsafe { pac::Sysconfig::steal() }
|
||||
.peripheral_reset()
|
||||
.modify(|r, w| unsafe { w.bits(r.bits() | (1 << periph as u8)) });
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn assert_periph_reset_for_two_cycles(periph: PeripheralSelect) {
|
||||
assert_periph_reset(periph);
|
||||
cortex_m::asm::nop();
|
||||
cortex_m::asm::nop();
|
||||
deassert_periph_reset(periph);
|
||||
}
|
||||
|
||||
#[derive(Debug, Eq, Copy, Clone, PartialEq)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub enum FunSel {
|
||||
@ -100,6 +181,45 @@ pub fn port_function_select(
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub trait SyscfgExt {
|
||||
fn enable_peripheral_clock(&mut self, clock: PeripheralClock);
|
||||
|
||||
fn disable_peripheral_clock(&mut self, clock: PeripheralClock);
|
||||
|
||||
fn assert_periph_reset(&mut self, periph: PeripheralSelect);
|
||||
|
||||
fn deassert_periph_reset(&mut self, periph: PeripheralSelect);
|
||||
|
||||
fn assert_periph_reset_for_two_cycles(&mut self, periph: PeripheralSelect);
|
||||
}
|
||||
|
||||
impl SyscfgExt for pac::Sysconfig {
|
||||
#[inline(always)]
|
||||
fn enable_peripheral_clock(&mut self, clock: PeripheralClock) {
|
||||
enable_peripheral_clock(clock)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn disable_peripheral_clock(&mut self, clock: PeripheralClock) {
|
||||
disable_peripheral_clock(clock)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn assert_periph_reset(&mut self, clock: PeripheralSelect) {
|
||||
assert_periph_reset(clock)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn deassert_periph_reset(&mut self, clock: PeripheralSelect) {
|
||||
deassert_periph_reset(clock)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn assert_periph_reset_for_two_cycles(&mut self, periph: PeripheralSelect) {
|
||||
assert_periph_reset_for_two_cycles(periph)
|
||||
}
|
||||
}
|
||||
|
||||
/// Enable a specific interrupt using the NVIC peripheral.
|
||||
///
|
||||
/// # Safety
|
||||
|
@ -1,4 +1,5 @@
|
||||
//! Prelude
|
||||
pub use crate::clock::{ClkgenExt, SyscfgExt};
|
||||
pub use crate::clock::ClkgenExt;
|
||||
pub use crate::SyscfgExt;
|
||||
pub use fugit::ExtU32 as _;
|
||||
pub use fugit::RateExtU32 as _;
|
||||
|
@ -12,7 +12,7 @@ use core::{convert::Infallible, marker::PhantomData, ops::Deref};
|
||||
use embedded_hal::spi::{Mode, MODE_0};
|
||||
|
||||
use crate::{
|
||||
clock::{Clocks, PeripheralSelect, SyscfgExt},
|
||||
clock::Clocks,
|
||||
gpio::{
|
||||
AltFunc1, AltFunc2, AltFunc3, Pin, PA0, PA1, PA2, PA3, PA4, PA5, PA6, PA7, PA8, PA9, PB0,
|
||||
PB1, PB12, PB13, PB14, PB15, PB2, PB3, PB4, PC0, PC1, PC10, PC11, PC7, PC8, PC9, PE12,
|
||||
@ -21,6 +21,7 @@ use crate::{
|
||||
pac,
|
||||
time::Hertz,
|
||||
typelevel::{NoneT, Sealed},
|
||||
PeripheralSelect,
|
||||
};
|
||||
|
||||
#[cfg(not(feature = "va41628"))]
|
||||
@ -1040,15 +1041,14 @@ where
|
||||
/// to be done once.
|
||||
/// * `syscfg` - Can be passed optionally to enable the peripheral clock
|
||||
pub fn new(
|
||||
syscfg: &mut pac::Sysconfig,
|
||||
clocks: &crate::clock::Clocks,
|
||||
spi: SpiI,
|
||||
pins: (Sck, Miso, Mosi),
|
||||
spi_cfg: SpiConfig,
|
||||
) -> Self {
|
||||
crate::clock::enable_peripheral_clock(syscfg, SpiI::PERIPH_SEL);
|
||||
crate::enable_peripheral_clock(SpiI::PERIPH_SEL);
|
||||
// This is done in the C HAL.
|
||||
syscfg.assert_periph_reset_for_two_cycles(SpiI::PERIPH_SEL);
|
||||
crate::assert_periph_reset_for_two_cycles(SpiI::PERIPH_SEL);
|
||||
let SpiConfig {
|
||||
clk,
|
||||
init_mode,
|
||||
|
@ -15,10 +15,10 @@ use core::ops::Deref;
|
||||
use embedded_hal_nb::serial::Read;
|
||||
use fugit::RateExtU32;
|
||||
|
||||
use crate::clock::{Clocks, PeripheralSelect, SyscfgExt};
|
||||
use crate::clock::Clocks;
|
||||
use crate::gpio::PF13;
|
||||
use crate::time::Hertz;
|
||||
use crate::{disable_nvic_interrupt, enable_nvic_interrupt};
|
||||
use crate::{disable_nvic_interrupt, enable_nvic_interrupt, PeripheralSelect};
|
||||
use crate::{
|
||||
gpio::{
|
||||
AltFunc1, AltFunc2, AltFunc3, Pin, PA2, PA3, PB14, PB15, PC14, PC4, PC5, PD11, PD12, PE2,
|
||||
@ -38,6 +38,21 @@ pub enum Bank {
|
||||
Uart2 = 2,
|
||||
}
|
||||
|
||||
impl Bank {
|
||||
/// Retrieve the peripheral register block.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// Circumvents the HAL safety guarantees.
|
||||
pub const unsafe fn reg_block(&self) -> &'static uart_base::RegisterBlock {
|
||||
match self {
|
||||
Bank::Uart0 => unsafe { &(*pac::Uart0::ptr()) },
|
||||
Bank::Uart1 => unsafe { &(*pac::Uart1::ptr()) },
|
||||
Bank::Uart2 => unsafe { &(*pac::Uart2::ptr()) },
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//==================================================================================================
|
||||
// Type-Level support
|
||||
//==================================================================================================
|
||||
@ -348,7 +363,7 @@ pub struct BufferTooShortError {
|
||||
|
||||
pub trait Instance: Deref<Target = uart_base::RegisterBlock> {
|
||||
const IDX: u8;
|
||||
const PERIPH_SEL: PeripheralSelect;
|
||||
const PERIPH_SEL: crate::PeripheralSelect;
|
||||
const PTR: *const uart_base::RegisterBlock;
|
||||
const IRQ_RX: pac::Interrupt;
|
||||
const IRQ_TX: pac::Interrupt;
|
||||
@ -421,21 +436,6 @@ impl Instance for Uart2 {
|
||||
}
|
||||
}
|
||||
|
||||
impl Bank {
|
||||
/// Retrieve the peripheral register block.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// Circumvents the HAL safety guarantees.
|
||||
pub unsafe fn reg_block(&self) -> &'static uart_base::RegisterBlock {
|
||||
match self {
|
||||
Bank::Uart0 => unsafe { pac::Uart0::reg_block() },
|
||||
Bank::Uart1 => unsafe { pac::Uart1::reg_block() },
|
||||
Bank::Uart2 => unsafe { pac::Uart2::reg_block() },
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//==================================================================================================
|
||||
// UART implementation
|
||||
//==================================================================================================
|
||||
@ -630,15 +630,14 @@ impl<TxPinInst: TxPin<UartInstance>, RxPinInst: RxPin<UartInstance>, UartInstanc
|
||||
Uart<UartInstance, (TxPinInst, RxPinInst)>
|
||||
{
|
||||
pub fn new(
|
||||
syscfg: &mut va416xx::Sysconfig,
|
||||
uart: UartInstance,
|
||||
pins: (TxPinInst, RxPinInst),
|
||||
config: impl Into<Config>,
|
||||
clocks: &Clocks,
|
||||
) -> Self {
|
||||
crate::clock::enable_peripheral_clock(syscfg, UartInstance::PERIPH_SEL);
|
||||
crate::enable_peripheral_clock(UartInstance::PERIPH_SEL);
|
||||
// This is done in the C HAL.
|
||||
syscfg.assert_periph_reset_for_two_cycles(UartInstance::PERIPH_SEL);
|
||||
crate::assert_periph_reset_for_two_cycles(UartInstance::PERIPH_SEL);
|
||||
Uart {
|
||||
inner: UartBase {
|
||||
uart,
|
||||
@ -651,13 +650,12 @@ impl<TxPinInst: TxPin<UartInstance>, RxPinInst: RxPin<UartInstance>, UartInstanc
|
||||
}
|
||||
|
||||
pub fn new_with_clock_freq(
|
||||
syscfg: &mut va416xx::Sysconfig,
|
||||
uart: UartInstance,
|
||||
pins: (TxPinInst, RxPinInst),
|
||||
config: impl Into<Config>,
|
||||
clock: impl Into<Hertz>,
|
||||
) -> Self {
|
||||
crate::clock::enable_peripheral_clock(syscfg, UartInstance::PERIPH_SEL);
|
||||
crate::enable_peripheral_clock(UartInstance::PERIPH_SEL);
|
||||
Uart {
|
||||
inner: UartBase {
|
||||
uart,
|
||||
|
@ -4,12 +4,8 @@
|
||||
//!
|
||||
//! - [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::{
|
||||
clock::{Clocks, PeripheralSelect},
|
||||
pac,
|
||||
prelude::SyscfgExt,
|
||||
};
|
||||
use crate::{disable_nvic_interrupt, enable_nvic_interrupt};
|
||||
use crate::{clock::Clocks, pac};
|
||||
use crate::{disable_nvic_interrupt, enable_nvic_interrupt, PeripheralSelect, SyscfgExt as _};
|
||||
|
||||
pub const WDT_UNLOCK_VALUE: u32 = 0x1ACC_E551;
|
||||
|
||||
|
@ -16,7 +16,7 @@ categories = ["embedded", "no-std", "hardware-support"]
|
||||
cortex-m = "0.7"
|
||||
vcell = "0.1.3"
|
||||
|
||||
defmt = { version = "0.3", optional = true }
|
||||
defmt = { version = "1", optional = true }
|
||||
critical-section = { version = "1", optional = true }
|
||||
|
||||
[dependencies.cortex-m-rt]
|
||||
|
Loading…
x
Reference in New Issue
Block a user