diff --git a/c/common/i2cdriver.c b/c/common/i2cdriver.c index 318cc99..a3a5ac2 100644 --- a/c/common/i2cdriver.c +++ b/c/common/i2cdriver.c @@ -18,7 +18,9 @@ #if defined(WIN32) // { +#ifndef NOMINMAX #define NOMINMAX +#endif #include void ErrorExit(const char *func_name) @@ -323,7 +325,7 @@ uint8_t i2c_reset(I2CDriver *sd) int i2c_start(I2CDriver *sd, uint8_t dev, uint8_t op) { - uint8_t start[2] = {'s', (dev << 1) | op}; + uint8_t start[2] = {'s', (uint8_t)((dev << 1) | op)}; writeToSerialPort(sd->port, start, sizeof(start)); return i2c_ack(sd); } @@ -367,6 +369,56 @@ void i2c_monitor(I2CDriver *sd, int enable) charCommand(sd, enable ? 'm' : '@'); } +void i2c_capture(I2CDriver *sd) +{ + printf("Capture started\n"); + charCommand(sd, 'c'); + uint8_t bytes[1]; + + int starting = 0; + int nbits = 0, bits = 0; + while (1) { + int i; + readFromSerialPort(sd->port, bytes, 1); + for (i = 0; i < 2; i++) { + int symbol = (i == 0) ? (bytes[0] >> 4) : (bytes[0] & 0xf); + switch (symbol) { + case 0: + break; + case 1: + starting = 1; + break; + case 2: + printf("STOP\n"); + starting = 1; + break; + case 8: + case 9: + case 10: + case 11: + case 12: + case 13: + case 14: + case 15: + bits = (bits << 3) | (symbol & 7); + nbits += 3; + if (nbits == 9) { + int b8 = (bits >> 1), ack = !(bits & 1); + if (starting) { + starting = 0; + printf("START %02x %s", b8 >> 1, (b8 & 1) ? "READ" : "WRITE"); + } else { + printf("BYTE %02x", b8); + } + printf(" %s\n", ack ? "ACK" : "NAK"); + nbits = 0; + bits = 0; + } + } + } + } +} + int i2c_commands(I2CDriver *sd, int argc, char *argv[]) { int i; @@ -479,6 +531,12 @@ int i2c_commands(I2CDriver *sd, int argc, char *argv[]) } break; + case 'c': + { + i2c_capture(sd); + } + break; + default: badcommand: fprintf(stderr, "Bad command '%s'\n", token); @@ -492,6 +550,7 @@ int i2c_commands(I2CDriver *sd, int argc, char *argv[]) fprintf(stderr, " p send a STOP\n"); fprintf(stderr, " r dev N read N bytes from I2C device dev, then STOP\n"); fprintf(stderr, " m enter I2C bus monitor mode\n"); + fprintf(stderr, " c enter I2C bus capture mode\n"); fprintf(stderr, "\n"); return 1; diff --git a/c/common/i2cdriver.h b/c/common/i2cdriver.h index 79f8675..aa14652 100644 --- a/c/common/i2cdriver.h +++ b/c/common/i2cdriver.h @@ -36,6 +36,7 @@ int i2c_start(I2CDriver *sd, uint8_t dev, uint8_t op); void i2c_stop(I2CDriver *sd); void i2c_monitor(I2CDriver *sd, int enable); +void i2c_capture(I2CDriver *sd); int i2c_commands(I2CDriver *sd, int argc, char *argv[]); diff --git a/c/linux/Makefile.clang b/c/linux/Makefile.clang new file mode 100644 index 0000000..18cceec --- /dev/null +++ b/c/linux/Makefile.clang @@ -0,0 +1,11 @@ +CC=clang +CFLAGS += -I common -Wall -Wpointer-sign -xc++ -std=c++17 # -Werror + +all: build/i2ccl + +install: all + cp build/i2ccl /usr/local/bin + +build/i2ccl: linux/i2c.c common/i2cdriver.c + mkdir -p build/ + $(CC) -o $@ $(CPPFLAGS) $(CFLAGS) $^ diff --git a/python/i2cdriver.py b/python/i2cdriver.py index 8bb3d06..463c218 100644 --- a/python/i2cdriver.py +++ b/python/i2cdriver.py @@ -68,7 +68,15 @@ class I2CDriver: """ A connected I2CDriver. - :ivar product: product code e.g. 'i2cdriver1' + :param port: The USB port to connect to + :type port: str + :param reset: Issue an I2C bus reset on connection + :type reset: bool + + After connection, the following object variables reflect the current values of the I2CDriver. + They are updated by calling :py:meth:`getstatus`. + + :ivar product: product code e.g. 'i2cdriver1' or 'i2cdriverm' :ivar serial: serial string of I2CDriver :ivar uptime: time since I2CDriver boot, in seconds :ivar voltage: USB voltage, in V @@ -269,7 +277,7 @@ class I2CDriver: :param reg: register address 0-255 :param fmt: :py:func:`struct.unpack` format string for the register contents, or an integer byte count - If device 0x75 has a 16-bit register 102, it can be read with: + If device 0x75 has a 16-bit unsigned big-endian register 102, it can be read with: >>> i2c.regrd(0x75, 102, ">H") 4999 @@ -335,7 +343,6 @@ class I2CDriver: self.__echo(0x40) def introspect(self): - """ Update all status variables """ self.ser.write(b'J') r = self.ser.read(80) assert len(r) == 80, r @@ -360,6 +367,11 @@ class I2CDriver: self.sda) def capture_start(self, idle=False, start = START, abyte = BYTE, stop = STOP): + """Enter I2C capture mode, capturing I2C transactions. + :param idle: If ``True`` the generator returns ``None`` when the bus is idle. If ``False`` the generator does nothing during bus idle. + + :return: a generator which returns an object for each I2C primitive captured. + """ self.__ser_w([ord('c')]) def nstream(): while True: diff --git a/python/samples/mux.py b/python/samples/mux.py new file mode 100644 index 0000000..a240b89 --- /dev/null +++ b/python/samples/mux.py @@ -0,0 +1,40 @@ +import sys +from i2cdriver import I2CDriver, EDS + +# Using a TCA9548A Low-Voltage 8-Channel I2C Switch +# Three LM75B temperature sensors are connected to +# channels 0,1 and 2. All are at address 0x48. + +class Mux: + def __init__(self, i2, a = 0x70): + self.i2 = i2 + self.a = a + + def select(self, n): + assert n in range(8) + self.i2.start(self.a, 0) + self.i2.write([1 << n]) + self.i2.stop() + +if __name__ == '__main__': + i2 = I2CDriver(sys.argv[1]) + + mux = Mux(i2) + sensors = [ + (0, EDS.Temp(i2)), + (1, EDS.Temp(i2)), + (2, EDS.Temp(i2)) + ] + + # Reset all 8 channels + for chan in range(8): + mux.select(chan) + i2.reset() + + def read(chan, dev): + mux.select(chan) + celsius = dev.read() + return celsius + + while 1: + print(" ".join(["%.1f" % read(chan, dev) for chan,dev in sensors])) diff --git a/testall b/testall index 43cd463..ce9b195 100755 --- a/testall +++ b/testall @@ -14,3 +14,4 @@ python -c 'from i2cdriver import I2CDriver' cd c make -f linux/Makefile +make -f linux/Makefile.clang