Skip to content

i2c write register issue #72

@f3dora19

Description

@f3dora19

Hi,

I'm mainly experienced in C development and I saw in Rust a massive game changer in memory safety & some other things like concurrency, so I started to move to Rust for my developments.
My first program was to develop a device driver using I2C bus, I'm on RPI4, but I'm facing one massive issue, I am not able to write register on the device, but I don't know why.
There is program samples & strace logs:

From my working C program:

#include <stdio.h>
#include <stdint.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <errno.h>
#include <linux/i2c.h>
#include <linux/i2c-dev.h>

int read_status(int fd) {
	int rc = 0;
	uint8_t buf[3] = {0x6a, 0x0, 0x0};
    	struct i2c_msg messages[] = {
		{.addr = 0x4b, .flags = 0, .buf = buf, .len = 1},
		{.addr = 0x4b, .flags = I2C_M_RD, .buf = buf, .len = 3},
	};

	struct i2c_rdwr_ioctl_data ioctl_data = {
		.msgs = messages,
		.nmsgs = 2,
	};

	rc = ioctl(fd, I2C_RDWR, &ioctl_data);
	if(rc != 2) {
		perror("Unable to read\n");
		return -1;
	}

	printf("Status: [%x, %x, %x]\n", buf[0], buf[1], buf[2]);
	return 0;
}

int start_fan(int fd, uint8_t start_stop) {
    uint8_t buf[2] = {0x2b, start_stop};
    int ret = 0;

    ret = i2c_smbus_write_byte_data(fd, buf[0], buf[1]);
    if(ret < 0) {
	perror("Failed to start fan");
        return -1;
    }

    return 0;
}

int main() {
    int ret = 0;

    int fd = open("/dev/i2c-1", O_RDWR);

    if (fd < 0) {
        printf("Failed to open i2c-1\n");
        return 1;
    }

    uint8_t addr = 0x4b;

    if (ioctl(fd, I2C_SLAVE, addr) < 0) {
        printf("Failed to acquire bus access/talk to slave\n");
        return 1;
    }

    ret = read_status(fd);
    if(ret < 0) {
        printf("Failed to verify status\n");
        return 1;
    }

    ret = start_fan(fd, 1);
    if(ret < 0) {
        printf("Failed to start fan\n");
        return 1;
    }

    printf("Fan started\n");

    ret = read_status(fd);
    if(ret < 0) {
        printf("Failed to verify status\n");
        return 1;
    }
    
    close(fd);

    return 0;
}
Status: [0, 78, f0]
Fan started
Status: [1, f1, e1]
openat(AT_FDCWD, "/dev/i2c-1", O_RDWR)  = 3
ioctl(3, _IOC(_IOC_NONE, 0x7, 0x3, 0), 0x4b) = 0
ioctl(3, _IOC(_IOC_NONE, 0x7, 0x20, 0), 0xbee68538) = 0
ioctl(3, _IOC(_IOC_NONE, 0x7, 0x7, 0), 0xbee68570) = 2
write(1, "Status: [0, 78, f0]\n", 20Status: [0, 78, f0]
)   = 20
ioctl(3, _IOC(_IOC_NONE, 0x7, 0x20, 0), 0xbee68538) = 0
write(1, "Fan started\n", 12Fan started
)           = 12
ioctl(3, _IOC(_IOC_NONE, 0x7, 0x7, 0), 0xbee68570) = 2
write(1, "Status: [1, f1, e1]\n", 20Status: [1, f1, e1]
)   = 20
close(3)                                = 0

From my non-working rust program :

device.rs

use embedded_hal::i2c::blocking::{I2c, Operation};
#[cfg(feature = "serde")]
use serde::Serialize;

pub const Devcie_I2C_ADDR: u8 = 0x4b;

#[derive(Debug)]
pub enum Error<E> {
    /// I²C bus error
    I2c(E),
    /// Failed to parse sensor data
    InvalidData,
    /// No calibration data is available (probably forgot to call or check BME280::init for failure)
    NoCalibrationData,
    /// Chip ID doesn't match expected value
    UnsupportedChip,
}

#[derive(Debug, Default)]
pub struct Device<I2C> {
    /// concrete I²C device implementation
    i2c: I2C,
    /// I²C device address
    address: u8,
}

impl<I2C> Device<I2C>
where
    I2C: I2c,
{
    /// Create a new BME280 struct using a custom I²C address
    pub fn new(i2c: I2C, address: u8) -> Self {
        Device { i2c, address }
    }

    pub fn verify_status(&mut self) -> Result<u8, I2C::Error> {
        let status = self.read_register(0x64, 3)?;
        let crc = crc16(&status[..2]);
        Ok(status[0])
    }

    pub fn start_mode(&mut self) -> Result<(), I2C::Error> {
        self.write_register(0x10, 0x2)
    }

    pub fn start_fan(&mut self) -> Result<(), I2C::Error> {
        self.write_register(0x2b, 1)
    }

    fn read_register(&mut self, register: u8, length: u8) -> Result<Vec<u8>, I2C::Error> {
        let mut data: Vec<u8> = vec![0; length as usize];
        let buf: Vec<u8> = vec![register];
        let mut ops = [Operation::Write(&buf), Operation::Read(&mut data)];
        self.i2c.transaction(self.address, &mut ops)?;
        println!("{:x?}", data);

        Ok(data)
    }

    fn write_register(&mut self, register: u8, payload: u8) -> Result<(), I2C::Error> {
        println!("Writing to register 0x{:02x}: 0x{:02x}", register, payload);
        let data: Vec<u8> = vec![register, payload];
        let mut ops = [Operation::Write(&data)];
        self.i2c.transaction(self.address, &mut ops)
    }
}

