背景:ESP32-C3で室内の温湿度、CO2、気圧を測りたい。だが周辺I/OのI2Cバスは1系統しかない(デイジー・チェーン接続してもいいけど、I2C HUBが必要になる。ちょっと大げさ)
課題:ソフトウエアによりGPIOポートを制御してI2Cバスを実現する(Soft I2Cドライバ)。併せてQMP6988用ドライバも作成する
結論:なんとか動くバージョンができた(RustのLifetime等はたまたまうまく行っているだけ、正確には理解できておらずエラーメッセージのヒントをそのまま使っている)
詳細:
QMP6988ドライバ
use crate::SoftI2C; pub struct QMP6988<'a,> { pub i2c: SoftI2C::I2CBus<'a,>, } impl QMP6988<'_>{ pub const QMP6988_I2C_ADDRESS: u8 = 0x70; pub const REG_PRESS_TXD2: u8 = 0xF7; pub const REG_IO_SETUP: u8 = 0xF5; pub const REG_CTRL_MEAS: u8 = 0xF4; pub const REG_DEVICE_STAT: u8 = 0xF3; pub const REG_CHIP_ID: u8 = 0xD1; const a0 :f32 = -2105.9375; const a1 :f32 = -0.006391493270668661; const a2 :f32 = -3.732941679128391e-11; const b00:f32 = 23489.5; const bt1:f32 = 0.11356932279427473; const bt2:f32 = 6.638398388622699e-08; const bp1:f32 = 0.03578676717429121; const b11:f32 = 2.248985259559923e-07; const bp2:f32 = -6.172249519333475e-10; const b12:f32 = 4.795419171727653e-13; const b21:f32 = 4.15744499038667e-16; const bp3:f32 = 1.2433906064027832e-16; pub fn start(mut self)-> Self { self.i2c = self.i2c.write_register(Self::QMP6988_I2C_ADDRESS, Self::REG_CTRL_MEAS, 0x93); // TEMP_AVE:8, PRESS_AVE:8, POWER:3 self } pub fn read_register(mut self, addr:u8) -> (Self, u8){ let mut values = Vec::<u8>::new(); (self.i2c, values) = self.i2c.read_registers(Self::QMP6988_I2C_ADDRESS, addr, 1); (self, values[0]) } pub fn get_temp_press(mut self)->(Self, f32, f32){ // <'static> let mut values = Vec::<u8>::new(); let addr = Self::REG_PRESS_TXD2; (self.i2c, values) = self.i2c.read_registers(Self::QMP6988_I2C_ADDRESS, addr, 6); for i in 0..values.len(){ print!("{:x}: {:x}\n", addr + i as u8, values[i]); } let PR_TXD2 = values[0] as u8; let PR_TXD1 = values[1] as u8; let PR_TXD0 = values[2] as u8; let TP_TXD2 = values[3] as u8; let TP_TXD1 = values[4] as u8; let TP_TXD0 = values[5] as u8; let (temp, press) = self.calc_temp_press(PR_TXD2, PR_TXD1, PR_TXD0, TP_TXD2, TP_TXD1, TP_TXD0); (self, temp, press) } fn calc_temp_press(&self, PRESS_TXD2:u8, PRESS_TXD1:u8, PRESS_TXD0:u8, TEMP_TXD2:u8, TEMP_TXD1:u8, TEMP_TXD0:u8) -> (f32, f32){ let Dt = (((TEMP_TXD2 as u32) << 16) + ((TEMP_TXD1 as u32) << 8) + TEMP_TXD0 as u32) as f32 - 2_u64.pow(23) as f32; let Dp = (((PRESS_TXD2 as u32) << 16) + (( PRESS_TXD1 as u32) << 8) + PRESS_TXD0 as u32) as f32 - 2_u64.pow(23) as f32; let Tr = Self::a0 + Self::a1 * Dt + Self::a2 * Dt * Dt; let Pr = Self::b00 + Self::bt1 * Tr + Self::bp1 * Dp + Self::b11 * Tr * Dp + Self::bt2 * Tr * Tr + Self::bp2 * Dp * Dp + Self::b12 * Dp * Tr * Tr + Self::b21 * Dp * Dp * Tr + Self::bp3 * Dp * Dp * Dp; (Tr/256.0, Pr/100.0) } }
SoftI2Cドライバ
use esp_idf_svc::hal::gpio::{PinDriver, Pin, Output, Input, Level, Gpio2, Gpio3}; use esp_idf_svc::hal::gpio::Level::{Low, High}; use std::{thread::sleep, time::Duration}; pub struct I2CBus<'a, >{ pub scl: PinDriver<'a, Gpio2, Output> , pub sda: PinDriver<'a, Gpio3, Output> , } impl I2CBus<'_> { pub fn send_start_cond(mut self)->Self{ self.sda.set_high().unwrap(); sleep(Duration::from_millis(10)); self.scl.set_high().unwrap(); sleep(Duration::from_millis(10)); self.sda.set_low().unwrap(); sleep(Duration::from_millis(10)); self.scl.set_low().unwrap(); sleep(Duration::from_millis(10)); self } pub fn send_stop_cond(mut self)->Self{ self.scl.set_low().unwrap(); sleep(Duration::from_millis(10)); self.sda.set_low().unwrap(); sleep(Duration::from_millis(10)); self.scl.set_high().unwrap(); sleep(Duration::from_millis(10)); self.sda.set_high().unwrap(); sleep(Duration::from_millis(10)); self } pub fn send_ack(mut self, data:u8)->Self{ self = self.send_bit(data); self } pub fn send_bit(mut self, data:u8)->Self{ self.sda.set_low().unwrap(); sleep(Duration::from_millis(1)); self.scl.set_low().unwrap(); sleep(Duration::from_millis(1)); if data == 1 { self.sda.set_high().unwrap(); }else{ self.sda.set_low().unwrap(); } sleep(Duration::from_millis(1)); self.scl.set_high().unwrap(); sleep(Duration::from_millis(1)); self.scl.set_low().unwrap(); sleep(Duration::from_millis(1)); self.sda.set_low().unwrap(); sleep(Duration::from_millis(1)); self } pub fn send_byte(mut self, data:u8) -> Self { let mut mask = 0x80; //print!("send_byte()\n"); //print!("{:x}\n",data); for _ in 0..8 { if data & mask != 0{ self = self.send_bit(1); }else{ self = self.send_bit(0); } mask >>= 1; } self } pub fn send_device_address_rw(mut self, device_address:u8, rw:u8)->Self{ let data = (device_address << 1) | rw; self = self.send_byte(data); self } pub fn recv_ack(mut self)->(Self,Level){ self.scl.set_low().unwrap(); let sda_in = self.sda.into_input().unwrap(); sleep(Duration::from_millis(1)); self.scl.set_high().unwrap(); sleep(Duration::from_millis(1)); let level = sda_in.get_level(); sleep(Duration::from_millis(1)); self.scl.set_low().unwrap(); sleep(Duration::from_millis(1)); self.sda = sda_in.into_output().unwrap(); self.sda.set_low().unwrap(); if level == High { print!("NACK<<<<<<<<<<<<<\n"); } (self, level) } pub fn recv_byte(mut self)->(Self,u8){ let mut data:u8 = 0; let sda_in = self.sda.into_input().unwrap(); for _ in 0..8 { self.scl.set_low().unwrap(); sleep(Duration::from_millis(1)); self.scl.set_high().unwrap(); sleep(Duration::from_millis(1)); data <<= 1; if sda_in.get_level() == High { data += 1; } sleep(Duration::from_millis(1)); self.scl.set_low().unwrap(); sleep(Duration::from_millis(1)); } self.sda = sda_in.into_output().unwrap(); self.sda.set_low().unwrap(); (self, data) } pub fn read_registers(mut self, device_address:u8, mut register_address:u8, size:u8) -> (Self,Vec<u8>){ let mut values = Vec::<u8>::new(); let mut data:u8; for _ in 0..size{ (self, data) = self.read_register(device_address, register_address); values.push(data); register_address += 1; } (self, values) } pub fn read_register(mut self, device_address:u8, register_address:u8)->(Self,u8){ let mut level:Level = Low; let mut data: u8 = 0; self = self.send_start_cond(); self = self.send_device_address_rw(device_address, 0); // 0: write (self, level) = self.recv_ack(); //(1/4) self = self.send_byte(register_address); (self, level) = self.recv_ack(); //(2/4) self = self.send_start_cond(); // start cond self = self.send_device_address_rw(device_address, 1); // 1: read (self, level) = self.recv_ack(); //(3/4) (self, data) = self.recv_byte(); self = self.send_ack(1); //(4/4) // 1: nack self = self.send_stop_cond(); (self, data) } pub fn write_register(mut self, device_address:u8, register_address:u8, data:u8) -> Self{ let mut level:Level = Low; self = self.send_start_cond(); self = self.send_device_address_rw(device_address, 0); // 0: write (self, level) = self.recv_ack(); //(1/4) if level == High { print!("NACK<<<<<<<<<<<<<\n"); } self = self.send_byte(register_address); (self, level) = self.recv_ack(); //(2/4) if level == High { print!("NACK<<<<<<<<<<<<<\n"); } //print!("send:{:x}\n",data); self = self.send_byte(data); (self, level) = self.recv_ack(); //(3/4) if level == High { print!("NACK<<<<<<<<<<<<<\n"); } self = self.send_stop_cond(); self } }