Introduction
In this tutorial we will explore how to interface an Adafruit RA8875 and a 7″ TFT LCD with a STM32 µP. The RA8875 uses 2 wire SPI to communicate with the µP (Micro Processor).
To enable this project to use existing Arduino libraries, they were modified to use the HAL function calls, as well replacement of other Arduino specific code to work with STM32 Family products.
It is a perfect match for any Micro Processor that wants to draw on a big TFT screen but doesn’t quite have the oomph (whether it be hardware or speed). Inside is 768KB of RAM, so it can buffer the display (and depending on the screen size also have double overlaying). For this tutorial, we have chosen the Nucelo-F439Zi board.
The interface is SPI with a very basic register read/write method of communication (no strange and convoluted packets). The chip has a range of hardware-accelerated shapes such as lines, rectangles, triangles, ellipses, built in and rounded-rectangles. There is also a built in English/European font set. This makes it possible to draw fast even over SPI.
The RA8875 can also handle standard 4-wire resistive touchscreens over the same SPI interface to save you pins. There’s an IRQ pin that you can use to help manage touch interrupts. However, in this tutorial we did not dive into connecting nor using the Touch Screen.
The Touch Screen code library was also not ported for this tutorial. The two Arduino libraries that were converted for this project are the Adafruit RA8875 and the Adafruit GFX libraries.
This tutorial uses the same method explained in a previous tutorial for combining standard C code used by the STM32 Family products with C++ code. This tutorial can be found here: How to write C++ code for an STM32 µP
Materials List
- FTDI to USB
- NUCLEO-F439ZI
- Breadboards Kit Include 2PCS 830 Point 2PCS 400 Point Solderless Breadboards
- RA8875 Driver Board for 40-pin TFT Touch Displays – 800×480 Max
- 7.0″ 40-pin TFT Display – 800×480 with Touchscreen
FTDI Pinouts
FTDI to USB Pinout from right to left
- Pin 1 – GND
- Pin 2 – CTS
- Pin 3 – VCC
- Pin 4 – TX
- Pin 5 – RX
- Pin 6 – DTR
- USB Mini – Connect to PC via USB cable
Make sure the jumper is set for 5v or 3v which ever is being used to power the FTDI device. If we look below at the schematic, it is showing in this case as 5v.


Adafruit RA8875 Pinouts

RS8875 | Nucleo-F439 |
VIN | 5V |
GND | GND |
3Vo | Not Connected |
LITE | Not Connected |
SCK | PA5 |
MISO | PA6 |
MOSI | PB5 |
CS | PG1 |
RST | PG11 |
WAIT | PG10 |
INT | Not Connected |
Y+ | Not Connected |
Y- | Not Connected |
X- | Not Connected |
X+ | Not Connected |
Adafruit 7.0″ 40-pin TFT Display – 800×480 with Touchscreen
Assembly Intructions.

Schematic
The LED’s are for display purposes only. The Red LED indicates that the WAIT is low showing that the RA8875 CPU is busy. The Blue LED indicates that the CS (Chip Select) is low and that the Micro Processor is talking to the RA8875. When the Yellow LED goes off, this indicates that the RA8875 has been reset by the Micro Processor. If the Yellow LED is on, this indicates that the RA8875 is not in reset mode.

Pinouts & Configurations
Testing the Modifications
Before we get into examining the source code, let’s talk about how this code was translated from an Arduino Library into a library that can be used with the STM32 family of micro processors.
The Arduino Library was migrated into this project by reverse engineering the existing Arduino code and making appropriate changes to fit the architecture of the STM32 family. We then tested the code using two translated “Example INO Sketches”. The first being the Example “buildtest.ino” and the second was using Example “textmode.ino” both of these examples were also translated to fit into the STM32 Family design.
There were other cosmetic changes that were made to add text debug messages to display using the USART unto a dumb terminal with “printf()” statements. Other changes included adding delays to make the visual screen changes easier to see by slowing things down.
***Note: To clarify, we won’t actually be using the Arduino sketches, but are listed here for documentation purposes to show where our test code originated.
buildtest.ino
/******************************************************************
This is an example for the Adafruit RA8875 Driver board for TFT displays
---------------> http://www.adafruit.com/products/1590
The RA8875 is a TFT driver for up to 800x480 dotclock'd displays
It is tested to work with displays in the Adafruit shop. Other displays
may need timing adjustments and are not guanteed to work.
Adafruit invests time and resources providing this open
source code, please support Adafruit and open-source hardware
by purchasing products from Adafruit!
Written by Limor Fried/Ladyada for Adafruit Industries.
BSD license, check license.txt for more information.
All text above must be included in any redistribution.
******************************************************************/
#include <SPI.h>
#include "Adafruit_GFX.h"
#include "Adafruit_RA8875.h"
// Library only supports hardware SPI at this time
// Connect SCLK to UNO Digital #13 (Hardware SPI clock)
// Connect MISO to UNO Digital #12 (Hardware SPI MISO)
// Connect MOSI to UNO Digital #11 (Hardware SPI MOSI)
#define RA8875_INT 3
#define RA8875_CS 10
#define RA8875_RESET 9
Adafruit_RA8875 tft = Adafruit_RA8875(RA8875_CS, RA8875_RESET);
uint16_t tx, ty;
void setup()
{
Serial.begin(9600);
Serial.println("RA8875 start");
/* Initialize the display using 'RA8875_480x80', 'RA8875_480x128', 'RA8875_480x272' or 'RA8875_800x480' */
if (!tft.begin(RA8875_480x272)) {
Serial.println("RA8875 Not Found!");
while (1);
}
Serial.println("Found RA8875");
tft.displayOn(true);
tft.GPIOX(true); // Enable TFT - display enable tied to GPIOX
tft.PWM1config(true, RA8875_PWM_CLK_DIV1024); // PWM output for backlight
tft.PWM1out(255);
// With hardware accelleration this is instant
tft.fillScreen(RA8875_WHITE);
// Play with PWM
for (uint8_t i=255; i!=0; i-=5 )
{
tft.PWM1out(i);
delay(10);
}
for (uint8_t i=0; i!=255; i+=5 )
{
tft.PWM1out(i);
delay(10);
}
tft.PWM1out(255);
tft.fillScreen(RA8875_RED);
delay(500);
tft.fillScreen(RA8875_YELLOW);
delay(500);
tft.fillScreen(RA8875_GREEN);
delay(500);
tft.fillScreen(RA8875_CYAN);
delay(500);
tft.fillScreen(RA8875_MAGENTA);
delay(500);
tft.fillScreen(RA8875_BLACK);
// Try some GFX acceleration!
tft.drawCircle(100, 100, 50, RA8875_BLACK);
tft.fillCircle(100, 100, 49, RA8875_GREEN);
tft.fillRect(11, 11, 398, 198, RA8875_BLUE);
tft.drawRect(10, 10, 400, 200, RA8875_GREEN);
tft.fillRoundRect(200, 10, 200, 100, 10, RA8875_RED);
tft.drawPixel(10,10,RA8875_BLACK);
tft.drawPixel(11,11,RA8875_BLACK);
tft.drawLine(10, 10, 200, 100, RA8875_RED);
tft.drawTriangle(200, 15, 250, 100, 150, 125, RA8875_BLACK);
tft.fillTriangle(200, 16, 249, 99, 151, 124, RA8875_YELLOW);
tft.drawEllipse(300, 100, 100, 40, RA8875_BLACK);
tft.fillEllipse(300, 100, 98, 38, RA8875_GREEN);
// Argument 5 (curvePart) is a 2-bit value to control each corner (select 0, 1, 2, or 3)
tft.drawCurve(50, 100, 80, 40, 2, RA8875_BLACK);
tft.fillCurve(50, 100, 78, 38, 2, RA8875_WHITE);
pinMode(RA8875_INT, INPUT);
digitalWrite(RA8875_INT, HIGH);
tft.touchEnable(true);
Serial.print("Status: "); Serial.println(tft.readStatus(), HEX);
Serial.println("Waiting for touch events ...");
}
void loop()
{
float xScale = 1024.0F/tft.width();
float yScale = 1024.0F/tft.height();
/* Wait around for touch events */
if (! digitalRead(RA8875_INT))
{
if (tft.touched())
{
Serial.print("Touch: ");
tft.touchRead(&tx, &ty);
Serial.print(tx); Serial.print(", "); Serial.println(ty);
/* Draw a circle */
tft.fillCircle((uint16_t)(tx/xScale), (uint16_t)(ty/yScale), 4, RA8875_WHITE);
}
}
}
textmode.ino
/******************************************************************
This is an example for the Adafruit RA8875 Driver board for TFT displays
---------------> http://www.adafruit.com/products/1590
The RA8875 is a TFT driver for up to 800x480 dotclock'd displays
It is tested to work with displays in the Adafruit shop. Other displays
may need timing adjustments and are not guanteed to work.
Adafruit invests time and resources providing this open
source code, please support Adafruit and open-source hardware
by purchasing products from Adafruit!
Written by Limor Fried/Ladyada for Adafruit Industries.
BSD license, check license.txt for more information.
All text above must be included in any redistribution.
******************************************************************/
#include <SPI.h>
#include "Adafruit_GFX.h"
#include "Adafruit_RA8875.h"
// Library only supports hardware SPI at this time
// Connect SCLK to UNO Digital #13 (Hardware SPI clock)
// Connect MISO to UNO Digital #12 (Hardware SPI MISO)
// Connect MOSI to UNO Digital #11 (Hardware SPI MOSI)
#define RA8875_INT 3
#define RA8875_CS 10
#define RA8875_RESET 9
Adafruit_RA8875 tft = Adafruit_RA8875(RA8875_CS, RA8875_RESET);
uint16_t tx, ty;
void setup()
{
Serial.begin(9600);
Serial.println("RA8875 start");
/* Initialize the display using 'RA8875_480x80', 'RA8875_480x128', 'RA8875_480x272' or 'RA8875_800x480' */
if (!tft.begin(RA8875_480x272)) {
Serial.println("RA8875 Not Found!");
while (1);
}
tft.displayOn(true);
tft.GPIOX(true); // Enable TFT - display enable tied to GPIOX
tft.PWM1config(true, RA8875_PWM_CLK_DIV1024); // PWM output for backlight
tft.PWM1out(255);
tft.fillScreen(RA8875_BLACK);
/* Switch to text mode */
tft.textMode();
tft.cursorBlink(32);
/* Set a solid for + bg color ... */
/* ... or a fore color plus a transparent background */
/* Set the cursor location (in pixels) */
tft.textSetCursor(10, 10);
/* Render some text! */
char string[15] = "Hello, World! ";
tft.textTransparent(RA8875_WHITE);
tft.textWrite(string);
tft.textColor(RA8875_WHITE, RA8875_RED);
tft.textWrite(string);
tft.textTransparent(RA8875_CYAN);
tft.textWrite(string);
tft.textTransparent(RA8875_GREEN);
tft.textWrite(string);
tft.textColor(RA8875_YELLOW, RA8875_CYAN);
tft.textWrite(string);
tft.textColor(RA8875_BLACK, RA8875_MAGENTA);
tft.textWrite(string);
/* Change the cursor location and color ... */
tft.textSetCursor(100, 100);
tft.textTransparent(RA8875_RED);
/* If necessary, enlarge the font */
tft.textEnlarge(1);
/* ... and render some more text! */
tft.textWrite(string);
tft.textSetCursor(100, 150);
tft.textEnlarge(2);
tft.textWrite(string);
}
void loop()
{
}
Exploring the Source Code
Project Structure

