Refactor setRGBled() and add freeRGBled()

- setRGBled() has been rewritten to directly control the hardware
  instead of using the Arduino analogWrite() function.

- Added a two parameter version of setRGBled() that sets the brightness
  of one LED without affecting the others.

- Added function freeRGBled() for freeing the PWM control of the LEDs
  so they can be used digitally.

- Added example sketch RGBled.
This commit is contained in:
Scott Allen 2018-02-22 14:36:55 -05:00
parent eb041d24f8
commit fb77929126
5 changed files with 449 additions and 7 deletions

View File

@ -140,6 +140,9 @@ Placed in the public domain:
BeepDemo example sketch:
By Scott Allen
RGBled example sketch:
By Scott Allen
===============================================================================
\endverbatim
*/

354
examples/RGBled/RGBled.ino Normal file
View File

@ -0,0 +1,354 @@
/*
This sketch demonstrates controlling the Arduboy's RGB LED,
in both analog and digital modes.
*/
/*
To the extent possible under law, Scott Allen has waived all copyright and
related or neighboring rights to this BeepDemo program.
*/
#include <Arduboy2.h>
// The frame rate determines the button auto-repeat rate
#define FRAME_RATE 25
// The increment/decrement amount when auto-repeating
#define REPEAT_AMOUNT 3
// Delay time before button auto-repeat starts, in milliseconds
#define REPEAT_DELAY 700
// Calculation of the number of frames to wait before button auto-repeat starts
#define DELAY_FRAMES (REPEAT_DELAY / (1000 / FRAME_RATE))
#define ANALOG false
#define DIGITAL true
#define ANALOG_MAX 255
// Color array index
enum class Color {
RED,
GREEN,
BLUE,
COUNT
};
// Map LED color index to LED name
const byte LEDpin[(byte)(Color::COUNT)] = {
RED_LED,
GREEN_LED,
BLUE_LED
};
Arduboy2 arduboy;
// Analog LED values
byte analogValue[3] = { 0, 0, 0};
// Digital LED states
byte digitalState[3] = { RGB_OFF, RGB_OFF, RGB_OFF };
byte analogSelected = (byte)(Color::RED);
byte digitalSelected = (byte)(Color::RED);
boolean controlMode = ANALOG;
// Button repeat handling
unsigned int delayCount = 0;
boolean repeating = false;
// ============================= SETUP ===================================
void setup() {
arduboy.begin();
arduboy.setFrameRate(FRAME_RATE);
analogSet();
}
// =======================================================================
// =========================== MAIN LOOP =================================
void loop() {
if (!arduboy.nextFrame()) {
return;
}
arduboy.pollButtons();
// Toggle analog/digital control mode
if (arduboy.justPressed(A_BUTTON)) {
if ((controlMode = !controlMode) == DIGITAL) {
arduboy.freeRGBled();
digitalSet();
}
else {
analogSet();
}
}
// Reset to Analog mode and all LEDs off
if (arduboy.justPressed(B_BUTTON)) {
reset();
}
// Handle D-pad buttons for current mode
if (controlMode == ANALOG) {
modeAnalog();
}
else {
modeDigital();
}
// Handle delay before button auto-repeat starts
if ((delayCount != 0) && (--delayCount == 0)) {
repeating = true;
}
renderScreen(); // Render and display the entire screen
}
// =======================================================================
// Analog control
void modeAnalog() {
if (arduboy.justPressed(RIGHT_BUTTON)) {
valueInc(1);
startButtonDelay();
}
else if (arduboy.justPressed(LEFT_BUTTON)) {
valueDec(1);
startButtonDelay();
}
else if (repeating && arduboy.pressed(RIGHT_BUTTON)) {
valueInc(REPEAT_AMOUNT);
}
else if (repeating && arduboy.pressed(LEFT_BUTTON)) {
valueDec(REPEAT_AMOUNT);
}
else if (arduboy.justPressed(DOWN_BUTTON)) {
analogSelectInc();
}
else if (arduboy.justPressed(UP_BUTTON)) {
analogSelectDec();
}
else if (repeating) {
stopButtonRepeat();
}
}
// Digital control
void modeDigital() {
if (arduboy.justPressed(RIGHT_BUTTON) || arduboy.justPressed(LEFT_BUTTON)) {
digitalState[digitalSelected] = (digitalState[digitalSelected] == RGB_ON) ?
RGB_OFF : RGB_ON;
arduboy.digitalWriteRGB(LEDpin[digitalSelected],
digitalState[digitalSelected]);
}
else if (arduboy.justPressed(DOWN_BUTTON)) {
digitalSelectInc();
}
else if (arduboy.justPressed(UP_BUTTON)) {
digitalSelectDec();
}
}
// Reset to analog mode and turn all LEDs off
void reset() {
digitalState[(byte)(Color::RED)] = RGB_OFF;
digitalState[(byte)(Color::GREEN)] = RGB_OFF;
digitalState[(byte)(Color::BLUE)] = RGB_OFF;
digitalSet();
analogValue[(byte)(Color::RED)] = 0;
analogValue[(byte)(Color::GREEN)] = 0;
analogValue[(byte)(Color::BLUE)] = 0;
analogSet();
digitalSelected = (byte)(Color::RED);
analogSelected = (byte)(Color::RED);
controlMode = ANALOG;
}
// Increment the selected analog LED value by the specified amount
// and update the LED
void valueInc(byte amount) {
if ((ANALOG_MAX - analogValue[analogSelected]) <= amount) {
analogValue[analogSelected] = ANALOG_MAX;
}
else {
analogValue[analogSelected] += amount;
}
arduboy.setRGBled(LEDpin[analogSelected], analogValue[analogSelected]);
}
// Decrement the selected analog LED value by the specified amount
// and update the LED
void valueDec(byte amount) {
if (analogValue[analogSelected] <= amount) {
analogValue[analogSelected] = 0;
}
else {
analogValue[analogSelected] -= amount;
}
arduboy.setRGBled(LEDpin[analogSelected], analogValue[analogSelected]);
}
// Select the next analog color index with wrap
void analogSelectInc() {
selectInc(analogSelected);
}
// Select the previous analog color index with wrap
void analogSelectDec() {
selectDec(analogSelected);
}
// Select the next digital color index with wrap
void digitalSelectInc() {
selectInc(digitalSelected);
}
// Select the previous digital color index with wrap
void digitalSelectDec() {
selectDec(digitalSelected);
}
// Select the next color index with wrap
void selectInc(byte &index) {
if (++index == (byte)(Color::COUNT)) {
index = 0;
}
}
// Select the previous color index with wrap
void selectDec(byte &index) {
if (index == 0) {
index = ((byte)(Color::COUNT) - 1);
}
else {
index--;
}
}
// Update all LEDs in analog mode
void analogSet() {
arduboy.setRGBled(analogValue[(byte)(Color::RED)],
analogValue[(byte)(Color::GREEN)],
analogValue[(byte)(Color::BLUE)]);
}
// Update all LEDs in digital mode
void digitalSet() {
arduboy.digitalWriteRGB(digitalState[(byte)(Color::RED)],
digitalState[(byte)(Color::GREEN)],
digitalState[(byte)(Color::BLUE)]);
}
// Start the button auto-repeat delay
void startButtonDelay() {
delayCount = DELAY_FRAMES;
repeating = false;
}
// Stop the button auto-repeat or delay
void stopButtonRepeat() {
delayCount = 0;
repeating = false;
}
// Render and display the screen
void renderScreen() {
arduboy.setCursor(12, 0);
arduboy.print(F("RGB LED"));
arduboy.setCursor(15, 56);
arduboy.print(F("A:Mode B:Reset"));
arduboy.setCursor(74, 0);
if (controlMode == ANALOG) {
arduboy.print(F(" Analog"));
drawAnalog(9, Color::RED, "Red:");
drawAnalog(25, Color::GREEN, "Green:");
drawAnalog(41, Color::BLUE, "Blue:");
}
else { // Digital
arduboy.print(F("Digital"));
drawDigital(9, Color::RED, "Red:");
drawDigital(25, Color::GREEN, "Green:");
drawDigital(41, Color::BLUE, "Blue:");
}
arduboy.display(CLEAR_BUFFER);
}
// Draw the information for one analog color
void drawAnalog(int y, Color color, const char* name) {
byte value = analogValue[(byte)color];
arduboy.setCursor(0, y);
arduboy.print(name);
arduboy.setCursor(42, y);
printValue(value);
if (analogSelected == (byte)color) {
arduboy.print(F(" <--"));
}
drawBar(y + 8, color, value);
}
// Draw the value bar for an analog color
void drawBar(int y, Color color, byte value) {
byte barLength = value / 2;
if (barLength == 0) {
return;
}
if (analogSelected == (byte)color) {
arduboy.fillRect(0, y, barLength, 5);
}
else {
arduboy.drawRect(0, y, barLength, 5);
}
}
// Draw the informaton for one digital color
void drawDigital(int y, Color color, const char* name) {
byte state = digitalState[(byte)color];
arduboy.setCursor(34, y + 3);
arduboy.print(name);
arduboy.setCursor(76, y + 3);
if (state == RGB_ON) {
arduboy.print(F("ON "));
arduboy.fillCircle(22, y + 6, 4);
}
else {
arduboy.print(F("OFF"));
arduboy.drawCircle(22, y + 6, 4);
}
if (digitalSelected == (byte)color) {
arduboy.print(F(" <--"));
arduboy.drawRect(16, y, 13, 13);
}
}
// Print a byte in decimal and hex
void printValue(byte val) {
if (val < 100) {
arduboy.print(' ');
}
if (val < 10) {
arduboy.print(' ');
}
arduboy.print(val);
arduboy.print(F(" 0x"));
if (val < 0x10) {
arduboy.print('0');
}
arduboy.print(val, HEX);
}

