// Copyright (c) 2026 Rigless Labs.
// SPDX-License-Identifier: MIT OR Apache-2.0
//! The analog-to-digital converters of RM0090 rev 22 chapter 13: [`Adc`], written against the `adc1` layout
//! and answering for ADC1, ADC2 and ADC3, and [`AdcCommon`], the common block of 13.13.15 to 13.13.17 that
//! clocks the three and switches the internal channels on.
//!
//! **A converter reads a voltage on a pin, and the board says what's on the pin.**
//! [`Adc::set_input`] puts millivolts on a channel and [`Adc::set_reference`] says what `VREF+` is.
//! The word a conversion answers is that ratio at the resolution `RES` names. The two internal
//! channels of ADC1, `VREFINT` and the temperature sensor or `VBAT`, answer from DS9405 rev 13's
//! figures once the common block enables them.
//!
//! **The common block is a clock and two request lines.** `ADCPRE` divides `PCLK2` into `ADCCLK`,
//! which the block drives to all three converters as its clock output. `TSVREFE` and
//! `VBATE` reach ADC1 as request lines. Each converter drives its six flags back as
//! request lines, and the block's `CSR` is an image of them.
//!
//! | Register or section | Behavior | Source | Test |
//! |---|---|---|---|
//! | `ADON`, `SWSTART` | The converter converts only while `ADON` stands. `SWSTART` starts the regular group and clears itself as the conversion starts, and `STRT` marks the start. Clearing `ADON` powers the converter down mid-sequence | 13.3.1, 13.13.3 | `a_single_conversion_answers_the_pins_ratio_to_the_reference_after_its_cycles`, `clearing_adon_stops_a_sequence` |
//! | Conversion time | `SMPx` cycles of sampling on the channel's row of `SMPR1` or `SMPR2`, then 12, 10, 8 or 6 by `RES`, at `ADCCLK`. A run of conversions is timed from where it began | 13.5, 13.7, 13.13.4, 13.13.5 | `a_single_conversion_answers_the_pins_ratio_to_the_reference_after_its_cycles`, `the_conversion_time_follows_the_sampling_time_and_the_resolution` |
//! | The word | `DATA` is the pin's share of `VREF+` at the resolution, right- or left-aligned by `ALIGN`, on a half-word basis but a byte basis at six bits | 13.4, 13.7, 13.13.14 | `the_word_is_aligned_as_align_and_res_say` |
//! | Single and continuous | With `CONT` clear the group runs once. With it set, it starts again at its first channel as it ends | 13.3.4, 13.3.5 | `a_single_conversion_answers_the_pins_ratio_to_the_reference_after_its_cycles`, `a_continuous_scan_starts_its_sequence_again` |
//! | Scan | With `SCAN` set the group is `L + 1` channels of `SQR3`, `SQR2` and `SQR1` in order. Without it, the first alone | 13.3.3, 13.3.8, 13.13.9 to 13.13.11 | `a_scan_converts_the_sequence_in_order_and_eoc_comes_where_eocs_says` |
//! | `EOC` | At the end of each conversion under `EOCS`, else at the end of the sequence. Cleared by a read of `DR` or a written zero | 13.3.8, 13.13.1, 13.13.3 | `a_scan_converts_the_sequence_in_order_and_eoc_comes_where_eocs_says` |
//! | DMA | Under `DMA` each regular conversion drives the request until `DR` is read. The requests go on under `DDS` | 13.8.1 | `each_conversion_drives_the_request_until_the_data_register_is_read` |
//! | `OVR` | With detection on, `DMA` or `EOCS`, a conversion ending with the last word still unread sets it, aborts the group and refuses triggers until it's cleared. Requests are blocked with it | 13.8.1, 13.8.2, 13.13.1 | `an_unread_word_overruns_and_holds_the_converter_until_cleared` |
//! | Injected | `JSWSTART` converts the last `JL + 1` channels of `JSQR` in the order the note gives, each less its offset into the data register of its rank with the sign, and `JEOC` at the end. A group in flight is reset and resumed after. `JAUTO` runs the injected group after the regular one | 13.3.9, 13.4, 13.13.6, 13.13.12, 13.13.13 | `an_injected_group_converts_its_last_channels_less_their_offsets`, `an_injected_trigger_interrupts_the_regular_group_and_it_resumes`, `jauto_runs_the_injected_group_after_the_regular_one` |
//! | Discontinuous | Under `DISCEN` each trigger converts the next `DISCNUM + 1` channels of the sequence, each with its `EOC`, and the sequence does not roll over | 13.3.10 | `discontinuous_mode_takes_the_sequence_a_few_channels_a_trigger` |
//! | Analog watchdog | `AWD` when a guarded conversion's word, before alignment, is below `LTR` or above `HTR`. Guarded as table 66 says by `AWDEN`, `JAWDEN`, `AWDSGL` and `AWDCH` | 13.3.7, 13.13.7, 13.13.8 | `the_watchdog_flags_a_word_outside_its_thresholds_on_the_channels_it_guards` |
//! | Interrupts | One line, table 70: `EOC`, `JEOC`, `AWD` and `OVR` under their enables | 13.12 | `each_enable_raises_the_line_for_its_own_flag` |
//! | `SR` | Every flag `rc_w0`: a written zero clears it, a written one leaves it | 13.13.1 | `a_written_zero_clears_a_flag_and_a_written_one_leaves_it` |
//! | The internal channels | Channel 17 is `VREFINT`, 1.21 V, and channel 18 the temperature sensor, 0.76 V at 25 °C and 2.5 mV a degree, under `TSVREFE`. `VBAT` over four under `VBATE`, which wins where both stand. On ADC1 alone | 13.3.3, 13.10, 13.11, DS9405 tables 81, 83 and 84 | `the_internal_channels_answer_the_datasheets_figures_once_the_common_block_enables_them` |
//! | `ADCCLK` | `PCLK2` over 2, 4, 6 or 8 by `ADCPRE`, driven to all three converters as the tree drives `PCLK2` | 13.3.2, 13.13.16 | `the_common_block_clocks_the_converters_at_pclk2_over_the_prescaler` |
//! | `CSR` | An image of the three status registers | 13.13.15 | `the_common_status_register_mirrors_each_converters_flags` |
//!
//! **What the model leaves out, and why.**
//!
//! | Behavior | Source | Reason |
//! |---|---|---|
//! | External triggers, `EXTEN`, `EXTSEL`, `JEXTEN`, `JEXTSEL` | 13.6 | A timer's trigger output reaches no converter yet: the timers publish `TRGO` and the wiring of it to a converter's start is a request line the description does not yet draw. The bits are stored and only software starts a group |
//! | Multi-ADC modes, `MULTI`, `DELAY`, the common `DMA` and `DDS`, `CDR` | 13.9, 13.13.16, 13.13.17 | Three converters working as one, and the first board runs each on its own. `CDR` reads zero |
//! | The stabilization time `tSTAB` and the sensors' start-up times | 13.3.6, DS9405 tables 81 and 84 | Analog settling with no register to see it in. A conversion started at once answers at once |
//! | `DDS` clear ending the requests after the controller's last transfer | 13.8.1 | The converter would have to be told the stream's count is out, a signal the runtime doesn't carry back. The requests go on as under `DDS` |
//! | The calibration values `TS_CAL1`, `TS_CAL2` and `VREFIN_CAL` in system memory | DS9405 tables 82 and 85 | System memory is the engine's, and a firmware reading them reads what the image put there |
//! | `JSQR` or `SQRx` written mid-conversion resetting it | 13.3.3 | The conversion in flight finishes and the next takes the new sequence |
//! | A software-started sequence rewritten mid-conversion not restarting | ES0206 rev 24 2.5.1 | Table 3 marks it on every revision. The model doesn't reset a conversion on a rewritten sequence at all, the row above, so there's no reset for it to fail to restart from |
//! | Internal noise on the supplies costing the converter accuracy | ES0206 rev 24 2.2.8 | Table 3 marks it on every revision. It's analog noise with no register to see it in, and a conversion answers the pin's ratio to the reference exactly |
//!
//! **Where the model departs from the vendor's file.** ST types every flag of `SR` read-write with
//! no write rule where 13.13.1 types them `rc_w0`. The model keeps the flags itself and puts them
//! back after a write.
use crate::device::{Device, DeviceContext, Level, RegisterFile, RequestChange};
use crate::soc::FieldSpec;
use crate::soc::stm32f427::generated::adc1::{Register, cr1, cr2, jsqr, sqr1, sqr2, sqr3, sr};
use crate::soc::stm32f427::generated::c_adc::{self, ccr, csr};
use crate::time::{EventId, VirtualNanos};
/// The request output a regular conversion drives, table 44's `ADCx`.
pub const DMA_REQUEST: usize = 0;
/// The six flags as request outputs, `AWD` first, in the order of 13.13.1, which the common block reads.
pub const FLAG_REQUESTS: [usize; 6] = [1, 2, 3, 4, 5, 6];
/// The common block's clock output, `ADCCLK`.
pub const ADCCLK: usize = 0;
/// The common block's request output that is `TSVREFE`, which the first converter reads.
pub const TSVREFE_REQUEST: usize = 0;
/// The common block's request output that is `VBATE`.
pub const VBATE_REQUEST: usize = 1;
/// The instance's one interrupt line, table 70 and table 63 line 18.
const GLOBAL: usize = 0;
const NANOS_PER_SECOND: u128 = 1_000_000_000;
/// `SMPx`, 13.13.4, in `ADCCLK` cycles.
const SAMPLING_CYCLES: [u64; 8] = [3, 15, 28, 56, 84, 112, 144, 480];
/// `RES`, 13.13.2, in bits.
const RESOLUTION_BITS: [u32; 4] = [12, 10, 8, 6];
/// `ADCPRE`, 13.13.16.
const PRESCALERS: [u64; 4] = [2, 4, 6, 8];
const PINS: usize = 16;
const VREFINT_CHANNEL: usize = 17;
const SENSOR_CHANNEL: usize = 18;
/// DS9405 rev 13 table 84: 1.21 V typical.
const VREFINT_MILLIVOLTS: u32 = 1210;
/// DS9405 rev 13 table 81: 0.76 V at 25 °C and 2.5 mV a degree, which is a millivolt every forty
/// hundredths of a degree.
const V25_MILLIVOLTS: i64 = 760;
const CENTI_DEGREES_PER_MILLIVOLT: i64 = 40;
/// DS9405 rev 13 table 83: `VBAT` over four.
const VBAT_DIVIDER: u32 = 4;
const TWELVE_BITS: u64 = 0xFFF;
const FLAGS: [&FieldSpec; 6] = [sr::AWD, sr::EOC, sr::JEOC, sr::JSTRT, sr::STRT, sr::OVR];
/// `SQ1` to `SQ16`, each with the register it sits in, 13.13.9 to 13.13.11.
const SEQUENCE: [(Register, &FieldSpec); 16] = [
(Register::Sqr3, sqr3::SQ1),
(Register::Sqr3, sqr3::SQ2),
(Register::Sqr3, sqr3::SQ3),
(Register::Sqr3, sqr3::SQ4),
(Register::Sqr3, sqr3::SQ5),
(Register::Sqr3, sqr3::SQ6),
(Register::Sqr2, sqr2::SQ7),
(Register::Sqr2, sqr2::SQ8),
(Register::Sqr2, sqr2::SQ9),
(Register::Sqr2, sqr2::SQ10),
(Register::Sqr2, sqr2::SQ11),
(Register::Sqr2, sqr2::SQ12),
(Register::Sqr1, sqr1::SQ13),
(Register::Sqr1, sqr1::SQ14),
(Register::Sqr1, sqr1::SQ15),
(Register::Sqr1, sqr1::SQ16),
];
const INJECTED_SEQUENCE: [&FieldSpec; 4] = [jsqr::JSQ1, jsqr::JSQ2, jsqr::JSQ3, jsqr::JSQ4];
/// The three converters' flags as `CSR` images them, 13.13.15, in the order of [`FLAG_REQUESTS`].
const IMAGES: [[&FieldSpec; 6]; 3] = [
[
csr::AWD1,
csr::EOC1,
csr::JEOC1,
csr::JSTRT1,
csr::STRT1,
csr::OVR1,
],
[
csr::AWD2,
csr::EOC2,
csr::JEOC2,
csr::JSTRT2,
csr::STRT2,
csr::OVR2,
],
[
csr::AWD3,
csr::EOC3,
csr::JEOC3,
csr::JSTRT3,
csr::STRT3,
csr::OVR3,
],
];
const JOFR: [Register; 4] = [
Register::Jofr1,
Register::Jofr2,
Register::Jofr3,
Register::Jofr4,
];
const JDR: [Register; 4] = [
Register::Jdr1,
Register::Jdr2,
Register::Jdr3,
Register::Jdr4,
];
/// An input channel, 0 to 15 on the pins.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Channel(u8);
impl Channel {
/// Channel `number`, 0 to 15.
#[must_use]
pub const fn new(number: u8) -> Option<Self> {
if (number as usize) < PINS {
Some(Self(number))
} else {
None
}
}
}
/// A conversion on the wire, ending at its event.
#[derive(Debug, Clone, Copy, serde::Serialize, serde::Deserialize)]
struct Conversion {
event: EventId,
channel: usize,
group: Group,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
enum Group {
Regular,
Injected,
}
/// A run of conversions: where the last ends, and the fraction of a nanosecond carried.
#[derive(Debug, Clone, Copy, serde::Serialize, serde::Deserialize)]
struct Run {
ends: VirtualNanos,
carry: u64,
}
/// One converter of an STM32F42xxx, RM0090 rev 22 chapter 13.
#[derive(Debug, serde::Serialize, serde::Deserialize)]
pub struct Adc {
clock_hz: u64,
inputs: [u32; PINS],
reference: u32,
centi_celsius: i32,
backup_battery: u32,
enables: [Level; 2],
status: u64,
converting: Option<Conversion>,
run: Option<Run>,
position: usize,
resumes: Option<usize>,
injected_done: usize,
data_unread: bool,
interrupt: Level,
requests: [Level; 7],
}
impl Adc {
/// A converter clocked at `clock_hz`, the `ADCCLK` of 13.3.2, until the common block drives it
/// otherwise. Every pin is at zero and `VREF+` at 3.3 V until the board says otherwise.
#[must_use]
pub fn with_clock(clock_hz: u64) -> Self {
Self {
clock_hz,
inputs: [0; PINS],
reference: 3300,
centi_celsius: 2500,
backup_battery: 0,
enables: [Level::Low; 2],
status: 0,
converting: None,
run: None,
position: 0,
resumes: None,
injected_done: 0,
data_unread: false,
interrupt: Level::Low,
requests: [Level::Low; 7],
}
}
/// What's on `from`'s pins, on these: every input, the reference, the temperature and `VBAT`.
pub fn inherit(&mut self, from: &Self) {
self.inputs = from.inputs;
self.reference = from.reference;
self.centi_celsius = from.centi_celsius;
self.backup_battery = from.backup_battery;
}
/// The voltage on `channel`'s pin from now on, in millivolts.
pub fn set_input(&mut self, channel: Channel, millivolts: u32) {
self.inputs[channel.0 as usize] = millivolts;
}
/// `VREF+`, which is `VDDA` where the pin isn't brought out, table 65, in millivolts.
pub fn set_reference(&mut self, millivolts: u32) {
self.reference = millivolts;
}
/// The junction temperature the sensor of 13.10 measures, in hundredths of a degree Celsius.
pub fn set_temperature(&mut self, centi_celsius: i32) {
self.centi_celsius = centi_celsius;
}
/// The voltage on the `VBAT` pin, 13.11, in millivolts.
pub fn set_backup_battery(&mut self, millivolts: u32) {
self.backup_battery = millivolts;
}
/// The voltage a channel presents to the converter.
fn millivolts(&self, channel: usize) -> u32 {
let sensors = self.enables[TSVREFE_REQUEST] == Level::High;
let battery = self.enables[VBATE_REQUEST] == Level::High;
match channel {
VREFINT_CHANNEL if sensors => VREFINT_MILLIVOLTS,
SENSOR_CHANNEL if battery => self.backup_battery / VBAT_DIVIDER,
SENSOR_CHANNEL if sensors => {
let above = i64::from(self.centi_celsius) - 2500;
u32::try_from((V25_MILLIVOLTS + above / CENTI_DEGREES_PER_MILLIVOLT).max(0))
.expect("a positive voltage")
}
pin if pin < PINS => self.inputs[pin],
_ => 0,
}
}
fn bits(registers: &RegisterFile) -> u32 {
RESOLUTION_BITS
[usize::try_from(cr1::RES.get(registers.get(Register::Cr1))).expect("two bits")]
}
/// The word at the resolution, before alignment, 13.4: the pin's share of the reference, saturated.
fn word(&self, channel: usize, registers: &RegisterFile) -> u64 {
let full = (1_u64 << Self::bits(registers)) - 1;
if self.reference == 0 {
return full;
}
(u64::from(self.millivolts(channel)) * full / u64::from(self.reference)).min(full)
}
/// Where the bits of a word sit, 13.4: right aligned as they are, left aligned against the top of the
/// half-word, or of the byte at six bits, with a bit spare for the sign of an injected word.
fn aligned(value: i64, group: Group, registers: &RegisterFile) -> u64 {
let bits = Self::bits(registers);
let base = if bits == 6 { 8 } else { 16 };
let shift = if cr2::ALIGN.is_set(registers.get(Register::Cr2)) {
base - bits - u32::from(group == Group::Injected)
} else {
0
};
u64::try_from((value << shift) & i64::from(u16::MAX)).expect("masked")
}
/// `SMPx` of `channel`, 13.13.4 and 13.13.5, in cycles. ST's file has each register as one
/// field, so the channel's three bits are taken by hand.
fn sampling_cycles(channel: usize, registers: &RegisterFile) -> u64 {
let (register, index) = if channel < 10 {
(Register::Smpr2, channel)
} else {
(Register::Smpr1, channel - 10)
};
let field = (registers.get(register) >> (3 * index)) & 0b111;
SAMPLING_CYCLES[usize::try_from(field).expect("three bits")]
}
/// How long the regular sequence is: `L + 1` channels under `SCAN`, the first alone without.
fn length(registers: &RegisterFile) -> usize {
if cr1::SCAN.is_set(registers.get(Register::Cr1)) {
usize::try_from(sqr1::L.get(registers.get(Register::Sqr1))).expect("four bits") + 1
} else {
1
}
}
/// The `nth` channel of the regular sequence, `SQ1` to `SQ16` across `SQR3`, `SQR2` and `SQR1`.
fn regular(registers: &RegisterFile, nth: usize) -> usize {
let (register, field) = SEQUENCE[nth];
usize::try_from(field.get(registers.get(register))).expect("five bits")
}
fn injected_length(registers: &RegisterFile) -> usize {
usize::try_from(jsqr::JL.get(registers.get(Register::Jsqr))).expect("two bits") + 1
}
/// The `nth` channel of the injected sequence, 13.13.12's note: the last `JL + 1` of `JSQ1` to
/// `JSQ4`, in order.
fn injected(registers: &RegisterFile, nth: usize) -> usize {
let slot = 4 - Self::injected_length(registers) + nth;
usize::try_from(INJECTED_SEQUENCE[slot].get(registers.get(Register::Jsqr)))
.expect("five bits")
}
/// Puts the next channel of `group` on the converter, ending after its sampling and its
/// resolution's cycles. When `chained` it starts from the end of the conversion before it,
/// carrying that one's fraction of a nanosecond, and otherwise from now.
fn convert(
&mut self,
group: Group,
chained: bool,
registers: &mut RegisterFile,
context: &mut DeviceContext<'_>,
) {
if self.clock_hz == 0 {
return;
}
let channel = match group {
Group::Regular => Self::regular(registers, self.position),
Group::Injected => Self::injected(registers, self.injected_done),
};
let cycles = Self::sampling_cycles(channel, registers) + u64::from(Self::bits(registers));
let now = context.now();
let (start, carry) = match self.run {
Some(run) if chained => (run.ends, run.carry),
_ => (now, 0),
};
let numerator = u128::from(cycles) * NANOS_PER_SECOND + u128::from(carry);
let denominator = u128::from(self.clock_hz);
let nanos = u64::try_from(numerator / denominator).expect("a conversion fits the clock");
let carry = u64::try_from(numerator % denominator).expect("less than the denominator");
let ends = VirtualNanos(start.0.saturating_add(nanos));
self.run = Some(Run { ends, carry });
self.converting = Some(Conversion {
event: context.schedule(ends),
channel,
group,
});
}
/// Starts the regular group at `position`, 13.3.4 and 13.3.10.
fn start_regular(
&mut self,
chained: bool,
registers: &mut RegisterFile,
context: &mut DeviceContext<'_>,
) {
if self.position >= Self::length(registers) {
self.position = 0;
}
self.status = sr::STRT.set(self.status, 1);
self.convert(Group::Regular, chained, registers, context);
}
/// Starts the injected group from where it stands, 13.3.9.
fn start_injected(
&mut self,
chained: bool,
registers: &mut RegisterFile,
context: &mut DeviceContext<'_>,
) {
self.status = sr::JSTRT.set(self.status, 1);
self.convert(Group::Injected, chained, registers, context);
}
/// The watchdog of 13.3.7 and table 66 on a word before alignment.
fn watch(&mut self, channel: usize, word: u64, group: Group, registers: &RegisterFile) {
let control = registers.get(Register::Cr1);
let guarded = match group {
Group::Regular => cr1::AWDEN.is_set(control),
Group::Injected => cr1::JAWDEN.is_set(control),
};
let single = cr1::AWDSGL.is_set(control)
&& usize::try_from(cr1::AWDCH.get(control)).expect("five bits") != channel;
if !guarded || single {
return;
}
let low = registers.get(Register::Ltr) & TWELVE_BITS;
let high = registers.get(Register::Htr) & TWELVE_BITS;
if word < low || word > high {
self.status = sr::AWD.set(self.status, 1);
}
}
/// A conversion has ended.
fn finish(
&mut self,
conversion: Conversion,
registers: &mut RegisterFile,
context: &mut DeviceContext<'_>,
) {
self.converting = None;
let word = self.word(conversion.channel, registers);
self.watch(conversion.channel, word, conversion.group, registers);
match conversion.group {
Group::Injected => self.finish_injected(word, registers, context),
Group::Regular => self.finish_regular(word, registers, context),
}
}
fn finish_injected(
&mut self,
word: u64,
registers: &mut RegisterFile,
context: &mut DeviceContext<'_>,
) {
let slot = self.injected_done;
let offset = i64::try_from(registers.get(JOFR[slot]) & TWELVE_BITS).expect("twelve bits");
let value = i64::try_from(word).expect("sixteen bits") - offset;
registers.set(JDR[slot], Self::aligned(value, Group::Injected, registers));
self.injected_done += 1;
if self.injected_done < Self::injected_length(registers) {
self.start_injected(true, registers, context);
return;
}
self.injected_done = 0;
self.status = sr::JEOC.set(self.status, 1);
if let Some(position) = self.resumes.take() {
self.position = position;
self.start_regular(true, registers, context);
}
}
fn finish_regular(
&mut self,
word: u64,
registers: &mut RegisterFile,
context: &mut DeviceContext<'_>,
) {
let control = registers.get(Register::Cr1);
let control2 = registers.get(Register::Cr2);
let by_dma = cr2::DMA.is_set(control2);
let each = cr2::EOCS.is_set(control2) || cr1::DISCEN.is_set(control);
if self.data_unread && (by_dma || cr2::EOCS.is_set(control2)) {
self.status = sr::OVR.set(self.status, 1);
self.position = 0;
return;
}
registers.set(
Register::Dr,
Self::aligned(
i64::try_from(word).expect("sixteen bits"),
Group::Regular,
registers,
),
);
self.data_unread = true;
self.position += 1;
let done = self.position >= Self::length(registers);
if each || done {
self.status = sr::EOC.set(self.status, 1);
}
if cr1::DISCEN.is_set(control) {
let taken = self.position
% (usize::try_from(cr1::DISCNUM.get(control)).expect("three bits") + 1);
if done {
self.position = 0;
}
if taken == 0 || done {
return;
}
}
if !done {
self.start_regular(true, registers, context);
return;
}
self.position = 0;
if cr1::JAUTO.is_set(control) {
self.resumes = cr2::CONT.is_set(control2).then_some(0);
self.start_injected(true, registers, context);
} else if cr2::CONT.is_set(control2) {
self.start_regular(true, registers, context);
}
}
/// `SWSTART`, `JSWSTART` and `ADON` as written, 13.13.3.
fn write_cr2(&mut self, registers: &mut RegisterFile, context: &mut DeviceContext<'_>) {
let value = registers.get(Register::Cr2);
let kept = cr2::JSWSTART.set(cr2::SWSTART.set(value, 0), 0);
if !cr2::ADON.is_set(value) {
self.converting = None;
self.position = 0;
self.injected_done = 0;
self.resumes = None;
registers.set(Register::Cr2, kept);
return;
}
if cr2::SWSTART.is_set(value)
&& !sr::OVR.is_set(self.status)
&& self.converting.is_none()
&& self.resumes.is_none()
{
self.start_regular(false, registers, context);
}
if cr2::JSWSTART.is_set(value) {
match self.converting {
Some(conversion) if conversion.group == Group::Regular => {
self.resumes = Some(self.position);
self.start_injected(false, registers, context);
}
Some(_) => {}
None => self.start_injected(false, registers, context),
}
}
registers.set(Register::Cr2, kept);
}
/// The line of table 70 and the seven request outputs. The model keeps the level it last drove.
fn refresh_signals(&mut self, registers: &mut RegisterFile, context: &mut DeviceContext<'_>) {
registers.set(Register::Sr, self.status);
let control = registers.get(Register::Cr1);
let control2 = registers.get(Register::Cr2);
let dma = cr2::DMA.is_set(control2) && self.data_unread && !sr::OVR.is_set(self.status);
let wanted = std::iter::once(Level::from(dma)).chain(
FLAGS
.iter()
.map(|flag| Level::from(flag.is_set(self.status))),
);
for (output, level) in wanted.enumerate() {
if level != self.requests[output] {
self.requests[output] = level;
context.request(output, level);
}
}
let line = (cr1::EOCIE.is_set(control) && sr::EOC.is_set(self.status))
|| (cr1::JEOCIE.is_set(control) && sr::JEOC.is_set(self.status))
|| (cr1::AWDIE.is_set(control) && sr::AWD.is_set(self.status))
|| (cr1::OVRIE.is_set(control) && sr::OVR.is_set(self.status));
let line = Level::from(line);
if line != self.interrupt {
self.interrupt = line;
context.interrupt(GLOBAL, line);
}
}
}
impl Device for Adc {
type Register = Register;
fn on_clock(
&mut self,
hz: u64,
_registers: &mut RegisterFile,
_context: &mut DeviceContext<'_>,
) {
if hz == self.clock_hz {
return;
}
self.clock_hz = hz;
if let Some(run) = &mut self.run {
run.carry = 0;
}
}
/// The common block's `TSVREFE` and `VBATE`, 13.13.16, which reach ADC1 alone.
fn on_request(
&mut self,
change: RequestChange,
_registers: &mut RegisterFile,
_context: &mut DeviceContext<'_>,
) {
if change.instance != super::ADC_COMMON {
return;
}
if let Some(enable) = self.enables.get_mut(change.request) {
*enable = change.level;
}
}
/// `DR` clears `EOC` and takes the word, 13.13.1.
fn read(
&mut self,
register: Register,
registers: &mut RegisterFile,
context: &mut DeviceContext<'_>,
) -> Option<u64> {
if register != Register::Dr {
return None;
}
self.status = sr::EOC.set(self.status, 0);
self.data_unread = false;
self.refresh_signals(registers, context);
Some(registers.get(Register::Dr))
}
fn write(
&mut self,
register: Register,
value: u64,
registers: &mut RegisterFile,
context: &mut DeviceContext<'_>,
) {
match register {
Register::Sr => {
self.status &= value;
if !sr::EOC.is_set(self.status) {
self.data_unread = false;
}
}
Register::Cr2 => self.write_cr2(registers, context),
_ => {}
}
self.refresh_signals(registers, context);
}
fn on_event(
&mut self,
event: EventId,
registers: &mut RegisterFile,
context: &mut DeviceContext<'_>,
) {
let Some(conversion) = self
.converting
.filter(|conversion| conversion.event == event)
else {
return;
};
self.finish(conversion, registers, context);
self.refresh_signals(registers, context);
}
}
/// The common block of the three converters, 13.13.15 to 13.13.17: the prescaler that clocks them, the
/// enables of ADC1's internal channels, and an image of their flags.
#[derive(Debug, serde::Serialize, serde::Deserialize)]
pub struct AdcCommon {
pclk2_hz: u64,
driven_hz: u64,
flags: [[Level; 6]; 3],
requests: [Level; 2],
}
impl AdcCommon {
/// The block on a peripheral bus at `pclk2_hz`, until the tree drives it otherwise.
#[must_use]
pub fn with_clock(pclk2_hz: u64) -> Self {
Self {
pclk2_hz,
driven_hz: pclk2_hz / PRESCALERS[0],
flags: [[Level::Low; 6]; 3],
requests: [Level::Low; 2],
}
}
/// The rate the block drives its converters at until the tree or a firmware moves it.
pub(super) const fn driven_hz(&self) -> u64 {
self.driven_hz
}
/// `ADCCLK`, 13.3.2 and 13.13.16.
fn adc_clock(&self, registers: &RegisterFile) -> u64 {
let prescaler =
PRESCALERS[usize::try_from(ccr::ADCPRE.get(registers.get(c_adc::Register::Ccr)))
.expect("two bits")];
self.pclk2_hz / prescaler
}
fn refresh(&mut self, registers: &mut RegisterFile, context: &mut DeviceContext<'_>) {
let control = registers.get(c_adc::Register::Ccr);
let adc_hz = self.adc_clock(registers);
if adc_hz != self.driven_hz {
self.driven_hz = adc_hz;
context.clock(ADCCLK, adc_hz);
}
for (output, enable) in [(TSVREFE_REQUEST, ccr::TSVREFE), (VBATE_REQUEST, ccr::VBATE)] {
let level = Level::from(enable.is_set(control));
if level != self.requests[output] {
self.requests[output] = level;
context.request(output, level);
}
}
let image = IMAGES
.iter()
.zip(&self.flags)
.flat_map(|(fields, levels)| fields.iter().zip(levels))
.fold(0, |image, (field, level)| {
field.set(image, u64::from(*level == Level::High))
});
registers.set(c_adc::Register::Csr, image);
}
}
impl Device for AdcCommon {
type Register = c_adc::Register;
fn on_clock(&mut self, hz: u64, registers: &mut RegisterFile, context: &mut DeviceContext<'_>) {
self.pclk2_hz = hz;
self.refresh(registers, context);
}
/// A converter's flag, as it drives it, into the image `CSR` is, 13.13.15.
fn on_request(
&mut self,
change: RequestChange,
registers: &mut RegisterFile,
context: &mut DeviceContext<'_>,
) {
let Some(which) = super::CONVERTERS
.iter()
.position(|instance| *instance == change.instance)
else {
return;
};
let Some(flag) = FLAG_REQUESTS
.iter()
.position(|output| *output == change.request)
else {
return;
};
self.flags[which][flag] = change.level;
self.refresh(registers, context);
}
fn write(
&mut self,
register: c_adc::Register,
_value: u64,
registers: &mut RegisterFile,
context: &mut DeviceContext<'_>,
) {
if register == c_adc::Register::Ccr {
self.refresh(registers, context);
}
}
}