mirror of
https://github.com/embassy-rs/embassy.git
synced 2024-11-22 14:53:03 +00:00
Merge pull request #3148 from andresv/stm32-adc-blocking_read
stm32 adc: introduce blocking_read
This commit is contained in:
commit
e2f9a48457
@ -258,7 +258,7 @@ impl<'d, T: Instance> Adc<'d, T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Read an ADC pin.
|
/// Read an ADC pin.
|
||||||
pub fn read(&mut self, channel: &mut impl AdcChannel<T>) -> u16 {
|
pub fn blocking_read(&mut self, channel: &mut impl AdcChannel<T>) -> u16 {
|
||||||
channel.setup();
|
channel.setup();
|
||||||
|
|
||||||
self.read_channel(channel.channel())
|
self.read_channel(channel.channel())
|
||||||
|
@ -97,10 +97,10 @@ impl<'d, T: Instance> Adc<'d, T> {
|
|||||||
/// The length of the `dma_buf` should be a multiple of the ADC channel count.
|
/// The length of the `dma_buf` should be a multiple of the ADC channel count.
|
||||||
/// For example, if 3 channels are measured, its length can be 3 * 40 = 120 measurements.
|
/// For example, if 3 channels are measured, its length can be 3 * 40 = 120 measurements.
|
||||||
///
|
///
|
||||||
/// `read_exact` method is used to read out measurements from the DMA ring buffer, and its buffer should be exactly half of the `dma_buf` length.
|
/// `read` method is used to read out measurements from the DMA ring buffer, and its buffer should be exactly half of the `dma_buf` length.
|
||||||
/// It is critical to call `read_exact` frequently to prevent DMA buffer overrun.
|
/// It is critical to call `read` frequently to prevent DMA buffer overrun.
|
||||||
///
|
///
|
||||||
/// [`read_exact`]: #method.read_exact
|
/// [`read`]: #method.read
|
||||||
pub fn into_ring_buffered(
|
pub fn into_ring_buffered(
|
||||||
self,
|
self,
|
||||||
dma: impl Peripheral<P = impl RxDma<T>> + 'd,
|
dma: impl Peripheral<P = impl RxDma<T>> + 'd,
|
||||||
@ -331,7 +331,7 @@ impl<'d, T: Instance> RingBufferedAdc<'d, T> {
|
|||||||
///
|
///
|
||||||
/// Receive in the background is terminated if an error is returned.
|
/// Receive in the background is terminated if an error is returned.
|
||||||
/// It must then manually be started again by calling `start()` or by re-calling `read()`.
|
/// It must then manually be started again by calling `start()` or by re-calling `read()`.
|
||||||
pub fn read<const N: usize>(&mut self, buf: &mut [u16; N]) -> Result<usize, OverrunError> {
|
pub fn blocking_read<const N: usize>(&mut self, buf: &mut [u16; N]) -> Result<usize, OverrunError> {
|
||||||
let r = T::regs();
|
let r = T::regs();
|
||||||
|
|
||||||
// Start background receive if it was not already started
|
// Start background receive if it was not already started
|
||||||
@ -362,14 +362,14 @@ impl<'d, T: Instance> RingBufferedAdc<'d, T> {
|
|||||||
/// This method fills the provided `measurements` array with ADC readings from the DMA buffer.
|
/// This method fills the provided `measurements` array with ADC readings from the DMA buffer.
|
||||||
/// The length of the `measurements` array should be exactly half of the DMA buffer length. Because interrupts are only generated if half or full DMA transfer completes.
|
/// The length of the `measurements` array should be exactly half of the DMA buffer length. Because interrupts are only generated if half or full DMA transfer completes.
|
||||||
///
|
///
|
||||||
/// Each call to `read_exact` will populate the `measurements` array in the same order as the channels defined with `set_sample_sequence`.
|
/// Each call to `read` will populate the `measurements` array in the same order as the channels defined with `set_sample_sequence`.
|
||||||
/// There will be many sequences worth of measurements in this array because it only returns if at least half of the DMA buffer is filled.
|
/// There will be many sequences worth of measurements in this array because it only returns if at least half of the DMA buffer is filled.
|
||||||
/// For example if 3 channels are sampled `measurements` contain: `[sq0 sq1 sq3 sq0 sq1 sq3 sq0 sq1 sq3 sq0 sq1 sq3..]`.
|
/// For example if 3 channels are sampled `measurements` contain: `[sq0 sq1 sq3 sq0 sq1 sq3 sq0 sq1 sq3 sq0 sq1 sq3..]`.
|
||||||
///
|
///
|
||||||
/// If an error is returned, it indicates a DMA overrun, and the process must be restarted by calling `start` or `read_exact` again.
|
/// If an error is returned, it indicates a DMA overrun, and the process must be restarted by calling `start` or `read` again.
|
||||||
///
|
///
|
||||||
/// By default, the ADC fills the DMA buffer as quickly as possible. To control the sample rate, call `teardown_adc` after each readout, and then start the DMA again at the desired interval.
|
/// By default, the ADC fills the DMA buffer as quickly as possible. To control the sample rate, call `teardown_adc` after each readout, and then start the DMA again at the desired interval.
|
||||||
/// Note that even if using `teardown_adc` to control the sample rate, with each call to `read_exact`, measurements equivalent to half the size of the DMA buffer are still collected.
|
/// Note that even if using `teardown_adc` to control the sample rate, with each call to `read`, measurements equivalent to half the size of the DMA buffer are still collected.
|
||||||
///
|
///
|
||||||
/// Example:
|
/// Example:
|
||||||
/// ```rust,ignore
|
/// ```rust,ignore
|
||||||
@ -383,7 +383,7 @@ impl<'d, T: Instance> RingBufferedAdc<'d, T> {
|
|||||||
///
|
///
|
||||||
/// let mut measurements = [0u16; DMA_BUF_LEN / 2];
|
/// let mut measurements = [0u16; DMA_BUF_LEN / 2];
|
||||||
/// loop {
|
/// loop {
|
||||||
/// match adc.read_exact(&mut measurements).await {
|
/// match adc.read(&mut measurements).await {
|
||||||
/// Ok(_) => {
|
/// Ok(_) => {
|
||||||
/// defmt::info!("adc1: {}", measurements);
|
/// defmt::info!("adc1: {}", measurements);
|
||||||
/// // Only needed to manually control sample rate.
|
/// // Only needed to manually control sample rate.
|
||||||
@ -391,7 +391,7 @@ impl<'d, T: Instance> RingBufferedAdc<'d, T> {
|
|||||||
/// }
|
/// }
|
||||||
/// Err(e) => {
|
/// Err(e) => {
|
||||||
/// defmt::warn!("Error: {:?}", e);
|
/// defmt::warn!("Error: {:?}", e);
|
||||||
/// // DMA overrun, next call to `read_exact` restart ADC.
|
/// // DMA overrun, next call to `read` restarts ADC.
|
||||||
/// }
|
/// }
|
||||||
/// }
|
/// }
|
||||||
///
|
///
|
||||||
@ -404,7 +404,7 @@ impl<'d, T: Instance> RingBufferedAdc<'d, T> {
|
|||||||
/// [`set_sample_sequence`]: #method.set_sample_sequence
|
/// [`set_sample_sequence`]: #method.set_sample_sequence
|
||||||
/// [`teardown_adc`]: #method.teardown_adc
|
/// [`teardown_adc`]: #method.teardown_adc
|
||||||
/// [`start`]: #method.start
|
/// [`start`]: #method.start
|
||||||
pub async fn read_exact<const N: usize>(&mut self, measurements: &mut [u16; N]) -> Result<usize, OverrunError> {
|
pub async fn read<const N: usize>(&mut self, measurements: &mut [u16; N]) -> Result<usize, OverrunError> {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
self.ring_buf.capacity() / 2,
|
self.ring_buf.capacity() / 2,
|
||||||
N,
|
N,
|
||||||
|
@ -178,7 +178,7 @@ where
|
|||||||
T::regs().dr().read().0 as u16
|
T::regs().dr().read().0 as u16
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn read(&mut self, channel: &mut impl AdcChannel<T>) -> u16 {
|
pub fn blocking_read(&mut self, channel: &mut impl AdcChannel<T>) -> u16 {
|
||||||
channel.setup();
|
channel.setup();
|
||||||
|
|
||||||
// Configure ADC
|
// Configure ADC
|
||||||
|
@ -254,12 +254,36 @@ impl<'d, T: Instance> Adc<'d, T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Read an ADC channel.
|
/// Read an ADC channel.
|
||||||
pub fn read(&mut self, channel: &mut impl AdcChannel<T>) -> u16 {
|
pub fn blocking_read(&mut self, channel: &mut impl AdcChannel<T>) -> u16 {
|
||||||
self.read_channel(channel)
|
self.read_channel(channel)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Asynchronously read from sequence of ADC channels.
|
/// Read one or multiple ADC channels using DMA.
|
||||||
pub async fn read_async(
|
///
|
||||||
|
/// `sequence` iterator and `readings` must have the same length.
|
||||||
|
///
|
||||||
|
/// Example
|
||||||
|
/// ```rust,ignore
|
||||||
|
/// use embassy_stm32::adc::{Adc, AdcChannel}
|
||||||
|
///
|
||||||
|
/// let mut adc = Adc::new(p.ADC1);
|
||||||
|
/// let mut adc_pin0 = p.PA0.degrade_adc();
|
||||||
|
/// let mut adc_pin1 = p.PA1.degrade_adc();
|
||||||
|
/// let mut measurements = [0u16; 2];
|
||||||
|
///
|
||||||
|
/// adc.read_async(
|
||||||
|
/// p.DMA1_CH2,
|
||||||
|
/// [
|
||||||
|
/// (&mut *adc_pin0, SampleTime::CYCLES160_5),
|
||||||
|
/// (&mut *adc_pin1, SampleTime::CYCLES160_5),
|
||||||
|
/// ]
|
||||||
|
/// .into_iter(),
|
||||||
|
/// &mut measurements,
|
||||||
|
/// )
|
||||||
|
/// .await;
|
||||||
|
/// defmt::info!("measurements: {}", measurements);
|
||||||
|
/// ```
|
||||||
|
pub async fn read(
|
||||||
&mut self,
|
&mut self,
|
||||||
rx_dma: &mut impl RxDma<T>,
|
rx_dma: &mut impl RxDma<T>,
|
||||||
sequence: impl ExactSizeIterator<Item = (&mut AnyAdcChannel<T>, SampleTime)>,
|
sequence: impl ExactSizeIterator<Item = (&mut AnyAdcChannel<T>, SampleTime)>,
|
||||||
|
@ -318,12 +318,36 @@ impl<'d, T: Instance> Adc<'d, T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Read an ADC channel.
|
/// Read an ADC channel.
|
||||||
pub fn read(&mut self, channel: &mut impl AdcChannel<T>) -> u16 {
|
pub fn blocking_read(&mut self, channel: &mut impl AdcChannel<T>) -> u16 {
|
||||||
self.read_channel(channel)
|
self.read_channel(channel)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Asynchronously read from sequence of ADC channels.
|
/// Read one or multiple ADC channels using DMA.
|
||||||
pub async fn read_async(
|
///
|
||||||
|
/// `sequence` iterator and `readings` must have the same length.
|
||||||
|
///
|
||||||
|
/// Example
|
||||||
|
/// ```rust,ignore
|
||||||
|
/// use embassy_stm32::adc::{Adc, AdcChannel}
|
||||||
|
///
|
||||||
|
/// let mut adc = Adc::new(p.ADC1);
|
||||||
|
/// let mut adc_pin0 = p.PA0.degrade_adc();
|
||||||
|
/// let mut adc_pin2 = p.PA2.degrade_adc();
|
||||||
|
/// let mut measurements = [0u16; 2];
|
||||||
|
///
|
||||||
|
/// adc.read_async(
|
||||||
|
/// p.DMA2_CH0,
|
||||||
|
/// [
|
||||||
|
/// (&mut *adc_pin0, SampleTime::CYCLES112),
|
||||||
|
/// (&mut *adc_pin2, SampleTime::CYCLES112),
|
||||||
|
/// ]
|
||||||
|
/// .into_iter(),
|
||||||
|
/// &mut measurements,
|
||||||
|
/// )
|
||||||
|
/// .await;
|
||||||
|
/// defmt::info!("measurements: {}", measurements);
|
||||||
|
/// ```
|
||||||
|
pub async fn read(
|
||||||
&mut self,
|
&mut self,
|
||||||
rx_dma: &mut impl RxDma<T>,
|
rx_dma: &mut impl RxDma<T>,
|
||||||
sequence: impl ExactSizeIterator<Item = (&mut AnyAdcChannel<T>, SampleTime)>,
|
sequence: impl ExactSizeIterator<Item = (&mut AnyAdcChannel<T>, SampleTime)>,
|
||||||
|
@ -23,7 +23,7 @@ async fn main(_spawner: Spawner) {
|
|||||||
// Startup delay can be combined to the maximum of either
|
// Startup delay can be combined to the maximum of either
|
||||||
delay.delay_us(Temperature::start_time_us().max(VrefInt::start_time_us()));
|
delay.delay_us(Temperature::start_time_us().max(VrefInt::start_time_us()));
|
||||||
|
|
||||||
let vrefint_sample = adc.read(&mut vrefint);
|
let vrefint_sample = adc.blocking_read(&mut vrefint);
|
||||||
|
|
||||||
let convert_to_millivolts = |sample| {
|
let convert_to_millivolts = |sample| {
|
||||||
// From http://www.st.com/resource/en/datasheet/DM00071990.pdf
|
// From http://www.st.com/resource/en/datasheet/DM00071990.pdf
|
||||||
@ -50,16 +50,16 @@ async fn main(_spawner: Spawner) {
|
|||||||
|
|
||||||
loop {
|
loop {
|
||||||
// Read pin
|
// Read pin
|
||||||
let v = adc.read(&mut pin);
|
let v = adc.blocking_read(&mut pin);
|
||||||
info!("PC1: {} ({} mV)", v, convert_to_millivolts(v));
|
info!("PC1: {} ({} mV)", v, convert_to_millivolts(v));
|
||||||
|
|
||||||
// Read internal temperature
|
// Read internal temperature
|
||||||
let v = adc.read(&mut temp);
|
let v = adc.blocking_read(&mut temp);
|
||||||
let celcius = convert_to_celcius(v);
|
let celcius = convert_to_celcius(v);
|
||||||
info!("Internal temp: {} ({} C)", v, celcius);
|
info!("Internal temp: {} ({} C)", v, celcius);
|
||||||
|
|
||||||
// Read internal voltage reference
|
// Read internal voltage reference
|
||||||
let v = adc.read(&mut vrefint);
|
let v = adc.blocking_read(&mut vrefint);
|
||||||
info!("VrefInt: {}", v);
|
info!("VrefInt: {}", v);
|
||||||
|
|
||||||
Timer::after_millis(100).await;
|
Timer::after_millis(100).await;
|
||||||
|
@ -44,7 +44,7 @@ async fn adc_task(mut p: Peripherals) {
|
|||||||
let _ = adc.start();
|
let _ = adc.start();
|
||||||
let _ = adc2.start();
|
let _ = adc2.start();
|
||||||
loop {
|
loop {
|
||||||
match adc.read_exact(&mut buffer1).await {
|
match adc.read(&mut buffer1).await {
|
||||||
Ok(_data) => {
|
Ok(_data) => {
|
||||||
let toc = Instant::now();
|
let toc = Instant::now();
|
||||||
info!(
|
info!(
|
||||||
@ -62,7 +62,7 @@ async fn adc_task(mut p: Peripherals) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
match adc2.read_exact(&mut buffer2).await {
|
match adc2.read(&mut buffer2).await {
|
||||||
Ok(_data) => {
|
Ok(_data) => {
|
||||||
let toc = Instant::now();
|
let toc = Instant::now();
|
||||||
info!(
|
info!(
|
||||||
|
@ -16,7 +16,7 @@ async fn main(_spawner: Spawner) {
|
|||||||
let mut pin = p.PA3;
|
let mut pin = p.PA3;
|
||||||
|
|
||||||
let mut vrefint = adc.enable_vrefint();
|
let mut vrefint = adc.enable_vrefint();
|
||||||
let vrefint_sample = adc.read(&mut vrefint);
|
let vrefint_sample = adc.blocking_read(&mut vrefint);
|
||||||
let convert_to_millivolts = |sample| {
|
let convert_to_millivolts = |sample| {
|
||||||
// From http://www.st.com/resource/en/datasheet/DM00273119.pdf
|
// From http://www.st.com/resource/en/datasheet/DM00273119.pdf
|
||||||
// 6.3.27 Reference voltage
|
// 6.3.27 Reference voltage
|
||||||
@ -26,7 +26,7 @@ async fn main(_spawner: Spawner) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
let v = adc.read(&mut pin);
|
let v = adc.blocking_read(&mut pin);
|
||||||
info!("--> {} - {} mV", v, convert_to_millivolts(v));
|
info!("--> {} - {} mV", v, convert_to_millivolts(v));
|
||||||
Timer::after_millis(100).await;
|
Timer::after_millis(100).await;
|
||||||
}
|
}
|
||||||
|
@ -17,7 +17,7 @@ async fn main(_spawner: Spawner) {
|
|||||||
let mut pin = p.PA1;
|
let mut pin = p.PA1;
|
||||||
|
|
||||||
let mut vrefint = adc.enable_vrefint();
|
let mut vrefint = adc.enable_vrefint();
|
||||||
let vrefint_sample = adc.read(&mut vrefint);
|
let vrefint_sample = adc.blocking_read(&mut vrefint);
|
||||||
let convert_to_millivolts = |sample| {
|
let convert_to_millivolts = |sample| {
|
||||||
// From https://www.st.com/resource/en/datasheet/stm32g031g8.pdf
|
// From https://www.st.com/resource/en/datasheet/stm32g031g8.pdf
|
||||||
// 6.3.3 Embedded internal reference voltage
|
// 6.3.3 Embedded internal reference voltage
|
||||||
@ -27,7 +27,7 @@ async fn main(_spawner: Spawner) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
let v = adc.read(&mut pin);
|
let v = adc.blocking_read(&mut pin);
|
||||||
info!("--> {} - {} mV", v, convert_to_millivolts(v));
|
info!("--> {} - {} mV", v, convert_to_millivolts(v));
|
||||||
Timer::after_millis(100).await;
|
Timer::after_millis(100).await;
|
||||||
}
|
}
|
||||||
|
@ -24,7 +24,7 @@ async fn main(_spawner: Spawner) {
|
|||||||
let mut pa0 = p.PA0.degrade_adc();
|
let mut pa0 = p.PA0.degrade_adc();
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
adc.read_async(
|
adc.read(
|
||||||
&mut dma,
|
&mut dma,
|
||||||
[
|
[
|
||||||
(&mut vrefint_channel, SampleTime::CYCLES160_5),
|
(&mut vrefint_channel, SampleTime::CYCLES160_5),
|
||||||
|
@ -36,7 +36,7 @@ async fn main(_spawner: Spawner) {
|
|||||||
adc.oversampling_enable(true);
|
adc.oversampling_enable(true);
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
let v = adc.read(&mut pin);
|
let v = adc.blocking_read(&mut pin);
|
||||||
info!("--> {} ", v); //max 65520 = 0xFFF0
|
info!("--> {} ", v); //max 65520 = 0xFFF0
|
||||||
Timer::after_millis(100).await;
|
Timer::after_millis(100).await;
|
||||||
}
|
}
|
||||||
|
@ -32,7 +32,7 @@ async fn main(_spawner: Spawner) {
|
|||||||
adc.set_sample_time(SampleTime::CYCLES24_5);
|
adc.set_sample_time(SampleTime::CYCLES24_5);
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
let measured = adc.read(&mut p.PA7);
|
let measured = adc.blocking_read(&mut p.PA7);
|
||||||
info!("measured: {}", measured);
|
info!("measured: {}", measured);
|
||||||
Timer::after_millis(500).await;
|
Timer::after_millis(500).await;
|
||||||
}
|
}
|
||||||
|
@ -51,9 +51,9 @@ async fn main(_spawner: Spawner) {
|
|||||||
let mut vrefint_channel = adc.enable_vrefint();
|
let mut vrefint_channel = adc.enable_vrefint();
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
let vrefint = adc.read(&mut vrefint_channel);
|
let vrefint = adc.blocking_read(&mut vrefint_channel);
|
||||||
info!("vrefint: {}", vrefint);
|
info!("vrefint: {}", vrefint);
|
||||||
let measured = adc.read(&mut p.PC0);
|
let measured = adc.blocking_read(&mut p.PC0);
|
||||||
info!("measured: {}", measured);
|
info!("measured: {}", measured);
|
||||||
Timer::after_millis(500).await;
|
Timer::after_millis(500).await;
|
||||||
}
|
}
|
||||||
|
@ -56,7 +56,7 @@ async fn main(_spawner: Spawner) {
|
|||||||
let mut pc0 = p.PC0.degrade_adc();
|
let mut pc0 = p.PC0.degrade_adc();
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
adc.read_async(
|
adc.read(
|
||||||
&mut dma,
|
&mut dma,
|
||||||
[
|
[
|
||||||
(&mut vrefint_channel, SampleTime::CYCLES387_5),
|
(&mut vrefint_channel, SampleTime::CYCLES387_5),
|
||||||
|
@ -23,7 +23,7 @@ fn main() -> ! {
|
|||||||
let mut channel = p.PC0;
|
let mut channel = p.PC0;
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
let v = adc.read(&mut channel);
|
let v = adc.blocking_read(&mut channel);
|
||||||
info!("--> {}", v);
|
info!("--> {}", v);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -23,7 +23,7 @@ fn main() -> ! {
|
|||||||
let mut channel = p.PC0;
|
let mut channel = p.PC0;
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
let v = adc.read(&mut channel);
|
let v = adc.blocking_read(&mut channel);
|
||||||
info!("--> {}", v);
|
info!("--> {}", v);
|
||||||
embassy_time::block_for(Duration::from_millis(200));
|
embassy_time::block_for(Duration::from_millis(200));
|
||||||
}
|
}
|
||||||
|
@ -38,7 +38,7 @@ async fn main(_spawner: Spawner) {
|
|||||||
dac.set(Value::Bit8(0));
|
dac.set(Value::Bit8(0));
|
||||||
// Now wait a little to obtain a stable value
|
// Now wait a little to obtain a stable value
|
||||||
Timer::after_millis(30).await;
|
Timer::after_millis(30).await;
|
||||||
let offset = adc.read(&mut adc_pin);
|
let offset = adc.blocking_read(&mut adc_pin);
|
||||||
|
|
||||||
for v in 0..=255 {
|
for v in 0..=255 {
|
||||||
// First set the DAC output value
|
// First set the DAC output value
|
||||||
@ -49,7 +49,7 @@ async fn main(_spawner: Spawner) {
|
|||||||
Timer::after_millis(30).await;
|
Timer::after_millis(30).await;
|
||||||
|
|
||||||
// Need to steal the peripherals here because PA4 is obviously in use already
|
// Need to steal the peripherals here because PA4 is obviously in use already
|
||||||
let measured = adc.read(&mut unsafe { embassy_stm32::Peripherals::steal() }.PA4);
|
let measured = adc.blocking_read(&mut unsafe { embassy_stm32::Peripherals::steal() }.PA4);
|
||||||
// Calibrate and normalize the measurement to get close to the dac_output_val
|
// Calibrate and normalize the measurement to get close to the dac_output_val
|
||||||
let measured_normalized = ((measured as i32 - offset as i32) / normalization_factor) as i16;
|
let measured_normalized = ((measured as i32 - offset as i32) / normalization_factor) as i16;
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user