1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
//! This module includes an overview of ADC features available.
//! For project structure and debugging boilerplate, see the `synax_overview` example.
#![no_main]
#![no_std]
use core::cell::{Cell, RefCell};
use cortex_m::{
delay::Delay,
interrupt::{free, Mutex},
peripheral::NVIC,
};
use cortex_m_rt::entry;
use stm32_hal2::{
adc::{Adc, AdcChannel, AdcDevice, AdcInterrupt, Align, ClockMode, InputType, OperationMode},
clocks::Clocks,
dma::{self, Dma, DmaChannel, DmaInterrupt, DmaWriteBuf},
gpio::{Pin, PinMode, Port},
low_power, pac,
traits::ClockCfg,
};
#[entry]
fn main() -> ! {
// Set up CPU peripherals
let mut cp = cortex_m::Peripherals::take().unwrap();
// Set up microcontroller peripherals
let mut dp = pac::Peripherals::take().unwrap();
let clock_cfg = Clocks::default();
clock_cfg.setup(&mut dp.RCC, &mut dp.FLASH).unwrap();
let mut delay = Delay::new(cp.SYST, clock_cfg.systick());
let chan_num = 2;
// Configure the ADC pin in analog mode. (This is the default state for some STM32 families,
// but not all)
let _adc_pin = Pin::new(Port::B, 0, PinMode::Analog);
let mut adc = Adc::new_adc1(
dp.ADC1,
AdcDevice::One,
&mut dp.ADC_COMMON,
ClockMode::default(),
&clock_cfg,
);
// 1: Confiuration options:
// Set a channel to a specific position in a sequence:
adc.set_sequence(1, 2); // Set channel 1 to be the second position in the sequence.
// Set the length of the sequence to read. (ie number of channels):
adc.set_sequence_len(2);
// Set up differential mode:
adc.set_input_type(chan_num, InputType::Differential);
// Change the sample rate:
adc.set_sample_time(chan_num, SampleTime::T2);
// Set left align mode:
adc.set_align(Align::Left);
adc.enable_interrupt(AdcInterrupt::EndOfSequence);
// 2: Set up DMA, for nonblocking (generally faster) conversion transfers:
let mut dma = Dma::new(&mut dp.DMA1, &dp.RCC);
let mut dma_buf = [0];
// Begin a DMA transfer. Note that the `DmaChannel` we pass here is only used on
// MCUs that use `DMAMUX`, eg L5, G0, and G4. For those, you need to run `mux`, to
// set the channel: `dma::mux(DmaChannel::C1, MuxInput::Adc1, &mut dp.DMAMUX);
unsafe { adc.read_dma(&mut dma_buf, chan_num, DmaChannel::C1, &mut dma) };
// Wait for the transfer to complete. Ie by handling the channel's transfer-complete
// interrupt in an ISR, which is enabled by the `read_dma` command.
// For this example, we block until the flag is set.
while !dma.transfer_is_complete(DmaChannel::C1) {}
dma.stop(DmaChannel::C1);
defmt::info!("Reading: {:?}", &dma_buf[0]);
// Unmask the interrupt line. See the `DMA_CH1` interrupt handler below.
unsafe { NVIC::unmask(pac::Interrupt::DMA1_CH1) }
// 3: Alternatively, we can take readings without DMA. This provides a simpler, memory-safe API,
// and is compatible with the `embedded_hal::adc::OneShot trait.
// Take a OneShot reading from channel 3. (Note that the Embedded HAL trait is also available,
// for use in embedded drivers). Channels for EH usage are included: `stm32hal2::adc::AdcChannel::C3`
let reading = adc.read(chan_num);
// Convert a reading to voltage, which includes compensation for the built-in VDDA
// reference voltage
let voltage = adc.reading_to_voltage(reading);
// Or, start reading in continuous mode, reading a single channel
adc.start_conversion(&[chan_num], OperationMode::Continuous);
// Or, read multiple channels in a sequence:
adc.start_conversion([1, 2, 3], OperationMode::Continuous);
// Read from the ADC's latest (continuously-running) conversion:
let reading = adc.read_result();
loop {
low_power::sleep_now(&mut SCB);
}
}
#[interrupt]
/// This interrupt fires when the ADC transfer is complete.
fn DMA1_CH1() {
free(|cs| {
unsafe { (*pac::DMA1::ptr()).ifcr.write(|w| w.tcif1().set_bit()) }
// Or, if you have access to the Dma peripheral struct:
// dma.clear_interrupt(DmaChannel::C1);
// dma.stop(DmaChannel::C1);
});
}