From 289d2350c69470babe657a1d68bc3b46441e43ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20Stefa=C5=84ski?= Date: Tue, 16 Jun 2026 02:46:15 +0200 Subject: [PATCH] abcd --- src/main.rs | 141 ++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 125 insertions(+), 16 deletions(-) diff --git a/src/main.rs b/src/main.rs index 6214812..421f7b0 100644 --- a/src/main.rs +++ b/src/main.rs @@ -3,7 +3,8 @@ use defmt::*; use embassy_executor::Spawner; -use embassy_stm32::{Config, bind_interrupts, dma::InterruptHandler, i2c::{self, ErrorInterruptHandler, EventInterruptHandler, I2c}, peripherals::{DMA1_CH1, DMA1_CH2, I2C2}}; +use embassy_stm32::mode::Async; +use embassy_stm32::{Config, bind_interrupts, dma::InterruptHandler, i2c::{self, ErrorInterruptHandler, EventInterruptHandler, I2c, Master}, peripherals::{DMA1_CH1, DMA1_CH2, I2C2}}; use embassy_time::Timer; use defmt_rtt as _; @@ -15,7 +16,49 @@ bind_interrupts!(struct Irqs { I2C2_3 => EventInterruptHandler, ErrorInterruptHandler; }); -const ADDR: u8 = 0x1A; +const SENSOR: u8 = 0x1A; +const EEPROM: u8 = 0x1B; + +type Bus = I2c<'static, Async, Master>; + +/// Read a 16-bit word from the sensor EEPROM (read-only sequence, never writes). +async fn eeprom_word(i2c: &mut Bus, addr: u16) -> u16 { + i2c.write(EEPROM, &[0x09, (addr >> 8) as u8, addr as u8]).await.unwrap(); // SET_ADDRESS + i2c.write(EEPROM, &[0x06]).await.unwrap(); // NORMAL_READ + let mut b = [0u8; 2]; + i2c.write_read(EEPROM, &[0x0B], &mut b).await.unwrap(); // GET_DATA + i2c.write(EEPROM, &[0x01]).await.unwrap(); // ACTIVE + (b[1] as u16) << 8 | b[0] as u16 +} + +/// Reconstruct a little-endian float stored across two EEPROM words. +async fn eeprom_f32(i2c: &mut Bus, lo_addr: u16, hi_addr: u16) -> f32 { + let lo = eeprom_word(i2c, lo_addr).await as u32; + let hi = eeprom_word(i2c, hi_addr).await as u32; + f32::from_bits(hi << 16 | lo) +} + +/// Run one conversion with the given configuration byte and read both halves. +async fn read_block(i2c: &mut Bus, config: u8) -> ([u8; 130], [u8; 130]) { + i2c.write(SENSOR, &[0x01, config]).await.unwrap(); + let mut status = [0u8; 1]; + loop { + i2c.write_read(SENSOR, &[0x02], &mut status).await.unwrap(); + if status[0] & 0x01 != 0 { + break; + } + } + let mut top = [0u8; 130]; + let mut bottom = [0u8; 130]; + i2c.write_read(SENSOR, &[0x0A], &mut top).await.unwrap(); + i2c.write_read(SENSOR, &[0x0B], &mut bottom).await.unwrap(); + (top, bottom) +} + +/// Pixel `j` (0..63) of a half-block buffer: word0 is PTAT/VDD, pixels start at byte 2. +fn px(buf: &[u8; 130], j: usize) -> u16 { + (buf[2 * j + 2] as u16) << 8 | buf[2 * j + 3] as u16 +} #[embassy_executor::main] async fn main(_spawner: Spawner) { @@ -26,22 +69,88 @@ async fn main(_spawner: Spawner) { i2c_cfg.scl_pullup = false; let mut i2c = I2c::new(p.I2C2, p.PA7, p.PA6, p.DMA1_CH1, p.DMA1_CH2, Irqs, i2c_cfg); - i2c.write(ADDR, &[0x01, 0x01]).await.unwrap(); - Timer::after_millis(30).await; + // --- read calibration data from EEPROM (read-only) --- + let mbit = eeprom_word(&mut i2c, 0x001A).await as u8; + let bias = eeprom_word(&mut i2c, 0x001B).await as u8; + let clk = eeprom_word(&mut i2c, 0x001C).await as u8; + let bpa = eeprom_word(&mut i2c, 0x001D).await as u8; + let pu = eeprom_word(&mut i2c, 0x001E).await as u8; + let gradscale = eeprom_word(&mut i2c, 0x0008).await as u8; + let ptatgr = eeprom_f32(&mut i2c, 0x0034, 0x0035).await; + let ptatoff = eeprom_f32(&mut i2c, 0x0036, 0x0037).await; - // Start a PTAT measurement of block 0 (1<<3 | 1). - i2c.write(ADDR, &[0x01, 0x09]).await.unwrap(); - - let mut status = [0u8; 1]; - while { - i2c.write_read(ADDR, &[0x02], &mut status).await.unwrap(); - status[0] & 0x01 == 0 - } { - Timer::after_millis(250).await; + // ThGrad and ThOffset (16x16 signed), bottom half is stored row-reversed. + let mut thgrad = [[0i16; 16]; 16]; + let mut thoffset = [[0i16; 16]; 16]; + for (base, dst) in [(0x0100u16, &mut thgrad), (0x0200u16, &mut thoffset)] { + for m in 0..8 { + for n in 0..16 { + dst[m][n] = eeprom_word(&mut i2c, base + (n + m * 16) as u16).await as i16; + } + } + for k in 0..8 { + for n in 0..16 { + dst[15 - k][n] = eeprom_word(&mut i2c, base + 128 + (n + k * 16) as u16).await as i16; + } + } } - let mut data = [0u8; 258]; - i2c.write_read(ADDR, &[0x0A], &mut data).await.unwrap(); + // --- wake up sensor and load calibration into trim registers --- + i2c.write(SENSOR, &[0x01, 0x01]).await.unwrap(); + Timer::after_millis(30).await; + for (reg, val) in [(0x03, mbit), (0x04, bias), (0x05, bias), (0x06, clk), (0x07, bpa), (0x08, bpa), (0x09, pu)] { + i2c.write(SENSOR, &[reg, val]).await.unwrap(); + Timer::after_millis(5).await; + } - info!("Measurement: {:?}", data); + // --- read both pixel blocks (each carries a PTAT value + its pixels) --- + let (top0, bot0) = read_block(&mut i2c, 0x09).await; // block 0, PTAT + let (top1, bot1) = read_block(&mut i2c, 0x19).await; // block 1, PTAT + // --- electrical offset (blind measurement) --- + let (eo_top, eo_bot) = read_block(&mut i2c, 0x0B).await; + + // ambient temperature from averaged PTAT (datasheet 11.1) + let ptat = |b: &[u8; 130]| (b[0] as u32) << 8 | b[1] as u32; + let ptat_av = ((ptat(&top0) + ptat(&top1) + ptat(&bot0) + ptat(&bot1)) / 4) as f32; + let ambient_dk = ptat_av * ptatgr + ptatoff; + let ambient_c = ambient_dk / 10.0 - 273.15; + + // sort raw pixels into a 16x16 image (datasheet ordering) + let mut image = [[0i32; 16]; 16]; + let gradscale_div = 1i32 << gradscale; + for n in 0..16 { + let raw = [ + px(&top0, n), px(&top0, n + 16), px(&top0, n + 32), px(&top0, n + 48), + px(&top1, n), px(&top1, n + 16), px(&top1, n + 32), px(&top1, n + 48), + px(&bot1, n + 48), px(&bot1, n + 32), px(&bot1, n + 16), px(&bot1, n), + px(&bot0, n + 48), px(&bot0, n + 32), px(&bot0, n + 16), px(&bot0, n), + ]; + let eo = [ + px(&eo_top, n), px(&eo_top, n + 16), px(&eo_top, n + 32), px(&eo_top, n + 48), + px(&eo_bot, n + 48), px(&eo_bot, n + 32), px(&eo_bot, n + 16), px(&eo_bot, n), + ]; + for m in 0..16 { + // thermal-offset compensation (11.2) + electrical-offset compensation (11.3) + let comp = raw[m] as i32 + - (thgrad[m][n] as i32 * ptat_av as i32) / gradscale_div + - thoffset[m][n] as i32; + let eo_row = if m < 8 { m % 4 } else { m % 4 + 4 }; + image[m][n] = comp - eo[eo_row] as i32; + } + } + + let mut min = i32::MAX; + let mut max = i32::MIN; + for row in &image { + for &v in row { + min = min.min(v); + max = max.max(v); + } + } + + info!("ambient temperature: {} C (PTAT_av={})", ambient_c, ptat_av); + info!("compensated image range: {}..{}", min, max); + for m in 0..16 { + info!("row {}: {}", m, image[m]); + } }