背景: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
}
}