chakokuのブログ(rev4)

テック・コミック・DTM・・・ごくまれにチャリ

QMP6988用ドライバ、Soft I2CドライバをRustで作成する

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