From 0e31464554f178d752870effea7603d3fb300251 Mon Sep 17 00:00:00 2001 From: Peli de Halleux Date: Tue, 2 Apr 2019 11:12:45 -0700 Subject: [PATCH] add helpers to write to i2c registers (#778) * add helpers to write to i2c registers * fixing defaults * remove register format * fix typo * adding color sensor * adding color sensor impl * missing files * moving project * stick with 8bit LE --- libs/color-sensor/README.md | 3 + libs/color-sensor/colorsensor.ts | 22 +++ libs/color-sensor/pxt.json | 16 ++ libs/color-sensor/tcs34725.ts | 272 +++++++++++++++++++++++++++++++ libs/color-sensor/test.ts | 1 + libs/core/i2c.ts | 38 ++++- pxtarget.json | 1 + 7 files changed, 352 insertions(+), 1 deletion(-) create mode 100644 libs/color-sensor/README.md create mode 100644 libs/color-sensor/colorsensor.ts create mode 100644 libs/color-sensor/pxt.json create mode 100644 libs/color-sensor/tcs34725.ts create mode 100644 libs/color-sensor/test.ts diff --git a/libs/color-sensor/README.md b/libs/color-sensor/README.md new file mode 100644 index 00000000..02c30bcc --- /dev/null +++ b/libs/color-sensor/README.md @@ -0,0 +1,3 @@ +# Color Sensor + +Ambient light sensor \ No newline at end of file diff --git a/libs/color-sensor/colorsensor.ts b/libs/color-sensor/colorsensor.ts new file mode 100644 index 00000000..2d409a06 --- /dev/null +++ b/libs/color-sensor/colorsensor.ts @@ -0,0 +1,22 @@ +namespace sensors { + export interface ColorSensor { + /** + * Reads an RGB color from the sensor + */ + color(): number; + } +} + +namespace input { + let _colorSensor: sensors.ColorSensor; + + /** + * Uses a color sensor to capture the ambient color as a RGB value. + */ + //% blockId=sensor_lightcolor block="light color" + export function lightColor(): number { + if (!_colorSensor) + _colorSensor = new sensors.TCS34725(); + return _colorSensor.color(); + } +} \ No newline at end of file diff --git a/libs/color-sensor/pxt.json b/libs/color-sensor/pxt.json new file mode 100644 index 00000000..5cbef23a --- /dev/null +++ b/libs/color-sensor/pxt.json @@ -0,0 +1,16 @@ +{ + "name": "color-sensor", + "description": "Color sensor (TCS24725)", + "files": [ + "README.md", + "colorsensor.ts", + "tcs34725.ts" + ], + "testFiles": [ + "test.ts" + ], + "public": true, + "dependencies": { + "core": "file:../core" + } +} diff --git a/libs/color-sensor/tcs34725.ts b/libs/color-sensor/tcs34725.ts new file mode 100644 index 00000000..ead037a1 --- /dev/null +++ b/libs/color-sensor/tcs34725.ts @@ -0,0 +1,272 @@ +/** + * Makecode package for the TCS34725 Color sensor. + * + * More details here: https://ams.com/documents/20143/36005/TCS3472_DS000390_2-00.pdf/6e452176-2407-faaf-a590-d526c78c7432 + */ +namespace sensors { + + const TCS34725_I2C_ADDRESS = 0x29 //I2C address of the TCS34725 (Page 34) + + /* TCS34725 register addresses (Page 20)*/ + + const TCS34725_REGISTER_COMMAND = 0x80 // Specifies register address + + const TCS34725_REGISTER_ENABLE = 0x00 // Enables states and interrupts + const TCS34725_REGISTER_AIEN_ENABLE = 0x10 // RGBC interrupt enable. When asserted, permits RGBC interrupts to be generated. + const TCS34725_REGISTER_WEN_ENABLE = 0x08 // Wait enable. This bit activates the wait feature. Writing a 1 activates the wait timer. Writing a 0 disables the wait timer. + const TCS34725_REGISTER_AEN_ENABLE = 0x02 // RGBC enable. This bit actives the two-channel ADC. Writing a 1 activates the RGBC. Writing a 0 disables the RGBC. + const TCS34725_REGISTER_PON_ENABLE = 0x01 // Power ON. This bit activates the internal oscillator to permit the timers and ADC channels to operate. Writing a 1 activates the oscillator. Writing a 0 disables the oscillator + + const TCS34725_REGISTER_ATIME = 0x01 // The RGBC timing register controls the internal integration time of the RGBC clear and IR channel ADCs in 2.4-ms increments. + const TCS34725_REGISTER_WTIME = 0x03 // Wait time is set 2.4 ms increments unless the WLONG bit is asserted, in which case the wait times are 12× longer. WTIME is programmed as a 2’s complement number. + + const TCS34725_REGISTER_AILTL = 0x04 // Clear interrupt low threshold low byte + const TCS34725_REGISTER_AILTH = 0x05 // Clear interrupt low threshold high byte + const TCS34725_REGISTER_AIHTL = 0x06 // Clear interrupt high threshold low byte + const TCS34725_REGISTER_AIHTH = 0x07 // Clear interrupt high threshold high byte + + const TCS34725_REGISTER_PERS = 0x0C // The persistence register controls the filtering interrupt capabilities of the device. + + const TCS34725_REGISTER_CONFIG = 0x0D // The configuration register sets the wait long time. + const TCS34725_REGISTER_CONFIG_WLONG = 0x02 // Configuration: Wait Long. When asserted, the wait cycles are increased by a factor 12× from that programmed in the WTIME register + + const TCS34725_REGISTER_CONTROL = 0x0F // The Control register provides eight bits of miscellaneous control to the analog block. These bits typically control functions such as gain settings and/or diode selection + const TCS34725_REGISTER_ID = 0x12 // The ID Register provides the value for the part number. The ID register is a read-only register. + + const TCS34725_REGISTER_STATUS = 0x13 // The Status Register provides the internal status of the device. This register is read only. + const TCS34725_REGISTER_STATUS_AINT = 0x10 // Device status: RGBC clear channel Interrupt + const TCS34725_REGISTER_STATUS_AVALID = 0x01 // Device status: RGBC Valid. Indicates that the RGBC channels have completed an integration cycle + + const TCS34725_REGISTER_CDATAL = 0x14 // Clear data low byte + const TCS34725_REGISTER_CDATAH = 0x15 // Clear data high byte + + const TCS34725_REGISTER_RDATAL = 0x16 // Red data low byte + const TCS34725_REGISTER_RDATAH = 0x17 // Red data high byte + + const TCS34725_REGISTER_GDATAL = 0x18 // Green data low byte + const TCS34725_REGISTER_GDATAH = 0x19 // Green data high byte + + const TCS34725_REGISTER_BDATAL = 0x1A // Blue data low byte + const TCS34725_REGISTER_BDATAH = 0x1B // Blue data high byte + + + /* #region Enums for Modes, etc */ + + // Parameters for setting the internal integration time of the RGBC clear and IR channel. + export enum TCS34725_ATIME { + ATIME_2_4_MS = 0xFF, // 1 2.4 ms 1024 + ATIME_24_MS = 0xF6, // 10 24 ms 10240 + ATIME_100_MS = 0xD5, // 42 101 ms 43008 + ATIME_154_MS = 0xC0, // 64 154 ms 65535 + ATIME_700_MS = 0x00 // 256 700 ms 65535 + } + + // Parameters for setting the wait time register. + enum TCS34725_WTIME { + WTIME_2_4_MS = 0xFF, // 1 2.4 ms 0.029 sec + WTIME_204_MS = 0xAB, // 85 204 ms 2.45 sec + WTIME_614_MS = 0x00 // 256 614 ms 7.4 sec + } + + // Parameters for setting the persistence register. The persistence register controls the filtering interrupt capabilities of the device. + enum TCS34725_APERS { + APERS_0_CLEAR = 0b0000, // Every RGBC cycle generates an interrupt + APERS_1_CLEAR = 0b0001, // 1 clear channel value outside of threshold range + APERS_2_CLEAR = 0b0010, // 2 clear channel consecutive values out of range + APERS_3_CLEAR = 0b0011, // 3 clear channel consecutive values out of range + APERS_5_CLEAR = 0b0100, // 5 clear channel consecutive values out of range + APERS_10_CLEAR = 0b0101, // 10 clear channel consecutive values out of range + APERS_15_CLEAR = 0b0110, // 15 clear channel consecutive values out of range + APERS_20_CLEAR = 0b0111, // 20 clear channel consecutive values out of range + APERS_25_CLEAR = 0b1000, // 25 clear channel consecutive values out of range + APERS_30_CLEAR = 0b1001, // 30 clear channel consecutive values out of range + APERS_35_CLEAR = 0b1010, // 35 clear channel consecutive values out of range + APERS_40_CLEAR = 0b1011, // 40 clear channel consecutive values out of range + APERS_45_CLEAR = 0b1100, // 45 clear channel consecutive values out of range + APERS_50_CLEAR = 0b1101, // 50 clear channel consecutive values out of range + APERS_55_CLEAR = 0b1110, // 55 clear channel consecutive values out of range + APERS_60_CLEAR = 0b1111, // 60 clear channel consecutive values out of range + } + + // Parameters for setting the gain of the sensor. + enum TCS34725_AGAIN { + AGAIN_1X = 0x0, // 1x gain + AGAIN_4X = 0x1, // 4x gain + AGAIN_16X = 0x2, // 16x gain + AGAIN_60X = 0x3 // 60x gain + } + + + export class TCS34725 implements sensors.ColorSensor { + isConnected: boolean; + atimeIntegrationValue: TCS34725_ATIME; + gainSensorValue: TCS34725_AGAIN; + + constructor() { + this.isConnected = false; + this.atimeIntegrationValue = 0; + this.gainSensorValue = 0; + + this.start(TCS34725_ATIME.ATIME_2_4_MS, TCS34725_AGAIN.AGAIN_1X); + } + + private connect() { + let retry = 0; + while (!this.isConnected && retry < 5) { + + //REGISTER FORMAT: CMD | TRANSACTION | ADDRESS + //REGISTER READ: TCS34725_REGISTER_COMMAND (0x80) | TCS34725_REGISTER_ID (0x12) + const device_id = pins.i2cReadRegister(TCS34725_I2C_ADDRESS, TCS34725_REGISTER_COMMAND | TCS34725_REGISTER_ID) + + //Check that device Identification has one of 2 i2c addresses + if ((device_id == 0x44) || (device_id == 0x10)) + this.isConnected = true; + + retry++; + } + } + + private turnSensorOn() { + + //REGISTER FORMAT: CMD | TRANSACTION | ADDRESS + //REGISTER VALUE: TCS34725_REGISTER_COMMAND (0x80) | TCS34725_REGISTER_ENABLE (0x00) + //REGISTER WRITE: TCS34725_REGISTER_PON_ENABLE (0x01) + pins.i2cWriteRegister(TCS34725_I2C_ADDRESS, TCS34725_REGISTER_COMMAND | TCS34725_REGISTER_ENABLE, TCS34725_REGISTER_PON_ENABLE); + basic.pause(300); + + + //REGISTER FORMAT: CMD | TRANSACTION | ADDRESS + //REGISTER VALUE: TCS34725_REGISTER_COMMAND (0x80) | TCS34725_REGISTER_ENABLE (0x00) + //REGISTER WRITE: TCS34725_REGISTER_PON_ENABLE (0x01) | TCS34725_REGISTER_AEN_ENABLE (0x02) + pins.i2cWriteRegister(TCS34725_I2C_ADDRESS, TCS34725_REGISTER_COMMAND | TCS34725_REGISTER_ENABLE, TCS34725_REGISTER_PON_ENABLE | TCS34725_REGISTER_AEN_ENABLE); + + this.pauseSensorForIntegrationTime(); + } + + private pauseSensorForIntegrationTime() { + switch (this.atimeIntegrationValue) { + case TCS34725_ATIME.ATIME_2_4_MS: { + basic.pause(2.4); + break; + } + case TCS34725_ATIME.ATIME_24_MS: { + basic.pause(24); + break; + } + case TCS34725_ATIME.ATIME_100_MS: { + basic.pause(100); + break; + } + case TCS34725_ATIME.ATIME_154_MS: { + basic.pause(154); + break; + } + case TCS34725_ATIME.ATIME_700_MS: { + basic.pause(700); + break; + } + } + } + + private turnOff() { + //REGISTER FORMAT: CMD | TRANSACTION | ADDRESS + //REGISTER READ: TCS34725_REGISTER_COMMAND (0x80) | TCS34725_REGISTER_ID (0x12) + let sensorReg = pins.i2cReadNumber(TCS34725_I2C_ADDRESS, TCS34725_REGISTER_COMMAND | TCS34725_REGISTER_ENABLE); + + //REGISTER FORMAT: CMD | TRANSACTION | ADDRESS + //REGISTER VALUE: TCS34725_REGISTER_COMMAND (0x80) | TCS34725_REGISTER_ENABLE (0x00) + //REGISTER WRITE: sensorReg & ~(TCS34725_REGISTER_PON_ENABLE (0x01) | TCS34725_REGISTER_AEN_ENABLE (0x02)) + pins.i2cWriteRegister(TCS34725_I2C_ADDRESS, TCS34725_REGISTER_COMMAND | TCS34725_REGISTER_ENABLE, sensorReg & ~(TCS34725_REGISTER_PON_ENABLE | TCS34725_REGISTER_AEN_ENABLE)); + } + + setATIMEintegration(atime: TCS34725_ATIME) { + //Always make sure the color sensor is connected. Useful for cases when this block is used but the sensor wasn't set randomly. + this.connect(); + + //REGISTER FORMAT: CMD | TRANSACTION | ADDRESS + //REGISTER VALUE: TCS34725_REGISTER_COMMAND (0x80) | TCS34725_REGISTER_ATIME (0x01) + //REGISTER WRITE: atime + pins.i2cWriteRegister(TCS34725_I2C_ADDRESS, TCS34725_REGISTER_COMMAND | TCS34725_REGISTER_ATIME, atime) + this.atimeIntegrationValue = atime; + + } + + setGAINsensor(gain: TCS34725_AGAIN) { + //Always make sure the color sensor is connected. Useful for cases when this block is used but the sensor wasn't set randomly. + this.connect(); + + //REGISTER FORMAT: CMD | TRANSACTION | ADDRESS + //REGISTER VALUE: TCS34725_REGISTER_COMMAND (0x80) | TCS34725_REGISTER_CONTROL (0x0F) + //REGISTER WRITE: gain + pins.i2cWriteRegister(TCS34725_I2C_ADDRESS, TCS34725_REGISTER_COMMAND | TCS34725_REGISTER_CONTROL, gain) + + this.gainSensorValue = gain; + } + + private start(atime: TCS34725_ATIME, gain: TCS34725_AGAIN) { + this.connect(); + this.setATIMEintegration(atime); + this.setGAINsensor(gain); + this.turnSensorOn(); + } + + color(): number { + //Always check that sensor is/was turned on + this.connect(); + + if (!this.isConnected) + return 0; + + //REGISTER FORMAT: CMD | TRANSACTION | ADDRESS + //REGISTER READ: TCS34725_REGISTER_COMMAND (0x80) | TCS34725_REGISTER_RDATAL (0x16) + const redColorValue = pins.i2cReadRegister(TCS34725_I2C_ADDRESS, TCS34725_REGISTER_COMMAND | TCS34725_REGISTER_RDATAL, NumberFormat.UInt16LE); + + //REGISTER FORMAT: CMD | TRANSACTION | ADDRESS + //REGISTER READ: TCS34725_REGISTER_COMMAND (0x80) | TCS34725_REGISTER_GDATAL (0x18) + const greenColorValue = pins.i2cReadRegister(TCS34725_I2C_ADDRESS, TCS34725_REGISTER_COMMAND | TCS34725_REGISTER_GDATAL, NumberFormat.UInt16LE); + + //REGISTER FORMAT: CMD | TRANSACTION | ADDRESS + //REGISTER READ: TCS34725_REGISTER_COMMAND (0x80) | TCS34725_REGISTER_BDATAL (0x1A) + const blueColorValue = pins.i2cReadRegister(TCS34725_I2C_ADDRESS, TCS34725_REGISTER_COMMAND | TCS34725_REGISTER_BDATAL, NumberFormat.UInt16LE); + + //REGISTER FORMAT: CMD | TRANSACTION | ADDRESS + //REGISTER READ: TCS34725_REGISTER_COMMAND (0x80) | TCS34725_REGISTER_CDATAL (0x14) + const clearColorValue = pins.i2cReadRegister(TCS34725_I2C_ADDRESS, TCS34725_REGISTER_COMMAND | TCS34725_REGISTER_CDATAL, NumberFormat.UInt16LE); + + this.pauseSensorForIntegrationTime(); + + let sum = clearColorValue; + let r = 0; + let g = 0; + let b = 0; + + if (clearColorValue == 0) + return 0; + else { + r = (redColorValue / sum * 255) & 0xff; + g = (greenColorValue / sum * 255) & 0xff; + b = (blueColorValue / sum * 255) & 0xff; + + return (r << 16) | (g << 8) | b; + } + } + } +} + +/* +let strip: neopixel.Strip = null +strip = neopixel.create(DigitalPin.P1, 30, NeoPixelMode.RGB) +strip.setBrightness(255) + +TCS34725.start(TCS34725_ATIME.ATIME_2_4_MS, TCS34725_AGAIN.AGAIN_1X); + +basic.forever(function () { + basic.pause(2000); + serial.writeLine(TCS34725.isConnected + ""); + let a = TCS34725.getSensorRGB(); + + strip.showColor(neopixel.rgb(a.red, a.green, a.blue)); + +}) + +*/ \ No newline at end of file diff --git a/libs/color-sensor/test.ts b/libs/color-sensor/test.ts new file mode 100644 index 00000000..858cfedb --- /dev/null +++ b/libs/color-sensor/test.ts @@ -0,0 +1 @@ +// tests diff --git a/libs/core/i2c.ts b/libs/core/i2c.ts index 0473b5f8..a74c561e 100644 --- a/libs/core/i2c.ts +++ b/libs/core/i2c.ts @@ -16,12 +16,48 @@ namespace pins { */ //% help=pins/i2c-write-number weight=4 group="i2c" //% blockId=i2c_writenumber block="i2c write number|at address %address|with value %value|of format %format|repeated %repeated" - export function i2cWriteNumber(address: number, value: number, format: NumberFormat, repeated?: boolean): void { + export function i2cWriteNumber(address: number, value: number, format?: NumberFormat, repeated?: boolean): void { + if (format == undefined) + format = NumberFormat.UInt8LE; const buf = control.createBuffer(pins.sizeOf(format)) buf.setNumber(format, 0, value) pins.i2cWriteBuffer(address, buf, repeated) } + /** + * Writes a value in a I2C register. + * @param address I2c address of the device + * @param register register index + * @param value value to write + * @param valueFormat format of the value, default is UInt8LE + */ + //% weight=3 group="i2c" + //% blockId=i2c_writereg block="i2c write register|at address $address|at register $register|value $value" + export function i2cWriteRegister(address: number, register: number, value: number, valueFormat?: NumberFormat): void { + if (valueFormat === undefined) + valueFormat = NumberFormat.UInt8LE; + const valueSize = pins.sizeOf(valueFormat); + const buf = control.createBuffer(1 + valueSize); + buf.setNumber(NumberFormat.UInt8LE, 0, register); + buf.setNumber(valueFormat, 1, value); + pins.i2cWriteBuffer(address, buf); + } + + /** + * Reads the value from a I2C register. + * @param address I2c address of the device + * @param register register index + * @param valueFormat format of the value, default is UInt8LE + */ + //% weight=3 group="i2c" + //% blockId=i2c_readreg block="i2c read register|at address $addfress|at register $register" + export function i2cReadRegister(address: number, register: number, valueFormat?: NumberFormat): number { + if (valueFormat === undefined) + valueFormat = NumberFormat.UInt8LE; + pins.i2cWriteNumber(address, register, NumberFormat.UInt8LE); + return pins.i2cReadNumber(address, valueFormat); + } + /** * Read `size` bytes from a 7-bit I2C `address`. */ diff --git a/pxtarget.json b/pxtarget.json index d08b9810..2ded1d72 100644 --- a/pxtarget.json +++ b/pxtarget.json @@ -35,6 +35,7 @@ "libs/lora", "libs/power", "libs/controller", + "libs/color-sensor", "libs/edge-connector" ] }