fn crc16(data: &[u8]) -> u16 {
    let mut _data: u16 = 0;
    let mut crc = 0xffff_u16;

    for i in 0..data.len() {
        _data = 0xff as u16 & data[i] as u16;
        for _ in 0..8 {
            if (crc & 0x0001_u16) ^ (_data & 0x0001_u16) != 0 {
                crc = (crc >> 1) ^ 0x8408_u16;
            } else {
                crc >>= 1;
            }
            _data >>= 1;
        }
    }
    crc = !crc;
    _data = crc;
    crc = (crc << 8) | (_data >> 8 & 0xff);
    crc
}

#[cfg(test)]
#[test]
pub fn test_crc_16() {
    // Test simple command
    let data = [0_u8, 0x78_u8, 0xf0_u8];
    let crc = crc16(&data[..data.len() - 2]);
    assert_eq!(crc, 0x78f0_u16);

    let data = [0x0_u8, 0x9_u8, 0x3a_u8, 0x80_u8, 0x1a_u8, 0xaf_u8];
    let interval = u32::from_be_bytes([data[0], data[1], data[2], data[3]]);
    assert_eq!(interval, 604800_u32);
    let crc = crc16(&data[..data.len() - 2]);
    assert_eq!(crc, 0x1aaf_u16);

    let data = [
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xca, 6,
    ];
    let crc = crc16(&data[..data.len() - 2]);
    println!("{:x}", crc);
    assert_eq!(crc, 0xca06_u16);

    let data = [
        0, 0x3, 0x90, 0x4c, 0, 1, 0xf5, 0x7a, 0, 0, 0x6e, 0xb4, 0, 0, 5, 0xd5, 0, 0, 1, 0xf, 0, 0,
        0, 0, 0, 0, 0, 0, 0x11, 0xe1,
    ];
    let crc = crc16(&data[..data.len() - 2]);
    assert_eq!(crc, 0x11e1_u16);

    let data = [
        0, 2, 0x63, 0x8d, 0, 1, 0x43, 0x1d, 0, 0, 0x42, 0x29, 0, 0, 1, 0xf7, 0, 0, 0, 0x15, 0, 0,
        0, 0, 0, 0, 0, 0, 0xb4, 0x19,
    ];

    let crc = crc16(&data[..data.len() - 2]);
    assert_eq!(crc, 0xb419_u16);
}

main.rs

use linux_embedded_hal::I2cdev;

mod device;

fn main() {
    let i2c_bus = I2cdev::new("/dev/i2c-1").unwrap();
    let mut ipsx100: ips7100_v2::Device<I2cdev> =
        ips7100_v2::Device::new(i2c_bus, ips7100_v2::Devcie_I2C_ADDR);

    match ipsx100.verify_status() {
        Ok(status) => println!("Device status verified: {}", status),
        Err(e) => println!("Error: {:?}", e),
    }

    match ipsx100.start_fan() {
        Ok(_) => println!("Fan started"),
        Err(e) => println!("Error: {:?}", e),
    }

    match ipsx100.verify_status() {
        Ok(status) => println!("Device status verified: {:x}", status),
        Err(e) => println!("Error: {:?}", e),
    }
}
[0, 78, f0]
Device status verified: 0
Writing to register 0x2b: 0x01
Fan started
[0, 78, f0]
Device status verified: 0
openat(AT_FDCWD, "/dev/i2c-1", O_RDWR|O_LARGEFILE|O_CLOEXEC) = 4
ioctl(4, _IOC(_IOC_NONE, 0x7, 0x3, 0), 0x4b) = 0
ioctl(4, _IOC(_IOC_NONE, 0x7, 0x8, 0), 0) = 0
ioctl(4, _IOC(_IOC_NONE, 0x7, 0x7, 0), 0xbebc1388) = 2
write(1, "[0, 78, f0]\n", 12[0, 78, f0]
)           = 12
ioctl(4, _IOC(_IOC_NONE, 0x7, 0x7, 0), 0xbea0e3a0) = 1
write(1, "Fan started\n", 12Fan started
)           = 12
ioctl(4, _IOC(_IOC_NONE, 0x7, 0x7, 0), 0xbebc1388) = 2
write(1, "[0, 78, f0]\n", 12[0, 78, f0]
)           = 12
close(4)                                = 0

What I'm doing wrong ?
Can someone help me ?

Thanks in advance, and sorry for this long post.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions