// Package i2c provides low level control over the Linux i2c bus. // // Before usage you should load the i2c-dev kernel module // // sudo modprobe i2c-dev // // Each i2c bus can address 127 independent i2c devices, and most // Linux systems contain several buses. package i2c import ( "encoding/hex" "fmt" "os" "syscall" ) // I2C represents a connection to I2C-device. type I2C struct { addr uint8 bus int rc *os.File } // NewI2C opens a connection for I2C-device. // SMBus (System Management Bus) protocol over I2C // supported as well: you should preliminary specify // register address to read from, either write register // together with the data in case of write operations. func NewI2C(addr uint8, bus int) (*I2C, error) { f, err := os.OpenFile(fmt.Sprintf("/dev/i2c-%d", bus), os.O_RDWR, 0600) if err != nil { return nil, err } if err := ioctl(f.Fd(), I2C_SLAVE, uintptr(addr)); err != nil { return nil, err } v := &I2C{rc: f, bus: bus, addr: addr} return v, nil } // GetBus return bus line, where I2C-device is allocated. func (v *I2C) GetBus() int { return v.bus } // GetAddr return device occupied address in the bus. func (v *I2C) GetAddr() uint8 { return v.addr } func (v *I2C) write(buf []byte) (int, error) { return v.rc.Write(buf) } // WriteBytes send bytes to the remote I2C-device. The interpretation of // the message is implementation-dependent. func (v *I2C) WriteBytes(buf []byte) (int, error) { lg.Debugf("Write %d hex bytes: [%+v]", len(buf), hex.EncodeToString(buf)) return v.write(buf) } func (v *I2C) read(buf []byte) (int, error) { return v.rc.Read(buf) } // ReadBytes read bytes from I2C-device. // Number of bytes read correspond to buf parameter length. func (v *I2C) ReadBytes(buf []byte) (int, error) { n, err := v.read(buf) if err != nil { return n, err } lg.Debugf("Read %d hex bytes: [%+v]", len(buf), hex.EncodeToString(buf)) return n, nil } // Close I2C-connection. func (v *I2C) Close() error { return v.rc.Close() } // ReadRegBytes read count of n byte's sequence from I2C-device // starting from reg address. // SMBus (System Management Bus) protocol over I2C. func (v *I2C) ReadRegBytes(reg byte, n int) ([]byte, int, error) { lg.Debugf("Read %d bytes starting from reg 0x%0X...", n, reg) _, err := v.WriteBytes([]byte{reg}) if err != nil { return nil, 0, err } buf := make([]byte, n) c, err := v.ReadBytes(buf) if err != nil { return nil, 0, err } return buf, c, nil } // ReadRegU8 reads byte from I2C-device register specified in reg. // SMBus (System Management Bus) protocol over I2C. func (v *I2C) ReadRegU8(reg byte) (byte, error) { _, err := v.WriteBytes([]byte{reg}) if err != nil { return 0, err } buf := make([]byte, 1) _, err = v.ReadBytes(buf) if err != nil { return 0, err } lg.Debugf("Read U8 %d from reg 0x%0X", buf[0], reg) return buf[0], nil } // WriteRegU8 writes byte to I2C-device register specified in reg. // SMBus (System Management Bus) protocol over I2C. func (v *I2C) WriteRegU8(reg byte, value byte) error { buf := []byte{reg, value} _, err := v.WriteBytes(buf) if err != nil { return err } lg.Debugf("Write U8 %d to reg 0x%0X", value, reg) return nil } // ReadRegU16BE reads unsigned big endian word (16 bits) // from I2C-device starting from address specified in reg. // SMBus (System Management Bus) protocol over I2C. func (v *I2C) ReadRegU16BE(reg byte) (uint16, error) { _, err := v.WriteBytes([]byte{reg}) if err != nil { return 0, err } buf := make([]byte, 2) _, err = v.ReadBytes(buf) if err != nil { return 0, err } w := uint16(buf[0])<<8 + uint16(buf[1]) lg.Debugf("Read U16 %d from reg 0x%0X", w, reg) return w, nil } // ReadRegU16LE reads unsigned little endian word (16 bits) // from I2C-device starting from address specified in reg. // SMBus (System Management Bus) protocol over I2C. func (v *I2C) ReadRegU16LE(reg byte) (uint16, error) { w, err := v.ReadRegU16BE(reg) if err != nil { return 0, err } // exchange bytes w = (w&0xFF)<<8 + w>>8 return w, nil } // ReadRegS16BE reads signed big endian word (16 bits) // from I2C-device starting from address specified in reg. // SMBus (System Management Bus) protocol over I2C. func (v *I2C) ReadRegS16BE(reg byte) (int16, error) { _, err := v.WriteBytes([]byte{reg}) if err != nil { return 0, err } buf := make([]byte, 2) _, err = v.ReadBytes(buf) if err != nil { return 0, err } w := int16(buf[0])<<8 + int16(buf[1]) lg.Debugf("Read S16 %d from reg 0x%0X", w, reg) return w, nil } // ReadRegS16LE reads signed little endian word (16 bits) // from I2C-device starting from address specified in reg. // SMBus (System Management Bus) protocol over I2C. func (v *I2C) ReadRegS16LE(reg byte) (int16, error) { w, err := v.ReadRegS16BE(reg) if err != nil { return 0, err } // exchange bytes w = (w&0xFF)<<8 + w>>8 return w, nil } // WriteRegU16BE writes unsigned big endian word (16 bits) // value to I2C-device starting from address specified in reg. // SMBus (System Management Bus) protocol over I2C. func (v *I2C) WriteRegU16BE(reg byte, value uint16) error { buf := []byte{reg, byte((value & 0xFF00) >> 8), byte(value & 0xFF)} _, err := v.WriteBytes(buf) if err != nil { return err } lg.Debugf("Write U16 %d to reg 0x%0X", value, reg) return nil } // WriteRegU16LE writes unsigned little endian word (16 bits) // value to I2C-device starting from address specified in reg. // SMBus (System Management Bus) protocol over I2C. func (v *I2C) WriteRegU16LE(reg byte, value uint16) error { w := (value*0xFF00)>>8 + value<<8 return v.WriteRegU16BE(reg, w) } // WriteRegS16BE writes signed big endian word (16 bits) // value to I2C-device starting from address specified in reg. // SMBus (System Management Bus) protocol over I2C. func (v *I2C) WriteRegS16BE(reg byte, value int16) error { buf := []byte{reg, byte((uint16(value) & 0xFF00) >> 8), byte(value & 0xFF)} _, err := v.WriteBytes(buf) if err != nil { return err } lg.Debugf("Write S16 %d to reg 0x%0X", value, reg) return nil } // WriteRegS16LE writes signed little endian word (16 bits) // value to I2C-device starting from address specified in reg. // SMBus (System Management Bus) protocol over I2C. func (v *I2C) WriteRegS16LE(reg byte, value int16) error { w := int16((uint16(value)*0xFF00)>>8) + value<<8 return v.WriteRegS16BE(reg, w) } func ioctl(fd, cmd, arg uintptr) error { _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, fd, cmd, arg, 0, 0, 0) if err != 0 { return err } return nil }