diff --git a/examples/c/CMakeLists.txt b/examples/c/CMakeLists.txt index eecfa318..756de1b4 100644 --- a/examples/c/CMakeLists.txt +++ b/examples/c/CMakeLists.txt @@ -137,6 +137,7 @@ add_example (guvas12d) add_example (otp538u) add_example (button) add_example (button_intr) +add_example (my9221) # Custom examples add_custom_example (nmea_gps_i2c-example-c nmea_gps_i2c.c nmea_gps) diff --git a/examples/c/my9221.c b/examples/c/my9221.c new file mode 100644 index 00000000..0c461b13 --- /dev/null +++ b/examples/c/my9221.c @@ -0,0 +1,89 @@ +/* + * Author: Jon Trulson + * Copyright (c) 2016 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include +#include +#include + +#include +#include + +int shouldRun = true; + +void sig_handler(int signo) +{ + if (signo == SIGINT) + shouldRun = false; +} + + +int main () +{ + signal(SIGINT, sig_handler); + +//! [Interesting] + + // Instantiate a GroveLEDBar, we use D8 for the data, and D9 for the + // clock. We only use a single instance. + my9221_context leds = my9221_init(8, 9, 1); + + if (!leds) + { + printf("my9221_init() failed\n"); + return 1; + } + + while (shouldRun) + { + // count up + printf("Counting up: "); + for (int i=0; i=0; i--) + { + printf("%d ", i); + my9221_clear_all(leds); + my9221_set_led(leds, i, true); + upm_delay_ms(100); + } + printf("\n"); + upm_delay_ms(100); + } + + printf("Exiting...\n"); + + my9221_close(leds); +//! [Interesting] + return 0; +} diff --git a/src/my9221/CMakeLists.txt b/src/my9221/CMakeLists.txt index caa50937..8ecb01f1 100644 --- a/src/my9221/CMakeLists.txt +++ b/src/my9221/CMakeLists.txt @@ -1,5 +1,8 @@ -set (libname "my9221") -set (libdescription "12-channel (RBG x 4) constant current LED driver with grayscale") -set (module_src ${libname}.cxx groveledbar.cxx grovecircularled.cxx) -set (module_hpp ${libname}.hpp groveledbar.hpp grovecircularled.hpp) -upm_module_init() +upm_mixed_module_init (NAME my9221 + DESCRIPTION "12-channel constant current LED driver with grayscale" + C_HDR my9221.h + C_SRC my9221.c + CPP_HDR my9221.hpp groveledbar.hpp grovecircularled.hpp + CPP_SRC my9221.cxx groveledbar.cxx grovecircularled.cxx + CPP_WRAPS_C + REQUIRES mraa) diff --git a/src/my9221/grovecircularled.cxx b/src/my9221/grovecircularled.cxx index 43a8fcee..87c245c0 100644 --- a/src/my9221/grovecircularled.cxx +++ b/src/my9221/grovecircularled.cxx @@ -45,11 +45,11 @@ using namespace upm; using namespace std; GroveCircularLED::GroveCircularLED (uint8_t dataPin, uint8_t clockPin) - : MY9221(dataPin, clockPin, 2) + : MY9221(dataPin, clockPin, 2) { - // auto refresh by default - setAutoRefresh(true); - clearAll(); + // auto refresh by default + setAutoRefresh(true); + clearAll(); } GroveCircularLED::~GroveCircularLED() @@ -58,37 +58,44 @@ GroveCircularLED::~GroveCircularLED() void GroveCircularLED::setSpinner(uint8_t position) { - if (position > 23) - position = 23; + if (position > 23) + position = 23; - for (uint8_t i=0; i<(LEDS_PER_INSTANCE * m_instances); i++) - m_bitStates[i] = (i == position) ? m_highIntensity : m_lowIntensity; + unsigned int ledsPerInstance = m_my9221->max_leds_per_instance; - if (m_autoRefresh) - refresh(); + for (uint8_t i=0; i<(ledsPerInstance * m_my9221->instances); i++) + m_my9221->bitStates[i] = + (i == position) ? m_my9221->highIntensity : m_my9221->lowIntensity; - return; + if (m_my9221->autoRefresh) + refresh(); + + return; } void GroveCircularLED::setLevel(uint8_t level, bool direction) { - if (level > 23) - level = 23; + if (level > 23) + level = 23; - if (!direction) + unsigned int ledsPerInstance = m_my9221->max_leds_per_instance; + + if (!direction) { - for (int i=0; i < static_cast(LEDS_PER_INSTANCE * m_instances); i++) - m_bitStates[i] = (i < level) ? m_highIntensity : m_lowIntensity; + for (unsigned int i=0; i < (ledsPerInstance * m_my9221->instances); i++) + m_my9221->bitStates[i] = + (i < level) ? m_my9221->highIntensity : m_my9221->lowIntensity; } - else + else { - for (int i=0; i< static_cast(LEDS_PER_INSTANCE * m_instances); i++) - m_bitStates[i] = (((LEDS_PER_INSTANCE * m_instances) - i) <= level) - ? m_highIntensity : m_lowIntensity; + for (unsigned int i=0; i<(ledsPerInstance * m_my9221->instances); i++) + m_my9221->bitStates[i] = + (((ledsPerInstance * m_my9221->instances) - i) <= level) + ? m_my9221->highIntensity : m_my9221->lowIntensity; } - if (m_autoRefresh) - refresh(); + if (m_my9221->autoRefresh) + refresh(); - return; + return; } diff --git a/src/my9221/groveledbar.cxx b/src/my9221/groveledbar.cxx index 4b98af7e..1ea29f79 100644 --- a/src/my9221/groveledbar.cxx +++ b/src/my9221/groveledbar.cxx @@ -45,44 +45,47 @@ using namespace upm; using namespace std; GroveLEDBar::GroveLEDBar (uint8_t dataPin, uint8_t clockPin, int instances) - : MY9221(dataPin, clockPin, instances) + : MY9221(dataPin, clockPin, instances) { - // auto refresh by default - setAutoRefresh(true); - clearAll(); + // auto refresh by default + setAutoRefresh(true); + clearAll(); } GroveLEDBar::~GroveLEDBar() { } -void GroveLEDBar::setBarLevel(uint8_t level, bool greenToRed, int barNumber) +void GroveLEDBar::setBarLevel(uint8_t level, bool greenToRed, + unsigned int barNumber) { - if (level > 10) - level = 10; + // here we manipulate the my9221 context struct directly + if (level > 10) + level = 10; - if (barNumber >= static_cast(m_instances)) - barNumber = m_instances - 1; + if (barNumber >= m_my9221->instances) + barNumber = m_my9221->instances - 1; - int start = barNumber * LEDS_PER_INSTANCE; - int end = start + LEDS_PER_INSTANCE; + unsigned int ledsPerInstance = m_my9221->max_leds_per_instance; + unsigned int start = barNumber * ledsPerInstance; + unsigned int end = start + ledsPerInstance; - if (!greenToRed) + if (!greenToRed) { - for (int i=start; ibitStates[i] = (i < (level + start)) ? + m_my9221->highIntensity : m_my9221->lowIntensity; } - else + else { - for (int i=start; ibitStates[i] = ( ((start + ledsPerInstance) - i) <= + (level + 2 + start)) ? + m_my9221->highIntensity : m_my9221->lowIntensity; } - if (m_autoRefresh) - refresh(); + if (m_my9221->autoRefresh) + refresh(); - return; + return; } diff --git a/src/my9221/groveledbar.hpp b/src/my9221/groveledbar.hpp index 96533e17..719174f2 100644 --- a/src/my9221/groveledbar.hpp +++ b/src/my9221/groveledbar.hpp @@ -90,7 +90,8 @@ namespace upm { * together, this argument selects a specific bar starting at 0. * The default is 0. */ - void setBarLevel(uint8_t level, bool greenToRed=true, int barNumber=0); + void setBarLevel(uint8_t level, bool greenToRed=true, + unsigned int barNumber=0); protected: private: diff --git a/src/my9221/my9221.c b/src/my9221/my9221.c new file mode 100644 index 00000000..1ac9ae09 --- /dev/null +++ b/src/my9221/my9221.c @@ -0,0 +1,287 @@ +/* + * Author: Jon Trulson + * Copyright (c) 2016 Intel Corporation. + * + * These modules were rewritten, based on original work by: + * + * (original my9221/groveledbar driver) + * Author: Yevgeniy Kiveisha + * Copyright (c) 2014 Intel Corporation. + * + * (grovecircularled driver) + * Author: Jun Kato and Yevgeniy Kiveisha + * Contributions: Jon Trulson + * Copyright (c) 2014 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include +#include + +#include +#include + +#include "my9221.h" + +// 12 LED channels per chip (instance) +#define LEDS_PER_INSTANCE (12) + +// forward declarations +static void my9221_lock_data(const my9221_context dev); +static void my9221_send_16bit_block(const my9221_context dev, uint16_t data); + +my9221_context my9221_init(uint8_t dataPin, uint8_t clockPin, + int instances) +{ + if (instances < 1) + instances = 1; + + my9221_context dev = + (my9221_context)malloc(sizeof(struct _my9221_context)); + + if (!dev) + return NULL; + + memset((void *)dev, 0, sizeof(struct _my9221_context)); + dev->gpioClk = NULL; + dev->gpioData = NULL; + + // make sure MRAA is initialized + mraa_result_t mraa_rv; + if ((mraa_rv = mraa_init()) != MRAA_SUCCESS) + { + printf("%s: mraa_init() failed (%d).\n", __FUNCTION__, mraa_rv); + my9221_close(dev); + return NULL; + } + + // MRAA contexts... + if ( !(dev->gpioClk = mraa_gpio_init(clockPin)) ) + { + printf("%s: mraa_gpio_init(clk) failed\n", + __FUNCTION__); + my9221_close(dev); + return NULL; + } + + mraa_gpio_dir(dev->gpioClk, MRAA_GPIO_OUT); + + + if ( !(dev->gpioData = mraa_gpio_init(dataPin)) ) + { + printf("%s: mraa_gpio_init(data) failed\n", + __FUNCTION__); + my9221_close(dev); + return NULL; + } + + mraa_gpio_dir(dev->gpioData, MRAA_GPIO_OUT); + +#if defined(UPM_PLATFORM_LINUX) + // we warn if these fail, since it may not be possible to handle + // more than one instance + + if (mraa_gpio_use_mmaped(dev->gpioClk, 1)) + printf("%s: Warning: mmap of Clk pin failed, correct operation " + "may be affected.\n", __FUNCTION__); + + if (mraa_gpio_use_mmaped(dev->gpioData, 1)) + printf("%s: Warning: mmap of Data pin failed, correct operation " + "may be affected.\n", __FUNCTION__); +#endif // UPM_PLATFORM_LINUX + + my9221_set_low_intensity_value(dev, 0x00); // full off + my9221_set_high_intensity_value(dev, 0xff); // full bright + + dev->commandWord = 0x0000; // all defaults + dev->instances = instances; + dev->max_leds_per_instance = LEDS_PER_INSTANCE; + + if ( !(dev->bitStates = + malloc(sizeof(uint16_t) * instances * LEDS_PER_INSTANCE) ) ) + { + printf("%s: bit state allocation failed\n", + __FUNCTION__); + my9221_close(dev); + return NULL; + } + + my9221_set_auto_refresh(dev, true); + my9221_clear_all(dev); + + + dev->maxLEDS = dev->instances * LEDS_PER_INSTANCE; + dev->initialized = true; + return dev; +} + +void my9221_close(my9221_context dev) +{ + assert(dev != NULL); + + if (dev->initialized) + { + my9221_clear_all(dev); + + if (!dev->autoRefresh) + my9221_refresh(dev); + } + + if (dev->bitStates) + free(dev->bitStates); + + if (dev->gpioClk) + mraa_gpio_close(dev->gpioClk); + if (dev->gpioData) + mraa_gpio_close(dev->gpioData); + + free(dev); +} + +void my9221_set_led(const my9221_context dev, int led, bool on) +{ + assert(dev != NULL); + + int maxLed = dev->maxLEDS - 1; + + if (led > maxLed) + led = maxLed; + if (led < 0) + led = 0; + + dev->bitStates[led] = (on) ? dev->highIntensity : dev->lowIntensity; + + if (dev->autoRefresh) + my9221_refresh(dev); +} + +void my9221_set_low_intensity_value(const my9221_context dev, + int intensity) +{ + assert(dev != NULL); + + dev->lowIntensity = (intensity & 0xff); +} + +void my9221_set_high_intensity_value(const my9221_context dev, + int intensity) +{ + assert(dev != NULL); + + dev->highIntensity = (intensity & 0xff); +} + +void my9221_set_all(const my9221_context dev) +{ + assert(dev != NULL); + + for (unsigned int i=0; imaxLEDS; i++) + dev->bitStates[i] = dev->highIntensity; + + if (dev->autoRefresh) + my9221_refresh(dev); +} + +void my9221_clear_all(const my9221_context dev) +{ + assert(dev != NULL); + + for (unsigned int i=0; imaxLEDS; i++) + dev->bitStates[i] = dev->lowIntensity; + + if (dev->autoRefresh) + my9221_refresh(dev); +} + +void my9221_refresh(const my9221_context dev) +{ + assert(dev != NULL); + + for (unsigned int i=0; imaxLEDS; i++) + { + if (i % 12 == 0) + { + my9221_send_16bit_block(dev, dev->commandWord); + } + my9221_send_16bit_block(dev, dev->bitStates[i]); + } + + my9221_lock_data(dev); +} + +void my9221_set_auto_refresh(const my9221_context dev, bool enable) +{ + assert(dev != NULL); + + dev->autoRefresh = enable; +} + +int my9221_get_max_leds(const my9221_context dev) +{ + assert(dev != NULL); + + return dev->maxLEDS; +} + +static void my9221_lock_data(const my9221_context dev) +{ + assert(dev != NULL); + + mraa_gpio_write(dev->gpioData, 0); + upm_delay_us(220); + + for (int idx = 0; idx < 4; idx++) + { + mraa_gpio_write(dev->gpioData, 1); + mraa_gpio_write(dev->gpioData, 0); + } + + // in reality, we only need > 200ns + (dev->instances * 10ns), so the + // following should be good for up to dev->instances < 80), if the + // datasheet is to be believed :) + upm_delay_us(1); + + return; +} + +static void my9221_send_16bit_block(const my9221_context dev, uint16_t data) +{ + assert(dev != NULL); + + for (uint8_t bit_idx = 0; bit_idx < 16; bit_idx++) + { + uint32_t state = (data & 0x8000) ? 1 : 0; + mraa_gpio_write(dev->gpioData, state); + + state = mraa_gpio_read(dev->gpioClk); + + if (state) + state = 0; + else + state = 1; + + mraa_gpio_write(dev->gpioClk, state); + + data <<= 1; + } + + return; +} diff --git a/src/my9221/my9221.cxx b/src/my9221/my9221.cxx index c5073cf9..736c47a1 100644 --- a/src/my9221/my9221.cxx +++ b/src/my9221/my9221.cxx @@ -44,148 +44,47 @@ using namespace upm; using namespace std; -MY9221::MY9221 (uint8_t dataPin, uint8_t clockPin, int instances) - : m_gpioData(dataPin), m_gpioClk(clockPin), m_bitStates(0) +MY9221::MY9221 (uint8_t dataPin, uint8_t clockPin, int instances) : + m_my9221(my9221_init(dataPin, clockPin, instances)) { - if (instances < 1) - { - throw std::out_of_range(std::string(__FUNCTION__) + - ": instances must be at least 1"); - } + if (!m_my9221) + throw std::runtime_error(std::string(__FUNCTION__) + + ": my9221_init() failed"); - // set directions - m_gpioClk.dir(mraa::DIR_OUT); - m_gpioData.dir(mraa::DIR_OUT); - - // we warn if these fail, since it may not be possible to handle - // more than one instance - - if (m_gpioClk.useMmap(true) != mraa::SUCCESS) - cerr << __FUNCTION__ - << ": Warning: mmap of Clk pin failed, correct operation " - << "may be affected." - << endl; - - if (m_gpioData.useMmap(true) != mraa::SUCCESS) - cerr << __FUNCTION__ - << ": Warning: mmap of Data pin failed, correct operation " - << "may be affected." - << endl; - - setLowIntensityValue(0x00); // full off - setHighIntensityValue(0xff); // full brightness - - m_commandWord = 0x0000; // all defaults - m_instances = instances; - - m_bitStates = new uint16_t[instances * LEDS_PER_INSTANCE]; - - setAutoRefresh(true); - clearAll(); } MY9221::~MY9221() { - clearAll(); - - if (!m_autoRefresh) - refresh(); - - delete m_bitStates; + my9221_close(m_my9221); } void MY9221::setLED(int led, bool on) { - int maxLed = (LEDS_PER_INSTANCE * m_instances) - 1; - - if (led > maxLed) - led = maxLed; - if (led < 0) - led = 0; - - m_bitStates[led] = (on) ? m_highIntensity : m_lowIntensity; - - if (m_autoRefresh) - refresh(); + my9221_set_led(m_my9221, led, on); } void MY9221::setLowIntensityValue(int intensity) { - m_lowIntensity = (intensity & 0xff); + my9221_set_low_intensity_value(m_my9221, intensity); } void MY9221::setHighIntensityValue(int intensity) { - m_highIntensity = (intensity & 0xff); + my9221_set_high_intensity_value(m_my9221, intensity); } void MY9221::setAll() { - for (int i=0; i< static_cast(m_instances * LEDS_PER_INSTANCE); i++) - m_bitStates[i] = m_highIntensity; - - if (m_autoRefresh) - refresh(); + my9221_set_all(m_my9221); } void MY9221::clearAll() { - for (int i=0; i< static_cast(m_instances * LEDS_PER_INSTANCE); i++) - m_bitStates[i] = m_lowIntensity; - - if (m_autoRefresh) - refresh(); + my9221_clear_all(m_my9221); } void MY9221::refresh() { - for (int i=0; i< static_cast(m_instances * LEDS_PER_INSTANCE); i++) - { - if (i % 12 == 0) - { - send16bitBlock(m_commandWord); - } - send16bitBlock(m_bitStates[i]); - } - - lockData(); + my9221_refresh(m_my9221); } -void MY9221::lockData() -{ - m_gpioData.write(0); - usleep(220); - - for(int idx = 0; idx < 4; idx++) - { - m_gpioData.write(1); - m_gpioData.write(0); - } - - // in reality, we only need > 200ns + (m_instances * 10ns), so the - // following should be good for up to m_instances < 80), if the - // datasheet is to be believed :) - usleep(1); - - return; -} - -void MY9221::send16bitBlock(uint16_t data) -{ - for (uint8_t bit_idx = 0; bit_idx < 16; bit_idx++) - { - uint32_t state = (data & 0x8000) ? 1 : 0; - m_gpioData.write(state); - state = m_gpioClk.read(); - - if (state) - state = 0; - else - state = 1; - - m_gpioClk.write(state); - - data <<= 1; - } - return; -} diff --git a/src/my9221/my9221.h b/src/my9221/my9221.h new file mode 100644 index 00000000..1f7bfdfc --- /dev/null +++ b/src/my9221/my9221.h @@ -0,0 +1,183 @@ +/* + * Author: Jon Trulson + * Copyright (c) 2016 Intel Corporation. + * + * These modules were rewritten, based on original work by: + * + * (original my9221/groveledbar) + * Author: Yevgeniy Kiveisha + * Copyright (c) 2014 Intel Corporation. + * + * (grovecircularled) + * Author: Jun Kato and Yevgeniy Kiveisha + * Contributions: Jon Trulson + * Copyright (c) 2014 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#pragma once + +#include +#include +#include + +#include + +#ifdef __cplusplus +extern "C" { +#endif + + /** + * @file my9221.h + * @library my9221 + * @brief C API for the my9221 driver + * + * @include my9221.c + */ + + /** + * Device context + */ + typedef struct _my9221_context { + mraa_gpio_context gpioClk; + mraa_gpio_context gpioData; + + bool autoRefresh; + // we're only doing 8-bit greyscale, so the high order bits are + // always 0 + uint16_t lowIntensity; + uint16_t highIntensity; + + unsigned int instances; + unsigned int maxLEDS; + + // an array of uint16_t's representing our bit states (on/off) + // intensities. Only the low 8 bits are used, but in the future + // 16bit support can work here as well. + uint16_t *bitStates; + + uint16_t commandWord; + + bool initialized; + + // A helper for the users of this driver + unsigned int max_leds_per_instance; + } *my9221_context; + + + /** + * Instantiates an MY9221 object + * + * @param dataPin Data pin + * @param clockPin Clock pin + * @param instances Number of daisy-chained my9221s, must be at + * least 1 + * @return Device context + */ + my9221_context my9221_init(uint8_t dataPin, uint8_t clockPin, + int instances); + + /** + * MY9221 close + * + * @param dev Device context + */ + void my9221_close(my9221_context dev); + + /** + * Enable or disable auto refresh. When auto refresh is enabled, + * update the LED display as soon as the internal state changes. + * When false, the display(s) will not be updated until the + * refresh() method is called. + * + * @param dev Device context + * @param enable true to enable auto refresh, false otherwise + */ + void my9221_set_auto_refresh(const my9221_context dev, bool enable); + + /** + * Set an LED to a specific on (high intensity) or off (low + * intensity) value. + * + * @param dev Device context + * @param led The LED whose state you wish to change + * @param on true to turn on the LED, false to turn the LED off + */ + void my9221_set_led(const my9221_context dev, int led, bool on); + + /** + * Set the greyscale intensity of an LED in the OFF state. The + * intensity is a value from 0 (fully off) to 255 (fully on). + * This will take effect on any future LED set or clear + * operations. + * + * @param dev Device context + * @param intensity a value from 0 (fully off) to 255 (fully on) + */ + void my9221_set_low_intensity_value(const my9221_context dev, + int intensity); + + /** + * Set the greyscale intensity of an LED in the ON state. The + * intensity is a value from 0 (fully off) to 255 (fully on). + * This will take effect on any future LED set or clear + * operations. + * + * @param dev Device context + * @param intensity a value from 0 (fully off) to 255 (fully on) + */ + void my9221_set_high_intensity_value(const my9221_context dev, + int intensity); + + /** + * Set all of the LEDS to the ON (high intensity value) state. + * + * @param dev Device context + */ + void my9221_set_all(const my9221_context dev); + + /** + * Set all of the LEDS to the OFF (low intensity value) state. + * + * @param dev Device context + */ + void my9221_clear_all(const my9221_context dev); + + /** + * Set the LED states to match the internal stored states. This + * is useful when auto refresh (setAutoRefresh()) is false to + * update the display. + * + * @param dev Device context + */ + void my9221_refresh(const my9221_context dev); + + /** + * Return the maximum number of LEDs present, based on the number + * of instances specified when the device context was initialized. + * + * @param dev Device context + * @return The number of LEDs that can be controlled. + */ + int my9221_get_max_leds(const my9221_context dev); + +#ifdef __cplusplus +} +#endif diff --git a/src/my9221/my9221.hpp b/src/my9221/my9221.hpp index 732aeaaa..45d4066f 100644 --- a/src/my9221/my9221.hpp +++ b/src/my9221/my9221.hpp @@ -35,118 +35,96 @@ #pragma once #include -#include -#include +#include namespace upm { - /** - * @brief MY9221 LED Controller library - * @defgroup my9221 libupm-my9221 - * @ingroup seeed display gpio eak - */ - class MY9221 { - public: - - // 12 LED channels per chip (instance) - static const int LEDS_PER_INSTANCE = 12; - /** - * Instantiates an MY9221 object - * - * @param dataPin Data pin - * @param clockPin Clock pin - * @param instances Number of daisy-chained my9221s, default 1 + * @brief MY9221 LED Controller library + * @defgroup my9221 libupm-my9221 + * @ingroup seeed display gpio eak */ - MY9221(uint8_t dataPin, uint8_t clockPin, int instances=1); + class MY9221 { + public: - /** - * MY9221 destructor - */ - virtual ~MY9221(); + /** + * Instantiates an MY9221 object + * + * @param dataPin Data pin + * @param clockPin Clock pin + * @param instances Number of daisy-chained my9221s, default 1 + */ + MY9221(uint8_t dataPin, uint8_t clockPin, int instances=1); - /** - * Enable or disable auto refresh. When auto refresh is enabled, - * update the LED display as soon as the internal state changes. - * When false, the display(s) will not be updated until the - * refresh() method is called. - * - * @param enable true to enable auto refresh, false otherwise - */ - void setAutoRefresh(bool enable) - { - m_autoRefresh = enable; - } + /** + * MY9221 destructor + */ + virtual ~MY9221(); - /** - * Set an LED to a specific on (high intensity) or off (low - * intensity) value. - * - * @param led The LED whose state you wish to change - * @param on true to turn on the LED, false to turn the LED off - */ - void setLED(int led, bool on); + /** + * Enable or disable auto refresh. When auto refresh is enabled, + * update the LED display as soon as the internal state changes. + * When false, the display(s) will not be updated until the + * refresh() method is called. + * + * @param enable true to enable auto refresh, false otherwise + */ + void setAutoRefresh(bool enable) + { + my9221_set_auto_refresh(m_my9221, enable); + } - /** - * Set the greyscale intensity of an LED in the OFF state. The - * intensity is a value from 0 (fully off) to 255 (fully on). - * This will take effect on any future LED set or clear - * operations. - * - * @param intensity a value from 0 (fully off) to 255 (fully on) - */ - void setLowIntensityValue(int intensity); + /** + * Set an LED to a specific on (high intensity) or off (low + * intensity) value. + * + * @param led The LED whose state you wish to change + * @param on true to turn on the LED, false to turn the LED off + */ + void setLED(int led, bool on); - /** - * Set the greyscale intensity of an LED in the ON state. The - * intensity is a value from 0 (fully off) to 255 (fully on). - * This will take effect on any future LED set or clear - * operations. - * - * @param intensity a value from 0 (fully off) to 255 (fully on) - */ - void setHighIntensityValue(int intensity); + /** + * Set the greyscale intensity of an LED in the OFF state. The + * intensity is a value from 0 (fully off) to 255 (fully on). + * This will take effect on any future LED set or clear + * operations. + * + * @param intensity a value from 0 (fully off) to 255 (fully on) + */ + void setLowIntensityValue(int intensity); - /** - * Set all of the LEDS to the ON (high intensity value) state. - */ - void setAll(); + /** + * Set the greyscale intensity of an LED in the ON state. The + * intensity is a value from 0 (fully off) to 255 (fully on). + * This will take effect on any future LED set or clear + * operations. + * + * @param intensity a value from 0 (fully off) to 255 (fully on) + */ + void setHighIntensityValue(int intensity); - /** - * Set all of the LEDS to the OFF (low intensity value) state. - */ - void clearAll(); + /** + * Set all of the LEDS to the ON (high intensity value) state. + */ + void setAll(); - /** - * Set the LED states to match the internal stored states. This - * is useful when auto refresh (setAutoRefresh()) is false to - * update the display. - */ - void refresh(); + /** + * Set all of the LEDS to the OFF (low intensity value) state. + */ + void clearAll(); - protected: - virtual void lockData(); - virtual void send16bitBlock(uint16_t data); + /** + * Set the LED states to match the internal stored states. This + * is useful when auto refresh (setAutoRefresh()) is false to + * update the display. + */ + void refresh(); - bool m_autoRefresh; - // we're only doing 8-bit greyscale, so the high order bits are - // always 0 - uint16_t m_lowIntensity; - uint16_t m_highIntensity; + protected: - unsigned int m_instances; + my9221_context m_my9221; - mraa::Gpio m_gpioData; - mraa::Gpio m_gpioClk; - - // an array of uint16_t's representing our bit states (on/off) - // intensities. Only the low 8 bits are used, but in the future - // 16bit support can work here as well. - uint16_t *m_bitStates; - - uint16_t m_commandWord; - - private: - }; + private: + }; }