#include #if defined(ARCADA_CALLBACKTIMER) && defined(__SAMD51__) static Adafruit_ZeroTimer zerotimer = Adafruit_ZeroTimer(ARCADA_CALLBACKTIMER); void ARCADA_CALLBACKTIMER_HANDLER() { Adafruit_ZeroTimer::timerHandler(ARCADA_CALLBACKTIMER); } #endif #if defined(NRF52_SERIES) void (*nrf52_callback)() = NULL; extern "C" { void SysTick_Handler(void) { if (nrf52_callback != NULL) nrf52_callback(); } } // extern C #endif /**************************************************************************/ /*! @brief Instantiator for Arcada class, will allso inistantiate (but not init) the TFT */ /**************************************************************************/ Adafruit_Arcada_SPITFT::Adafruit_Arcada_SPITFT() { _sd_cs = ARCADA_SD_CS; _speaker_en = ARCADA_SPEAKER_ENABLE; _neopixel_pin = ARCADA_NEOPIXEL_PIN; _neopixel_num = ARCADA_NEOPIXEL_NUM; _backlight_pin = ARCADA_TFT_LITE; _battery_sensor = ARCADA_BATTERY_SENSOR; _light_sensor = ARCADA_LIGHT_SENSOR; _touch_xp = ARCADA_TOUCHSCREEN_XP; _touch_yp = ARCADA_TOUCHSCREEN_YP; _touch_xm = ARCADA_TOUCHSCREEN_XM; _touch_ym = ARCADA_TOUCHSCREEN_YM; _ts_xmin = ARCADA_TOUCHSCREEN_CALIBX_MIN; _ts_xmax = ARCADA_TOUCHSCREEN_CALIBX_MAX; _ts_ymin = ARCADA_TOUCHSCREEN_CALIBY_MIN; _ts_ymax = ARCADA_TOUCHSCREEN_CALIBY_MAX; _start_button = ARCADA_BUTTONPIN_START; _select_button = ARCADA_BUTTONPIN_SELECT; _a_button = ARCADA_BUTTONPIN_A; _b_button = ARCADA_BUTTONPIN_B; _up_button = ARCADA_BUTTONPIN_UP; _down_button = ARCADA_BUTTONPIN_DOWN; _left_button = ARCADA_BUTTONPIN_LEFT; _right_button = ARCADA_BUTTONPIN_RIGHT; _button_latch = ARCADA_BUTTON_LATCH; _button_clock = ARCADA_BUTTON_CLOCK; _button_data = ARCADA_BUTTON_DATA; _shift_up = ARCADA_BUTTON_SHIFTMASK_UP; _shift_down = ARCADA_BUTTON_SHIFTMASK_DOWN; _shift_left = ARCADA_BUTTON_SHIFTMASK_LEFT; _shift_right = ARCADA_BUTTON_SHIFTMASK_RIGHT; _shift_a = ARCADA_BUTTON_SHIFTMASK_A; _shift_b = ARCADA_BUTTON_SHIFTMASK_B; _shift_start = ARCADA_BUTTON_SHIFTMASK_START; _shift_select = ARCADA_BUTTON_SHIFTMASK_SELECT; _joystick_x = ARCADA_JOYSTICK_X; _joystick_y = ARCADA_JOYSTICK_Y; } /**************************************************************************/ /*! @brief Initialize GPIO, NeoPixels, and speaker @return True on success, False if something failed! */ /**************************************************************************/ bool Adafruit_Arcada_SPITFT::arcadaBegin(void) { if (!variantBegin()) return false; setBacklight(0); if (_sd_cs >= 0) { pinMode(_sd_cs, OUTPUT); digitalWrite(_sd_cs, HIGH); } pinMode(ARCADA_TFT_CS, OUTPUT); digitalWrite(ARCADA_TFT_CS, HIGH); if (_speaker_en >= 0) { pinMode(_speaker_en, OUTPUT); enableSpeaker(false); } // current working dir is / strcpy(_cwd_path, "/"); if (_neopixel_pin >= 0) { pixels.updateLength(_neopixel_num); pixels.setPin(_neopixel_pin); pixels.begin(); delay(10); pixels.setBrightness(20); pixels.fill(0); pixels.show(); // turn off delay(10); pixels.show(); // turn off } _touchscreen = NULL; if (_touch_xp >= 0) { _touchscreen = new TouchScreen(_touch_xp, _touch_yp, _touch_xm, _touch_ym, 300); setTouchscreenCalibration(_ts_xmin, _ts_xmax, _ts_ymin, _ts_ymax); } if (_start_button >= 0) { pinMode(_start_button, INPUT_PULLUP); } if (_select_button >= 0) { pinMode(_select_button, INPUT_PULLUP); } if (_a_button >= 0) { pinMode(_a_button, INPUT_PULLUP); } if (_b_button >= 0) { pinMode(_b_button, INPUT_PULLUP); } if (_up_button >= 0) { pinMode(_up_button, INPUT_PULLUP); } if (_down_button >= 0) { pinMode(_down_button, INPUT_PULLUP); } if (_left_button >= 0) { pinMode(_left_button, INPUT_PULLUP); } if (_right_button >= 0) { pinMode(_right_button, INPUT_PULLUP); } if (_button_clock >= 0) { pinMode(_button_clock, OUTPUT); digitalWrite(_button_clock, HIGH); } if (_button_latch >= 0) { pinMode(_button_latch, OUTPUT); digitalWrite(_button_latch, HIGH); } if (_button_data >= 0) { pinMode(_button_data, INPUT); } #ifndef SPIWIFI _has_wifi = false; #else WiFi.status(); delay(100); if (WiFi.status() == WL_NO_MODULE) { _has_wifi = false; } else { _has_wifi = true; } #endif // we can keep track of buttons for ya last_buttons = curr_buttons = justpressed_buttons = justreleased_buttons = 0; return true; } /**************************************************************************/ /*! @brief Set the backlight brightness and save to the configuration @param brightness From 0 (off) to 255 (full on) @param saveToDisk Whether we save this permanently to disk, default is false @returns Whether saving to disk succeeded, or true if we don't save */ /**************************************************************************/ bool Adafruit_Arcada_SPITFT::setBacklight(uint8_t brightness, bool saveToDisk) { _brightness = brightness; if (_backlight_pin >= 0) { pinMode(_backlight_pin, OUTPUT); if (_brightness == 0) { digitalWrite(_backlight_pin, LOW); } else { analogWrite(_backlight_pin, brightness); } } #ifdef ARCADA_USE_JSON configJSON["brightness"] = _brightness; if (saveToDisk) { return saveConfigurationFile(); } #endif return true; } /**************************************************************************/ /*! @brief Get the backlight brightness @returns brightness From 0 (off) to 255 (full on) */ /**************************************************************************/ uint8_t Adafruit_Arcada_SPITFT::getBacklight(void) { return _brightness; } /**************************************************************************/ /*! @brief Set the audio volume (not working at this time) @param volume From 0 (off) to 255 (full on) @param saveToDisk Whether we save this permanently to disk, default is false @returns Whether saving to disk succeeded, or true if we don't save */ /**************************************************************************/ bool Adafruit_Arcada_SPITFT::setVolume(uint8_t volume, bool saveToDisk) { _volume = volume; #ifdef ARCADA_USE_JSON configJSON["volume"] = _volume; if (saveToDisk) { return saveConfigurationFile(); } #endif return true; } /**************************************************************************/ /*! @brief Get the audio volume (not working at this time) @returns Volume From 0 (off) to 255 (full on) */ /**************************************************************************/ uint8_t Adafruit_Arcada_SPITFT::getVolume(void) { return _volume; } /**************************************************************************/ /*! @brief Turn on the speaker amplifier @param on True to enable, False to disable */ /**************************************************************************/ void Adafruit_Arcada_SPITFT::enableSpeaker(bool on) { if (_speaker_en >= 0) { digitalWrite(_speaker_en, on); } } /**************************************************************************/ /*! @brief Create a repetative callback to a function using a timer @param freq The callback frequency, must be between 0.75 Hz and 24MHz (slower is better) @param callback A pointer to the function we'll call every time @return True on success, False if something failed! */ /**************************************************************************/ bool Adafruit_Arcada_SPITFT::timerCallback(float freq, void (*callback)()) { #if defined(__SAMD51__) Serial.printf("Desired freq: %f Hz\n", freq); uint16_t divider = 1; uint16_t compare = 0; tc_clock_prescaler prescaler = TC_CLOCK_PRESCALER_DIV1; if ((freq < 24000000) && (freq > 800)) { divider = 1; prescaler = TC_CLOCK_PRESCALER_DIV1; compare = 48000000 / freq; } else if (freq > 400) { divider = 2; prescaler = TC_CLOCK_PRESCALER_DIV2; compare = (48000000 / 2) / freq; } else if (freq > 200) { divider = 4; prescaler = TC_CLOCK_PRESCALER_DIV4; compare = (48000000 / 4) / freq; } else if (freq > 100) { divider = 8; prescaler = TC_CLOCK_PRESCALER_DIV8; compare = (48000000 / 8) / freq; } else if (freq > 50) { divider = 16; prescaler = TC_CLOCK_PRESCALER_DIV16; compare = (48000000 / 16) / freq; } else if (freq > 12) { divider = 64; prescaler = TC_CLOCK_PRESCALER_DIV64; compare = (48000000 / 64) / freq; } else if (freq > 3) { divider = 256; prescaler = TC_CLOCK_PRESCALER_DIV256; compare = (48000000 / 256) / freq; } else if (freq >= 0.75) { divider = 1024; prescaler = TC_CLOCK_PRESCALER_DIV1024; compare = (48000000 / 1024) / freq; } else { return false; } _callback_freq = ((48000000.0 / (float)divider) / (float)compare); _callback_func = callback; // Serial.printf("Divider %d / compare %d -> %f Hz\n", // divider, compare, _callback_freq); zerotimer.enable(false); zerotimer.configure(prescaler, // prescaler TC_COUNTER_SIZE_16BIT, // bit width of timer/counter TC_WAVE_GENERATION_MATCH_PWM // frequency or PWM mode ); zerotimer.setCompare(0, compare); zerotimer.setCallback(true, TC_CALLBACK_CC_CHANNEL0, callback); zerotimer.enable(true); return true; #elif defined(NRF52_SERIES) SysTick_Config(F_CPU / freq); nrf52_callback = callback; return true; #else (void)freq; (void)callback; return false; #endif } /**************************************************************************/ /*! @brief Get the final frequency created for the callback helper @return The callback frequency */ /**************************************************************************/ float Adafruit_Arcada_SPITFT::getTimerCallbackFreq(void) { #if defined(__SAMD51__) return _callback_freq; #else return 0; #endif } /**************************************************************************/ /*! @brief Get the previous callback function we were using @return A pointer to a function that takes no arguments, and returns nothing or NULL on no callback set */ /**************************************************************************/ arcada_callback_t Adafruit_Arcada_SPITFT::getTimerCallback(void) { #if defined(__SAMD51__) return _callback_func; #else return NULL; #endif } /**************************************************************************/ /*! @brief Stop a previously-initiated timer. */ /**************************************************************************/ void Adafruit_Arcada_SPITFT::timerStop(void) { #if defined(__SAMD51__) zerotimer.enable(false); #endif } /**************************************************************************/ /*! @brief printf wrapper to serial debug, handy for logging config, C inclusion @param format The printf-compatible format and extra args */ /**************************************************************************/ void Adafruit_Arcada_SPITFT::printf(const char *format, ...) { va_list args; va_start(args, format); Serial.printf(format, args); va_end(args); } /**************************************************************************/ /*! @brief Read X analog joystick @param sampling How many samples to read and average, default is 3 @return Signed 16 bits, from -512 to 511, 0 being 'center' */ /**************************************************************************/ int16_t Adafruit_Arcada_SPITFT::readJoystickX(uint8_t sampling) { float reading = 0; if (_joystick_x >= 0) { for (int i = 0; i < sampling; i++) { reading += analogRead(_joystick_x); } reading /= sampling; // adjust range from 0->1024 to -512 to 511; reading -= _joyx_center; } return reading; } /**************************************************************************/ /*! @brief Read Y analog joystick @param sampling How many samples to read and average, default is 3 @return Signed 16 bits, from -512 to 511, 0 being 'center' */ /**************************************************************************/ int16_t Adafruit_Arcada_SPITFT::readJoystickY(uint8_t sampling) { float reading = 0; if (_joystick_y >= 0) { for (int i = 0; i < sampling; i++) { reading += analogRead(_joystick_y); } reading /= sampling; // adjust range from 0->1024 to -512 to 511; reading -= _joyy_center; } return reading; } /**************************************************************************/ /*! @brief Read all buttons/joystick and return a bitmask of which buttons are pressed, check ARCADA_BUTTONMASK_* for valid bitmasks to check again. If there's an analog joystick, it will 'simulate' the button presses. @return Bit array with up to 32 buttons, 1 for pressed, 0 for not. */ /**************************************************************************/ uint32_t Adafruit_Arcada_SPITFT::readButtons(void) { uint32_t buttons = variantReadButtons(); // start with whatever the variant can do // Use a latch to read 8 bits if (_button_clock >= 0) { uint8_t shift_buttons = 0; digitalWrite(_button_latch, LOW); delayMicroseconds(1); digitalWrite(_button_latch, HIGH); delayMicroseconds(1); for (int i = 0; i < 8; i++) { shift_buttons <<= 1; shift_buttons |= digitalRead(_button_data); digitalWrite(_button_clock, HIGH); delayMicroseconds(1); digitalWrite(_button_clock, LOW); delayMicroseconds(1); } if (shift_buttons & _shift_b) buttons |= ARCADA_BUTTONMASK_B; if (shift_buttons & _shift_a) buttons |= ARCADA_BUTTONMASK_A; if (shift_buttons & _shift_select) buttons |= ARCADA_BUTTONMASK_SELECT; if (shift_buttons & _shift_start) buttons |= ARCADA_BUTTONMASK_START; if (shift_buttons & _shift_up) buttons |= ARCADA_BUTTONMASK_UP; if (shift_buttons & _shift_down) buttons |= ARCADA_BUTTONMASK_DOWN; if (shift_buttons & _shift_left) buttons |= ARCADA_BUTTONMASK_LEFT; if (shift_buttons & _shift_right) buttons |= ARCADA_BUTTONMASK_RIGHT; } // GPIO buttons! if ((_start_button >= 0) && !digitalRead(_start_button)) buttons |= ARCADA_BUTTONMASK_START; if ((_select_button >= 0) && !digitalRead(_select_button)) buttons |= ARCADA_BUTTONMASK_SELECT; if ((_a_button >= 0) && !digitalRead(_a_button)) buttons |= ARCADA_BUTTONMASK_A; if ((_b_button >= 0) && !digitalRead(_b_button)) buttons |= ARCADA_BUTTONMASK_B; if ((_up_button >= 0) && !digitalRead(_up_button)) buttons |= ARCADA_BUTTONMASK_UP; if ((_down_button >= 0) && !digitalRead(_down_button)) buttons |= ARCADA_BUTTONMASK_DOWN; if ((_left_button >= 0) && !digitalRead(_left_button)) buttons |= ARCADA_BUTTONMASK_LEFT; if ((_right_button >= 0) && !digitalRead(_right_button)) buttons |= ARCADA_BUTTONMASK_RIGHT; // Potentiometers for X & Y int16_t x = readJoystickX(); // returns 0 on no joystick if (x > 350) buttons |= ARCADA_BUTTONMASK_RIGHT; else if (x < -350) buttons |= ARCADA_BUTTONMASK_LEFT; int16_t y = readJoystickY(); // returns 0 on no joystick if (y > 350) buttons |= ARCADA_BUTTONMASK_DOWN; else if (y < -350) buttons |= ARCADA_BUTTONMASK_UP; // Touchscreen if (_touchscreen) { TSPoint p = getTouchscreenPoint(); if (p.z > 100) { // Serial.printf("(%d, %d)\n", p.x, p.y); // up! if ((p.y < display->height() / 4) && (p.x > display->width() / 4) && (p.x < (display->width() * 3.0 / 4.0))) { buttons |= ARCADA_BUTTONMASK_UP; } // down! if ((p.y > (display->height() * 3.0 / 4.0)) && (p.x > display->width() / 3) && (p.x < (display->width() * 3.0 / 4.0))) { buttons |= ARCADA_BUTTONMASK_DOWN; } // left! if ((p.x < display->width() / 4) && (p.y > display->height() / 4) && (p.y < (display->height() * 3.0 / 4.0))) { buttons |= ARCADA_BUTTONMASK_LEFT; } // right! if ((p.x > (display->width() * 3.0 / 4.0)) && (p.y > display->height() / 4) && (p.y < (display->height() * 3.0 / 4.0))) { buttons |= ARCADA_BUTTONMASK_RIGHT; } // B if ((p.x > display->width() / 4) && (p.x < display->width() / 2) // 2nd quarter && (p.y > display->height() / 4) && (p.y < (display->height() * 3.0 / 4.0))) { buttons |= ARCADA_BUTTONMASK_B; } // A if ((p.x > display->width() / 2) && (p.x < (display->width() * 3.0 / 4.0)) // 3rd quarter && (p.y > display->height() / 4) && (p.y < (display->height() * 3.0 / 4.0))) { buttons |= ARCADA_BUTTONMASK_A; } } } last_buttons = curr_buttons; curr_buttons = buttons; justpressed_buttons = (last_buttons ^ curr_buttons) & curr_buttons; justreleased_buttons = (last_buttons ^ curr_buttons) & last_buttons; return buttons; } /**************************************************************************/ /*! @brief What buttons were just pressed as of the last readButtons() call. Use ARCADA_BUTTONMASK_* defines to extract which bits are true (just pressed) @return Bitmask of all buttons that were just pressed */ /**************************************************************************/ uint32_t Adafruit_Arcada_SPITFT::justPressedButtons(void) { return justpressed_buttons; } /**************************************************************************/ /*! @brief What buttons were just released as of the last readButtons() call. Use ARCADA_BUTTONMASK_* defines to extract which bits are true (just releasd) @return Bitmask of all buttons that were just released */ /**************************************************************************/ uint32_t Adafruit_Arcada_SPITFT::justReleasedButtons(void) { return justreleased_buttons; } /**************************************************************************/ /*! @brief Read the light sensor onboard if there is one @return 0 (darkest) to 1023 (brightest) or 0 if there is no sensor */ /**************************************************************************/ uint16_t Adafruit_Arcada_SPITFT::readLightSensor(void) { if (_light_sensor >= 0) { return analogRead(_light_sensor); } else { return 0; } } /**************************************************************************/ /*! @brief Read the batterysensor onboard if there is one @return Voltage as floating point or NAN if there is no sensor */ /**************************************************************************/ float Adafruit_Arcada_SPITFT::readBatterySensor(void) { if (_battery_sensor >= 0) { return ((float)analogRead(_battery_sensor) / 1023.0) * 2.0 * 3.3; } else { return NAN; } } /**************************************************************************/ /*! @brief Create (allocate) an internal GFX canvas of given width and height @param width Number of pixels wide @param height Number of pixels tall @return True on success (could allocate) or false on failure */ /**************************************************************************/ bool Adafruit_Arcada_SPITFT::createFrameBuffer(uint16_t width, uint16_t height) { if (_canvas) delete (_canvas); _canvas = new GFXcanvas16(width, height); return (_canvas != NULL); } /**************************************************************************/ /*! @brief Write the internal framebuffer to the display at coord (x, y) @param x X coordinate in the TFT screen to write it to @param y Y coordinate in the TFT screen to write it to @param blocking If true, function waits until blit is done. Otherwise we let DMA do the blitting and return immediately (THIS ISN'T NECESSARILY TRUE, SEE NOTE BELOW) @param bigEndian If true, frame buffer data is already in big-endian order (which is NOT SAMD-native order) and an actual background DMA blit can take place (SEE NOTE BELOW) @param blitdisplay The pointer to display we'll blit to. If not passed in, we'll use the 'internal' default of 'display' that the variant creates @return True on success, failure if no canvas exists yet @note Even if blocking is 'false,' this function may still block. For starters, DMA must be enabled in Adafruit_SPITFT.h. If bigEndian is NOT true (and this is the normal case on SAMD, being little-endian, and with GFX pixels in RAM being in MCU-native order), then every pixel needs to be byte-swapped before issuing to the display (which tend to be big-endian). If blocking is false, DMA transfers are used on a per- scanline basis and we at least get the cycles to perform this byte- swapping "free," but really it's no faster than a blocking write without byte swaps (except for the last scanline, which we allow the transfer to complete in the background). To really truly get a non-blocking full DMA transfer, blocking must be false AND bigEndian must be true...and graphics must be drawn to the canvas using byte-swapped colors, which is not normal (GFX uses device-native 16-bit type for pixels, i.e. little- endian). ONLY THEN will the entire transfer take place in the background (and the application should wait before further drawing in the framebuffer until the transfer completes). */ /**************************************************************************/ bool Adafruit_Arcada_SPITFT::blitFrameBuffer(uint16_t x, uint16_t y, bool blocking, bool bigEndian, Adafruit_SPITFT *blitdisplay) { if (!blitdisplay) { blitdisplay = display; } if (_canvas) { if (!_first_frame) { blitdisplay->dmaWait(); // Wait for prior DMA transfer to complete blitdisplay->endWrite(); // End transaction from any prior call } else { _first_frame = false; } blitdisplay->startWrite(); // Start new display transaction blitdisplay->setAddrWindow(x, y, _canvas->width(), _canvas->height()); blitdisplay->writePixels(_canvas->getBuffer(), _canvas->width() * _canvas->height(), blocking, bigEndian); return true; } return false; // No canvas allocated yet } /**************************************************************************/ /*! @brief Does this board have a touchscreen @returns True if it does */ /**************************************************************************/ bool Adafruit_Arcada_SPITFT::hasTouchscreen(void) { return (_touch_xp >= 0); } /**************************************************************************/ /*! @brief Set the X/Y calibration values for the touchscreen @param xmin The value of X which corresponds to 0 on that axis @param ymin The value of Y which corresponds to 0 on that axis @param xmax The value of X which corresponds to the TFT width on that axis @param ymax The value of Y which corresponds to the TFT height on that axis */ /**************************************************************************/ void Adafruit_Arcada_SPITFT::setTouchscreenCalibration(int16_t xmin, int16_t xmax, int16_t ymin, int16_t ymax) { _ts_xmin = xmin; _ts_xmax = xmax; _ts_ymin = ymin; _ts_ymax = ymax; } /**************************************************************************/ /*! @brief Get a calibrated point that corresponds to the TFT @returns A TSPoint with x, y and z pressure readings. If z is 0 no touch was detected. */ /**************************************************************************/ TSPoint Adafruit_Arcada_SPITFT::getTouchscreenPoint(void) { if (!_touchscreen) { TSPoint p; p.x = p.y = p.z = 0; return p; } TSPoint points[8]; for (int i = 0; i < 8; i++) { points[i] = _touchscreen->getPoint(); // the uncalibrated point } bool invalid = true; TSPoint p; for (int i = 0; i < 8; i++) { if ((points[i].z > 100) && (points[i].z < 1000)) { p.x = points[i].x; p.y = points[i].y; p.z = points[i].z; invalid = false; } } if (invalid) { p.x = p.y = p.z = 0; return p; } // Serial.printf("rot: %d (%d, %d) \t", getRotation(), p.x, p.y); if (display->getRotation() == 0) { int _y = map(p.y, _ts_ymin, _ts_ymax, 0, display->height()); int _x = map(p.x, _ts_xmin, _ts_xmax, 0, display->width()); p.x = _x; p.y = _y; } if (display->getRotation() == 1) { int _x = map(p.y, _ts_ymin, _ts_ymax, 0, display->width()); int _y = map(p.x, _ts_xmax, _ts_xmin, 0, display->height()); p.x = _x; p.y = _y; } if (display->getRotation() == 2) { int _y = map(p.y, _ts_ymax, _ts_ymin, 0, display->height()); int _x = map(p.x, _ts_xmax, _ts_xmin, 0, display->width()); p.x = _x; p.y = _y; } if (display->getRotation() == 3) { int _x = map(p.y, _ts_ymax, _ts_ymin, 0, display->width()); int _y = map(p.x, _ts_xmin, _ts_xmax, 0, display->height()); p.x = _x; p.y = _y; } return p; } /**************************************************************************/ /*! @brief Does this board have a control pad - latch, buttons or analog joystick? @returns True if it does */ /**************************************************************************/ bool Adafruit_Arcada_SPITFT::hasControlPad(void) { if (_joystick_x >= 0 || _button_clock >= 0 || _a_button >= 0) { return true; } else { return false; } } /*! @brief Convert hue, saturation and value into a packed 16-bit RGB color that can be passed to TFT @param H The Hue ranging from 0 to 359 @param S Saturation, 8-bit value, 0 (min or pure grayscale) to 100 (max or pure hue) @param V Value (brightness), 8-bit value, 0 (min / black / off) to 100 (max or full brightness) @return Packed 16-bit 5-6-5 RGB. Result is linearly but not perceptually correct for LEDs. Intended for TFT use only. */ // https://gist.github.com/kuathadianto/200148f53616cbd226d993b400214a7f uint16_t Adafruit_Arcada_SPITFT::ColorHSV565(int16_t H, uint8_t S, uint8_t V) { double C = S * V / 10000.0; double X = C * (1 - abs(fmod(H / 60.0, 2) - 1)); double m = (V / 100.0) - C; double Rs, Gs, Bs; if (H >= 0 && H < 60) { Rs = C; Gs = X; Bs = 0; } else if (H >= 60 && H < 120) { Rs = X; Gs = C; Bs = 0; } else if (H >= 120 && H < 180) { Rs = 0; Gs = C; Bs = X; } else if (H >= 180 && H < 240) { Rs = 0; Gs = X; Bs = C; } else if (H >= 240 && H < 300) { Rs = X; Gs = 0; Bs = C; } else { Rs = C; Gs = 0; Bs = X; } uint8_t red = (Rs + m) * 255; uint8_t green = (Gs + m) * 255; uint8_t blue = (Bs + m) * 255; return display->color565(red, green, blue); }