From af1af1e22ddd4bcd55fe9eaaf98f4a640329a4c7 Mon Sep 17 00:00:00 2001 From: Lorenz Meier Date: Tue, 29 Apr 2014 18:38:30 +0200 Subject: Port expander driver first hacky version --- makefiles/config_px4fmu-v2_default.mk | 1 + makefiles/config_px4fmu-v2_test.mk | 1 + src/drivers/drv_io_expander.h | 65 ++++ src/drivers/pca8574/module.mk | 6 + src/drivers/pca8574/pca8574.cpp | 569 ++++++++++++++++++++++++++++++++++ 5 files changed, 642 insertions(+) create mode 100644 src/drivers/drv_io_expander.h create mode 100644 src/drivers/pca8574/module.mk create mode 100644 src/drivers/pca8574/pca8574.cpp diff --git a/makefiles/config_px4fmu-v2_default.mk b/makefiles/config_px4fmu-v2_default.mk index e13421acc..602b0af3f 100644 --- a/makefiles/config_px4fmu-v2_default.mk +++ b/makefiles/config_px4fmu-v2_default.mk @@ -40,6 +40,7 @@ MODULES += drivers/meas_airspeed MODULES += drivers/frsky_telemetry MODULES += modules/sensors MODULES += drivers/mkblctrl +MODULES += drivers/pca8574 # Needs to be burned to the ground and re-written; for now, diff --git a/makefiles/config_px4fmu-v2_test.mk b/makefiles/config_px4fmu-v2_test.mk index 79922374d..0dcfd04f2 100644 --- a/makefiles/config_px4fmu-v2_test.mk +++ b/makefiles/config_px4fmu-v2_test.mk @@ -24,6 +24,7 @@ MODULES += drivers/lsm303d MODULES += drivers/l3gd20 MODULES += drivers/hmc5883 MODULES += drivers/ms5611 +MODULES += drivers/pca8574 MODULES += systemcmds/perf MODULES += systemcmds/reboot MODULES += systemcmds/tests diff --git a/src/drivers/drv_io_expander.h b/src/drivers/drv_io_expander.h new file mode 100644 index 000000000..2705d6f9e --- /dev/null +++ b/src/drivers/drv_io_expander.h @@ -0,0 +1,65 @@ +/**************************************************************************** + * + * Copyright (c) 2014 PX4 Development Team. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name PX4 nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +/** + * @file drv_io_expander.h + * + * IO expander device API + */ + +#pragma once + +#include +#include + +/* + * ioctl() definitions + */ + +#define _IOXIOCBASE (0x2800) +#define _IOXIOC(_n) (_IOC(_IOXIOCBASE, _n)) + +/** enable the device */ +#define IOX_ENABLE _IOXIOC(1) + +/** set constant values */ +#define IOX_SET_VALUE _IOXIOC(2) + +/** set constant values */ +#define IOX_SET_MODE _IOXIOC(3) + +/* enum passed to RGBLED_SET_MODE ioctl()*/ +enum IOX_MODE { + IOX_MODE_OFF, + IOX_MODE_ON +}; diff --git a/src/drivers/pca8574/module.mk b/src/drivers/pca8574/module.mk new file mode 100644 index 000000000..825ee9bb7 --- /dev/null +++ b/src/drivers/pca8574/module.mk @@ -0,0 +1,6 @@ +# +# PCA8574 driver for RGB LED +# + +MODULE_COMMAND = pca8574 +SRCS = pca8574.cpp diff --git a/src/drivers/pca8574/pca8574.cpp b/src/drivers/pca8574/pca8574.cpp new file mode 100644 index 000000000..ce882b636 --- /dev/null +++ b/src/drivers/pca8574/pca8574.cpp @@ -0,0 +1,569 @@ +/**************************************************************************** + * + * Copyright (C) 2012, 2013 PX4 Development Team. All rights reserved. + * Author: Julian Oes + * Anton Babushkin + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name PX4 nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +/** + * @file pca8574.cpp + * + * Driver for the onboard RGB LED controller (TCA62724FMG) connected via I2C. + * + */ + +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +#include + +#include + +#define PCA8574_ONTIME 120 +#define PCA8574_OFFTIME 120 +#define PCA8574_DEVICE_PATH "/dev/pca8574" + +#define ADDR 0x20 /**< I2C adress of PCA8574 (default, A0-A2 pulled to GND) */ +#define SUB_ADDR_START 0x01 /**< write everything (with auto-increment) */ +#define SUB_ADDR_PWM0 0x81 /**< blue (without auto-increment) */ +#define SUB_ADDR_PWM1 0x82 /**< green (without auto-increment) */ +#define SUB_ADDR_PWM2 0x83 /**< red (without auto-increment) */ +#define SUB_ADDR_SETTINGS 0x84 /**< settings (without auto-increment)*/ + +#define SETTING_NOT_POWERSAVE 0x01 /**< power-save mode not off */ +#define SETTING_ENABLE 0x02 /**< on */ + + +class PCA8574 : public device::I2C +{ +public: + PCA8574(int bus, int pca8574); + virtual ~PCA8574(); + + + virtual int init(); + virtual int probe(); + virtual int info(); + virtual int ioctl(struct file *filp, int cmd, unsigned long arg); + +private: + work_s _work; + + uint8_t _values[8]; + float _brightness; + + enum IOX_MODE _mode; + bool _running; + int _led_interval; + bool _should_run; + int _counter; + + static void led_trampoline(void *arg); + void led(); + + int send_led_enable(bool enable); + int send_led_values(); +}; + +/* for now, we only support one PCA8574 */ +namespace +{ +PCA8574 *g_pca8574; +} + +void pca8574_usage(); + +extern "C" __EXPORT int pca8574_main(int argc, char *argv[]); + +PCA8574::PCA8574(int bus, int pca8574) : + I2C("pca8574", PCA8574_DEVICE_PATH, bus, pca8574, 100000), + _mode(IOX_MODE_OFF), + _values({}), + _brightness(1.0f), + _running(false), + _led_interval(0), + _should_run(false), + _counter(0) +{ + memset(&_work, 0, sizeof(_work)); +} + +PCA8574::~PCA8574() +{ +} + +int +PCA8574::init() +{ + int ret; + ret = I2C::init(); + + if (ret != OK) { + return ret; + } + + /* switch off LED on start */ + send_led_enable(false); + + /* kick it in */ + _should_run = true; + _led_interval = 80; + work_queue(LPWORK, &_work, (worker_t)&PCA8574::led_trampoline, this, 1); + + return OK; +} + +int +PCA8574::probe() +{ + + return send_led_enable(false); +} + +int +PCA8574::info() +{ + int ret = OK; + + return ret; +} + +int +PCA8574::ioctl(struct file *filp, int cmd, unsigned long arg) +{ + int ret = ENOTTY; + + switch (cmd) { + case IOX_SET_VALUE ... (IOX_SET_VALUE + 8): + { + /* set the specified color */ + unsigned prev = _values[cmd - IOX_SET_VALUE]; + _values[cmd - IOX_SET_VALUE] = arg; + if (_values[cmd - IOX_SET_VALUE] != prev) { + // XXX will be done with a change flag + send_led_values(); + } + return OK; + } + + case IOX_ENABLE: + send_led_enable(arg); + return OK; + + default: + /* see if the parent class can make any use of it */ + ret = CDev::ioctl(filp, cmd, arg); + break; + } + + return ret; +} + + +void +PCA8574::led_trampoline(void *arg) +{ + PCA8574 *rgbl = reinterpret_cast(arg); + + rgbl->led(); +} + +/** + * Main loop function + */ +void +PCA8574::led() +{ + if (!_should_run) { + _running = false; + return; + } + + // switch (_mode) { + // case PCA8574_MODE_BLINK_SLOW: + // case PCA8574_MODE_BLINK_NORMAL: + // case PCA8574_MODE_BLINK_FAST: + // if (_counter >= 2) + // _counter = 0; + + // send_led_enable(_counter == 0); + + // break; + + // case PCA8574_MODE_BREATHE: + + // if (_counter >= 62) + // _counter = 0; + + // int n; + + // if (_counter < 32) { + // n = _counter; + + // } else { + // n = 62 - _counter; + // } + + // _brightness = n * n / (31.0f * 31.0f); + // send_led_rgb(); + // break; + + // case PCA8574_MODE_PATTERN: + + // /* don't run out of the pattern array and stop if the next frame is 0 */ + // if (_counter >= PCA8574_PATTERN_LENGTH || _pattern.duration[_counter] <= 0) + // _counter = 0; + + // set_color(_pattern.color[_counter]); + // send_led_rgb(); + // _led_interval = _pattern.duration[_counter]; + // break; + + // default: + // break; + // } + + + // we count only seven states + _counter &= 0xF; + _counter++; + + for (int i = 0; i < 8; i++) { + if (i < _counter) { + _values[i] = 1; + } else { + _values[i] = 0; + } + } + + send_led_values(); + + /* re-queue ourselves to run again later */ + work_queue(LPWORK, &_work, (worker_t)&PCA8574::led_trampoline, this, _led_interval); +} + +// /** +// * Set mode, if mode not changed has no any effect (doesn't reset blinks phase) +// */ +// void +// PCA8574::set_mode(pca8574_mode_t mode) +// { +// if (mode != _mode) { +// _mode = mode; + +// switch (mode) { +// // case PCA8574_MODE_OFF: +// // _should_run = false; +// // send_led_enable(false); +// // break; + +// // case PCA8574_MODE_ON: +// // _brightness = 1.0f; +// // send_led_rgb(); +// // send_led_enable(true); +// // break; + +// // case PCA8574_MODE_BLINK_SLOW: +// // _should_run = true; +// // _counter = 0; +// // _led_interval = 2000; +// // _brightness = 1.0f; +// // send_led_rgb(); +// // break; + +// // case PCA8574_MODE_BLINK_NORMAL: +// // _should_run = true; +// // _counter = 0; +// // _led_interval = 500; +// // _brightness = 1.0f; +// // send_led_rgb(); +// // break; + +// // case PCA8574_MODE_BLINK_FAST: +// // _should_run = true; +// // _counter = 0; +// // _led_interval = 100; +// // _brightness = 1.0f; +// // send_led_rgb(); +// // break; + +// // case PCA8574_MODE_BREATHE: +// // _should_run = true; +// // _counter = 0; +// // _led_interval = 25; +// // send_led_enable(true); +// // break; + +// // case PCA8574_MODE_PATTERN: +// // _should_run = true; +// // _counter = 0; +// // _brightness = 1.0f; +// // send_led_enable(true); +// // break; + +// default: +// warnx("mode unknown"); +// break; +// } + +// /* if it should run now, start the workq */ +// if (_should_run && !_running) { +// _running = true; +// work_queue(LPWORK, &_work, (worker_t)&PCA8574::led_trampoline, this, 1); +// } + +// } +// } + +/** + * Sent ENABLE flag to LED driver + */ +int +PCA8574::send_led_enable(bool enable) +{ + uint8_t msg; + + if (enable) { + /* active low */ + msg = 0x00; + } else { + /* active low, so off */ + msg = 0xFF; + } + + int ret = transfer(&msg, sizeof(msg), nullptr, 0); + + return ret; +} + +/** + * Send RGB PWM settings to LED driver according to current color and brightness + */ +int +PCA8574::send_led_values() +{ + uint8_t msg = 0; + + for (int i = 0; i < 8; i++) { + if (_values[i]) { + msg |= (1 << i); + } + } + + int ret = transfer(&msg, sizeof(msg), nullptr, 0); +} + +// int +// PCA8574::get(bool &on, bool &powersave, uint8_t &r, uint8_t &g, uint8_t &b) +// { +// uint8_t result[2]; +// int ret; + +// ret = transfer(nullptr, 0, &result[0], 2); + +// if (ret == OK) { +// on = result[0] & SETTING_ENABLE; +// powersave = !(result[0] & SETTING_NOT_POWERSAVE); +// /* XXX check, looks wrong */ +// r = (result[0] & 0x0f) << 4; +// g = (result[1] & 0xf0); +// b = (result[1] & 0x0f) << 4; +// } + +// return ret; +// } + +void +pca8574_usage() +{ + warnx("missing command: try 'start', 'test', 'info', 'off', 'stop', 'val 0 100'"); + warnx("options:"); + warnx(" -b i2cbus (%d)", PX4_I2C_BUS_LED); + warnx(" -a addr (0x%x)", ADDR); +} + +int +pca8574_main(int argc, char *argv[]) +{ + int i2cdevice = -1; + int pca8574adr = ADDR; /* 7bit */ + + int ch; + + /* jump over start/off/etc and look at options first */ + while ((ch = getopt(argc, argv, "a:b:")) != EOF) { + switch (ch) { + case 'a': + pca8574adr = strtol(optarg, NULL, 0); + break; + + case 'b': + i2cdevice = strtol(optarg, NULL, 0); + break; + + default: + pca8574_usage(); + exit(0); + } + } + + if (optind >= argc) { + pca8574_usage(); + exit(1); + } + + const char *verb = argv[optind]; + + int fd; + int ret; + + if (!strcmp(verb, "start")) { + if (g_pca8574 != nullptr) + errx(1, "already started"); + + if (i2cdevice == -1) { + // try the external bus first + i2cdevice = PX4_I2C_BUS_EXPANSION; + g_pca8574 = new PCA8574(PX4_I2C_BUS_EXPANSION, pca8574adr); + + if (g_pca8574 != nullptr && OK != g_pca8574->init()) { + delete g_pca8574; + g_pca8574 = nullptr; + } + + if (g_pca8574 == nullptr) { + // fall back to default bus + if (PX4_I2C_BUS_LED == PX4_I2C_BUS_EXPANSION) { + errx(1, "init failed"); + } + i2cdevice = PX4_I2C_BUS_LED; + } + } + + if (g_pca8574 == nullptr) { + g_pca8574 = new PCA8574(i2cdevice, pca8574adr); + + if (g_pca8574 == nullptr) + errx(1, "new failed"); + + if (OK != g_pca8574->init()) { + delete g_pca8574; + g_pca8574 = nullptr; + errx(1, "init failed"); + } + } + + exit(0); + } + + /* need the driver past this point */ + if (g_pca8574 == nullptr) { + warnx("not started"); + pca8574_usage(); + exit(1); + } + + if (!strcmp(verb, "test")) { + fd = open(PCA8574_DEVICE_PATH, 0); + + if (fd == -1) { + errx(1, "Unable to open " PCA8574_DEVICE_PATH); + } + + ret = ioctl(fd, IOX_SET_VALUE, 255); + // ret = ioctl(fd, PCA8574_SET_MODE, (unsigned long)PCA8574_MODE_PATTERN); + + close(fd); + exit(ret); + } + + if (!strcmp(verb, "info")) { + g_pca8574->info(); + exit(0); + } + + if (!strcmp(verb, "off") || !strcmp(verb, "stop")) { + fd = open(PCA8574_DEVICE_PATH, 0); + + if (fd == -1) { + errx(1, "Unable to open " PCA8574_DEVICE_PATH); + } + + ret = ioctl(fd, IOX_SET_MODE, (unsigned long)IOX_MODE_OFF); + close(fd); + exit(ret); + } + + if (!strcmp(verb, "stop")) { + delete g_pca8574; + g_pca8574 = nullptr; + exit(0); + } + + if (!strcmp(verb, "val")) { + if (argc < 4) { + errx(1, "Usage: pca8574 val "); + } + + fd = open(PCA8574_DEVICE_PATH, 0); + + if (fd == -1) { + errx(1, "Unable to open " PCA8574_DEVICE_PATH); + } + + unsigned channel = strtol(argv[2], NULL, 0); + unsigned val = strtol(argv[3], NULL, 0); + ret = ioctl(fd, (IOX_SET_VALUE+channel), val); + ret = ioctl(fd, IOX_ENABLE, 1); + close(fd); + exit(ret); + } + + pca8574_usage(); + exit(0); +} -- cgit v1.2.3 From 17d8e2a1663b36504a432b120c3993e4912842b5 Mon Sep 17 00:00:00 2001 From: Lorenz Meier Date: Thu, 29 May 2014 12:24:50 +0200 Subject: PCA8574 driver: Cleanup, ready for final testing and production --- src/drivers/drv_io_expander.h | 18 ++- src/drivers/pca8574/pca8574.cpp | 341 ++++++++++++++++------------------------ 2 files changed, 148 insertions(+), 211 deletions(-) diff --git a/src/drivers/drv_io_expander.h b/src/drivers/drv_io_expander.h index 2705d6f9e..106354377 100644 --- a/src/drivers/drv_io_expander.h +++ b/src/drivers/drv_io_expander.h @@ -49,17 +49,23 @@ #define _IOXIOCBASE (0x2800) #define _IOXIOC(_n) (_IOC(_IOXIOCBASE, _n)) -/** enable the device */ -#define IOX_ENABLE _IOXIOC(1) +/** set a bitmask (non-blocking) */ +#define IOX_SET_MASK _IOXIOC(1) -/** set constant values */ -#define IOX_SET_VALUE _IOXIOC(2) +/** get a bitmask (blocking) */ +#define IOX_GET_MASK _IOXIOC(2) -/** set constant values */ +/** set device mode (non-blocking) */ #define IOX_SET_MODE _IOXIOC(3) +/** set constant values (non-blocking) */ +#define IOX_SET_VALUE _IOXIOC(4) + +/* ... to IOX_SET_VALUE + 8 */ + /* enum passed to RGBLED_SET_MODE ioctl()*/ enum IOX_MODE { IOX_MODE_OFF, - IOX_MODE_ON + IOX_MODE_ON, + IOX_MODE_TEST_OUT }; diff --git a/src/drivers/pca8574/pca8574.cpp b/src/drivers/pca8574/pca8574.cpp index ce882b636..cd1ffc82c 100644 --- a/src/drivers/pca8574/pca8574.cpp +++ b/src/drivers/pca8574/pca8574.cpp @@ -1,8 +1,6 @@ /**************************************************************************** * - * Copyright (C) 2012, 2013 PX4 Development Team. All rights reserved. - * Author: Julian Oes - * Anton Babushkin + * Copyright (c) 2012-2014 PX4 Development Team. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -36,8 +34,11 @@ /** * @file pca8574.cpp * - * Driver for the onboard RGB LED controller (TCA62724FMG) connected via I2C. + * Driver for an 8 I/O controller (PC8574) connected via I2C. * + * @author Lorenz Meier + * @author Julian Oes + * @author Anton Babushkin */ #include @@ -68,16 +69,7 @@ #define PCA8574_OFFTIME 120 #define PCA8574_DEVICE_PATH "/dev/pca8574" -#define ADDR 0x20 /**< I2C adress of PCA8574 (default, A0-A2 pulled to GND) */ -#define SUB_ADDR_START 0x01 /**< write everything (with auto-increment) */ -#define SUB_ADDR_PWM0 0x81 /**< blue (without auto-increment) */ -#define SUB_ADDR_PWM1 0x82 /**< green (without auto-increment) */ -#define SUB_ADDR_PWM2 0x83 /**< red (without auto-increment) */ -#define SUB_ADDR_SETTINGS 0x84 /**< settings (without auto-increment)*/ - -#define SETTING_NOT_POWERSAVE 0x01 /**< power-save mode not off */ -#define SETTING_ENABLE 0x02 /**< on */ - +#define ADDR 0x20 ///< I2C adress of PCA8574 (default, A0-A2 pulled to GND) class PCA8574 : public device::I2C { @@ -94,20 +86,23 @@ public: private: work_s _work; - uint8_t _values[8]; - float _brightness; + uint8_t _values_out; + uint8_t _values_in; enum IOX_MODE _mode; bool _running; int _led_interval; bool _should_run; + bool _update_out; int _counter; static void led_trampoline(void *arg); void led(); - int send_led_enable(bool enable); + int send_led_enable(uint8_t arg); int send_led_values(); + + int get(uint8_t &vals); }; /* for now, we only support one PCA8574 */ @@ -122,12 +117,13 @@ extern "C" __EXPORT int pca8574_main(int argc, char *argv[]); PCA8574::PCA8574(int bus, int pca8574) : I2C("pca8574", PCA8574_DEVICE_PATH, bus, pca8574, 100000), + _values_out(0), + _values_in(0), _mode(IOX_MODE_OFF), - _values({}), - _brightness(1.0f), _running(false), _led_interval(0), _should_run(false), + _update_out(false), _counter(0) { memset(&_work, 0, sizeof(_work)); @@ -147,10 +143,10 @@ PCA8574::init() return ret; } - /* switch off LED on start */ - send_led_enable(false); + // switch off LED on start (active low on Pixhawk) + send_led_enable(0xFF); - /* kick it in */ + // kick it in _should_run = true; _led_interval = 80; work_queue(LPWORK, &_work, (worker_t)&PCA8574::led_trampoline, this, 1); @@ -179,24 +175,69 @@ PCA8574::ioctl(struct file *filp, int cmd, unsigned long arg) int ret = ENOTTY; switch (cmd) { - case IOX_SET_VALUE ... (IOX_SET_VALUE + 8): - { - /* set the specified color */ - unsigned prev = _values[cmd - IOX_SET_VALUE]; - _values[cmd - IOX_SET_VALUE] = arg; - if (_values[cmd - IOX_SET_VALUE] != prev) { - // XXX will be done with a change flag - send_led_values(); + case IOX_SET_VALUE ...(IOX_SET_VALUE + 8): { + // set the specified on / off state + uint8_t position = (1 << (cmd - IOX_SET_VALUE)); + uint8_t prev = _values_out; + + if (arg) { + _values_out |= position; + + } else { + _values_out &= ~(position); + } + + if (_values_out != prev) { + send_led_values(); + } + + return OK; } - return OK; - } - case IOX_ENABLE: + case IOX_SET_MASK: send_led_enable(arg); return OK; + case IOX_GET_MASK: { + uint8_t val; + ret = get(val); + + if (ret == OK) { + return val; + + } else { + return -1; + } + } + + case IOX_SET_MODE: + + if (_mode != (IOX_MODE)arg) { + + switch ((IOX_MODE)arg) { + case IOX_MODE_OFF: + _values_out = 0xFF; + break; + + case IOX_MODE_ON: + _values_out = 0; + break; + + case IOX_MODE_TEST_OUT: + break; + + default: + return -1; + } + + _mode = (IOX_MODE)arg; + send_led_values(); + } + + return OK; + default: - /* see if the parent class can make any use of it */ + // see if the parent class can make any use of it ret = CDev::ioctl(filp, cmd, arg); break; } @@ -224,204 +265,93 @@ PCA8574::led() return; } - // switch (_mode) { - // case PCA8574_MODE_BLINK_SLOW: - // case PCA8574_MODE_BLINK_NORMAL: - // case PCA8574_MODE_BLINK_FAST: - // if (_counter >= 2) - // _counter = 0; - - // send_led_enable(_counter == 0); + if (_mode == IOX_MODE_TEST_OUT) { - // break; + // we count only seven states + _counter &= 0xF; + _counter++; - // case PCA8574_MODE_BREATHE: + for (int i = 0; i < 8; i++) { + if (i < _counter) { + _values_out |= (1 << i); - // if (_counter >= 62) - // _counter = 0; - - // int n; - - // if (_counter < 32) { - // n = _counter; - - // } else { - // n = 62 - _counter; - // } - - // _brightness = n * n / (31.0f * 31.0f); - // send_led_rgb(); - // break; - - // case PCA8574_MODE_PATTERN: - - // /* don't run out of the pattern array and stop if the next frame is 0 */ - // if (_counter >= PCA8574_PATTERN_LENGTH || _pattern.duration[_counter] <= 0) - // _counter = 0; - - // set_color(_pattern.color[_counter]); - // send_led_rgb(); - // _led_interval = _pattern.duration[_counter]; - // break; + } else { + _values_out &= ~(1 << i); + } + } - // default: - // break; - // } + _update_out = true; + } + if (_update_out) { + uint8_t msg = _values_out; - // we count only seven states - _counter &= 0xF; - _counter++; + int ret = transfer(&msg, sizeof(msg), nullptr, 0); - for (int i = 0; i < 8; i++) { - if (i < _counter) { - _values[i] = 1; - } else { - _values[i] = 0; + if (!ret) { + _update_out = false; } } - send_led_values(); + // check if any activity remains, else stp + if (!_update_out) { + _running = false; + return; + } - /* re-queue ourselves to run again later */ + // re-queue ourselves to run again later work_queue(LPWORK, &_work, (worker_t)&PCA8574::led_trampoline, this, _led_interval); } -// /** -// * Set mode, if mode not changed has no any effect (doesn't reset blinks phase) -// */ -// void -// PCA8574::set_mode(pca8574_mode_t mode) -// { -// if (mode != _mode) { -// _mode = mode; - -// switch (mode) { -// // case PCA8574_MODE_OFF: -// // _should_run = false; -// // send_led_enable(false); -// // break; - -// // case PCA8574_MODE_ON: -// // _brightness = 1.0f; -// // send_led_rgb(); -// // send_led_enable(true); -// // break; - -// // case PCA8574_MODE_BLINK_SLOW: -// // _should_run = true; -// // _counter = 0; -// // _led_interval = 2000; -// // _brightness = 1.0f; -// // send_led_rgb(); -// // break; - -// // case PCA8574_MODE_BLINK_NORMAL: -// // _should_run = true; -// // _counter = 0; -// // _led_interval = 500; -// // _brightness = 1.0f; -// // send_led_rgb(); -// // break; - -// // case PCA8574_MODE_BLINK_FAST: -// // _should_run = true; -// // _counter = 0; -// // _led_interval = 100; -// // _brightness = 1.0f; -// // send_led_rgb(); -// // break; - -// // case PCA8574_MODE_BREATHE: -// // _should_run = true; -// // _counter = 0; -// // _led_interval = 25; -// // send_led_enable(true); -// // break; - -// // case PCA8574_MODE_PATTERN: -// // _should_run = true; -// // _counter = 0; -// // _brightness = 1.0f; -// // send_led_enable(true); -// // break; - -// default: -// warnx("mode unknown"); -// break; -// } - -// /* if it should run now, start the workq */ -// if (_should_run && !_running) { -// _running = true; -// work_queue(LPWORK, &_work, (worker_t)&PCA8574::led_trampoline, this, 1); -// } - -// } -// } - /** * Sent ENABLE flag to LED driver */ int -PCA8574::send_led_enable(bool enable) +PCA8574::send_led_enable(uint8_t arg) { - uint8_t msg; - - if (enable) { - /* active low */ - msg = 0x00; - } else { - /* active low, so off */ - msg = 0xFF; - } - int ret = transfer(&msg, sizeof(msg), nullptr, 0); + int ret = transfer(&arg, sizeof(arg), nullptr, 0); return ret; } /** - * Send RGB PWM settings to LED driver according to current color and brightness + * Send 8 outputs */ int PCA8574::send_led_values() { - uint8_t msg = 0; + _update_out = true; + _should_run = true; - for (int i = 0; i < 8; i++) { - if (_values[i]) { - msg |= (1 << i); - } + // if not active, kick it + if (!_running) { + work_queue(LPWORK, &_work, (worker_t)&PCA8574::led_trampoline, this, 1); } - int ret = transfer(&msg, sizeof(msg), nullptr, 0); + return 0; } -// int -// PCA8574::get(bool &on, bool &powersave, uint8_t &r, uint8_t &g, uint8_t &b) -// { -// uint8_t result[2]; -// int ret; +int +PCA8574::get(uint8_t &vals) +{ + uint8_t result; + int ret; -// ret = transfer(nullptr, 0, &result[0], 2); + ret = transfer(nullptr, 0, &result, 1); -// if (ret == OK) { -// on = result[0] & SETTING_ENABLE; -// powersave = !(result[0] & SETTING_NOT_POWERSAVE); -// /* XXX check, looks wrong */ -// r = (result[0] & 0x0f) << 4; -// g = (result[1] & 0xf0); -// b = (result[1] & 0x0f) << 4; -// } + if (ret == OK) { + _values_in = result; + vals = result; + } -// return ret; -// } + return ret; +} void pca8574_usage() { - warnx("missing command: try 'start', 'test', 'info', 'off', 'stop', 'val 0 100'"); + warnx("missing command: try 'start', 'test', 'info', 'off', 'stop', 'val 0 1'"); warnx("options:"); warnx(" -b i2cbus (%d)", PX4_I2C_BUS_LED); warnx(" -a addr (0x%x)", ADDR); @@ -431,11 +361,11 @@ int pca8574_main(int argc, char *argv[]) { int i2cdevice = -1; - int pca8574adr = ADDR; /* 7bit */ + int pca8574adr = ADDR; // 7bit int ch; - /* jump over start/off/etc and look at options first */ + // jump over start/off/etc and look at options first while ((ch = getopt(argc, argv, "a:b:")) != EOF) { switch (ch) { case 'a': @@ -452,10 +382,10 @@ pca8574_main(int argc, char *argv[]) } } - if (optind >= argc) { - pca8574_usage(); - exit(1); - } + if (optind >= argc) { + pca8574_usage(); + exit(1); + } const char *verb = argv[optind]; @@ -463,8 +393,9 @@ pca8574_main(int argc, char *argv[]) int ret; if (!strcmp(verb, "start")) { - if (g_pca8574 != nullptr) + if (g_pca8574 != nullptr) { errx(1, "already started"); + } if (i2cdevice == -1) { // try the external bus first @@ -481,6 +412,7 @@ pca8574_main(int argc, char *argv[]) if (PX4_I2C_BUS_LED == PX4_I2C_BUS_EXPANSION) { errx(1, "init failed"); } + i2cdevice = PX4_I2C_BUS_LED; } } @@ -488,8 +420,9 @@ pca8574_main(int argc, char *argv[]) if (g_pca8574 == nullptr) { g_pca8574 = new PCA8574(i2cdevice, pca8574adr); - if (g_pca8574 == nullptr) + if (g_pca8574 == nullptr) { errx(1, "new failed"); + } if (OK != g_pca8574->init()) { delete g_pca8574; @@ -501,7 +434,7 @@ pca8574_main(int argc, char *argv[]) exit(0); } - /* need the driver past this point */ + // need the driver past this point if (g_pca8574 == nullptr) { warnx("not started"); pca8574_usage(); @@ -515,8 +448,7 @@ pca8574_main(int argc, char *argv[]) errx(1, "Unable to open " PCA8574_DEVICE_PATH); } - ret = ioctl(fd, IOX_SET_VALUE, 255); - // ret = ioctl(fd, PCA8574_SET_MODE, (unsigned long)PCA8574_MODE_PATTERN); + ret = ioctl(fd, IOX_SET_MODE, (unsigned long)IOX_MODE_TEST_OUT); close(fd); exit(ret); @@ -547,7 +479,7 @@ pca8574_main(int argc, char *argv[]) if (!strcmp(verb, "val")) { if (argc < 4) { - errx(1, "Usage: pca8574 val "); + errx(1, "Usage: pca8574 val <0 or 1>"); } fd = open(PCA8574_DEVICE_PATH, 0); @@ -558,8 +490,7 @@ pca8574_main(int argc, char *argv[]) unsigned channel = strtol(argv[2], NULL, 0); unsigned val = strtol(argv[3], NULL, 0); - ret = ioctl(fd, (IOX_SET_VALUE+channel), val); - ret = ioctl(fd, IOX_ENABLE, 1); + ret = ioctl(fd, (IOX_SET_VALUE + channel), val); close(fd); exit(ret); } -- cgit v1.2.3 From 0c2e70d30f3e1d927a22fdd066a4a14ecc698de1 Mon Sep 17 00:00:00 2001 From: Lorenz Meier Date: Thu, 29 May 2014 13:52:29 +0200 Subject: Enable the driver as default for boards having it --- ROMFS/px4fmu_common/init.d/rcS | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/ROMFS/px4fmu_common/init.d/rcS b/ROMFS/px4fmu_common/init.d/rcS index 8c413e087..87ec4293e 100644 --- a/ROMFS/px4fmu_common/init.d/rcS +++ b/ROMFS/px4fmu_common/init.d/rcS @@ -93,14 +93,18 @@ then # if rgbled start then - echo "[init] Using external RGB Led" + echo "[init] RGB Led" else if blinkm start then - echo "[init] Using blinkm" + echo "[init] BlinkM" blinkm systemstate fi fi + + if pca8574 start + then + fi # # Set default values -- cgit v1.2.3 From eb02e74d30a215ff415983913bf569a23e916a2b Mon Sep 17 00:00:00 2001 From: Lorenz Meier Date: Thu, 29 May 2014 13:52:47 +0200 Subject: PCA cleanup: Full interfaces ready for AP use --- src/drivers/pca8574/pca8574.cpp | 108 ++++++++++++++++++++++++++++++---------- 1 file changed, 81 insertions(+), 27 deletions(-) diff --git a/src/drivers/pca8574/pca8574.cpp b/src/drivers/pca8574/pca8574.cpp index cd1ffc82c..904ce18e8 100644 --- a/src/drivers/pca8574/pca8574.cpp +++ b/src/drivers/pca8574/pca8574.cpp @@ -82,12 +82,15 @@ public: virtual int probe(); virtual int info(); virtual int ioctl(struct file *filp, int cmd, unsigned long arg); + bool is_running() { return _running; } private: work_s _work; uint8_t _values_out; uint8_t _values_in; + uint8_t _blinking; + uint8_t _blink_phase; enum IOX_MODE _mode; bool _running; @@ -119,9 +122,11 @@ PCA8574::PCA8574(int bus, int pca8574) : I2C("pca8574", PCA8574_DEVICE_PATH, bus, pca8574, 100000), _values_out(0), _values_in(0), + _blinking(0), + _blink_phase(0), _mode(IOX_MODE_OFF), _running(false), - _led_interval(0), + _led_interval(80), _should_run(false), _update_out(false), _counter(0) @@ -143,22 +148,14 @@ PCA8574::init() return ret; } - // switch off LED on start (active low on Pixhawk) - send_led_enable(0xFF); - - // kick it in - _should_run = true; - _led_interval = 80; - work_queue(LPWORK, &_work, (worker_t)&PCA8574::led_trampoline, this, 1); - return OK; } int PCA8574::probe() { - - return send_led_enable(false); + uint8_t val; + return get(val); } int @@ -188,6 +185,9 @@ PCA8574::ioctl(struct file *filp, int cmd, unsigned long arg) } if (_values_out != prev) { + if (_values_out) { + _mode = IOX_MODE_ON; + } send_led_values(); } @@ -260,11 +260,6 @@ PCA8574::led_trampoline(void *arg) void PCA8574::led() { - if (!_should_run) { - _running = false; - return; - } - if (_mode == IOX_MODE_TEST_OUT) { // we count only seven states @@ -281,10 +276,37 @@ PCA8574::led() } _update_out = true; + _should_run = true; + } else if (_mode == IOX_MODE_OFF) { + _update_out = true; + _should_run = false; + } else { + + // Any of the normal modes + if (_blinking > 0) { + /* we need to be running to blink */ + _should_run = true; + } else { + _should_run = false; + } } if (_update_out) { - uint8_t msg = _values_out; + uint8_t msg; + + if (_blinking) { + msg = (_values_out & _blinking & _blink_phase); + + // wipe out all positions that are marked as blinking + msg &= ~(_blinking); + + // fill blink positions + msg |= ((_blink_phase) ? _blinking : 0); + + _blink_phase = !_blink_phase; + } else { + msg = _values_out; + } int ret = transfer(&msg, sizeof(msg), nullptr, 0); @@ -294,12 +316,13 @@ PCA8574::led() } // check if any activity remains, else stp - if (!_update_out) { + if (!_should_run) { _running = false; return; } // re-queue ourselves to run again later + _running = true; work_queue(LPWORK, &_work, (worker_t)&PCA8574::led_trampoline, this, _led_interval); } @@ -322,10 +345,10 @@ int PCA8574::send_led_values() { _update_out = true; - _should_run = true; // if not active, kick it if (!_running) { + _running = true; work_queue(LPWORK, &_work, (worker_t)&PCA8574::led_trampoline, this, 1); } @@ -436,8 +459,7 @@ pca8574_main(int argc, char *argv[]) // need the driver past this point if (g_pca8574 == nullptr) { - warnx("not started"); - pca8574_usage(); + warnx("not started, run pca8574 start"); exit(1); } @@ -459,10 +481,10 @@ pca8574_main(int argc, char *argv[]) exit(0); } - if (!strcmp(verb, "off") || !strcmp(verb, "stop")) { + if (!strcmp(verb, "off")) { fd = open(PCA8574_DEVICE_PATH, 0); - if (fd == -1) { + if (fd < 0) { errx(1, "Unable to open " PCA8574_DEVICE_PATH); } @@ -472,9 +494,36 @@ pca8574_main(int argc, char *argv[]) } if (!strcmp(verb, "stop")) { - delete g_pca8574; - g_pca8574 = nullptr; - exit(0); + fd = open(PCA8574_DEVICE_PATH, 0); + + if (fd == -1) { + errx(1, "Unable to open " PCA8574_DEVICE_PATH); + } + + ret = ioctl(fd, IOX_SET_MODE, (unsigned long)IOX_MODE_OFF); + close(fd); + + // wait until we're not running any more + for (unsigned i = 0; i < 15; i++) { + if (!g_pca8574->is_running()) { + break; + } + + usleep(50000); + printf("."); + fflush(stdout); + } + printf("\n"); + fflush(stdout); + + if (!g_pca8574->is_running()) { + delete g_pca8574; + g_pca8574 = nullptr; + exit(0); + } else { + warnx("stop failed."); + exit(1); + } } if (!strcmp(verb, "val")) { @@ -490,7 +539,12 @@ pca8574_main(int argc, char *argv[]) unsigned channel = strtol(argv[2], NULL, 0); unsigned val = strtol(argv[3], NULL, 0); - ret = ioctl(fd, (IOX_SET_VALUE + channel), val); + + if (channel < 8) { + ret = ioctl(fd, (IOX_SET_VALUE + channel), val); + } else { + ret = -1; + } close(fd); exit(ret); } -- cgit v1.2.3