This commit is contained in:
2026-06-16 02:46:15 +02:00
parent 1d7ef1acfb
commit 289d2350c6
+125 -16
View File
@@ -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<I2C2>, ErrorInterruptHandler<I2C2>;
});
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]);
}
}