View File

@ -57,6 +57,7 @@ fillTriangle KEYWORD2
flashlight KEYWORD2
flipVertical KEYWORD2
flipHorizontal KEYWORD2
freeRGBled KEYWORD2
getBuffer KEYWORD2
getCursorX KEYWORD2
getCursorY KEYWORD2

View File

@ -429,10 +429,17 @@ void Arduboy2Core::flipHorizontal(bool flipped)
void Arduboy2Core::setRGBled(uint8_t red, uint8_t green, uint8_t blue)
{
#ifdef ARDUBOY_10 // RGB, all the pretty colors
// inversion is necessary because these are common annode LEDs
analogWrite(RED_LED, 255 - red);
analogWrite(GREEN_LED, 255 - green);
analogWrite(BLUE_LED, 255 - blue);
// timer 0: Fast PWM, OC0A clear on compare / set at top
// We must stay in Fast PWM mode because timer 0 is used for system timing.
// We can't use "inverted" mode because it won't allow full shut off.
TCCR0A = _BV(COM0A1) | _BV(WGM01) | _BV(WGM00);
OCR0A = 255 - green;
// timer 1: Phase correct PWM 8 bit
// OC1A and OC1B set on up-counting / clear on down-counting (inverted). This
// allows the value to be directly loaded into the OCR with common anode LED.
TCCR1A = _BV(COM1A1) | _BV(COM1A0) | _BV(COM1B1) | _BV(COM1B0) | _BV(WGM10);
OCR1AL = blue;
OCR1BL = red;
#elif defined(AB_DEVKIT)
// only blue on DevKit, which is not PWM capable
(void)red; // parameter unused
@ -441,6 +448,39 @@ void Arduboy2Core::setRGBled(uint8_t red, uint8_t green, uint8_t blue)
#endif
}
void Arduboy2Core::setRGBled(uint8_t color, uint8_t val)
{
#ifdef ARDUBOY_10
if (color == RED_LED)
{
OCR1BL = val;
}
else if (color == GREEN_LED)
{
OCR0A = 255 - val;
}
else if (color == BLUE_LED)
{
OCR1AL = val;
}
#elif defined(AB_DEVKIT)
// only blue on DevKit, which is not PWM capable
if (color == BLUE_LED)
{
bitWrite(BLUE_LED_PORT, BLUE_LED_BIT, val ? RGB_ON : RGB_OFF);
}
#endif
}
void Arduboy2Core::freeRGBled()
{
#ifdef ARDUBOY_10
// clear the COM bits to return the pins to normal I/O mode
TCCR0A = _BV(WGM01) | _BV(WGM00);
TCCR1A = _BV(WGM10);
#endif
}
void Arduboy2Core::digitalWriteRGB(uint8_t red, uint8_t green, uint8_t blue)
{
#ifdef ARDUBOY_10

View File

@ -609,10 +609,45 @@ class Arduboy2Core
* LEDs will light.
* \endparblock
*
* \see digitalWriteRGB()
* \see setRGBled(uint8_t, uint8_t) digitalWriteRGB() freeRGBled()
*/
void static setRGBled(uint8_t red, uint8_t green, uint8_t blue);
/** \brief
* Set the brightness of one of the RGB LEDs without affecting the others.
*
* \param color The name of the LED to set. The value given should be one
* of RED_LED, GREEN_LED or BLUE_LED.
*
* \param val The brightness value for the LED, from 0 to 255.
*
* \note
* In order to use this function, the 3 parameter version must first be
* called at least once, in order to initialize the hardware.
*
* \details
* This 2 parameter version of the function will set the brightness of a
* single LED within the RGB LED without affecting the current brightness
* of the other two. See the description of the 3 parameter version of this
* function for more details on the RGB LED.
*
* \see setRGBled(uint8_t, uint8_t, uint8_t) digitalWriteRGB() freeRGBled()
*/
void static setRGBled(uint8_t color, uint8_t val);
/** \brief
* Relinquish analog control of the RGB LED.
*
* \details
* Using the RGB LED in analog mode prevents further use of the LED in
* digital mode. This function will restore the pins used for the LED, so
* it can be used in digital mode.
*
* \see digitalWriteRGB() setRGBled()
*/
void static freeRGBled();
/** \brief
* Set the RGB LEDs digitally, to either fully on or fully off.
*
@ -638,14 +673,23 @@ class Arduboy2Core
* RGB_ON RGB_ON RGB_ON White
*
* \note
* \parblock
* Using the RGB LED in analog mode will prevent digital control of the
* LED. To restore the ability to control the LED digitally, use the
* `freeRGBled()` function.
* \endparblock
*
* \note
* \parblock
* Many of the Kickstarter Arduboys were accidentally shipped with the
* RGB LED installed incorrectly. For these units, the green LED cannot be
* lit. As long as the green led is set to off, turning on the red LED will
* actually light the blue LED and turning on the blue LED will actually
* light the red LED. If the green LED is turned on, none of the LEDs
* will light.
* \endparblock
*
* \see digitalWriteRGB(uint8_t, uint8_t) setRGBled()
* \see digitalWriteRGB(uint8_t, uint8_t) setRGBled() freeRGBled()
*/
void static digitalWriteRGB(uint8_t red, uint8_t green, uint8_t blue);
@ -663,7 +707,7 @@ class Arduboy2Core
* the RGB LED either fully on or fully off. See the description of the
* 3 parameter version of this function for more details on the RGB LED.
*
* \see digitalWriteRGB(uint8_t, uint8_t, uint8_t) setRGBled()
* \see digitalWriteRGB(uint8_t, uint8_t, uint8_t) setRGBled() freeRGBled()
*/
void static digitalWriteRGB(uint8_t color, uint8_t val);