Mercurial > hg > hid-usb-to-ir
view hid-usb-to-ir.c @ 4:4f66576cd77a default tip
add README
author | Peter Meerwald <p.meerwald@bct-electronic.com> |
---|---|
date | Thu, 03 Jan 2013 13:12:08 +0100 |
parents | 95b8b8a0b2d0 |
children |
line wrap: on
line source
/* * Demo program for the Silabs IR Gesture USB Reference Design, * http://www.silabs.com/products/sensors/Pages/HID-USB-to-IR-Reference-Design.aspx * * Reads the part number of the cp2112 bridge, * http://www.silabs.com/products/interface/usbtouart/Pages/HID-USB-to-SMBus-Bridge.aspx, * and the part/revision/sequencer version of the si1143 via SMBus/I2C, * finally lets the four blue LEDs dance :). * * Copyright (c) 2012 Peter Meerwald, <pmeerw@pmeerw.net> * Released under GPLv3 license, see http://www.gnu.org/licenses/gpl-3.0.html. * * Depends on the HID API multi-platform library, available from * http://www.signal11.us/oss/hidapi/. * * Build with * gcc -Wall -g -o hid-usb-to-ir hid-usb-to-ir.c `pkg-config hidapi-hidraw --libs` * * May need root permission to access USB device (or appropriate udev rule). * * Based upon information from Silabs AN495 v0.2. */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include "hidapi/hidapi.h" #define SI114X_SLAVE_ADDR 0x5a #define SI114x_REG_PART_ID 0x00 #define SI114x_REG_REV_ID 0x01 #define SI114x_REG_SEQ_ID 0x02 #define SI114X_REG_HWKEY 0x07 #define SI114X_REG_MEAS_RATE 0x08 #define SI114X_REG_ALS_RATE 0x09 #define SI114X_REG_PS_RATE 0x0a #define SI114X_REG_PS_LED21 0x0f #define SI114X_REG_PS_LED3 0x10 #define SI114X_REG_PARAM_WR 0x17 #define SI114X_REG_COMMAND 0x18 #define SI114X_REG_ALSVIS_DATA0 0x22 #define SI114X_REG_ALSVIS_DATA1 0x23 #define SI114X_REG_ALSIR_DATA0 0x24 #define SI114X_REG_ALSIR_DATA1 0x25 #define SI114X_REG_PS1_DATA0 0x26 #define SI114X_REG_PS1_DATA1 0x27 #define SI114X_REG_PS2_DATA0 0x28 #define SI114X_REG_PS2_DATA1 0x29 #define SI114X_REG_PS3_DATA0 0x2a #define SI114X_REG_PS3_DATA1 0x2b #define SI114X_REG_AUX_DATA0 0x2c #define SI114X_REG_AUX_DATA1 0x2d /* Commands for COMMAND */ #define SI114X_CMD_NOP 0x00 #define SI114X_CMD_RESET 0x01 #define SI114X_CMD_BUSADDR 0x02 #define SI114X_CMD_PS_FORCE 0x05 #define SI114X_CMD_ALS_FORCE 0x06 #define SI114X_CMD_PSALS_FORCE 0x07 #define SI114X_CMD_PS_PAUSE 0x09 #define SI114X_CMD_ALS_PAUSE 0x0a #define SI114X_CMD_PSALS_PAUSE 0x0b #define SI114X_CMD_PS_AUTO 0x0d #define SI114X_CMD_ALS_AUTO 0x0e #define SI114X_CMD_PSALS_AUTO 0x0f #define SI114X_CMD_PARAM_QUERY 0x80 #define SI114X_CMD_PARAM_SET 0xa0 #define SI114X_CMD_PARAM_AND 0xc0 #define SI114X_CMD_PARAM_OR 0xe0 /* Parameter offsets */ #define SI114X_PARAM_CHLIST 0x01 #define SI114X_PARAM_PSLED12_SELECT 0x02 #define SI114X_PARAM_PSLED3_SELECT 0x03 #define SI114X_PARAM_PS_ADC_COUNTER 0x0a #define SI114X_PARAM_PS_ADC_GAIN 0x0b #define SI114X_PARAM_PS_ADC_MISC 0x0c /* Channel enable masks for CHLIST parameter */ #define SI114X_CHLIST_EN_PS1 0x01 #define SI114X_CHLIST_EN_PS2 0x02 #define SI114X_CHLIST_EN_PS3 0x04 #define SI114X_CHLIST_EN_ALSVIS 0x10 #define SI114X_CHLIST_EN_ALSIR 0x20 #define SI114X_CHLIST_EN_AUX 0x40 /* Measurement rate settings */ #define SI114X_MEAS_RATE_FORCED 0x00 #define SI114X_MEAS_RATE_10MS 0x84 #define SI114X_MEAS_RATE_20MS 0x94 #define SI114X_MEAS_RATE_100MS 0xb9 #define SI114X_MEAS_RATE_496MS 0xdf #define SI114X_MEAS_RATE_1984MS 0xff /* ALS rate settings relative to measurement rate */ #define SI114X_ALS_RATE_OFF 0x00 #define SI114X_ALS_RATE_1X 0x08 #define SI114X_ALS_RATE_10X 0x32 #define SI114X_ALS_RATE_100X 0x69 /* PS rate settings relative to measurement rate */ #define SI114X_PS_RATE_OFF 0x00 #define SI114X_PS_RATE_1X 0x08 #define SI114X_PS_RATE_10X 0x32 #define SI114X_PS_RATE_100X 0x69 #define CP2112_GETSET_GPIO_CONFIG 0x02 #define CP2112_GET_GPIO 0x03 #define CP2112_SET_GPIO 0x04 #define CP2122_GET_VERSION_INFO 0x05 #define CP2112_GETSET_SMBUS_CONFIG 0x06 #define CP2112_DATA_WRITE_READ_REQ 0x11 #define CP2112_DATA_READ_RESP 0x13 #define CP2112_DATA_WRITE 0x14 #define CP2112_TRANSFER_STATUS_REQ 0x15 #define CP2112_TRANSFER_STATUS_RESP 0x16 #define CP2112_LED_DOWN 0x01 /* LED DS2, GPIO 0 */ #define CP2112_LED_UP 0x02 /* LED DS3, GPIO 1 */ #define CP2112_LED_RIGHT 0x08 /* LED DS8, GPIO 3 */ #define CP2112_LED_LEFT 0x80 /* LED DS4, GPIO 7 */ #define CP2112_LEDS_MASK (CP2112_LED_DOWN | CP2112_LED_UP | \ CP2112_LED_RIGHT | CP2112_LED_LEFT) /* get cp2112 part number and version */ static int cp2112_get_version(hid_device *hd, unsigned char data[2]) { unsigned char buf[3] = { CP2122_GET_VERSION_INFO, }; int ret = hid_get_feature_report(hd, buf, sizeof(buf)); if (ret < 0) { fprintf(stderr, "hid_get_feature_report() failed: %ls\n", hid_error(hd)); return -1; } data[0] = buf[1]; /* part number */ data[1] = buf[2]; /* version */ return 0; } /* configures cp2112 to automatically send read data, see AN495 section 4.6 */ static int cp2112_set_auto_send_read(hid_device *hd, int on_off) { unsigned char buf[14] = { CP2112_GETSET_SMBUS_CONFIG, }; int ret = hid_get_feature_report(hd, buf, sizeof(buf)); if (ret < 0) { fprintf(stderr, "hid_get_feature_report() failed: %ls\n", hid_error(hd)); return -1; } buf[6] = on_off; ret = hid_send_feature_report(hd, buf, sizeof(buf)); if (ret < 0) { fprintf(stderr, "hid_send_feature_report() failed: %ls\n", hid_error(hd)); return -1; } return 0; } /* see if the cp2112 is idle */ static int cp2112_is_idle(hid_device *hd) { unsigned char status_req[2] = { CP2112_TRANSFER_STATUS_REQ, 0x01, }; if (hid_write(hd, status_req, sizeof(status_req)) < 0) { fprintf(stderr, "hid_write() failed: %ls\n", hid_error(hd)); return -1; } unsigned char status_resp[7] = { 0x00, }; if (hid_read(hd, status_resp, sizeof(status_resp)) < 0) { fprintf(stderr, "hid_read() failed: %ls\n", hid_error(hd)); return -1; } if (status_resp[0] == CP2112_TRANSFER_STATUS_RESP) { if (status_resp[1] == 0x00) /* is idle? */ return 1; } return 0; } /* write a data byte to register reg of the si1143 */ static int cp2112_write_byte(hid_device *hd, unsigned char reg, unsigned char data) { /* 0x5a is the 7-bit SMBus slave address of the si1143 */ unsigned char buf_out[5] = { CP2112_DATA_WRITE, SI114X_SLAVE_ADDR<<1, 0x02, }; buf_out[3] = reg; buf_out[4] = data; int ret = hid_write(hd, buf_out, sizeof(buf_out)); if (ret < 0) { fprintf(stderr, "hid_write() failed: %ls\n", hid_error(hd)); return -1; } return 0; } static int cp2112_write_param_byte(hid_device *hd, unsigned char param, unsigned char data) { if (cp2112_write_byte(hd, SI114X_REG_PARAM_WR, data) < 0) return -1; if (cp2112_write_byte(hd, SI114X_REG_COMMAND, param | SI114X_CMD_PARAM_SET) < 0) return -1; return 0; } /* read a data byte from register reg of the si1143 */ static int cp2112_read_byte(hid_device *hd, unsigned char reg, unsigned char data[1]) { /* 0x5a is the 7-bit SMBus slave address of the si1143 */ unsigned char buf_out[6] = { CP2112_DATA_WRITE_READ_REQ, SI114X_SLAVE_ADDR<<1, 0x00, 0x01, 0x01, }; buf_out[5] = reg; int ret = hid_write(hd, buf_out, sizeof(buf_out)); if (ret < 0) { fprintf(stderr, "hid_write() failed: %ls\n", hid_error(hd)); return -1; } /* FIXME: may loop forever, error handling needed */ while (1) { unsigned char buf_in[8] = { 0x00, }; ret = hid_read(hd, buf_in, sizeof(buf_in)); if (ret < 0) { fprintf(stderr, "hid_read_() failed: %ls\n", hid_error(hd)); return -1; } if (buf_in[0] == CP2112_TRANSFER_STATUS_REQ) continue; if (buf_in[0] == CP2112_DATA_READ_RESP) { if (buf_in[1] == 0x02 && buf_in[2] == 0x00) continue; /* no data yet */ if (buf_in[1] == 0x02 && buf_in[2] == 0x01) { data[0] = buf_in[3]; /* data available */ return 0; } } } return -1; } static int cp2112_read_word(hid_device *hd, unsigned char reg, unsigned short data[1]) { unsigned char lsb, msb; if (cp2112_read_byte(hd, reg, &lsb) < 0) return -1; if (cp2112_read_byte(hd, reg+1, &msb) < 0) return -1; data[0] = (msb << 8) | lsb; return 0; } /* configure cp2112 GPIO pins */ static int cp2112_config_gpio(hid_device *hd) { unsigned char buf[5] = { CP2112_GETSET_GPIO_CONFIG, }; int ret = hid_get_feature_report(hd, buf, sizeof(buf)); if (ret < 0) { fprintf(stderr, "hid_get_feature_report() failed: %ls\n", hid_error(hd)); return -1; } buf[1] = 0x8b; /* output direction for GPIOs 0, 1, 3, 7 */ buf[2] = 0x8b; /* push-pull for GPIOs 0, 1, 3, 7 */ buf[3] = 0x00; /* no special functions, i.e. use pins as GPIO */ ret = hid_send_feature_report(hd, buf, sizeof(buf)); if (ret < 0) { fprintf(stderr, "hid_send_feature_report() failed: %ls\n", hid_error(hd)); return -1; } return 0; } /* set cp2112 GPIO pins */ static int cp2112_set_gpio(hid_device *hd, unsigned char mask, unsigned char value) { unsigned char buf[3] = { CP2112_SET_GPIO, }; buf[1] = ~value; /* set GPIO pins: 0 .. turn LED on, 1 .. off */ buf[2] = mask; /* mask of pins to set, ignore others */ int ret = hid_send_feature_report(hd, buf, sizeof(buf)); if (ret < 0) { fprintf(stderr, "hid_send_feature_report() failed: %ls\n", hid_error(hd)); return -1; } return 0; } int main() { if (hid_init() < 0) { fprintf(stderr, "hid_init() failed, exit.\n"); exit(EXIT_FAILURE); } /* open Silabs IR Gesture USB reference design by USB product:vendor id */ hid_device *hd = hid_open(0x10c4, 0xea90, NULL); if (hd == NULL) { fprintf(stderr, "hid_open() failed\n"); exit(EXIT_FAILURE); } unsigned char version_data[2]; if (cp2112_get_version(hd, version_data) < 0) exit(EXIT_FAILURE); printf("cp2112 part number 0x%02x, device version %d\n", version_data[0], version_data[1]); if (cp2112_set_auto_send_read(hd, 1) < 0) exit(EXIT_FAILURE); /* write 0x17 to si114x HW_KEY register as per datasheet */ if (cp2112_write_byte(hd, SI114X_REG_HWKEY, 0x17) < 0) exit(EXIT_FAILURE); unsigned char part, rev, seq; if (cp2112_read_byte(hd, SI114x_REG_PART_ID, &part) < 0) exit(EXIT_FAILURE); if (cp2112_read_byte(hd, SI114x_REG_REV_ID, &rev) < 0) exit(EXIT_FAILURE); if (cp2112_read_byte(hd, SI114x_REG_SEQ_ID, &seq) < 0) exit(EXIT_FAILURE); printf("si114x part number 0x%02x, revision 0x%02x, sequencer 0x%02x\n", part, rev, seq); if (cp2112_write_byte(hd, SI114X_REG_COMMAND, SI114X_CMD_RESET) < 0) exit(EXIT_FAILURE); usleep(20000); if (cp2112_write_byte(hd, SI114X_REG_HWKEY, 0x17) < 0) exit(EXIT_FAILURE); usleep(20000); cp2112_is_idle(hd); /* set LED currents to maximum */ if (cp2112_write_byte(hd, SI114X_REG_PS_LED3, 0x04)) exit(EXIT_FAILURE); if (cp2112_write_byte(hd, SI114X_REG_PS_LED21, 0x32)) exit(EXIT_FAILURE); if (cp2112_write_param_byte(hd, SI114X_PARAM_PS_ADC_MISC, 0x00 | 0x04)) exit(EXIT_FAILURE); if (cp2112_write_param_byte(hd, SI114X_PARAM_PSLED12_SELECT, 0x21)) exit(EXIT_FAILURE); if (cp2112_write_param_byte(hd, SI114X_PARAM_PSLED3_SELECT, 0x04)) exit(EXIT_FAILURE); if (cp2112_write_param_byte(hd, SI114X_PARAM_CHLIST, SI114X_CHLIST_EN_ALSVIS | SI114X_CHLIST_EN_PS3 | SI114X_CHLIST_EN_PS2 | SI114X_CHLIST_EN_PS1)) exit(EXIT_FAILURE); if (cp2112_write_param_byte(hd, SI114X_PARAM_PS_ADC_GAIN, 0x01)) exit(EXIT_FAILURE); if (cp2112_write_param_byte(hd, SI114X_PARAM_PS_ADC_COUNTER, 0x06 << 4)) exit(EXIT_FAILURE); /* in autonomous mode, wakeup every 100 ms */ if (cp2112_write_byte(hd, SI114X_REG_MEAS_RATE, SI114X_MEAS_RATE_100MS)) exit(EXIT_FAILURE); /* measure ALS every time device wakes up */ if (cp2112_write_byte(hd, SI114X_REG_ALS_RATE, SI114X_ALS_RATE_1X)) exit(EXIT_FAILURE); /* measure proximity every time device wakes up */ if (cp2112_write_byte(hd, SI114X_REG_PS_RATE, SI114X_PS_RATE_1X)) exit(EXIT_FAILURE); if (cp2112_write_byte(hd, SI114X_REG_COMMAND, SI114X_CMD_PSALS_AUTO)) exit(EXIT_FAILURE); cp2112_is_idle(hd); if (cp2112_config_gpio(hd) < 0) exit(EXIT_FAILURE); while (1) { unsigned short als, ps1, ps2, ps3; if (cp2112_set_gpio(hd, CP2112_LEDS_MASK, CP2112_LED_DOWN) < 0) exit(EXIT_FAILURE); usleep(50*1000); if (cp2112_set_gpio(hd, CP2112_LEDS_MASK, CP2112_LED_RIGHT) < 0) exit(EXIT_FAILURE); usleep(50*1000); if (cp2112_set_gpio(hd, CP2112_LEDS_MASK, CP2112_LED_UP) < 0) exit(EXIT_FAILURE); usleep(50*1000); if (cp2112_set_gpio(hd, CP2112_LEDS_MASK, CP2112_LED_LEFT) < 0) exit(EXIT_FAILURE); usleep(50*1000); if (cp2112_read_word(hd, SI114X_REG_ALSVIS_DATA0, &als) < 0) exit(EXIT_FAILURE); if (cp2112_read_word(hd, SI114X_REG_PS1_DATA0, &ps1) < 0) exit(EXIT_FAILURE); if (cp2112_read_word(hd, SI114X_REG_PS2_DATA0, &ps2) < 0) exit(EXIT_FAILURE); if (cp2112_read_word(hd, SI114X_REG_PS3_DATA0, &ps3) < 0) exit(EXIT_FAILURE); printf("%d %d %d %d\n", ps1, ps2, ps3, als); } /* never get here */ printf("done\n"); hid_close(hd); hid_exit(); return EXIT_SUCCESS; }