My new Arduino Font Pal is now completely functional, released as alpha version 2.00. Feedback invited. I will try and answer all questions, and will continue to post updates here. --Alastair Roxburgh (kiwiengr)
A late change (compatible with ATmega 2560) moved the font bitmaps to flash memory. A random handful of demo examples enabled in the void loop() section shows a significant reduction in overall RAM use, from around 4400 bytes to 2100 bytes. This is more than a 50% reduction, so is well worth doing!
I have had to change some font flag characters (long story) which unfortunately seems less intuitive (e.g., b for bold caps, B for bold lowercase). To assist you with this, I have included a handy table of codes at the end of the ReadMe.ino
If you want to use this software on a non-ATmega Arduino (say Renesas RA4M1), here is what you need to change to move the font bitmaps back to RAM:
-
Remove #include <avr/pgmspace.h> at the start of the main program.
-
In the font declarations, revert
const byte bargraphfont[][8] PROGMEM = {
back to
byte bargraphfont[][8] { //This is the first of 26 instances to change
-
Also in the main program, revert the struct from:
typedef struct {
char fontFlag;
const byte (*fontBits)[8]; // Pointer to the CC bitmap arrays.
String fontChars;
} CustomFont;
back to:
typedef struct {
char fontFlag;
byte (*fontBits)[8]; // Pointer to the CC bitmap arrays.
String fontChars;
} CustomFont;
Note: If you don't revert to the struct with the const, Font Pal will still compile and run, but you'll get a bunch of compiler warnings (one for each font): E.g., invalid conversion from 'const byte ()[8] {aka const unsigned char ()[8]}' to 'byte ()[8] {aka unsigned char ()[8]}' [-fpermissive]
- Also in the main program, replace this:
uint8_t* getBitmap(char myfontChar, char myfontFlag) {
for (const CustomFont& font : fonts) {
if (font.fontFlag == myfontFlag) {
int charIndex = font.fontChars.indexOf(myfontChar);
if (charIndex != -1) {
static uint8_t bitmap[8];
for (int i = 0; i < 8; i++) {
bitmap[i] = pgm_read_byte_near(font.fontBits[charIndex] + I);**
}
return bitmap;
}
}
}
return nullptr; // Font or Character not found
}
with this:
uint8_t* getBitmap(char myfontChar, char myfontFlag) {
for (const CustomFont& font : fonts) {
if (font.fontFlag == myfontFlag) {
int charIndex = font.fontChars.indexOf(myfontChar);
if (charIndex != -1) return font.fontBits[charIndex];
}
}
return nullptr; // Font or Character not found
}
If you decide to comment-out some of the font data, make sure to also comment-out the relevant items in lines 600-638 below the font bitmaps.
Here is the full Font Pal alpha 2.00 code:
// #############################################################################
// ################## A r d u i n o - F o n t - P a l ##################
// ################## ( F o n t P a l o o z a ) ##################
// ################## F O R H I T A C H I H D 4 4 7 8 0 ##################
// ################## L C D C O N T R O L L E R S ##################
// #############################################################################
//
// Copyright (c) 2024 Alastair Roxburgh
// (Font Pal recreates the functionality of an original 68HC11 stack-frame
// assembly language program named SCREENS, devised in 1997 by Alastair
// Roxburgh & Sunil Rawal for the EOS series of TheaterMaster digital home theater
// audio processors. Written in Arduino C++, Font Pal makes heavy use of the String
// class and arrays of struct to create a custom font manager for 2x20 LCD displays
// based on the Hitachi HD44780 chip.)
//
// Note:
// While Font Pal, which was developed expressly for the Arduino Mega platform,
// reproduces most of the functionality of its 1977 68HC11 predecessor its
// internal algorithms are only remotely similar.
//
// Font Pal is free software: you can redistribute it and/or modify it
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation version 3 of the License.
//
// Font Pal is distributed in the hope that it will be useful, but WITHOUT
// WITHOUT ANY WARRANTY; without even the implied warranty of MECHANTABILITY
// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Public License for more
// details.
//
// You should have received a copy of the GNU General Public License along
// with Font Pal. If not, see <http://www.gnu.org/licenses/>.
//
// See the license.txt file for further licensing & copyright details.
//
// -----------------------------------------------------------------------
// History
//
// 2024.09.04 Alastair Roxburgh (kiwiengr) - initial creation
// 2024.09.13 Alastair Roxburgh (kiwiengr) - Updated Example 1: 'LCD scene'
// 2024.09.15 Alastair Roxburgh (kiwiengr) - Updated Documentation and ReadMe
// 2024.09.20 Alastair Roxburgh (kiwiengr) - Release of alpha version 2.00
//
//
//
// ---To Do---
// Add more error checking:
// If the lcdString is inconsistently formatted, display "*" flag on the LCD.
// Rules: One '|' => lcdString.length() - 2 * marker_idx = 1
// Two '|' => lcdString.length() - 4 * marker_idx = 2
// Add an LCD size option: 16x1, 20x1, 16x2, 20x2, 20x4.
// Add an interface option: 4-bit parallel, I2C.
// Study the feasibility of configuring Font Pal as an Arduino library.
//
// = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
#include <Arduino.h>
#include <LiquidCrystal.h>
#include <avr/pgmspace.h>
// Initialize the library with the numbers of the interface pins
LiquidCrystal lcd(7, 6, 8, 9, 10, 11, 12); // RS, RW, E, D0, D1, D2, D3. 4-bit transfer with R/W
// %%%%%%%%%%% GLOBALS:
String screenImage = ""; // Holds 40 fontChars (0-39) and 40 fontFlags in an interleaved format (80 characters numbered 0-79).
String lcdString = "";
int16_t startPos = 0; // Position in screenImage that will be overwritten with a new font-formatted String.
String CC_id_table[8]; // e.g., {"gL","dL","",...}
int8_t CGRAM_ptr; // Has values 0-7 to address the CC_id_table, but we add 8 (giving 8-f) to address CGRAM!
byte blankBitmap[8] = {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; // All pixels off.
bool in_CC_id_table = false;
/*----------------------------------------------*/
/* A Collection of Useful CC Strings */
/*----------------------------------------------*/
String daisy[] = { "242|CCC"
"1d1|CCC", // 3/2/0 daisy 0 (daisies 0-6 are without LFE channel)
"2c2|CCC"
"a5a|CCC", // 2/P/0 daisy 1
"2c2|CCC"
"ada|CCC", // 2/0/0 daisy 2
"242|CCC"
"a5a|CCC", // 3/1/0 daisy 3
"242|CCC"
"ada|CCC", // 3/0/0 daisy 4
"2c2|CCC"
"1d1|CCC", // 2/2/0 daisy 5
"b4b|CCC"
"ada|CCC", // 1/0/0 daisy 6
"bcb|CCC"
"ada|CCC", // 0/0/0 daisy 7 (no lock pattern)
"232|CCC"
"1e1|CCC", // 3/2/.1 daisy 8 (daisy 8-14 are with LFE channel)
"2f2|CCC"
"a6a|CCC", // 2/P/.1 daisy 9
"2f2|CCC"
"aea|CCC", // 2/0/.1 daisy 10
"232|CCC"
"a6a|CCC", // 3/1/.1 daisy 11
"232|CCC"
"a6a|CCC", // 3/0/.1 daisy 12
"2f2|CCC"
"1e1|CCC", // 2/2/.1 daisy 13
"b3b|CCC"
"aea|CCC", // 1/0/.1 daisy 14
"242|CCC" // 3/3/0 daisy 15
"151|CCC",
"232|CCC" // 3/3/.1 daisy 16
"161|CCC",
" | " // 0/0/0 daisy xx (for no lock mute)
" | "
};
String normalSpeakers[] = {
"LF|cc", // 0. E.g., Display a selectable menu of speakers (all are in small caps): LF CR RF
"RF|cc", // 1. LS SB RS
"Ls|c ", // 2.
"Rs|c ", // 3.
"cR| c", // 4.
"sB| c" // 5.
};
String revSpeakers[ ] = {
"LF|VV", // 0. Example of use: indicate a speaker selection for adjustment (all are in reverse small caps).
"RF|VV", // 1.
"Ls|VV", // 2.
"Rs|VV", // 3.
"cR|VV", // 4.
"sB|VV" // 5.
};
String HorizontalSPLmeter = {"SPL: Ref! . dB|" // Boiler plate for horizontal SPL meter. Ref "!|G" (down arrow) marks a reference level.
" G "}; // However, the actual bar graph is constructed on the bottom LCD row (not shown)
String invArray[][2] = {
{ "a a a a a|I I I I I", "a a a a a|O O O O O" }, // 0
{ "b b b b b|I I I I I", "b b b b b|O O O O O" }, // 1
{ "c c c c c|I I I I I", "c c c c c|O O O O O" }, // 2
{ "d d d d d|I I I I I", "d d d d d|O O O O O" } // 3
};
/*----------------------------------------------*/
/* Custom Character Fonts */
/*----------------------------------------------*/
// @@@@@@@@@@ G - BARGRAPH FONT @@@@@@@@@@
const byte bargraphfont[][8] PROGMEM = { // "0abcLR!87654321"
// Horizontal Bar Graph:
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // '0' no bars
{ 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10 }, // 'a' one bar
{ 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14 }, // 'b' two bars
{ 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15 }, // 'c' three bars
{ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18 }, // 'L' L-bar
{ 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03 }, // 'R' R-bar
{ 0x00, 0x1c, 0x04, 0x04, 0x04, 0x15, 0x0e, 0x04 }, // '!' bent down-arrow
// Multi-Channel VU Meter Bar Graph (6 dB per step):
{ 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b }, // '8'
{ 0x00, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b }, // '7'
{ 0x00, 0x00, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b }, // '6'
{ 0x00, 0x00, 0x00, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b }, // '5'
{ 0x00, 0x00, 0x00, 0x00, 0x1b, 0x1b, 0x1b, 0x1b }, // '4'
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x1b, 0x1b, 0x1b }, // '3'
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1b, 0x1b }, // '2'
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1b } // '1'
};
// @@@@@@@@@@ 0 to 9 - BIGNUMS FONTS @@@@@@@@@@
const byte big0font[][8] PROGMEM = { // "ABCD"
// '0' = 4*0 + 0,1,2,3
{ 0x03, 0x07, 0x0f, 0x0e, 0x1c, 0x1c, 0x18, 0x18 }, // 'A'
{ 0x18, 0x1c, 0x1e, 0x0e, 0x07, 0x07, 0x03, 0x03 }, // 'B'
{ 0x18, 0x18, 0x1c, 0x1c, 0x0e, 0x0f, 0x07, 0x03 }, // 'C'
{ 0x03, 0x03, 0x07, 0x07, 0x0e, 0x1e, 0x1c, 0x18 } // 'D'
};
const byte big1font[][8] PROGMEM = { // "ABCD"
// '1' = 4*1 + 0,1,2,3
{ 0x01, 0x07, 0x0f, 0x0f, 0x01, 0x01, 0x01, 0x01 }, // 'A'
{ 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10 }, // 'B'
{ 0x01, 0x01, 0x01, 0x01, 0x01, 0x0f, 0x0f, 0x0f }, // 'C'
{ 0x10, 0x10, 0x10, 0x10, 0x10, 0x1e, 0x1e, 0x1e } // 'D'
};
const byte big2font[][8] PROGMEM = { // "ABCD"
// '2' = 4*2 + 1
{ 0x01, 0x07, 0x0f, 0x1e, 0x18, 0x00, 0x00, 0x00 }, // 'A'
{ 0x18, 0x1c, 0x1e, 0x07, 0x03, 0x03, 0x07, 0x0e }, // 'B'
{ 0x00, 0x01, 0x03, 0x07, 0x0e, 0x1f, 0x1f, 0x1f }, // 'C'
{ 0x1c, 0x18, 0x10, 0x00, 0x03, 0x1f, 0x1f, 0x1f } // 'D'
};
const byte big3font[][8] PROGMEM = { // "ABCD"
// '3'
{ 0x03, 0x0f, 0x1f, 0x18, 0x00, 0x00, 0x00, 0x03 }, // 'A'
{ 0x18, 0x1c, 0x1e, 0x06, 0x06, 0x06, 0x0e, 0x1c }, // 'B'
{ 0x03, 0x00, 0x00, 0x10, 0x18, 0x1f, 0x0f, 0x07 }, // 'C'
{ 0x1e, 0x0f, 0x07, 0x07, 0x07, 0x1e, 0x1c, 0x18 } // 'D'
};
const byte big4font[][8] PROGMEM = { // "ABCD"
// '4'
{ 0x00, 0x00, 0x01, 0x01, 0x03, 0x03, 0x07, 0x0e }, // 'A'
{ 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x0c, 0x0c, 0x0c }, // 'B'
{ 0x1f, 0x1f, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00 }, // 'C'
{ 0x1f, 0x1f, 0x1f, 0x0c, 0x0c, 0x1e, 0x1e, 0x1e } // 'D'
};
const byte big5font[][8] PROGMEM = { // "ABCD"
// '5'
{ 0x0f, 0x0f, 0x0f, 0x0c, 0x0c, 0x0c, 0x0f, 0x0f }, // 'A'
{ 0x1f, 0x1f, 0x1f, 0x00, 0x00, 0x00, 0x1c, 0x1e }, // 'B'
{ 0x00, 0x00, 0x00, 0x08, 0x0c, 0x0f, 0x07, 0x03 }, // 'C'
{ 0x07, 0x03, 0x03, 0x03, 0x07, 0x1e, 0x1c, 0x18 } // 'D'
};
const byte big6font[][8] PROGMEM = { // "ABCD"
// '6'
{ 0x03, 0x07, 0x0e, 0x1c, 0x18, 0x18, 0x1b, 0x1f }, // 'A'
{ 0x1c, 0x1e, 0x07, 0x03, 0x01, 0x00, 0x1c, 0x1e }, // 'B'
{ 0x1c, 0x18, 0x18, 0x1c, 0x0e, 0x0f, 0x07, 0x03 }, // 'C'
{ 0x07, 0x03, 0x03, 0x03, 0x07, 0x1e, 0x1c, 0x18 } // 'D'
};
const byte big7font[][8] PROGMEM = { // "ABCD"
// '7'
{ 0x0f, 0x0f, 0x0f, 0x0c, 0x00, 0x00, 0x00, 0x00 }, // 'A'
{ 0x1f, 0x1f, 0x1f, 0x07, 0x07, 0x0e, 0x0e, 0x0e }, // 'B'
{ 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01 }, // 'C'
{ 0x1c, 0x18, 0x18, 0x18, 0x18, 0x10, 0x10, 0x10 } // 'D'
};
const byte big8font[][8] PROGMEM = { // "ABCD"
// '8'
{ 0x03, 0x07, 0x0f, 0x1c, 0x1c, 0x1c, 0x0e, 0x07 }, // 'A'
{ 0x18, 0x1c, 0x1e, 0x07, 0x07, 0x07, 0x0e, 0x1c }, // 'B'
{ 0x0f, 0x1c, 0x1c, 0x1c, 0x1c, 0x0f, 0x07, 0x03 }, // 'C'
{ 0x1e, 0x07, 0x07, 0x07, 0x07, 0x1e, 0x1c, 0x18 } // 'D'
};
const byte big9font[][8] PROGMEM = { // "ABCD"
// '9'
{ 0x03, 0x07, 0x0f, 0x1c, 0x18, 0x18, 0x18, 0x1c }, // 'A'
{ 0x18, 0x1c, 0x1e, 0x0e, 0x07, 0x03, 0x03, 0x07 }, // 'B'
{ 0x0f, 0x07, 0x00, 0x10, 0x18, 0x1c, 0x0f, 0x07 }, // 'C'
{ 0x1f, 0x1b, 0x03, 0x03, 0x07, 0x0e, 0x1c, 0x18 } // 'D'
};
// @@@@@@@@@@ X - SYMBOL FONT @@@@@@@@@@
const byte symbolfont[][8] PROGMEM = { // "DSTMH><^vuds_1234KkLQnNbCF"
{ 0x04, 0x04, 0x0a, 0x0a, 0x11, 0x11, 0x1f, 0x00 }, // 'D' Greek Delta
{ 0x1f, 0x10, 0x08, 0x04, 0x08, 0x10, 0x1f, 0x00 }, // 'S' Greek Sigma
{ 0x1f, 0x04, 0x04, 0x04, 0x00, 0x00, 0x00, 0x00 }, // 'T' Trademark symbol, use "TM|SS"
{ 0x11, 0x1b, 0x15, 0x11, 0x00, 0x00, 0x00, 0x00 }, // 'M'
{ 0x1b, 0x1f, 0x1b, 0x00, 0x1f, 0x18, 0x1e, 0x18 }, // 'H' single HF character symbol, use "H|S"
{ 0x10, 0x18, 0x1c, 0x1e, 0x1c, 0x18, 0x10, 0x00 }, // '>' Right Arrow symbol
{ 0x01, 0x03, 0x07, 0x0f, 0x07, 0x03, 0x01, 0x00 }, // '<' Left Arrow symbol
{ 0x04, 0x04, 0x0e, 0x0e, 0x1f, 0x1f, 0x00, 0x00 }, // '^' Up Arrow symbol
{ 0x00, 0x00, 0x1f, 0x1f, 0x0e, 0x0e, 0x04, 0x04 }, // 'v' Down Arrow symbol
{ 0x04, 0x0e, 0x1f, 0x00, 0x04, 0x0e, 0x1f, 0x00 }, // 'u' Up double arrow
{ 0x1f, 0x0e, 0x04, 0x00, 0x1f, 0x0e, 0x04, 0x00 }, // 'd' Down double arrow
{ 0x00, 0x00, 0x00, 0x0e, 0x10, 0x0c, 0x02, 0x1c }, // 's' Subscript 's'
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f }, // '_' underline char (in the cursor row)
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00 }, // '1' Centered 4-dot screen-saver pattern:
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00 }, // '2'
{ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // '3'
{ 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // '4'
{ 0x00, 0x01, 0x03, 0x1f, 0x1f, 0x1f, 0x03, 0x01 }, // 'K' Big Speaker (Kicker)
{ 0x00, 0x00, 0x01, 0x03, 0x0f, 0x03, 0x01, 0x00 }, // 'k' small speaker (small-k kicker)
{ 0x10, 0x18, 0x1f, 0x1f, 0x1f, 0x18, 0x10, 0x00 }, // 'L' Big Speaker reversed
{ 0x06, 0x08, 0x08, 0x1c, 0x08, 0x09, 0x16, 0x00 }, // 'Q' Pound Sterling £ "Quid" (ALT-163)
{ 0x04, 0x06, 0x05, 0x05, 0x0c, 0x1c, 0x18, 0x00 }, // 'n' Musical note
{ 0x06, 0x05, 0x07, 0x05, 0x05, 0x1d, 0x1b, 0x03 }, // 'N' Two musical notes
{ 0x04, 0x0e, 0x0e, 0x0e, 0x1f, 0x04, 0x00, 0x00 }, // 'b' Bell
{ 0x18, 0x18, 0x00, 0x07, 0x08, 0x08, 0x08, 0x07 }, // 'C' Celsius degrees
{ 0x18, 0x18, 0x00, 0x0f, 0x08, 0x0e, 0x08, 0x08 } // 'F' Fahrenheit degrees
};
// @@@@@@@@@@ b - BOLD Uppercase FONT @@@@@@@@@@
const byte Boldfont[][8] PROGMEM = { // "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
{ 0x0e, 0x19, 0x19, 0x19, 0x1f, 0x19, 0x19, 0x00 }, // 'A'
{ 0x1e, 0x19, 0x19, 0x1e, 0x19, 0x19, 0x1e, 0x00 }, // 'B'
{ 0x0e, 0x19, 0x18, 0x18, 0x18, 0x19, 0x0e, 0x00 }, // 'C'
{ 0x1c, 0x1a, 0x19, 0x19, 0x19, 0x1a, 0x1c, 0x00 }, // 'D'
{ 0x1f, 0x18, 0x18, 0x1e, 0x18, 0x18, 0x1f, 0x00 }, // 'E'
{ 0x1f, 0x18, 0x18, 0x1e, 0x18, 0x18, 0x18, 0x00 }, // 'F'
{ 0x0e, 0x19, 0x18, 0x1b, 0x19, 0x19, 0x0f, 0x00 }, // 'G'
{ 0x19, 0x19, 0x19, 0x1f, 0x19, 0x19, 0x19, 0x00 }, // 'H'
{ 0x0f, 0x06, 0x06, 0x06, 0x06, 0x06, 0x0f, 0x00 }, // 'I'
{ 0x0f, 0x06, 0x06, 0x06, 0x06, 0x16, 0x0c, 0x00 }, // 'J'
{ 0x1b, 0x1a, 0x1c, 0x18, 0x1c, 0x1a, 0x1b, 0x00 }, // 'K'
{ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x1f, 0x00 }, // 'L'
{ 0x11, 0x1b, 0x1f, 0x1f, 0x1b, 0x1b, 0x1b, 0x00 }, // 'M'
{ 0x19, 0x19, 0x1d, 0x1f, 0x1b, 0x19, 0x19, 0x00 }, // 'N'
{ 0x0e, 0x19, 0x19, 0x19, 0x19, 0x19, 0x0e, 0x00 }, // 'O'
{ 0x1e, 0x19, 0x19, 0x19, 0x1e, 0x18, 0x18, 0x00 }, // 'P'
{ 0x0e, 0x19, 0x19, 0x19, 0x19, 0x1a, 0x0d, 0x00 }, // 'Q'
{ 0x1e, 0x13, 0x13, 0x1e, 0x1c, 0x16, 0x13, 0x00 }, // 'R' rhs bold
{ 0x0e, 0x19, 0x1c, 0x0e, 0x07, 0x13, 0x0e, 0x00 }, // 'S'
{ 0x0f, 0x0f, 0x06, 0x06, 0x06, 0x06, 0x06, 0x00 }, // 'T'
{ 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x0e, 0x00 }, // 'U'
{ 0x19, 0x19, 0x19, 0x19, 0x19, 0x0a, 0x04, 0x00 }, // 'V'
{ 0x11, 0x11, 0x11, 0x15, 0x1f, 0x1f, 0x0a, 0x00 }, // 'W'
{ 0x19, 0x19, 0x0e, 0x04, 0x0e, 0x13, 0x13, 0x00 }, // 'X'
{ 0x19, 0x19, 0x19, 0x0e, 0x04, 0x04, 0x0e, 0x00 }, // 'Y'
{ 0x1f, 0x03, 0x06, 0x0c, 0x18, 0x10, 0x1f, 0x00 }, // 'Z'
{ 0x0e, 0x19, 0x19, 0x19, 0x19, 0x19, 0x0e, 0x00 }, // '0'
{ 0x06, 0x0e, 0x16, 0x06, 0x06, 0x06, 0x1f, 0x00 }, // '1'
{ 0x0e, 0x13, 0x03, 0x06, 0x0c, 0x18, 0x1f, 0x00 }, // '2'
{ 0x1f, 0x03, 0x06, 0x0e, 0x03, 0x13, 0x0e, 0x00 }, // '3'
{ 0x06, 0x0e, 0x16, 0x16, 0x1f, 0x06, 0x06, 0x00 }, // '4'
{ 0x1f, 0x18, 0x18, 0x1e, 0x01, 0x11, 0x1e, 0x00 }, // '5'
{ 0x07, 0x0c, 0x18, 0x1e, 0x19, 0x19, 0x0e, 0x00 }, // '6'
{ 0x1f, 0x13, 0x03, 0x06, 0x0c, 0x0c, 0x0c, 0x00 }, // '7'
{ 0x0e, 0x19, 0x19, 0x0e, 0x19, 0x19, 0x0e, 0x00 }, // '8'
{ 0x0e, 0x19, 0x19, 0x0f, 0x01, 0x02, 0x0c, 0x00 } // '9'
};
// @@@@@@@@@@ B - bold Lowercase Font @@@@@@@@@@
const byte boldlcfont[][8] PROGMEM = { // "abcdefghijklmnopqrstuvwxyz"
{ 0x00, 0x00, 0x0e, 0x03, 0x0f, 0x1b, 0x0f, 0x00 }, // 'a'
{ 0x18, 0x18, 0x1e, 0x1b, 0x1b, 0x1b, 0x1e, 0x00 }, // 'b'
{ 0x00, 0x00, 0x0e, 0x1b, 0x18, 0x1b, 0x0e, 0x00 }, // 'c'
{ 0x03, 0x03, 0x0f, 0x1b, 0x1b, 0x1b, 0x0f, 0x00 }, // 'd'
{ 0x00, 0x00, 0x0e, 0x1b, 0x1f, 0x18, 0x0e, 0x00 }, // 'e'
{ 0x06, 0x0d, 0x0c, 0x1e, 0x0c, 0x0c, 0x0c, 0x00 }, // 'f'
{ 0x00, 0x00, 0x0f, 0x1b, 0x1b, 0x0f, 0x03, 0x0e }, // 'g'
{ 0x18, 0x18, 0x1e, 0x1b, 0x1b, 0x1b, 0x1b, 0x00 }, // 'h'
{ 0x06, 0x00, 0x0e, 0x06, 0x06, 0x06, 0x0f, 0x00 }, // 'i'
{ 0x03, 0x00, 0x07, 0x03, 0x03, 0x03, 0x13, 0x0e }, // 'j'
{ 0x18, 0x18, 0x1b, 0x1e, 0x1c, 0x1a, 0x1b, 0x00 }, // 'k'
{ 0x0e, 0x06, 0x06, 0x06, 0x06, 0x06, 0x0f, 0x00 }, // 'l'
{ 0x00, 0x00, 0x1a, 0x1f, 0x15, 0x15, 0x15, 0x00 }, // 'm'
{ 0x00, 0x00, 0x16, 0x1b, 0x1b, 0x1b, 0x1b, 0x00 }, // 'n'
{ 0x00, 0x00, 0x0e, 0x1b, 0x1b, 0x1b, 0x0e, 0x00 }, // 'o'
{ 0x00, 0x00, 0x1e, 0x1b, 0x1b, 0x1e, 0x18, 0x18 }, // 'p'
{ 0x00, 0x00, 0x0d, 0x13, 0x13, 0x0f, 0x03, 0x03 }, // 'q'
{ 0x00, 0x00, 0x16, 0x19, 0x18, 0x18, 0x18, 0x00 }, // 'r'
{ 0x00, 0x00, 0x0f, 0x1c, 0x0e, 0x07, 0x1e, 0x00 }, // 's'
{ 0x0c, 0x0c, 0x1e, 0x0c, 0x0c, 0x0d, 0x06, 0x00 }, // 't'
{ 0x00, 0x00, 0x1b, 0x1b, 0x1b, 0x1b, 0x0d, 0x00 }, // 'u'
{ 0x00, 0x00, 0x1b, 0x1b, 0x1b, 0x0a, 0x04, 0x00 }, // 'v'
{ 0x00, 0x00, 0x11, 0x15, 0x1f, 0x1b, 0x11, 0x00 }, // 'w'
{ 0x00, 0x00, 0x11, 0x1b, 0x0e, 0x1b, 0x11, 0x00 }, // 'x'
{ 0x00, 0x00, 0x1b, 0x1b, 0x1b, 0x0f, 0x03, 0x0e }, // 'y'
{ 0x00, 0x00, 0x1f, 0x03, 0x06, 0x0c, 0x1f, 0x00 } // 'z'
};
// %%%%%%%%%% L - LOWERCASE FONT %%%%%%%%%%
const byte lowercasefont[][8] PROGMEM = { // "gjpqym"
{ 0x00, 0x00, 0x0f, 0x11, 0x11, 0x0f, 0x01, 0x0e }, // 'g'
{ 0x02, 0x00, 0x06, 0x02, 0x02, 0x02, 0x12, 0x0c }, // 'j'
{ 0x00, 0x00, 0x1e, 0x11, 0x11, 0x1e, 0x10, 0x10 }, // 'p'
{ 0x00, 0x00, 0x0e, 0x12, 0x12, 0x0e, 0x02, 0x03 }, // 'q'
{ 0x00, 0x00, 0x11, 0x11, 0x11, 0x0f, 0x01, 0x0e }, // 'y'
{ 0x00, 0x00, 0x1a, 0x15, 0x15, 0x15, 0x15, 0x00 } // 'm' An alternate to the ROM 'm'
};
// @@@@@@@@@@ c - SMALL CAPS FONT @@@@@@@@@@
const byte smallcapsfont[][8] PROGMEM = { // "ABDEFGHIJKLMNPQRTUY"
{ 0x00, 0x00, 0x0e, 0x11, 0x1f, 0x11, 0x11, 0x00 }, // 'A'
{ 0x00, 0x00, 0x1e, 0x11, 0x1e, 0x11, 0x1e, 0x00 }, // 'B'
// Use c
{ 0x00, 0x00, 0x1e, 0x11, 0x11, 0x11, 0x1e, 0x00 }, // 'D'
{ 0x00, 0x00, 0x1f, 0x10, 0x1c, 0x10, 0x1f, 0x00 }, // 'E'
{ 0x00, 0x00, 0x1f, 0x10, 0x1c, 0x10, 0x10, 0x00 }, // 'F'
{ 0x00, 0x00, 0x0f, 0x10, 0x17, 0x11, 0x0f, 0x00 }, // 'G'
{ 0x00, 0x00, 0x11, 0x11, 0x1f, 0x11, 0x11, 0x00 }, // 'H'
{ 0x00, 0x00, 0x0e, 0x04, 0x04, 0x04, 0x0e, 0x00 }, // 'I'
{ 0x00, 0x00, 0x01, 0x01, 0x01, 0x11, 0x0e, 0x00 }, // 'J'
{ 0x00, 0x00, 0x12, 0x14, 0x18, 0x14, 0x12, 0x00 }, // 'K'
{ 0x00, 0x00, 0x10, 0x10, 0x10, 0x10, 0x1f, 0x00 }, // 'L'
{ 0x00, 0x00, 0x11, 0x1b, 0x15, 0x11, 0x11, 0x00 }, // 'M'
{ 0x00, 0x00, 0x11, 0x19, 0x15, 0x13, 0x11, 0x00 }, // 'N'
// Use o
{ 0x00, 0x00, 0x1e, 0x11, 0x1e, 0x10, 0x10, 0x00 }, // 'P'
{ 0x00, 0x00, 0x0e, 0x11, 0x11, 0x15, 0x0e, 0x01 }, // 'Q'
{ 0x00, 0x00, 0x1e, 0x11, 0x1e, 0x14, 0x12, 0x00 }, // 'R'
// Use s
{ 0x00, 0x00, 0x1f, 0x04, 0x04, 0x04, 0x04, 0x00 }, // 'T'
{ 0x00, 0x00, 0x11, 0x11, 0x11, 0x11, 0x0e, 0x00 }, // 'U'
// Use v
// Use w
// Use x
{ 0x00, 0x00, 0x11, 0x11, 0x0a, 0x04, 0x04, 0x00 } // 'Y'
// Use z
};
// @@@@@@@@@@ F - SMALL FREQUENCY FONT @@@@@@@@@@
const byte smlfrequfont[][8] PROGMEM = { // "kHz"
{ 0x00, 0x00, 0x08, 0x0a, 0x0c, 0x0a, 0x09, 0x00 }, // 'k' as in kHz
{ 0x00, 0x00, 0x09, 0x09, 0x0f, 0x09, 0x09, 0x00 }, // 'H' as in Hz
{ 0x00, 0x00, 0x00, 0x1e, 0x04, 0x08, 0x1e, 0x00 } // 'z' as in Hz
};
// @@@@@@@@@@ V - SMALL REVERSE CAPS FONT @@@@@@@@@@
const byte smlrevcapsfont[][8] PROGMEM = { // "BCcFLSsTR"
{ 0x1f, 0x1f, 0x13, 0x15, 0x13, 0x15, 0x13, 0x1f }, // 'B'
{ 0x1f, 0x1f, 0x19, 0x17, 0x17, 0x17, 0x19, 0x1f }, // 'C'
{ 0x1f, 0x1f, 0x19, 0x17, 0x17, 0x17, 0x19, 0x1f }, // 'c'
{ 0x1f, 0x1f, 0x11, 0x17, 0x11, 0x17, 0x17, 0x1f }, // 'F'
{ 0x1f, 0x1f, 0x17, 0x17, 0x17, 0x17, 0x11, 0x1f }, // 'L'
{ 0x1f, 0x1f, 0x19, 0x17, 0x1b, 0x1d, 0x13, 0x1f }, // 'S'
{ 0x1f, 0x1f, 0x19, 0x17, 0x1b, 0x1d, 0x13, 0x1f }, // 's'
{ 0x1f, 0x1f, 0x11, 0x1b, 0x1b, 0x1b, 0x1b, 0x1f }, // 'T'
{ 0x1f, 0x1f, 0x13, 0x15, 0x13, 0x15, 0x15, 0x1f } // 'R'
};
// @@@@@@@@@@ v - REVERSE CAPS FONT @@@@@@@@@@
const byte revcapsfont[][8] PROGMEM = { // "ABCDEFGHIKLOPRSTUVXYZ0123456789=:"
{ 0x1f, 0x1b, 0x15, 0x15, 0x11, 0x15, 0x15, 0x1f }, // 'A'
{ 0x1f, 0x13, 0x15, 0x13, 0x15, 0x15, 0x13, 0x1f }, // 'B'
{ 0x1f, 0x19, 0x17, 0x17, 0x17, 0x17, 0x19, 0x1f }, // 'C'
{ 0x1f, 0x13, 0x15, 0x15, 0x15, 0x15, 0x13, 0x1f }, // 'D'
{ 0x1f, 0x11, 0x17, 0x11, 0x17, 0x17, 0x11, 0x1f }, // 'E'
{ 0x1f, 0x11, 0x17, 0x13, 0x17, 0x17, 0x17, 0x1f }, // 'F'
{ 0x1f, 0x11, 0x17, 0x17, 0x15, 0x15, 0x11, 0x1f }, // 'G'
{ 0x1f, 0x15, 0x15, 0x11, 0x15, 0x15, 0x15, 0x1f }, // 'H'
{ 0x1f, 0x11, 0x1b, 0x1b, 0x1b, 0x1b, 0x11, 0x1f }, // 'I'
// No J
{ 0x1f, 0x15, 0x13, 0x17, 0x13, 0x15, 0x15, 0x1f }, // 'K'
{ 0x1f, 0x17, 0x17, 0x17, 0x17, 0x17, 0x11, 0x1f }, // 'L'
// No M,N
{ 0x1f, 0x11, 0x15, 0x15, 0x15, 0x15, 0x11, 0x1f }, // 'O'
{ 0x1f, 0x13, 0x15, 0x15, 0x13, 0x17, 0x17, 0x1f }, // 'P'
// No Q
{ 0x1f, 0x11, 0x15, 0x11, 0x13, 0x15, 0x15, 0x1f }, // 'R'
{ 0x1f, 0x19, 0x17, 0x1b, 0x1d, 0x1d, 0x13, 0x1f }, // 'S'
{ 0x1f, 0x11, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1f }, // 'T'
{ 0x1f, 0x15, 0x15, 0x15, 0x15, 0x15, 0x11, 0x1f }, // 'U'
{ 0x1f, 0x15, 0x15, 0x15, 0x15, 0x15, 0x1b, 0x1f }, // 'V'
// No W
{ 0x1f, 0x15, 0x15, 0x1b, 0x1b, 0x15, 0x15, 0x1f }, // 'X'
{ 0x1f, 0x15, 0x15, 0x1b, 0x1b, 0x1b, 0x1b, 0x1f }, // 'Y'
{ 0x1f, 0x11, 0x1d, 0x1b, 0x1b, 0x17, 0x11, 0x1f }, // 'Z'
{ 0x1f, 0x1b, 0x15, 0x15, 0x15, 0x15, 0x1b, 0x1f }, // '0'
{ 0x1f, 0x1b, 0x13, 0x1b, 0x1b, 0x1b, 0x11, 0x1f }, // '1'
{ 0x1f, 0x1b, 0x15, 0x1d, 0x1b, 0x17, 0x11, 0x1f }, // '2'
{ 0x1f, 0x11, 0x1d, 0x1b, 0x1d, 0x15, 0x1b, 0x1f }, // '3'
{ 0x1f, 0x17, 0x15, 0x15, 0x11, 0x1d, 0x1d, 0x1f }, // '4'
{ 0x1f, 0x11, 0x17, 0x1b, 0x1d, 0x15, 0x1b, 0x1f }, // '5'
{ 0x1f, 0x1b, 0x15, 0x17, 0x13, 0x15, 0x1b, 0x1f }, // '6'
{ 0x1f, 0x11, 0x15, 0x1d, 0x1b, 0x17, 0x17, 0x1f }, // '7'
{ 0x1f, 0x11, 0x15, 0x1b, 0x15, 0x15, 0x11, 0x1f }, // '8' thin waist, 1b is rounded top and bottom
{ 0x1f, 0x1b, 0x15, 0x19, 0x1d, 0x15, 0x1b, 0x1f }, // '9'
{ 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f }, // '=' space (5x8 black pixels)
{ 0x1f, 0x1f, 0x1f, 0x1b, 0x1f, 0x1b, 0x1f, 0x1f } // ':' colon
};
// @@@@@@@@@@ C - CHANNEL-DAISY FONT @@@@@@@@@@
const byte channelfont[][8] PROGMEM = { // "241dca5b3ef6xyzuvwjkl"
{ 0x00, 0x00, 0x00, 0x0e, 0x1f, 0x1f, 0x1f, 0x0e }, // '2'
{ 0x0e, 0x1f, 0x1f, 0x1f, 0x0e, 0x00, 0x0e, 0x11 }, // '4'
{ 0x0e, 0x1f, 0x1f, 0x1f, 0x0e, 0x00, 0x00, 0x00 }, // '1'
{ 0x11, 0x0e, 0x00, 0x0e, 0x11, 0x11, 0x11, 0x0e }, // 'd'
//=================================================
{ 0x0e, 0x11, 0x11, 0x11, 0x0e, 0x00, 0x0e, 0x11 }, // 'c'
{ 0x0e, 0x11, 0x11, 0x11, 0x0e, 0x00, 0x00, 0x00 }, // 'a'
{ 0x11, 0x0e, 0x00, 0x0e, 0x1f, 0x1f, 0x1f, 0x0e }, // '5'
//=================================================
{ 0x00, 0x00, 0x00, 0x0e, 0x11, 0x11, 0x11, 0x0e }, // 'b'
//=================================================
{ 0x0e, 0x1f, 0x1f, 0x1f, 0x0e, 0x00, 0x0e, 0x1f }, // '3'
{ 0x1f, 0x0e, 0x00, 0x0e, 0x11, 0x11, 0x11, 0x0e }, // 'e'
//=================================================
{ 0x0e, 0x11, 0x11, 0x11, 0x0e, 0x00, 0x0e, 0x1f }, // 'f'
{ 0x1f, 0x0e, 0x00, 0x0e, 0x1f, 0x1f, 0x1f, 0x0e }, // '6'
//=================================================
{ 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x05, 0x00 }, // 'x' Analog pass-thru lower-half of daisy (vertical triangle) "xyz|CCC"
{ 0x04, 0x04, 0x15, 0x15, 0x15, 0x15, 0x15, 0x00 }, // 'y'
{ 0x00, 0x00, 0x00, 0x00, 0x10, 0x10, 0x14, 0x00 }, // 'z'
//=================================================
{ 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x07, 0x00 }, // 'u' Analog pass-thru 'Mute' daisy (hor. triangle) "uvw|CCC" (alternate with xyz|CCC)
{ 0x04, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00 }, // 'v'
{ 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x1c, 0x00 }, // 'w'
//=================================================
{ 0x00, 0x00, 0x00, 0x00, 0x01, 0x03, 0x07, 0x00 }, // 'j' Solid triangle symbol (same shape as the vert,. and hor. triangles) "jkl|CCC"
{ 0x04, 0x0e, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x00 }, // 'k'
{ 0x00, 0x00, 0x00, 0x00, 0x10, 0x18, 0x1c, 0x00 } // 'l'
};
// @@@@@@@@@@ l - LOGOS FONT @@@@@@@@@@
const byte logofont[][8] PROGMEM = { // "LRdtsHDC0123456 789EQ"
{ 0x1f, 0x13, 0x11, 0x11, 0x11, 0x13, 0x1f, 0x00 }, // 'L' (Dolby Labs logo, use "LR|LL")
{ 0x1f, 0x19, 0x11, 0x11, 0x11, 0x19, 0x1f, 0x00 }, // 'R'
//
{ 0x03, 0x03, 0x03, 0x0f, 0x1b, 0x1b, 0x0f, 0x00 }, // 'd' (Digital Theater Systems logo. use "dts|LLL")
{ 0x04, 0x0c, 0x1f, 0x0c, 0x0c, 0x0c, 0x07, 0x00 }, // 't'
{ 0x00, 0x00, 0x0f, 0x1c, 0x0e, 0x07, 0x1c, 0x00 }, // 's'
//
{ 0x00, 0x00, 0x11, 0x11, 0x1f, 0x11, 0x11, 0x00 }, // 'H' ('HDCD' small caps logo, use "HDCD|LLLL")
{ 0x00, 0x00, 0x1e, 0x11, 0x11, 0x11, 0x1e, 0x00 }, // 'D'
{ 0x00, 0x00, 0x0f, 0x10, 0x10, 0x10, 0x0f, 0x00 }, // 'C'
//
// Standard version of CinEQ: Use "0123456"
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00 }, // '0' _ ('-cinEQ--' EAD CinEQ logo, use "0123456|LLLLLLL")
{ 0x03, 0x04, 0x04, 0x03, 0x00, 0x1f, 0x00, 0x00 }, // '1' C
{ 0x0e, 0x04, 0x04, 0x0e, 0x00, 0x1f, 0x00, 0x00 }, // '2' I
{ 0x0c, 0x12, 0x12, 0x12, 0x02, 0x12, 0x08, 0x07 }, // '3' N
{ 0x1e, 0x10, 0x10, 0x1e, 0x10, 0x1e, 0x00, 0x1f }, // '4' E
{ 0x0e, 0x11, 0x11, 0x11, 0x15, 0x0e, 0x04, 0x1f }, // '5' Q
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e } // '6' + (underbar)
};
// @@@@@@@@@@ O,I,P,E,s,U INVADERS FONTS @@@@@@@@@@
// O - Outvader (Invader legs out)
const byte outvaderfont[][8] PROGMEM = { // "abcd"
{ 0x0e, 0x15, 0x0e, 0x11, 0x00, 0x00, 0x00, 0x00 }, // 'a' /oo\ row 1
{ 0x00, 0x0e, 0x15, 0x0e, 0x11, 0x00, 0x00, 0x00 }, // 'b' /oo\ row 2
{ 0x00, 0x00, 0x0e, 0x15, 0x0e, 0x11, 0x00, 0x00 }, // 'c' /oo\ row 3
{ 0x00, 0x00, 0x00, 0x0e, 0x15, 0x0e, 0x11, 0x00 } // 'd' /oo\ row 4
};
// I - Invader (Invader legs in)
const byte invaderfont[][8] PROGMEM = { // "abcd"
{ 0x0e, 0x15, 0x0e, 0x0a, 0x00, 0x00, 0x00, 0x00 }, // 'a' \oo/ row 1
{ 0x00, 0x0e, 0x15, 0x0e, 0x0a, 0x00, 0x00, 0x00 }, // 'b' \oo/ row 2
{ 0x00, 0x00, 0x0e, 0x15, 0x0e, 0x0a, 0x00, 0x00 }, // 'c' \oo/ row 3
{ 0x00, 0x00, 0x00, 0x0e, 0x15, 0x0e, 0x0a, 0x00 } // 'd' \oo/ row 4
};
// P - Laser
const byte laserfont[][8] PROGMEM = { // "12345_"
{ 0x00, 0x00, 0x00, 0x00, 0x04, 0x0e, 0x1f, 0x1f }, // '1' frame 1
{ 0x00, 0x00, 0x04, 0x04, 0x04, 0x0e, 0x1f, 0x1f }, // '2' frame 2
{ 0x04, 0x04, 0x04, 0x00, 0x04, 0x0e, 0x1f, 0x1f }, // '3' frame 3
{ 0x04, 0x04, 0x00, 0x00, 0x04, 0x0e, 0x1f, 0x1f }, // '4' frame 4
{ 0x04, 0x00, 0x00, 0x00, 0x04, 0x0e, 0x1f, 0x1f }, // '5' frame 5
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f } // '_' Empty laser storage
};
// E - Explosion
const byte kapowfont[][8] PROGMEM = { // "12345"
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x0e, 0x00 }, // '1' frame 1
{ 0x00, 0x00, 0x00, 0x00, 0x15, 0x0e, 0x0e, 0x15 }, // '2' frame 2
{ 0x00, 0x00, 0x00, 0x00, 0x11, 0x04, 0x04, 0x11 }, // '3' frame 3
{ 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x11 }, // '4' frame 4
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } // '5' frame 5
};
// s - Shield
const byte shieldfont[][8] PROGMEM = { // "LR"
{ 0x0f, 0xff, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00 }, // 'L' LH half
{ 0x1e, 0xff, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00 } // 'R' RH half
};
// U - Mothership/UFO
// Six frames of 4-char animation. Gameplay stops and
// the mothership flies across the LCD screen.
const byte UFOfont[][8] PROGMEM = { // "ABCDIJKLQRSTabcdijklqrst"
{ 0x00, 0x03, 0x07, 0x0d, 0x1f, 0x07, 0x02, 0x00 }, // 'A'
{ 0x1e, 0x1f, 0x1f, 0x0d, 0x1f, 0x0c, 0x00, 0x00 }, // 'B'
{ 0x00, 0x10, 0x18, 0x0c, 0x1e, 0x18, 0x10, 0x00 }, // 'C'
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // 'D'
//
{ 0x00, 0x01, 0x03, 0x05, 0x0f, 0x03, 0x01, 0x00 }, // 'I'
{ 0x0f, 0x1f, 0x1f, 0x0d, 0x1f, 0x06, 0x00, 0x00 }, // 'J'
{ 0x00, 0x18, 0x1c, 0x0e, 0x1f, 0x1c, 0x08, 0x00 }, // 'K'
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // 'L'
//
{ 0x00, 0x00, 0x01, 0x03, 0x07, 0x01, 0x00, 0x00 }, // 'Q'
{ 0x07, 0x1f, 0x1f, 0x0d, 0x1f, 0x13, 0x00, 0x00 }, // 'R'
{ 0x00, 0x1c, 0x1e, 0x0d, 0x1f, 0x0e, 0x04, 0x00 }, // 'S'
{ 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00 }, // 'T'
//
{ 0x00, 0x00, 0x00, 0x01, 0x03, 0x00, 0x00, 0x00 }, // 'a'
{ 0x03, 0x0f, 0x1f, 0x0d, 0x1f, 0x19, 0x10, 0x00 }, // 'b'
{ 0x10, 0x1e, 0x1f, 0x0d, 0x1f, 0x07, 0x02, 0x00 }, // 'c'
{ 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00 }, // 'd'
//
{ 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00 }, // 'i'
{ 0x01, 0x0f, 0x1f, 0x0d, 0x1f, 0x1c, 0x08, 0x00 }, // 'j'
{ 0x18, 0x1f, 0x1f, 0x0d, 0x1f, 0x13, 0x01, 0x00 }, // 'k'
{ 0x00, 0x00, 0x00, 0x10, 0x18, 0x00, 0x00, 0x00 }, // 'l'
//
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // 'q'
{ 0x00, 0x07, 0x0f, 0x1d, 0x1f, 0x0e, 0x04, 0x00 }, // 'r'
{ 0x1c, 0x1f, 0x1f, 0x0d, 0x1f, 0x19, 0x00, 0x00 }, // 's'
{ 0x00, 0x00, 0x10, 0x18, 0x1c, 0x10, 0x00, 0x00 } // 't'
};
/*----------------------------------------------*/
/* Define the CustomFont struct using typedef */
/*----------------------------------------------*/
typedef struct {
char fontFlag;
const byte (*fontBits)[8]; // Pointer to the CC bitmap arrays.
String fontChars;
} CustomFont;
/*----------------------------------------------*/
/* Create instances of the CustomFont struct, */
/* and assemble into a CustomFont array of */
/* struct, one array per font */
/*----------------------------------------------*/
// Order of fields: fontFlag, fontBits, fontChars
CustomFont font0 = { 'G', bargraphfont, "0abcLR!87654321" }; // 15
CustomFont font1 = { '0', big0font, "ABCD" }; // 4 Location of the parts forming two bignums on-screen:
CustomFont font2 = { '1', big1font, "ABCD" }; // 4 AB A'B'
CustomFont font3 = { '2', big2font, "ABCD" }; // 4 CD C'D'
CustomFont font4 = { '3', big3font, "ABCD" }; // 4 A maximum of two bignums digits!
CustomFont font5 = { '4', big4font, "ABCD" }; // 4
CustomFont font6 = { '5', big5font, "ABCD" }; // 4
CustomFont font7 = { '6', big6font, "ABCD" }; // 4
CustomFont font8 = { '7', big7font, "ABCD" }; // 4
CustomFont font9 = { '8', big8font, "ABCD" }; // 4
CustomFont font10 = { '9', big9font, "ABCD" }; // 4
CustomFont font11 = { 'X', symbolfont, "DSTMH><^vuds_1234KkLQnNbCF" }; // 26
CustomFont font12 = { 'b', Boldfont, "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" }; // 36
CustomFont font13 = { 'B', boldlcfont, "abcdefghijklmnopqrstuvwxyz" }; // 26
CustomFont font14 = { 'L', lowercasefont, "gjpqym" }; // 6
CustomFont font15 = { 'c', smallcapsfont, "ABDEFGHIJKLMNPQRTUY" }; // 19
CustomFont font16 = { 'F', smlfrequfont, "kHz" }; // 3
CustomFont font17 = { 'V', smlrevcapsfont, "BCcFLSsTR" }; // 9
CustomFont font18 = { 'v', revcapsfont, "ABCDEFGHIKLOPRSTUVXYZ0123456789=:" }; // 33
CustomFont font19 = { 'C', channelfont, "241dca5b3ef6xyzuvwjkl" }; // 21
CustomFont font20 = { 'l', logofont, "LRdtsHDC0123456" }; // 15
CustomFont font21 = { 'O', outvaderfont, "abcd" }; // 4
CustomFont font22 = { 'I', invaderfont, "abcd" }; // 4
CustomFont font23 = { 'P', laserfont, "12345_" }; // 6
CustomFont font24 = { 'E', kapowfont, "12345" }; // 5
CustomFont font25 = { 's', shieldfont, "LR" }; // 2
CustomFont font26 = { 'U', UFOfont, "ABCDIJKLQRSTabcdijklqrst" }; // 24
CustomFont fonts[] = { font0, font1, font2, font3, font4, font5, font6,
font7, font8, font9, font10, font11, font12, font13,
font14, font15, font16, font17, font18, font19, font20,
font21, font22, font23, font24, font25, font26 };
/*----------------------------------------------*/
/* getBitmap() - FETCH CUSTOM CHARACTER BITMAPS */
/*----------------------------------------------*/
// This function returns the bitmap for a fontFlag, fontChar pair.
// NOTES: The foreach loop (a.k.a "range-based for loop"), introduced with C++11,
// simplifies traversal over an iterable data set. It does this by eliminating the
// initialization process and traversing over each and every element rather than an iterator.
// uint8_t* getBitmap(char myfontChar, char myfontFlag) {
// for (const CustomFont& font : fonts) {
// if (font.fontFlag == myfontFlag) {
// int charIndex = font.fontChars.indexOf(myfontChar);
// if (charIndex != -1) return font.fontBits[charIndex];
// }
// }
// return nullptr; // Font or Character not found
// }
uint8_t* getBitmap(char myfontChar, char myfontFlag) {
for (const CustomFont& font : fonts) {
if (font.fontFlag == myfontFlag) {
int charIndex = font.fontChars.indexOf(myfontChar);
if (charIndex != -1) {
static uint8_t bitmap[8];
for (int i = 0; i < 8; i++) {
bitmap[i] = pgm_read_byte_near(font.fontBits[charIndex] + i);
}
return bitmap;
}
}
}
return nullptr; // Font or Character not found
}
/*********************************************************/
/* setupRow2() - Set up Row 2 for Double-row CC graphics */
/*********************************************************/
void setupRow2() {
int8_t startPos2 = startPos + 20;
startPos = startPos2; // Update startPos for row2
lcdString = lcdString.substring((lcdString.indexOf('|') << 1) +1 );
return;
}
/*----------------------------------------------------*/
/* clearTheStage() - Clears the LCD 'stage', making */
/* it ready for a new LCD 'scene' composed of one or */
/* several lcdStrings. */
/* It clears the LCD and various relevant variables: */
/* the CC_id_table[], the LCD CGRAM, and screenImage. */
/*----------------------------------------------------*/
void clearTheStage() {
for (CGRAM_ptr = 0; CGRAM_ptr < 8; CGRAM_ptr++) {
CC_id_table[CGRAM_ptr] = ""; // Clears out CC_id_table [0 to 7].
lcd.createChar(CGRAM_ptr, blankBitmap); delay(1); // Blank out all 8 CGRAM locations.
}
screenImage = ""; for (int i = 0; i < 80; i++) screenImage += ' '; // Fill the stage with 80 spaces
lcd.setCursor(0,0); lcd.print(" "); // 20 spaces
lcd.setCursor(0,1); lcd.print(" ");
// ---This is the equivalent of: startPos = 0; lcdString = " 80 spaces "; LCDfontManager();
return;
}
/*---------------------------------*/
/* LCDsplash() - LCD SIGN-ON */
/* (Does not use the font manager) */
/*---------------------------------*/
// Uses CGRAM locations 8,9,10,11,12.
void LCDsplash() {
byte my_F[8] = { 0x1f, 0x18, 0x18, 0x1e, 0x18, 0x18, 0x18, 0x00 }; // Bold F
byte my_P[8] = { 0x1e, 0x19, 0x19, 0x19, 0x1e, 0x18, 0x18, 0x00 }; // Bold P
byte myBar8[8] = { 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b }; // Bar graph = 8
byte myBar6[8] = { 0x00, 0x00, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b }; // Bar graph = 6
byte myBar4[8] = { 0x00, 0x00, 0x00, 0x00, 0x1b, 0x1b, 0x1b, 0x1b }; // Bar graph = 4
byte my_star[8] = { 0x04, 0x15, 0x0e, 0x1f, 0x0e, 0x15, 0x04, 0x00 }; // custom '*' char 1
lcd.createChar(8,my_F);
lcd.createChar(9,my_P);
lcd.createChar(10,myBar4);
lcd.createChar(11,myBar6);
lcd.createChar(12,myBar8);
lcd.createChar(13,my_star);
lcd.setCursor(0,0);
// Very important: DO NOT embed byte(0) in Strings, and don't try to print it to the LCD!!! (it's equivalent to \0, the
// end-of-c-string null character). The issue arises because Hitachi HD44870 LCD controllers use byte(0) as a CGRAM address.
// Fortunately, the CGRAM 0x00-0x07 addresses are mirrored from 0x08-0x0f. Those embed in Strings without problems!
lcd.write(byte(13));lcd.print("-----");
lcd.write(byte(8));lcd.print("ont ");
lcd.write(byte(9));lcd.print("al------");
lcd.setCursor(0,1);
for(uint8_t i = 0; i < 3; i++) lcd.write(byte(10)); //4
lcd.print("2");
for(uint8_t i = 0; i < 3; i++) lcd.write(byte(11)); //6
lcd.print(".");
for(uint8_t i = 0; i < 4; i++) lcd.write(byte(12)); //8
lcd.print("0");
for(uint8_t i = 0; i < 3; i++) lcd.write(byte(11)); //6
lcd.print("0");
for(uint8_t i = 0; i < 3; i++) lcd.write(byte(10)); //4
delay(1000);
}
/*---------------------------------*/
/* printStringHex() */
/*---------------------------------*/
void printStringHex(const String& myString)
{
String Interp = "";
Serial.print(" ");
for (size_t i = 0; i < myString.length(); ++i)
{
char c = myString.charAt(i);
Interp = Interp + String(c); // + " ";
if (c > 15) Serial.print("0x");
else Serial.print("0x0");
Serial.print(c, HEX); // Print the character as a hexadecimal value
Serial.print(" "); // Add inter-character space
}
Serial.print(Interp);
//Serial.println(); // Print a newline at the end
}
/*---------------------------------------------*/
/* ++++++++++++++++ S E T U P ++++++++++++++++ */
/* ++++++++++++++++ S E T U P ++++++++++++++++ */
/* ++++++++++++++++ S E T U P ++++++++++++++++ */
/*---------------------------------------------*/
void setup() {
lcd.begin(20,2);
lcd.clear();
//Serial.begin(9600);
//Serial.print("\n");myFontsPrinter(); // FOR DEBUG & MAINTENANCE ONLY
LCDsplash();
delay(1000);
// The following six or seven lines are an example of how you might set up
// a device spash screen, in place of Font Pal's own LCDsplash() screen,
// and can be run as part of Example 1, below, or commented out.
//
}
/*---------------------------------------------*/
/* ++++++++++++++++ L O O P ++++++++++++++++ */
/* ++++++++++++++++ L O O P ++++++++++++++++ */
/* ++++++++++++++++ L O O P ++++++++++++++++ */
/*---------------------------------------------*/
void loop() {
// - - - - - - - - - - - - - - - S P E C I A L T E S T - - - - - - - - - - - - - - - - - - - - -
// The following pair of tests show that when constructing a sceene (in this example, from two parts), font-
// formatted text written into a second field will not affect font-formatted text written to the LCD previously.
// In both cases (which change the order of writing), the result of writing ten bold "I" characters is correctly
// shown as "ABCDEFGH ##########T", and "##########T ABCDEFGH", respectively.
//
// -----SCENE 1:
// clearTheStage();
// startPos = 0;
// lcdString = "ABCDEFGH Plain Text!|"
// "bbbbbbbb ";
// LCDfontManager();
// delay(1000);
// startPos = 9;
// lcdString = "IIIIIIIIII |"
// "bbbbbbbbbb ";
// LCDfontManager();
// -----SCENE 2:
// clearTheStage();
// startPos = 0;
// lcdString = "Plain Text! ABCDEFGH|"
// " bbbbbbbb";
// LCDfontManager();
// delay(1000);
// startPos = 0;
// lcdString = "IIIIIIIIII |"
// "bbbbbbbbbb ";
// LCDfontManager();
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
//while(true){};
// clearTheStage();
// startPos = 0;
// lcdString = "B AB BAB Plain Text!|" // Row 0 should be: [B AB BAB Plain Text!] (correct!)
// "b bb bbb " //
// "B AA BAA BAEbHIJKXYZ|" // Row 1 should be: [B AA BAA BAE@HIJKX##] (correct!)
// "b bb bbb bbbbbbbbbbb";
// LCDfontManager();
// setupRow2();
// LCDfontManager();
// delay(3000);
// clearTheStage();
// startPos = 0;
// lcdString = "B A BBBBBAADEFGHIJKZ|" // Row 0 should be: [B A BBBBBAADEFGHI###] (correct)
// "b b bbbbbbbbbbbbbbbb"
// "B AB B sB BAB AAAZ s|" // Row 1 should be: [B AB B @B BAB AAA# @] (correct)
// "b bb b bb bbb bbbb b";
// LCDfontManager();
// setupRow2();
// LCDfontManager();
// delay(3000);
// clearTheStage();
// startPos = 0;
// lcdString = "ABCDABCDABCDABCDABCD|" //(correct)
// "bbbbbbbbbbbbbbbbbbbb";
// LCDfontManager();
// delay(3000);
// clearTheStage(); // WORKS!
// startPos = 0;
// lcdString = "DTD DDDDD TTTTT DTDT|" //(correct)
// "bbb bbbbb bbbbb bbbb";
// LCDfontManager();
// delay(3000);
// clearTheStage();
// startPos = 0;
// lcdString = "D T D DDDDD TTTTT DT|" //(correct)
// "b b b bbbbb bbbbb bb";
// LCDfontManager();
// delay(3000);
// clearTheStage();
// startPos = 0;
// lcdString = "B ZBBBAB ZB T DBB|" //(correct)
// "b bbbbbb bb b bbb";
// LCDfontManager();
// startPos = 20;
// lcdString = "Works!";
// LCDfontManager();
// delay(3000);
// clearTheStage();
// startPos = 0;
// lcdString = "B AB BBBB AAAA AB |" //(correct)
// "b bb bbbb bbbb bb ";
// LCDfontManager();
// startPos = 20;
// lcdString = "Works!";
// LCDfontManager();
// delay(3000);
// clearTheStage();
// startPos = 0;
// lcdString = "B A B BBBB AAAA A B |" //(Correct)
// "b b b bbbb bbbb b b ";
// LCDfontManager();
// startPos = 20;
// lcdString = "Space AB & it Works!";
// LCDfontManager();
// delay(3000);
// clearTheStage();
// startPos = 0;
// lcdString = "C AC CCCCC AAAAA AC|" //(Correct)
// "b bb bbbbb bbbbb bb";
// LCDfontManager();
// startPos = 20;
// lcdString = "Swap B <--> C Works!";
// LCDfontManager();
// delay(3000);
// clearTheStage();
// startPos = 0;
// lcdString = "B|" //(Correct)
// "b";
// LCDfontManager();
// startPos = 20;
// lcdString = "Works!";
// LCDfontManager();
// delay(3000);
// clearTheStage();
// startPos = 0;
// lcdString = "BA|" //(Correct)
// "bb";
// LCDfontManager();
// startPos = 20;
// lcdString = "Works!";
// LCDfontManager();
// delay(3000);
// clearTheStage();
// startPos = 0;
// lcdString = "B A|" //(Correct)
// "b b";
// LCDfontManager();
// startPos = 20;
// lcdString = "Works!";
// LCDfontManager();
// delay(3000);
// clearTheStage();
// startPos = 0;
// lcdString = "B AB|" //(Correct)
// "b bb";
// LCDfontManager();
// startPos = 20;
// lcdString = "Works!";
// LCDfontManager();
// delay(3000);
// clearTheStage();
// startPos = 0;
// lcdString = "B A B|" //(Correct)
// "b b b";
// LCDfontManager();
// startPos = 20;
// lcdString = "Works!";
// LCDfontManager();
// delay(3000);
// =========
// EXAMPLE 1: LCD 'scene construction' emulating the display of a home theater digital surround sound processor:
// =========
// ---------------------------------------------------------------------------
// TEST THE CONSTRUCTION OF AN APPLICATION SCREEN FROM SEVERAL SMALLER STRINGS:
//
// (i) clearTheStage() is generally used once only, prior to any LCD loop. One
// exception to this would occur if we jump out of that loop to display a bignums
// volume up/dpwm adjustment screen (which also uses all 8 CCs). Then before
// starting the type of loop shown in this example, clearTheStage() would be called.
// Therefore, because this test loops through various status readouts, which change
// by emulating the user changing inputs, I have moved clearThe Stage() to the end
// of void Setup().
//
// (ii) Perhaps not so obvious, is that the daisy[n] strings have two rows, and
// therefore require a call to setupRow2() and a second call to LCDfontManager().
//
// (iii) Perhaps even less obvious is that lcdStrings written to the LCD must be
// padded with spaces, not only to prevent fragments of prior lcdStrings from being
// visible, but also because this is how Font Pal purges any CCs referenced by the
// overwritten String from the CC_id_table, thereby freeing up those table locations
// for new lcdStrings and their CC references. This is why, generally, we don't need
// to have a line of code like the following to free up CC_id_table locations:
// startPos = 4; lcdString = " "; LCDfontManager();
// The only case I can think of where this line might be needed is if a field is
// purposefully blanked. A non-font-formatted string of plain-text spaces is
// sufficient for this purpose.
clearTheStage(); // Clear LCD image and CCs prior to 'building an LCD Scene'
startPos = 0;
lcdString = " TheaterMaster SIGNATURE |"
" bBBBBBBbBBBBB ";
LCDfontManager();
delay(3000);
//while(true){};
clearTheStage(); // Clear LCD image and CCs for an 'LCD Scene' (Already done at the end of Void Setup(),
startPos = 32; // above, but may be needed here if other exampes are run immediately before this.
lcdString = "-24.5 dB"; // 0 CCs
LCDfontManager();
delay(100);
startPos = 24;
lcdString = "D3:DVD"; // 0 CCs An example of a plain-text string.
LCDfontManager();
delay(100);
startPos = 4;
lcdString = "No Signal |" // 1 CC. String is padded with spaces to the end of the field.
" L ";
LCDfontManager();
delay(100);
startPos = 0;
lcdString = daisy[7]; // 0/0/0 channel configuration not yet determined (4 CCs)
LCDfontManager();
setupRow2();
LCDfontManager();
delay(1000);
startPos = 24;
lcdString = "D1:Sat"; // 0 CCs An example of a non font-encoded string.
LCDfontManager();
delay(100);
startPos = 4;
lcdString = "LR Digital 5.1 |" // 3 CCs. String is padded with spaces to the end of the field.
"ll L ";
LCDfontManager();
delay(100);
startPos = 0;
lcdString = daisy[8]; // 3/2/0.1 ('5.1') channel configuration (4 CCs)
LCDfontManager();
setupRow2();
LCDfontManager();
delay(1000);
startPos = 24;
lcdString = "D2:Blu"; // 0 CCs An example of a non font-encoded string.
LCDfontManager();
delay(100);
startPos = 4;
lcdString = "dts ES 6.1 |" // 4 CCs. Padded out with spaces to match the length of the field.
"lll ";
LCDfontManager();
delay(100);
startPos = 0;
lcdString = daisy[16]; // 3/3/0.1 ('6.1') channel configuration (4 CCs)
LCDfontManager();
setupRow2();
LCDfontManager();
delay(1000);
//while(true){}; // uncomment to stop the loop
//----------------------------------------------------------------------------------------
// Other Examples of font-formatted 'blurb' Strings:
// clearTheStage();
// startPos = 5;
// lcdString = "ABABABAB |88880000 " // Double-row Strings must
// "CDCDCDCDPro|88880000 "; // be of equal length! Correct: 8800Pro
// LCDfontManager();
// setupRow2();
// LCDfontManager();
// delay(3000);
// clearTheStage();
// startPos = 0;
// lcdString = "LR RL Hey Baby Bluejay LLLLLLLLLL RRRRR|" \\(Correct) Correctly shows ##
// "ll ll b L b L bBBBLBL llllllllll lllll";
// LCDfontManager();
// delay(3000);
// clearTheStage();
// startPos = 0;
// lcdString = "SDS B AB S DS v|" // (correct)
// "XXX b bb X XX X"
// "SDS B AB S DS ^|"
// "XXX b bb X XX X";
// LCDfontManager();
// setupRow2();
// LCDfontManager();
// delay(2000);
// clearTheStage();
// startPos = 0;
// lcdString = "All Bold A A A A A A A A A A A A A A A B|" //
// " b b b b b b b b b b b b b b b b b";
// LCDfontManager();
// delay(3000);
// clearTheStage();
// startPos = 0;
// lcdString = "ABABABABABABABABABABABABABABABABABABABAB|" //
// "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb";
// LCDfontManager();
// delay(3000);
// clearTheStage();
// startPos = 0;
// lcdString = "All Bold AAAAAAAAABA|" // (Correct)
// " b bbbbbbbbbbb"
// "AAAAAAAAAAAAAAAAAAAB|"
// "bbbbbbbbbbbbbbbbbbbb";
// LCDfontManager();
// setupRow2();
// LCDfontManager();
// delay(3000);
// clearTheStage();
// startPos = 0;
// lcdString = "WWW b wwwwwwwwwwwwwb|" // (Correct)
// " B BBBBBBBBBBBBBB"
// "AAAAAAAAAAAAAAAAAAAA|"
// "bbbbbbbbbbbbbbbbbbbb";
// LCDfontManager();
// setupRow2();
// LCDfontManager();
// delay(3000);
// clearTheStage();
// startPos = 0;
// lcdString = "SSSSSSSSSSSSSSSSSSSTSSSSSSSSSSSSSSSSSSST|" // (Correct)
// "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb";
// LCDfontManager();
// delay(3000);
//while(true){}
/*
// INVADER SPLASH SCREEN (7 CCs, 7 unique)
clearTheStage();
delay(500);
startPos = 0;
lcdString = " Space Invaders |"
" bL b "
" b- ABC- |"
" I UUU ";
LCDfontManager();
setupRow2();
LCDfontManager();
delay(800);
startPos = 25;
lcdString = "2"; // Add points.
LCDfontManager();
delay(200);
startPos = 26;
lcdString = "5";
LCDfontManager();
delay(400);
startPos = 34;
lcdString = "1";
LCDfontManager();
delay(200);
startPos = 35;
lcdString = "0";
LCDfontManager();
delay(200);
startPos = 36;
lcdString = "0";
LCDfontManager();
delay(1500);
// INVADER GET READY! SCREEN (3 CCs, 2 unique)
clearTheStage();
startPos = 0;
lcdString = " GET 000"
" READY! ";
LCDfontManager();
delay(1000);
startPos = 37;
lcdString = "111|PPP"; // Stock up on lasers.
LCDfontManager();
delay(1000);
startPos + 37;
lcdString = "_|P"; // Show one laser used/in use: '_'
LCDfontManager();
delay(500);
//while(true) {}
// INVADER GAME SCREEN (cannot work properly until section clear wipes out CCs from CC_id_table)
clearTheStage();
startPos = 0;
lcdString = " 000|"
" "
" LR LR 3 LR LR _11|"
" ss ss P ss ss PPP";
LCDfontManager();
setupRow2();
LCDfontManager();
for (int8_t row=0; row<4; row++){ // 0 to 3
for (int8_t col=0; col<7; col++){ // 0 to 8
startPos = 0;
lcdString = " "; // 16 " ". Update to section clear later
LCDfontManager();
if (col % 2) lcdString = invArray[row][0]; else lcdString = invArray[row][1];
if (row % 2) startPos = 7 - col; else startPos = 1 + col;
LCDfontManager();
delay(600);
}
}
delay(2000);
// MOTHERSHIP FLY-BY
// INVADER OUTRO SCREEN (9 CCs, 8 unique, but with animation we have 10 CCs, 9 unique)
clearTheStage();
startPos = 0;
lcdString = "Hi-Scdrers: AJR SCR|"
"bB bBOBBBB "
" AR JSH|"
" ";
LCDfontManager();
setupRow2();
LCDfontManager();
// Now continue with a brief animation of one of the CCs:
startPos = 5;
for (uint8_t k=0; k < 8; k++) {
lcdString = "d|I"; // Space Invaders alien, legs in.
LCDfontManager();
delay(500);
lcdString = "d|O"; // Space Invaders alien, legs out.
LCDfontManager();
delay(500);
}
while(true){};
*/
// clearTheStage();
// startPos = 0;
// lcdString = "Space is big.|" // 15 CCs, 12 unique, which should trigger CGRAM overflow --> # # # # (correct)
// "bBBBB BB BBB "
// " Really big.|"
// " b L BBB ";
// LCDfontManager();
// setupRow2();
// LCDfontManager();
// delay(3000);
// startPos = 0;
// clearTheStage();
// lcdString = "You just won't believe how vastly,|" // 8 CCs, 8 unique. One @ due to bad fontFlag (correct)
// " L L BBBBBB ";
// LCDfontManager();
// delay(3000);
// clearTheStage();
// lcdString = "hugely, mind-bogglingly big it is.|" // 13 CCs, 8 unique. (Correct)
// " L L BBBB LL L L BBB ";
// LCDfontManager();
// delay(3000);
// clearTheStage();
// lcdString = "THE UNIVERSE IS HUGE|" // 17 CCs, 10 unique, which should trigger CGRAM overflow --> # # # (correct)
// "bbb bbbbbbbb bb bbbb"
// "(Douglas Adams HHGG)";
// LCDfontManager();
// setupRow2();
// LCDfontManager();
// delay(3000);
// clearTheStage();
// startPos = 0;
// lcdString = "A bright LCD displayis very easy to read|" // Example with exactly 40 screen characters. Requires 82 bytes of buffer memory. (Correct)
// " L bbb L L L L ";
// LCDfontManager();
// delay(3000);
// clearTheStage();
// startPos = 8;
// lcdString = "Hello World!"; // Example of plain text (i.e., no CCs) (Correct)
// LCDfontManager();
// delay(3000);
//while(true){};
// clearTheStage();
// startPos = 0;
// lcdString = " TheaterMaster SIGNATURE |"
// " bBBBBBBbBBBBB ";
// LCDfontManager();
// delay(3000);
// clearTheStage();
// startPos = 0;
// lcdString = " TheaterMaster Ovation |"
// " bBBBBBB ";
// LCDfontManager();
// delay(3000);
// clearTheStage();
// startPos = 0;
// lcdString = " TheaterMaster Encore |"
// " bBBBBB ";
// LCDfontManager();
// delay(3000);
// clearTheStage();
// startPos = 0;
// lcdString = "CPU:6.9o Z1:00302004DIP:FF-3 Z2:00302004|" // 10 CCs, 8 unique.
// "vvv vv vvv vv ";
// LCDfontManager();
// delay(3000);
// clearTheStage();
// startPos = 0;
// lcdString = " LF cR RF Ls sB Rs |"
// " cc c cc c c c "
// " 55 77 55 77 55 77 |" // The small caps channel labels use 4 CCs. These bars need 1 CC.
// " GG GG GG GG GG GG "; // This will always work.
// LCDfontManager();
// setupRow2();
// LCDfontManager();
// delay(3000);
//while(true) {};
// Test many selection variations of a 5.1-channel surround sound speaker setup.
// Note the use of "s" and "c" lowercase characters as stand-ins for small caps.
// Without this work-around most of the following screen would be impossible.
// The best way to avoid a messy substitution (of "C" for "c" and "S" for "s")
// would be to extend the Reverse Caps font with "c" and "s", using the same
// bitmaps as the ordinary Reverse Caps (Update: Already implmented).
// Another approach would be to have reversed version of the big and small
// speakers, and to indicate selection with those.
// clearTheStage();
// startPos = 0;
// lcdString = " LF:K cR:kx RF:K Ls:kx sB:K Rs:kx|" // 14 CCs, 6 unique. 3 speakers full range, 3 limited range.
// " VV X c X cc X c X c X c X "; // The 'x' indicates that a particular small speaker has a cross-over.
// LCDfontManager();
// delay(4000);
//clearTheStage();
// startPos = 0;
// lcdString = "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF|" // 40 CCs comprising 2 unique.
// "bvbvbvbvbvbvbvbvbvbvbvbvbvbvbvbvbvbvbvbv";
// LCDfontManager();
// delay(1000);
//while(true){};
// Test of channel daisies in a home theater controller:
// WHEN IT GET THE CC_id_table CLEARING WORKING, THE WHOLE OF THIS MUST BE in ONE LOOP:
/*
clearTheStage();
startPos = 4;
lcdString = "LR Digital |" // 3 CCs
"ll L ";
LCDfontManager();
startPos = 0;
lcdString = daisy[0];
LCDfontManager();
setupRow2();
LCDfontManager();
startPos = 24;
lcdString = "3/2/0 "; // Plain text
LCDfontManager();
delay(500);
//clearTheStage();
//startPos = 4;
//lcdString = "LR Digital |" // 3 CCs
// "ll L ";
//LCDfontManager();
startPos = 0;
lcdString = daisy[1];
LCDfontManager();
setupRow2();
LCDfontManager();
startPos = 24;
lcdString = "2/P/0 "; // Plain text
LCDfontManager();
delay(500);
//clearTheStage();
//startPos = 4;
//lcdString = "LR Digital |" // 3 CCs
// "ll L ";
//LCDfontManager();
startPos = 0;
lcdString = daisy[2];
LCDfontManager();
setupRow2();
LCDfontManager();
startPos = 24;
lcdString = "2/0/0 "; // Plain text
LCDfontManager();
delay(500);
//clearTheStage();
//startPos = 4;
//lcdString = "LR Digital |" // 3 CCs
// "ll L ";
//LCDfontManager();
startPos = 0;
lcdString = daisy[3];
LCDfontManager();
setupRow2();
LCDfontManager();
startPos = 24;
lcdString = "3/1/0 "; // Plain text
LCDfontManager();
delay(500);
//clearTheStage();
//startPos = 4;
//lcdString = "LR Digital |" // 3 CCs
// "ll L ";
//LCDfontManager();
startPos = 0;
lcdString = daisy[4];
LCDfontManager();
setupRow2();
LCDfontManager();
startPos = 24;
lcdString = "3/0/0 "; // Plain text
LCDfontManager();
delay(500);
//clearTheStage();
//startPos = 4;
//lcdString = "LR Digital |" // 3 CCs
// "ll L ";
//LCDfontManager();
startPos = 0;
lcdString = daisy[5];
LCDfontManager();
setupRow2();
LCDfontManager();
startPos = 24;
lcdString = "2/2/0 "; // Plain text
LCDfontManager();
delay(500);
//clearTheStage();
//startPos = 4;
//lcdString = "LR Digital |" // 3 CCs
// "ll L ";
//LCDfontManager();
startPos = 0;
lcdString = daisy[6];
LCDfontManager();
setupRow2();
LCDfontManager();
startPos = 24;
lcdString = "1/0/0 "; // Plain text
LCDfontManager();
delay(500);
clearTheStage(); // Makes sure the LCD is cleared of previous modes.
startPos = 4;
lcdString = "No Lock"; // input PLL unable to lock (caused by input signal fault)
LCDfontManager();
startPos = 0;
lcdString = daisy[7];
LCDfontManager();
setupRow2();
LCDfontManager();
startPos = 24;
lcdString = "0/0/0 "; // Plain text
LCDfontManager();
delay(1000);
//clearTheStage();
startPos = 4;
lcdString = "LR Digital |" // 3 CCs
"ll L ";
LCDfontManager();
startPos = 0;
lcdString = daisy[8];
LCDfontManager();
setupRow2();
LCDfontManager();
startPos = 24;
lcdString = "3/2/0.1"; // Plain text
LCDfontManager();
delay(500);
//clearTheStage();
//startPos = 4;
//lcdString = "LR Digital |" // 3 CCs
// "ll L ";
//LCDfontManager();
startPos = 0;
lcdString = daisy[9];
LCDfontManager();
setupRow2();
LCDfontManager();
startPos = 24;
lcdString = "2/P/0.1"; // Plain text
LCDfontManager();
delay(500);
//clearTheStage();
//startPos = 4;
//lcdString = "LR Digital |" // 3 CCs
// "ll L ";
//LCDfontManager();
startPos = 0;
lcdString = daisy[10];
LCDfontManager();
setupRow2();
LCDfontManager();
startPos = 24;
lcdString = "2/0/0.1"; // Plain text
LCDfontManager();
delay(500);
//clearTheStage();
//startPos = 4;
//lcdString = "LR Digital |" // 3 CCs
// "ll L ";
//LCDfontManager();
startPos = 0;
lcdString = daisy[11];
LCDfontManager();
setupRow2();
LCDfontManager();
startPos = 24;
lcdString = "3/1/0.1"; // Plain text
LCDfontManager();
delay(500);
//clearTheStage();
//startPos = 4;
//lcdString = "LR Digital |" // 3 CCs
// "ll L ";
//LCDfontManager();
startPos = 0;
lcdString = daisy[12];
LCDfontManager();
setupRow2();
LCDfontManager();
startPos = 24;
lcdString = "3/0/0.1"; // Plain text
LCDfontManager();
delay(500);
//clearTheStage();
//startPos = 4;
//lcdString = "LR Digital |" // 3 CCs
// "ll L ";
//LCDfontManager();
startPos = 0;
lcdString = daisy[13];
LCDfontManager();
setupRow2();
LCDfontManager();
startPos = 24;
lcdString = "2/2/0.1"; // Plain text
LCDfontManager();
delay(500);
//clearTheStage();
//startPos = 4;
//lcdString = "LR Digital |" // 3 CCs
// "ll L ";
//LCDfontManager();
startPos = 0;
lcdString = daisy[14];
LCDfontManager();
setupRow2();
LCDfontManager();
startPos = 24;
lcdString = "1/0/0.1"; // Plain text
LCDfontManager();
delay(500);
clearTheStage();
startPos = 4;
lcdString = "dts ES |" // 3 CCs
"lll ";
LCDfontManager();
startPos = 0;
lcdString = daisy[15];
LCDfontManager();
setupRow2();
LCDfontManager();
startPos = 24;
lcdString = "3/3/0 "; // Plain text
LCDfontManager();
delay(500);
//clearTheStage();
//startPos = 4;
//lcdString = "dts ES |" // 3 CCs
// "lll ";
//LCDfontManager();
startPos = 0;
lcdString = daisy[16];
LCDfontManager();
setupRow2();
LCDfontManager();
startPos = 24;
lcdString = "3/3/0.1"; // Plain text
LCDfontManager();
delay(500);
*/
} // ------END------
LCDfontManager.ino
/*-------------------------------------------------------*/
/* LCDfontManager() - Liquid Crstal Display Font Manager */
/*-------------------------------------------------------*/
void LCDfontManager() {
//Serial.begin(9600);
bool fontEncoded = true;
String merged_lcdString= "";
// BEGIN FONT PROCESSING...
// ==[1]====================
// Scan lcdString to detect '|' markers...
int8_t marker_idx = lcdString.indexOf('|');
if (marker_idx == -1) fontEncoded = false; // If lcdString is plain text, print it more-or-less directly...
// ==[2]====================
// Perform an interleave merge of fontChar & fontFlag sections of lcdString, excluding the '|' marker
// (e.g., "LR DoobyoD|ll BBB LBB" --> "LlRl DBoBoBb yLoBDB"), and also processes the top row of double-row
// symbols (e.g., "Test|B CASE|BBBB|" --> "TBe s t "):
if (fontEncoded == true) {
merged_lcdString = "";
for (uint8_t i = 0; i < marker_idx; i++) {
merged_lcdString += lcdString.charAt(i);
merged_lcdString += lcdString.charAt(marker_idx + 1 + i);
}
}
else { // if lcdString isn't font-encoded, treat as plain text...
merged_lcdString = "";
for (uint8_t i = 0; i < lcdString.length(); i++) merged_lcdString += (lcdString.charAt(i) + String(" "));
in_CC_id_table = false; // Is this line needed?
}
// ==[3]====================
// TO MAXIMIZE THE EIGHT-CC CGRAM RESOURCE, WE NOW NEED TO CLEAN UP THE CC_id_table,
// removing any entries that will be overwritten by CCs in the new lcdString.
int16_t mystartPos;
mystartPos = startPos << 1;
uint8_t scrn_ptr = mystartPos;
while (scrn_ptr < (mystartPos + merged_lcdString.length())) {
uint8_t fontByte = screenImage.charAt(scrn_ptr);
if ((fontByte >= 0x08) && (fontByte <= 0x0f)) CC_id_table[fontByte - 8] = "";
scrn_ptr += 2;
}
screenImage = screenImage.substring(0, mystartPos) + merged_lcdString + screenImage.substring((mystartPos) + merged_lcdString.length());
// ==[4]====================
// BEGIN A LONG LOOP, PROCESSING THE MERGED LCDSTRING INTO SCREENIMAGE AT STARTPOS, LOADING CC
// BITMAPS INTO THE CGRAM, FLAGGING FONT ERRORS AND ANY CGRAM OVERFLOW (@ and # errors, respectively).
// Parse screenImage for CC flags, loading the CC bitmaps to the LCD chip, also replacing fontChars
// in screenImage with CGRAM_ptrs. Processed CCs have the fontChar part replaced with a byte between 0x08
// and 0x0f, and the fontFlag part with " ". Subtracting 8 from the 0x08-0x0f byte gives the 0-7 location
// of the CC in the CC_id_table. This offset of 8 avoids problems caused by ASCII null characters (0x00),
// related to the use of \0 as an end-of-string marker in C and C++.
scrn_ptr = startPos;
while (scrn_ptr < 40) {
uint16_t even_scrn_ptr = scrn_ptr << 1;
uint16_t odd_scrn_ptr = even_scrn_ptr + 1;
String current_fontChar = String(screenImage.charAt(even_scrn_ptr)); // e.g., "q" font char IF FONTCHAR IS 8-15, ignore
String current_fontFlag = String(screenImage.charAt(odd_scrn_ptr)); // e.g., "l" bold caps font-flag
// %%%%% If the current fontChar has a non-blank fontFlag, we assume that it must be an unprocessed CC...
// (There are two types of CCs: Those that are in the cc_id_table and those that are not.)
if (current_fontFlag != " ") {
uint8_t* gBitmap = getBitmap(current_fontChar.c_str()[0], current_fontFlag.c_str()[0]);
// - where uint8_t* is a pointer into the CC bitmap array of struct. Example: Get the 'g' bitmap from the 'L' font. [0] ensures 1 byte
String current_CC_id = current_fontChar + current_fontFlag; // e.g., "dL" ( = "d" + "L")
if (gBitmap) {
// Type (A) --- Those that already have a bitmap in CGRAM and are in the CC_id_table:
// For these we replace all instances of it in screenImage with the index of the current_CC_id in the CC_id_table (0x08-0x0f + " ")...
in_CC_id_table = false; // initial assumption is that current_CC_id is not in CC_id_table
for (CGRAM_ptr = 0; CGRAM_ptr < 8; CGRAM_ptr++)
if (CC_id_table[CGRAM_ptr] == current_CC_id) { // E.g., if CC_id_table[0-7] == "ql", then...
in_CC_id_table = true; // ...set in_CC_id_table to true, and replace all instances of current_CC_id:
screenImage.replace(current_CC_id, String(char(CGRAM_ptr + 0x08)) + String(" "));
}
// Type (B) --- Those that have no bitmap and are not in the CC_id_table:
// IF the current_CC_id is not already in the CC_id_table, save it to a free location in the table, and then replace
// all instances of it in screenImage with the index of the current_CC_id in the CC_id_table (0x08-0x0f + " " )...
if (in_CC_id_table == false) {
// Locate 1st free location in CC_id_table:
CGRAM_ptr = 0; while ((CGRAM_ptr < 8) && (CC_id_table[CGRAM_ptr] != "")) CGRAM_ptr++;
// If CC_id_table has a free location, save the current_CC_id there,
// load the bitmap into CGRAM, and replace all instances in screenImage with (CGRAM_ptr + 0x08) + " ":
if (CGRAM_ptr < 8) {
CC_id_table[CGRAM_ptr] = current_CC_id; // CC_id format is, e.g., "ql"
lcd.createChar((CGRAM_ptr + 0x08), gBitmap); // Load bitmap into the LCD chip's CGRAM
in_CC_id_table = true; // Set in == true, and then replace all instances of current_CC_id in...
screenImage.replace(current_CC_id, String(char(CGRAM_ptr + 0x08)) + String(" ")); // ...screenImage with (CGRAM_ptr + 0x08) + " "
}
// If the CC_id_table is full (due to all eight CCGRAM locations being in use),
// signal CGRAM overflow by replacing all instances of the current_CC_id in screenImage with "# ":
if (CGRAM_ptr == 8) screenImage.replace(current_CC_id, String("# "));
}
}
// IF the current bitmap is not found (due to the char/font combination being missing or incorrect),
// signal a Char/Font Error by replacing all instances of the current_CC_id in screenImage with "@ ":
else screenImage.replace(current_CC_id, String("@ "));
}
// %%%%% CC_ids ignored by the above font processing include the following:
// Plain-text characters in font-formatted Strings, e.g., "a ";
// Characters in plain-text Strings e.g., "Plain Text!";
// Processed CCs, e.g. String(char(0x0a) + String(" ");
// The "@ " (bad char/font), and "# " (CGRAM overflow) error flags.
//
scrn_ptr += 1; // Loop back to beginning to process the next CC_id in screenImage...
}
// ==[5]====================
// Finally, print all EVEN characters in screenImage 0,2,4,6,..., to the LCD display:
lcd.setCursor(0,0);
String final_screenImage = "";
for (scrn_ptr = 0; scrn_ptr < screenImage.length(); scrn_ptr += 2) final_screenImage = final_screenImage + screenImage.charAt(scrn_ptr);
lcd.setCursor(0,0); // row 0
lcd.print(final_screenImage.substring(0, 20));
lcd.setCursor(0,1); // row 1
lcd.print(final_screenImage.substring(20, 40));
final_screenImage = "";
}
ReadMe.ino
/*
#############################################################################
################## A r d u i n o - F o n t - P a l ##################
################## ( F o n t P a l o o z a ) ##################
################## F O R H I T A C H I H D 4 4 7 8 0 ##################
################## L C D C O N T R O L L E R S ##################
#############################################################################
Copyright (c) 2024 Alastair Roxburgh
(Abstract: Font Pal recreates the functionality of an original 68HC11 stack-
frame assembly language program named SCREENS, devised in 1997 by Alastair
Roxburgh & Sunil Rawal for the EOS series of TheaterMaster digital home theater
audio processors. Written in Arduino C++, Font Pal makes heavy use of the String
class and arrays of struct to create a custom font manager for 2x20 LCD displays
based on the Hitachi HD44780 chip.)
Note:
While Font Pal, which was developed expressly for the Arduino Mega platform,
reproduces most of the functionality of its 1977 68HC11 predecessor its
internal algorithms are only remotely similar.
Font Pal is free software: you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
Foundation version 3 of the License.
Font Pal is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MECHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU Public License for more details.
You should have received a copy of the GNU General Public License along with
Font Pal. If not, see <http://www.gnu.org/licenses/>.
See the license.txt file for further licensing & copyright details.
-----------------------------------------------------------------------
-----------------------------------------------------------------------
History
2024.09.04 Alastair Roxburgh (kiwiengr) - initial creation
2024.09.13 Alastair Roxburgh (kiwiengr) - Updated Example 1: 'LCD scene'
2024.09.19 Alastair Roxburgh (kiwiengr) - Alpha release. Note: Font letter name changes.
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
---To Do---
Once everything is working, add more error chekcing:
a) Check that the length of lcdString <=40.
b) If there is one '|' in lcdString, check that lcdString.length() - 2 * marker_idx = 1.
c) If there are two '|' in lcdString, check that lcdString.length() - 4 * marker_idx = 2.
d) Consider how to select LCD size: 16x1, 20x1, 16x2, or 20x4.
e) Determine if there a sensible way to configure Font Pal as an Arduino library.
SUMMARY
=======
Font Pal is a font manager for Arduino and Hitachi HD44780-based LCDs, so-named
as a more easily pronounced version of Font-Palooza, a handle inspired by (a more
challenging to pronounce) Lollapalooza, the famous recurring music festival first
held in 1991. Since then, the word "palooza" has been used so often that it has
entered the mainstream language as a stand-alone word, a neologism. Indeed, following
in Lollapalooza's footsteps, "palooza" is now defined as an event or thing that aspires
to be "an extraordinary or unusual thing, person, or event; an exceptional example or
instance" (dictionary.com). Some of my favorite examples are Hula Palooza, Datapalooza,
law-LA-palooza, St. Lou-a-palooza, and MINE-a-palooza (sometimes hyphenated, sometimes
not; sometimes preceded by the "a" from Lolla). OED Online traces Lollapalooza in its
various spellings back to 1904 but, strangely, makes no mention of Lala Palooza, the
name of the female protagonist in Pulitzer prize winner Rube Goldberg's 1936-1939 comic
strip of the same name.
Hence, with apologies to Douglas Adams, Font Pal hopes it, too, will be seen as
impressive and wholly remarkable, even if not as wildly popular and in-your-face
as the asteroid named Arthurdent (asteroid 25924).
Written in Arduino C++ and making heavy use of the String type and arrays of struct,
Font Pal recreates the functionality of an original 68HC11 stack-frame assembly
language program that was devised in 1997 by Alastair Roxburgh & Sunil Rawal for
the ground-breaking EOS series of TheaterMaster digital home theater audio processors.
Although this new implementation mirrors many of the capabilities and structure
of its 68HC11-based predecessor, apart from using a similar character bitmap
database, many of the algorithmic details are different. Interestingly, whereas
the 68HV11E1 microcontroller used in EOS TheaterMasters is an 8-/16-bit 2 MHz
CISC-based design, the Arduino's 16 MHz 8-bit RISC-based ATmega2560 runs at a
similar speed overall, the bottleneck being the modest speed of the Hitachi
HD44780 controller chip in typical LCDs, not to mention the relatively slow
response of typical Okaya STN Transflective LCDs (which can take 150 ms to go
from clear to full black when a typical character is displayed and 300 ms to
fade to clear when a space is displayed.)
TABLE OF CONTENTS:
=================
INTRODUCTION
USING FONT PAL
FONT FOMATTING OF LCD DISPLAY STRINGS
DISPLAYING PLAIN TEXT
MORE HINTS ON HOW TO USE FONT PAL
THE FOLLOWING METHOD IS NOT RECOMMENDED FOR DOUBLE ROW CC GRAPHICS
ERROR REPORTING
GENERAL POINTS ABOUT HITACHI HD44780-BASED LCD DISPLAYS
NOTES:
A SUMMARY OF FONT PAL FORMATS AND COMMANDS
LCDfontManager() Implemtation Notes
INTRODUCTION
============
Font Pal for the Arduino Mega platform allows the C++ app designer to bypass
much of the frequently complex chore of custom character (CC) and font manage-
ment for typical Hitachi HD44780 character-based LCDs. In its essentials, Font
Pal automates the management of CGRAM, a scarce memory resource internal to the
HD44780 LCD controller chip, that stores the bit-maps of up to eight CCs. Font
Pal reduces some of the barriers to programming complex and varied sequences of
CCs. Working behind the scenes, the HD44780 loads and unloads CC bitmaps in real
time, according to the moment-by-moment graphics needs of a particular software
application, be it a simple hobby video game, or something more serious such as
an embedded controller in a home theater receiver or a home control system. Font
Pal's value proposition is that it separates the chore of font management from
the application code, giving the application programmer more time to develop
application features not bogged down by the tedious task of manually managing
CCs and CGRAM memory.
When Font Pal loads a CC into CGRAM, it stores a tag to that CC in every data
display memory (DDRAM) location that will display the CC. When parts of the
screen are updated with new CCs, Font Pal first erases the relevant locations
in CGRAM. If this happens to erase a CC that is still in use in a different
part of the screen, and as long as it will not exceed the limit of eight CCs,
the CC will be reloaded into CGRAM.
Consider how much of an all-around improvement Font Pal can produce:
1) Reduced application development time,
2) Improved application quality,
3) Nicer-looking and more legible displays,
4) Better LCD utilization through optimized CC automation.
Such improvements will not necessarily be subtle, and may also defer,
indefinitely, the need to move to a bit-mapped display and faster
microprocessor.
Several examples of Font Pal use follow:
EXAMPLE (1):
Font Pal can quickly turn a 2x20 LCD into a 2- to 10-channel VU meter,
simultaneously displaying up to 16 levels per channel at 6 dB per step.
If we reserve the bottom bar for zero signal and ignore the 6 dB gap
between the two LCD rows, we obtain a total range on each channel of
15*6 dB = 90 dB, which can be updated several times a second. Indeed,
by using the two LCD rows separately, we can display even 20 channels
with half the vertical resolution. In these extreme cases of power
through repetition of bitmaps, eight static bitmaps stored in the
CGRAM are stretched to as many as twenty simultaneously displayed
locations on the LCD, as in the following examples of symmetrical
VU mater layouts:
xxxxxxx xxxxxxx 2 channels
xxxxxx xxxxxx xxxxxx 3 channels
xxxx xxxx xxxx xxxx 4 channels
xxx xxx xxxx xxx xxx 5 channels
xx xx xxx xxx xx xx 6 channels
xx xx xx xx xx xx xx 8 channels
x x x x xx x x x x 9 channels
x x x x x x x x x x 10 channels
where 'x' represents a single bar-font character. The following diagram
shows a VU meter for ten audio channels constructed using a 2x20 char LCD.
For 6 dB steps, converting from linear amplitude to logarithmic (dB)
amplitude is straightforward: the audio DSP averages the observed position
of the highest 1-bit in arithmetically positive audio samples (typically
16- to 24-bit words). It does not matter if the number representation is
integer or fractional. If the VU meter processes five values per second,
an appropriate averaging period is about 200 ms or 10,000 audio samples.
If you require a peak-reading VU meter, display the position of the highest
bit observed during the same period rather than the average. You can also
slide the dB scale up or down in 6 dB steps to set the 0 dB wherever you
want. The diagram (below) shows amplitudes of 0, 0, 60 dB, 66 dB, 18 dB,
78 dB, 30 dB, 36 dB, 96 dB, and 0, where '0' (not defined on a logarithmic
scale) represents zero signal. This example illustrates how you can stretch
the eight CGRAM locations to create a VU meter for up to 10 channels on a
2x20 LCD:
+--------------------+
| 2 3 5 8 |
| 1 1 8 8 4 8 6 7 8 1|
+--------------------+
where the numeral signifies the height in pixels of a block-shaped CC.
A '1' alone in a channel (i.e., the bottom row of pixels only) represents
zero signal, leaving a range of 16 x 6 dB = 96 dB (including the 1-pixel-
height inter-row gap of 6 dB). You can find an example of this type of bar
graph in the optional Font Pal splash screen. Also included in Font Pal is
a utility function, printFonts(), which can help you verify the correctness
of the font bitmap tables after you've made changes.
In the vertical bargraphs just described, the entire resource of eight CCs
is dedicated to this one use. Nevertheless, we are free to choose the number
of channels and bar widths because these take advantage of the freedom to
repeat CCs to build bars of any suitable widths. This way, we can create
displays that compel the eye of the beholder, creating the impression that
there are more than eight individually programmable CCs. I need to point
out that the Font Pal LCD manager avoids the method of tricking the eye
through CC animation, which can lead to less-than-satisfactory results due
to the relatively slow response time of STN (super-twisted nematic) trans-
flective LCDs. Font Pal's bar graph characters have a central gap to hide
the distracting presence of the inter-character gap when the bar width is
greater than one character. This gap also allows for flexibility in setting
the width of bars and using mixed bar widths while maintaining the same look
of the bars. For example, if we construct three bars having widths of one,
two, and three characters, respectively, with heights of 1, 3, and 2 pixels,
they will look something like:
XX XX XX XX XX XX XX XX XX XX
XX XX XX XX XX XX XX XX XX XX
XX XX XX XX XX XX XX XX XX XX XX XX XX XX
rather than:
XXXXX XXXXX XXXXX
XXXXX XXXXX XXXXX XXXXX XXXXX
XXXXX XXXXX XXXXX XXXXX XXXXX XXXXX XXXXX
The split bar design of the top example makes the bars look more uniform.
EXAMPLE (2):
A home theater processor's LCD screen might look like this:
+--------------------+
|vOv LR Digital 5.1 |
|^Q^ D1:Blu X -15 dB|
+--------------------+
where the eight unique CCs are marked as vO^QLRXg, for a total of ten CCs
on screen. The vOv and ^Q^ represent a "channel daisy" that indicates which
channels are in use with the current signal source, requiring four unique
CCs. The "LR" is two CCs forming a Dolby "Double-Dee" logo; the "g" is an
improved lowercase g with a true descender, and the "X" is an equalization
indicator, a single CC in the shape of 'E/Q'.
The Channel ("C") font produces 6.1 channel layouts similar to the following:
* C C = Center front
* * LF RF LF = Left Front
* correspond channels: SUB RF = Right Front
* * LS RS LS = Left Surround
* ES RS = Right Surround
SUB = Subwoofer
EXAMPLE (3):
A micro-space-invaders game layout might look like this:
+--------------------+
| x x x x x 000|
| /\ /\ ! /\ /\ _!!|
+--------------------+
where the five on screen CCs are marked x/\!, representing an invader (repeated
five times), the "/\" is a pair of CCs that has the shape of a shield, while "!"
is a CC in the shape of a laser. The invaders use ten different CCs (five with
legs in and 5 with legs out) that load in sequence as the waves of aliens descend
ever closer. Arduino C++ code for constructing the above screen (I have added
comments about the various items in this code fragment):
startPos = 0; // The position (0-39) on the screen where the String will be written.
lcdString = "a a a a 000|" // No ';' "|" marks the end of a font-formatted String
"I I I I " // No ';'
" LR LR 3 LR LR _11|" // No ';' Another "|" because this is a 2-row screen-write.
" ss ss P ss ss PPP"; // Yes ';' marks end of C++ statement.
LCDfontManager(); // This writes the first LCD row.
setupRow2(); // Housekeeping function needed...
LCDfontManager(); // ahead of writing the second row of the 2-row String.
EXAMPLE (4):
A screen example that might look at home in a home theater audio processor:
+--------------------+
|LF:O CR:ox LR:O |
|LS:ox SB:O RS:ox |
+--------------------+
where the LF, CR, LR, LS, SB, and RS, use a mixture of lowercase ('c' and 's')
and small-caps CCs (L,F,R,B) to render a larger set of small-caps using only four
CCs. Together with speaker-shaped CCs (large and small, denoted here as 'O' and
'o'), and a regular lowercase 'x' indicating channels with small speakers that
cross-over the low frequencies to the subwoofer channel, a readout that helps to
simplify the speaker and basemanagement setup for a home theater processor.
This screen uses fourteen CCs, Six of these are unique, but, as a result of
repetition, gives the impression that there are more than that.
USING FONT PAL
==============
FONT FOMATTING OF LCD DISPLAY STRINGS:
-------------------------------------
This routine parses font-encoded Strings, N characters at a time, loading the
relevant custom characters before sending the processed String to a Hitachi
HD44780-compatible LCD module for display.
Example String (where "_" represents an ASCII space 0x20, but you would type
an actual space character):
clearTheStage();
startPos = 0;
lcdString = "Large LCD Display|"
" l BBB l l";
LCDfontManager();
There can be up to 40 display characters before the '|' marker character,
with the corresponding font flags in the following row, which is convenient
when constructing a display String on the fly. The VU meter capability is
discussed later. In the meantime, here is a more normal string that fills
all 40 locations on a 2x20 LCD:
clearTheStage();
startPos = 0
lcdString = "This long String fills two lines exactly|" // 2 CCs, 2 unique.
" l l l";
LCDfontManager();
The number of characters after the "|" must equal the number before it.
Counting is zero-relative, so the marker character is at position 40. Thus,
the number of fontChars before the "|" is 40, and there are 40 fontFlags
after the "|". A space in the second line indicates that the LCD's default
ROM-based characters will be used. The total number of characters in this
string is 81, of which only the first 40 get displayed (after CC substitutions).
Important Note: The Font Pal LCD font manager expects ALL font-formatted
Strings to have the second part marked by "|" and containing the fontFlags.
DISPLAYING PLAIN TEXT:
---------------------
In addition to displaying font-formatted strings, Font Pal also supports
un-font-formatted Strings, a.k.a. plain text. Or it can display a mixture
of the two. However, if plain text suffices for a particular LCD field, and
is 100% plain text (i.e., no font flags or "|" font marker character), the
text is displayed more quickly. Refer to the examples below for more guidance
on using this feature.
MORE HINTS ON HOW TO USE FONT PAL:
------------------------------------
You can copy and paste the following examples of Font Pal into your Arduino
IDE, or you can uncomments the examples in the void loop() section of the
main .ino file.
(a) An example of how to display a plain text String at position 25 on the
LCD, euivalent to placing the text starting at position 5 in the second row.
Note: If our intention is to add a new String to an an existing LCD 'scene',
we would omit clearTheStage().
clearTheStage(); // This line may not be needed (see note)
startPos = 25; // Allowable range is 0-39
lcdString = "D2:DVD"; // Plaintext example (no font encoding)
LCDfontManager();
The variable named startPos is used to select the position on the LCD where
you want to display the text. Strings that are positioned so that they extend
beyond the end of row 0 of the LCD, wrap around onto row 1. Strings that extend
past 40 characters (i.e., the size of the screen) are truncated. The variable
startPos has a default value of 0, and can go as high as 39.
(b) The best way to blank out only part of the LCD is to write a blank plain-
text String:
// Write six spaces to the LCD, starting at position 8:
startPos = 8;
lcdString = " "; // <-- Note: no '|' marker.
LCDfontManager();
(c) An example of displaying a String at position 4 that has mixed font-formatted
text and plain text:
clearTheStage();
startPos = 4;
lcdString = "LR Dolby digital|" // <-- Note the '|' marker, and no ';'
"LL l l "; // Need the ';' here, though!
LCDfontManager();
(d) To make sure that a new character string written to the LCD completely overwrites
any previous write to that field, it is a simple matter to pad an item with spaces,
illustrated by the extra space in the following bold-formatted "ON" String. Without
this extra space, the last "F" in "OFF would still be on the LCD, giving "ONF":
"ON |BB "; and "OFF|BBB";
(e) Whenever beginning a new LCD 'scene', the best way to clear the LCD and all
CCs stored in the CGRAM,of the LCD is to 'clear the stage':
clearTheStage(); // Do this before building a new 'scene'
On the other hand, if we just need to clear all or part of the LCD, without
clearing the relevant CCs out of CGRAM, we can write some plain-text spaces.
The following writes 8 plain-text spaces beginning at screen position 10:
startPos = 10;
lcdString = " "; // 8 plain-text spaces beginning at 10.
Note: This is simpler and takes much less time than the following alternative
approach of using a font-formated string of spaces to write over the same field:
startPos = 10;
lcdString = " |" // 8 spaces beginning at 10.
" ";
or:
startPos = 10;
lcdString = " | "; // 8 spaces beginning at 10.
(f) Most useful for shapes that take up two lines (e.g., the channel 'daisy'
CC patterns defined in the fint library) is the ability to break up the
pattern into two short Strings of equal length, rather than overwrite the
rest of the display with spaces. These lcdStrings differ from the standard
lcdString in the use of two "|" markers, one for each row:
startPos = 0;
lcdString = "232|CCC"
"1e1|CCC";
If you prefer, this may be written in the following alternative (equivalent)
form:
startPos = 0;
lcdString = "232|" // Make sure all four Strings have the same length.
"CCC"
"1e1|"
"CCC";
However, to make either of these double-row examples work, you must use two
calls to LCDfontManager(). I have omitted the optional call to clearTheStage()
because if the LCD already displayed a different 'daisy', this new one would
rewrite it, leaving any other text on the LCD unchanged. It is important to
note that the 'daisy' examples all use four unique CCs, leaving only four
for any other lcsStrings that are part of this 'scene'.
Either:
// Recommended for formatting double row CC graphics:
clearTheStage();
startPos = 0;
lcdString = "232|CCC"
"1e1|CCC";
LCDfontManager();
setupRow2();
LCDfontManager();
Or:
// Recommended for formatting double row CC graphics:
clearTheStage();
startPos = 0;
lcdString = "232|"
"CCC"
"1e1|"
"CCC";
LCDfontManager();
setupRow2();
LCDfontManager();
Without the setupRow2() function and the second call to LCDfontManager(),
the second row ("1e1|CCC") would be ignored and not get printed.
THE FOLLOWING METHOD IS NOT RECOMMENDED FOR DOUBLE ROW CC GRAPHICS:
------------------------------------------------------------------
The method of combining both rows of characters into a single string that
wraps onto the second row of the LCD fails to be as useful because not only
does it fail to mimic the LCD layout, but it cannot avoid overwriting other
items on the LCD. Although it bypasses the need for the setupRow2() helper
function and a second call to the LCD font manager, it leaves you with the
need to pack out lcdString with enough spaces to push the second row characters
("1e1|CCC" in this example) past the end of the first row and onto the second
row, which (depending on the value of startPos) may write over parts of LCD
screen you wish to keep. However, unless that is your intention, the two "|"
marker format shown above largely avoids the use of space characters to
correctly format double-row graphics, and is the preferred method.
// The following is not recommended for double-row CC graphics:
startPos = 0;
lcdString = "232 1e1|CCC CCC";
LCDfontManager();
ERROR REPORTING:
---------------
Various error conditions are indicated by the appearance of the following special
characters on the LCD (which the controller displays in place of every requested,
but unavailable custom character):
"#" --- CGRAM overflow (more than 8 unique custom characters)
"@" --- Bad font-char or bad font-flag
GENERAL POINTS ABOUT HITACHI HD44780-BASED LCD DISPLAYS:
-------------------------------------------------------
The LCD is used write-only, with all screen editing taking place in the screenImage
RAM buffer that is copied to the LCD display by calls to the LCDfontManager() function.
A major part of any digital controller design that uses a Hitachi HD44780-based LCD display
is consideration of the font-formatting design; juggling the timing of *what* is shown on
the LCD, and *when*, given the scarcity of CGRAM locations in the HD44780. This controller
chip can display only eight unique custom characters (CCs), mitigated by the ease with which
any CC can be repeated on-screen up to the limit of 40 characters in a 20x2 LCD display.
With due care during the design phase of a project (such as the EAD EOS TheaterMasters), the
limitation on the number of unique CCs can be made to look like many more. Esay to overlook,
but useful in this regard are the following points:
(i) Lowercase ROM characters scouvwxz are sufficiently shape-similar to the custom smallcaps
font in this package, that they can do the work of up to eight more CCs.
(ii) By carefully choosing your onscreen words and names, Font Pal can aid you in minimizing
your use of custom characters versus the use of built-in ROM characters, especially in
the all-important 'splash screen' that uniquely identifies a product or system:
Splash screen example 1: TheaterMaster SIGNATURE (8 unique CCs, 9 unique ROM chars)
clearTheStage(); startPos = 3;
lcdString = "TheaterMaster|BbbbbbbBbbbbb"; // Bold and lowercase bold
LCDfontManager(); startPos = 25;
lcdString = "SIGNATURE"; // Plain text
LCDfontManager();
delay(3000);
or
clearTheStage(); startPos = 3;
lcdString = "TheaterMaster SIGNATURE|"
"BbbbbbbBbbbbb ";
LCDfontManager();
delay(3000);
Splash screen example 2: TheaterMaster Ovation (7 unique CCs, 8 unique ROM chars)
clearTheStage(); startPos = 3;
lcdString = "TheaterMaster Ovation|"
" Bbbbbbb";
LCDfontManager();
delay(3000);
Splash screen example 3: TheaterMaster Encore (6 unique CCs, 8 unique ROM chars)
clearTheStage(); startPos = 3;
lcdString = "TheaterMaster Encore|"
" Bbbbbb";
LCDfontManager();
delay(3000);
Splash screen example 4: 8800Pro (8 unique bignum CCs, 3 unique ROM characters)
clearTheStage(); startPos = 5;
lcdString = "ABABABAB |88880000 " // Double-row Strings must
"CDCDCDCDPro|88880000 "; // be of equal length!
LCDfontManager(); setupRow2(); LCDfontManager();
delay(3000);
Splash/Feature screen example 5: (6 unique CCs, 9 unique ROM characters)
clearTheStage(); startPos = 1;
lcdString = "Reference CinemaTM & Cinema 7.1TM|"
"B B SS B B BSS";
LCDfontManager();
delay(3000);
(iii) Letters in-common between font-enhanced words come at no additional CGRAM cost.
(iv) Although CGRAM limits unique custom characters (CCs) to eight, they can be repeated
up to the size of the display (a 2x20 LCD can display as many as 40 CCs).
(v) Eight CCs is sufficient to code multi-channel vertical bargraphs that have 16 steps,
and horizontal bargraphs that have up to 100 steps.
(vi) Sometimes it's feasible to design multi-character CCs in a way that hides the one-pixel
gap between LCD characters and LCD rows (with reference to Okaya and similar 2x20 LCDs).
Examples of this are provided in the bargraph font.
(vii) Use the "#" and "@" error flags to help you design screen layouts free from CGRAM
overflow and font errors.
NOTES:
During this development, I discovered that Arduino variables of type String, and built-in
functions that process them, often have a problem with bytes or characters with the value
0x00, i.e., byte(0). This is a carry-over from C (and C++'s) use of the \0 null character
(0x00) the end-of-string marker in traditional C-strings, and even though the relatively
new String type does not use such markers, my attempts to print Strings containing 0x00
sometimes had problems. Perhaps the Hitachi HD44780 designers already anticipated this
problem, because, to their credit, they arranged for a set of shadow addresses, allowing
us to access to those same eight CC bitmap locations in the chip's CGRAM at 0x00-0x07 or
0x08-0x0f (BTW, that OR is an inclusive-OR). Therefore, where it matters in this program,
we number the HD44780 LCD controller's CGRAM locations from 0x08 to 0x0f (8 to 15).
However, because no such duplicate addressing exists for the various arrays, all of which
start at index 0, make sure to add 8 to all zero-relative custom character (CC) CGRAM
pointer values when assembling a 'scene' (a name used here to describe an LCD display
that is made up of two or more small Strings that are assembled into the screenImage
String being written to the LCD.
Example of Arduino C++ code for displaying two aligned 20-character Strings:
clearTheStage();
startPos = 0;
lcdString = "Hi-Sccrers: AJR SCR|" // Begins at startPos on the LCD.
"bb bbObbbb "
" AR JSH|" // Begins at startPos + 20 on LCD.
" ";
LCDfontManager();
setupRow2();
LCDfontManager();
Note that all such two-row strings have two '|' font markers. While the same
display may be constructed using a single String, the lack of inherent screen
layout makes verification more difficult, despite fewer function calls:
clearTheStage();
startPos = 0;
lcdString = "Hi-Scorers: AJR SCR AR JSH|"
"bB bBOBBBB ";
LCDfontManager();
Behind the scenes, after custom font processing, lcdString overwrites screenImage,
which, in turn, is written to the LCD. Whichever way you edit your Strings, the
resulting LCD screen layout will be similar to the following:
+--------------------+
|Hi-Scorers: AJR SCR|
| AR JSH|
+--------------------+
*/
// = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
/*
-----A SUMMARY OF FONT PAL FORMATS AND COMMANDS-----
-----A SUMMARY OF FONT PAL FORMATS AND COMMANDS-----
-----A SUMMARY OF FONT PAL FORMATS AND COMMANDS-----
1) Plain text (1-40 chars) starting at position 0-39. Example:
startPos = 5; lcdString = "Hello World!"; LCDfontManager();
2) Font-formatted text and CCs (1-40 chars) starting at position 0-39.
Formatted text must end with '|'. The fontChar and fontFlag parts of
lcdString must match. Using two lines as shown allows you to visually
check the font formatting. Example:
startPos = 24; lcdString = "Nice day for it!|"
"b L ";
LCDfontManager();
3) Optional command to clear font formatting prior to overwriting
the LCD with a new font-formatted string. This is useful when creating
an LCD 'scene' from multiple lcdStrings:
clearTheStage();
4) Font-formatting of double-height aligned blocks of text and CCs.
This is particularly useful when creating 'big nums' or other CC groupings.
Every double-height lcdString must have exactly two '|' marker characters.
The string lengths must match. Due to the HD44780 limit of eight CCs,
repetition of CCs is your friend, because, for example, a single CC can
be repeated 40 times to fill the display, at no cost. Bar graphs, in
particular can take full advantage of this. Example:
clearTheStage();
startPos = 0;
lcdString = "232|"
"CCC"
"1e1|"
"CCC";
LCDfontManager();
setupRow2();
LCDfontManager();
5) If your LCD is the STN (super-twisted nematic) transflective type, and
your display data changes several times a second, you may need to add delays
to ensure full visibility. Although providing excellent sunlight visibility
at low cost, STN LCDs can take 150 ms to go from clear to full black, and
300 ms to fade to clear. To make your display easy to read under all light
conditions, you may need to wait out these times by adding delays of 100 ms
or more. Experimentation is your best guide.
6) Dynamic displays such as bar graphs require lcdString to be constructed
on the fly,. e.g. using C++ built-in String functions.
// = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
LCDfontManager() - Liquid Crstal Display Font Manager Implemtation Notes
(1) Scan lcdString to detect '|' markers...
(2a) Perform an interleave merge of fontChar & fontFlag sections of lcdString, excluding the '|' marker,
e.g., "LR DoobyoD|ll BBB LBB" --> "LlRl DBoBoBb yLoBDB"), and also processes the top row of double-row
symbols (e.g., "Test|B CASE|BBBB|" --> "TBe s t "):
(3) We are nearly ready to copy the merged lcdString into screenImage starting at startPos, but first
we need to clean up the CC_id_table, removing any entries that will be overwritten by the current
lcdString, necessary to maximize the tiny eight CC CGRAM resource.
DETAILS: Parse the even characters in merged_screenImage for CC codes that will be overwritten by a new
merged lcdString, beginning at startPos, and ending at startPos + lcdString.length() -1. As we proceed to
discover these CCs in the relevant section of screenImage, we will delete them from the CC_id_table. And
because CCs in screenImage are codes in the range 0x08-0x0f, that is the range we will look for. The tricky
part of this parsing is to take into account that just like merged_lcsString, screenImage also uses merged
format of screenImage, wherein the relevent fontchars are at even locations, 0, 2, 4, 6, ... We can obtain
even addresses by using a simple arithmetic left-shift (<< 1). However, we don't do this for CC_id_table
(a table that contains a list of the CCs loaded into CGRAM of the LCD's controller chip). Given that C++
arrays are 0-relative, the CC_id_table addressing is 0x00 to 0x07, but because 0x00 (\nul) has special
use in C and C++ for marking the end of c-char character strings, we cannot cannot use 0x00 as a CC code
value in String function and char processing. Luckily, the HD44780 LCD controller provides a set of shadow
addresses for the CGRAM: 0x08-0x0f, obtained by adding 8 to the 0x00-0x07 addresses. This avoids having to
treat the 0x00 CC code value as a special case. However, since as mentioned above, C and C++ arrays start
at 0 and proceed up from there, this program makes use of both ranges, 0x00-0x07 and 0x08-0x0f as and when
necessary. Typcal contents of CC_id_table may look like this:
CC_id_table[0] = "2C"; // ==> CC code = 0x08 = 0 + 8 = 8 Table Format: "2C" = fonchar,fontflag
CC_id_table[1] = "3C"; // ==> CC code = 0x09 = 1 + 8 = 9
CC_id_table[2] = "1C"; // ==> CC code = 0x0a = 2 + 8 = 10
CC_id_table[3] = "eC"; // ==> CC code = 0x0b = 3 + 8 = 11
CC_id_table[4] = "dL"; // ==> CC code = 0x0c = 4 + 8 = 12
CC_id_table[5] = "tL"; // ==> CC code = 0x0d = 5 + 8 = 13
CC_id_table[6] = "gl"; // ==> CC code = 0x0e = 6 + 8 = 14 lowercase g with true descender.
CC_id_table[7] = "sL"; // ==> CC code = 0x0f = 7 + 8 = 15
If our parse finds, e.g., a CC code of 6 in the merged_screenImage overwrite section, this is a reference
to the gl entry in the table, currently stored at CC_id_table[6]. We delete it from the table with ???????????????????
Then, after lcdString is overwritten on at its startPos in the merged_screenImage, any new CCs in lcdString are
saved into free locations in CC_id_table. A condition called CGRAM overflow can occur if there are more than
eight unique custom characters (CCs). This cindition is signalled with one or more "#" flags on the LCD.
%%%%% Continuing, we now parse the to-be-overwritten section of merged_screenImage, and delete any priorCCs found:
(Prior CCs have a fontChar between 0x08 and 0x0f, with fontFlag == " ")
(4) Now parse screenImage for CC flags, load the CC bitmaps to the LCD chip, and place CGRAM_ptrs in // screenImage, in place of fontChars. Use CC_id_table to keep track of the limit of only 8 CCs!
%%%%%%%%%%%%%% BEGIN PROCESSING THE MERGED LCDSTRING INTO SCREENIMAGE AT STARTPOS, INSERTING CCs AND ERROR FLAGS AS NEEDED...
(5) Now print all EVEN characters, 0,2,4,6,..., in screen_image to the LCD display...
Note: STN transflective LCDs typically transition relatively slowly (150 ms transparent to 90% black, and 300 ms black
to 90% transparent), requiring a delay of about 200 ms before the next LCD screen write, a time best determined by experiment.
A List of Font Letter Names and Font Characters:
===============================================
Font Font Name Characters Size
G = bargraphfont 0abcLR!87654321 15
0 = big0font ABCD 4
1 = big1font ABCD 4 AB A'B'
2 = big2font ABCD 4 CD C'D'
3 = big3font ABCD 4 A maximum of two bignums digits!
4 = big4font ABCD 4
5 = big5font ABCD 4
6 = big6font ABCD 4
7 = big7font ABCD 4
8 = big8font ABCD 4
9 = big9font ABCD 4
X = symbolfont DSTMH><^vuds_1234KkLQnNbCF 26
b = Boldfont ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789 36
B = boldlcfont abcdefghijklmnopqrstuvwxyz 26
L = lowercasefont gjpqym 6
c = smallcapsfont ABDEFGHIJKLMNPQRTUY 19
F = smlfrequfont kHz 3
V = smlrevcapsfont BCcFLSsTR 9
v = revcapsfont ABCDEFGHIKLOPRSTUVXYZ0123456789=: 33
C = channelfont 241dca5b3ef6xyzuvwjkl 21
l = logofont LRdtsHDC0123456 15
O = outvaderfont abcd 4
I = invaderfont abcd 4
P = laserfont 12345_ 6
E = kapowfont 12345 5
s = shieldfont LR 2
U = UFOfont ABCDIJKLQRSTabcdijklqrst 24
*/
// = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
printFonts.ino
/*------------------------------------------------------------------------*/
/* myFontsPrinter() - PRINT ALL FONT TABLES (USE FOR DEBUG PURPOSES ONLY) */
/*------------------------------------------------------------------------*/
void myFontsPrinter() {
Serial.begin(9600);
Serial.print("\n");Serial.print("----- FONT TABLES -----\n");
for (const CustomFont &font : fonts) { // Use a foreach statement for a compact printout of the structs.
Serial.print("\nFont Flag: ");
Serial.print(font.fontFlag);
//Serial.print("\nFont Name: ");
//Serial.print(font.fontName);
Serial.print("\nFont Characters: ");
Serial.print(font.fontChars);
Serial.print("\nFont Bitmaps for Each Custom Character:");
for (uint8_t i = 0; i < font.fontChars.length(); i++) {
Serial.print("\nCharacter '");
Serial.print(font.fontChars[i]);
Serial.print("':");
for (int j = 0; j < 8; j++) {
Serial.print("0x");
if (font.fontBits[i][j] < 0x10) Serial.print("0");
Serial.print(font.fontBits[i][j], HEX);
Serial.print(" ");
}
Serial.print("\n");
}
Serial.print("\n");
}
return;
}