finished basic ADC and DAC HAL #15
70
examples/simple/examples/adc.rs
Normal file
70
examples/simple/examples/adc.rs
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
//! Simple ADC example.
|
||||||
|
#![no_main]
|
||||||
|
#![no_std]
|
||||||
|
|
||||||
|
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::{
|
||||||
|
adc::{Adc, ChannelSelect, ChannelValue, MultiChannelSelect},
|
||||||
|
pac,
|
||||||
|
prelude::*,
|
||||||
|
timer::CountdownTimer,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Quite spammy and disabled by default.
|
||||||
|
const ENABLE_BUF_PRINTOUT: bool = false;
|
||||||
|
|
||||||
|
#[entry]
|
||||||
|
fn main() -> ! {
|
||||||
|
rtt_init_print!();
|
||||||
|
rprintln!("VA416xx ADC example");
|
||||||
|
|
||||||
|
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 adc = Adc::new_with_channel_tag(&mut dp.sysconfig, dp.adc, &clocks);
|
||||||
|
let mut delay_provider = CountdownTimer::new(&mut dp.sysconfig, dp.tim0, &clocks);
|
||||||
|
let mut read_buf: [ChannelValue; 8] = [ChannelValue::default(); 8];
|
||||||
|
loop {
|
||||||
|
let single_value = adc
|
||||||
|
.trigger_and_read_single_channel(va416xx_hal::adc::ChannelSelect::AnIn0)
|
||||||
|
.expect("reading single channel value failed");
|
||||||
|
rprintln!("Read single ADC value on channel 0: {:?}", single_value);
|
||||||
|
let read_num = adc
|
||||||
|
.sweep_and_read_range(0, 7, &mut read_buf)
|
||||||
|
.expect("ADC range read failed");
|
||||||
|
if ENABLE_BUF_PRINTOUT {
|
||||||
|
rprintln!("ADC Range Read (0-8) read {} values", read_num);
|
||||||
|
rprintln!("ADC Range Read (0-8): {:?}", read_buf);
|
||||||
|
}
|
||||||
|
assert_eq!(read_num, 8);
|
||||||
|
for (idx, ch_val) in read_buf.iter().enumerate() {
|
||||||
|
assert_eq!(
|
||||||
|
ch_val.channel(),
|
||||||
|
ChannelSelect::try_from(idx as u8).unwrap()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
adc.sweep_and_read_multiselect(
|
||||||
|
MultiChannelSelect::AnIn0 | MultiChannelSelect::AnIn2 | MultiChannelSelect::TempSensor,
|
||||||
|
&mut read_buf[0..3],
|
||||||
|
)
|
||||||
|
.expect("ADC multiselect read failed");
|
||||||
|
if ENABLE_BUF_PRINTOUT {
|
||||||
|
rprintln!("ADC Multiselect Read(0, 2 and 10): {:?}", &read_buf[0..3]);
|
||||||
|
}
|
||||||
|
assert_eq!(read_buf[0].channel(), ChannelSelect::AnIn0);
|
||||||
|
assert_eq!(read_buf[1].channel(), ChannelSelect::AnIn2);
|
||||||
|
assert_eq!(read_buf[2].channel(), ChannelSelect::TempSensor);
|
||||||
|
delay_provider.delay_ms(500);
|
||||||
|
}
|
||||||
|
}
|
@ -16,7 +16,6 @@ fn main() -> ! {
|
|||||||
let mut dp = pac::Peripherals::take().unwrap();
|
let mut dp = pac::Peripherals::take().unwrap();
|
||||||
let portg = PinsG::new(&mut dp.sysconfig, dp.portg);
|
let portg = PinsG::new(&mut dp.sysconfig, dp.portg);
|
||||||
let mut led = portg.pg5.into_readable_push_pull_output();
|
let mut led = portg.pg5.into_readable_push_pull_output();
|
||||||
//let mut delay = CountDownTimer::new(&mut dp.SYSCONFIG, 50.mhz(), dp.TIM0);
|
|
||||||
loop {
|
loop {
|
||||||
cortex_m::asm::delay(2_000_000);
|
cortex_m::asm::delay(2_000_000);
|
||||||
led.toggle().ok();
|
led.toggle().ok();
|
||||||
|
78
examples/simple/examples/dac-adc.rs
Normal file
78
examples/simple/examples/dac-adc.rs
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
//! Simple DAC-ADC example.
|
||||||
|
#![no_main]
|
||||||
|
#![no_std]
|
||||||
|
|
||||||
|
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::{adc::Adc, dac::Dac, pac, prelude::*, timer::CountdownTimer};
|
||||||
|
|
||||||
|
const DAC_INCREMENT: u16 = 256;
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
|
pub enum AppMode {
|
||||||
|
// Measurements on AIN0.
|
||||||
|
AdcOnly,
|
||||||
|
// AOUT0. You can use a multi-meter to measure the changing voltage on the pin.
|
||||||
|
DacOnly,
|
||||||
|
/// AOUT0 needs to be wired to AIN0.
|
||||||
|
DacAndAdc,
|
||||||
|
}
|
||||||
|
|
||||||
|
const APP_MODE: AppMode = AppMode::DacAndAdc;
|
||||||
|
|
||||||
|
#[entry]
|
||||||
|
fn main() -> ! {
|
||||||
|
rtt_init_print!();
|
||||||
|
rprintln!("VA416xx DAC/ADC example");
|
||||||
|
|
||||||
|
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 mut dac = None;
|
||||||
|
if APP_MODE == AppMode::DacOnly || APP_MODE == AppMode::DacAndAdc {
|
||||||
|
dac = Some(Dac::new(
|
||||||
|
&mut dp.sysconfig,
|
||||||
|
dp.dac0,
|
||||||
|
va416xx_hal::dac::DacSettling::Apb2Times100,
|
||||||
|
&clocks,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
let mut adc = None;
|
||||||
|
if APP_MODE == AppMode::AdcOnly || APP_MODE == AppMode::DacAndAdc {
|
||||||
|
adc = Some(Adc::new(&mut dp.sysconfig, dp.adc, &clocks));
|
||||||
|
}
|
||||||
|
let mut delay_provider = CountdownTimer::new(&mut dp.sysconfig, dp.tim0, &clocks);
|
||||||
|
let mut current_val = 0;
|
||||||
|
loop {
|
||||||
|
if let Some(dac) = &dac {
|
||||||
|
rprintln!("loading DAC with value {}", current_val);
|
||||||
|
dac.load_and_trigger_manually(current_val)
|
||||||
|
.expect("loading DAC value failed");
|
||||||
|
if current_val + DAC_INCREMENT >= 4096 {
|
||||||
|
current_val = 0;
|
||||||
|
} else {
|
||||||
|
current_val += DAC_INCREMENT;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let Some(dac) = &dac {
|
||||||
|
// This should never block.
|
||||||
|
nb::block!(dac.is_settled()).unwrap();
|
||||||
|
}
|
||||||
|
if let Some(adc) = &adc {
|
||||||
|
let ch_value = adc
|
||||||
|
.trigger_and_read_single_channel(va416xx_hal::adc::ChannelSelect::AnIn0)
|
||||||
|
.expect("reading ADC channel 0 failed");
|
||||||
|
rprintln!("Received channel value {:?}", ch_value);
|
||||||
|
}
|
||||||
|
|
||||||
|
delay_provider.delay_ms(500);
|
||||||
|
}
|
||||||
|
}
|
@ -22,7 +22,8 @@ variants:
|
|||||||
range:
|
range:
|
||||||
start: 0x0
|
start: 0x0
|
||||||
end: 0x40000
|
end: 0x40000
|
||||||
is_boot_memory: true
|
access:
|
||||||
|
boot: true
|
||||||
cores:
|
cores:
|
||||||
- main
|
- main
|
||||||
- !Generic
|
- !Generic
|
||||||
|
@ -17,7 +17,9 @@ paste = "1"
|
|||||||
embedded-hal-nb = "1"
|
embedded-hal-nb = "1"
|
||||||
embedded-hal = "1"
|
embedded-hal = "1"
|
||||||
embedded-io = "0.6"
|
embedded-io = "0.6"
|
||||||
|
num_enum = { version = "0.7", default-features = false }
|
||||||
typenum = "1"
|
typenum = "1"
|
||||||
|
bitflags = "2"
|
||||||
defmt = { version = "0.3", optional = true }
|
defmt = { version = "0.3", optional = true }
|
||||||
fugit = "0.3"
|
fugit = "0.3"
|
||||||
delegate = "0.12"
|
delegate = "0.12"
|
||||||
|
402
va416xx-hal/src/adc.rs
Normal file
402
va416xx-hal/src/adc.rs
Normal file
@ -0,0 +1,402 @@
|
|||||||
|
use core::marker::PhantomData;
|
||||||
|
|
||||||
|
use crate::clock::Clocks;
|
||||||
|
use crate::pac;
|
||||||
|
use crate::prelude::*;
|
||||||
|
use crate::time::Hertz;
|
||||||
|
use num_enum::{IntoPrimitive, TryFromPrimitive};
|
||||||
|
|
||||||
|
pub const ADC_MIN_CLK: Hertz = Hertz::from_raw(2_000_000);
|
||||||
|
pub const ADC_MAX_CLK: Hertz = Hertz::from_raw(12_500_000);
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq, Copy, Clone, TryFromPrimitive, IntoPrimitive)]
|
||||||
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
|
#[repr(u8)]
|
||||||
|
pub enum ChannelSelect {
|
||||||
|
/// Analogue Input 0 external channel
|
||||||
|
AnIn0 = 0,
|
||||||
|
/// Analogue Input 1 external channel
|
||||||
|
AnIn1 = 1,
|
||||||
|
/// Analogue Input 2 external channel
|
||||||
|
AnIn2 = 2,
|
||||||
|
/// Analogue Input 3 external channel
|
||||||
|
AnIn3 = 3,
|
||||||
|
/// Analogue Input 4 external channel
|
||||||
|
AnIn4 = 4,
|
||||||
|
/// Analogue Input 5 external channel
|
||||||
|
AnIn5 = 5,
|
||||||
|
/// Analogue Input 6 external channel
|
||||||
|
AnIn6 = 6,
|
||||||
|
/// Analogue Input 7 external channel
|
||||||
|
AnIn7 = 7,
|
||||||
|
/// DAC 0 internal channel
|
||||||
|
Dac0 = 8,
|
||||||
|
/// DAC 1 internal channel
|
||||||
|
Dac1 = 9,
|
||||||
|
/// Internal temperature sensor
|
||||||
|
TempSensor = 10,
|
||||||
|
/// Internal bandgap 1 V reference
|
||||||
|
Bandgap1V = 11,
|
||||||
|
/// Internal bandgap 1.5 V reference
|
||||||
|
Bandgap1_5V = 12,
|
||||||
|
Avdd1_5 = 13,
|
||||||
|
Dvdd1_5 = 14,
|
||||||
|
/// Internally generated Voltage equal to VREFH / 2
|
||||||
|
Vrefp5 = 15,
|
||||||
|
}
|
||||||
|
|
||||||
|
bitflags::bitflags! {
|
||||||
|
pub struct MultiChannelSelect: u16 {
|
||||||
|
const AnIn0 = 1;
|
||||||
|
const AnIn1 = 1 << 1;
|
||||||
|
const AnIn2 = 1 << 2;
|
||||||
|
const AnIn3 = 1 << 3;
|
||||||
|
const AnIn4 = 1 << 4;
|
||||||
|
const AnIn5 = 1 << 5;
|
||||||
|
const AnIn6 = 1 << 6;
|
||||||
|
const AnIn7 = 1 << 7;
|
||||||
|
const Dac0 = 1 << 8;
|
||||||
|
const Dac1 = 1 << 9;
|
||||||
|
const TempSensor = 1 << 10;
|
||||||
|
const Bandgap1V = 1 << 11;
|
||||||
|
const Bandgap1_5V = 1 << 12;
|
||||||
|
const Avdd1_5 = 1 << 13;
|
||||||
|
const Dvdd1_5 = 1 << 14;
|
||||||
|
const Vrefp5 = 1 << 15;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
|
||||||
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
|
pub struct AdcEmptyError;
|
||||||
|
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
|
||||||
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
|
pub struct InvalidChannelRangeError;
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
|
||||||
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
|
pub struct BufferTooSmallError;
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
|
||||||
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
|
pub enum AdcRangeReadError {
|
||||||
|
InvalidChannelRange(InvalidChannelRangeError),
|
||||||
|
BufferTooSmall(BufferTooSmallError),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<InvalidChannelRangeError> for AdcRangeReadError {
|
||||||
|
fn from(value: InvalidChannelRangeError) -> Self {
|
||||||
|
AdcRangeReadError::InvalidChannelRange(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<BufferTooSmallError> for AdcRangeReadError {
|
||||||
|
fn from(value: BufferTooSmallError) -> Self {
|
||||||
|
AdcRangeReadError::BufferTooSmall(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
|
||||||
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
|
pub struct ChannelValue {
|
||||||
|
/// If the channel tag is enabled, this field will contain the determined channel tag.
|
||||||
|
channel: ChannelSelect,
|
||||||
|
/// Raw value.
|
||||||
|
value: u16,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for ChannelValue {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
channel: ChannelSelect::AnIn0,
|
||||||
|
value: Default::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ChannelValue {
|
||||||
|
#[inline]
|
||||||
|
pub fn value(&self) -> u16 {
|
||||||
|
self.value
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn channel(&self) -> ChannelSelect {
|
||||||
|
self.channel
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum ChannelTagEnabled {}
|
||||||
|
pub enum ChannelTagDisabled {}
|
||||||
|
|
||||||
|
pub struct Adc<TagEnabled = ChannelTagDisabled> {
|
||||||
|
adc: pac::Adc,
|
||||||
|
phantom: PhantomData<TagEnabled>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Adc<ChannelTagEnabled> {}
|
||||||
|
|
||||||
|
impl Adc<ChannelTagDisabled> {
|
||||||
|
pub fn new(syscfg: &mut pac::Sysconfig, adc: pac::Adc, clocks: &Clocks) -> Self {
|
||||||
|
Self::generic_new(syscfg, adc, clocks)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn trigger_and_read_single_channel(&self, ch: ChannelSelect) -> Result<u16, AdcEmptyError> {
|
||||||
|
self.generic_trigger_and_read_single_channel(ch)
|
||||||
|
.map(|v| v & 0xfff)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Perform a sweep for a specified range of ADC channels.
|
||||||
|
///
|
||||||
|
/// Returns the number of read values which were written to the passed RX buffer.
|
||||||
|
pub fn sweep_and_read_range(
|
||||||
|
&self,
|
||||||
|
lower_bound_idx: u8,
|
||||||
|
upper_bound_idx: u8,
|
||||||
|
rx_buf: &mut [u16],
|
||||||
|
) -> Result<(), AdcRangeReadError> {
|
||||||
|
self.generic_prepare_range_sweep_and_wait_until_ready(
|
||||||
|
lower_bound_idx,
|
||||||
|
upper_bound_idx,
|
||||||
|
rx_buf.len(),
|
||||||
|
)?;
|
||||||
|
for i in 0..self.adc.status().read().fifo_entry_cnt().bits() {
|
||||||
|
rx_buf[i as usize] = self.adc.fifo_data().read().bits() as u16 & 0xfff;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn sweep_and_read_multiselect(
|
||||||
|
&self,
|
||||||
|
ch_select: MultiChannelSelect,
|
||||||
|
rx_buf: &mut [u16],
|
||||||
|
) -> Result<(), BufferTooSmallError> {
|
||||||
|
self.generic_prepare_multiselect_sweep_and_wait_until_ready(ch_select, rx_buf.len())?;
|
||||||
|
for i in 0..self.adc.status().read().fifo_entry_cnt().bits() {
|
||||||
|
rx_buf[i as usize] = self.adc.fifo_data().read().bits() as u16 & 0xfff;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn try_read_single_value(&self) -> nb::Result<Option<u16>, ()> {
|
||||||
|
self.generic_try_read_single_value()
|
||||||
|
.map(|v| v.map(|v| v & 0xfff))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Adc<ChannelTagEnabled> {
|
||||||
|
pub fn new_with_channel_tag(
|
||||||
|
syscfg: &mut pac::Sysconfig,
|
||||||
|
adc: pac::Adc,
|
||||||
|
clocks: &Clocks,
|
||||||
|
) -> Self {
|
||||||
|
let mut adc = Self::generic_new(syscfg, adc, clocks);
|
||||||
|
adc.enable_channel_tag();
|
||||||
|
adc
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn trigger_and_read_single_channel(
|
||||||
|
&self,
|
||||||
|
ch: ChannelSelect,
|
||||||
|
) -> Result<ChannelValue, AdcEmptyError> {
|
||||||
|
self.generic_trigger_and_read_single_channel(ch)
|
||||||
|
.map(|v| self.create_channel_value(v))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn try_read_single_value(&self) -> nb::Result<Option<ChannelValue>, ()> {
|
||||||
|
self.generic_try_read_single_value()
|
||||||
|
.map(|v| v.map(|v| self.create_channel_value(v)))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Perform a sweep for a specified range of ADC channels.
|
||||||
|
///
|
||||||
|
/// Returns the number of read values which were written to the passed RX buffer.
|
||||||
|
pub fn sweep_and_read_range(
|
||||||
|
&self,
|
||||||
|
lower_bound_idx: u8,
|
||||||
|
upper_bound_idx: u8,
|
||||||
|
rx_buf: &mut [ChannelValue],
|
||||||
|
) -> Result<usize, AdcRangeReadError> {
|
||||||
|
self.generic_prepare_range_sweep_and_wait_until_ready(
|
||||||
|
lower_bound_idx,
|
||||||
|
upper_bound_idx,
|
||||||
|
rx_buf.len(),
|
||||||
|
)?;
|
||||||
|
let fifo_entry_count = self.adc.status().read().fifo_entry_cnt().bits();
|
||||||
|
for i in 0..core::cmp::min(fifo_entry_count, rx_buf.len() as u8) {
|
||||||
|
rx_buf[i as usize] =
|
||||||
|
self.create_channel_value(self.adc.fifo_data().read().bits() as u16);
|
||||||
|
}
|
||||||
|
Ok(fifo_entry_count as usize)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn sweep_and_read_multiselect(
|
||||||
|
&self,
|
||||||
|
ch_select: MultiChannelSelect,
|
||||||
|
rx_buf: &mut [ChannelValue],
|
||||||
|
) -> Result<(), BufferTooSmallError> {
|
||||||
|
self.generic_prepare_multiselect_sweep_and_wait_until_ready(ch_select, rx_buf.len())?;
|
||||||
|
for i in 0..self.adc.status().read().fifo_entry_cnt().bits() {
|
||||||
|
rx_buf[i as usize] =
|
||||||
|
self.create_channel_value(self.adc.fifo_data().read().bits() as u16);
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn create_channel_value(&self, raw_value: u16) -> ChannelValue {
|
||||||
|
ChannelValue {
|
||||||
|
value: raw_value & 0xfff,
|
||||||
|
channel: ChannelSelect::try_from(((raw_value >> 12) & 0xf) as u8).unwrap(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
adc.ctrl().write(|w| unsafe { w.bits(0) });
|
||||||
|
let adc = Self {
|
||||||
|
adc,
|
||||||
|
phantom: PhantomData,
|
||||||
|
};
|
||||||
|
adc.clear_fifo();
|
||||||
|
adc
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn enable_channel_tag(&mut self) {
|
||||||
|
self.adc.ctrl().modify(|_, w| w.chan_tag_en().set_bit());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn disable_channel_tag(&mut self) {
|
||||||
|
self.adc.ctrl().modify(|_, w| w.chan_tag_en().clear_bit());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn channel_tag_enabled(&self) -> bool {
|
||||||
|
self.adc.ctrl().read().chan_tag_en().bit_is_set()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn clear_fifo(&self) {
|
||||||
|
self.adc.fifo_clr().write(|w| unsafe { w.bits(1) });
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn generic_try_read_single_value(&self) -> nb::Result<Option<u16>, ()> {
|
||||||
|
if self.adc.status().read().adc_busy().bit_is_set() {
|
||||||
|
return Err(nb::Error::WouldBlock);
|
||||||
|
}
|
||||||
|
if self.adc.status().read().fifo_entry_cnt().bits() == 0 {
|
||||||
|
return Ok(None);
|
||||||
|
}
|
||||||
|
Ok(Some(self.adc.fifo_data().read().bits() as u16))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn generic_trigger_single_channel(&self, ch: ChannelSelect) {
|
||||||
|
self.adc.ctrl().modify(|_, w| {
|
||||||
|
w.ext_trig_en().clear_bit();
|
||||||
|
unsafe {
|
||||||
|
// N + 1 conversions, so set set 0 here.
|
||||||
|
w.conv_cnt().bits(0);
|
||||||
|
w.chan_en().bits(1 << ch as u8)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
self.clear_fifo();
|
||||||
|
|
||||||
|
self.adc.ctrl().modify(|_, w| w.manual_trig().set_bit());
|
||||||
|
}
|
||||||
|
|
||||||
|
fn generic_prepare_range_sweep_and_wait_until_ready(
|
||||||
|
&self,
|
||||||
|
lower_bound_idx: u8,
|
||||||
|
upper_bound_idx: u8,
|
||||||
|
buf_len: usize,
|
||||||
|
) -> Result<(), AdcRangeReadError> {
|
||||||
|
if (lower_bound_idx > 15 || upper_bound_idx > 15) || lower_bound_idx > upper_bound_idx {
|
||||||
|
return Err(InvalidChannelRangeError.into());
|
||||||
|
}
|
||||||
|
let ch_count = upper_bound_idx - lower_bound_idx + 1;
|
||||||
|
if buf_len < ch_count as usize {
|
||||||
|
return Err(BufferTooSmallError.into());
|
||||||
|
}
|
||||||
|
let mut ch_select = 0;
|
||||||
|
for i in lower_bound_idx..upper_bound_idx + 1 {
|
||||||
|
ch_select |= 1 << i;
|
||||||
|
}
|
||||||
|
self.generic_trigger_sweep(ch_select);
|
||||||
|
cortex_m::asm::nop();
|
||||||
|
cortex_m::asm::nop();
|
||||||
|
while self.adc.status().read().adc_busy().bit_is_set() {
|
||||||
|
cortex_m::asm::nop();
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn generic_prepare_multiselect_sweep_and_wait_until_ready(
|
||||||
|
&self,
|
||||||
|
ch_select: MultiChannelSelect,
|
||||||
|
buf_len: usize,
|
||||||
|
) -> Result<(), BufferTooSmallError> {
|
||||||
|
let ch_select = ch_select.bits();
|
||||||
|
let ch_count = ch_select.count_ones();
|
||||||
|
if buf_len < ch_count as usize {
|
||||||
|
return Err(BufferTooSmallError);
|
||||||
|
}
|
||||||
|
self.generic_trigger_sweep(ch_select);
|
||||||
|
while self.adc.status().read().adc_busy().bit_is_set() {
|
||||||
|
cortex_m::asm::nop();
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn generic_trigger_sweep(&self, ch_select: u16) {
|
||||||
|
let ch_num = ch_select.count_ones() as u8;
|
||||||
|
assert!(ch_num > 0);
|
||||||
|
self.adc.ctrl().modify(|_, w| {
|
||||||
|
w.ext_trig_en().clear_bit();
|
||||||
|
unsafe {
|
||||||
|
// N + 1 conversions.
|
||||||
|
w.conv_cnt().bits(0);
|
||||||
|
w.chan_en().bits(ch_select);
|
||||||
|
w.sweep_en().set_bit()
|
||||||
|
}
|
||||||
|
});
|
||||||
|
self.clear_fifo();
|
||||||
|
|
||||||
|
self.adc.ctrl().modify(|_, w| w.manual_trig().set_bit());
|
||||||
|
}
|
||||||
|
|
||||||
|
fn generic_trigger_and_read_single_channel(
|
||||||
|
&self,
|
||||||
|
ch: ChannelSelect,
|
||||||
|
) -> Result<u16, AdcEmptyError> {
|
||||||
|
self.generic_trigger_single_channel(ch);
|
||||||
|
nb::block!(self.generic_try_read_single_value())
|
||||||
|
.unwrap()
|
||||||
|
.ok_or(AdcEmptyError)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Adc<ChannelTagDisabled>> for Adc<ChannelTagEnabled> {
|
||||||
|
fn from(value: Adc<ChannelTagDisabled>) -> Self {
|
||||||
|
let mut adc = Self {
|
||||||
|
adc: value.adc,
|
||||||
|
phantom: PhantomData,
|
||||||
|
};
|
||||||
|
adc.enable_channel_tag();
|
||||||
|
adc
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Adc<ChannelTagEnabled>> for Adc<ChannelTagDisabled> {
|
||||||
|
fn from(value: Adc<ChannelTagEnabled>) -> Self {
|
||||||
|
let mut adc = Self {
|
||||||
|
adc: value.adc,
|
||||||
|
phantom: PhantomData,
|
||||||
|
};
|
||||||
|
adc.disable_channel_tag();
|
||||||
|
adc
|
||||||
|
}
|
||||||
|
}
|
@ -10,6 +10,7 @@
|
|||||||
//! # Examples
|
//! # Examples
|
||||||
//!
|
//!
|
||||||
//! - [UART example on the PEB1 board](https://egit.irs.uni-stuttgart.de/rust/va416xx-rs/src/branch/main/examples/simple/examples/uart.rs)
|
//! - [UART example on the PEB1 board](https://egit.irs.uni-stuttgart.de/rust/va416xx-rs/src/branch/main/examples/simple/examples/uart.rs)
|
||||||
|
use crate::adc::ADC_MAX_CLK;
|
||||||
use crate::pac;
|
use crate::pac;
|
||||||
|
|
||||||
use crate::time::Hertz;
|
use crate::time::Hertz;
|
||||||
@ -52,7 +53,7 @@ pub enum PeripheralSelect {
|
|||||||
PortG = 30,
|
PortG = 30,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type PeripheralClocks = PeripheralSelect;
|
pub type PeripheralClock = PeripheralSelect;
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq)]
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
pub enum FilterClkSel {
|
pub enum FilterClkSel {
|
||||||
@ -94,24 +95,34 @@ pub fn deassert_periph_reset(syscfg: &mut pac::Sysconfig, periph: PeripheralSele
|
|||||||
.modify(|r, w| unsafe { w.bits(r.bits() | (1 << periph as u8)) });
|
.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 {
|
pub trait SyscfgExt {
|
||||||
fn enable_peripheral_clock(&mut self, clock: PeripheralClocks);
|
fn enable_peripheral_clock(&mut self, clock: PeripheralClock);
|
||||||
|
|
||||||
fn disable_peripheral_clock(&mut self, clock: PeripheralClocks);
|
fn disable_peripheral_clock(&mut self, clock: PeripheralClock);
|
||||||
|
|
||||||
fn assert_periph_reset(&mut self, clock: PeripheralSelect);
|
fn assert_periph_reset(&mut self, periph: PeripheralSelect);
|
||||||
|
|
||||||
fn deassert_periph_reset(&mut self, clock: 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 {
|
impl SyscfgExt for pac::Sysconfig {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn enable_peripheral_clock(&mut self, clock: PeripheralClocks) {
|
fn enable_peripheral_clock(&mut self, clock: PeripheralClock) {
|
||||||
enable_peripheral_clock(self, clock)
|
enable_peripheral_clock(self, clock)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn disable_peripheral_clock(&mut self, clock: PeripheralClocks) {
|
fn disable_peripheral_clock(&mut self, clock: PeripheralClock) {
|
||||||
disable_peripheral_clock(self, clock)
|
disable_peripheral_clock(self, clock)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -124,6 +135,11 @@ impl SyscfgExt for pac::Sysconfig {
|
|||||||
fn deassert_periph_reset(&mut self, clock: PeripheralSelect) {
|
fn deassert_periph_reset(&mut self, clock: PeripheralSelect) {
|
||||||
deassert_periph_reset(self, clock)
|
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.
|
/// Refer to chapter 8 (p.57) of the programmers guide for detailed information.
|
||||||
@ -435,20 +451,23 @@ impl ClkgenCfgr {
|
|||||||
// ADC clock (must be 2-12.5 MHz)
|
// ADC clock (must be 2-12.5 MHz)
|
||||||
// NOTE: Not using divide by 1 or /2 ratio in REVA silicon because of triggering issue
|
// NOTE: Not using divide by 1 or /2 ratio in REVA silicon because of triggering issue
|
||||||
// For this reason, keep SYSCLK above 8MHz to have the ADC /4 ratio in range)
|
// For this reason, keep SYSCLK above 8MHz to have the ADC /4 ratio in range)
|
||||||
if final_sysclk.raw() <= 50_000_000 {
|
let adc_clk = if final_sysclk.raw() <= ADC_MAX_CLK.raw() * 4 {
|
||||||
self.clkgen
|
self.clkgen
|
||||||
.ctrl1()
|
.ctrl1()
|
||||||
.modify(|_, w| unsafe { w.adc_clk_div_sel().bits(AdcClkDivSel::Div4 as u8) });
|
.modify(|_, w| unsafe { w.adc_clk_div_sel().bits(AdcClkDivSel::Div4 as u8) });
|
||||||
|
final_sysclk / 4
|
||||||
} else {
|
} else {
|
||||||
self.clkgen
|
self.clkgen
|
||||||
.ctrl1()
|
.ctrl1()
|
||||||
.modify(|_, w| unsafe { w.adc_clk_div_sel().bits(AdcClkDivSel::Div8 as u8) });
|
.modify(|_, w| unsafe { w.adc_clk_div_sel().bits(AdcClkDivSel::Div8 as u8) });
|
||||||
}
|
final_sysclk / 8
|
||||||
|
};
|
||||||
|
|
||||||
Ok(Clocks {
|
Ok(Clocks {
|
||||||
sysclk: final_sysclk,
|
sysclk: final_sysclk,
|
||||||
apb1: final_sysclk / 2,
|
apb1: final_sysclk / 2,
|
||||||
apb2: final_sysclk / 4,
|
apb2: final_sysclk / 4,
|
||||||
|
adc_clk,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -464,6 +483,7 @@ pub struct Clocks {
|
|||||||
sysclk: Hertz,
|
sysclk: Hertz,
|
||||||
apb1: Hertz,
|
apb1: Hertz,
|
||||||
apb2: Hertz,
|
apb2: Hertz,
|
||||||
|
adc_clk: Hertz,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Clocks {
|
impl Clocks {
|
||||||
@ -491,6 +511,11 @@ impl Clocks {
|
|||||||
pub fn sysclk(&self) -> Hertz {
|
pub fn sysclk(&self) -> Hertz {
|
||||||
self.sysclk
|
self.sysclk
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the ADC clock frequency which has a separate divider.
|
||||||
|
pub fn adc_clk(&self) -> Hertz {
|
||||||
|
self.adc_clk
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn rearm_sysclk_lost() {
|
pub fn rearm_sysclk_lost() {
|
||||||
|
157
va416xx-hal/src/dac.rs
Normal file
157
va416xx-hal/src/dac.rs
Normal file
@ -0,0 +1,157 @@
|
|||||||
|
use core::ops::Deref;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
clock::{Clocks, PeripheralSelect, SyscfgExt},
|
||||||
|
pac,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub type DacRegisterBlock = pac::dac0::RegisterBlock;
|
||||||
|
|
||||||
|
/// Common trait implemented by all PAC peripheral access structures. The register block
|
||||||
|
/// format is the same for all DAC blocks.
|
||||||
|
pub trait Instance: Deref<Target = DacRegisterBlock> {
|
||||||
|
const IDX: u8;
|
||||||
|
|
||||||
|
fn ptr() -> *const DacRegisterBlock;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Instance for pac::Dac0 {
|
||||||
|
const IDX: u8 = 0;
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn ptr() -> *const DacRegisterBlock {
|
||||||
|
Self::ptr()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Instance for pac::Dac1 {
|
||||||
|
const IDX: u8 = 1;
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn ptr() -> *const DacRegisterBlock {
|
||||||
|
Self::ptr()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
|
||||||
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
|
pub enum DacSettling {
|
||||||
|
NoSettling = 0,
|
||||||
|
Apb2Times25 = 1,
|
||||||
|
Apb2Times50 = 2,
|
||||||
|
Apb2Times75 = 3,
|
||||||
|
Apb2Times100 = 4,
|
||||||
|
Apb2Times125 = 5,
|
||||||
|
Apb2Times150 = 6,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Dac<DacInstance> {
|
||||||
|
dac: DacInstance,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
|
||||||
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
|
pub struct ValueTooLarge;
|
||||||
|
|
||||||
|
impl<DacInstance: Instance> Dac<DacInstance> {
|
||||||
|
/// Create a new [Dac] driver instance.
|
||||||
|
///
|
||||||
|
/// The [Clocks] structure is expected here as well to ensure the clock was set up properly.
|
||||||
|
pub fn new(
|
||||||
|
syscfg: &mut pac::Sysconfig,
|
||||||
|
dac: DacInstance,
|
||||||
|
dac_settling: DacSettling,
|
||||||
|
_clocks: &Clocks,
|
||||||
|
) -> Self {
|
||||||
|
syscfg.enable_peripheral_clock(PeripheralSelect::Dac);
|
||||||
|
|
||||||
|
dac.ctrl1().write(|w| {
|
||||||
|
w.dac_en().set_bit();
|
||||||
|
// SAFETY: Enum values are valid values only.
|
||||||
|
unsafe { w.dac_settling().bits(dac_settling as u8) }
|
||||||
|
});
|
||||||
|
let dac = Self { dac };
|
||||||
|
dac.clear_fifo();
|
||||||
|
dac.clear_irqs();
|
||||||
|
dac
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn clear_irqs(&self) {
|
||||||
|
self.dac.irq_clr().write(|w| {
|
||||||
|
w.fifo_oflow().set_bit();
|
||||||
|
w.fifo_uflow().set_bit();
|
||||||
|
w.dac_done().set_bit();
|
||||||
|
w.trig_error().set_bit()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn clear_fifo(&self) {
|
||||||
|
self.dac.fifo_clr().write(|w| unsafe { w.bits(1) });
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Load next value into the FIFO.
|
||||||
|
///
|
||||||
|
/// Uses the [nb] API to allow blocking and non-blocking usage.
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn load_value(&self, val: u16) -> nb::Result<(), ValueTooLarge> {
|
||||||
|
if val > 2_u16.pow(12) - 1 {
|
||||||
|
return Err(nb::Error::Other(ValueTooLarge));
|
||||||
|
}
|
||||||
|
if self.dac.status().read().fifo_entry_cnt().bits() >= 32_u8 {
|
||||||
|
return Err(nb::Error::WouldBlock);
|
||||||
|
}
|
||||||
|
self.dac
|
||||||
|
.fifo_data()
|
||||||
|
.write(|w| unsafe { w.bits(val.into()) });
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// This loads and triggers the next value immediately. It also clears the FIFO before
|
||||||
|
/// loading the passed value.
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn load_and_trigger_manually(&self, val: u16) -> Result<(), ValueTooLarge> {
|
||||||
|
if val > 2_u16.pow(12) - 1 {
|
||||||
|
return Err(ValueTooLarge);
|
||||||
|
}
|
||||||
|
self.clear_fifo();
|
||||||
|
// This should never block, the FIFO was cleared. We checked the value as well, so unwrap
|
||||||
|
// is okay here.
|
||||||
|
nb::block!(self.load_value(val)).unwrap();
|
||||||
|
self.trigger_manually();
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Manually trigger the DAC. This will de-queue the next value inside the FIFO
|
||||||
|
/// to be processed by the DAC.
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn trigger_manually(&self) {
|
||||||
|
self.dac.ctrl0().write(|w| w.man_trig_en().set_bit());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn enable_external_trigger(&self) {
|
||||||
|
self.dac.ctrl0().write(|w| w.ext_trig_en().set_bit());
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_settled(&self) -> nb::Result<(), ()> {
|
||||||
|
if self.dac.status().read().dac_busy().bit_is_set() {
|
||||||
|
return Err(nb::Error::WouldBlock);
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn reset(&mut self, syscfg: &mut pac::Sysconfig) {
|
||||||
|
syscfg.enable_peripheral_clock(PeripheralSelect::Dac);
|
||||||
|
syscfg.assert_periph_reset_for_two_cycles(PeripheralSelect::Dac);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Relases the DAC, which also disables its peripheral clock.
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn release(self, syscfg: &mut pac::Sysconfig) -> DacInstance {
|
||||||
|
syscfg.disable_peripheral_clock(PeripheralSelect::Dac);
|
||||||
|
self.dac
|
||||||
|
}
|
||||||
|
}
|
@ -4,11 +4,9 @@
|
|||||||
//!
|
//!
|
||||||
//! - [PEB1 accelerometer example](https://egit.irs.uni-stuttgart.de/rust/va416xx-rs/src/branch/main/examples/simple/examples/peb1-accelerometer.rs)
|
//! - [PEB1 accelerometer example](https://egit.irs.uni-stuttgart.de/rust/va416xx-rs/src/branch/main/examples/simple/examples/peb1-accelerometer.rs)
|
||||||
use crate::{
|
use crate::{
|
||||||
clock::{
|
clock::{Clocks, PeripheralSelect},
|
||||||
assert_periph_reset, deassert_periph_reset, enable_peripheral_clock, Clocks,
|
|
||||||
PeripheralSelect,
|
|
||||||
},
|
|
||||||
pac,
|
pac,
|
||||||
|
prelude::SyscfgExt,
|
||||||
time::Hertz,
|
time::Hertz,
|
||||||
typelevel::Sealed,
|
typelevel::Sealed,
|
||||||
};
|
};
|
||||||
@ -125,6 +123,7 @@ impl Instance for pac::I2c0 {
|
|||||||
const IDX: u8 = 0;
|
const IDX: u8 = 0;
|
||||||
const PERIPH_SEL: PeripheralSelect = PeripheralSelect::I2c0;
|
const PERIPH_SEL: PeripheralSelect = PeripheralSelect::I2c0;
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
fn ptr() -> *const I2cRegBlock {
|
fn ptr() -> *const I2cRegBlock {
|
||||||
Self::ptr()
|
Self::ptr()
|
||||||
}
|
}
|
||||||
@ -134,6 +133,7 @@ impl Instance for pac::I2c1 {
|
|||||||
const IDX: u8 = 1;
|
const IDX: u8 = 1;
|
||||||
const PERIPH_SEL: PeripheralSelect = PeripheralSelect::I2c1;
|
const PERIPH_SEL: PeripheralSelect = PeripheralSelect::I2c1;
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
fn ptr() -> *const I2cRegBlock {
|
fn ptr() -> *const I2cRegBlock {
|
||||||
Self::ptr()
|
Self::ptr()
|
||||||
}
|
}
|
||||||
@ -143,6 +143,7 @@ impl Instance for pac::I2c2 {
|
|||||||
const IDX: u8 = 2;
|
const IDX: u8 = 2;
|
||||||
const PERIPH_SEL: PeripheralSelect = PeripheralSelect::I2c2;
|
const PERIPH_SEL: PeripheralSelect = PeripheralSelect::I2c2;
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
fn ptr() -> *const I2cRegBlock {
|
fn ptr() -> *const I2cRegBlock {
|
||||||
Self::ptr()
|
Self::ptr()
|
||||||
}
|
}
|
||||||
@ -312,17 +313,13 @@ impl<I2C> I2cBase<I2C> {
|
|||||||
impl<I2c: Instance> I2cBase<I2c> {
|
impl<I2c: Instance> I2cBase<I2c> {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
i2c: I2c,
|
i2c: I2c,
|
||||||
sys_cfg: &mut pac::Sysconfig,
|
syscfg: &mut pac::Sysconfig,
|
||||||
clocks: &Clocks,
|
clocks: &Clocks,
|
||||||
speed_mode: I2cSpeed,
|
speed_mode: I2cSpeed,
|
||||||
ms_cfg: Option<&MasterConfig>,
|
ms_cfg: Option<&MasterConfig>,
|
||||||
sl_cfg: Option<&SlaveConfig>,
|
sl_cfg: Option<&SlaveConfig>,
|
||||||
) -> Result<Self, ClockTooSlowForFastI2c> {
|
) -> Result<Self, ClockTooSlowForFastI2c> {
|
||||||
enable_peripheral_clock(sys_cfg, I2c::PERIPH_SEL);
|
syscfg.enable_peripheral_clock(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 {
|
let mut i2c_base = I2cBase {
|
||||||
i2c,
|
i2c,
|
||||||
|
@ -8,7 +8,9 @@ pub use va416xx as pac;
|
|||||||
|
|
||||||
pub mod prelude;
|
pub mod prelude;
|
||||||
|
|
||||||
|
pub mod adc;
|
||||||
pub mod clock;
|
pub mod clock;
|
||||||
|
pub mod dac;
|
||||||
pub mod gpio;
|
pub mod gpio;
|
||||||
pub mod i2c;
|
pub mod i2c;
|
||||||
pub mod pwm;
|
pub mod pwm;
|
||||||
|
@ -8,7 +8,7 @@ use core::{convert::Infallible, marker::PhantomData, ops::Deref};
|
|||||||
use embedded_hal::spi::Mode;
|
use embedded_hal::spi::Mode;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
clock::PeripheralSelect,
|
clock::{PeripheralSelect, SyscfgExt},
|
||||||
gpio::{
|
gpio::{
|
||||||
AltFunc1, AltFunc2, AltFunc3, Pin, PA0, PA1, PA2, PA3, PA4, PA5, PA6, PA7, PA8, PA9, PB0,
|
AltFunc1, AltFunc2, AltFunc3, Pin, PA0, PA1, PA2, PA3, PA4, PA5, PA6, PA7, PA8, PA9, PB0,
|
||||||
PB1, PB10, PB11, PB12, PB13, PB14, PB15, PB2, PB3, PB4, PB5, PB6, PB7, PB8, PB9, PC0, PC1,
|
PB1, PB10, PB11, PB12, PB13, PB14, PB15, PB2, PB3, PB4, PB5, PB6, PB7, PB8, PB9, PC0, PC1,
|
||||||
@ -365,6 +365,7 @@ impl Instance for pac::Spi0 {
|
|||||||
const IDX: u8 = 0;
|
const IDX: u8 = 0;
|
||||||
const PERIPH_SEL: PeripheralSelect = PeripheralSelect::Spi0;
|
const PERIPH_SEL: PeripheralSelect = PeripheralSelect::Spi0;
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
fn ptr() -> *const SpiRegBlock {
|
fn ptr() -> *const SpiRegBlock {
|
||||||
Self::ptr()
|
Self::ptr()
|
||||||
}
|
}
|
||||||
@ -374,6 +375,7 @@ impl Instance for pac::Spi1 {
|
|||||||
const IDX: u8 = 1;
|
const IDX: u8 = 1;
|
||||||
const PERIPH_SEL: PeripheralSelect = PeripheralSelect::Spi1;
|
const PERIPH_SEL: PeripheralSelect = PeripheralSelect::Spi1;
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
fn ptr() -> *const SpiRegBlock {
|
fn ptr() -> *const SpiRegBlock {
|
||||||
Self::ptr()
|
Self::ptr()
|
||||||
}
|
}
|
||||||
@ -383,6 +385,7 @@ impl Instance for pac::Spi2 {
|
|||||||
const IDX: u8 = 2;
|
const IDX: u8 = 2;
|
||||||
const PERIPH_SEL: PeripheralSelect = PeripheralSelect::Spi2;
|
const PERIPH_SEL: PeripheralSelect = PeripheralSelect::Spi2;
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
fn ptr() -> *const SpiRegBlock {
|
fn ptr() -> *const SpiRegBlock {
|
||||||
Self::ptr()
|
Self::ptr()
|
||||||
}
|
}
|
||||||
@ -472,6 +475,7 @@ where
|
|||||||
self.cfg_hw_cs(HwCs::CS_ID);
|
self.cfg_hw_cs(HwCs::CS_ID);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
pub fn cfg_hw_cs_disable(&mut self) {
|
pub fn cfg_hw_cs_disable(&mut self) {
|
||||||
self.spi.ctrl1().modify(|_, w| {
|
self.spi.ctrl1().modify(|_, w| {
|
||||||
w.sod().set_bit();
|
w.sod().set_bit();
|
||||||
@ -571,99 +575,6 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
macro_rules! spi_ctor {
|
|
||||||
($spiI:ident, $PeriphSel: path) => {
|
|
||||||
/// Create a new SPI struct
|
|
||||||
///
|
|
||||||
/// You can delete the pin type information by calling the
|
|
||||||
/// [`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 $spiI(
|
|
||||||
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, $PeriphSel);
|
|
||||||
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,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
impl<
|
impl<
|
||||||
SpiI: Instance,
|
SpiI: Instance,
|
||||||
Sck: PinSck<SpiI>,
|
Sck: PinSck<SpiI>,
|
||||||
@ -698,6 +609,8 @@ where
|
|||||||
transfer_cfg: Option<&ErasedTransferConfig>,
|
transfer_cfg: Option<&ErasedTransferConfig>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
crate::clock::enable_peripheral_clock(syscfg, SpiI::PERIPH_SEL);
|
crate::clock::enable_peripheral_clock(syscfg, SpiI::PERIPH_SEL);
|
||||||
|
// This is done in the C HAL.
|
||||||
|
syscfg.assert_periph_reset_for_two_cycles(SpiI::PERIPH_SEL);
|
||||||
let SpiConfig {
|
let SpiConfig {
|
||||||
ser_clock_rate_div,
|
ser_clock_rate_div,
|
||||||
ms,
|
ms,
|
||||||
|
@ -513,6 +513,7 @@ impl<Tim: ValidTim> CountdownTimer<Tim> {
|
|||||||
|
|
||||||
/// Listen for events. Depending on the IRQ configuration, this also activates the IRQ in the
|
/// 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
|
/// IRQSEL peripheral for the provided interrupt and unmasks the interrupt
|
||||||
|
#[inline]
|
||||||
pub fn listen(&mut self) {
|
pub fn listen(&mut self) {
|
||||||
self.listening = true;
|
self.listening = true;
|
||||||
self.enable_interrupt();
|
self.enable_interrupt();
|
||||||
@ -532,10 +533,12 @@ impl<Tim: ValidTim> CountdownTimer<Tim> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
pub fn stop(&mut self) {
|
pub fn stop(&mut self) {
|
||||||
self.tim.reg().ctrl().write(|w| w.enable().clear_bit());
|
self.tim.reg().ctrl().write(|w| w.enable().clear_bit());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
pub fn unlisten(&mut self) {
|
pub fn unlisten(&mut self) {
|
||||||
self.listening = true;
|
self.listening = true;
|
||||||
self.disable_interrupt();
|
self.disable_interrupt();
|
||||||
@ -552,6 +555,7 @@ impl<Tim: ValidTim> CountdownTimer<Tim> {
|
|||||||
self.tim.reg().ctrl().modify(|_, w| w.irq_enb().clear_bit());
|
self.tim.reg().ctrl().modify(|_, w| w.irq_enb().clear_bit());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
pub fn release(self, syscfg: &mut pac::Sysconfig) -> Tim {
|
pub fn release(self, syscfg: &mut pac::Sysconfig) -> Tim {
|
||||||
self.tim.reg().ctrl().write(|w| w.enable().clear_bit());
|
self.tim.reg().ctrl().write(|w| w.enable().clear_bit());
|
||||||
syscfg
|
syscfg
|
||||||
|
@ -9,7 +9,7 @@ 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::{Clocks, PeripheralSelect};
|
use crate::clock::{Clocks, PeripheralSelect, SyscfgExt};
|
||||||
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::{disable_interrupt, enable_interrupt};
|
||||||
@ -520,6 +520,8 @@ impl<UartInstance: Instance, Pins> Uart<UartInstance, Pins> {
|
|||||||
clocks: &Clocks,
|
clocks: &Clocks,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
crate::clock::enable_peripheral_clock(syscfg, UartInstance::PERIPH_SEL);
|
crate::clock::enable_peripheral_clock(syscfg, UartInstance::PERIPH_SEL);
|
||||||
|
// This is done in the C HAL.
|
||||||
|
syscfg.assert_periph_reset_for_two_cycles(UartInstance::PERIPH_SEL);
|
||||||
Uart {
|
Uart {
|
||||||
inner: UartBase {
|
inner: UartBase {
|
||||||
uart,
|
uart,
|
||||||
|
@ -51,12 +51,7 @@ impl WdtController {
|
|||||||
wdt_freq_ms: u32,
|
wdt_freq_ms: u32,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
syscfg.enable_peripheral_clock(PeripheralSelect::Watchdog);
|
syscfg.enable_peripheral_clock(PeripheralSelect::Watchdog);
|
||||||
// It's done like that in Vorago examples. Not exactly sure why the reset is necessary
|
syscfg.assert_periph_reset_for_two_cycles(PeripheralSelect::Watchdog);
|
||||||
// though..
|
|
||||||
syscfg.assert_periph_reset(PeripheralSelect::Watchdog);
|
|
||||||
cortex_m::asm::nop();
|
|
||||||
cortex_m::asm::nop();
|
|
||||||
syscfg.deassert_periph_reset(PeripheralSelect::Watchdog);
|
|
||||||
|
|
||||||
let wdt_clock = clocks.apb2();
|
let wdt_clock = clocks.apb2();
|
||||||
let mut wdt_ctrl = Self {
|
let mut wdt_ctrl = Self {
|
||||||
|
@ -4,6 +4,56 @@
|
|||||||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||||
"version": "0.2.0",
|
"version": "0.2.0",
|
||||||
"configurations": [
|
"configurations": [
|
||||||
|
{
|
||||||
|
"preLaunchTask": "blinky-example",
|
||||||
|
"type": "probe-rs-debug",
|
||||||
|
"request": "launch",
|
||||||
|
"name": "probe-rs Debug Blinky",
|
||||||
|
"flashingConfig": {
|
||||||
|
"flashingEnabled": true,
|
||||||
|
"haltAfterReset": true
|
||||||
|
},
|
||||||
|
"chip": "VA416xx",
|
||||||
|
"coreConfigs": [
|
||||||
|
{
|
||||||
|
"programBinary": "${workspaceFolder}/target/thumbv7em-none-eabihf/debug/examples/blinky",
|
||||||
|
"rttEnabled": true,
|
||||||
|
"svdFile": "${workspaceFolder}/va416xx/svd/va416xx.svd.patched"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"preLaunchTask": "rtt-log-example",
|
||||||
|
"type": "probe-rs-debug",
|
||||||
|
"request": "launch",
|
||||||
|
"name": "probe-rs Debug RTT",
|
||||||
|
"flashingConfig": {
|
||||||
|
"flashingEnabled": true,
|
||||||
|
"haltAfterReset": false
|
||||||
|
},
|
||||||
|
"chip": "VA416xx",
|
||||||
|
"coreConfigs": [
|
||||||
|
{
|
||||||
|
"programBinary": "${workspaceFolder}/target/thumbv7em-none-eabihf/debug/examples/rtt-log",
|
||||||
|
"rttEnabled": true,
|
||||||
|
"svdFile": "${workspaceFolder}/va416xx/svd/va416xx.svd.patched"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"preLaunchTask": "rtt-log-example",
|
||||||
|
"type": "probe-rs-debug",
|
||||||
|
"request": "attach",
|
||||||
|
"name": "probe-rs Attach RTT",
|
||||||
|
"chip": "VA416xx",
|
||||||
|
"coreConfigs": [
|
||||||
|
{
|
||||||
|
"programBinary": "${workspaceFolder}/target/thumbv7em-none-eabihf/debug/examples/rtt-log",
|
||||||
|
"rttEnabled": true,
|
||||||
|
"svdFile": "${workspaceFolder}/va416xx/svd/va416xx.svd"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"type": "cortex-debug",
|
"type": "cortex-debug",
|
||||||
"request": "launch",
|
"request": "launch",
|
||||||
@ -19,7 +69,7 @@
|
|||||||
"monitor reset",
|
"monitor reset",
|
||||||
"load",
|
"load",
|
||||||
],
|
],
|
||||||
"executable": "${workspaceFolder}/target/thumbv7em-none-eabihf/debug/examples/blinky-hal",
|
"executable": "${workspaceFolder}/target/thumbv7em-none-eabihf/debug/examples/blinky",
|
||||||
"interface": "swd",
|
"interface": "swd",
|
||||||
"runToEntryPoint": "main",
|
"runToEntryPoint": "main",
|
||||||
"rttConfig": {
|
"rttConfig": {
|
||||||
@ -185,5 +235,67 @@
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"type": "cortex-debug",
|
||||||
|
"request": "launch",
|
||||||
|
"name": "Debug DAC/ADC Example",
|
||||||
|
"servertype": "jlink",
|
||||||
|
"jlinkscript": "${workspaceFolder}/jlink/JLinkSettings.JLinkScript",
|
||||||
|
"cwd": "${workspaceRoot}",
|
||||||
|
"device": "Cortex-M4",
|
||||||
|
"svdFile": "${workspaceFolder}/va416xx/svd/va416xx.svd.patched",
|
||||||
|
"preLaunchTask": "dac-adc-example",
|
||||||
|
"overrideLaunchCommands": [
|
||||||
|
"monitor halt",
|
||||||
|
"monitor reset",
|
||||||
|
"load",
|
||||||
|
],
|
||||||
|
"executable": "${workspaceFolder}/target/thumbv7em-none-eabihf/debug/examples/dac-adc",
|
||||||
|
"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"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "cortex-debug",
|
||||||
|
"request": "launch",
|
||||||
|
"name": "Debug ADC Example",
|
||||||
|
"servertype": "jlink",
|
||||||
|
"jlinkscript": "${workspaceFolder}/jlink/JLinkSettings.JLinkScript",
|
||||||
|
"cwd": "${workspaceRoot}",
|
||||||
|
"device": "Cortex-M4",
|
||||||
|
"svdFile": "${workspaceFolder}/va416xx/svd/va416xx.svd.patched",
|
||||||
|
"preLaunchTask": "adc-example",
|
||||||
|
"overrideLaunchCommands": [
|
||||||
|
"monitor halt",
|
||||||
|
"monitor reset",
|
||||||
|
"load",
|
||||||
|
],
|
||||||
|
"executable": "${workspaceFolder}/target/thumbv7em-none-eabihf/debug/examples/adc",
|
||||||
|
"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"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
]
|
]
|
||||||
}
|
}
|
@ -36,7 +36,7 @@
|
|||||||
"args": [
|
"args": [
|
||||||
"build",
|
"build",
|
||||||
"--example",
|
"--example",
|
||||||
"blinky-hal"
|
"blinky"
|
||||||
],
|
],
|
||||||
"group": {
|
"group": {
|
||||||
"kind": "build",
|
"kind": "build",
|
||||||
@ -82,5 +82,31 @@
|
|||||||
"kind": "build",
|
"kind": "build",
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"label": "dac-adc-example",
|
||||||
|
"type": "shell",
|
||||||
|
"command": "~/.cargo/bin/cargo", // note: full path to the cargo
|
||||||
|
"args": [
|
||||||
|
"build",
|
||||||
|
"--example",
|
||||||
|
"dac-adc"
|
||||||
|
],
|
||||||
|
"group": {
|
||||||
|
"kind": "build",
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "adc-example",
|
||||||
|
"type": "shell",
|
||||||
|
"command": "~/.cargo/bin/cargo", // note: full path to the cargo
|
||||||
|
"args": [
|
||||||
|
"build",
|
||||||
|
"--example",
|
||||||
|
"adc"
|
||||||
|
],
|
||||||
|
"group": {
|
||||||
|
"kind": "build",
|
||||||
|
}
|
||||||
|
},
|
||||||
]
|
]
|
||||||
}
|
}
|
Loading…
Reference in New Issue
Block a user