main.h
/* USER CODE BEGIN Header */
/**
******************************************************************************
* @file : main.h
* @brief : Header for main.c file.
* This file contains the common defines of the application.
******************************************************************************
* @attention
*
* Copyright (c) 2025 STMicroelectronics.
* All rights reserved.
*
* This software is licensed under terms that can be found in the LICENSE file
* in the root directory of this software component.
* If no LICENSE file comes with this software, it is provided AS-IS.
*
******************************************************************************
*/
/* USER CODE END Header */
/* Define to prevent recursive inclusion -------------------------------------*/
#ifndef __MAIN_H
#define __MAIN_H
#ifdef __cplusplus
extern "C" {
#endif
/* Includes ------------------------------------------------------------------*/
#include "stm32f4xx_hal.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
/* USER CODE END Includes */
/* Exported types ------------------------------------------------------------*/
/* USER CODE BEGIN ET */
/* USER CODE END ET */
/* Exported constants --------------------------------------------------------*/
/* USER CODE BEGIN EC */
/* USER CODE END EC */
/* Exported macro ------------------------------------------------------------*/
/* USER CODE BEGIN EM */
/* USER CODE END EM */
/* Exported functions prototypes ---------------------------------------------*/
/* USER CODE BEGIN EFP */
/* USER CODE END EFP */
/* Private defines -----------------------------------------------------------*/
/* USER CODE BEGIN Private defines */
void _Error_Handler(const char *, int);
#define Error_Handler() _Error_Handler((const char *)__FILE__, __LINE__)
#define REDIRECT_PRINTF
// Port and Pin #defines
#define RA8875_CS_Pin GPIO_PIN_1
#define RA8875_CS_GPIO_Port GPIOG
#define LCD_WAIT_Pin GPIO_PIN_10
#define LCD_WAIT_GPIO_Port GPIOG
#define LCD_RESET_Pin GPIO_PIN_11
#define LCD_RESET_GPIO_Port GPIOG
#define CS_DISABLE GPIO_PIN_SET
#define CS_ENABLE GPIO_PIN_RESET
/* USER CODE END Private defines */
#ifdef __cplusplus
}
#endif
#endif /* __MAIN_H */
main.c
We set the initial SPI baud rate low to make sure that we can communicate with the RA8875 chip and get a response on line 159. However, we change the SPI baud rate on line 173. The test functions are called on lines 183 and 185. These two calls essentially utilize the code taken from the two Arduino sketches mentioned previously.
/* USER CODE BEGIN Header */
/**
******************************************************************************
* @file : main.c
* @brief : Main program body
******************************************************************************
* @attention
*
* Copyright (c) 2025 STMicroelectronics.
* All rights reserved.
*
* This software is licensed under terms that can be found in the LICENSE file
* in the root directory of this software component.
* If no LICENSE file comes with this software, it is provided AS-IS.
*
******************************************************************************
*/
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "stdio.h"
#include "string.h"
#include "stdbool.h"
#include "entryPointCPP.hpp"
/* USER CODE END Includes */
/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
/* USER CODE END PTD */
/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
/* USER CODE END PD */
/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */
/* USER CODE END PM */
/* Private variables ---------------------------------------------------------*/
SPI_HandleTypeDef hspi1;
TIM_HandleTypeDef htim1;
UART_HandleTypeDef huart2;
/* USER CODE BEGIN PV */
/* USER CODE END PV */
/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_SPI1_Init(void);
static void MX_TIM1_Init(void);
static void MX_USART2_UART_Init(void);
/* USER CODE BEGIN PFP */
/* USER CODE END PFP */
/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
#ifdef REDIRECT_PRINTF
#define PUTCHAR_PROTOTYPE int __io_putchar(int ch)
#endif
#ifdef REDIRECT_PRINTF
/**
* @brief Retargets the C library printf function to the USART.
* @param None
* @retval None
*/
PUTCHAR_PROTOTYPE
{
/* Place your implementation of fputc here */
/* e.g. write a character to the USART2 and Loop until the end of transmission */
HAL_UART_Transmit(&huart2, (uint8_t *)&ch, 1, 0xFFFF);
return ch;
}
#endif
void delay(uint16_t time)
{
__HAL_TIM_SET_COUNTER(&htim1, 0);
while (__HAL_TIM_GET_COUNTER (&htim1) < time);
}
void MX_SPI1_ReInit(uint32_t scaler)
{
/* USER CODE BEGIN SPI1_Init 0 */
/* USER CODE END SPI1_Init 0 */
/* USER CODE BEGIN SPI1_Init 1 */
/* USER CODE END SPI1_Init 1 */
/* SPI1 parameter configuration*/
hspi1.Instance = SPI1;
hspi1.Init.Mode = SPI_MODE_MASTER;
hspi1.Init.Direction = SPI_DIRECTION_2LINES;
hspi1.Init.DataSize = SPI_DATASIZE_8BIT;
hspi1.Init.CLKPolarity = SPI_POLARITY_LOW;
hspi1.Init.CLKPhase = SPI_PHASE_1EDGE;
hspi1.Init.NSS = SPI_NSS_SOFT;
hspi1.Init.BaudRatePrescaler = scaler;
hspi1.Init.FirstBit = SPI_FIRSTBIT_MSB;
hspi1.Init.TIMode = SPI_TIMODE_DISABLE;
hspi1.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
hspi1.Init.CRCPolynomial = 10;
if (HAL_SPI_Init(&hspi1) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN SPI1_Init 2 */
/* USER CODE END SPI1_Init 2 */
}
/* USER CODE END 0 */
/**
* @brief The application entry point.
* @retval int
*/
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_SPI1_Init();
MX_TIM1_Init();
MX_USART2_UART_Init();
/* USER CODE BEGIN 2 */
printf("\x1b[2J\x1b[H"); // Clear the dumb terminal screen
printf("Starting Initialization Process\r\n");
HAL_TIM_Base_Start(&htim1);
initTest(&hspi1);
HAL_Delay(1000);
MX_SPI1_ReInit(SPI_BAUDRATEPRESCALER_32); // Increase the SPI clock rate
HAL_Delay(500);
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
printf("Start Tests\r\n");
testLCD(true);
HAL_Delay(5000);
testLCD(false);
printf("Done\r\n");
HAL_Delay(5000);
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
/**
* @brief System Clock Configuration
* @retval None
*/
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
/** Configure the main internal regulator output voltage
*/
__HAL_RCC_PWR_CLK_ENABLE();
__HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);
/** Initializes the RCC Oscillators according to the specified parameters
* in the RCC_OscInitTypeDef structure.
*/
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;
RCC_OscInitStruct.HSIState = RCC_HSI_ON;
RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSI;
RCC_OscInitStruct.PLL.PLLM = 8;
RCC_OscInitStruct.PLL.PLLN = 180;
RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;
RCC_OscInitStruct.PLL.PLLQ = 4;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
Error_Handler();
}
/** Activate the Over-Drive mode
*/
if (HAL_PWREx_EnableOverDrive() != HAL_OK)
{
Error_Handler();
}
/** Initializes the CPU, AHB and APB buses clocks
*/
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV4;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV2;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_5) != HAL_OK)
{
Error_Handler();
}
}
/**
* @brief SPI1 Initialization Function
* @param None
* @retval None
*/
static void MX_SPI1_Init(void)
{
/* USER CODE BEGIN SPI1_Init 0 */
/* USER CODE END SPI1_Init 0 */
/* USER CODE BEGIN SPI1_Init 1 */
/* USER CODE END SPI1_Init 1 */
/* SPI1 parameter configuration*/
hspi1.Instance = SPI1;
hspi1.Init.Mode = SPI_MODE_MASTER;
hspi1.Init.Direction = SPI_DIRECTION_2LINES;
hspi1.Init.DataSize = SPI_DATASIZE_8BIT;
hspi1.Init.CLKPolarity = SPI_POLARITY_LOW;
hspi1.Init.CLKPhase = SPI_PHASE_1EDGE;
hspi1.Init.NSS = SPI_NSS_SOFT;
hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_128;
hspi1.Init.FirstBit = SPI_FIRSTBIT_MSB;
hspi1.Init.TIMode = SPI_TIMODE_DISABLE;
hspi1.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
hspi1.Init.CRCPolynomial = 10;
if (HAL_SPI_Init(&hspi1) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN SPI1_Init 2 */
/* USER CODE END SPI1_Init 2 */
}
/**
* @brief TIM1 Initialization Function
* @param None
* @retval None
*/
static void MX_TIM1_Init(void)
{
/* USER CODE BEGIN TIM1_Init 0 */
/* USER CODE END TIM1_Init 0 */
TIM_ClockConfigTypeDef sClockSourceConfig = {0};
TIM_MasterConfigTypeDef sMasterConfig = {0};
/* USER CODE BEGIN TIM1_Init 1 */
/* USER CODE END TIM1_Init 1 */
htim1.Instance = TIM1;
htim1.Init.Prescaler = 180-1;
htim1.Init.CounterMode = TIM_COUNTERMODE_UP;
htim1.Init.Period = 65535;
htim1.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
htim1.Init.RepetitionCounter = 0;
htim1.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
if (HAL_TIM_Base_Init(&htim1) != HAL_OK)
{
Error_Handler();
}
sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
if (HAL_TIM_ConfigClockSource(&htim1, &sClockSourceConfig) != HAL_OK)
{
Error_Handler();
}
sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
if (HAL_TIMEx_MasterConfigSynchronization(&htim1, &sMasterConfig) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN TIM1_Init 2 */
/* USER CODE END TIM1_Init 2 */
}
/**
* @brief USART2 Initialization Function
* @param None
* @retval None
*/
static void MX_USART2_UART_Init(void)
{
/* USER CODE BEGIN USART2_Init 0 */
/* USER CODE END USART2_Init 0 */
/* USER CODE BEGIN USART2_Init 1 */
/* USER CODE END USART2_Init 1 */
huart2.Instance = USART2;
huart2.Init.BaudRate = 19200;
huart2.Init.WordLength = UART_WORDLENGTH_8B;
huart2.Init.StopBits = UART_STOPBITS_1;
huart2.Init.Parity = UART_PARITY_NONE;
huart2.Init.Mode = UART_MODE_TX_RX;
huart2.Init.HwFlowCtl = UART_HWCONTROL_NONE;
huart2.Init.OverSampling = UART_OVERSAMPLING_16;
if (HAL_UART_Init(&huart2) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN USART2_Init 2 */
/* USER CODE END USART2_Init 2 */
}
/**
* @brief GPIO Initialization Function
* @param None
* @retval None
*/
static void MX_GPIO_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
/* USER CODE BEGIN MX_GPIO_Init_1 */
/* USER CODE END MX_GPIO_Init_1 */
/* GPIO Ports Clock Enable */
__HAL_RCC_GPIOC_CLK_ENABLE();
__HAL_RCC_GPIOH_CLK_ENABLE();
__HAL_RCC_GPIOA_CLK_ENABLE();
__HAL_RCC_GPIOG_CLK_ENABLE();
__HAL_RCC_GPIOD_CLK_ENABLE();
__HAL_RCC_GPIOB_CLK_ENABLE();
/*Configure GPIO pin Output Level */
HAL_GPIO_WritePin(GPIOG, GPIO_PIN_1|GPIO_PIN_10|GPIO_PIN_11, GPIO_PIN_RESET);
/*Configure GPIO pin : PG1 */
GPIO_InitStruct.Pin = GPIO_PIN_1;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_PULLUP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOG, &GPIO_InitStruct);
/*Configure GPIO pins : PG10 PG11 */
GPIO_InitStruct.Pin = GPIO_PIN_10|GPIO_PIN_11;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOG, &GPIO_InitStruct);
/* USER CODE BEGIN MX_GPIO_Init_2 */
/* USER CODE END MX_GPIO_Init_2 */
}
/* USER CODE BEGIN 4 */
/**
* @brief This function is executed in case of error occurrence.
* @param file: The file name as string.
* @param line: The line in file as a number.
* @retval None
*/
void _Error_Handler(const char *file, int line)
{
/* USER CODE BEGIN Error_Handler_Debug */
__disable_irq();
#ifdef REDIRECT_PRINTF
char buf[80];
sprintf(buf, "Trapped in Error_Handler(). Called from: %s, line: %d\r\n", file, line);
printf(buf);
#endif
/* User can add his own implementation to report the HAL error return state */
while(1)
{
}
}
/* USER CODE END 4 */
#ifdef USE_FULL_ASSERT
/**
* @brief Reports the name of the source file and the source line number
* where the assert_param error has occurred.
* @param file: pointer to the source file name
* @param line: assert_param error line source number
* @retval None
*/
void assert_failed(uint8_t *file, uint32_t line)
{
/* USER CODE BEGIN 6 */
/* User can add his own implementation to report the file name and line number,
ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
/* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */
entryPointCPP.hpp
/*
* entryPointCPP.hpp
*
* Created on: Jan 26, 2025
* Author: johng
*/
#ifndef INC_ENTRYPOINTCPP_HPP_
#define INC_ENTRYPOINTCPP_HPP_
#include "main.h"
// Define all C function calls that will be called from main.c in the following extern "C" group
// Using extern "C" stops name mangling and allows "C" code to call C++ code
#ifdef __cplusplus
extern "C" {
#endif
void initTest(SPI_HandleTypeDef *halSPI);
void testLCD(bool buildTest);
#ifdef __cplusplus
}
#endif
#endif /* INC_ENTRYPOINTCPP_HPP_ */
entryPointCPP.cpp
This C++ source file is the how we are able to call C++ code from main.c It is the gateway to the C++ world in our code. This allows us to create C++ objects and call member functions in the instantiated objects. Line 25 is where we are creating the C++ object Adafruit_RA8875. Keep in mind that class Adafruit_RA8875 is a derived class off of class Adafruit_GFX, which is why you see this “class Adafruit_RA8875 : public Adafruit_GFX” in the declaration of class Adafruit_RA8875 in the include file Adafruit_RA8875.h on line 118. Even though the tests do not use the Adafruit_GFX library, we had to also convert it to compile with STM32 Micro Processors, because of the inheritance.
/*
* entryPointCPP.cpp
*
* Created on: Jan 26, 2025
* Author: johng
*/
#include "entryPointCPP.hpp"
#include "Adafruit_RA8875.h"
Adafruit_RA8875 *tft;
void initTest(SPI_HandleTypeDef *halSPI) {
HAL_GPIO_WritePin(RA8875_CS_GPIO_Port, RA8875_CS_Pin, CS_DISABLE);
HAL_GPIO_WritePin(LCD_RESET_GPIO_Port, LCD_RESET_Pin, GPIO_PIN_SET);
delay(100);
HAL_GPIO_WritePin(LCD_RESET_GPIO_Port, LCD_RESET_Pin, GPIO_PIN_RESET);
delay(100);
HAL_GPIO_WritePin(LCD_RESET_GPIO_Port, LCD_RESET_Pin, GPIO_PIN_SET);
HAL_GPIO_WritePin(LCD_WAIT_GPIO_Port, LCD_WAIT_Pin, GPIO_PIN_SET);
tft = new Adafruit_RA8875(halSPI);
int stat = tft->begin(RA8875_800x480);
if (stat) {
printf("RA8875 Found\r\n");
} else {
Error_Handler();
}
}
void testLCD(bool buildTest) {
if (buildTest) {
printf("================ Graphics Mode =============\r\n");
tft->displayOn(true);
HAL_Delay(1);
tft->GPIOX(true);
HAL_Delay(1);
tft->PWM1config(true, RA8875_PWM_CLK_DIV1024);
tft->PWM1out(255);
HAL_Delay(1);
tft->graphicsMode();
tft->fillScreen(RA8875_WHITE);
HAL_Delay(1000);
// Play with PWM
printf("Start: Play with PWM\r\n");
for (uint8_t i=255; i!=0; i-=5 )
{
tft->PWM1out(i);
delay(100);
}
for (uint8_t i=0; i!=255; i+=5 )
{
tft->PWM1out(i);
delay(100);
}
tft->PWM1out(255);
printf("END: Play with PWM\r\n");
HAL_Delay(2000);
printf("FillSreen Red\r\n");
tft->fillScreen(RA8875_RED);
HAL_Delay(1000);
printf("FillSreen Yellow\r\n");
tft->fillScreen(RA8875_YELLOW);
HAL_Delay(1000);
printf("FillSreen Green\r\n");
tft->fillScreen(RA8875_GREEN);
HAL_Delay(1000);
printf("FillSreen Cyan\r\n");
tft->fillScreen(RA8875_CYAN);
HAL_Delay(1000);
printf("FillSreen Magenta\r\n");
tft->fillScreen(RA8875_MAGENTA);
HAL_Delay(1000);
printf("FillSreen Black\r\n");
tft->fillScreen(RA8875_BLACK);
HAL_Delay(1000);
// Try some GFX acceleration!
printf("drawCircle\r\n");
tft->drawCircle(100, 100, 50, RA8875_BLACK);
printf("fillCircle\r\n");
tft->fillCircle(100, 100, 49, RA8875_GREEN);
HAL_Delay(1000);
printf("fillRect\r\n");
tft->fillRect(11, 11, 398, 198, RA8875_BLUE);
HAL_Delay(1000);
printf("drawRect\r\n");
tft->drawRect(10, 10, 400, 200, RA8875_GREEN);
HAL_Delay(1000);
printf("fillRoundRect\r\n");
tft->fillRoundRect(200, 10, 200, 100, 10, RA8875_RED);
HAL_Delay(1000);
printf("drawPixel\r\n");
tft->drawPixel(10,10,RA8875_BLACK);
HAL_Delay(1000);
printf("drawPixel\r\n");
tft->drawPixel(11,11,RA8875_BLACK);
HAL_Delay(1000);
printf("drawLine\r\n");
tft->drawLine(10, 10, 200, 100, RA8875_RED);
HAL_Delay(1000);
printf("drawTriangle\r\n");
tft->drawTriangle(200, 15, 250, 100, 150, 125, RA8875_BLACK);
HAL_Delay(1000);
printf("fillTriangle\r\n");
tft->fillTriangle(200, 16, 249, 99, 151, 124, RA8875_YELLOW);
HAL_Delay(1000);
printf("drawEllipse\r\n");
tft->drawEllipse(300, 100, 100, 40, RA8875_BLACK);
HAL_Delay(1000);
printf("fillEllipse\r\n");
tft->fillEllipse(300, 100, 98, 38, RA8875_GREEN);
HAL_Delay(1000);
// Argument 5 (curvePart) is a 2-bit value to control each corner (select 0, 1, 2, or 3)
printf("drawCurve\r\n");
tft->drawCurve(50, 100, 80, 40, 2, RA8875_BLACK);
HAL_Delay(1000);
printf("fillCurve\r\n");
tft->fillCurve(50, 100, 78, 38, 2, RA8875_WHITE);
} else {
printf("================ Text Mode =============\r\n");
tft->displayOn(true);
tft->GPIOX(true); // Enable TFT - display enable tied to GPIOX
tft->PWM1config(true, RA8875_PWM_CLK_DIV1024); // PWM output for backlight
tft->PWM1out(255);
printf("fillScreen\r\n");
tft->fillScreen(RA8875_BLACK);
/* Switch to text mode */
tft->textMode();
tft->cursorBlink(32);
/* Set a solid for + bg color ... */
/* ... or a fore color plus a transparent background */
/* Set the cursor location (in pixels) */
tft->textSetCursor(10, 10);
/* Render some text! */
printf("Render lots of Texts\r\n");
char string[80] = "Hello, World! ";
tft->textTransparent(RA8875_WHITE);
tft->textWrite(string);
HAL_Delay(500);
tft->textColor(RA8875_WHITE, RA8875_RED);
tft->textWrite(string);
HAL_Delay(500);
tft->textTransparent(RA8875_CYAN);
tft->textWrite(string);
HAL_Delay(500);
tft->textTransparent(RA8875_GREEN);
tft->textWrite(string);
HAL_Delay(500);
tft->textColor(RA8875_YELLOW, RA8875_CYAN);
tft->textWrite(string);
HAL_Delay(500);
tft->textColor(RA8875_BLACK, RA8875_MAGENTA);
tft->textWrite(string);
HAL_Delay(1000);
tft->textSetCursor(100, 100);
tft->textEnlarge(1);
tft->textTransparent(RA8875_YELLOW);
tft->textWrite(string);
tft->textSetCursor(100, 150);
tft->textEnlarge(2);
tft->textTransparent(RA8875_YELLOW);
tft->textWrite(string);
tft->textSetCursor(100, 200);
tft->textEnlarge(3);
tft->textTransparent(RA8875_YELLOW);
tft->textWrite(string);
tft->textSetCursor(100, 300);
tft->textEnlarge(0);
tft->textTransparent(RA8875_YELLOW);
tft->textWrite(string);
}
}
Adafruit_RA8875.h
The following code was migrated from an Arduino Library provided by Adafruit to work with STM32 Family Products.
/*
* Adafruit_RA8875.h
*
* Created on: Jan 19, 2025
* Author: johng
*/
#include "main.h"
#include <Adafruit_GFX.h>
#include "stdio.h"
#include "string.h"
#include "stdbool.h"
#ifndef _ADAFRUIT_RA8875_H
#define _ADAFRUIT_RA8875_H ///< File has been included
#ifdef __cplusplus
extern "C" {
#endif
extern void delay(uint16_t time);
#ifdef __cplusplus
}
#endif
#define USE_ADAFRUIT_GFX_FONTS
// Touchscreen Calibration and EEPROM Storage Defines
#define CFG_EEPROM_TOUCHSCREEN_CAL_AN 0 ///< EEPROM Storage Location
#define CFG_EEPROM_TOUCHSCREEN_CAL_BN 4 ///< EEPROM Storage Location
#define CFG_EEPROM_TOUCHSCREEN_CAL_CN 8 ///< EEPROM Storage Location
#define CFG_EEPROM_TOUCHSCREEN_CAL_DN 12 ///< EEPROM Storage Location
#define CFG_EEPROM_TOUCHSCREEN_CAL_EN 16 ///< EEPROM Storage Location
#define CFG_EEPROM_TOUCHSCREEN_CAL_FN 20 ///< EEPROM Storage Location
#define CFG_EEPROM_TOUCHSCREEN_CAL_DIVIDER 24 ///< EEPROM Storage Location
#define CFG_EEPROM_TOUCHSCREEN_CALIBRATED 28 ///< EEPROM Storage Location
/// @cond DISABLE
#if defined(EEPROM_SUPPORTED)
#if defined(__AVR_ATmega328P__)
/// @endcond
#define EEPROMSIZE 1024 ///< 1KB EEPROM
/// @cond DISABLE
#elif defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)
/// @endcond
#define EEPROMSIZE 4096 ///< 4KB EEPROM
/// @cond DISABLE
#else
/// @endcond
#define EEPROMSIZE 512 ///< 512 Byte EEPROM
/// @cond DISABLE
#endif
#endif
/// @endcond
// Sizes!
/**************************************************************************/
/*!
@enum RA8875sizes The Supported Screen Sizes
*/
/**************************************************************************/
enum RA8875sizes {
RA8875_480x80, /*!< 480x80 Pixel Display */
RA8875_480x128, /*!< 480x128 Pixel Display */
RA8875_480x272, /*!< 480x272 Pixel Display */
RA8875_800x480 /*!< 800x480 Pixel Display */
};
/**************************************************************************/
/*!
@struct Point
Calibration Point
@var Point::x
x-coordinate
@var Point::y
y-coordinate
*/
/**************************************************************************/
typedef struct Point {
int32_t x;
int32_t y;
} tsPoint_t; ///< Nameless struct variable!
/**************************************************************************/
/*!
@struct tsMatrix_t
Calibration Data Structure
@var tsMatrix_t::An
A Coefficient with the coarsest granularity
@var tsMatrix_t::Bn
B Coeffiecient
@var tsMatrix_t::Cn
C Coefficient
@var tsMatrix_t::Dn
D Coeffiecient
@var tsMatrix_t::En
E Coefficient
@var tsMatrix_t::Fn
F Coeffiecient with the finest granularity
@var tsMatrix_t::Divider
Divider for Coefficients
*/
/**************************************************************************/
typedef struct // Matrix
{
int32_t An, Bn, Cn, Dn, En, Fn, Divider;
} tsMatrix_t;
/**************************************************************************/
/*!
@brief Class that stores state and functions for interacting with
the RA8875 display controller.
*/
/**************************************************************************/
class Adafruit_RA8875 : public Adafruit_GFX {
public:
Adafruit_RA8875(SPI_HandleTypeDef *halSPI);
Adafruit_RA8875(uint8_t cs, uint8_t rst);
bool begin(enum RA8875sizes s);
void softReset(void);
void displayOn(bool on);
void sleep(bool sleep);
/* Text functions */
void textMode(void);
void textSetCursor(uint16_t x, uint16_t y);
void textColor(uint16_t foreColor, uint16_t bgColor);
void textTransparent(uint16_t foreColor);
void textEnlarge(uint8_t scale);
void textWrite(const char *buffer, uint16_t len = 0);
void cursorBlink(uint8_t rate);
/* Graphics functions */
void graphicsMode(void);
void setXY(uint16_t x, uint16_t y);
void pushPixels(uint32_t num, uint16_t p);
void fillRect(void);
/* Adafruit_GFX functions */
void drawPixel(int16_t x, int16_t y, uint16_t color);
void drawPixels(uint16_t *p, uint32_t count, int16_t x, int16_t y);
void drawFastVLine(int16_t x, int16_t y, int16_t h, uint16_t color);
void drawFastHLine(int16_t x, int16_t y, int16_t w, uint16_t color);
/* HW accelerated wrapper functions (override Adafruit_GFX prototypes) */
void fillScreen(uint16_t color);
void drawLine(int16_t x0, int16_t y0, int16_t x1, int16_t y1, uint16_t color);
void drawRect(int16_t x, int16_t y, int16_t w, int16_t h, uint16_t color);
void fillRect(int16_t x, int16_t y, int16_t w, int16_t h, uint16_t color);
void drawCircle(int16_t x, int16_t y, int16_t r, uint16_t color);
void fillCircle(int16_t x, int16_t y, int16_t r, uint16_t color);
void drawTriangle(int16_t x0, int16_t y0, int16_t x1, int16_t y1, int16_t x2,
int16_t y2, uint16_t color);
void fillTriangle(int16_t x0, int16_t y0, int16_t x1, int16_t y1, int16_t x2,
int16_t y2, uint16_t color);
void drawEllipse(int16_t xCenter, int16_t yCenter, int16_t longAxis,
int16_t shortAxis, uint16_t color);
void fillEllipse(int16_t xCenter, int16_t yCenter, int16_t longAxis,
int16_t shortAxis, uint16_t color);
void drawCurve(int16_t xCenter, int16_t yCenter, int16_t longAxis,
int16_t shortAxis, uint8_t curvePart, uint16_t color);
void fillCurve(int16_t xCenter, int16_t yCenter, int16_t longAxis,
int16_t shortAxis, uint8_t curvePart, uint16_t color);
void drawRoundRect(int16_t x, int16_t y, int16_t w, int16_t h, int16_t r,
uint16_t color);
void fillRoundRect(int16_t x, int16_t y, int16_t w, int16_t h, int16_t r,
uint16_t color);
/* Scroll */
void setScrollWindow(int16_t x, int16_t y, int16_t w, int16_t h,
uint8_t mode);
void scrollX(int16_t dist);
void scrollY(int16_t dist);
/* Backlight */
void GPIOX(bool on);
void PWM1config(bool on, uint8_t clock);
void PWM2config(bool on, uint8_t clock);
void PWM1out(uint8_t p);
void PWM2out(uint8_t p);
/* Touch screen */
void touchEnable(bool on);
bool touched(void);
bool touchRead(uint16_t *x, uint16_t *y);
/// @cond DISABLE
#if defined(EEPROM_SUPPORTED)
/// @endcond
/* Touch screen calibration persistence*/
uint32_t eepromReadS32(int location);
void eepromWriteS32(int location, int32_t value);
bool readCalibration(int location, tsMatrix_t *matrixPtr);
void writeCalibration(int location, tsMatrix_t *matrixPtr);
/// @cond DISABLE
#endif
/// @endcond
/* Low level access */
void writeReg(uint8_t reg, uint8_t val);
uint8_t readReg(uint8_t reg);
void writeData(uint8_t d);
uint8_t readData(void);
void writeCommand(uint8_t d);
uint8_t readStatus(void);
bool waitPoll(uint8_t r, uint8_t f);
uint16_t width(void);
uint16_t height(void);
void setRotation(int8_t rotation);
int8_t getRotation(void);
#ifndef USE_ADAFRUIT_GFX_FONTS
/**************************************************************************/
/*!
Alias of textWrite to Play nice with Arduino's Print class
@param b The string to write
@return The number of bytes written
*/
/**************************************************************************/
virtual size_t write(uint8_t b) {
textWrite((const char *)&b, 1);
return 1;
}
/**************************************************************************/
/*!
Alias of textWrite to Play nice with Arduino's Print class
@param buffer The buffer to write
@param size The size of the buffer
@return The number of bytes written
*/
/**************************************************************************/
virtual size_t write(const uint8_t *buffer, size_t size) {
textWrite((const char *)buffer, size);
return size;
}
#endif
private:
void PLLinit(void);
void initialize(void);
/* GFX Helper Functions */
void circleHelper(int16_t x, int16_t y, int16_t r, uint16_t color,
bool filled);
void rectHelper(int16_t x, int16_t y, int16_t w, int16_t h, uint16_t color,
bool filled);
void triangleHelper(int16_t x0, int16_t y0, int16_t x1, int16_t y1,
int16_t x2, int16_t y2, uint16_t color, bool filled);
void ellipseHelper(int16_t xCenter, int16_t yCenter, int16_t longAxis,
int16_t shortAxis, uint16_t color, bool filled);
void curveHelper(int16_t xCenter, int16_t yCenter, int16_t longAxis,
int16_t shortAxis, uint8_t curvePart, uint16_t color,
bool filled);
void roundRectHelper(int16_t x, int16_t y, int16_t w, int16_t h, int16_t r,
uint16_t color, bool filled);
/* Rotation Functions */
int16_t applyRotationX(int16_t x);
int16_t applyRotationY(int16_t y);
void swap(int16_t &x, int16_t &y) {
int16_t temp = x;
x = y;
y = temp;
}
uint8_t _cs, _rst;
uint16_t _width, _height;
uint8_t _textScale;
uint8_t _rotation;
uint8_t _voffset;
enum RA8875sizes _size;
};
// Colors (RGB565)
#define RA8875_BLACK 0x0000 ///< Black Color
#define RA8875_BLUE 0x001F ///< Blue Color
#define RA8875_RED 0xF800 ///< Red Color
#define RA8875_GREEN 0x07E0 ///< Green Color
#define RA8875_CYAN 0x07FF ///< Cyan Color
#define RA8875_MAGENTA 0xF81F ///< Magenta Color
#define RA8875_YELLOW 0xFFE0 ///< Yellow Color
#define RA8875_WHITE 0xFFFF ///< White Color
// Command/Data pins for SPI
#define RA8875_DATAWRITE 0x00 ///< See datasheet
#define RA8875_DATAREAD 0x40 ///< See datasheet
#define RA8875_CMDWRITE 0x80 ///< See datasheet
#define RA8875_CMDREAD 0xC0 ///< See datasheet
// Registers & bits
#define RA8875_PWRR 0x01 ///< See datasheet
#define RA8875_PWRR_DISPON 0x80 ///< See datasheet
#define RA8875_PWRR_DISPOFF 0x00 ///< See datasheet
#define RA8875_PWRR_SLEEP 0x02 ///< See datasheet
#define RA8875_PWRR_NORMAL 0x00 ///< See datasheet
#define RA8875_PWRR_SOFTRESET 0x01 ///< See datasheet
#define RA8875_MRWC 0x02 ///< See datasheet
#define RA8875_GPIOX 0xC7 ///< See datasheet
#define RA8875_PLLC1 0x88 ///< See datasheet
#define RA8875_PLLC1_PLLDIV2 0x80 ///< See datasheet
#define RA8875_PLLC1_PLLDIV1 0x00 ///< See datasheet
#define RA8875_PLLC2 0x89 ///< See datasheet
#define RA8875_PLLC2_DIV1 0x00 ///< See datasheet
#define RA8875_PLLC2_DIV2 0x01 ///< See datasheet
#define RA8875_PLLC2_DIV4 0x02 ///< See datasheet
#define RA8875_PLLC2_DIV8 0x03 ///< See datasheet
#define RA8875_PLLC2_DIV16 0x04 ///< See datasheet
#define RA8875_PLLC2_DIV32 0x05 ///< See datasheet
#define RA8875_PLLC2_DIV64 0x06 ///< See datasheet
#define RA8875_PLLC2_DIV128 0x07 ///< See datasheet
#define RA8875_SYSR 0x10 ///< See datasheet
#define RA8875_SYSR_8BPP 0x00 ///< See datasheet
#define RA8875_SYSR_16BPP 0x0C ///< See datasheet
#define RA8875_SYSR_MCU8 0x00 ///< See datasheet
#define RA8875_SYSR_MCU16 0x03 ///< See datasheet
#define RA8875_PCSR 0x04 ///< See datasheet
#define RA8875_PCSR_PDATR 0x00 ///< See datasheet
#define RA8875_PCSR_PDATL 0x80 ///< See datasheet
#define RA8875_PCSR_CLK 0x00 ///< See datasheet
#define RA8875_PCSR_2CLK 0x01 ///< See datasheet
#define RA8875_PCSR_4CLK 0x02 ///< See datasheet
#define RA8875_PCSR_8CLK 0x03 ///< See datasheet
#define RA8875_HDWR 0x14 ///< See datasheet
#define RA8875_HNDFTR 0x15 ///< See datasheet
#define RA8875_HNDFTR_DE_HIGH 0x00 ///< See datasheet
#define RA8875_HNDFTR_DE_LOW 0x80 ///< See datasheet
#define RA8875_HNDR 0x16 ///< See datasheet
#define RA8875_HSTR 0x17 ///< See datasheet
#define RA8875_HPWR 0x18 ///< See datasheet
#define RA8875_HPWR_LOW 0x00 ///< See datasheet
#define RA8875_HPWR_HIGH 0x80 ///< See datasheet
#define RA8875_VDHR0 0x19 ///< See datasheet
#define RA8875_VDHR1 0x1A ///< See datasheet
#define RA8875_VNDR0 0x1B ///< See datasheet
#define RA8875_VNDR1 0x1C ///< See datasheet
#define RA8875_VSTR0 0x1D ///< See datasheet
#define RA8875_VSTR1 0x1E ///< See datasheet
#define RA8875_VPWR 0x1F ///< See datasheet
#define RA8875_VPWR_LOW 0x00 ///< See datasheet
#define RA8875_VPWR_HIGH 0x80 ///< See datasheet
#define RA8875_HSAW0 0x30 ///< See datasheet
#define RA8875_HSAW1 0x31 ///< See datasheet
#define RA8875_VSAW0 0x32 ///< See datasheet
#define RA8875_VSAW1 0x33 ///< See datasheet
#define RA8875_HEAW0 0x34 ///< See datasheet
#define RA8875_HEAW1 0x35 ///< See datasheet
#define RA8875_VEAW0 0x36 ///< See datasheet
#define RA8875_VEAW1 0x37 ///< See datasheet
#define RA8875_MCLR 0x8E ///< See datasheet
#define RA8875_MCLR_START 0x80 ///< See datasheet
#define RA8875_MCLR_STOP 0x00 ///< See datasheet
#define RA8875_MCLR_READSTATUS 0x80 ///< See datasheet
#define RA8875_MCLR_FULL 0x00 ///< See datasheet
#define RA8875_MCLR_ACTIVE 0x40 ///< See datasheet
#define RA8875_DCR 0x90 ///< See datasheet
#define RA8875_DCR_LINESQUTRI_START 0x80 ///< See datasheet
#define RA8875_DCR_LINESQUTRI_STOP 0x00 ///< See datasheet
#define RA8875_DCR_LINESQUTRI_STATUS 0x80 ///< See datasheet
#define RA8875_DCR_CIRCLE_START 0x40 ///< See datasheet
#define RA8875_DCR_CIRCLE_STATUS 0x40 ///< See datasheet
#define RA8875_DCR_CIRCLE_STOP 0x00 ///< See datasheet
#define RA8875_DCR_FILL 0x20 ///< See datasheet
#define RA8875_DCR_NOFILL 0x00 ///< See datasheet
#define RA8875_DCR_DRAWLINE 0x00 ///< See datasheet
#define RA8875_DCR_DRAWTRIANGLE 0x01 ///< See datasheet
#define RA8875_DCR_DRAWSQUARE 0x10 ///< See datasheet
#define RA8875_ELLIPSE 0xA0 ///< See datasheet
#define RA8875_ELLIPSE_STATUS 0x80 ///< See datasheet
#define RA8875_MWCR0 0x40 ///< See datasheet
#define RA8875_MWCR0_GFXMODE 0x00 ///< See datasheet
#define RA8875_MWCR0_TXTMODE 0x80 ///< See datasheet
#define RA8875_MWCR0_CURSOR 0x40 ///< See datasheet
#define RA8875_MWCR0_BLINK 0x20 ///< See datasheet
#define RA8875_MWCR0_DIRMASK 0x0C ///< Bitmask for Write Direction
#define RA8875_MWCR0_LRTD 0x00 ///< Left->Right then Top->Down
#define RA8875_MWCR0_RLTD 0x04 ///< Right->Left then Top->Down
#define RA8875_MWCR0_TDLR 0x08 ///< Top->Down then Left->Right
#define RA8875_MWCR0_DTLR 0x0C ///< Down->Top then Left->Right
#define RA8875_BTCR 0x44 ///< See datasheet
#define RA8875_CURH0 0x46 ///< See datasheet
#define RA8875_CURH1 0x47 ///< See datasheet
#define RA8875_CURV0 0x48 ///< See datasheet
#define RA8875_CURV1 0x49 ///< See datasheet
#define RA8875_P1CR 0x8A ///< See datasheet
#define RA8875_P1CR_ENABLE 0x80 ///< See datasheet
#define RA8875_P1CR_DISABLE 0x00 ///< See datasheet
#define RA8875_P1CR_CLKOUT 0x10 ///< See datasheet
#define RA8875_P1CR_PWMOUT 0x00 ///< See datasheet
#define RA8875_P1DCR 0x8B ///< See datasheet
#define RA8875_P2CR 0x8C ///< See datasheet
#define RA8875_P2CR_ENABLE 0x80 ///< See datasheet
#define RA8875_P2CR_DISABLE 0x00 ///< See datasheet
#define RA8875_P2CR_CLKOUT 0x10 ///< See datasheet
#define RA8875_P2CR_PWMOUT 0x00 ///< See datasheet
#define RA8875_P2DCR 0x8D ///< See datasheet
#define RA8875_PWM_CLK_DIV1 0x00 ///< See datasheet
#define RA8875_PWM_CLK_DIV2 0x01 ///< See datasheet
#define RA8875_PWM_CLK_DIV4 0x02 ///< See datasheet
#define RA8875_PWM_CLK_DIV8 0x03 ///< See datasheet
#define RA8875_PWM_CLK_DIV16 0x04 ///< See datasheet
#define RA8875_PWM_CLK_DIV32 0x05 ///< See datasheet
#define RA8875_PWM_CLK_DIV64 0x06 ///< See datasheet
#define RA8875_PWM_CLK_DIV128 0x07 ///< See datasheet
#define RA8875_PWM_CLK_DIV256 0x08 ///< See datasheet
#define RA8875_PWM_CLK_DIV512 0x09 ///< See datasheet
#define RA8875_PWM_CLK_DIV1024 0x0A ///< See datasheet
#define RA8875_PWM_CLK_DIV2048 0x0B ///< See datasheet
#define RA8875_PWM_CLK_DIV4096 0x0C ///< See datasheet
#define RA8875_PWM_CLK_DIV8192 0x0D ///< See datasheet
#define RA8875_PWM_CLK_DIV16384 0x0E ///< See datasheet
#define RA8875_PWM_CLK_DIV32768 0x0F ///< See datasheet
#define RA8875_TPCR0 0x70 ///< See datasheet
#define RA8875_TPCR0_ENABLE 0x80 ///< See datasheet
#define RA8875_TPCR0_DISABLE 0x00 ///< See datasheet
#define RA8875_TPCR0_WAIT_512CLK 0x00 ///< See datasheet
#define RA8875_TPCR0_WAIT_1024CLK 0x10 ///< See datasheet
#define RA8875_TPCR0_WAIT_2048CLK 0x20 ///< See datasheet
#define RA8875_TPCR0_WAIT_4096CLK 0x30 ///< See datasheet
#define RA8875_TPCR0_WAIT_8192CLK 0x40 ///< See datasheet
#define RA8875_TPCR0_WAIT_16384CLK 0x50 ///< See datasheet
#define RA8875_TPCR0_WAIT_32768CLK 0x60 ///< See datasheet
#define RA8875_TPCR0_WAIT_65536CLK 0x70 ///< See datasheet
#define RA8875_TPCR0_WAKEENABLE 0x08 ///< See datasheet
#define RA8875_TPCR0_WAKEDISABLE 0x00 ///< See datasheet
#define RA8875_TPCR0_ADCCLK_DIV1 0x00 ///< See datasheet
#define RA8875_TPCR0_ADCCLK_DIV2 0x01 ///< See datasheet
#define RA8875_TPCR0_ADCCLK_DIV4 0x02 ///< See datasheet
#define RA8875_TPCR0_ADCCLK_DIV8 0x03 ///< See datasheet
#define RA8875_TPCR0_ADCCLK_DIV16 0x04 ///< See datasheet
#define RA8875_TPCR0_ADCCLK_DIV32 0x05 ///< See datasheet
#define RA8875_TPCR0_ADCCLK_DIV64 0x06 ///< See datasheet
#define RA8875_TPCR0_ADCCLK_DIV128 0x07 ///< See datasheet
#define RA8875_TPCR1 0x71 ///< See datasheet
#define RA8875_TPCR1_AUTO 0x00 ///< See datasheet
#define RA8875_TPCR1_MANUAL 0x40 ///< See datasheet
#define RA8875_TPCR1_VREFINT 0x00 ///< See datasheet
#define RA8875_TPCR1_VREFEXT 0x20 ///< See datasheet
#define RA8875_TPCR1_DEBOUNCE 0x04 ///< See datasheet
#define RA8875_TPCR1_NODEBOUNCE 0x00 ///< See datasheet
#define RA8875_TPCR1_IDLE 0x00 ///< See datasheet
#define RA8875_TPCR1_WAIT 0x01 ///< See datasheet
#define RA8875_TPCR1_LATCHX 0x02 ///< See datasheet
#define RA8875_TPCR1_LATCHY 0x03 ///< See datasheet
#define RA8875_TPXH 0x72 ///< See datasheet
#define RA8875_TPYH 0x73 ///< See datasheet
#define RA8875_TPXYL 0x74 ///< See datasheet
#define RA8875_INTC1 0xF0 ///< See datasheet
#define RA8875_INTC1_KEY 0x10 ///< See datasheet
#define RA8875_INTC1_DMA 0x08 ///< See datasheet
#define RA8875_INTC1_TP 0x04 ///< See datasheet
#define RA8875_INTC1_BTE 0x02 ///< See datasheet
#define RA8875_INTC2 0xF1 ///< See datasheet
#define RA8875_INTC2_KEY 0x10 ///< See datasheet
#define RA8875_INTC2_DMA 0x08 ///< See datasheet
#define RA8875_INTC2_TP 0x04 ///< See datasheet
#define RA8875_INTC2_BTE 0x02 ///< See datasheet
#define RA8875_SCROLL_BOTH 0x00 ///< See datasheet
#define RA8875_SCROLL_LAYER1 0x40 ///< See datasheet
#define RA8875_SCROLL_LAYER2 0x80 ///< See datasheet
#define RA8875_SCROLL_BUFFER 0xC0 ///< See datasheet
#endif
Adafruit_RA8875.cpp
The following code was migrated from an Arduino Library provided by Adafruit to work with STM32 Family Products.
/*
* Adafruit_RA8875.cpp
*
* Created on: Jan 19, 2025
* Author: johng
*/
#include "stdio.h"
#include "string.h"
#include "stdbool.h"
#include "Adafruit_RA8875.h"
SPI_HandleTypeDef *spiHandle;
uint8_t _cs, _rst;
uint16_t _width, _height;
uint8_t _textScale;
uint8_t _rotation;
uint8_t _voffset;
enum RA8875sizes _size;
void swap(int16_t x, int16_t y) {
int16_t temp = x;
x = y;
y = temp;
}
void Adafruit_RA8875(SPI_HandleTypeDef *halSPI) {
spiHandle = halSPI;
}
bool begin(enum RA8875sizes s) {
_size = s;
if (_size == RA8875_480x80) {
_width = 480;
_height = 80;
} else if (_size == RA8875_480x128) {
_width = 480;
_height = 128;
} else if (_size == RA8875_480x272) {
_width = 480;
_height = 272;
} else if (_size == RA8875_800x480) {
_width = 800;
_height = 480;
} else {
return false;
}
_rotation = 0;
HAL_GPIO_WritePin(RA8875_CS_GPIO_Port, RA8875_CS_Pin, CS_ENABLE);
delayMS(100);
HAL_GPIO_WritePin(RA8875_CS_GPIO_Port, RA8875_CS_Pin, CS_DISABLE);
delayMS(100);
HAL_GPIO_WritePin(LCD_RESET_GPIO_Port, LCD_RESET_Pin, RESET);
delayMS(100);
HAL_GPIO_WritePin(LCD_RESET_GPIO_Port, LCD_RESET_Pin, SET);
uint8_t x = readReg(0);
// Serial.print("x = 0x"); Serial.println(x,HEX);
if (x != 0x75) {
printf("x=0x%2x\r\n", x);
return false;
}
initialize();
return true;
}
/************************* Text Mode ***********************************/
/**************************************************************************/
/*!
Sets the display in text mode (as opposed to graphics mode)
*/
/**************************************************************************/
void textMode(void) {
/* Set text mode */
writeCommand(RA8875_MWCR0);
uint8_t temp = readData();
temp |= RA8875_MWCR0_TXTMODE; // Set bit 7
writeData(temp);
/* Select the internal (ROM) font */
writeCommand(0x21);
temp = readData();
temp &= ~((1 << 7) | (1 << 5)); // Clear bits 7 and 5
writeData(temp);
}
/**************************************************************************/
/*!
Sets the display in text mode (as opposed to graphics mode)
@param x The x position of the cursor (in pixels, 0..1023)
@param y The y position of the cursor (in pixels, 0..511)
*/
/**************************************************************************/
void textSetCursor(uint16_t x, uint16_t y) {
x = applyRotationX(x);
y = applyRotationY(y);
/* Set cursor location */
writeCommand(0x2A);
writeData(x & 0xFF);
writeCommand(0x2B);
writeData(x >> 8);
writeCommand(0x2C);
writeData(y & 0xFF);
writeCommand(0x2D);
writeData(y >> 8);
}
/**************************************************************************/
/*!
Sets the fore and background color when rendering text
@param foreColor The RGB565 color to use when rendering the text
@param bgColor The RGB565 colot to use for the background
*/
/**************************************************************************/
void textColor(uint16_t foreColor, uint16_t bgColor) {
/* Set Fore Color */
writeCommand(0x63);
writeData((foreColor & 0xf800) >> 11);
writeCommand(0x64);
writeData((foreColor & 0x07e0) >> 5);
writeCommand(0x65);
writeData((foreColor & 0x001f));
/* Set Background Color */
writeCommand(0x60);
writeData((bgColor & 0xf800) >> 11);
writeCommand(0x61);
writeData((bgColor & 0x07e0) >> 5);
writeCommand(0x62);
writeData((bgColor & 0x001f));
/* Clear transparency flag */
writeCommand(0x22);
uint8_t temp = readData();
temp &= ~(1 << 6); // Clear bit 6
writeData(temp);
}
/**************************************************************************/
/*!
Sets the fore color when rendering text with a transparent bg
@param foreColor The RGB565 color to use when rendering the text
*/
/**************************************************************************/
void textTransparent(uint16_t foreColor) {
/* Set Fore Color */
writeCommand(0x63);
writeData((foreColor & 0xf800) >> 11);
writeCommand(0x64);
writeData((foreColor & 0x07e0) >> 5);
writeCommand(0x65);
writeData((foreColor & 0x001f));
/* Set transparency flag */
writeCommand(0x22);
uint8_t temp = readData();
temp |= (1 << 6); // Set bit 6
writeData(temp);
}
/**************************************************************************/
/*!
Sets the text enlarge settings, using one of the following values:
0 = 1x zoom
1 = 2x zoom
2 = 3x zoom
3 = 4x zoom
@param scale The zoom factor (0..3 for 1-4x zoom)
*/
/**************************************************************************/
void textEnlarge(uint8_t scale) {
if (scale > 3)
scale = 3; // highest setting is 3
/* Set font size flags */
writeCommand(0x22);
uint8_t temp = readData();
temp &= ~(0xF); // Clears bits 0..3
temp |= scale << 2;
temp |= scale;
writeData(temp);
_textScale = scale;
}
/**************************************************************************/
/*!
Enable Cursor Visibility and Blink
Here we set bits 6 and 5 in 40h
As well as the set the blink rate in 44h
The rate is 0 through max 255
the lower the number the faster it blinks (00h is 1 frame time,
FFh is 256 Frames time.
Blink Time (sec) = BTCR[44h]x(1/Frame_rate)
@param rate The frame rate to blink
*/
/**************************************************************************/
void cursorBlink(uint8_t rate) {
writeCommand(RA8875_MWCR0);
uint8_t temp = readData();
temp |= RA8875_MWCR0_CURSOR;
writeData(temp);
writeCommand(RA8875_MWCR0);
temp = readData();
temp |= RA8875_MWCR0_BLINK;
writeData(temp);
if (rate > 255)
rate = 255;
writeCommand(RA8875_BTCR);
writeData(rate);
}
/**************************************************************************/
/*!
Renders some text on the screen when in text mode
@param buffer The buffer containing the characters to render
@param len The size of the buffer in bytes
*/
/**************************************************************************/
void textWrite(const char *buffer) {
textWrite2(buffer, strlen(buffer));
}
/**************************************************************************/
/*!
Renders some text on the screen when in text mode
@param buffer The buffer containing the characters to render
@param len The size of the buffer in bytes
*/
/**************************************************************************/
void textWrite2(const char *buffer, uint16_t len) {
if (len == 0)
len = strlen(buffer);
writeCommand(RA8875_MRWC);
for (uint16_t i = 0; i < len; i++) {
writeData(buffer[i]);
/// @cond DISABLE
#if !defined(__arm__)
/// @endcond
// This delay is needed with textEnlarge(1) because
// Teensy 3.X is much faster than Arduino Uno
if (_textScale > 0)
delay(1);
/// @cond DISABLE
#else
/// @endcond
// For others, delay starting with textEnlarge(2)
if (_textScale > 0)
delay(500);
/// @cond DISABLE
#endif
/// @endcond
}
}
/************************* Graphics ***********************************/
/**************************************************************************/
/*!
Sets the display in graphics mode (as opposed to text mode)
*/
/**************************************************************************/
void graphicsMode(void) {
writeCommand(RA8875_MWCR0);
uint8_t temp = readData();
temp &= ~RA8875_MWCR0_TXTMODE; // bit #7
writeData(temp);
}
/**************************************************************************/
/*!
Draws a single pixel at the specified location
@param x The 0-based x location
@param y The 0-base y location
@param color The RGB565 color to use when drawing the pixel
*/
/**************************************************************************/
void drawPixel(int16_t x, int16_t y, uint16_t color) {
x = applyRotationX(x);
y = applyRotationY(y);
writeReg(RA8875_CURH0, x);
writeReg(RA8875_CURH1, x >> 8);
writeReg(RA8875_CURV0, y);
writeReg(RA8875_CURV1, y >> 8);
writeCommand(RA8875_MRWC);
HAL_StatusTypeDef halStatus = HAL_OK;
uint8_t pData = RA8875_DATAWRITE;
HAL_GPIO_WritePin(RA8875_CS_GPIO_Port, RA8875_CS_Pin, CS_ENABLE);
halStatus = HAL_SPI_Transmit(spiHandle, &pData, sizeof(uint8_t), 100);
if (halStatus != HAL_OK) {
Error_Handler();
}
while (!HAL_GPIO_ReadPin(LCD_WAIT_GPIO_Port, LCD_WAIT_Pin)) {
delay(1);
}
pData = color >> 8;
halStatus = HAL_SPI_Transmit(spiHandle, &pData, sizeof(uint8_t), 100);
if (halStatus != HAL_OK) {
Error_Handler();
}
while (!HAL_GPIO_ReadPin(LCD_WAIT_GPIO_Port, LCD_WAIT_Pin)) {
delay(1);
}
pData = color;
halStatus = HAL_SPI_Transmit(spiHandle, &pData, sizeof(uint8_t), 100);
if (halStatus != HAL_OK) {
Error_Handler();
}
HAL_GPIO_WritePin(RA8875_CS_GPIO_Port, RA8875_CS_Pin, CS_DISABLE);
}
/**************************************************************************/
/*!
Draws a series of pixels at the specified location without the overhead
@param p An array of RGB565 color pixels
@param num The number of the pixels to draw
@param x The 0-based x location
@param y The 0-base y location
*/
/**************************************************************************/
void drawPixels(uint16_t *p, uint32_t num, int16_t x, int16_t y) {
x = applyRotationX(x);
y = applyRotationY(y);
writeReg(RA8875_CURH0, x);
writeReg(RA8875_CURH1, x >> 8);
writeReg(RA8875_CURV0, y);
writeReg(RA8875_CURV1, y >> 8);
uint8_t dir = RA8875_MWCR0_LRTD;
if (_rotation == 2) {
dir = RA8875_MWCR0_RLTD;
}
writeReg(RA8875_MWCR0, (readReg(RA8875_MWCR0) & ~RA8875_MWCR0_DIRMASK) | dir);
writeCommand(RA8875_MRWC);
HAL_StatusTypeDef halStatus = HAL_OK;
while (!HAL_GPIO_ReadPin(LCD_WAIT_GPIO_Port, LCD_WAIT_Pin)) {
delay(1);
}
uint8_t pData = RA8875_DATAWRITE;
HAL_GPIO_WritePin(RA8875_CS_GPIO_Port, RA8875_CS_Pin, CS_ENABLE);
halStatus = HAL_SPI_Transmit(spiHandle, &pData, sizeof(uint8_t), 100);
if (halStatus != HAL_OK) {
Error_Handler();
}
while (num--) {
while (!HAL_GPIO_ReadPin(LCD_WAIT_GPIO_Port, LCD_WAIT_Pin)) {
delay(1);
}
pData = *p++;
halStatus = HAL_SPI_Transmit(spiHandle, &pData, sizeof(uint8_t), 100);
if (halStatus != HAL_OK) {
Error_Handler();
}
}
HAL_GPIO_WritePin(RA8875_CS_GPIO_Port, RA8875_CS_Pin, CS_DISABLE);
}
/**************************************************************************/
/*!
Draws a HW accelerated line on the display
@param x0 The 0-based starting x location
@param y0 The 0-base starting y location
@param x1 The 0-based ending x location
@param y1 The 0-base ending y location
@param color The RGB565 color to use when drawing the pixel
*/
/**************************************************************************/
void drawLine(int16_t x0, int16_t y0, int16_t x1, int16_t y1,
uint16_t color) {
x0 = applyRotationX(x0);
y0 = applyRotationY(y0);
x1 = applyRotationX(x1);
y1 = applyRotationY(y1);
/* Set X */
writeCommand(0x91);
writeData(x0);
writeCommand(0x92);
writeData(x0 >> 8);
/* Set Y */
writeCommand(0x93);
writeData(y0);
writeCommand(0x94);
writeData(y0 >> 8);
/* Set X1 */
writeCommand(0x95);
writeData(x1);
writeCommand(0x96);
writeData((x1) >> 8);
/* Set Y1 */
writeCommand(0x97);
writeData(y1);
writeCommand(0x98);
writeData((y1) >> 8);
/* Set Color */
writeCommand(0x63);
writeData((color & 0xf800) >> 11);
writeCommand(0x64);
writeData((color & 0x07e0) >> 5);
writeCommand(0x65);
writeData((color & 0x001f));
/* Draw! */
writeCommand(RA8875_DCR);
writeData(0x80);
/* Wait for the command to finish */
waitPoll(RA8875_DCR, RA8875_DCR_LINESQUTRI_STATUS);
}
/**************************************************************************/
/*!
Draw a vertical line
@param x The X position
@param y The Y position
@param h Height
@param color The color
*/
/**************************************************************************/
void drawFastVLine(int16_t x, int16_t y, int16_t h,
uint16_t color) {
drawLine(x, y, x, y + h, color);
}
/**************************************************************************/
/*!
Draw a horizontal line
@param x The X position
@param y The Y position
@param w Width
@param color The color
*/
/**************************************************************************/
void drawFastHLine(int16_t x, int16_t y, int16_t w,
uint16_t color) {
drawLine(x, y, x + w, y, color);
}
/**************************************************************************/
/*!
Draws a HW accelerated rectangle on the display
@param x The 0-based x location of the top-right corner
@param y The 0-based y location of the top-right corner
@param w The rectangle width
@param h The rectangle height
@param color The RGB565 color to use when drawing the pixel
*/
/**************************************************************************/
void drawRect(int16_t x, int16_t y, int16_t w, int16_t h,
uint16_t color) {
rectHelper(x, y, x + w - 1, y + h - 1, color, false);
}
/**************************************************************************/
/*!
Fill the screen with the current color
*/
/**************************************************************************/
void fillRect(void) {
writeCommand(RA8875_DCR);
writeData(RA8875_DCR_LINESQUTRI_STOP | RA8875_DCR_DRAWSQUARE);
writeData(RA8875_DCR_LINESQUTRI_START | RA8875_DCR_FILL |
RA8875_DCR_DRAWSQUARE);
}
/**************************************************************************/
/*!
Draws a HW accelerated filled rectangle on the display
@param x The 0-based x location of the top-right corner
@param y The 0-based y location of the top-right corner
@param w The rectangle width
@param h The rectangle height
@param color The RGB565 color to use when drawing the pixel
*/
/**************************************************************************/
void fillRectArea(int16_t x, int16_t y, int16_t w, int16_t h, uint16_t color) {
rectHelper(x, y, x + w - 1, y + h - 1, color, true);
}
/**************************************************************************/
/*!
Fills the screen with the spefied RGB565 color
@param color The RGB565 color to use when drawing the pixel
*/
/**************************************************************************/
void fillScreen(uint16_t color) {
rectHelper(0, 0, _width - 1, _height - 1, color, true);
}
/**************************************************************************/
/*!
Draws a HW accelerated circle on the display
@param x The 0-based x location of the center of the circle
@param y The 0-based y location of the center of the circle
@param r The circle's radius
@param color The RGB565 color to use when drawing the pixel
*/
/**************************************************************************/
void drawCircle(int16_t x, int16_t y, int16_t r, uint16_t color) {
circleHelper(x, y, r, color, false);
}
/**************************************************************************/
/*!
Draws a HW accelerated filled circle on the display
@param x The 0-based x location of the center of the circle
@param y The 0-based y location of the center of the circle
@param r The circle's radius
@param color The RGB565 color to use when drawing the pixel
*/
/**************************************************************************/
void fillCircle(int16_t x, int16_t y, int16_t r, uint16_t color) {
circleHelper(x, y, r, color, true);
}
/**************************************************************************/
/*!
Helper function for higher level rectangle drawing code
*/
/**************************************************************************/
void rectHelper(int16_t x, int16_t y, int16_t w, int16_t h,
uint16_t color, bool filled) {
x = applyRotationX(x);
y = applyRotationY(y);
w = applyRotationX(w);
h = applyRotationY(h);
/* Set X */
writeCommand(0x91);
writeData(x);
writeCommand(0x92);
writeData(x >> 8);
/* Set Y */
writeCommand(0x93);
writeData(y);
writeCommand(0x94);
writeData(y >> 8);
/* Set X1 */
writeCommand(0x95);
writeData(w);
writeCommand(0x96);
writeData((w) >> 8);
/* Set Y1 */
writeCommand(0x97);
writeData(h);
writeCommand(0x98);
writeData((h) >> 8);
/* Set Color */
writeCommand(0x63);
writeData((color & 0xf800) >> 11);
writeCommand(0x64);
writeData((color & 0x07e0) >> 5);
writeCommand(0x65);
writeData((color & 0x001f));
/* Draw! */
writeCommand(RA8875_DCR);
if (filled) {
writeData(0xB0);
} else {
writeData(0x90);
}
/* Wait for the command to finish */
waitPoll(RA8875_DCR, RA8875_DCR_LINESQUTRI_STATUS);
}
/**************************************************************************/
/*!
Helper function for higher level triangle drawing code
*/
/**************************************************************************/
void triangleHelper(int16_t x0, int16_t y0, int16_t x1,
int16_t y1, int16_t x2, int16_t y2,
uint16_t color, bool filled) {
x0 = applyRotationX(x0);
y0 = applyRotationY(y0);
x1 = applyRotationX(x1);
y1 = applyRotationY(y1);
x2 = applyRotationX(x2);
y2 = applyRotationY(y2);
/* Set Point 0 */
writeCommand(0x91);
writeData(x0);
writeCommand(0x92);
writeData(x0 >> 8);
writeCommand(0x93);
writeData(y0);
writeCommand(0x94);
writeData(y0 >> 8);
/* Set Point 1 */
writeCommand(0x95);
writeData(x1);
writeCommand(0x96);
writeData(x1 >> 8);
writeCommand(0x97);
writeData(y1);
writeCommand(0x98);
writeData(y1 >> 8);
/* Set Point 2 */
writeCommand(0xA9);
writeData(x2);
writeCommand(0xAA);
writeData(x2 >> 8);
writeCommand(0xAB);
writeData(y2);
writeCommand(0xAC);
writeData(y2 >> 8);
/* Set Color */
writeCommand(0x63);
writeData((color & 0xf800) >> 11);
writeCommand(0x64);
writeData((color & 0x07e0) >> 5);
writeCommand(0x65);
writeData((color & 0x001f));
/* Draw! */
writeCommand(RA8875_DCR);
if (filled) {
writeData(0xA1);
} else {
writeData(0x81);
}
/* Wait for the command to finish */
waitPoll(RA8875_DCR, RA8875_DCR_LINESQUTRI_STATUS);
}
/**************************************************************************/
/*!
Helper function for higher level ellipse drawing code
*/
/**************************************************************************/
void ellipseHelper(int16_t xCenter, int16_t yCenter,
int16_t longAxis, int16_t shortAxis,
uint16_t color, bool filled) {
xCenter = applyRotationX(xCenter);
yCenter = applyRotationY(yCenter);
/* Set Center Point */
writeCommand(0xA5);
writeData(xCenter);
writeCommand(0xA6);
writeData(xCenter >> 8);
writeCommand(0xA7);
writeData(yCenter);
writeCommand(0xA8);
writeData(yCenter >> 8);
/* Set Long and Short Axis */
writeCommand(0xA1);
writeData(longAxis);
writeCommand(0xA2);
writeData(longAxis >> 8);
writeCommand(0xA3);
writeData(shortAxis);
writeCommand(0xA4);
writeData(shortAxis >> 8);
/* Set Color */
writeCommand(0x63);
writeData((color & 0xf800) >> 11);
writeCommand(0x64);
writeData((color & 0x07e0) >> 5);
writeCommand(0x65);
writeData((color & 0x001f));
/* Draw! */
writeCommand(0xA0);
if (filled) {
writeData(0xC0);
} else {
writeData(0x80);
}
/* Wait for the command to finish */
waitPoll(RA8875_ELLIPSE, RA8875_ELLIPSE_STATUS);
}
/**************************************************************************/
/*!
Helper function for higher level curve drawing code
*/
/**************************************************************************/
void curveHelper(int16_t xCenter, int16_t yCenter,
int16_t longAxis, int16_t shortAxis,
uint8_t curvePart, uint16_t color,
bool filled) {
xCenter = applyRotationX(xCenter);
yCenter = applyRotationY(yCenter);
curvePart = (curvePart + _rotation) % 4;
/* Set Center Point */
writeCommand(0xA5);
writeData(xCenter);
writeCommand(0xA6);
writeData(xCenter >> 8);
writeCommand(0xA7);
writeData(yCenter);
writeCommand(0xA8);
writeData(yCenter >> 8);
/* Set Long and Short Axis */
writeCommand(0xA1);
writeData(longAxis);
writeCommand(0xA2);
writeData(longAxis >> 8);
writeCommand(0xA3);
writeData(shortAxis);
writeCommand(0xA4);
writeData(shortAxis >> 8);
/* Set Color */
writeCommand(0x63);
writeData((color & 0xf800) >> 11);
writeCommand(0x64);
writeData((color & 0x07e0) >> 5);
writeCommand(0x65);
writeData((color & 0x001f));
/* Draw! */
writeCommand(0xA0);
if (filled) {
writeData(0xD0 | (curvePart & 0x03));
} else {
writeData(0x90 | (curvePart & 0x03));
}
/* Wait for the command to finish */
waitPoll(RA8875_ELLIPSE, RA8875_ELLIPSE_STATUS);
}
/**************************************************************************/
/*!
Helper function for higher level rounded rectangle drawing code
*/
/**************************************************************************/
void roundRectHelper(int16_t x, int16_t y, int16_t w, int16_t h, int16_t r, uint16_t color, bool filled) {
x = applyRotationX(x);
y = applyRotationY(y);
w = applyRotationX(w);
h = applyRotationY(h);
if (x > w)
swap(x, w);
if (y > h)
swap(y, h);
/* Set X */
writeCommand(0x91);
writeData(x);
writeCommand(0x92);
writeData(x >> 8);
/* Set Y */
writeCommand(0x93);
writeData(y);
writeCommand(0x94);
writeData(y >> 8);
/* Set X1 */
writeCommand(0x95);
writeData(w);
writeCommand(0x96);
writeData((w) >> 8);
/* Set Y1 */
writeCommand(0x97);
writeData(h);
writeCommand(0x98);
writeData((h) >> 8);
writeCommand(0xA1);
writeData(r);
writeCommand(0xA2);
writeData((r) >> 8);
writeCommand(0xA3);
writeData(r);
writeCommand(0xA4);
writeData((r) >> 8);
/* Set Color */
writeCommand(0x63);
writeData((color & 0xf800) >> 11);
writeCommand(0x64);
writeData((color & 0x07e0) >> 5);
writeCommand(0x65);
writeData((color & 0x001f));
/* Draw! */
writeCommand(RA8875_ELLIPSE);
if (filled) {
writeData(0xE0);
} else {
writeData(0xA0);
}
/* Wait for the command to finish */
waitPoll(RA8875_ELLIPSE, RA8875_DCR_LINESQUTRI_STATUS);
}
/**************************************************************************/
/*!
Set the scroll window
@param x X position of the scroll window
@param y Y position of the scroll window
@param w Width of the Scroll Window
@param h Height of the Scroll window
@param mode Layer to Scroll
*/
/**************************************************************************/
void setScrollWindow(int16_t x, int16_t y, int16_t w,
int16_t h, uint8_t mode) {
// Horizontal Start point of Scroll Window
writeCommand(0x38);
writeData(x);
writeCommand(0x39);
writeData(x >> 8);
// Vertical Start Point of Scroll Window
writeCommand(0x3a);
writeData(y);
writeCommand(0x3b);
writeData(y >> 8);
// Horizontal End Point of Scroll Window
writeCommand(0x3c);
writeData(x + w);
writeCommand(0x3d);
writeData((x + w) >> 8);
// Vertical End Point of Scroll Window
writeCommand(0x3e);
writeData(y + h);
writeCommand(0x3f);
writeData((y + h) >> 8);
// Scroll function setting
writeCommand(0x52);
writeData(mode);
}
/**************************************************************************/
/*!
Scroll in the X direction
@param dist The distance to scroll
*/
/**************************************************************************/
void scrollX(int16_t dist) {
writeCommand(0x24);
writeData(dist);
writeCommand(0x25);
writeData(dist >> 8);
}
/**************************************************************************/
/*!
Scroll in the Y direction
@param dist The distance to scroll
*/
/**************************************************************************/
void scrollY(int16_t dist) {
writeCommand(0x26);
writeData(dist);
writeCommand(0x27);
writeData(dist >> 8);
}
/**************************************************************************/
/*!
Draws a HW accelerated triangle on the display
@param x0 The 0-based x location of point 0 on the triangle
@param y0 The 0-based y location of point 0 on the triangle
@param x1 The 0-based x location of point 1 on the triangle
@param y1 The 0-based y location of point 1 on the triangle
@param x2 The 0-based x location of point 2 on the triangle
@param y2 The 0-based y location of point 2 on the triangle
@param color The RGB565 color to use when drawing the pixel
*/
/**************************************************************************/
void drawTriangle(int16_t x0, int16_t y0, int16_t x1,
int16_t y1, int16_t x2, int16_t y2,
uint16_t color) {
triangleHelper(x0, y0, x1, y1, x2, y2, color, false);
}
/**************************************************************************/
/*!
Draws a HW accelerated filled triangle on the display
@param x0 The 0-based x location of point 0 on the triangle
@param y0 The 0-based y location of point 0 on the triangle
@param x1 The 0-based x location of point 1 on the triangle
@param y1 The 0-based y location of point 1 on the triangle
@param x2 The 0-based x location of point 2 on the triangle
@param y2 The 0-based y location of point 2 on the triangle
@param color The RGB565 color to use when drawing the pixel
*/
/**************************************************************************/
void fillTriangle(int16_t x0, int16_t y0, int16_t x1,
int16_t y1, int16_t x2, int16_t y2,
uint16_t color) {
triangleHelper(x0, y0, x1, y1, x2, y2, color, true);
}
/**************************************************************************/
/*!
Draws a HW accelerated ellipse on the display
@param xCenter The 0-based x location of the ellipse's center
@param yCenter The 0-based y location of the ellipse's center
@param longAxis The size in pixels of the ellipse's long axis
@param shortAxis The size in pixels of the ellipse's short axis
@param color The RGB565 color to use when drawing the pixel
*/
/**************************************************************************/
void drawEllipse(int16_t xCenter, int16_t yCenter,
int16_t longAxis, int16_t shortAxis,
uint16_t color) {
ellipseHelper(xCenter, yCenter, longAxis, shortAxis, color, false);
}
/**************************************************************************/
/*!
Draws a HW accelerated filled ellipse on the display
@param xCenter The 0-based x location of the ellipse's center
@param yCenter The 0-based y location of the ellipse's center
@param longAxis The size in pixels of the ellipse's long axis
@param shortAxis The size in pixels of the ellipse's short axis
@param color The RGB565 color to use when drawing the pixel
*/
/**************************************************************************/
void fillEllipse(int16_t xCenter, int16_t yCenter,
int16_t longAxis, int16_t shortAxis,
uint16_t color) {
ellipseHelper(xCenter, yCenter, longAxis, shortAxis, color, true);
}
/**************************************************************************/
/*!
Draws a HW accelerated curve on the display
@param xCenter The 0-based x location of the ellipse's center
@param yCenter The 0-based y location of the ellipse's center
@param longAxis The size in pixels of the ellipse's long axis
@param shortAxis The size in pixels of the ellipse's short axis
@param curvePart The corner to draw, where in clock-wise motion:
0 = 180-270°
1 = 270-0°
2 = 0-90°
3 = 90-180°
@param color The RGB565 color to use when drawing the pixel
*/
/**************************************************************************/
void drawCurve(int16_t xCenter, int16_t yCenter,
int16_t longAxis, int16_t shortAxis,
uint8_t curvePart, uint16_t color) {
curveHelper(xCenter, yCenter, longAxis, shortAxis, curvePart, color, false);
}
/**************************************************************************/
/*!
Draws a HW accelerated filled curve on the display
@param xCenter The 0-based x location of the ellipse's center
@param yCenter The 0-based y location of the ellipse's center
@param longAxis The size in pixels of the ellipse's long axis
@param shortAxis The size in pixels of the ellipse's short axis
@param curvePart The corner to draw, where in clock-wise motion:
0 = 180-270°
1 = 270-0°
2 = 0-90°
3 = 90-180°
@param color The RGB565 color to use when drawing the pixel
*/
/**************************************************************************/
void fillCurve(int16_t xCenter, int16_t yCenter,
int16_t longAxis, int16_t shortAxis,
uint8_t curvePart, uint16_t color) {
curveHelper(xCenter, yCenter, longAxis, shortAxis, curvePart, color, true);
}
/**************************************************************************/
/*!
Draws a HW accelerated rounded rectangle on the display
@param x The 0-based x location of the rectangle's upper left corner
@param y The 0-based y location of the rectangle's upper left corner
@param w The size in pixels of the rectangle's width
@param h The size in pixels of the rectangle's height
@param r The radius of the curves in the corners of the rectangle
@param color The RGB565 color to use when drawing the pixel
*/
/**************************************************************************/
void drawRoundRect(int16_t x, int16_t y, int16_t w, int16_t h, int16_t r, uint16_t color) {
roundRectHelper(x, y, x + w, y + h, r, color, false);
}
/**************************************************************************/
/*!
Draws a HW accelerated filled rounded rectangle on the display
@param x The 0-based x location of the rectangle's upper left corner
@param y The 0-based y location of the rectangle's upper left corner
@param w The size in pixels of the rectangle's width
@param h The size in pixels of the rectangle's height
@param r The radius of the curves in the corners of the rectangle
@param color The RGB565 color to use when drawing the pixel
*/
/**************************************************************************/
void fillRoundRect(int16_t x, int16_t y, int16_t w, int16_t h, int16_t r, uint16_t color) {
roundRectHelper(x, y, x + w, y + h, r, color, true);
}
/**************************************************************************/
/*!
Helper function for higher level circle drawing code
*/
/**************************************************************************/
void circleHelper(int16_t x, int16_t y, int16_t r, uint16_t color, bool filled) {
x = applyRotationX(x);
y = applyRotationY(y);
/* Set X */
writeCommand(0x99);
writeData(x);
writeCommand(0x9a);
writeData(x >> 8);
/* Set Y */
writeCommand(0x9b);
writeData(y);
writeCommand(0x9c);
writeData(y >> 8);
/* Set Radius */
writeCommand(0x9d);
writeData(r);
/* Set Color */
writeCommand(0x63);
writeData((color & 0xf800) >> 11);
writeCommand(0x64);
writeData((color & 0x07e0) >> 5);
writeCommand(0x65);
writeData((color & 0x001f));
/* Draw! */
writeCommand(RA8875_DCR);
if (filled) {
writeData(RA8875_DCR_CIRCLE_START | RA8875_DCR_FILL);
} else {
writeData(RA8875_DCR_CIRCLE_START | RA8875_DCR_NOFILL);
}
/* Wait for the command to finish */
waitPoll(RA8875_DCR, RA8875_DCR_CIRCLE_STATUS);
}
/**************************************************************************/
/*!
Waits for screen to finish by polling the status!
@param regname The register name to check
@param waitflag The value to wait for the status register to match
@return True if the expected status has been reached
*/
/**************************************************************************/
bool waitPoll(uint8_t regname, uint8_t waitflag) {
/* Wait for the command to finish */
printf("Entering............ waitPoll regname 0x%x waitflag 0x%x\r\n", regname, waitflag);
int cntr = 0;
while (1) {
while (!HAL_GPIO_ReadPin(LCD_WAIT_GPIO_Port, LCD_WAIT_Pin)) {
delay(1);
}
uint8_t temp = readReg(regname);
printf("waitPoll regname 0x%x waitflag 0x%x temp = 0x%x\r\n", regname, waitflag, temp);
if (!(temp & waitflag)) {
printf("Exiting.......... waitPoll regname 0x%x waitflag 0x%x temp = 0x%x\r\n", regname, waitflag, temp);
return true;
} else {
delay(5);
cntr ++;
if (cntr > 9) {
printf("Exiting.......... waitPoll abnormally!\r\n");
return true;
}
}
}
return false; // MEMEFIX: yeah i know, unreached! - add timeout?
}
/**************************************************************************/
/*!
Apply current rotation in the X direction
@return the X value with current rotation applied
*/
/**************************************************************************/
int16_t applyRotationX(int16_t x) {
switch (_rotation) {
case 2:
x = _width - 1 - x;
break;
}
return x;
}
/**************************************************************************/
/*!
Apply current rotation in the Y direction
@return the Y value with current rotation applied
*/
/**************************************************************************/
int16_t applyRotationY(int16_t y) {
switch (_rotation) {
case 2:
y = _height - 1 - y;
break;
}
return y + _voffset;
}
/************************* Initialization *********************************/
/**************************************************************************/
/*!
Performs a SW-based reset of the RA8875
*/
/**************************************************************************/
void softReset(void) {
writeCommand(RA8875_PWRR);
writeData(RA8875_PWRR_SOFTRESET);
writeData(RA8875_PWRR_NORMAL);
delayMS(1);
}
/**************************************************************************/
/*!
Initialise the PLL
*/
/**************************************************************************/
void PLLinit(void) {
if (_size == RA8875_480x80 || _size == RA8875_480x128 ||
_size == RA8875_480x272) {
writeReg(RA8875_PLLC1, RA8875_PLLC1_PLLDIV1 + 10);
delayMS(1);
writeReg(RA8875_PLLC2, RA8875_PLLC2_DIV4);
delayMS(1);
} else /* (_size == RA8875_800x480) */ {
writeReg(RA8875_PLLC1, RA8875_PLLC1_PLLDIV1 + 11);
delayMS(1);
writeReg(RA8875_PLLC2, RA8875_PLLC2_DIV4);
delayMS(1);
}
}
/**************************************************************************/
/*!
Initialises the driver IC (clock setup, etc.)
*/
/**************************************************************************/
void initialize(void) {
PLLinit();
writeReg(RA8875_SYSR, RA8875_SYSR_16BPP | RA8875_SYSR_MCU8);
/* Timing values */
uint8_t pixclk;
uint8_t hsync_start;
uint8_t hsync_pw;
uint8_t hsync_finetune;
uint8_t hsync_nondisp;
uint8_t vsync_pw;
uint16_t vsync_nondisp;
uint16_t vsync_start;
/* Set the correct values for the display being used */
if (_size == RA8875_480x80) {
pixclk = RA8875_PCSR_PDATL | RA8875_PCSR_4CLK;
hsync_nondisp = 10;
hsync_start = 8;
hsync_pw = 48;
hsync_finetune = 0;
vsync_nondisp = 3;
vsync_start = 8;
vsync_pw = 10;
_voffset = 192; // This uses the bottom 80 pixels of a 272 pixel controller
} else if (_size == RA8875_480x128 || _size == RA8875_480x272) {
pixclk = RA8875_PCSR_PDATL | RA8875_PCSR_4CLK;
hsync_nondisp = 10;
hsync_start = 8;
hsync_pw = 48;
hsync_finetune = 0;
vsync_nondisp = 3;
vsync_start = 8;
vsync_pw = 10;
_voffset = 0;
} else // (_size == RA8875_800x480)
{
pixclk = RA8875_PCSR_PDATL | RA8875_PCSR_2CLK;
hsync_nondisp = 26;
hsync_start = 32;
hsync_pw = 96;
hsync_finetune = 0;
vsync_nondisp = 32;
vsync_start = 23;
vsync_pw = 2;
_voffset = 0;
}
writeReg(RA8875_PCSR, pixclk);
// writeCommand(RA8875_PLLC1);
// writeData(0x0C);
delayMS(1);
/* Horizontal settings registers */
writeReg(RA8875_HDWR, (_width / 8) - 1); // H width: (HDWR + 1) * 8 = 480
writeReg(RA8875_HNDFTR, RA8875_HNDFTR_DE_HIGH + hsync_finetune);
writeReg(RA8875_HNDR, (hsync_nondisp - hsync_finetune - 2) /
8); // H non-display: HNDR * 8 + HNDFTR + 2 = 10
writeReg(RA8875_HSTR, hsync_start / 8 - 1); // Hsync start: (HSTR + 1)*8
writeReg(RA8875_HPWR,
RA8875_HPWR_LOW +
(hsync_pw / 8 - 1)); // HSync pulse width = (HPWR+1) * 8
/* Vertical settings registers */
writeReg(RA8875_VDHR0, (uint16_t)(_height - 1 + _voffset) & 0xFF);
writeReg(RA8875_VDHR1, (uint16_t)(_height - 1 + _voffset) >> 8);
writeReg(RA8875_VNDR0, vsync_nondisp - 1); // V non-display period = VNDR + 1
writeReg(RA8875_VNDR1, vsync_nondisp >> 8);
writeReg(RA8875_VSTR0, vsync_start - 1); // Vsync start position = VSTR + 1
writeReg(RA8875_VSTR1, vsync_start >> 8);
writeReg(RA8875_VPWR,
RA8875_VPWR_LOW + vsync_pw - 1); // Vsync pulse width = VPWR + 1
/* Set active window X */
writeReg(RA8875_HSAW0, 0); // horizontal start point
writeReg(RA8875_HSAW1, 0);
writeReg(RA8875_HEAW0, (uint16_t)(_width - 1) & 0xFF); // horizontal end point
writeReg(RA8875_HEAW1, (uint16_t)(_width - 1) >> 8);
/* Set active window Y */
writeReg(RA8875_VSAW0, 0 + _voffset); // vertical start point
writeReg(RA8875_VSAW1, 0 + _voffset);
writeReg(RA8875_VEAW0,
(uint16_t)(_height - 1 + _voffset) & 0xFF); // vertical end point
writeReg(RA8875_VEAW1, (uint16_t)(_height - 1 + _voffset) >> 8);
/* ToDo: Setup touch panel? */
/* Clear the entire window */
writeReg(RA8875_MCLR, RA8875_MCLR_START | RA8875_MCLR_FULL);
delayMS(500);
}
/**************************************************************************/
/*!
Turns the display on or off
@param on Whether to turn the display on or not
*/
/**************************************************************************/
void displayOn(bool on) {
if (on)
writeReg(RA8875_PWRR, RA8875_PWRR_NORMAL | RA8875_PWRR_DISPON);
else
writeReg(RA8875_PWRR, RA8875_PWRR_NORMAL | RA8875_PWRR_DISPOFF);
}
/**************************************************************************/
/*!
Puts the display in sleep mode, or disables sleep mode if enabled
@param sleep Whether to sleep or not
*/
/**************************************************************************/
void sleep(bool sleep) {
if (sleep)
writeReg(RA8875_PWRR, RA8875_PWRR_DISPOFF | RA8875_PWRR_SLEEP);
else
writeReg(RA8875_PWRR, RA8875_PWRR_DISPOFF);
}
/************************* Mid Level ***********************************/
/**************************************************************************/
/*!
Set the Extra General Purpose IO Register
@param on Whether to turn Extra General Purpose IO on or not
*/
/**************************************************************************/
void GPIOX(bool on) {
if (on)
writeReg(RA8875_GPIOX, 1);
else
writeReg(RA8875_GPIOX, 0);
}
/**************************************************************************/
/*!
Configure the PWM 1 Clock
@param on Whether to enable the clock
@param clock The Clock Divider
*/
/**************************************************************************/
void PWM1config(bool on, uint8_t clock) {
if (on) {
writeReg(RA8875_P1CR, RA8875_P1CR_ENABLE | (clock & 0xF));
} else {
writeReg(RA8875_P1CR, RA8875_P1CR_DISABLE | (clock & 0xF));
}
}
/**************************************************************************/
/*!
Set the duty cycle of the PWM 1 Clock
@param p The duty Cycle (0-255)
*/
/**************************************************************************/
void PWM1out(uint8_t p) {
writeReg(RA8875_P1DCR, p);
}
/**************************************************************************/
/*!
Configure the PWM 2 Clock
@param on Whether to enable the clock
@param clock The Clock Divider
*/
/**************************************************************************/
void PWM2config(bool on, uint8_t clock) {
if (on) {
writeReg(RA8875_P2CR, RA8875_P2CR_ENABLE | (clock & 0xF));
} else {
writeReg(RA8875_P2CR, RA8875_P2CR_DISABLE | (clock & 0xF));
}
}
/**************************************************************************/
/*!
Set the duty cycle of the PWM 2 Clock
@param p The duty Cycle (0-255)
*/
/**************************************************************************/
void PWM2out(uint8_t p) {
writeReg(RA8875_P2DCR, p);
}
/************************* Low Level ***********************************/
/**************************************************************************/
/*!
Write data to the specified register
@param reg Register to write to
@param val Value to write
*/
/**************************************************************************/
void writeReg(uint8_t reg, uint8_t val) {
writeCommand(reg);
writeData(val);
}
/**************************************************************************/
/*!
Set the register to read from
@param reg Register to read
@return The value
*/
/**************************************************************************/
uint8_t readReg(uint8_t reg) {
while (!HAL_GPIO_ReadPin(LCD_WAIT_GPIO_Port, LCD_WAIT_Pin)) {
delay(1);
}
writeCommand(reg);
while (!HAL_GPIO_ReadPin(LCD_WAIT_GPIO_Port, LCD_WAIT_Pin)) {
delay(1);
}
uint8_t retVal = readData();
return retVal;
}
/**************************************************************************/
/*!
Write data to the current register
@param d Data to write
*/
/**************************************************************************/
void writeData(uint8_t d) {
HAL_StatusTypeDef halStatus = HAL_OK;
while (!HAL_GPIO_ReadPin(LCD_WAIT_GPIO_Port, LCD_WAIT_Pin)) {
delay(1);
}
uint8_t pData = RA8875_DATAWRITE;
HAL_GPIO_WritePin(RA8875_CS_GPIO_Port, RA8875_CS_Pin, CS_ENABLE);
halStatus = HAL_SPI_Transmit(spiHandle, &pData, sizeof(uint8_t), 100);
if (halStatus != HAL_OK) {
Error_Handler();
}
while (!HAL_GPIO_ReadPin(LCD_WAIT_GPIO_Port, LCD_WAIT_Pin)) {
delay(1);
}
pData = d;
halStatus = HAL_SPI_Transmit(spiHandle, &pData, sizeof(uint8_t), 100);
if (halStatus != HAL_OK) {
Error_Handler();
}
HAL_GPIO_WritePin(RA8875_CS_GPIO_Port, RA8875_CS_Pin, CS_DISABLE);
}
/**************************************************************************/
/*!
Read the data from the current register
@return The Value
*/
/**************************************************************************/
uint8_t readData(void) {
HAL_StatusTypeDef halStatus = HAL_OK;
while (!HAL_GPIO_ReadPin(LCD_WAIT_GPIO_Port, LCD_WAIT_Pin)) {
delay(1);
}
HAL_GPIO_WritePin(RA8875_CS_GPIO_Port, RA8875_CS_Pin, CS_ENABLE);
uint8_t pData[1];
pData[0] = RA8875_DATAREAD;
halStatus = HAL_SPI_Transmit(spiHandle, &pData[0], sizeof(pData), 100);
if (halStatus != HAL_OK) {
Error_Handler();
}
while (!HAL_GPIO_ReadPin(LCD_WAIT_GPIO_Port, LCD_WAIT_Pin)) {
delay(1);
}
uint8_t retVal = 0x00;
halStatus = HAL_SPI_Receive(spiHandle, &retVal, sizeof(uint8_t), 100);
if (halStatus != HAL_OK) {
Error_Handler();
}
HAL_GPIO_WritePin(RA8875_CS_GPIO_Port, RA8875_CS_Pin, CS_DISABLE);
return retVal;
}
/**************************************************************************/
/*!
Write a command to the current register
@param d The data to write as a command
*/
/**************************************************************************/
void writeCommand(uint8_t d) {
HAL_StatusTypeDef halStatus = HAL_OK;
while (!HAL_GPIO_ReadPin(LCD_WAIT_GPIO_Port, LCD_WAIT_Pin)) {
delay(1);
}
HAL_GPIO_WritePin(RA8875_CS_GPIO_Port, RA8875_CS_Pin, CS_ENABLE);
uint8_t pData;
pData = RA8875_CMDWRITE;
halStatus = HAL_SPI_Transmit(spiHandle, &pData, sizeof(pData), 100);
if (halStatus != HAL_OK) {
Error_Handler();
}
while (!HAL_GPIO_ReadPin(LCD_WAIT_GPIO_Port, LCD_WAIT_Pin)) {
delay(1);
}
pData = d;
halStatus = HAL_SPI_Receive(spiHandle, &pData, sizeof(uint8_t), 100);
if (halStatus != HAL_OK) {
Error_Handler();
}
HAL_GPIO_WritePin(RA8875_CS_GPIO_Port, RA8875_CS_Pin, CS_DISABLE);
}
/**************************************************************************/
/*!
Read the status from the current register
@return The value
*/
/**************************************************************************/
uint8_t readStatus(void) {
HAL_StatusTypeDef halStatus = HAL_OK;
while (HAL_GPIO_ReadPin(LCD_WAIT_GPIO_Port, LCD_WAIT_Pin)) {
delay(1);
}
HAL_GPIO_WritePin(RA8875_CS_GPIO_Port, RA8875_CS_Pin, CS_ENABLE);
uint8_t pData = RA8875_CMDREAD;
halStatus = HAL_SPI_Transmit(spiHandle, &pData, sizeof(uint8_t), 100);
if (halStatus != HAL_OK) {
Error_Handler();
}
return pData;
}
Adafruit_GFX.h
The following code was migrated from an Arduino Library provided by Adafruit to work with STM32 Family Products.
/*
* Adafruit_GFX.h
*
* Created on: Jan 26, 2025
* Author: johng
*/
#ifndef _ADAFRUIT_GFX_H
#define _ADAFRUIT_GFX_H
#include "main.h"
#include "gfxfont.h"
#include "stdio.h"
#include "stdlib.h"
#include "string.h"
#include "malloc.h"
/// A generic graphics superclass that can handle all sorts of drawing. At a
/// minimum you can subclass and provide drawPixel(). At a maximum you can do a
/// ton of overriding to optimize. Used for any/all Adafruit displays!
class Adafruit_GFX { // : public Print {
public:
Adafruit_GFX(int16_t w, int16_t h); // Constructor
/**********************************************************************/
/*!
@brief Draw to the screen/framebuffer/etc.
Must be overridden in subclass.
@param x X coordinate in pixels
@param y Y coordinate in pixels
@param color 16-bit pixel color.
*/
/**********************************************************************/
virtual void drawPixel(int16_t x, int16_t y, uint16_t color) = 0;
// TRANSACTION API / CORE DRAW API
// These MAY be overridden by the subclass to provide device-specific
// optimized code. Otherwise 'generic' versions are used.
virtual void startWrite(void);
virtual void writePixel(int16_t x, int16_t y, uint16_t color);
virtual void writeFillRect(int16_t x, int16_t y, int16_t w, int16_t h,
uint16_t color);
virtual void writeFastVLine(int16_t x, int16_t y, int16_t h, uint16_t color);
virtual void writeFastHLine(int16_t x, int16_t y, int16_t w, uint16_t color);
virtual void writeLine(int16_t x0, int16_t y0, int16_t x1, int16_t y1,
uint16_t color);
virtual void endWrite(void);
// CONTROL API
// These MAY be overridden by the subclass to provide device-specific
// optimized code. Otherwise 'generic' versions are used.
virtual void setRotation(uint8_t r);
virtual void invertDisplay(bool i);
// BASIC DRAW API
// These MAY be overridden by the subclass to provide device-specific
// optimized code. Otherwise 'generic' versions are used.
// It's good to implement those, even if using transaction API
virtual void drawFastVLine(int16_t x, int16_t y, int16_t h, uint16_t color);
virtual void drawFastHLine(int16_t x, int16_t y, int16_t w, uint16_t color);
virtual void fillRect(int16_t x, int16_t y, int16_t w, int16_t h,
uint16_t color);
virtual void fillScreen(uint16_t color);
// Optional and probably not necessary to change
virtual void drawLine(int16_t x0, int16_t y0, int16_t x1, int16_t y1,
uint16_t color);
virtual void drawRect(int16_t x, int16_t y, int16_t w, int16_t h,
uint16_t color);
// These exist only with Adafruit_GFX (no subclass overrides)
void drawCircle(int16_t x0, int16_t y0, int16_t r, uint16_t color);
void drawCircleHelper(int16_t x0, int16_t y0, int16_t r, uint8_t cornername,
uint16_t color);
void fillCircle(int16_t x0, int16_t y0, int16_t r, uint16_t color);
void fillCircleHelper(int16_t x0, int16_t y0, int16_t r, uint8_t cornername,
int16_t delta, uint16_t color);
void drawTriangle(int16_t x0, int16_t y0, int16_t x1, int16_t y1, int16_t x2,
int16_t y2, uint16_t color);
void fillTriangle(int16_t x0, int16_t y0, int16_t x1, int16_t y1, int16_t x2,
int16_t y2, uint16_t color);
void drawRoundRect(int16_t x0, int16_t y0, int16_t w, int16_t h,
int16_t radius, uint16_t color);
void fillRoundRect(int16_t x0, int16_t y0, int16_t w, int16_t h,
int16_t radius, uint16_t color);
void drawBitmap(int16_t x, int16_t y, const uint8_t bitmap[], int16_t w,
int16_t h, uint16_t color);
void drawBitmap(int16_t x, int16_t y, const uint8_t bitmap[], int16_t w,
int16_t h, uint16_t color, uint16_t bg);
void drawBitmap(int16_t x, int16_t y, uint8_t *bitmap, int16_t w, int16_t h,
uint16_t color);
void drawBitmap(int16_t x, int16_t y, uint8_t *bitmap, int16_t w, int16_t h,
uint16_t color, uint16_t bg);
void drawXBitmap(int16_t x, int16_t y, const uint8_t bitmap[], int16_t w,
int16_t h, uint16_t color);
void drawGrayscaleBitmap(int16_t x, int16_t y, const uint8_t bitmap[],
int16_t w, int16_t h);
void drawGrayscaleBitmap(int16_t x, int16_t y, uint8_t *bitmap, int16_t w,
int16_t h);
void drawGrayscaleBitmap(int16_t x, int16_t y, const uint8_t bitmap[],
const uint8_t mask[], int16_t w, int16_t h);
void drawGrayscaleBitmap(int16_t x, int16_t y, uint8_t *bitmap, uint8_t *mask,
int16_t w, int16_t h);
void drawRGBBitmap(int16_t x, int16_t y, const uint16_t bitmap[], int16_t w,
int16_t h);
void drawRGBBitmap(int16_t x, int16_t y, uint16_t *bitmap, int16_t w,
int16_t h);
void drawRGBBitmap(int16_t x, int16_t y, const uint16_t bitmap[],
const uint8_t mask[], int16_t w, int16_t h);
void drawRGBBitmap(int16_t x, int16_t y, uint16_t *bitmap, uint8_t *mask,
int16_t w, int16_t h);
void drawChar(int16_t x, int16_t y, unsigned char c, uint16_t color,
uint16_t bg, uint8_t size);
void drawChar(int16_t x, int16_t y, unsigned char c, uint16_t color,
uint16_t bg, uint8_t size_x, uint8_t size_y);
void getTextBounds(const char *string, int16_t x, int16_t y, int16_t *x1,
int16_t *y1, uint16_t *w, uint16_t *h);
// void getTextBounds(const __FlashStringHelper *s, int16_t x, int16_t y,
// int16_t *x1, int16_t *y1, uint16_t *w, uint16_t *h);
// void getTextBounds(const String &str, int16_t x, int16_t y, int16_t *x1,
// int16_t *y1, uint16_t *w, uint16_t *h);
void setTextSize(uint8_t s);
void setTextSize(uint8_t sx, uint8_t sy);
void setFont(const GFXfont *f = NULL);
/**********************************************************************/
/*!
@brief Set text cursor location
@param x X coordinate in pixels
@param y Y coordinate in pixels
*/
/**********************************************************************/
void setCursor(int16_t x, int16_t y) {
cursor_x = x;
cursor_y = y;
}
/**********************************************************************/
/*!
@brief Set text font color with transparant background
@param c 16-bit 5-6-5 Color to draw text with
@note For 'transparent' background, background and foreground
are set to same color rather than using a separate flag.
*/
/**********************************************************************/
void setTextColor(uint16_t c) { textcolor = textbgcolor = c; }
/**********************************************************************/
/*!
@brief Set text font color with custom background color
@param c 16-bit 5-6-5 Color to draw text with
@param bg 16-bit 5-6-5 Color to draw background/fill with
*/
/**********************************************************************/
void setTextColor(uint16_t c, uint16_t bg) {
textcolor = c;
textbgcolor = bg;
}
/**********************************************************************/
/*!
@brief Set whether text that is too long for the screen width should
automatically wrap around to the next line (else clip right).
@param w true for wrapping, false for clipping
*/
/**********************************************************************/
void setTextWrap(bool w) { wrap = w; }
/**********************************************************************/
/*!
@brief Enable (or disable) Code Page 437-compatible charset.
There was an error in glcdfont.c for the longest time -- one
character (#176, the 'light shade' block) was missing -- this
threw off the index of every character that followed it.
But a TON of code has been written with the erroneous
character indices. By default, the library uses the original
'wrong' behavior and old sketches will still work. Pass
'true' to this function to use correct CP437 character values
in your code.
@param x true = enable (new behavior), false = disable (old behavior)
*/
/**********************************************************************/
void cp437(bool x = true) { _cp437 = x; }
// using Print::write;
virtual size_t write(uint8_t);
/************************************************************************/
/*!
@brief Get width of the display, accounting for current rotation
@returns Width in pixels
*/
/************************************************************************/
int16_t width(void) const { return _width; };
/************************************************************************/
/*!
@brief Get height of the display, accounting for current rotation
@returns Height in pixels
*/
/************************************************************************/
int16_t height(void) const { return _height; }
/************************************************************************/
/*!
@brief Get rotation setting for display
@returns 0 thru 3 corresponding to 4 cardinal rotations
*/
/************************************************************************/
uint8_t getRotation(void) const { return rotation; }
// get current cursor position (get rotation safe maximum values,
// using: width() for x, height() for y)
/************************************************************************/
/*!
@brief Get text cursor X location
@returns X coordinate in pixels
*/
/************************************************************************/
int16_t getCursorX(void) const { return cursor_x; }
/************************************************************************/
/*!
@brief Get text cursor Y location
@returns Y coordinate in pixels
*/
/************************************************************************/
int16_t getCursorY(void) const { return cursor_y; };
protected:
void charBounds(unsigned char c, int16_t *x, int16_t *y, int16_t *minx,
int16_t *miny, int16_t *maxx, int16_t *maxy);
int16_t WIDTH; ///< This is the 'raw' display width - never changes
int16_t HEIGHT; ///< This is the 'raw' display height - never changes
int16_t _width; ///< Display width as modified by current rotation
int16_t _height; ///< Display height as modified by current rotation
int16_t cursor_x; ///< x location to start print()ing text
int16_t cursor_y; ///< y location to start print()ing text
uint16_t textcolor; ///< 16-bit background color for print()
uint16_t textbgcolor; ///< 16-bit text color for print()
uint8_t textsize_x; ///< Desired magnification in X-axis of text to print()
uint8_t textsize_y; ///< Desired magnification in Y-axis of text to print()
uint8_t rotation; ///< Display rotation (0 thru 3)
bool wrap; ///< If set, 'wrap' text at right edge of display
bool _cp437; ///< If set, use correct CP437 charset (default is off)
GFXfont *gfxFont; ///< Pointer to special font
};
/// A simple drawn button UI element
class Adafruit_GFX_Button {
public:
Adafruit_GFX_Button(void);
// "Classic" initButton() uses center & size
void initButton(Adafruit_GFX *gfx, int16_t x, int16_t y, uint16_t w,
uint16_t h, uint16_t outline, uint16_t fill,
uint16_t textcolor, char *label, uint8_t textsize);
void initButton(Adafruit_GFX *gfx, int16_t x, int16_t y, uint16_t w,
uint16_t h, uint16_t outline, uint16_t fill,
uint16_t textcolor, char *label, uint8_t textsize_x,
uint8_t textsize_y);
// New/alt initButton() uses upper-left corner & size
void initButtonUL(Adafruit_GFX *gfx, int16_t x1, int16_t y1, uint16_t w,
uint16_t h, uint16_t outline, uint16_t fill,
uint16_t textcolor, char *label, uint8_t textsize);
void initButtonUL(Adafruit_GFX *gfx, int16_t x1, int16_t y1, uint16_t w,
uint16_t h, uint16_t outline, uint16_t fill,
uint16_t textcolor, char *label, uint8_t textsize_x,
uint8_t textsize_y);
void drawButton(bool inverted = false);
bool contains(int16_t x, int16_t y);
/**********************************************************************/
/*!
@brief Sets button state, should be done by some touch function
@param p True for pressed, false for not.
*/
/**********************************************************************/
void press(bool p) {
laststate = currstate;
currstate = p;
}
bool justPressed();
bool justReleased();
/**********************************************************************/
/*!
@brief Query whether the button is currently pressed
@returns True if pressed
*/
/**********************************************************************/
bool isPressed(void) { return currstate; };
private:
Adafruit_GFX *_gfx;
int16_t _x1, _y1; // Coordinates of top-left corner
uint16_t _w, _h;
uint8_t _textsize_x;
uint8_t _textsize_y;
uint16_t _outlinecolor, _fillcolor, _textcolor;
char _label[10];
bool currstate, laststate;
};
/// A GFX 1-bit canvas context for graphics
class GFXcanvas1 : public Adafruit_GFX {
public:
GFXcanvas1(uint16_t w, uint16_t h);
~GFXcanvas1(void);
void drawPixel(int16_t x, int16_t y, uint16_t color);
void fillScreen(uint16_t color);
void drawFastVLine(int16_t x, int16_t y, int16_t h, uint16_t color);
void drawFastHLine(int16_t x, int16_t y, int16_t w, uint16_t color);
bool getPixel(int16_t x, int16_t y) const;
/**********************************************************************/
/*!
@brief Get a pointer to the internal buffer memory
@returns A pointer to the allocated buffer
*/
/**********************************************************************/
uint8_t *getBuffer(void) const { return buffer; }
protected:
bool getRawPixel(int16_t x, int16_t y) const;
void drawFastRawVLine(int16_t x, int16_t y, int16_t h, uint16_t color);
void drawFastRawHLine(int16_t x, int16_t y, int16_t w, uint16_t color);
uint8_t *buffer; ///< Raster data: no longer private, allow subclass access
private:
#ifdef __AVR__
// Bitmask tables of 0x80>>X and ~(0x80>>X), because X>>Y is slow on AVR
static const uint8_t PROGMEM GFXsetBit[], GFXclrBit[];
#endif
};
/// A GFX 8-bit canvas context for graphics
class GFXcanvas8 : public Adafruit_GFX {
public:
GFXcanvas8(uint16_t w, uint16_t h);
~GFXcanvas8(void);
void drawPixel(int16_t x, int16_t y, uint16_t color);
void fillScreen(uint16_t color);
void drawFastVLine(int16_t x, int16_t y, int16_t h, uint16_t color);
void drawFastHLine(int16_t x, int16_t y, int16_t w, uint16_t color);
uint8_t getPixel(int16_t x, int16_t y) const;
/**********************************************************************/
/*!
@brief Get a pointer to the internal buffer memory
@returns A pointer to the allocated buffer
*/
/**********************************************************************/
uint8_t *getBuffer(void) const { return buffer; }
protected:
uint8_t getRawPixel(int16_t x, int16_t y) const;
void drawFastRawVLine(int16_t x, int16_t y, int16_t h, uint16_t color);
void drawFastRawHLine(int16_t x, int16_t y, int16_t w, uint16_t color);
uint8_t *buffer; ///< Raster data: no longer private, allow subclass access
};
/// A GFX 16-bit canvas context for graphics
class GFXcanvas16 : public Adafruit_GFX {
public:
GFXcanvas16(uint16_t w, uint16_t h);
~GFXcanvas16(void);
void drawPixel(int16_t x, int16_t y, uint16_t color);
void fillScreen(uint16_t color);
void byteSwap(void);
void drawFastVLine(int16_t x, int16_t y, int16_t h, uint16_t color);
void drawFastHLine(int16_t x, int16_t y, int16_t w, uint16_t color);
uint16_t getPixel(int16_t x, int16_t y) const;
/**********************************************************************/
/*!
@brief Get a pointer to the internal buffer memory
@returns A pointer to the allocated buffer
*/
/**********************************************************************/
uint16_t *getBuffer(void) const { return buffer; }
protected:
uint16_t getRawPixel(int16_t x, int16_t y) const;
void drawFastRawVLine(int16_t x, int16_t y, int16_t h, uint16_t color);
void drawFastRawHLine(int16_t x, int16_t y, int16_t w, uint16_t color);
uint16_t *buffer; ///< Raster data: no longer private, allow subclass access
};
#endif // _ADAFRUIT_GFX_H
Adafruit_GFX.cpp
The following code was migrated from an Arduino Library provided by Adafruit to work with STM32 Family Products.
/*
* Adafruit_GFX.cpp
*
* Created on: Jan 26, 2025
* Author: johng
*/
#include "Adafruit_GFX.h"
#include "glcdfont.c"
#ifdef __AVR__
#include <avr/pgmspace.h>
#elif defined(ESP8266) || defined(ESP32)
#include <pgmspace.h>
#endif
// Many (but maybe not all) non-AVR board installs define macros
// for compatibility with existing PROGMEM-reading AVR code.
// Do our own checks and defines here for good measure...
#ifndef pgm_read_byte
#define pgm_read_byte(addr) (*(const unsigned char *)(addr))
#endif
#ifndef pgm_read_word
#define pgm_read_word(addr) (*(const unsigned short *)(addr))
#endif
#ifndef pgm_read_dword
#define pgm_read_dword(addr) (*(const unsigned long *)(addr))
#endif
// Pointers are a peculiar case...typically 16-bit on AVR boards,
// 32 bits elsewhere. Try to accommodate both...
#if !defined(__INT_MAX__) || (__INT_MAX__ > 0xFFFF)
#define pgm_read_pointer(addr) ((void *)pgm_read_dword(addr))
#else
#define pgm_read_pointer(addr) ((void *)pgm_read_word(addr))
#endif
inline GFXglyph *pgm_read_glyph_ptr(const GFXfont *gfxFont, uint8_t c) {
#ifdef __AVR__
return &(((GFXglyph *)pgm_read_pointer(&gfxFont->glyph))[c]);
#else
// expression in __AVR__ section may generate "dereferencing type-punned
// pointer will break strict-aliasing rules" warning In fact, on other
// platforms (such as STM32) there is no need to do this pointer magic as
// program memory may be read in a usual way So expression may be simplified
return gfxFont->glyph + c;
#endif //__AVR__
}
inline uint8_t *pgm_read_bitmap_ptr(const GFXfont *gfxFont) {
#ifdef __AVR__
return (uint8_t *)pgm_read_pointer(&gfxFont->bitmap);
#else
// expression in __AVR__ section generates "dereferencing type-punned pointer
// will break strict-aliasing rules" warning In fact, on other platforms (such
// as STM32) there is no need to do this pointer magic as program memory may
// be read in a usual way So expression may be simplified
return gfxFont->bitmap;
#endif //__AVR__
}
#ifndef min
#define min(a, b) (((a) < (b)) ? (a) : (b))
#endif
#ifndef _swap_int16_t
#define _swap_int16_t(a, b) \
{ \
int16_t t = a; \
a = b; \
b = t; \
}
#endif
/**************************************************************************/
/*!
@brief Instatiate a GFX context for graphics! Can only be done by a
superclass
@param w Display width, in pixels
@param h Display height, in pixels
*/
/**************************************************************************/
Adafruit_GFX::Adafruit_GFX(int16_t w, int16_t h) : WIDTH(w), HEIGHT(h) {
_width = WIDTH;
_height = HEIGHT;
rotation = 0;
cursor_y = cursor_x = 0;
textsize_x = textsize_y = 1;
textcolor = textbgcolor = 0xFFFF;
wrap = true;
_cp437 = false;
gfxFont = NULL;
}
/**************************************************************************/
/*!
@brief Write a line. Bresenham's algorithm - thx wikpedia
@param x0 Start point x coordinate
@param y0 Start point y coordinate
@param x1 End point x coordinate
@param y1 End point y coordinate
@param color 16-bit 5-6-5 Color to draw with
*/
/**************************************************************************/
void Adafruit_GFX::writeLine(int16_t x0, int16_t y0, int16_t x1, int16_t y1,
uint16_t color) {
#if defined(ESP8266)
yield();
#endif
int16_t steep = abs(y1 - y0) > abs(x1 - x0);
if (steep) {
_swap_int16_t(x0, y0);
_swap_int16_t(x1, y1);
}
if (x0 > x1) {
_swap_int16_t(x0, x1);
_swap_int16_t(y0, y1);
}
int16_t dx, dy;
dx = x1 - x0;
dy = abs(y1 - y0);
int16_t err = dx / 2;
int16_t ystep;
if (y0 < y1) {
ystep = 1;
} else {
ystep = -1;
}
for (; x0 <= x1; x0++) {
if (steep) {
writePixel(y0, x0, color);
} else {
writePixel(x0, y0, color);
}
err -= dy;
if (err < 0) {
y0 += ystep;
err += dx;
}
}
}
/**************************************************************************/
/*!
@brief Start a display-writing routine, overwrite in subclasses.
*/
/**************************************************************************/
void Adafruit_GFX::startWrite() {}
/**************************************************************************/
/*!
@brief Write a pixel, overwrite in subclasses if startWrite is defined!
@param x x coordinate
@param y y coordinate
@param color 16-bit 5-6-5 Color to fill with
*/
/**************************************************************************/
void Adafruit_GFX::writePixel(int16_t x, int16_t y, uint16_t color) {
drawPixel(x, y, color);
}
/**************************************************************************/
/*!
@brief Write a perfectly vertical line, overwrite in subclasses if
startWrite is defined!
@param x Top-most x coordinate
@param y Top-most y coordinate
@param h Height in pixels
@param color 16-bit 5-6-5 Color to fill with
*/
/**************************************************************************/
void Adafruit_GFX::writeFastVLine(int16_t x, int16_t y, int16_t h,
uint16_t color) {
// Overwrite in subclasses if startWrite is defined!
// Can be just writeLine(x, y, x, y+h-1, color);
// or writeFillRect(x, y, 1, h, color);
drawFastVLine(x, y, h, color);
}
/**************************************************************************/
/*!
@brief Write a perfectly horizontal line, overwrite in subclasses if
startWrite is defined!
@param x Left-most x coordinate
@param y Left-most y coordinate
@param w Width in pixels
@param color 16-bit 5-6-5 Color to fill with
*/
/**************************************************************************/
void Adafruit_GFX::writeFastHLine(int16_t x, int16_t y, int16_t w,
uint16_t color) {
// Overwrite in subclasses if startWrite is defined!
// Example: writeLine(x, y, x+w-1, y, color);
// or writeFillRect(x, y, w, 1, color);
drawFastHLine(x, y, w, color);
}
/**************************************************************************/
/*!
@brief Write a rectangle completely with one color, overwrite in
subclasses if startWrite is defined!
@param x Top left corner x coordinate
@param y Top left corner y coordinate
@param w Width in pixels
@param h Height in pixels
@param color 16-bit 5-6-5 Color to fill with
*/
/**************************************************************************/
void Adafruit_GFX::writeFillRect(int16_t x, int16_t y, int16_t w, int16_t h,
uint16_t color) {
// Overwrite in subclasses if desired!
fillRect(x, y, w, h, color);
}
/**************************************************************************/
/*!
@brief End a display-writing routine, overwrite in subclasses if
startWrite is defined!
*/
/**************************************************************************/
void Adafruit_GFX::endWrite() {}
/**************************************************************************/
/*!
@brief Draw a perfectly vertical line (this is often optimized in a
subclass!)
@param x Top-most x coordinate
@param y Top-most y coordinate
@param h Height in pixels
@param color 16-bit 5-6-5 Color to fill with
*/
/**************************************************************************/
void Adafruit_GFX::drawFastVLine(int16_t x, int16_t y, int16_t h,
uint16_t color) {
startWrite();
writeLine(x, y, x, y + h - 1, color);
endWrite();
}
/**************************************************************************/
/*!
@brief Draw a perfectly horizontal line (this is often optimized in a
subclass!)
@param x Left-most x coordinate
@param y Left-most y coordinate
@param w Width in pixels
@param color 16-bit 5-6-5 Color to fill with
*/
/**************************************************************************/
void Adafruit_GFX::drawFastHLine(int16_t x, int16_t y, int16_t w,
uint16_t color) {
startWrite();
writeLine(x, y, x + w - 1, y, color);
endWrite();
}
/**************************************************************************/
/*!
@brief Fill a rectangle completely with one color. Update in subclasses if
desired!
@param x Top left corner x coordinate
@param y Top left corner y coordinate
@param w Width in pixels
@param h Height in pixels
@param color 16-bit 5-6-5 Color to fill with
*/
/**************************************************************************/
void Adafruit_GFX::fillRect(int16_t x, int16_t y, int16_t w, int16_t h,
uint16_t color) {
startWrite();
for (int16_t i = x; i < x + w; i++) {
writeFastVLine(i, y, h, color);
}
endWrite();
}
/**************************************************************************/
/*!
@brief Fill the screen completely with one color. Update in subclasses if
desired!
@param color 16-bit 5-6-5 Color to fill with
*/
/**************************************************************************/
void Adafruit_GFX::fillScreen(uint16_t color) {
fillRect(0, 0, _width, _height, color);
}
/**************************************************************************/
/*!
@brief Draw a line
@param x0 Start point x coordinate
@param y0 Start point y coordinate
@param x1 End point x coordinate
@param y1 End point y coordinate
@param color 16-bit 5-6-5 Color to draw with
*/
/**************************************************************************/
void Adafruit_GFX::drawLine(int16_t x0, int16_t y0, int16_t x1, int16_t y1,
uint16_t color) {
// Update in subclasses if desired!
if (x0 == x1) {
if (y0 > y1)
_swap_int16_t(y0, y1);
drawFastVLine(x0, y0, y1 - y0 + 1, color);
} else if (y0 == y1) {
if (x0 > x1)
_swap_int16_t(x0, x1);
drawFastHLine(x0, y0, x1 - x0 + 1, color);
} else {
startWrite();
writeLine(x0, y0, x1, y1, color);
endWrite();
}
}
/**************************************************************************/
/*!
@brief Draw a circle outline
@param x0 Center-point x coordinate
@param y0 Center-point y coordinate
@param r Radius of circle
@param color 16-bit 5-6-5 Color to draw with
*/
/**************************************************************************/
void Adafruit_GFX::drawCircle(int16_t x0, int16_t y0, int16_t r,
uint16_t color) {
#if defined(ESP8266)
yield();
#endif
int16_t f = 1 - r;
int16_t ddF_x = 1;
int16_t ddF_y = -2 * r;
int16_t x = 0;
int16_t y = r;
startWrite();
writePixel(x0, y0 + r, color);
writePixel(x0, y0 - r, color);
writePixel(x0 + r, y0, color);
writePixel(x0 - r, y0, color);
while (x < y) {
if (f >= 0) {
y--;
ddF_y += 2;
f += ddF_y;
}
x++;
ddF_x += 2;
f += ddF_x;
writePixel(x0 + x, y0 + y, color);
writePixel(x0 - x, y0 + y, color);
writePixel(x0 + x, y0 - y, color);
writePixel(x0 - x, y0 - y, color);
writePixel(x0 + y, y0 + x, color);
writePixel(x0 - y, y0 + x, color);
writePixel(x0 + y, y0 - x, color);
writePixel(x0 - y, y0 - x, color);
}
endWrite();
}
/**************************************************************************/
/*!
@brief Quarter-circle drawer, used to do circles and roundrects
@param x0 Center-point x coordinate
@param y0 Center-point y coordinate
@param r Radius of circle
@param cornername Mask bit #1 or bit #2 to indicate which quarters of
the circle we're doing
@param color 16-bit 5-6-5 Color to draw with
*/
/**************************************************************************/
void Adafruit_GFX::drawCircleHelper(int16_t x0, int16_t y0, int16_t r,
uint8_t cornername, uint16_t color) {
int16_t f = 1 - r;
int16_t ddF_x = 1;
int16_t ddF_y = -2 * r;
int16_t x = 0;
int16_t y = r;
while (x < y) {
if (f >= 0) {
y--;
ddF_y += 2;
f += ddF_y;
}
x++;
ddF_x += 2;
f += ddF_x;
if (cornername & 0x4) {
writePixel(x0 + x, y0 + y, color);
writePixel(x0 + y, y0 + x, color);
}
if (cornername & 0x2) {
writePixel(x0 + x, y0 - y, color);
writePixel(x0 + y, y0 - x, color);
}
if (cornername & 0x8) {
writePixel(x0 - y, y0 + x, color);
writePixel(x0 - x, y0 + y, color);
}
if (cornername & 0x1) {
writePixel(x0 - y, y0 - x, color);
writePixel(x0 - x, y0 - y, color);
}
}
}
/**************************************************************************/
/*!
@brief Draw a circle with filled color
@param x0 Center-point x coordinate
@param y0 Center-point y coordinate
@param r Radius of circle
@param color 16-bit 5-6-5 Color to fill with
*/
/**************************************************************************/
void Adafruit_GFX::fillCircle(int16_t x0, int16_t y0, int16_t r,
uint16_t color) {
startWrite();
writeFastVLine(x0, y0 - r, 2 * r + 1, color);
fillCircleHelper(x0, y0, r, 3, 0, color);
endWrite();
}
/**************************************************************************/
/*!
@brief Quarter-circle drawer with fill, used for circles and roundrects
@param x0 Center-point x coordinate
@param y0 Center-point y coordinate
@param r Radius of circle
@param corners Mask bits indicating which quarters we're doing
@param delta Offset from center-point, used for round-rects
@param color 16-bit 5-6-5 Color to fill with
*/
/**************************************************************************/
void Adafruit_GFX::fillCircleHelper(int16_t x0, int16_t y0, int16_t r,
uint8_t corners, int16_t delta,
uint16_t color) {
int16_t f = 1 - r;
int16_t ddF_x = 1;
int16_t ddF_y = -2 * r;
int16_t x = 0;
int16_t y = r;
int16_t px = x;
int16_t py = y;
delta++; // Avoid some +1's in the loop
while (x < y) {
if (f >= 0) {
y--;
ddF_y += 2;
f += ddF_y;
}
x++;
ddF_x += 2;
f += ddF_x;
// These checks avoid double-drawing certain lines, important
// for the SSD1306 library which has an INVERT drawing mode.
if (x < (y + 1)) {
if (corners & 1)
writeFastVLine(x0 + x, y0 - y, 2 * y + delta, color);
if (corners & 2)
writeFastVLine(x0 - x, y0 - y, 2 * y + delta, color);
}
if (y != py) {
if (corners & 1)
writeFastVLine(x0 + py, y0 - px, 2 * px + delta, color);
if (corners & 2)
writeFastVLine(x0 - py, y0 - px, 2 * px + delta, color);
py = y;
}
px = x;
}
}
/**************************************************************************/
/*!
@brief Draw a rectangle with no fill color
@param x Top left corner x coordinate
@param y Top left corner y coordinate
@param w Width in pixels
@param h Height in pixels
@param color 16-bit 5-6-5 Color to draw with
*/
/**************************************************************************/
void Adafruit_GFX::drawRect(int16_t x, int16_t y, int16_t w, int16_t h,
uint16_t color) {
startWrite();
writeFastHLine(x, y, w, color);
writeFastHLine(x, y + h - 1, w, color);
writeFastVLine(x, y, h, color);
writeFastVLine(x + w - 1, y, h, color);
endWrite();
}
/**************************************************************************/
/*!
@brief Draw a rounded rectangle with no fill color
@param x Top left corner x coordinate
@param y Top left corner y coordinate
@param w Width in pixels
@param h Height in pixels
@param r Radius of corner rounding
@param color 16-bit 5-6-5 Color to draw with
*/
/**************************************************************************/
void Adafruit_GFX::drawRoundRect(int16_t x, int16_t y, int16_t w, int16_t h,
int16_t r, uint16_t color) {
int16_t max_radius = ((w < h) ? w : h) / 2; // 1/2 minor axis
if (r > max_radius)
r = max_radius;
// smarter version
startWrite();
writeFastHLine(x + r, y, w - 2 * r, color); // Top
writeFastHLine(x + r, y + h - 1, w - 2 * r, color); // Bottom
writeFastVLine(x, y + r, h - 2 * r, color); // Left
writeFastVLine(x + w - 1, y + r, h - 2 * r, color); // Right
// draw four corners
drawCircleHelper(x + r, y + r, r, 1, color);
drawCircleHelper(x + w - r - 1, y + r, r, 2, color);
drawCircleHelper(x + w - r - 1, y + h - r - 1, r, 4, color);
drawCircleHelper(x + r, y + h - r - 1, r, 8, color);
endWrite();
}
/**************************************************************************/
/*!
@brief Draw a rounded rectangle with fill color
@param x Top left corner x coordinate
@param y Top left corner y coordinate
@param w Width in pixels
@param h Height in pixels
@param r Radius of corner rounding
@param color 16-bit 5-6-5 Color to draw/fill with
*/
/**************************************************************************/
void Adafruit_GFX::fillRoundRect(int16_t x, int16_t y, int16_t w, int16_t h,
int16_t r, uint16_t color) {
int16_t max_radius = ((w < h) ? w : h) / 2; // 1/2 minor axis
if (r > max_radius)
r = max_radius;
// smarter version
startWrite();
writeFillRect(x + r, y, w - 2 * r, h, color);
// draw four corners
fillCircleHelper(x + w - r - 1, y + r, r, 1, h - 2 * r - 1, color);
fillCircleHelper(x + r, y + r, r, 2, h - 2 * r - 1, color);
endWrite();
}
/**************************************************************************/
/*!
@brief Draw a triangle with no fill color
@param x0 Vertex #0 x coordinate
@param y0 Vertex #0 y coordinate
@param x1 Vertex #1 x coordinate
@param y1 Vertex #1 y coordinate
@param x2 Vertex #2 x coordinate
@param y2 Vertex #2 y coordinate
@param color 16-bit 5-6-5 Color to draw with
*/
/**************************************************************************/
void Adafruit_GFX::drawTriangle(int16_t x0, int16_t y0, int16_t x1, int16_t y1,
int16_t x2, int16_t y2, uint16_t color) {
drawLine(x0, y0, x1, y1, color);
drawLine(x1, y1, x2, y2, color);
drawLine(x2, y2, x0, y0, color);
}
/**************************************************************************/
/*!
@brief Draw a triangle with color-fill
@param x0 Vertex #0 x coordinate
@param y0 Vertex #0 y coordinate
@param x1 Vertex #1 x coordinate
@param y1 Vertex #1 y coordinate
@param x2 Vertex #2 x coordinate
@param y2 Vertex #2 y coordinate
@param color 16-bit 5-6-5 Color to fill/draw with
*/
/**************************************************************************/
void Adafruit_GFX::fillTriangle(int16_t x0, int16_t y0, int16_t x1, int16_t y1,
int16_t x2, int16_t y2, uint16_t color) {
int16_t a, b, y, last;
// Sort coordinates by Y order (y2 >= y1 >= y0)
if (y0 > y1) {
_swap_int16_t(y0, y1);
_swap_int16_t(x0, x1);
}
if (y1 > y2) {
_swap_int16_t(y2, y1);
_swap_int16_t(x2, x1);
}
if (y0 > y1) {
_swap_int16_t(y0, y1);
_swap_int16_t(x0, x1);
}
startWrite();
if (y0 == y2) { // Handle awkward all-on-same-line case as its own thing
a = b = x0;
if (x1 < a)
a = x1;
else if (x1 > b)
b = x1;
if (x2 < a)
a = x2;
else if (x2 > b)
b = x2;
writeFastHLine(a, y0, b - a + 1, color);
endWrite();
return;
}
int16_t dx01 = x1 - x0, dy01 = y1 - y0, dx02 = x2 - x0, dy02 = y2 - y0,
dx12 = x2 - x1, dy12 = y2 - y1;
int32_t sa = 0, sb = 0;
// For upper part of triangle, find scanline crossings for segments
// 0-1 and 0-2. If y1=y2 (flat-bottomed triangle), the scanline y1
// is included here (and second loop will be skipped, avoiding a /0
// error there), otherwise scanline y1 is skipped here and handled
// in the second loop...which also avoids a /0 error here if y0=y1
// (flat-topped triangle).
if (y1 == y2)
last = y1; // Include y1 scanline
else
last = y1 - 1; // Skip it
for (y = y0; y <= last; y++) {
a = x0 + sa / dy01;
b = x0 + sb / dy02;
sa += dx01;
sb += dx02;
/* longhand:
a = x0 + (x1 - x0) * (y - y0) / (y1 - y0);
b = x0 + (x2 - x0) * (y - y0) / (y2 - y0);
*/
if (a > b)
_swap_int16_t(a, b);
writeFastHLine(a, y, b - a + 1, color);
}
// For lower part of triangle, find scanline crossings for segments
// 0-2 and 1-2. This loop is skipped if y1=y2.
sa = (int32_t)dx12 * (y - y1);
sb = (int32_t)dx02 * (y - y0);
for (; y <= y2; y++) {
a = x1 + sa / dy12;
b = x0 + sb / dy02;
sa += dx12;
sb += dx02;
/* longhand:
a = x1 + (x2 - x1) * (y - y1) / (y2 - y1);
b = x0 + (x2 - x0) * (y - y0) / (y2 - y0);
*/
if (a > b)
_swap_int16_t(a, b);
writeFastHLine(a, y, b - a + 1, color);
}
endWrite();
}
// BITMAP / XBITMAP / GRAYSCALE / RGB BITMAP FUNCTIONS ---------------------
/**************************************************************************/
/*!
@brief Draw a PROGMEM-resident 1-bit image at the specified (x,y)
position, using the specified foreground color (unset bits are transparent).
@param x Top left corner x coordinate
@param y Top left corner y coordinate
@param bitmap byte array with monochrome bitmap
@param w Width of bitmap in pixels
@param h Height of bitmap in pixels
@param color 16-bit 5-6-5 Color to draw with
*/
/**************************************************************************/
void Adafruit_GFX::drawBitmap(int16_t x, int16_t y, const uint8_t bitmap[],
int16_t w, int16_t h, uint16_t color) {
int16_t byteWidth = (w + 7) / 8; // Bitmap scanline pad = whole byte
uint8_t b = 0;
startWrite();
for (int16_t j = 0; j < h; j++, y++) {
for (int16_t i = 0; i < w; i++) {
if (i & 7)
b <<= 1;
else
b = pgm_read_byte(&bitmap[j * byteWidth + i / 8]);
if (b & 0x80)
writePixel(x + i, y, color);
}
}
endWrite();
}
/**************************************************************************/
/*!
@brief Draw a PROGMEM-resident 1-bit image at the specified (x,y)
position, using the specified foreground (for set bits) and background (unset
bits) colors.
@param x Top left corner x coordinate
@param y Top left corner y coordinate
@param bitmap byte array with monochrome bitmap
@param w Width of bitmap in pixels
@param h Height of bitmap in pixels
@param color 16-bit 5-6-5 Color to draw pixels with
@param bg 16-bit 5-6-5 Color to draw background with
*/
/**************************************************************************/
void Adafruit_GFX::drawBitmap(int16_t x, int16_t y, const uint8_t bitmap[],
int16_t w, int16_t h, uint16_t color,
uint16_t bg) {
int16_t byteWidth = (w + 7) / 8; // Bitmap scanline pad = whole byte
uint8_t b = 0;
startWrite();
for (int16_t j = 0; j < h; j++, y++) {
for (int16_t i = 0; i < w; i++) {
if (i & 7)
b <<= 1;
else
b = pgm_read_byte(&bitmap[j * byteWidth + i / 8]);
writePixel(x + i, y, (b & 0x80) ? color : bg);
}
}
endWrite();
}
/**************************************************************************/
/*!
@brief Draw a RAM-resident 1-bit image at the specified (x,y) position,
using the specified foreground color (unset bits are transparent).
@param x Top left corner x coordinate
@param y Top left corner y coordinate
@param bitmap byte array with monochrome bitmap
@param w Width of bitmap in pixels
@param h Height of bitmap in pixels
@param color 16-bit 5-6-5 Color to draw with
*/
/**************************************************************************/
void Adafruit_GFX::drawBitmap(int16_t x, int16_t y, uint8_t *bitmap, int16_t w,
int16_t h, uint16_t color) {
int16_t byteWidth = (w + 7) / 8; // Bitmap scanline pad = whole byte
uint8_t b = 0;
startWrite();
for (int16_t j = 0; j < h; j++, y++) {
for (int16_t i = 0; i < w; i++) {
if (i & 7)
b <<= 1;
else
b = bitmap[j * byteWidth + i / 8];
if (b & 0x80)
writePixel(x + i, y, color);
}
}
endWrite();
}
/**************************************************************************/
/*!
@brief Draw a RAM-resident 1-bit image at the specified (x,y) position,
using the specified foreground (for set bits) and background (unset bits)
colors.
@param x Top left corner x coordinate
@param y Top left corner y coordinate
@param bitmap byte array with monochrome bitmap
@param w Width of bitmap in pixels
@param h Height of bitmap in pixels
@param color 16-bit 5-6-5 Color to draw pixels with
@param bg 16-bit 5-6-5 Color to draw background with
*/
/**************************************************************************/
void Adafruit_GFX::drawBitmap(int16_t x, int16_t y, uint8_t *bitmap, int16_t w,
int16_t h, uint16_t color, uint16_t bg) {
int16_t byteWidth = (w + 7) / 8; // Bitmap scanline pad = whole byte
uint8_t b = 0;
startWrite();
for (int16_t j = 0; j < h; j++, y++) {
for (int16_t i = 0; i < w; i++) {
if (i & 7)
b <<= 1;
else
b = bitmap[j * byteWidth + i / 8];
writePixel(x + i, y, (b & 0x80) ? color : bg);
}
}
endWrite();
}
/**************************************************************************/
/*!
@brief Draw PROGMEM-resident XBitMap Files (*.xbm), exported from GIMP.
Usage: Export from GIMP to *.xbm, rename *.xbm to *.c and open in editor.
C Array can be directly used with this function.
There is no RAM-resident version of this function; if generating bitmaps
in RAM, use the format defined by drawBitmap() and call that instead.
@param x Top left corner x coordinate
@param y Top left corner y coordinate
@param bitmap byte array with monochrome bitmap
@param w Width of bitmap in pixels
@param h Height of bitmap in pixels
@param color 16-bit 5-6-5 Color to draw pixels with
*/
/**************************************************************************/
void Adafruit_GFX::drawXBitmap(int16_t x, int16_t y, const uint8_t bitmap[],
int16_t w, int16_t h, uint16_t color) {
int16_t byteWidth = (w + 7) / 8; // Bitmap scanline pad = whole byte
uint8_t b = 0;
startWrite();
for (int16_t j = 0; j < h; j++, y++) {
for (int16_t i = 0; i < w; i++) {
if (i & 7)
b >>= 1;
else
b = pgm_read_byte(&bitmap[j * byteWidth + i / 8]);
// Nearly identical to drawBitmap(), only the bit order
// is reversed here (left-to-right = LSB to MSB):
if (b & 0x01)
writePixel(x + i, y, color);
}
}
endWrite();
}
/**************************************************************************/
/*!
@brief Draw a PROGMEM-resident 8-bit image (grayscale) at the specified
(x,y) pos. Specifically for 8-bit display devices such as IS31FL3731; no
color reduction/expansion is performed.
@param x Top left corner x coordinate
@param y Top left corner y coordinate
@param bitmap byte array with grayscale bitmap
@param w Width of bitmap in pixels
@param h Height of bitmap in pixels
*/
/**************************************************************************/
void Adafruit_GFX::drawGrayscaleBitmap(int16_t x, int16_t y,
const uint8_t bitmap[], int16_t w,
int16_t h) {
startWrite();
for (int16_t j = 0; j < h; j++, y++) {
for (int16_t i = 0; i < w; i++) {
writePixel(x + i, y, (uint8_t)pgm_read_byte(&bitmap[j * w + i]));
}
}
endWrite();
}
/**************************************************************************/
/*!
@brief Draw a RAM-resident 8-bit image (grayscale) at the specified (x,y)
pos. Specifically for 8-bit display devices such as IS31FL3731; no color
reduction/expansion is performed.
@param x Top left corner x coordinate
@param y Top left corner y coordinate
@param bitmap byte array with grayscale bitmap
@param w Width of bitmap in pixels
@param h Height of bitmap in pixels
*/
/**************************************************************************/
void Adafruit_GFX::drawGrayscaleBitmap(int16_t x, int16_t y, uint8_t *bitmap,
int16_t w, int16_t h) {
startWrite();
for (int16_t j = 0; j < h; j++, y++) {
for (int16_t i = 0; i < w; i++) {
writePixel(x + i, y, bitmap[j * w + i]);
}
}
endWrite();
}
/**************************************************************************/
/*!
@brief Draw a PROGMEM-resident 8-bit image (grayscale) with a 1-bit mask
(set bits = opaque, unset bits = clear) at the specified (x,y) position.
BOTH buffers (grayscale and mask) must be PROGMEM-resident.
Specifically for 8-bit display devices such as IS31FL3731; no color
reduction/expansion is performed.
@param x Top left corner x coordinate
@param y Top left corner y coordinate
@param bitmap byte array with grayscale bitmap
@param mask byte array with mask bitmap
@param w Width of bitmap in pixels
@param h Height of bitmap in pixels
*/
/**************************************************************************/
void Adafruit_GFX::drawGrayscaleBitmap(int16_t x, int16_t y,
const uint8_t bitmap[],
const uint8_t mask[], int16_t w,
int16_t h) {
int16_t bw = (w + 7) / 8; // Bitmask scanline pad = whole byte
uint8_t b = 0;
startWrite();
for (int16_t j = 0; j < h; j++, y++) {
for (int16_t i = 0; i < w; i++) {
if (i & 7)
b <<= 1;
else
b = pgm_read_byte(&mask[j * bw + i / 8]);
if (b & 0x80) {
writePixel(x + i, y, (uint8_t)pgm_read_byte(&bitmap[j * w + i]));
}
}
}
endWrite();
}
/**************************************************************************/
/*!
@brief Draw a RAM-resident 8-bit image (grayscale) with a 1-bit mask
(set bits = opaque, unset bits = clear) at the specified (x,y) position.
BOTH buffers (grayscale and mask) must be RAM-residentt, no mix-and-match
Specifically for 8-bit display devices such as IS31FL3731; no color
reduction/expansion is performed.
@param x Top left corner x coordinate
@param y Top left corner y coordinate
@param bitmap byte array with grayscale bitmap
@param mask byte array with mask bitmap
@param w Width of bitmap in pixels
@param h Height of bitmap in pixels
*/
/**************************************************************************/
void Adafruit_GFX::drawGrayscaleBitmap(int16_t x, int16_t y, uint8_t *bitmap,
uint8_t *mask, int16_t w, int16_t h) {
int16_t bw = (w + 7) / 8; // Bitmask scanline pad = whole byte
uint8_t b = 0;
startWrite();
for (int16_t j = 0; j < h; j++, y++) {
for (int16_t i = 0; i < w; i++) {
if (i & 7)
b <<= 1;
else
b = mask[j * bw + i / 8];
if (b & 0x80) {
writePixel(x + i, y, bitmap[j * w + i]);
}
}
}
endWrite();
}
/**************************************************************************/
/*!
@brief Draw a PROGMEM-resident 16-bit image (RGB 5/6/5) at the specified
(x,y) position. For 16-bit display devices; no color reduction performed.
@param x Top left corner x coordinate
@param y Top left corner y coordinate
@param bitmap byte array with 16-bit color bitmap
@param w Width of bitmap in pixels
@param h Height of bitmap in pixels
*/
/**************************************************************************/
void Adafruit_GFX::drawRGBBitmap(int16_t x, int16_t y, const uint16_t bitmap[],
int16_t w, int16_t h) {
startWrite();
for (int16_t j = 0; j < h; j++, y++) {
for (int16_t i = 0; i < w; i++) {
writePixel(x + i, y, pgm_read_word(&bitmap[j * w + i]));
}
}
endWrite();
}
/**************************************************************************/
/*!
@brief Draw a RAM-resident 16-bit image (RGB 5/6/5) at the specified (x,y)
position. For 16-bit display devices; no color reduction performed.
@param x Top left corner x coordinate
@param y Top left corner y coordinate
@param bitmap byte array with 16-bit color bitmap
@param w Width of bitmap in pixels
@param h Height of bitmap in pixels
*/
/**************************************************************************/
void Adafruit_GFX::drawRGBBitmap(int16_t x, int16_t y, uint16_t *bitmap,
int16_t w, int16_t h) {
startWrite();
for (int16_t j = 0; j < h; j++, y++) {
for (int16_t i = 0; i < w; i++) {
writePixel(x + i, y, bitmap[j * w + i]);
}
}
endWrite();
}
/**************************************************************************/
/*!
@brief Draw a PROGMEM-resident 16-bit image (RGB 5/6/5) with a 1-bit mask
(set bits = opaque, unset bits = clear) at the specified (x,y) position. BOTH
buffers (color and mask) must be PROGMEM-resident. For 16-bit display
devices; no color reduction performed.
@param x Top left corner x coordinate
@param y Top left corner y coordinate
@param bitmap byte array with 16-bit color bitmap
@param mask byte array with monochrome mask bitmap
@param w Width of bitmap in pixels
@param h Height of bitmap in pixels
*/
/**************************************************************************/
void Adafruit_GFX::drawRGBBitmap(int16_t x, int16_t y, const uint16_t bitmap[],
const uint8_t mask[], int16_t w, int16_t h) {
int16_t bw = (w + 7) / 8; // Bitmask scanline pad = whole byte
uint8_t b = 0;
startWrite();
for (int16_t j = 0; j < h; j++, y++) {
for (int16_t i = 0; i < w; i++) {
if (i & 7)
b <<= 1;
else
b = pgm_read_byte(&mask[j * bw + i / 8]);
if (b & 0x80) {
writePixel(x + i, y, pgm_read_word(&bitmap[j * w + i]));
}
}
}
endWrite();
}
/**************************************************************************/
/*!
@brief Draw a RAM-resident 16-bit image (RGB 5/6/5) with a 1-bit mask (set
bits = opaque, unset bits = clear) at the specified (x,y) position. BOTH
buffers (color and mask) must be RAM-resident. For 16-bit display devices; no
color reduction performed.
@param x Top left corner x coordinate
@param y Top left corner y coordinate
@param bitmap byte array with 16-bit color bitmap
@param mask byte array with monochrome mask bitmap
@param w Width of bitmap in pixels
@param h Height of bitmap in pixels
*/
/**************************************************************************/
void Adafruit_GFX::drawRGBBitmap(int16_t x, int16_t y, uint16_t *bitmap,
uint8_t *mask, int16_t w, int16_t h) {
int16_t bw = (w + 7) / 8; // Bitmask scanline pad = whole byte
uint8_t b = 0;
startWrite();
for (int16_t j = 0; j < h; j++, y++) {
for (int16_t i = 0; i < w; i++) {
if (i & 7)
b <<= 1;
else
b = mask[j * bw + i / 8];
if (b & 0x80) {
writePixel(x + i, y, bitmap[j * w + i]);
}
}
}
endWrite();
}
// TEXT- AND CHARACTER-HANDLING FUNCTIONS ----------------------------------
// Draw a character
/**************************************************************************/
/*!
@brief Draw a single character
@param x Bottom left corner x coordinate
@param y Bottom left corner y coordinate
@param c The 8-bit font-indexed character (likely ascii)
@param color 16-bit 5-6-5 Color to draw chraracter with
@param bg 16-bit 5-6-5 Color to fill background with (if same as color,
no background)
@param size Font magnification level, 1 is 'original' size
*/
/**************************************************************************/
void Adafruit_GFX::drawChar(int16_t x, int16_t y, unsigned char c,
uint16_t color, uint16_t bg, uint8_t size) {
drawChar(x, y, c, color, bg, size, size);
}
// Draw a character
/**************************************************************************/
/*!
@brief Draw a single character
@param x Bottom left corner x coordinate
@param y Bottom left corner y coordinate
@param c The 8-bit font-indexed character (likely ascii)
@param color 16-bit 5-6-5 Color to draw chraracter with
@param bg 16-bit 5-6-5 Color to fill background with (if same as color,
no background)
@param size_x Font magnification level in X-axis, 1 is 'original' size
@param size_y Font magnification level in Y-axis, 1 is 'original' size
*/
/**************************************************************************/
void Adafruit_GFX::drawChar(int16_t x, int16_t y, unsigned char c,
uint16_t color, uint16_t bg, uint8_t size_x,
uint8_t size_y) {
if (!gfxFont) { // 'Classic' built-in font
if ((x >= _width) || // Clip right
(y >= _height) || // Clip bottom
((x + 6 * size_x - 1) < 0) || // Clip left
((y + 8 * size_y - 1) < 0)) // Clip top
return;
if (!_cp437 && (c >= 176))
c++; // Handle 'classic' charset behavior
startWrite();
for (int8_t i = 0; i < 5; i++) { // Char bitmap = 5 columns
uint8_t line = pgm_read_byte(&font[c * 5 + i]);
for (int8_t j = 0; j < 8; j++, line >>= 1) {
if (line & 1) {
if (size_x == 1 && size_y == 1)
writePixel(x + i, y + j, color);
else
writeFillRect(x + i * size_x, y + j * size_y, size_x, size_y,
color);
} else if (bg != color) {
if (size_x == 1 && size_y == 1)
writePixel(x + i, y + j, bg);
else
writeFillRect(x + i * size_x, y + j * size_y, size_x, size_y, bg);
}
}
}
if (bg != color) { // If opaque, draw vertical line for last column
if (size_x == 1 && size_y == 1)
writeFastVLine(x + 5, y, 8, bg);
else
writeFillRect(x + 5 * size_x, y, size_x, 8 * size_y, bg);
}
endWrite();
} else { // Custom font
// Character is assumed previously filtered by write() to eliminate
// newlines, returns, non-printable characters, etc. Calling
// drawChar() directly with 'bad' characters of font may cause mayhem!
c -= (uint8_t)pgm_read_byte(&gfxFont->first);
GFXglyph *glyph = pgm_read_glyph_ptr(gfxFont, c);
uint8_t *bitmap = pgm_read_bitmap_ptr(gfxFont);
uint16_t bo = pgm_read_word(&glyph->bitmapOffset);
uint8_t w = pgm_read_byte(&glyph->width), h = pgm_read_byte(&glyph->height);
int8_t xo = pgm_read_byte(&glyph->xOffset),
yo = pgm_read_byte(&glyph->yOffset);
uint8_t xx, yy, bits = 0, bit = 0;
int16_t xo16 = 0, yo16 = 0;
if (size_x > 1 || size_y > 1) {
xo16 = xo;
yo16 = yo;
}
// Todo: Add character clipping here
// NOTE: THERE IS NO 'BACKGROUND' COLOR OPTION ON CUSTOM FONTS.
// THIS IS ON PURPOSE AND BY DESIGN. The background color feature
// has typically been used with the 'classic' font to overwrite old
// screen contents with new data. This ONLY works because the
// characters are a uniform size; it's not a sensible thing to do with
// proportionally-spaced fonts with glyphs of varying sizes (and that
// may overlap). To replace previously-drawn text when using a custom
// font, use the getTextBounds() function to determine the smallest
// rectangle encompassing a string, erase the area with fillRect(),
// then draw new text. This WILL infortunately 'blink' the text, but
// is unavoidable. Drawing 'background' pixels will NOT fix this,
// only creates a new set of problems. Have an idea to work around
// this (a canvas object type for MCUs that can afford the RAM and
// displays supporting setAddrWindow() and pushColors()), but haven't
// implemented this yet.
startWrite();
for (yy = 0; yy < h; yy++) {
for (xx = 0; xx < w; xx++) {
if (!(bit++ & 7)) {
bits = pgm_read_byte(&bitmap[bo++]);
}
if (bits & 0x80) {
if (size_x == 1 && size_y == 1) {
writePixel(x + xo + xx, y + yo + yy, color);
} else {
writeFillRect(x + (xo16 + xx) * size_x, y + (yo16 + yy) * size_y,
size_x, size_y, color);
}
}
bits <<= 1;
}
}
endWrite();
} // End classic vs custom font
}
/**************************************************************************/
/*!
@brief Print one byte/character of data, used to support print()
@param c The 8-bit ascii character to write
*/
/**************************************************************************/
size_t Adafruit_GFX::write(uint8_t c) {
if (!gfxFont) { // 'Classic' built-in font
if (c == '\n') { // Newline?
cursor_x = 0; // Reset x to zero,
cursor_y += textsize_y * 8; // advance y one line
} else if (c != '\r') { // Ignore carriage returns
if (wrap && ((cursor_x + textsize_x * 6) > _width)) { // Off right?
cursor_x = 0; // Reset x to zero,
cursor_y += textsize_y * 8; // advance y one line
}
drawChar(cursor_x, cursor_y, c, textcolor, textbgcolor, textsize_x,
textsize_y);
cursor_x += textsize_x * 6; // Advance x one char
}
} else { // Custom font
if (c == '\n') {
cursor_x = 0;
cursor_y +=
(int16_t)textsize_y * (uint8_t)pgm_read_byte(&gfxFont->yAdvance);
} else if (c != '\r') {
uint8_t first = pgm_read_byte(&gfxFont->first);
if ((c >= first) && (c <= (uint8_t)pgm_read_byte(&gfxFont->last))) {
GFXglyph *glyph = pgm_read_glyph_ptr(gfxFont, c - first);
uint8_t w = pgm_read_byte(&glyph->width),
h = pgm_read_byte(&glyph->height);
if ((w > 0) && (h > 0)) { // Is there an associated bitmap?
int16_t xo = (int8_t)pgm_read_byte(&glyph->xOffset); // sic
if (wrap && ((cursor_x + textsize_x * (xo + w)) > _width)) {
cursor_x = 0;
cursor_y += (int16_t)textsize_y *
(uint8_t)pgm_read_byte(&gfxFont->yAdvance);
}
drawChar(cursor_x, cursor_y, c, textcolor, textbgcolor, textsize_x,
textsize_y);
}
cursor_x +=
(uint8_t)pgm_read_byte(&glyph->xAdvance) * (int16_t)textsize_x;
}
}
}
return 1;
}
/**************************************************************************/
/*!
@brief Set text 'magnification' size. Each increase in s makes 1 pixel
that much bigger.
@param s Desired text size. 1 is default 6x8, 2 is 12x16, 3 is 18x24, etc
*/
/**************************************************************************/
void Adafruit_GFX::setTextSize(uint8_t s) { setTextSize(s, s); }
/**************************************************************************/
/*!
@brief Set text 'magnification' size. Each increase in s makes 1 pixel
that much bigger.
@param s_x Desired text width magnification level in X-axis. 1 is default
@param s_y Desired text width magnification level in Y-axis. 1 is default
*/
/**************************************************************************/
void Adafruit_GFX::setTextSize(uint8_t s_x, uint8_t s_y) {
textsize_x = (s_x > 0) ? s_x : 1;
textsize_y = (s_y > 0) ? s_y : 1;
}
/**************************************************************************/
/*!
@brief Set rotation setting for display
@param x 0 thru 3 corresponding to 4 cardinal rotations
*/
/**************************************************************************/
void Adafruit_GFX::setRotation(uint8_t x) {
rotation = (x & 3);
switch (rotation) {
case 0:
case 2:
_width = WIDTH;
_height = HEIGHT;
break;
case 1:
case 3:
_width = HEIGHT;
_height = WIDTH;
break;
}
}
/**************************************************************************/
/*!
@brief Set the font to display when print()ing, either custom or default
@param f The GFXfont object, if NULL use built in 6x8 font
*/
/**************************************************************************/
void Adafruit_GFX::setFont(const GFXfont *f) {
if (f) { // Font struct pointer passed in?
if (!gfxFont) { // And no current font struct?
// Switching from classic to new font behavior.
// Move cursor pos down 6 pixels so it's on baseline.
cursor_y += 6;
}
} else if (gfxFont) { // NULL passed. Current font struct defined?
// Switching from new to classic font behavior.
// Move cursor pos up 6 pixels so it's at top-left of char.
cursor_y -= 6;
}
gfxFont = (GFXfont *)f;
}
/**************************************************************************/
/*!
@brief Helper to determine size of a character with current font/size.
Broke this out as it's used by both the PROGMEM- and RAM-resident
getTextBounds() functions.
@param c The ASCII character in question
@param x Pointer to x location of character. Value is modified by
this function to advance to next character.
@param y Pointer to y location of character. Value is modified by
this function to advance to next character.
@param minx Pointer to minimum X coordinate, passed in to AND returned
by this function -- this is used to incrementally build a
bounding rectangle for a string.
@param miny Pointer to minimum Y coord, passed in AND returned.
@param maxx Pointer to maximum X coord, passed in AND returned.
@param maxy Pointer to maximum Y coord, passed in AND returned.
*/
/**************************************************************************/
void Adafruit_GFX::charBounds(unsigned char c, int16_t *x, int16_t *y,
int16_t *minx, int16_t *miny, int16_t *maxx,
int16_t *maxy) {
if (gfxFont) {
if (c == '\n') { // Newline?
*x = 0; // Reset x to zero, advance y by one line
*y += textsize_y * (uint8_t)pgm_read_byte(&gfxFont->yAdvance);
} else if (c != '\r') { // Not a carriage return; is normal char
uint8_t first = pgm_read_byte(&gfxFont->first),
last = pgm_read_byte(&gfxFont->last);
if ((c >= first) && (c <= last)) { // Char present in this font?
GFXglyph *glyph = pgm_read_glyph_ptr(gfxFont, c - first);
uint8_t gw = pgm_read_byte(&glyph->width),
gh = pgm_read_byte(&glyph->height),
xa = pgm_read_byte(&glyph->xAdvance);
int8_t xo = pgm_read_byte(&glyph->xOffset),
yo = pgm_read_byte(&glyph->yOffset);
if (wrap && ((*x + (((int16_t)xo + gw) * textsize_x)) > _width)) {
*x = 0; // Reset x to zero, advance y by one line
*y += textsize_y * (uint8_t)pgm_read_byte(&gfxFont->yAdvance);
}
int16_t tsx = (int16_t)textsize_x, tsy = (int16_t)textsize_y,
x1 = *x + xo * tsx, y1 = *y + yo * tsy, x2 = x1 + gw * tsx - 1,
y2 = y1 + gh * tsy - 1;
if (x1 < *minx)
*minx = x1;
if (y1 < *miny)
*miny = y1;
if (x2 > *maxx)
*maxx = x2;
if (y2 > *maxy)
*maxy = y2;
*x += xa * tsx;
}
}
} else { // Default font
if (c == '\n') { // Newline?
*x = 0; // Reset x to zero,
*y += textsize_y * 8; // advance y one line
// min/max x/y unchaged -- that waits for next 'normal' character
} else if (c != '\r') { // Normal char; ignore carriage returns
if (wrap && ((*x + textsize_x * 6) > _width)) { // Off right?
*x = 0; // Reset x to zero,
*y += textsize_y * 8; // advance y one line
}
int x2 = *x + textsize_x * 6 - 1, // Lower-right pixel of char
y2 = *y + textsize_y * 8 - 1;
if (x2 > *maxx)
*maxx = x2; // Track max x, y
if (y2 > *maxy)
*maxy = y2;
if (*x < *minx)
*minx = *x; // Track min x, y
if (*y < *miny)
*miny = *y;
*x += textsize_x * 6; // Advance x one char
}
}
}
/**************************************************************************/
/*!
@brief Helper to determine size of a string with current font/size.
Pass string and a cursor position, returns UL corner and W,H.
@param str The ASCII string to measure
@param x The current cursor X
@param y The current cursor Y
@param x1 The boundary X coordinate, returned by function
@param y1 The boundary Y coordinate, returned by function
@param w The boundary width, returned by function
@param h The boundary height, returned by function
*/
/**************************************************************************/
void Adafruit_GFX::getTextBounds(const char *str, int16_t x, int16_t y,
int16_t *x1, int16_t *y1, uint16_t *w,
uint16_t *h) {
uint8_t c; // Current character
int16_t minx = 0x7FFF, miny = 0x7FFF, maxx = -1, maxy = -1; // Bound rect
// Bound rect is intentionally initialized inverted, so 1st char sets it
*x1 = x; // Initial position is value passed in
*y1 = y;
*w = *h = 0; // Initial size is zero
while ((c = *str++)) {
// charBounds() modifies x/y to advance for each character,
// and min/max x/y are updated to incrementally build bounding rect.
charBounds(c, &x, &y, &minx, &miny, &maxx, &maxy);
}
if (maxx >= minx) { // If legit string bounds were found...
*x1 = minx; // Update x1 to least X coord,
*w = maxx - minx + 1; // And w to bound rect width
}
if (maxy >= miny) { // Same for height
*y1 = miny;
*h = maxy - miny + 1;
}
}
/**************************************************************************/
/*!
@brief Helper to determine size of a string with current font/size. Pass
string and a cursor position, returns UL corner and W,H.
@param str The ascii string to measure (as an arduino String() class)
@param x The current cursor X
@param y The current cursor Y
@param x1 The boundary X coordinate, set by function
@param y1 The boundary Y coordinate, set by function
@param w The boundary width, set by function
@param h The boundary height, set by function
*/
/**************************************************************************/
#ifdef COMMENT
void Adafruit_GFX::getTextBounds(const String &str, int16_t x, int16_t y,
int16_t *x1, int16_t *y1, uint16_t *w,
uint16_t *h) {
if (str.length() != 0) {
getTextBounds(const_cast<char *>(str.c_str()), x, y, x1, y1, w, h);
}
}
#endif
/**************************************************************************/
/*!
@brief Helper to determine size of a PROGMEM string with current
font/size. Pass string and a cursor position, returns UL corner and W,H.
@param str The flash-memory ascii string to measure
@param x The current cursor X
@param y The current cursor Y
@param x1 The boundary X coordinate, set by function
@param y1 The boundary Y coordinate, set by function
@param w The boundary width, set by function
@param h The boundary height, set by function
*/
/**************************************************************************/
#ifdef COMMENT
void Adafruit_GFX::getTextBounds(const __FlashStringHelper *str, int16_t x,
int16_t y, int16_t *x1, int16_t *y1,
uint16_t *w, uint16_t *h) {
uint8_t *s = (uint8_t *)str, c;
*x1 = x;
*y1 = y;
*w = *h = 0;
int16_t minx = _width, miny = _height, maxx = -1, maxy = -1;
while ((c = pgm_read_byte(s++)))
charBounds(c, &x, &y, &minx, &miny, &maxx, &maxy);
if (maxx >= minx) {
*x1 = minx;
*w = maxx - minx + 1;
}
if (maxy >= miny) {
*y1 = miny;
*h = maxy - miny + 1;
}
}
#endif
/**************************************************************************/
/*!
@brief Invert the display (ideally using built-in hardware command)
@param i True if you want to invert, false to make 'normal'
*/
/**************************************************************************/
void Adafruit_GFX::invertDisplay(bool i) {
// Do nothing, must be subclassed if supported by hardware
(void)i; // disable -Wunused-parameter warning
}
/***************************************************************************/
/**************************************************************************/
/*!
@brief Create a simple drawn button UI element
*/
/**************************************************************************/
Adafruit_GFX_Button::Adafruit_GFX_Button(void) { _gfx = 0; }
/**************************************************************************/
/*!
@brief Initialize button with our desired color/size/settings
@param gfx Pointer to our display so we can draw to it!
@param x The X coordinate of the center of the button
@param y The Y coordinate of the center of the button
@param w Width of the buttton
@param h Height of the buttton
@param outline Color of the outline (16-bit 5-6-5 standard)
@param fill Color of the button fill (16-bit 5-6-5 standard)
@param textcolor Color of the button label (16-bit 5-6-5 standard)
@param label Ascii string of the text inside the button
@param textsize The font magnification of the label text
*/
/**************************************************************************/
// Classic initButton() function: pass center & size
void Adafruit_GFX_Button::initButton(Adafruit_GFX *gfx, int16_t x, int16_t y,
uint16_t w, uint16_t h, uint16_t outline,
uint16_t fill, uint16_t textcolor,
char *label, uint8_t textsize) {
// Tweak arguments and pass to the newer initButtonUL() function...
initButtonUL(gfx, x - (w / 2), y - (h / 2), w, h, outline, fill, textcolor,
label, textsize);
}
/**************************************************************************/
/*!
@brief Initialize button with our desired color/size/settings
@param gfx Pointer to our display so we can draw to it!
@param x The X coordinate of the center of the button
@param y The Y coordinate of the center of the button
@param w Width of the buttton
@param h Height of the buttton
@param outline Color of the outline (16-bit 5-6-5 standard)
@param fill Color of the button fill (16-bit 5-6-5 standard)
@param textcolor Color of the button label (16-bit 5-6-5 standard)
@param label Ascii string of the text inside the button
@param textsize_x The font magnification in X-axis of the label text
@param textsize_y The font magnification in Y-axis of the label text
*/
/**************************************************************************/
// Classic initButton() function: pass center & size
void Adafruit_GFX_Button::initButton(Adafruit_GFX *gfx, int16_t x, int16_t y,
uint16_t w, uint16_t h, uint16_t outline,
uint16_t fill, uint16_t textcolor,
char *label, uint8_t textsize_x,
uint8_t textsize_y) {
// Tweak arguments and pass to the newer initButtonUL() function...
initButtonUL(gfx, x - (w / 2), y - (h / 2), w, h, outline, fill, textcolor,
label, textsize_x, textsize_y);
}
/**************************************************************************/
/*!
@brief Initialize button with our desired color/size/settings, with
upper-left coordinates
@param gfx Pointer to our display so we can draw to it!
@param x1 The X coordinate of the Upper-Left corner of the button
@param y1 The Y coordinate of the Upper-Left corner of the button
@param w Width of the buttton
@param h Height of the buttton
@param outline Color of the outline (16-bit 5-6-5 standard)
@param fill Color of the button fill (16-bit 5-6-5 standard)
@param textcolor Color of the button label (16-bit 5-6-5 standard)
@param label Ascii string of the text inside the button
@param textsize The font magnification of the label text
*/
/**************************************************************************/
void Adafruit_GFX_Button::initButtonUL(Adafruit_GFX *gfx, int16_t x1,
int16_t y1, uint16_t w, uint16_t h,
uint16_t outline, uint16_t fill,
uint16_t textcolor, char *label,
uint8_t textsize) {
initButtonUL(gfx, x1, y1, w, h, outline, fill, textcolor, label, textsize,
textsize);
}
/**************************************************************************/
/*!
@brief Initialize button with our desired color/size/settings, with
upper-left coordinates
@param gfx Pointer to our display so we can draw to it!
@param x1 The X coordinate of the Upper-Left corner of the button
@param y1 The Y coordinate of the Upper-Left corner of the button
@param w Width of the buttton
@param h Height of the buttton
@param outline Color of the outline (16-bit 5-6-5 standard)
@param fill Color of the button fill (16-bit 5-6-5 standard)
@param textcolor Color of the button label (16-bit 5-6-5 standard)
@param label Ascii string of the text inside the button
@param textsize_x The font magnification in X-axis of the label text
@param textsize_y The font magnification in Y-axis of the label text
*/
/**************************************************************************/
void Adafruit_GFX_Button::initButtonUL(Adafruit_GFX *gfx, int16_t x1,
int16_t y1, uint16_t w, uint16_t h,
uint16_t outline, uint16_t fill,
uint16_t textcolor, char *label,
uint8_t textsize_x, uint8_t textsize_y) {
_x1 = x1;
_y1 = y1;
_w = w;
_h = h;
_outlinecolor = outline;
_fillcolor = fill;
_textcolor = textcolor;
_textsize_x = textsize_x;
_textsize_y = textsize_y;
_gfx = gfx;
strncpy(_label, label, 9);
_label[9] = 0; // strncpy does not place a null at the end.
// When 'label' is >9 characters, _label is not terminated.
}
/**************************************************************************/
/*!
@brief Draw the button on the screen
@param inverted Whether to draw with fill/text swapped to indicate
'pressed'
*/
/**************************************************************************/
void Adafruit_GFX_Button::drawButton(bool inverted) {
uint16_t fill, outline, text;
if (!inverted) {
fill = _fillcolor;
outline = _outlinecolor;
text = _textcolor;
} else {
fill = _textcolor;
outline = _outlinecolor;
text = _fillcolor;
}
uint8_t r = min(_w, _h) / 4; // Corner radius
_gfx->fillRoundRect(_x1, _y1, _w, _h, r, fill);
_gfx->drawRoundRect(_x1, _y1, _w, _h, r, outline);
_gfx->setCursor(_x1 + (_w / 2) - (strlen(_label) * 3 * _textsize_x),
_y1 + (_h / 2) - (4 * _textsize_y));
_gfx->setTextColor(text);
_gfx->setTextSize(_textsize_x, _textsize_y);
//_gfx->print(_label); The Print Class does not exist for STM32 only Arduino
Error_Handler();
}
/**************************************************************************/
/*!
@brief Helper to let us know if a coordinate is within the bounds of the
button
@param x The X coordinate to check
@param y The Y coordinate to check
@returns True if within button graphics outline
*/
/**************************************************************************/
bool Adafruit_GFX_Button::contains(int16_t x, int16_t y) {
return ((x >= _x1) && (x < (int16_t)(_x1 + _w)) && (y >= _y1) &&
(y < (int16_t)(_y1 + _h)));
}
/**************************************************************************/
/*!
@brief Query whether the button was pressed since we last checked state
@returns True if was not-pressed before, now is.
*/
/**************************************************************************/
bool Adafruit_GFX_Button::justPressed() { return (currstate && !laststate); }
/**************************************************************************/
/*!
@brief Query whether the button was released since we last checked state
@returns True if was pressed before, now is not.
*/
/**************************************************************************/
bool Adafruit_GFX_Button::justReleased() { return (!currstate && laststate); }
// -------------------------------------------------------------------------
// GFXcanvas1, GFXcanvas8 and GFXcanvas16 (currently a WIP, don't get too
// comfy with the implementation) provide 1-, 8- and 16-bit offscreen
// canvases, the address of which can be passed to drawBitmap() or
// pushColors() (the latter appears only in a couple of GFX-subclassed TFT
// libraries at this time). This is here mostly to help with the recently-
// added proportionally-spaced fonts; adds a way to refresh a section of the
// screen without a massive flickering clear-and-redraw...but maybe you'll
// find other uses too. VERY RAM-intensive, since the buffer is in MCU
// memory and not the display driver...GXFcanvas1 might be minimally useful
// on an Uno-class board, but this and the others are much more likely to
// require at least a Mega or various recent ARM-type boards (recommended,
// as the text+bitmap draw can be pokey). GFXcanvas1 requires 1 bit per
// pixel (rounded up to nearest byte per scanline), GFXcanvas8 is 1 byte
// per pixel (no scanline pad), and GFXcanvas16 uses 2 bytes per pixel (no
// scanline pad).
// NOT EXTENSIVELY TESTED YET. MAY CONTAIN WORST BUGS KNOWN TO HUMANKIND.
#ifdef __AVR__
// Bitmask tables of 0x80>>X and ~(0x80>>X), because X>>Y is slow on AVR
const uint8_t PROGMEM GFXcanvas1::GFXsetBit[] = {0x80, 0x40, 0x20, 0x10,
0x08, 0x04, 0x02, 0x01};
const uint8_t PROGMEM GFXcanvas1::GFXclrBit[] = {0x7F, 0xBF, 0xDF, 0xEF,
0xF7, 0xFB, 0xFD, 0xFE};
#endif
/**************************************************************************/
/*!
@brief Instatiate a GFX 1-bit canvas context for graphics
@param w Display width, in pixels
@param h Display height, in pixels
*/
/**************************************************************************/
GFXcanvas1::GFXcanvas1(uint16_t w, uint16_t h) : Adafruit_GFX(w, h) {
uint32_t bytes = ((w + 7) / 8) * h;
if ((buffer = (uint8_t *)malloc(bytes))) {
memset(buffer, 0, bytes);
}
}
/**************************************************************************/
/*!
@brief Delete the canvas, free memory
*/
/**************************************************************************/
GFXcanvas1::~GFXcanvas1(void) {
if (buffer)
free(buffer);
}
/**************************************************************************/
/*!
@brief Draw a pixel to the canvas framebuffer
@param x x coordinate
@param y y coordinate
@param color Binary (on or off) color to fill with
*/
/**************************************************************************/
void GFXcanvas1::drawPixel(int16_t x, int16_t y, uint16_t color) {
if (buffer) {
if ((x < 0) || (y < 0) || (x >= _width) || (y >= _height))
return;
int16_t t;
switch (rotation) {
case 1:
t = x;
x = WIDTH - 1 - y;
y = t;
break;
case 2:
x = WIDTH - 1 - x;
y = HEIGHT - 1 - y;
break;
case 3:
t = x;
x = y;
y = HEIGHT - 1 - t;
break;
}
uint8_t *ptr = &buffer[(x / 8) + y * ((WIDTH + 7) / 8)];
#ifdef __AVR__
if (color)
*ptr |= pgm_read_byte(&GFXsetBit[x & 7]);
else
*ptr &= pgm_read_byte(&GFXclrBit[x & 7]);
#else
if (color)
*ptr |= 0x80 >> (x & 7);
else
*ptr &= ~(0x80 >> (x & 7));
#endif
}
}
/**********************************************************************/
/*!
@brief Get the pixel color value at a given coordinate
@param x x coordinate
@param y y coordinate
@returns The desired pixel's binary color value, either 0x1 (on) or 0x0
(off)
*/
/**********************************************************************/
bool GFXcanvas1::getPixel(int16_t x, int16_t y) const {
int16_t t;
switch (rotation) {
case 1:
t = x;
x = WIDTH - 1 - y;
y = t;
break;
case 2:
x = WIDTH - 1 - x;
y = HEIGHT - 1 - y;
break;
case 3:
t = x;
x = y;
y = HEIGHT - 1 - t;
break;
}
return getRawPixel(x, y);
}
/**********************************************************************/
/*!
@brief Get the pixel color value at a given, unrotated coordinate.
This method is intended for hardware drivers to get pixel value
in physical coordinates.
@param x x coordinate
@param y y coordinate
@returns The desired pixel's binary color value, either 0x1 (on) or 0x0
(off)
*/
/**********************************************************************/
bool GFXcanvas1::getRawPixel(int16_t x, int16_t y) const {
if ((x < 0) || (y < 0) || (x >= WIDTH) || (y >= HEIGHT))
return 0;
if (buffer) {
uint8_t *ptr = &buffer[(x / 8) + y * ((WIDTH + 7) / 8)];
#ifdef __AVR__
return ((*ptr) & pgm_read_byte(&GFXsetBit[x & 7])) != 0;
#else
return ((*ptr) & (0x80 >> (x & 7))) != 0;
#endif
}
return 0;
}
/**************************************************************************/
/*!
@brief Fill the framebuffer completely with one color
@param color Binary (on or off) color to fill with
*/
/**************************************************************************/
void GFXcanvas1::fillScreen(uint16_t color) {
if (buffer) {
uint32_t bytes = ((WIDTH + 7) / 8) * HEIGHT;
memset(buffer, color ? 0xFF : 0x00, bytes);
}
}
/**************************************************************************/
/*!
@brief Speed optimized vertical line drawing
@param x Line horizontal start point
@param y Line vertical start point
@param h Length of vertical line to be drawn, including first point
@param color Color to fill with
*/
/**************************************************************************/
void GFXcanvas1::drawFastVLine(int16_t x, int16_t y, int16_t h,
uint16_t color) {
if (h < 0) { // Convert negative heights to positive equivalent
h *= -1;
y -= h - 1;
if (y < 0) {
h += y;
y = 0;
}
}
// Edge rejection (no-draw if totally off canvas)
if ((x < 0) || (x >= width()) || (y >= height()) || ((y + h - 1) < 0)) {
return;
}
if (y < 0) { // Clip top
h += y;
y = 0;
}
if (y + h > height()) { // Clip bottom
h = height() - y;
}
if (getRotation() == 0) {
drawFastRawVLine(x, y, h, color);
} else if (getRotation() == 1) {
int16_t t = x;
x = WIDTH - 1 - y;
y = t;
x -= h - 1;
drawFastRawHLine(x, y, h, color);
} else if (getRotation() == 2) {
x = WIDTH - 1 - x;
y = HEIGHT - 1 - y;
y -= h - 1;
drawFastRawVLine(x, y, h, color);
} else if (getRotation() == 3) {
int16_t t = x;
x = y;
y = HEIGHT - 1 - t;
drawFastRawHLine(x, y, h, color);
}
}
/**************************************************************************/
/*!
@brief Speed optimized horizontal line drawing
@param x Line horizontal start point
@param y Line vertical start point
@param w Length of horizontal line to be drawn, including first point
@param color Color to fill with
*/
/**************************************************************************/
void GFXcanvas1::drawFastHLine(int16_t x, int16_t y, int16_t w,
uint16_t color) {
if (w < 0) { // Convert negative widths to positive equivalent
w *= -1;
x -= w - 1;
if (x < 0) {
w += x;
x = 0;
}
}
// Edge rejection (no-draw if totally off canvas)
if ((y < 0) || (y >= height()) || (x >= width()) || ((x + w - 1) < 0)) {
return;
}
if (x < 0) { // Clip left
w += x;
x = 0;
}
if (x + w >= width()) { // Clip right
w = width() - x;
}
if (getRotation() == 0) {
drawFastRawHLine(x, y, w, color);
} else if (getRotation() == 1) {
int16_t t = x;
x = WIDTH - 1 - y;
y = t;
drawFastRawVLine(x, y, w, color);
} else if (getRotation() == 2) {
x = WIDTH - 1 - x;
y = HEIGHT - 1 - y;
x -= w - 1;
drawFastRawHLine(x, y, w, color);
} else if (getRotation() == 3) {
int16_t t = x;
x = y;
y = HEIGHT - 1 - t;
y -= w - 1;
drawFastRawVLine(x, y, w, color);
}
}
/**************************************************************************/
/*!
@brief Speed optimized vertical line drawing into the raw canvas buffer
@param x Line horizontal start point
@param y Line vertical start point
@param h length of vertical line to be drawn, including first point
@param color Binary (on or off) color to fill with
*/
/**************************************************************************/
void GFXcanvas1::drawFastRawVLine(int16_t x, int16_t y, int16_t h,
uint16_t color) {
// x & y already in raw (rotation 0) coordinates, no need to transform.
int16_t row_bytes = ((WIDTH + 7) / 8);
uint8_t *ptr = &buffer[(x / 8) + y * row_bytes];
if (color > 0) {
#ifdef __AVR__
uint8_t bit_mask = pgm_read_byte(&GFXsetBit[x & 7]);
#else
uint8_t bit_mask = (0x80 >> (x & 7));
#endif
for (int16_t i = 0; i < h; i++) {
*ptr |= bit_mask;
ptr += row_bytes;
}
} else {
#ifdef __AVR__
uint8_t bit_mask = pgm_read_byte(&GFXclrBit[x & 7]);
#else
uint8_t bit_mask = ~(0x80 >> (x & 7));
#endif
for (int16_t i = 0; i < h; i++) {
*ptr &= bit_mask;
ptr += row_bytes;
}
}
}
/**************************************************************************/
/*!
@brief Speed optimized horizontal line drawing into the raw canvas buffer
@param x Line horizontal start point
@param y Line vertical start point
@param w length of horizontal line to be drawn, including first point
@param color Binary (on or off) color to fill with
*/
/**************************************************************************/
void GFXcanvas1::drawFastRawHLine(int16_t x, int16_t y, int16_t w,
uint16_t color) {
// x & y already in raw (rotation 0) coordinates, no need to transform.
int16_t rowBytes = ((WIDTH + 7) / 8);
uint8_t *ptr = &buffer[(x / 8) + y * rowBytes];
size_t remainingWidthBits = w;
// check to see if first byte needs to be partially filled
if ((x & 7) > 0) {
// create bit mask for first byte
uint8_t startByteBitMask = 0x00;
for (int8_t i = (x & 7); ((i < 8) && (remainingWidthBits > 0)); i++) {
#ifdef __AVR__
startByteBitMask |= pgm_read_byte(&GFXsetBit[i]);
#else
startByteBitMask |= (0x80 >> i);
#endif
remainingWidthBits--;
}
if (color > 0) {
*ptr |= startByteBitMask;
} else {
*ptr &= ~startByteBitMask;
}
ptr++;
}
// do the next remainingWidthBits bits
if (remainingWidthBits > 0) {
size_t remainingWholeBytes = remainingWidthBits / 8;
size_t lastByteBits = remainingWidthBits % 8;
uint8_t wholeByteColor = color > 0 ? 0xFF : 0x00;
memset(ptr, wholeByteColor, remainingWholeBytes);
if (lastByteBits > 0) {
uint8_t lastByteBitMask = 0x00;
for (size_t i = 0; i < lastByteBits; i++) {
#ifdef __AVR__
lastByteBitMask |= pgm_read_byte(&GFXsetBit[i]);
#else
lastByteBitMask |= (0x80 >> i);
#endif
}
ptr += remainingWholeBytes;
if (color > 0) {
*ptr |= lastByteBitMask;
} else {
*ptr &= ~lastByteBitMask;
}
}
}
}
/**************************************************************************/
/*!
@brief Instatiate a GFX 8-bit canvas context for graphics
@param w Display width, in pixels
@param h Display height, in pixels
*/
/**************************************************************************/
GFXcanvas8::GFXcanvas8(uint16_t w, uint16_t h) : Adafruit_GFX(w, h) {
uint32_t bytes = w * h;
if ((buffer = (uint8_t *)malloc(bytes))) {
memset(buffer, 0, bytes);
}
}
/**************************************************************************/
/*!
@brief Delete the canvas, free memory
*/
/**************************************************************************/
GFXcanvas8::~GFXcanvas8(void) {
if (buffer)
free(buffer);
}
/**************************************************************************/
/*!
@brief Draw a pixel to the canvas framebuffer
@param x x coordinate
@param y y coordinate
@param color 8-bit Color to fill with. Only lower byte of uint16_t is used.
*/
/**************************************************************************/
void GFXcanvas8::drawPixel(int16_t x, int16_t y, uint16_t color) {
if (buffer) {
if ((x < 0) || (y < 0) || (x >= _width) || (y >= _height))
return;
int16_t t;
switch (rotation) {
case 1:
t = x;
x = WIDTH - 1 - y;
y = t;
break;
case 2:
x = WIDTH - 1 - x;
y = HEIGHT - 1 - y;
break;
case 3:
t = x;
x = y;
y = HEIGHT - 1 - t;
break;
}
buffer[x + y * WIDTH] = color;
}
}
/**********************************************************************/
/*!
@brief Get the pixel color value at a given coordinate
@param x x coordinate
@param y y coordinate
@returns The desired pixel's 8-bit color value
*/
/**********************************************************************/
uint8_t GFXcanvas8::getPixel(int16_t x, int16_t y) const {
int16_t t;
switch (rotation) {
case 1:
t = x;
x = WIDTH - 1 - y;
y = t;
break;
case 2:
x = WIDTH - 1 - x;
y = HEIGHT - 1 - y;
break;
case 3:
t = x;
x = y;
y = HEIGHT - 1 - t;
break;
}
return getRawPixel(x, y);
}
/**********************************************************************/
/*!
@brief Get the pixel color value at a given, unrotated coordinate.
This method is intended for hardware drivers to get pixel value
in physical coordinates.
@param x x coordinate
@param y y coordinate
@returns The desired pixel's 8-bit color value
*/
/**********************************************************************/
uint8_t GFXcanvas8::getRawPixel(int16_t x, int16_t y) const {
if ((x < 0) || (y < 0) || (x >= WIDTH) || (y >= HEIGHT))
return 0;
if (buffer) {
return buffer[x + y * WIDTH];
}
return 0;
}
/**************************************************************************/
/*!
@brief Fill the framebuffer completely with one color
@param color 8-bit Color to fill with. Only lower byte of uint16_t is used.
*/
/**************************************************************************/
void GFXcanvas8::fillScreen(uint16_t color) {
if (buffer) {
memset(buffer, color, WIDTH * HEIGHT);
}
}
/**************************************************************************/
/*!
@brief Speed optimized vertical line drawing
@param x Line horizontal start point
@param y Line vertical start point
@param h Length of vertical line to be drawn, including first point
@param color 8-bit Color to fill with. Only lower byte of uint16_t is
used.
*/
/**************************************************************************/
void GFXcanvas8::drawFastVLine(int16_t x, int16_t y, int16_t h,
uint16_t color) {
if (h < 0) { // Convert negative heights to positive equivalent
h *= -1;
y -= h - 1;
if (y < 0) {
h += y;
y = 0;
}
}
// Edge rejection (no-draw if totally off canvas)
if ((x < 0) || (x >= width()) || (y >= height()) || ((y + h - 1) < 0)) {
return;
}
if (y < 0) { // Clip top
h += y;
y = 0;
}
if (y + h > height()) { // Clip bottom
h = height() - y;
}
if (getRotation() == 0) {
drawFastRawVLine(x, y, h, color);
} else if (getRotation() == 1) {
int16_t t = x;
x = WIDTH - 1 - y;
y = t;
x -= h - 1;
drawFastRawHLine(x, y, h, color);
} else if (getRotation() == 2) {
x = WIDTH - 1 - x;
y = HEIGHT - 1 - y;
y -= h - 1;
drawFastRawVLine(x, y, h, color);
} else if (getRotation() == 3) {
int16_t t = x;
x = y;
y = HEIGHT - 1 - t;
drawFastRawHLine(x, y, h, color);
}
}
/**************************************************************************/
/*!
@brief Speed optimized horizontal line drawing
@param x Line horizontal start point
@param y Line vertical start point
@param w Length of horizontal line to be drawn, including 1st point
@param color 8-bit Color to fill with. Only lower byte of uint16_t is
used.
*/
/**************************************************************************/
void GFXcanvas8::drawFastHLine(int16_t x, int16_t y, int16_t w,
uint16_t color) {
if (w < 0) { // Convert negative widths to positive equivalent
w *= -1;
x -= w - 1;
if (x < 0) {
w += x;
x = 0;
}
}
// Edge rejection (no-draw if totally off canvas)
if ((y < 0) || (y >= height()) || (x >= width()) || ((x + w - 1) < 0)) {
return;
}
if (x < 0) { // Clip left
w += x;
x = 0;
}
if (x + w >= width()) { // Clip right
w = width() - x;
}
if (getRotation() == 0) {
drawFastRawHLine(x, y, w, color);
} else if (getRotation() == 1) {
int16_t t = x;
x = WIDTH - 1 - y;
y = t;
drawFastRawVLine(x, y, w, color);
} else if (getRotation() == 2) {
x = WIDTH - 1 - x;
y = HEIGHT - 1 - y;
x -= w - 1;
drawFastRawHLine(x, y, w, color);
} else if (getRotation() == 3) {
int16_t t = x;
x = y;
y = HEIGHT - 1 - t;
y -= w - 1;
drawFastRawVLine(x, y, w, color);
}
}
/**************************************************************************/
/*!
@brief Speed optimized vertical line drawing into the raw canvas buffer
@param x Line horizontal start point
@param y Line vertical start point
@param h length of vertical line to be drawn, including first point
@param color 8-bit Color to fill with. Only lower byte of uint16_t is
used.
*/
/**************************************************************************/
void GFXcanvas8::drawFastRawVLine(int16_t x, int16_t y, int16_t h,
uint16_t color) {
// x & y already in raw (rotation 0) coordinates, no need to transform.
uint8_t *buffer_ptr = buffer + y * WIDTH + x;
for (int16_t i = 0; i < h; i++) {
(*buffer_ptr) = color;
buffer_ptr += WIDTH;
}
}
/**************************************************************************/
/*!
@brief Speed optimized horizontal line drawing into the raw canvas buffer
@param x Line horizontal start point
@param y Line vertical start point
@param w length of horizontal line to be drawn, including first point
@param color 8-bit Color to fill with. Only lower byte of uint16_t is
used.
*/
/**************************************************************************/
void GFXcanvas8::drawFastRawHLine(int16_t x, int16_t y, int16_t w,
uint16_t color) {
// x & y already in raw (rotation 0) coordinates, no need to transform.
memset(buffer + y * WIDTH + x, color, w);
}
/**************************************************************************/
/*!
@brief Instatiate a GFX 16-bit canvas context for graphics
@param w Display width, in pixels
@param h Display height, in pixels
*/
/**************************************************************************/
GFXcanvas16::GFXcanvas16(uint16_t w, uint16_t h) : Adafruit_GFX(w, h) {
uint32_t bytes = w * h * 2;
if ((buffer = (uint16_t *)malloc(bytes))) {
memset(buffer, 0, bytes);
}
}
/**************************************************************************/
/*!
@brief Delete the canvas, free memory
*/
/**************************************************************************/
GFXcanvas16::~GFXcanvas16(void) {
if (buffer)
free(buffer);
}
/**************************************************************************/
/*!
@brief Draw a pixel to the canvas framebuffer
@param x x coordinate
@param y y coordinate
@param color 16-bit 5-6-5 Color to fill with
*/
/**************************************************************************/
void GFXcanvas16::drawPixel(int16_t x, int16_t y, uint16_t color) {
if (buffer) {
if ((x < 0) || (y < 0) || (x >= _width) || (y >= _height))
return;
int16_t t;
switch (rotation) {
case 1:
t = x;
x = WIDTH - 1 - y;
y = t;
break;
case 2:
x = WIDTH - 1 - x;
y = HEIGHT - 1 - y;
break;
case 3:
t = x;
x = y;
y = HEIGHT - 1 - t;
break;
}
buffer[x + y * WIDTH] = color;
}
}
/**********************************************************************/
/*!
@brief Get the pixel color value at a given coordinate
@param x x coordinate
@param y y coordinate
@returns The desired pixel's 16-bit 5-6-5 color value
*/
/**********************************************************************/
uint16_t GFXcanvas16::getPixel(int16_t x, int16_t y) const {
int16_t t;
switch (rotation) {
case 1:
t = x;
x = WIDTH - 1 - y;
y = t;
break;
case 2:
x = WIDTH - 1 - x;
y = HEIGHT - 1 - y;
break;
case 3:
t = x;
x = y;
y = HEIGHT - 1 - t;
break;
}
return getRawPixel(x, y);
}
/**********************************************************************/
/*!
@brief Get the pixel color value at a given, unrotated coordinate.
This method is intended for hardware drivers to get pixel value
in physical coordinates.
@param x x coordinate
@param y y coordinate
@returns The desired pixel's 16-bit 5-6-5 color value
*/
/**********************************************************************/
uint16_t GFXcanvas16::getRawPixel(int16_t x, int16_t y) const {
if ((x < 0) || (y < 0) || (x >= WIDTH) || (y >= HEIGHT))
return 0;
if (buffer) {
return buffer[x + y * WIDTH];
}
return 0;
}
/**************************************************************************/
/*!
@brief Fill the framebuffer completely with one color
@param color 16-bit 5-6-5 Color to fill with
*/
/**************************************************************************/
void GFXcanvas16::fillScreen(uint16_t color) {
if (buffer) {
uint8_t hi = color >> 8, lo = color & 0xFF;
if (hi == lo) {
memset(buffer, lo, WIDTH * HEIGHT * 2);
} else {
uint32_t i, pixels = WIDTH * HEIGHT;
for (i = 0; i < pixels; i++)
buffer[i] = color;
}
}
}
/**************************************************************************/
/*!
@brief Reverses the "endian-ness" of each 16-bit pixel within the
canvas; little-endian to big-endian, or big-endian to little.
Most microcontrollers (such as SAMD) are little-endian, while
most displays tend toward big-endianness. All the drawing
functions (including RGB bitmap drawing) take care of this
automatically, but some specialized code (usually involving
DMA) can benefit from having pixel data already in the
display-native order. Note that this does NOT convert to a
SPECIFIC endian-ness, it just flips the bytes within each word.
*/
/**************************************************************************/
void GFXcanvas16::byteSwap(void) {
if (buffer) {
uint32_t i, pixels = WIDTH * HEIGHT;
for (i = 0; i < pixels; i++)
buffer[i] = __builtin_bswap16(buffer[i]);
}
}
/**************************************************************************/
/*!
@brief Speed optimized vertical line drawing
@param x Line horizontal start point
@param y Line vertical start point
@param h length of vertical line to be drawn, including first point
@param color color 16-bit 5-6-5 Color to draw line with
*/
/**************************************************************************/
void GFXcanvas16::drawFastVLine(int16_t x, int16_t y, int16_t h,
uint16_t color) {
if (h < 0) { // Convert negative heights to positive equivalent
h *= -1;
y -= h - 1;
if (y < 0) {
h += y;
y = 0;
}
}
// Edge rejection (no-draw if totally off canvas)
if ((x < 0) || (x >= width()) || (y >= height()) || ((y + h - 1) < 0)) {
return;
}
if (y < 0) { // Clip top
h += y;
y = 0;
}
if (y + h > height()) { // Clip bottom
h = height() - y;
}
if (getRotation() == 0) {
drawFastRawVLine(x, y, h, color);
} else if (getRotation() == 1) {
int16_t t = x;
x = WIDTH - 1 - y;
y = t;
x -= h - 1;
drawFastRawHLine(x, y, h, color);
} else if (getRotation() == 2) {
x = WIDTH - 1 - x;
y = HEIGHT - 1 - y;
y -= h - 1;
drawFastRawVLine(x, y, h, color);
} else if (getRotation() == 3) {
int16_t t = x;
x = y;
y = HEIGHT - 1 - t;
drawFastRawHLine(x, y, h, color);
}
}
/**************************************************************************/
/*!
@brief Speed optimized horizontal line drawing
@param x Line horizontal start point
@param y Line vertical start point
@param w Length of horizontal line to be drawn, including 1st point
@param color Color 16-bit 5-6-5 Color to draw line with
*/
/**************************************************************************/
void GFXcanvas16::drawFastHLine(int16_t x, int16_t y, int16_t w,
uint16_t color) {
if (w < 0) { // Convert negative widths to positive equivalent
w *= -1;
x -= w - 1;
if (x < 0) {
w += x;
x = 0;
}
}
// Edge rejection (no-draw if totally off canvas)
if ((y < 0) || (y >= height()) || (x >= width()) || ((x + w - 1) < 0)) {
return;
}
if (x < 0) { // Clip left
w += x;
x = 0;
}
if (x + w >= width()) { // Clip right
w = width() - x;
}
if (getRotation() == 0) {
drawFastRawHLine(x, y, w, color);
} else if (getRotation() == 1) {
int16_t t = x;
x = WIDTH - 1 - y;
y = t;
drawFastRawVLine(x, y, w, color);
} else if (getRotation() == 2) {
x = WIDTH - 1 - x;
y = HEIGHT - 1 - y;
x -= w - 1;
drawFastRawHLine(x, y, w, color);
} else if (getRotation() == 3) {
int16_t t = x;
x = y;
y = HEIGHT - 1 - t;
y -= w - 1;
drawFastRawVLine(x, y, w, color);
}
}
/**************************************************************************/
/*!
@brief Speed optimized vertical line drawing into the raw canvas buffer
@param x Line horizontal start point
@param y Line vertical start point
@param h length of vertical line to be drawn, including first point
@param color color 16-bit 5-6-5 Color to draw line with
*/
/**************************************************************************/
void GFXcanvas16::drawFastRawVLine(int16_t x, int16_t y, int16_t h,
uint16_t color) {
// x & y already in raw (rotation 0) coordinates, no need to transform.
uint16_t *buffer_ptr = buffer + y * WIDTH + x;
for (int16_t i = 0; i < h; i++) {
(*buffer_ptr) = color;
buffer_ptr += WIDTH;
}
}
/**************************************************************************/
/*!
@brief Speed optimized horizontal line drawing into the raw canvas buffer
@param x Line horizontal start point
@param y Line vertical start point
@param w length of horizontal line to be drawn, including first point
@param color color 16-bit 5-6-5 Color to draw line with
*/
/**************************************************************************/
void GFXcanvas16::drawFastRawHLine(int16_t x, int16_t y, int16_t w,
uint16_t color) {
// x & y already in raw (rotation 0) coordinates, no need to transform.
uint32_t buffer_index = y * WIDTH + x;
for (uint32_t i = buffer_index; i < buffer_index + w; i++) {
buffer[i] = color;
}
}
gfxfont.h
// Font structures for newer Adafruit_GFX (1.1 and later).
// Example fonts are included in 'Fonts' directory.
// To use a font in your Arduino sketch, #include the corresponding .h
// file and pass address of GFXfont struct to setFont(). Pass NULL to
// revert to 'classic' fixed-space bitmap font.
#ifndef _GFXFONT_H_
#define _GFXFONT_H_
/// Font data stored PER GLYPH
typedef struct {
uint16_t bitmapOffset; ///< Pointer into GFXfont->bitmap
uint8_t width; ///< Bitmap dimensions in pixels
uint8_t height; ///< Bitmap dimensions in pixels
uint8_t xAdvance; ///< Distance to advance cursor (x axis)
int8_t xOffset; ///< X dist from cursor pos to UL corner
int8_t yOffset; ///< Y dist from cursor pos to UL corner
} GFXglyph;
/// Data stored for FONT AS A WHOLE
typedef struct {
uint8_t *bitmap; ///< Glyph bitmaps, concatenated
GFXglyph *glyph; ///< Glyph array
uint16_t first; ///< ASCII extents (first char)
uint16_t last; ///< ASCII extents (last char)
uint8_t yAdvance; ///< Newline distance (y axis)
} GFXfont;
#endif // _GFXFONT_H_
glcdfont.h
// This is the 'classic' fixed-space bitmap font for Adafruit_GFX since 1.0.
// See gfxfont.h for newer custom bitmap font info.
#ifndef FONT5X7_H
#define FONT5X7_H
#ifdef __AVR__
#include <avr/io.h>
#include <avr/pgmspace.h>
#elif defined(ESP8266)
#include <pgmspace.h>
#elif defined(__IMXRT1052__) || defined(__IMXRT1062__)
// PROGMEM is defefind for T4 to place data in specific memory section
#undef PROGMEM
#define PROGMEM
#else
#define PROGMEM
#endif
// Standard ASCII 5x7 font
static const unsigned char font[] PROGMEM = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x3E, 0x5B, 0x4F, 0x5B, 0x3E, 0x3E, 0x6B,
0x4F, 0x6B, 0x3E, 0x1C, 0x3E, 0x7C, 0x3E, 0x1C, 0x18, 0x3C, 0x7E, 0x3C,
0x18, 0x1C, 0x57, 0x7D, 0x57, 0x1C, 0x1C, 0x5E, 0x7F, 0x5E, 0x1C, 0x00,
0x18, 0x3C, 0x18, 0x00, 0xFF, 0xE7, 0xC3, 0xE7, 0xFF, 0x00, 0x18, 0x24,
0x18, 0x00, 0xFF, 0xE7, 0xDB, 0xE7, 0xFF, 0x30, 0x48, 0x3A, 0x06, 0x0E,
0x26, 0x29, 0x79, 0x29, 0x26, 0x40, 0x7F, 0x05, 0x05, 0x07, 0x40, 0x7F,
0x05, 0x25, 0x3F, 0x5A, 0x3C, 0xE7, 0x3C, 0x5A, 0x7F, 0x3E, 0x1C, 0x1C,
0x08, 0x08, 0x1C, 0x1C, 0x3E, 0x7F, 0x14, 0x22, 0x7F, 0x22, 0x14, 0x5F,
0x5F, 0x00, 0x5F, 0x5F, 0x06, 0x09, 0x7F, 0x01, 0x7F, 0x00, 0x66, 0x89,
0x95, 0x6A, 0x60, 0x60, 0x60, 0x60, 0x60, 0x94, 0xA2, 0xFF, 0xA2, 0x94,
0x08, 0x04, 0x7E, 0x04, 0x08, 0x10, 0x20, 0x7E, 0x20, 0x10, 0x08, 0x08,
0x2A, 0x1C, 0x08, 0x08, 0x1C, 0x2A, 0x08, 0x08, 0x1E, 0x10, 0x10, 0x10,
0x10, 0x0C, 0x1E, 0x0C, 0x1E, 0x0C, 0x30, 0x38, 0x3E, 0x38, 0x30, 0x06,
0x0E, 0x3E, 0x0E, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5F,
0x00, 0x00, 0x00, 0x07, 0x00, 0x07, 0x00, 0x14, 0x7F, 0x14, 0x7F, 0x14,
0x24, 0x2A, 0x7F, 0x2A, 0x12, 0x23, 0x13, 0x08, 0x64, 0x62, 0x36, 0x49,
0x56, 0x20, 0x50, 0x00, 0x08, 0x07, 0x03, 0x00, 0x00, 0x1C, 0x22, 0x41,
0x00, 0x00, 0x41, 0x22, 0x1C, 0x00, 0x2A, 0x1C, 0x7F, 0x1C, 0x2A, 0x08,
0x08, 0x3E, 0x08, 0x08, 0x00, 0x80, 0x70, 0x30, 0x00, 0x08, 0x08, 0x08,
0x08, 0x08, 0x00, 0x00, 0x60, 0x60, 0x00, 0x20, 0x10, 0x08, 0x04, 0x02,
0x3E, 0x51, 0x49, 0x45, 0x3E, 0x00, 0x42, 0x7F, 0x40, 0x00, 0x72, 0x49,
0x49, 0x49, 0x46, 0x21, 0x41, 0x49, 0x4D, 0x33, 0x18, 0x14, 0x12, 0x7F,
0x10, 0x27, 0x45, 0x45, 0x45, 0x39, 0x3C, 0x4A, 0x49, 0x49, 0x31, 0x41,
0x21, 0x11, 0x09, 0x07, 0x36, 0x49, 0x49, 0x49, 0x36, 0x46, 0x49, 0x49,
0x29, 0x1E, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x40, 0x34, 0x00, 0x00,
0x00, 0x08, 0x14, 0x22, 0x41, 0x14, 0x14, 0x14, 0x14, 0x14, 0x00, 0x41,
0x22, 0x14, 0x08, 0x02, 0x01, 0x59, 0x09, 0x06, 0x3E, 0x41, 0x5D, 0x59,
0x4E, 0x7C, 0x12, 0x11, 0x12, 0x7C, 0x7F, 0x49, 0x49, 0x49, 0x36, 0x3E,
0x41, 0x41, 0x41, 0x22, 0x7F, 0x41, 0x41, 0x41, 0x3E, 0x7F, 0x49, 0x49,
0x49, 0x41, 0x7F, 0x09, 0x09, 0x09, 0x01, 0x3E, 0x41, 0x41, 0x51, 0x73,
0x7F, 0x08, 0x08, 0x08, 0x7F, 0x00, 0x41, 0x7F, 0x41, 0x00, 0x20, 0x40,
0x41, 0x3F, 0x01, 0x7F, 0x08, 0x14, 0x22, 0x41, 0x7F, 0x40, 0x40, 0x40,
0x40, 0x7F, 0x02, 0x1C, 0x02, 0x7F, 0x7F, 0x04, 0x08, 0x10, 0x7F, 0x3E,
0x41, 0x41, 0x41, 0x3E, 0x7F, 0x09, 0x09, 0x09, 0x06, 0x3E, 0x41, 0x51,
0x21, 0x5E, 0x7F, 0x09, 0x19, 0x29, 0x46, 0x26, 0x49, 0x49, 0x49, 0x32,
0x03, 0x01, 0x7F, 0x01, 0x03, 0x3F, 0x40, 0x40, 0x40, 0x3F, 0x1F, 0x20,
0x40, 0x20, 0x1F, 0x3F, 0x40, 0x38, 0x40, 0x3F, 0x63, 0x14, 0x08, 0x14,
0x63, 0x03, 0x04, 0x78, 0x04, 0x03, 0x61, 0x59, 0x49, 0x4D, 0x43, 0x00,
0x7F, 0x41, 0x41, 0x41, 0x02, 0x04, 0x08, 0x10, 0x20, 0x00, 0x41, 0x41,
0x41, 0x7F, 0x04, 0x02, 0x01, 0x02, 0x04, 0x40, 0x40, 0x40, 0x40, 0x40,
0x00, 0x03, 0x07, 0x08, 0x00, 0x20, 0x54, 0x54, 0x78, 0x40, 0x7F, 0x28,
0x44, 0x44, 0x38, 0x38, 0x44, 0x44, 0x44, 0x28, 0x38, 0x44, 0x44, 0x28,
0x7F, 0x38, 0x54, 0x54, 0x54, 0x18, 0x00, 0x08, 0x7E, 0x09, 0x02, 0x18,
0xA4, 0xA4, 0x9C, 0x78, 0x7F, 0x08, 0x04, 0x04, 0x78, 0x00, 0x44, 0x7D,
0x40, 0x00, 0x20, 0x40, 0x40, 0x3D, 0x00, 0x7F, 0x10, 0x28, 0x44, 0x00,
0x00, 0x41, 0x7F, 0x40, 0x00, 0x7C, 0x04, 0x78, 0x04, 0x78, 0x7C, 0x08,
0x04, 0x04, 0x78, 0x38, 0x44, 0x44, 0x44, 0x38, 0xFC, 0x18, 0x24, 0x24,
0x18, 0x18, 0x24, 0x24, 0x18, 0xFC, 0x7C, 0x08, 0x04, 0x04, 0x08, 0x48,
0x54, 0x54, 0x54, 0x24, 0x04, 0x04, 0x3F, 0x44, 0x24, 0x3C, 0x40, 0x40,
0x20, 0x7C, 0x1C, 0x20, 0x40, 0x20, 0x1C, 0x3C, 0x40, 0x30, 0x40, 0x3C,
0x44, 0x28, 0x10, 0x28, 0x44, 0x4C, 0x90, 0x90, 0x90, 0x7C, 0x44, 0x64,
0x54, 0x4C, 0x44, 0x00, 0x08, 0x36, 0x41, 0x00, 0x00, 0x00, 0x77, 0x00,
0x00, 0x00, 0x41, 0x36, 0x08, 0x00, 0x02, 0x01, 0x02, 0x04, 0x02, 0x3C,
0x26, 0x23, 0x26, 0x3C, 0x1E, 0xA1, 0xA1, 0x61, 0x12, 0x3A, 0x40, 0x40,
0x20, 0x7A, 0x38, 0x54, 0x54, 0x55, 0x59, 0x21, 0x55, 0x55, 0x79, 0x41,
0x22, 0x54, 0x54, 0x78, 0x42, // a-umlaut
0x21, 0x55, 0x54, 0x78, 0x40, 0x20, 0x54, 0x55, 0x79, 0x40, 0x0C, 0x1E,
0x52, 0x72, 0x12, 0x39, 0x55, 0x55, 0x55, 0x59, 0x39, 0x54, 0x54, 0x54,
0x59, 0x39, 0x55, 0x54, 0x54, 0x58, 0x00, 0x00, 0x45, 0x7C, 0x41, 0x00,
0x02, 0x45, 0x7D, 0x42, 0x00, 0x01, 0x45, 0x7C, 0x40, 0x7D, 0x12, 0x11,
0x12, 0x7D, // A-umlaut
0xF0, 0x28, 0x25, 0x28, 0xF0, 0x7C, 0x54, 0x55, 0x45, 0x00, 0x20, 0x54,
0x54, 0x7C, 0x54, 0x7C, 0x0A, 0x09, 0x7F, 0x49, 0x32, 0x49, 0x49, 0x49,
0x32, 0x3A, 0x44, 0x44, 0x44, 0x3A, // o-umlaut
0x32, 0x4A, 0x48, 0x48, 0x30, 0x3A, 0x41, 0x41, 0x21, 0x7A, 0x3A, 0x42,
0x40, 0x20, 0x78, 0x00, 0x9D, 0xA0, 0xA0, 0x7D, 0x3D, 0x42, 0x42, 0x42,
0x3D, // O-umlaut
0x3D, 0x40, 0x40, 0x40, 0x3D, 0x3C, 0x24, 0xFF, 0x24, 0x24, 0x48, 0x7E,
0x49, 0x43, 0x66, 0x2B, 0x2F, 0xFC, 0x2F, 0x2B, 0xFF, 0x09, 0x29, 0xF6,
0x20, 0xC0, 0x88, 0x7E, 0x09, 0x03, 0x20, 0x54, 0x54, 0x79, 0x41, 0x00,
0x00, 0x44, 0x7D, 0x41, 0x30, 0x48, 0x48, 0x4A, 0x32, 0x38, 0x40, 0x40,
0x22, 0x7A, 0x00, 0x7A, 0x0A, 0x0A, 0x72, 0x7D, 0x0D, 0x19, 0x31, 0x7D,
0x26, 0x29, 0x29, 0x2F, 0x28, 0x26, 0x29, 0x29, 0x29, 0x26, 0x30, 0x48,
0x4D, 0x40, 0x20, 0x38, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
0x38, 0x2F, 0x10, 0xC8, 0xAC, 0xBA, 0x2F, 0x10, 0x28, 0x34, 0xFA, 0x00,
0x00, 0x7B, 0x00, 0x00, 0x08, 0x14, 0x2A, 0x14, 0x22, 0x22, 0x14, 0x2A,
0x14, 0x08, 0x55, 0x00, 0x55, 0x00, 0x55, // #176 (25% block) missing in old
// code
0xAA, 0x55, 0xAA, 0x55, 0xAA, // 50% block
0xFF, 0x55, 0xFF, 0x55, 0xFF, // 75% block
0x00, 0x00, 0x00, 0xFF, 0x00, 0x10, 0x10, 0x10, 0xFF, 0x00, 0x14, 0x14,
0x14, 0xFF, 0x00, 0x10, 0x10, 0xFF, 0x00, 0xFF, 0x10, 0x10, 0xF0, 0x10,
0xF0, 0x14, 0x14, 0x14, 0xFC, 0x00, 0x14, 0x14, 0xF7, 0x00, 0xFF, 0x00,
0x00, 0xFF, 0x00, 0xFF, 0x14, 0x14, 0xF4, 0x04, 0xFC, 0x14, 0x14, 0x17,
0x10, 0x1F, 0x10, 0x10, 0x1F, 0x10, 0x1F, 0x14, 0x14, 0x14, 0x1F, 0x00,
0x10, 0x10, 0x10, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x1F, 0x10, 0x10, 0x10,
0x10, 0x1F, 0x10, 0x10, 0x10, 0x10, 0xF0, 0x10, 0x00, 0x00, 0x00, 0xFF,
0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0xFF, 0x10, 0x00,
0x00, 0x00, 0xFF, 0x14, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0x00, 0x1F,
0x10, 0x17, 0x00, 0x00, 0xFC, 0x04, 0xF4, 0x14, 0x14, 0x17, 0x10, 0x17,
0x14, 0x14, 0xF4, 0x04, 0xF4, 0x00, 0x00, 0xFF, 0x00, 0xF7, 0x14, 0x14,
0x14, 0x14, 0x14, 0x14, 0x14, 0xF7, 0x00, 0xF7, 0x14, 0x14, 0x14, 0x17,
0x14, 0x10, 0x10, 0x1F, 0x10, 0x1F, 0x14, 0x14, 0x14, 0xF4, 0x14, 0x10,
0x10, 0xF0, 0x10, 0xF0, 0x00, 0x00, 0x1F, 0x10, 0x1F, 0x00, 0x00, 0x00,
0x1F, 0x14, 0x00, 0x00, 0x00, 0xFC, 0x14, 0x00, 0x00, 0xF0, 0x10, 0xF0,
0x10, 0x10, 0xFF, 0x10, 0xFF, 0x14, 0x14, 0x14, 0xFF, 0x14, 0x10, 0x10,
0x10, 0x1F, 0x00, 0x00, 0x00, 0x00, 0xF0, 0x10, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00,
0x00, 0x00, 0xFF, 0xFF, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x38, 0x44, 0x44,
0x38, 0x44, 0xFC, 0x4A, 0x4A, 0x4A, 0x34, // sharp-s or beta
0x7E, 0x02, 0x02, 0x06, 0x06, 0x02, 0x7E, 0x02, 0x7E, 0x02, 0x63, 0x55,
0x49, 0x41, 0x63, 0x38, 0x44, 0x44, 0x3C, 0x04, 0x40, 0x7E, 0x20, 0x1E,
0x20, 0x06, 0x02, 0x7E, 0x02, 0x02, 0x99, 0xA5, 0xE7, 0xA5, 0x99, 0x1C,
0x2A, 0x49, 0x2A, 0x1C, 0x4C, 0x72, 0x01, 0x72, 0x4C, 0x30, 0x4A, 0x4D,
0x4D, 0x30, 0x30, 0x48, 0x78, 0x48, 0x30, 0xBC, 0x62, 0x5A, 0x46, 0x3D,
0x3E, 0x49, 0x49, 0x49, 0x00, 0x7E, 0x01, 0x01, 0x01, 0x7E, 0x2A, 0x2A,
0x2A, 0x2A, 0x2A, 0x44, 0x44, 0x5F, 0x44, 0x44, 0x40, 0x51, 0x4A, 0x44,
0x40, 0x40, 0x44, 0x4A, 0x51, 0x40, 0x00, 0x00, 0xFF, 0x01, 0x03, 0xE0,
0x80, 0xFF, 0x00, 0x00, 0x08, 0x08, 0x6B, 0x6B, 0x08, 0x36, 0x12, 0x36,
0x24, 0x36, 0x06, 0x0F, 0x09, 0x0F, 0x06, 0x00, 0x00, 0x18, 0x18, 0x00,
0x00, 0x00, 0x10, 0x10, 0x00, 0x30, 0x40, 0xFF, 0x01, 0x01, 0x00, 0x1F,
0x01, 0x01, 0x1E, 0x00, 0x19, 0x1D, 0x17, 0x12, 0x00, 0x3C, 0x3C, 0x3C,
0x3C, 0x00, 0x00, 0x00, 0x00, 0x00 // #255 NBSP
};
// allow clean compilation with [-Wunused-const-variable=] and [-Wall]
static inline void avoid_unused_const_variable_compiler_warning(void) {
(void)font;
}
#endif // FONT5X7_H
Demo Video
The original Arduino Example code converted for this demo, executed much faster, but there were delays added to the code to allow the viewer to see each step that was being executed. Otherwise this demo would have finished in less than 15 seconds.