diff --git a/.gitignore b/.gitignore index a7bccfd0e171fc3c29870381ffdf3bcc468a72bf..3db5e2b95b1280774cf7f2d4e7b9ac41247bd072 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ */avr/compiled */linux/compiled */matlab/*.autosave -*~ \ No newline at end of file +*~ +__pycache__ \ No newline at end of file diff --git a/Makefile b/Makefile index dcd58050f3ce9a322eeeb55aa1dbeec04d000936..fb29921c3d8ae3b4b3570f80c4b01dcb60666e5e 100644 --- a/Makefile +++ b/Makefile @@ -20,6 +20,9 @@ all: make -C $${d} $@ ; \ done +%.LINK: + make -C $$(./tools/find_avr_targets $*) $@ + %.LOAD: make -C $$(./tools/find_avr_targets $*) $@ diff --git a/china_io-2019/avr/Makefile b/china_io-2019/avr/Makefile index b658ee4533bfaf5f61963942e28951740aa1a367..2e6b2c84a3343360588bd7f6531e53583c49a6de 100644 --- a/china_io-2019/avr/Makefile +++ b/china_io-2019/avr/Makefile @@ -7,7 +7,7 @@ china-io.FUSE=--wr_fuse_l=0x1f --wr_fuse_h=0xd9 --wr_fuse_e=0xff china-io.FUSE_L=0x1f china-io.FUSE_H=0xd9 china-io.C=china-io -china-io.H=../lib/serialio i2c_master +china-io.H=../../lib/avr/serialio i2c_master china-io-test.ARCH=avr china-io-test.CHIP=atmega32 diff --git a/china_io-2019/avr/china-io.c b/china_io-2019/avr/china-io.c new file mode 100644 index 0000000000000000000000000000000000000000..be0854334f5a3a88901ff19f7413223a3b567f2a --- /dev/null +++ b/china_io-2019/avr/china-io.c @@ -0,0 +1,331 @@ +#include <avr/eeprom.h> +#include <avr/io.h> +#include <avr/interrupt.h> +#include <inttypes.h> +#include "i2c_master.h" +#include "serialio.h" + +/* + * Serial I/O assignments: + * + * AI 0 -- Analog In 0 + * AI 1 -- Analog In 1 + * AI 2 -- Analog In 2 + * AI 3 -- Analog In 3 + * + * AO 0 -- Analog Out 0 + * AO 1 -- Analog Out 1 + * + */ + +/* + * Used I/O pins + * + * PB4 SPI SS / ADS8694 SPI CS + * PB5 Programming MOSI / ADS8694 SPI SDI + * PB6 Programming MISO / ADS8694 SPI SDO + * PB7 Programming SCK / ADS8694 SPI SCLK + * + * PC0 I2C SCL + * PC1 I2C SDA + * + * PD0 Serial In + * PD1 Serial Out + * + */ + +#define DDR_SPI DDRB +#define DD_MISO PB6 +#define DD_MOSI PB5 +#define DD_SCK PB7 +#define DD_SS PB4 +#define PORT_SPI PORTB + +static volatile uint8_t serial_readchannels; +static volatile uint8_t serial_writechannels; +static volatile uint8_t serial_readconfig; + +static volatile struct dac { + uint16_t value[2]; +} dac; + +static volatile struct adc { + uint32_t value[4]; +} adc; + +static volatile struct calibration { + uint8_t not_valid; // Non-zero if eeprom not written + struct { + uint16_t offset; + int16_t min; + int16_t max; + } channel[2]; +} calibration; + +static uint8_t ADS8694_W( + uint8_t length, + const __flash uint8_t *data) +{ + DDR_SPI |= (1<<DD_MOSI) | (1<<DD_SCK) | (1<<DD_SS); + SPCR &= (1<<SPIE); + SPCR = (1<<SPE) | (1<<MSTR) | (1<<CPHA); + SPSR = (1<<SPI2X); + PORT_SPI |= (1<<DD_MISO); // Pull up + + PORT_SPI &= ~(1<<DD_SS); + for (uint8_t i = 0 ; i < length ; i++) { + SPDR = data[i]; + while ((SPSR & (1<<SPIF)) == 0) {} + } + uint8_t dummy = SPDR; // Clear SPIF flag + PORT_SPI |= (1<<DD_SS); + return dummy; +} + +/* + * DAC8574 4-channel D/A convert + */ + +#define DAC8574ADDR 0x4c + +static volatile struct dac8574 { + union dac8574_channel { + uint8_t bytes[3]; + struct { + uint8_t control; + uint8_t data[2]; + } reg; + } a, b, c, d; +} dac8574 = { + .a={ .reg={ .control=0x10 } }, + .b={ .reg={ .control=0x12 } }, + .c={ .reg={ .control=0x14 } }, + .d={ .reg={ .control=0x16 } }, +}; + +static void dac8574_set_chan( + volatile union dac8574_channel *channel, + int value) +{ + channel->reg.data[0] = (value & 0xff00) >> 8; + channel->reg.data[1] = (value & 0x00ff); +} + +static const __flash struct i2c_transcation write_all[] = { + I2C_WRITE_RAM(DAC8574ADDR, 3, dac8574.a.bytes), + I2C_WRITE_RAM(DAC8574ADDR, 3, dac8574.b.bytes), + I2C_WRITE_RAM(DAC8574ADDR, 3, dac8574.c.bytes), + I2C_WRITE_RAM(DAC8574ADDR, 3, dac8574.d.bytes), + I2C_END() +}; + +static void handle_calibration(uint32_t value) +{ + unsigned char pos = value & 0x7f; + unsigned char sign = (value >> 7) & 0x01; + int32_t v = value >> 8; + if (sign) { + v = -v; + } + switch (pos) { + case 0: calibration.channel[0].offset = v; break; + case 1: calibration.channel[1].offset = v; break; + case 2: calibration.channel[0].min = v; break; + case 3: calibration.channel[0].max = v; break; + case 4: calibration.channel[1].min = v; break; + case 5: calibration.channel[1].max = v; break; + case 6: eeprom_write_block((void*)&calibration, 0, sizeof(calibration)); break; + } +} + +SIGNAL(USART_RXC_vect) +{ + char ch = UDR; + + switch (serialio_RXC(ch)) { + case serialio_clearbit: { + // Ignored + } break; + case serialio_setbit: { + // Ignored + } break; + case serialio_pollbit: { + // Ignored + } break; + case serialio_pollchannel: { + if (serialio_channel < 4) { + serial_readchannels |= (1<<serialio_channel); + } else if (serialio_channel == 31) { + serial_readconfig = 1; + } + } break; + case serialio_setchannel: { + if (serialio_channel < 2) { + dac.value[serialio_channel] = serialio_value; + serial_writechannels |= (1<<serialio_channel); + } else if (serialio_channel == 31) { + handle_calibration(serialio_value); + } + } break; + case serialio_error: { + } break; + case serialio_more: { + } break; + } +} + +SIGNAL(TIMER1_COMPA_vect) +{ + unsigned char data[5]; + + SPCR = (1<<SPE) | (1<<MSTR) | (1<< CPHA); + PORT_SPI &= ~(1<<DD_SS); // Chip select + SPDR = 0x00; // NOP + for (int i = 0 ; i < 5 ; i++) { + while ((SPSR & 0x80) == 0) { } + data[i] = SPDR; + if (i < 4) { + // More to read + SPDR = 0; + } + } + PORT_SPI |= (1<<DD_SS); // Chip deselect + + union { + int32_t i; + unsigned char b[4]; + } v; + uint8_t chan; + v.i = 0; + v.b[2] = data[2]; + v.b[1] = data[3]; + v.b[0] = data[4]; + chan = (v.i>>2) & 0xf; + adc.value[chan] = (v.i>>6); +} + +static uint32_t conf_millivolt(uint32_t value) +{ + if (value < 0) { + return CONF_NEGATIVE_MILLIVOLT(value); + } else { + return CONF_POSITIVE_MILLIVOLT(value); + } +} + +int main() +{ + eeprom_read_block((void*)&calibration, 0, sizeof(calibration)); + if (calibration.not_valid) { + calibration.not_valid = 0; + calibration.channel[0].offset = 0x8000; + calibration.channel[0].min = -10000; + calibration.channel[0].max = 10000; + calibration.channel[1].offset = 0x8000; + calibration.channel[1].min = -10000; + calibration.channel[1].max = 10000; + } + + /* Setup serial port */ + UCSRA = 0x00; // USART: + UCSRB = 0x98; // USART: RxIntEnable|RxEnable|TxEnable + UCSRC = 0x86; // USART: 8bit, no parity + UBRRH = 0; // USART: 115200 @ 14.7456MHz + UBRRL = 7; // USART: 115200 @ 14.7456MHz + + /* Setup timer1 */ + TCCR1A = 0x00; // Normal port mode (no output) + TCCR1B = 0x09; // Clock / 1 + OCR1A = 0x400; // 14.7456MHz / 1024 = 14.4kHz + TIMSK = 0x10; // Enable Timer1 compare A interrupts + + /* Setup I2C (TWI), used by DAC8574 */ + TWBR = 0x0a; // Max allowed master speed + TWSR = 0x00; + + /* Setup DAC8574 (SPI setup is done in ADS8694_W) */ + static const __flash uint8_t RST[] = { 0x85, 0x00, 0x00 }; + static const __flash uint8_t SEQUENCE[] = { (0x01<<1)|0x01, 0x0f, 0x00 }; + static const __flash uint8_t FEATURES[] = { (0x03<<1)|0x01, 0x03, 0x00 }; + static const __flash uint8_t AUTO_RST[] = { 0xa0, 0x00, 0x00, 0x00, 0x00 }; + ADS8694_W(3, RST); + ADS8694_W(3, SEQUENCE); + ADS8694_W(3, FEATURES); + ADS8694_W(5, AUTO_RST); + + /* Setup serialio communication */ + serialio_init(); + serial_readchannels = 0; + serial_readconfig = 0; + + /* Setup DAC values */ + dac.value[0] = 0x8000; + dac.value[1] = 0x8000; + + sei(); // Global interrupt enable + + unsigned char readchannels = 0; + while (1) { + unsigned char config; + struct adc local_adc; + + cli(); // Global interrupt disable + if (readchannels == 0) { + readchannels = serial_readchannels; + serial_readchannels = 0; + } + config = serial_readconfig; + serial_readconfig = 0; + local_adc = adc; + sei(); // Global interrupt enable + if (readchannels & 0x01) { + serialio_putchannel(0, local_adc.value[0]); + readchannels &= ~0x01; + } else if (readchannels & 0x02) { + serialio_putchannel(1, local_adc.value[1]); + readchannels &= ~0x02; + } else if (readchannels & 0x04) { + serialio_putchannel(2, local_adc.value[2]); + readchannels &= ~0x04; + } else if (readchannels & 0x08) { + serialio_putchannel(3, local_adc.value[3]); + readchannels &= ~0x08; + } + if (i2c_idle()) { + cli(); + dac8574_set_chan(&dac8574.a, calibration.channel[0].offset); + dac8574_set_chan(&dac8574.b, calibration.channel[0].offset); + dac8574_set_chan(&dac8574.c, dac.value[0]); + dac8574_set_chan(&dac8574.d, dac.value[1]); + sei(); + i2c_start(write_all); + } + + if (config) { + CONF_ANALOG_IN(0, CONF_RESOLUTION(18)); // Analog In 0 + CONF_ANALOG_IN(0, CONF_MIN(CONF_NEGATIVE_MILLIVOLT(10000))); + CONF_ANALOG_IN(0, CONF_MAX(CONF_POSITIVE_MILLIVOLT(10000))); + CONF_ANALOG_IN(1, CONF_RESOLUTION(18)); // Analog In 1 + CONF_ANALOG_IN(1, CONF_MIN(CONF_NEGATIVE_MILLIVOLT(10000))); + CONF_ANALOG_IN(1, CONF_MAX(CONF_POSITIVE_MILLIVOLT(10000))); + CONF_ANALOG_IN(2, CONF_RESOLUTION(18)); // Analog In 2 + CONF_ANALOG_IN(2, CONF_MIN(CONF_NEGATIVE_MILLIVOLT(10000))); + CONF_ANALOG_IN(2, CONF_MAX(CONF_POSITIVE_MILLIVOLT(10000))); + CONF_ANALOG_IN(3, CONF_RESOLUTION(18)); // Analog In 3 + CONF_ANALOG_IN(3, CONF_MIN(CONF_NEGATIVE_MILLIVOLT(10000))); + CONF_ANALOG_IN(3, CONF_MAX(CONF_POSITIVE_MILLIVOLT(10000))); + + CONF_ANALOG_OUT(0, CONF_RESOLUTION(16)); // Analog Out 0 + //CONF_ANALOG_OUT(0, CONF_MIN(CONF_NEGATIVE_MILLIVOLT(10000))); + //CONF_ANALOG_OUT(0, CONF_MAX(CONF_POSITIVE_MILLIVOLT(10000))); + CONF_ANALOG_OUT(0, CONF_MIN(conf_millivolt(calibration.channel[0].min))); + CONF_ANALOG_OUT(0, CONF_MAX(conf_millivolt(calibration.channel[0].max))); + CONF_ANALOG_OUT(1, CONF_RESOLUTION(16)); // Analog Out 1 + CONF_ANALOG_OUT(1, CONF_MIN(conf_millivolt(calibration.channel[1].min))); + CONF_ANALOG_OUT(1, CONF_MAX(conf_millivolt(calibration.channel[1].max))); + + CONF_END(); + } + } +} + diff --git a/china_io-2019/python/calibrator.py b/china_io-2019/python/calibrator.py new file mode 100755 index 0000000000000000000000000000000000000000..1a0965ca477446b7253ccd9e50a0236d3f9ee775 --- /dev/null +++ b/china_io-2019/python/calibrator.py @@ -0,0 +1,237 @@ +#!/usr/bin/python3 + +import sys +import time +import numpy +import traceback +from serialio import SerialIO +import math + +class Welford: + + def __init__(self, value): + self.k = 0 + self.M = 0 + self.S = 0 + self.update(value) + pass + + def update(self, value): + self.k += 1 + newM = self.M + (value - self.M) / self.k + newS = self.S + (value - self.M) * (value - newM) + self.M = newM + self.S = newS + + def mean(self): + return self.M + + def stddev(self): + if self.k==1: + return 0 + return math.sqrt(self.S/(self.k-1)) + + def __repr__(self): + return "%f +- %f" % (self.mean(), self.stddev()) + + +class ChinaIO: + ANALOG_OUT_0 = 0 + ANALOG_OUT_1 = 1 + AIN_STEP = 20/(1<<18) # 20V, 18 bit resolution + AOUT_STEP = 20/(1<<16) # 20V, 18 bit resolution + + def __init__(self, port): + self.io = SerialIO(port) + self.action_msg = "" + self.status_msg = "" + pass + + def close(self): + self.io.setchannel(self.ANALOG_OUT_0, 0x8000) + self.io.setchannel(self.ANALOG_OUT_1, 0x8000) + + def measure(self, analogIn, N=100, message=''): + time.sleep(0.1) + stat = {} + for b in range(N): + print(message, '[', end='') + for i,c in analogIn.items(): + value = (self.io.getchannel(i) - (1<<17)) * self.AIN_STEP + if not i in stat: + stat[i] = Welford(value) + pass + else: + stat[i].update(value) + pass + print('%s ' % stat[i], end='') + pass + print(']', end='\r') + pass + print() + return [ stat[i] for i in sorted(stat.keys()) ] + + def setCalibration(self, index, value): + if value > 0: + self.io.setchannel(31, (value << 8) | index & 0x3f) + pass + else: + self.io.setchannel(31, (-value << 8) | 0x80 | index & 0x7f) + pass + pass + + def setVoltage(self, channel, value): + self.io.setchannel(channel, int(value/ self.AOUT_STEP + (1<<15))) + + def expect(self, analogIn, ref, expect, N, message): + for i,r in zip(range(100), ref): + if r != None: + self.setVoltage(i, r) + pass + pass + time.sleep(0.1) # Let values settle + sample = self.measure(analogIn, N=N, message=message) + for e,v in zip(expect, sample): + if e != None and (v.mean() < e[0] or e[1] < v.mean()): + print(" Expected:", expect) + return False + pass + return True + + def calibrate(self): + # Calibration steps: + # 1. Sanity check returned channels + # 2. Set offset calibration to default value + # 3. Read values from grounded inputs + # 4. Connect AOut0 -> AIn0,AIn2 (+9V) + # 5. Connect AOut1 -> AIn1,AIn3 (-9V) + # 6. Zero adjust + # 7. Determine min and max values + + # Sanity check + analogIn = self.io.analogIn() + analogOut = self.io.analogOut() + if len(analogIn) != 4: + raise Exception('Did not find 4 analogIn channels') + if len(analogOut) != 2: + raise Exception('Did not find 2 analogOut channels') + for i,c in analogIn.items(): + if c.bits != 18: + raise Exception('analogIn[%d] is not 18 bit (%d)' % (i, c.bits)) + pass + + # Set offset calibration to default value + # self.io.setchannel(31, (0x8000 << 8) | 0x00) + # self.io.setchannel(31, (0x8000 << 8) | 0x00) + self.setCalibration(0, 0x8000) + self.setCalibration(1, 0x8000) + + # Read values from grounded inputs + while not self.expect(analogIn, + [ -9, 9 ], + [ (-0.1, 0.1), (-0.1, 0.1), + (-0.1, 0.1), (-0.1, 0.1) ], + N=500, + message='Ground all input pins'): + break + pass + + # Connect AOut0 -> AIn0,AIn2 (+9V) + while not self.expect(analogIn, + [ 9, None ], + [ (8.7,9.3), None, + (8.7,9.3), None ], + N=500, + message='Connect: AOut0 -> AIn0, AIn2'): + pass + + # Connect AOut1 -> AIn1,AIn3 (-9V) + while not self.expect(analogIn, + [ None, -9 ], + [ None, (-9.3,-8.7), + None, (-9.3,-8.7) ], + N=500, + message='Connect: AOut1 -> AIn1, AIn3'): + pass + + # Zero adjust + while not self.expect(analogIn, + [ 0, 0 ], + [ (-0.1, 0.1), (-0.1, 0.1), + (-0.1, 0.1),(-0.1, 0.1) ], + N=500, + message='Calibrating zero'): + pass + + # 5. Zero adjust + v0 = 0x8000 + v1 = 0x8000 + delta = 0x4000 + self.io.setchannel(0, 0x8000) + self.io.setchannel(1, 0x8000) + self.setCalibration(0, v0) + self.setCalibration(1, v1) + sample = self.measure(analogIn, N=100, message='Sampling baseline') + def nextv(oldv, delta, sample): + if sample.mean() > 0: + return oldv + delta + else: + return oldv - delta + pass + candidate = [[], []] + while delta >= 1: + v0 = nextv(v0, delta, sample[0]) + v1 = nextv(v1, delta, sample[1]) + self.setCalibration(0, v0) + self.setCalibration(1, v1) + sample = self.measure(analogIn, N=200, + message='Zero (v0=%x, v1=%x)' % (v0, v1)) + candidate[0].append([v0, sample[0]]) + candidate[1].append([v1, sample[1]]) + delta = delta // 2 + pass + best = [ sorted(candidate[0], key=lambda x: abs(x[1].mean())), + sorted(candidate[1], key=lambda x: abs(x[1].mean())) ] + print("Done %x %x : %x %x" % (v0, v1, best[0][0][0], best[1][0][0])) + v0 = best[0][0][0] + v1 = best[0][0][0] + self.setCalibration(0, v0) + self.setCalibration(1, v1) + + # Determine min and max values + self.setVoltage(0, -9) + self.setVoltage(1, -9) + min_sample = self.measure(analogIn, N=200, message='Min (-9V)') + self.setVoltage(0, 9) + self.setVoltage(1, 9) + max_sample = self.measure(analogIn, N=200, message='Max (+9V)') + print("0: Min: %f, Max: %f" % (min_sample[0].mean() / 9 * 10, + max_sample[0].mean() / 9 * 10)) + print("1: Min: %f, Max: %f" % (min_sample[1].mean() / 9 * 10, + max_sample[1].mean() / 9 * 10)) + def millivolt(v): + return int(v * 1000) + self.setCalibration(2, millivolt(min_sample[0].mean() / 9 * 10)) + self.setCalibration(3, millivolt(max_sample[0].mean() / 9 * 10)) + self.setCalibration(4, millivolt(min_sample[1].mean() / 9 * 10)) + self.setCalibration(5, millivolt(max_sample[1].mean() / 9 * 10)) + self.setCalibration(6, 0); + pass + + def write_calibration(self, index, value): + if value < 0: + tmp = (int(-value * 1000) << 8) | 0x80 | index + else: + tmp = (int(value * 1000) << 8) | index + print("%d %8x" % (index, tmp)) + self.io.setchannel(31, tmp, 0xffffffff) + +if __name__ == "__main__": + io = ChinaIO(sys.argv[1]) + try: + io.calibrate() + except: + traceback.print_exc() + pass + + io.close() diff --git a/china_io-2019/python/serialio.py b/china_io-2019/python/serialio.py new file mode 100644 index 0000000000000000000000000000000000000000..c2ff52622f61b65055ce249a4c860a98eafe23da --- /dev/null +++ b/china_io-2019/python/serialio.py @@ -0,0 +1,227 @@ +#!/usr/bin/python3 + +import serial +import threading +import struct + +class IO: + + def __init__(self): + self.min = None + self.max = None + self.bits = None + + def parseMinMax(self, bits): + unit = bits & 0x07 + sign = (bits >> 3) & 0x01 + value = (bits >> 4) + if unit == 0b000 or unit == 0b100: + result = value + pass + elif unit == 0b001: + result = 1e-3 * value + pass + elif unit == 0b010: + result = 1e-6 * value + pass + else: + result = value + pass + if sign == 1: + result = -result + return result + + def setBits(self, bits): + self.bits = bits + + def setMin(self, min): + self.min = self.parseMinMax(min) + + def setMax(self, max): + self.max = self.parseMinMax(max) + + def __repr__(self): + return "bits:%s min:%s max:%s" % (self.bits, self.min, self.max) + +class DigitalIn(IO): + pass + +class DigitalOut(IO): + pass + +class AnalogIn(IO): + pass + +class AnalogOut(IO): + pass + +class Counter(IO): + pass + +class SerialIOConfig: + CMD = { + 0: IO.setBits, + 1: IO.setMin, + 2: IO.setMax, + } + + KIND = { + 0: None, + 1: DigitalIn, + 2: DigitalOut, + 3: AnalogIn, + 4: AnalogOut, + 5: Counter + } + + def __init__(self, serialio): + self.serialio = serialio + self.config = {} + self.cond = threading.Condition() + pass + + def digitalIn(self): + return self.config[DigitalIn] + + def digitalOut(self): + return self.config[DigitalOut] + + def analogIn(self): + return self.config[AnalogIn] + + def analogOut(self): + return self.config[AnalogOut] + + def counter(self): + return self.config[Counter] + + def __str__(self): + return "%s" % self.config + + def get(self): + self.serialio.pollchannel(31) + self.cond.acquire() + self.cond.wait(2) + self.cond.release() + pass + + def handle(self, value): + channel = value & 0x1f + kind = (value >> 5) & 0x07 + cmd = (value >> 8) & 0x03 + value = value >> 10 + kind = self.KIND[kind] + if kind == None: + self.cond.acquire() + self.cond.notifyAll() + self.cond.release() + return True + else: + if not kind in self.config: + self.config[kind] = {} + pass + if not channel in self.config[kind]: + self.config[kind][channel] = kind() + pass + cmd = self.CMD[cmd] + cmd(self.config[kind][channel], value) + pass + pass + + pass + + +class SerialIO: + def __init__(self, port): + self.channel = {} + self.tty = serial.Serial(port, 115200) + self.config = SerialIOConfig(self) + self.cond = threading.Condition() + t = threading.Thread(target=self.read) + t.setDaemon(True) + t.start() + self.config.get() + + def digitalIn(self): + return self.config.digitalIn() + + def digitalOut(self): + return self.config.digitalOut() + + def analogIn(self): + return self.config.analogIn() + + def analogOut(self): + return self.config.analogOut() + + def counter(self): + return self.config.counter() + + def pollchannel(self, index): + self.cond.acquire() + self.channel[index] = None + self.tty.write(struct.pack('B', (0x60 | index))) + self.cond.release() + + def waitchannel(self, index): + self.cond.acquire() + while self.channel[index] == None: + self.cond.wait() + result = self.channel[index] + self.cond.release() + return result + + def getchannel(self, index): + self.pollchannel(index) + return self.waitchannel(index) + + def setchannel(self, index, value): + #value = int(max(0, min(value, bound))) + self.cond.acquire() + if value >= (1<<30): + self.tty.write(struct.pack('B', 0x80 | ((value >> 30) & 0x03))) + if value >= (1<<23): + self.tty.write(struct.pack('B', 0x80 | ((value >> 23) & 0x7f))) + if value >= (1<<16): + self.tty.write(struct.pack('B', 0x80 | ((value >> 16) & 0x7f))) + if value >= (1<<9): + self.tty.write(struct.pack('B', 0x80 | ((value >> 9) & 0x7f))) + self.tty.write(struct.pack('B', 0x80 | ((value >> 2) & 0x7f))) + self.tty.write(struct.pack('B', ((value << 5) & 0x60) | (index & 0x1f))) + self.cond.release() + pass + + def read(self): + value = 0 + n = 0 + config = {} + while True: + ch = self.tty.read(1) + + value = value << 7 | ord(ch) & 0x7f + n += 1 + if ord(ch) & 0x80 == 0: + # Last byte, so lets handle it + if n == 1: + # Digital I/O or poll + pass + else: + channel = value & 0x1f + value = value >> 5 + if channel != 31: + self.cond.acquire() + self.channel[channel] = value + self.cond.notifyAll() + self.cond.release() + pass + else: + self.config.handle(value) + pass + pass + value = 0 + n = 0 + pass + pass + pass + + pass