rmk_q6_he_ansi/matrix/analog_matrix/
calibration.rs1use super::{AdcSampleTime, AnalogHallMatrix, get2};
9use crate::{
10 backlight::{
11 led_processor::{BACKLIGHT_CH, BacklightCmd, CalibPhase},
12 mapping::MATRIX_TO_LED,
13 },
14 matrix::{
15 analog_matrix::types::{
16 BOTTOM_JITTER,
17 CALIB_HOLD_DURATION_MS,
18 CALIB_PRESS_THRESHOLD,
19 CALIB_SETTLE_AFTER_ALL_DONE,
20 CALIB_ZERO_TOLERANCE,
21 DEFAULT_FULL_RANGE,
22 KeyCalibState,
23 MIN_USEFUL_FULL_RANGE,
24 REF_ZERO_TRAVEL,
25 UNCALIBRATED_ZERO,
26 VALID_RAW_MIN,
27 ZERO_TRAVEL_DEAD_ZONE,
28 },
29 calib_store,
30 calib_store::{CALIB_BUF_LEN, CalibEntry, EEPROM_BASE_ADDR},
31 sensor_mapping::SENSOR_POSITIONS,
32 },
33};
34use calib_store::try_deserialize;
35use embassy_stm32::{
36 adc::{BasicInstance, Instance, RxDma},
37 dma::InterruptHandler,
38 i2c::mode::MasterMode,
39 interrupt::typelevel::Binding,
40 pac::adc,
41};
42use embassy_time::{Duration, Instant, Timer};
43
44impl<'peripherals, ADC, D, IRQ, IM, const ROW: usize, const COL: usize>
45 AnalogHallMatrix<'peripherals, ADC, D, IRQ, IM, ROW, COL>
46where
47 ADC: Instance<Regs = adc::Adc> + BasicInstance,
48 D: RxDma<ADC>,
49 IRQ: Binding<D::Interrupt, InterruptHandler<D>> + Copy + 'peripherals,
50 IM: MasterMode,
51 AdcSampleTime<ADC>: Clone,
52{
53 pub(super) const fn default_entries() -> [[CalibEntry; COL]; ROW] {
57 [[CalibEntry { full: UNCALIBRATED_ZERO.saturating_sub(DEFAULT_FULL_RANGE) }; COL]; ROW]
58 }
59
60 pub(super) async fn calibrate_zero_raw(&mut self) -> [[u16; COL]; ROW] {
69 let mut acc = [[0_u32; COL]; ROW];
70 let mut seq = self.adc_part.configured_sequence(self.irq);
71 let mut buf = [0_u16; ROW];
72
73 for _ in 0..self.cfg.calib_passes {
74 for col in 0..COL {
75 self.cols.select(col);
76 Timer::after(self.cfg.col_settle_us).await;
77 seq.read(&mut buf).await;
78 for (acc_row, &raw) in acc.iter_mut().zip(buf.iter()) {
79 if let Some(cell) = acc_row.get_mut(col) {
80 *cell = cell.saturating_add(u32::from(raw));
81 }
82 }
83 }
84 }
85
86 let mut result = [[UNCALIBRATED_ZERO; COL]; ROW];
87 for (res_row, acc_row) in result.iter_mut().zip(acc.iter()) {
88 for (res, &total) in res_row.iter_mut().zip(acc_row.iter()) {
89 if let Some(avg) = total.checked_div(self.cfg.calib_passes) {
92 *res = u16::try_from(avg).unwrap_or(UNCALIBRATED_ZERO).saturating_sub(ZERO_TRAVEL_DEAD_ZONE);
93 }
94 }
95 }
96 result
97 }
98
99 pub(super) async fn sample_full_raw(
112 &mut self,
113 duration: Duration,
114 zero_raw: &[[u16; COL]; ROW],
115 ) -> [[u16; COL]; ROW] {
116 let deadline = Instant::now() + duration;
117 let mut min_raw = [[u16::MAX; COL]; ROW];
118
119 let mut update_min = |row: usize, col: usize, raw: u16| {
121 if let Some(cell) = min_raw.get_mut(row).and_then(|r| r.get_mut(col)) {
122 *cell = (*cell).min(raw);
123 }
124 };
125
126 let mut calib_state: [[KeyCalibState; COL]; ROW] = [[KeyCalibState::Waiting; COL]; ROW];
127 let mut calibrated_count: usize = 0;
128 let hold_duration = Duration::from_millis(CALIB_HOLD_DURATION_MS);
129
130 let total_keys = (0..ROW)
133 .flat_map(|r| (0..COL).map(move |c| (r, c)))
134 .filter(|&(r, c)| {
135 SENSOR_POSITIONS.get(r).and_then(|row| row.get(c)).copied().unwrap_or(false)
136 && get2(zero_raw, r, c).is_some_and(|z| z.abs_diff(REF_ZERO_TRAVEL) <= CALIB_ZERO_TOLERANCE)
137 })
138 .count()
139 .max(1);
140
141 let mut seq = self.adc_part.configured_sequence(self.irq);
142 let mut buf = [0_u16; ROW];
143 let mut last_pct: u8 = 0;
144
145 while Instant::now() < deadline && calibrated_count < total_keys {
147 for col in 0..COL {
148 self.cols.select(col);
149 Timer::after(self.cfg.col_settle_us).await;
150 seq.read(&mut buf).await;
151
152 for (row, &raw) in buf.iter().enumerate() {
153 update_min(row, col, raw);
156
157 if !SENSOR_POSITIONS.get(row).and_then(|r| r.get(col)).copied().unwrap_or(false) {
158 continue;
159 }
160
161 let Some(key_state) = calib_state.get_mut(row).and_then(|r| r.get_mut(col)) else {
162 continue;
163 };
164
165 if matches!(*key_state, KeyCalibState::Accepted) {
167 continue;
168 }
169
170 let zero = get2(zero_raw, row, col).unwrap_or(UNCALIBRATED_ZERO);
171 let pressed = zero.saturating_sub(raw) >= CALIB_PRESS_THRESHOLD;
172
173 match *key_state {
174 KeyCalibState::Waiting => {
175 if pressed {
176 *key_state = KeyCalibState::Holding(Instant::now());
178 }
179 }
180 KeyCalibState::Holding(first_seen) => {
181 if pressed {
182 if first_seen.elapsed() >= hold_duration {
183 *key_state = KeyCalibState::Accepted;
185 calibrated_count = calibrated_count.saturating_add(1);
186
187 if let Some(Some(led_idx)) =
188 MATRIX_TO_LED.get(row).and_then(|r| r.get(col)).copied()
189 {
190 BACKLIGHT_CH.sender().try_send(BacklightCmd::CalibKeyDone(led_idx)).ok();
191 }
192
193 let pct = u8::try_from(
194 calibrated_count.saturating_mul(100).checked_div(total_keys).unwrap_or(0),
195 )
196 .unwrap_or(100)
197 .min(100);
198
199 if pct != last_pct {
200 last_pct = pct;
201 BACKLIGHT_CH.sender().try_send(BacklightCmd::CalibProgress(pct)).ok();
202 }
203 }
204 } else {
206 *key_state = KeyCalibState::Waiting;
209 }
210 }
211 KeyCalibState::Accepted => {}
212 }
213 }
214 }
215 }
216
217 if calibrated_count >= total_keys {
221 BACKLIGHT_CH.sender().try_send(BacklightCmd::CalibPhase(CalibPhase::AllAccepted)).ok();
222 }
223
224 let settle_deadline = Instant::now().saturating_add(CALIB_SETTLE_AFTER_ALL_DONE);
230 while Instant::now() < settle_deadline {
231 for col in 0..COL {
232 self.cols.select(col);
233 Timer::after(self.cfg.col_settle_us).await;
234 seq.read(&mut buf).await;
235 for (row, &raw) in buf.iter().enumerate() {
236 update_min(row, col, raw);
237 }
238 }
239 }
240
241 min_raw
242 }
243
244 pub(super) async fn run_first_boot_calib(
258 &mut self,
259 eeprom_buf: &mut [u8; CALIB_BUF_LEN],
260 entries: &mut [[CalibEntry; COL]; ROW],
261 ) {
262 BACKLIGHT_CH.sender().try_send(BacklightCmd::CalibPhase(CalibPhase::Zero)).ok();
263 let zero_raw = self.calibrate_zero_raw().await;
264
265 BACKLIGHT_CH.sender().try_send(BacklightCmd::CalibPhase(CalibPhase::Full)).ok();
266 let full_raw = self.sample_full_raw(self.cfg.full_calib_duration, &zero_raw).await;
267
268 for ((entry_row, zero_row), full_row) in entries.iter_mut().zip(zero_raw.iter()).zip(full_raw.iter()) {
269 for ((entry, &zero), &seen_min) in entry_row.iter_mut().zip(zero_row.iter()).zip(full_row.iter()) {
270 let full = if zero > seen_min && zero.saturating_sub(seen_min) >= MIN_USEFUL_FULL_RANGE {
271 seen_min.saturating_add(BOTTOM_JITTER).min(zero.saturating_sub(MIN_USEFUL_FULL_RANGE))
272 } else {
273 zero.saturating_sub(DEFAULT_FULL_RANGE).max(VALID_RAW_MIN)
274 };
275 *entry = CalibEntry { full };
276 }
277 }
278
279 self.apply_calib(entries, &zero_raw);
280
281 Timer::after_micros(200).await;
283 calib_store::serialize(entries, eeprom_buf);
284 let erase_ok = self.eeprom.zero_out().await.is_ok();
285 let write_ok = self.eeprom.write(EEPROM_BASE_ADDR, eeprom_buf).await.is_ok();
286
287 let verified = erase_ok
290 && write_ok
291 && self.eeprom.read(EEPROM_BASE_ADDR, eeprom_buf).await.is_ok()
292 && try_deserialize::<ROW, COL>(eeprom_buf, entries);
293
294 if !verified {
295 BACKLIGHT_CH.sender().try_send(BacklightCmd::CalibPhase(CalibPhase::Zero)).ok();
298 return;
299 }
300
301 BACKLIGHT_CH.sender().try_send(BacklightCmd::CalibPhase(CalibPhase::Done)).ok();
302 }
303}