diff --git a/benches/gust/drivers/adc-thin/adc-thin-cm3.o b/benches/gust/drivers/adc-thin/adc-thin-cm3.o index e2630885..15e1fa3d 100644 Binary files a/benches/gust/drivers/adc-thin/adc-thin-cm3.o and b/benches/gust/drivers/adc-thin/adc-thin-cm3.o differ diff --git a/benches/gust/drivers/adc-thin/src/lib.rs b/benches/gust/drivers/adc-thin/src/lib.rs index aac49303..24519da4 100644 --- a/benches/gust/drivers/adc-thin/src/lib.rs +++ b/benches/gust/drivers/adc-thin/src/lib.rs @@ -261,23 +261,29 @@ fn wr(a: u32, v: u32) { /// Configure the ADC at `base` for a single-channel single conversion: sample time /// (SMPRx picked by channel), regular sequence (SQR3 channel, SQR1 length 1), and /// power on with CONT=0 (single-shot — never free-run). Table-free: every value is -/// bit arithmetic. +/// bit arithmetic. `cr2_extra` = caller-supplied CR2 bits OR'd into the managed +/// CR2 write (e.g. `TSVREFE` (1<<23) to read F1 internal channels Vrefint/temp; also +/// ALIGN, EXTTRIG). The managed bits (ADON, CONT=0) stay authoritative — gale#216. #[no_mangle] -pub extern "C" fn adc_configure(base: u32, channel: u32, sample_code: u32) { +pub extern "C" fn adc_configure(base: u32, channel: u32, sample_code: u32, cr2_extra: u32) { wr(base + smpr_reg(channel), smpr_bits(channel, sample_code)); wr(base + SQR3, sqr3_channel(channel)); wr(base + SQR1, sqr1_length(1)); - // ADON on, CONT off: exactly one conversion per SWSTART. - wr(base + CR2, CR2_ADON); + // ADON on, CONT off: exactly one conversion per SWSTART. cr2_extra ORs in + // caller-supplied CR2 bits (e.g. TSVREFE) without touching the managed bits. + wr(base + CR2, CR2_ADON | cr2_extra); } /// Power on + select `channel`: Off → Ready. Returns the packed Ready state or -/// `ADC_FAULT` (bad channel / wrong phase). Sets CR2.ADON. +/// `ADC_FAULT` (bad channel / wrong phase). Sets CR2.ADON. `cr2_extra` = caller- +/// supplied CR2 bits OR'd into the managed write (e.g. `TSVREFE` (1<<23) to read F1 +/// internal channels Vrefint/temp; also ALIGN, EXTTRIG). The managed bits (ADON, +/// CONT=0) stay authoritative — gale#216. #[no_mangle] -pub extern "C" fn adc_enable(base: u32, state: u32, channel: u32) -> u32 { +pub extern "C" fn adc_enable(base: u32, state: u32, channel: u32, cr2_extra: u32) -> u32 { match enable(unpack(state), channel) { Ok(next) => { - wr(base + CR2, CR2_ADON); + wr(base + CR2, CR2_ADON | cr2_extra); pack(next) } Err(_) => ADC_FAULT, @@ -286,11 +292,14 @@ pub extern "C" fn adc_enable(base: u32, state: u32, channel: u32) -> u32 { /// Start one conversion (SWSTART): Ready → Converting. Returns the packed Converting /// state or `ADC_FAULT` if not Ready. Sets CR2.SWSTART (keeps ADON, CONT stays 0). +/// `cr2_extra` = caller-supplied CR2 bits OR'd into the managed write (e.g. +/// `TSVREFE` (1<<23) to read F1 internal channels Vrefint/temp; also ALIGN, +/// EXTTRIG). The managed bits (ADON, SWSTART, CONT=0) stay authoritative — gale#216. #[no_mangle] -pub extern "C" fn adc_start(base: u32, state: u32) -> u32 { +pub extern "C" fn adc_start(base: u32, state: u32, cr2_extra: u32) -> u32 { match begin(unpack(state)) { Ok(next) => { - wr(base + CR2, CR2_ADON | CR2_SWSTART); + wr(base + CR2, CR2_ADON | CR2_SWSTART | cr2_extra); pack(next) } Err(_) => ADC_FAULT, diff --git a/benches/gust/src/bin/gust_adc.rs b/benches/gust/src/bin/gust_adc.rs index e04eb986..c2f058b7 100644 --- a/benches/gust/src/bin/gust_adc.rs +++ b/benches/gust/src/bin/gust_adc.rs @@ -28,9 +28,9 @@ pub extern "C" fn mmio_write32(addr: u32, val: u32) { } extern "C" { - fn adc_configure(base: u32, channel: u32, sample_code: u32); - fn adc_enable(base: u32, state: u32, channel: u32) -> u32; - fn adc_start(base: u32, state: u32) -> u32; + fn adc_configure(base: u32, channel: u32, sample_code: u32, cr2_extra: u32); + fn adc_enable(base: u32, state: u32, channel: u32, cr2_extra: u32) -> u32; + fn adc_start(base: u32, state: u32, cr2_extra: u32) -> u32; fn adc_poll(base: u32, state: u32) -> u32; fn adc_read(base: u32, state: u32) -> u32; fn adc_sample(state: u32) -> u32; @@ -99,7 +99,7 @@ fn main() -> ! { // 0) channel bounds: enable with a channel above MAX_CHANNEL is rejected, no // CR2 write. - let bad = adc_enable(ADC1, 0, MAX_CHANNEL + 1); + let bad = adc_enable(ADC1, 0, MAX_CHANNEL + 1, 0); let cr2_bad = read_volatile((ADC1 + CR2) as *const u32); tx(if bad == ADC_FAULT && cr2_bad == 0 { b"adc-chanbound-ok\n" @@ -108,7 +108,7 @@ fn main() -> ! { }); // 1) enable: Off → Ready, the DRIVER writes CR2=ADON. - let s1 = adc_enable(ADC1, 0, CH); + let s1 = adc_enable(ADC1, 0, CH, 0); let cr2_1 = read_volatile((ADC1 + CR2) as *const u32); tx(if s1 != ADC_FAULT && phase_of(s1) == PH_READY && cr2_1 == CR2_ADON { b"adc-enable-ok\n" @@ -118,7 +118,7 @@ fn main() -> ! { // 2) configure: table-free SMPR2/SQR3/SQR1 land exactly. // smpr_bits(3,4)=4<<9=0x800; SQR3 SQ1=3; SQR1 length(1)=0; CR2=ADON. - adc_configure(ADC1, CH, SAMPLE_CODE); + adc_configure(ADC1, CH, SAMPLE_CODE, 0); let smpr2 = read_volatile((ADC1 + SMPR2) as *const u32); let sqr3 = read_volatile((ADC1 + SQR3) as *const u32); let sqr1 = read_volatile((ADC1 + SQR1) as *const u32); @@ -130,7 +130,7 @@ fn main() -> ! { }); // 3) start: Ready → Converting, the DRIVER writes CR2=ADON|SWSTART. - let s3 = adc_start(ADC1, s1); + let s3 = adc_start(ADC1, s1, 0); let cr2_3 = read_volatile((ADC1 + CR2) as *const u32); tx(if s3 != ADC_FAULT && phase_of(s3) == PH_CONVERTING && cr2_3 == (CR2_ADON | CR2_SWSTART) { b"adc-start-ok\n" @@ -172,7 +172,7 @@ fn main() -> ! { // 7) single-shot: the read landed Ready (not Converting); reading again is // rejected and re-converting needs an explicit start. let twice = adc_read(ADC1, s6); - let rearm = adc_start(ADC1, s6); + let rearm = adc_start(ADC1, s6, 0); tx(if twice == ADC_FAULT && rearm != ADC_FAULT && phase_of(rearm) == PH_CONVERTING { b"adc-single-shot-ok\n" } else { diff --git a/benches/gust/src/bin/gust_adc_probe.rs b/benches/gust/src/bin/gust_adc_probe.rs index 712c35eb..50aeb211 100644 --- a/benches/gust/src/bin/gust_adc_probe.rs +++ b/benches/gust/src/bin/gust_adc_probe.rs @@ -30,9 +30,9 @@ pub extern "C" fn mmio_write32(addr: u32, val: u32) { } extern "C" { - fn adc_configure(base: u32, channel: u32, sample_code: u32); - fn adc_enable(base: u32, state: u32, channel: u32) -> u32; - fn adc_start(base: u32, state: u32) -> u32; + fn adc_configure(base: u32, channel: u32, sample_code: u32, cr2_extra: u32); + fn adc_enable(base: u32, state: u32, channel: u32, cr2_extra: u32) -> u32; + fn adc_start(base: u32, state: u32, cr2_extra: u32) -> u32; fn adc_poll(base: u32, state: u32) -> u32; fn adc_read(base: u32, state: u32) -> u32; fn adc_sample(state: u32) -> u32; @@ -88,7 +88,7 @@ fn main() -> ! { // 0) channel bounds: enable with a channel above MAX_CHANNEL (18) MUST be rejected // — ADC_FAULT, no CR2 write. A dissolve that silently no-ops the bound check // would latch an out-of-range mux input. - let bad = unsafe { adc_enable(base, 0, MAX_CHANNEL + 1) }; + let bad = unsafe { adc_enable(base, 0, MAX_CHANNEL + 1, 0) }; let cr2_after_bad = rw(CR2); if bad != ADC_FAULT || cr2_after_bad != 0 { hprintln!("adc-chanbound FAIL: enable(18)={:#x} CR2={:#x}", bad, cr2_after_bad); @@ -98,7 +98,7 @@ fn main() -> ! { } // 1) enable: Off(state=0) → Ready, driver WRITES CR2=ADON (a no-op leaves it 0). - let s1 = unsafe { adc_enable(base, 0, CH) }; + let s1 = unsafe { adc_enable(base, 0, CH, 0) }; let cr2_1 = rw(CR2); if s1 == ADC_FAULT || phase_of(s1) != PH_READY || cr2_1 != CR2_ADON { hprintln!("adc-enable FAIL: s1={:#x} phase={} CR2={:#x}", s1, phase_of(s1), cr2_1); @@ -110,7 +110,7 @@ fn main() -> ! { // 2) configure: table-free bit arithmetic lands in SMPR2/SQR3/SQR1 exactly. // smpr_bits(3,4) = 4 << ((3%10)*3) = 4 << 9 = 0x800 in SMPR2; SQR3 SQ1 = 3; // SQR1 length(1) = 0; CR2 = ADON (single-shot, CONT=0). - unsafe { adc_configure(base, CH, SAMPLE_CODE) }; + unsafe { adc_configure(base, CH, SAMPLE_CODE, 0) }; let smpr2 = rw(SMPR2); let sqr3 = rw(SQR3); let sqr1 = rw(SQR1); @@ -126,7 +126,7 @@ fn main() -> ! { } // 3) start: Ready → Converting, driver WRITES CR2=ADON|SWSTART (keeps CONT=0). - let s3 = unsafe { adc_start(base, s1) }; + let s3 = unsafe { adc_start(base, s1, 0) }; let cr2_3 = rw(CR2); if s3 == ADC_FAULT || phase_of(s3) != PH_CONVERTING || cr2_3 != (CR2_ADON | CR2_SWSTART) { hprintln!("adc-start FAIL: s3={:#x} phase={} CR2={:#x}", s3, phase_of(s3), cr2_3); @@ -175,7 +175,7 @@ fn main() -> ! { // free-runs. Reading AGAIN from Ready is rejected (exactly-once), and the only // way back to Converting is an explicit start — not an implicit re-arm. let twice = unsafe { adc_read(base, s6) }; - let rearm = unsafe { adc_start(base, s6) }; + let rearm = unsafe { adc_start(base, s6, 0) }; if twice != ADC_FAULT || rearm == ADC_FAULT || phase_of(rearm) != PH_CONVERTING { hprintln!("adc-single-shot FAIL: read-twice={:#x} restart={:#x} phase={}", twice, rearm, phase_of(rearm)); ok = false;