def i2c_bitbang(self, scl = 1, sda = 1, oe = 1):
        self.regs.i2c_w.write((scl << 0) | (oe << 1) | (sda << 2))
    
    def i2c_start(self):
        self.i2c_bitbang(1, 1)
        self.i2c_bitbang(1, 0)
        self.i2c_bitbang(0, 0)

    def i2c_stop(self):
        self.i2c_bitbang(0, 0)
        self.i2c_bitbang(1, 0)
        self.i2c_bitbang(1, 1)
        self.i2c_bitbang(oe = 0)

    def i2c_transmit_bit(self, b):
        self.i2c_bitbang(0, b)
        self.i2c_bitbang(1, b)
        self.i2c_bitbang(0, b)
        self.i2c_bitbang(0, 0, 0)

    def i2c_receive_bit(self):
        self.i2c_bitbang(0, 0, 0)
        self.i2c_bitbang(1, 0, 0)
        b = self.regs.i2c_r.read() & 1
        self.i2c_bitbang(0, 0, 0)
        return b
    
    def i2c_transmit_byte(self, b):
        for i in range(7, -1, -1):
            self.i2c_transmit_bit(1 if b & (1 << i) else 0)

        return self.i2c_receive_bit() == 0
    
    def i2c_receive_byte(self, ack):
        b = 0
        for i in range(7, -1, -1):
            b |= 1 << i if self.i2c_receive_bit() else 0
        self.i2c_transmit_bit(0 if ack else 1)

        return b
    
    def i2c_transfer(self, addr, write = '', read = 0):
        rdata = []

        try:
            if write:
                self.i2c_start()
                if not self.i2c_transmit_byte((addr << 1) | 0):
                    raise Exception('Write address NAK')
                
                for b in write:
                    if not self.i2c_transmit_byte(b):
                        raise Exception('Write NAK')

            if read:
                self.i2c_start()
                if not self.i2c_transmit_byte((addr << 1) | 1):
                    raise Exception('Read address NAK')
                
                while read:
                    rdata.append(self.i2c_receive_byte(read > 1))
                    read -= 1
                
        finally:
            self.i2c_stop()

        return bytes(rdata)