Incase anyone is interested, here's my MAIN.C code which creates ASCII vars/arrays for the main, aux and annunciators.
It's working as far as I can monitor the STM32 live vars in VS2022 and they are all working.
There may be some individual ASCII character issues, I still need to verify some of them i.e. the lower case ones and adjust the hex accordingly.
PS. I am not a software programmer, it's a WIP, so please be gentle!
Next step is the TFT LCD driver......at which point I will disable the existing OLED I/O as I need the pins.
Ian.
/**
******************************************************************************
* @file : main.c
* @brief : Main program body
******************************************************************************
* @attention
*
* Copyright (c) 2024 STMicroelectronics.
* All rights reserved.
*
* This software is licensed under terms that can be found in the LICENSE file
* in the root directory of this software component.
* If no LICENSE file comes with this software, it is provided AS-IS.
*
******************************************************************************
* Original code by MickleT, this version modded by IanJ,
* for 4.58" 960x320 TFT LCD (ST7701S) and using LT7680 adaptor
* Visual Studio 2022 with VisualGDB plugin:
* To upload HEX from VS2022 = BUILD then PROGRAM AND START WITHOUT DEBUGGING
* Use LIVE WATCH to view variables live debug
*
*/
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "dma.h"
#include "spi.h"
#include "gpio.h"
#include <stdarg.h>
#include <stdio.h>
#include <string.h>
#include <stdint.h>
/* Private includes ----------------------------------------------------------*/
#include "SSD1322_OLED_lib/SSD1322_HW_Driver.h"
#include "SSD1322_OLED_lib/SSD1322_API.h"
#include "SSD1322_OLED_lib/SSD1322_GFX.h"
/* Private variables ---------------------------------------------------------*/
static char main_display_debug[LINE1_LEN + 1]; // Main display debug string
#define FONT_HEIGHT 7
// Buffers for each LCD graphical item
char LCD_buffer_packets[128]; // For packet data
char LCD_buffer_bitmaps[128]; // For decoded bitmap data
char LCD_buffer_chars[128]; // For decoded characters
char G1, G2, G3, G4, G5, G6, G7, G8, G9, G10, G11, G12, G13, G14, G15, G16, G17, G18; // Main
char G19, G20, G21, G22, G23, G24, G25, G26, G27, G28, G29, G30, G31, G32, G33, G34, G35, G36, G37, G38, G39, G40, G41, G42, G43, G44, G45, G46, G47; // Aux
_Bool Annunciators[18]; // Array for MAIN annunciators. The order on LCD left to right = 8,7,6,5,4,3,2,1,017,16,15,14,13,12,11,10,9
_Bool AnnunciatorsReordered[19]; // G1 to G18 in left-to-right order
//******************************************************************************
// SPI receive buffer for packets data
volatile uint8_t rx_buffer[PACKET_WIDTH*PACKET_COUNT];
// Array with character bitmaps
uint8_t chars[CHAR_COUNT][CHAR_HEIGHT];
// Array with annunciators flags (boolean)
uint8_t flags[CHAR_COUNT];
// When scanning the display, the order of the characters output is not sequential due to optimization of the VFD PCB layout.
// The Reorder[] array is used as a lookup table to determine the correct position of characters.
const uint8_t Reorder[PACKET_COUNT] = { 8, 7, 6, 5, 4, 3, 2, 1, 0, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 17, 16, 15, 14, 13, 12, 11, 10, 9, 46, 45, 44, 43, 42, 41, 40, 39, 38, 37, 36 };
// Order of data to the display (effectively into the shift registers):
// 8, 7, 6, 5, 4, 3, 2, 1, 0, // MAIN: G9 to G1
// 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, // AUX: G19 to G36
// 17, 16, 15, 14, 13, 12, 11, 10, 9, // MAIN: G18 to G10
// 46, 45, 44, 43, 42, 41, 40, 39, 38, 37, 36 // AUX: G47 to G37
// But the actual display left to right:
// G1 to G18 - MAIN
// G19 to G47 - AUX
// AnnunciatorsReordered[1] to AnnunciatorsReordered[18]
// Used for line-by-line recoding and scaling of the characters of the main display line.
// All 32 possible variants of a 5-pixel wide character bitmap string are converted into
// the corresponding 10 OLED display pixels with 16 color gradations and packed into 5 bytes.
const uint8_t Upscale1[32][5] = {
{ 0x00, 0x00, 0x00, 0x00, 0 }, { 0x00, 0x00, 0x00, 0x00, 0xFF }, { 0x00, 0x00, 0x00, 0xFF, 0 }, { 0x00, 0x00, 0x00, 0xFF, 0xFF },
{ 0x00, 0x00, 0xFF, 0x00, 0 }, { 0x00, 0x00, 0xFF, 0x00, 0xFF }, { 0x00, 0x00, 0xFF, 0xFF, 0 }, { 0x00, 0x00, 0xFF, 0xFF, 0xFF },
{ 0x00, 0xFF, 0x00, 0x00, 0 }, { 0x00, 0xFF, 0x00, 0x00, 0xFF }, { 0x00, 0xFF, 0x00, 0xFF, 0 }, { 0x00, 0xFF, 0x00, 0xFF, 0xFF },
{ 0x00, 0xFF, 0xFF, 0x00, 0 }, { 0x00, 0xFF, 0xFF, 0x00, 0xFF }, { 0x00, 0xFF, 0xFF, 0xFF, 0 }, { 0x00, 0xFF, 0xFF, 0xFF, 0xFF },
{ 0xFF, 0x00, 0x00, 0x00, 0 }, { 0xFF, 0x00, 0x00, 0x00, 0xFF }, { 0xFF, 0x00, 0x00, 0xFF, 0 }, { 0xFF, 0x00, 0x00, 0xFF, 0xFF },
{ 0xFF, 0x00, 0xFF, 0x00, 0 }, { 0xFF, 0x00, 0xFF, 0x00, 0xFF }, { 0xFF, 0x00, 0xFF, 0xFF, 0 }, { 0xFF, 0x00, 0xFF, 0xFF, 0xFF },
{ 0xFF, 0xFF, 0x00, 0x00, 0 }, { 0xFF, 0xFF, 0x00, 0x00, 0xFF }, { 0xFF, 0xFF, 0x00, 0xFF, 0 }, { 0xFF, 0xFF, 0x00, 0xFF, 0xFF },
{ 0xFF, 0xFF, 0xFF, 0x00, 0 }, { 0xFF, 0xFF, 0xFF, 0x00, 0xFF }, { 0xFF, 0xFF, 0xFF, 0xFF, 0 }, { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }
};
// Used for line-by-line recoding and scaling of the characters of the auxiliary display line.
// All 32 possible variants of a 5-pixel wide character bitmap string are converted into
// the corresponding 5 OLED display pixels with 16 color gradations and packed into 3 bytes.
const uint8_t Upscale2[32][3] = {
{ 0x00, 0x00, 0x00 }, { 0x00, 0x00, 0xF0 }, { 0x00, 0x0F, 0x00 }, { 0x00, 0x0F, 0xF0 }, { 0x00, 0xF0, 0x00 }, { 0x00, 0xF0, 0xF0 },
{ 0x00, 0xFF, 0x00 }, { 0x00, 0xFF, 0xF0 }, { 0x0F, 0x00, 0x00 }, { 0x0F, 0x00, 0xF0 }, { 0x0F, 0x0F, 0x00 }, { 0x0F, 0x0F, 0xF0 },
{ 0x0F, 0xF0, 0x00 }, { 0x0F, 0xF0, 0xF0 }, { 0x0F, 0xFF, 0x00 }, { 0x0F, 0xFF, 0xF0 }, { 0xF0, 0x00, 0x00 }, { 0xF0, 0x00, 0xF0 },
{ 0xF0, 0x0F, 0x00 }, { 0xF0, 0x0F, 0xF0 }, { 0xF0, 0xF0, 0x00 }, { 0xF0, 0xF0, 0xF0 }, { 0xF0, 0xFF, 0x00 }, { 0xF0, 0xFF, 0xF0 },
{ 0xFF, 0x00, 0x00 }, { 0xFF, 0x00, 0xF0 }, { 0xFF, 0x0F, 0x00 }, { 0xFF, 0x0F, 0xF0 }, { 0xFF, 0xF0, 0x00 }, { 0xFF, 0xF0, 0xF0 },
{ 0xFF, 0xFF, 0x00 }, { 0xFF, 0xFF, 0xF0 }
};
// A 256x5 pixel sprite (128x5 bytes) is used to draw the annunciators by copying rectangular areas into the OLED buffer.
const uint8_t Sprites[5][128] = {
{ 0x00,0x00,0x00,0xF0,0xF0,0xF0,0x00,0x00,0x00,0xF0,0xFF,0xF0,0x0F,0x00,0x00,0x0F,0x00,0xF0,0xF0,0xFF,0xF0,0xF0,0x00,0xF0,0x00,0xF0,0x0F,0xFF,0x00,0xF0,0x0F,0x0F,
0x0F,0x0F,0x00,0xF0,0x00,0x0F,0xFF,0x00,0xFF,0xF0,0x00,0xF0,0x00,0xF0,0x0F,0x00,0xFF,0xF0,0x00,0x0F,0xF0,0x00,0xFF,0xFF,0x00,0x0F,0xFF,0x0F,0xF0,0x0F,0xF0,0x00,
0x0F,0x0F,0x00,0xF0,0xFF,0xF0,0xF0,0x00,0xFF,0x0F,0xF0,0x00,0xF0,0x00,0xFF,0x00,0xFF,0x00,0xF0,0x0F,0xF0,0x0F,0xFF,0x0F,0x00,0xF0,0xFF,0xF0,0x0F,0x00,0xF0,0x00,
0x00,0xFF,0xF0,0x0F,0xF0,0x0F,0xFF,0x0F,0x00,0x0F,0x00,0xFF,0xF0,0xF0,0x0F,0x0F,0x00,0x00,0xF0,0xFF,0xF0,0xF0,0x0F,0x00,0x0F,0xFF,0x0F,0xF0,0x0F,0xFF,0x00,0x00 },
{ 0x00,0x00,0x00,0x0F,0xFF,0x00,0x00,0x00,0x00,0xF0,0xF0,0x0F,0x0F,0x00,0x00,0xF0,0xF0,0xF0,0xF0,0x0F,0x0F,0x0F,0x00,0xF0,0x0F,0x0F,0x0F,0x0F,0x00,0xFF,0x0F,0x0F,
0x0F,0x0F,0x00,0xF0,0x00,0x0F,0x00,0xF0,0xF0,0x00,0x00,0xFF,0x0F,0xF0,0xF0,0xF0,0x0F,0x00,0x00,0xF0,0x0F,0x00,0x00,0x0F,0x00,0x0F,0x00,0x0F,0x0F,0x0F,0x0F,0x00,
0x0F,0x0F,0xF0,0xF0,0xF0,0x0F,0x0F,0x00,0xF0,0x0F,0x0F,0x0F,0x0F,0x00,0xF0,0xF0,0xF0,0x0F,0x0F,0x0F,0x0F,0x0F,0x00,0x0F,0x0F,0x0F,0x0F,0x00,0x0F,0x0F,0x0F,0x00,
0x0F,0x00,0x00,0x0F,0x0F,0x0F,0x00,0x0F,0xF0,0xFF,0x00,0x0F,0x00,0xF0,0x0F,0x0F,0x00,0x00,0xF0,0x0F,0x00,0xFF,0x0F,0x00,0x0F,0x00,0x0F,0x0F,0x0F,0x0F,0x00,0x00 },
{ 0x00,0x00,0x0F,0xFF,0x0F,0xFF,0x00,0x00,0x00,0xF0,0xF0,0x0F,0x0F,0x00,0x00,0xFF,0xF0,0xF0,0xF0,0x0F,0x0F,0x0F,0x00,0xF0,0x0F,0x0F,0x0F,0xFF,0x00,0xF0,0xFF,0x0F,
0x0F,0x0F,0x00,0xF0,0x00,0x0F,0x00,0xF0,0xFF,0x00,0x00,0xF0,0xF0,0xF0,0xFF,0xF0,0x0F,0x00,0x00,0xFF,0xFF,0x00,0x00,0xF0,0x00,0x0F,0xF0,0x0F,0x0F,0x0F,0x0F,0x00,
0x0F,0x0F,0x0F,0xF0,0xFF,0x0F,0x0F,0x00,0xFF,0x0F,0x0F,0x0F,0x0F,0x00,0xF0,0xF0,0xFF,0x0F,0xFF,0x0F,0x0F,0x0F,0xFF,0x0F,0x0F,0x0F,0x0F,0x00,0x0F,0x0F,0x0F,0x0F,
0x0F,0x0F,0xF0,0x0F,0x0F,0x0F,0xF0,0x0F,0x0F,0x0F,0x00,0x0F,0x00,0xF0,0x0F,0xF0,0x00,0x00,0xF0,0x0F,0x00,0xF0,0xFF,0x00,0x0F,0xFF,0x0F,0x0F,0x0F,0x0F,0x00,0x00 },
{ 0x00,0x00,0x00,0x0F,0xFF,0x00,0x00,0x00,0x00,0xF0,0xF0,0x0F,0x0F,0x00,0x00,0xF0,0xF0,0xF0,0xF0,0x0F,0x0F,0x0F,0x00,0xF0,0x0F,0x0F,0x0F,0x00,0x00,0xF0,0x0F,0x0F,
0x0F,0x0F,0x00,0xF0,0x00,0x0F,0x00,0xF0,0xF0,0x00,0x00,0xF0,0x00,0xF0,0xF0,0xF0,0x0F,0x00,0x00,0xF0,0x0F,0x00,0x0F,0x00,0x00,0x0F,0x00,0x0F,0xF0,0x0F,0xF0,0x00,
0x0F,0x0F,0x00,0xF0,0xF0,0x0F,0x0F,0x00,0xF0,0x0F,0xF0,0x0F,0x0F,0x00,0xFF,0x00,0xF0,0x0F,0x0F,0x0F,0xF0,0x00,0x0F,0x0F,0x0F,0x0F,0x0F,0x00,0x0F,0x0F,0x0F,0x00,
0x0F,0x00,0xF0,0x0F,0xF0,0x0F,0x00,0x0F,0x00,0x0F,0x00,0x0F,0x00,0xF0,0x0F,0x0F,0x00,0x00,0xF0,0x0F,0x00,0xF0,0x0F,0x00,0x00,0x0F,0x0F,0xF0,0x0F,0x0F,0x00,0x00 },
{ 0x00,0x00,0x00,0xF0,0xF0,0xF0,0x00,0x00,0x00,0xF0,0xFF,0xF0,0x0F,0xFF,0x00,0xF0,0xF0,0xFF,0xF0,0x0F,0x00,0xF0,0x00,0xFF,0xF0,0xF0,0x0F,0x00,0x00,0xF0,0x0F,0x0F,
0xFF,0x0F,0xF0,0xFF,0x00,0x0F,0xFF,0x00,0xF0,0x00,0x00,0xF0,0x00,0xF0,0xF0,0xF0,0x0F,0x00,0x00,0xF0,0x0F,0x00,0xFF,0xFF,0x00,0x0F,0xFF,0x0F,0x0F,0x0F,0x0F,0x00,
0x0F,0x0F,0x00,0xF0,0xF0,0x00,0xF0,0x00,0xF0,0x0F,0x0F,0x00,0xF0,0x00,0xF0,0xF0,0xFF,0x0F,0x0F,0x0F,0x0F,0x0F,0xFF,0x0F,0xF0,0xF0,0x0F,0x00,0x0F,0xF0,0xF0,0x00,
0x00,0xFF,0xF0,0x0F,0x0F,0x0F,0xFF,0x0F,0x00,0x0F,0x00,0x0F,0x00,0xFF,0x0F,0x0F,0x00,0x00,0xFF,0x0F,0x00,0xF0,0x0F,0x00,0x0F,0xFF,0x0F,0x0F,0x0F,0xF0,0xF0,0x00 } };
// Declare bytes array for a OLED frame buffer.
// Dimensions are divided by 2 because one byte contains two 4-bit grayscale pixels
uint8_t tx_buffer[64 * 256 / 2];
// Flag indicating finish of SPI transmission to OLED
volatile uint8_t SPI1_TX_completed_flag = 1;
// Flag indicating finish of SPI start-up initialization
volatile uint8_t Init_Completed_flag = 0;
/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
//******************************************************************************
// Buffer to store the converted string representation of the main display line
//char main_display_line[CHAR_COUNT + 1]; // +1 for null terminator
static char main_display_line[CHAR_COUNT + 1]; // Static ensures scope is global within the file
//SPI transmission finished interrupt callback
void HAL_SPI_TxCpltCallback(SPI_HandleTypeDef *hspi)
{
if (hspi->Instance == SPI1)
{
SPI1_TX_completed_flag = 1;
}
}
//******************************************************************************
static uint8_t InverseByte(uint8_t a)
{
a = ((a & 0x55) << 1) | ((a & 0xAA) >> 1);
a = ((a & 0x33) << 2) | ((a & 0xCC) >> 2);
return (a >> 4) | (a << 4);
}
//******************************************************************************
// Function to map character bitmaps to ASCII characters
typedef struct {
uint8_t bitmap[7]; // 7 bytes for 5x7 character bitmaps
char ascii; // Corresponding ASCII character
} BitmapChar;
// Font data: 96 characters, 7 bytes per character (each row)
const BitmapChar bitmap_characters[] = {
{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, ' '}, // Space
{{0x04, 0x04, 0x04, 0x04, 0x04, 0x00, 0x04}, '!'}, // 0x21, !
{{0x09, 0x09, 0x12, 0x00, 0x00, 0x00, 0x00}, '"'}, // 0x22, "
{{0x0A, 0x0A, 0x1F, 0x0A, 0x1F, 0x0A, 0x0A}, '#'}, // 0x23, #
{{0x04, 0x0F, 0x14, 0x0E, 0x05, 0x1E, 0x04}, '$'}, // 0x24, $
{{0x19, 0x19, 0x02, 0x04, 0x08, 0x13, 0x13}, '%'}, // 0x25, %
{{0x04, 0x0A, 0x0A, 0x0A, 0x15, 0x12, 0x0D}, '&'}, // 0x26, &
{{0x04, 0x04, 0x08, 0x00, 0x00, 0x00, 0x00}, '\''}, // 0x27, '
{{0x02, 0x04, 0x08, 0x08, 0x08, 0x04, 0x02}, '('}, // 0x28, (
{{0x08, 0x04, 0x02, 0x02, 0x02, 0x04, 0x08}, ')'}, // 0x29, )
{{0x04, 0x15, 0x0E, 0x1F, 0x0E, 0x15, 0x04}, '*'}, // 0x2A, *
{{0x00, 0x04, 0x04, 0x1F, 0x04, 0x04, 0x00}, '+'}, // 0x2B, +
{{0x00, 0x00, 0x00, 0x00, 0x04, 0x04, 0x08}, ','}, // 0x2C, ,
{{0x00, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x00}, '-'}, // 0x2D, -
{{0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x0C}, '.'}, // 0x2E, .
{{0x01, 0x01, 0x02, 0x04, 0x08, 0x10, 0x10}, '/'}, // 0x2F, /
{{0x0E, 0x11, 0x13, 0x15, 0x19, 0x11, 0x0E}, '0'}, // 0x30, 0
{{0x04, 0x0C, 0x04, 0x04, 0x04, 0x04, 0x0E}, '1'}, // 0x31, 1
{{0x0E, 0x11, 0x01, 0x02, 0x04, 0x08, 0x1F}, '2'}, // 0x32, 2
{{0x1F, 0x02, 0x04, 0x02, 0x01, 0x11, 0x0E}, '3'}, // 0x33, 3
{{0x02, 0x06, 0x0A, 0x12, 0x1F, 0x02, 0x02}, '4'}, // 0x34, 4
{{0x1F, 0x10, 0x1E, 0x01, 0x01, 0x11, 0x0E}, '5'}, // 0x35, 5
{{0x06, 0x08, 0x10, 0x1E, 0x11, 0x11, 0x0E}, '6'}, // 0x36, 6
{{0x1F, 0x01, 0x02, 0x04, 0x08, 0x08, 0x08}, '7'}, // 0x37, 7
{{0x0E, 0x11, 0x11, 0x0E, 0x11, 0x11, 0x0E}, '8'}, // 0x38, 8
{{0x0E, 0x11, 0x11, 0x0F, 0x01, 0x02, 0x0C}, '9'}, // 0x39, 9
{{0x00, 0x0C, 0x0C, 0x00, 0x0C, 0x0C, 0x00}, ':'}, // 0x3A, :
{{0x00, 0x0C, 0x0C, 0x00, 0x0C, 0x04, 0x08}, ';'}, // 0x3B, ;
{{0x02, 0x04, 0x08, 0x10, 0x08, 0x04, 0x02}, '<'}, // 0x3C, <
{{0x00, 0x00, 0x1F, 0x00, 0x1F, 0x00, 0x00}, '='}, // 0x3D, =
{{0x08, 0x04, 0x02, 0x01, 0x02, 0x04, 0x08}, '>'}, // 0x3E, >
{{0x0E, 0x11, 0x01, 0x02, 0x04, 0x00, 0x04}, '?'}, // 0x3F, ?
{{0x0E, 0x11, 0x17, 0x15, 0x17, 0x10, 0x0F}, '@'}, // 0x40, @
{{0x04, 0x0A, 0x11, 0x11, 0x1F, 0x11, 0x11}, 'A'}, // 0x41, A
{{0x1E, 0x11, 0x11, 0x1E, 0x11, 0x11, 0x1E}, 'B'}, // 0x42, B
{{0x0E, 0x11, 0x10, 0x10, 0x10, 0x11, 0x0E}, 'C'}, // 0x43, C
{{0x1C, 0x12, 0x11, 0x11, 0x11, 0x12, 0x1C}, 'D'}, // 0x44, D
{{0x1F, 0x10, 0x10, 0x1C, 0x10, 0x10, 0x1F}, 'E'}, // 0x45, E
{{0x1F, 0x10, 0x10, 0x1F, 0x10, 0x10, 0x10}, 'F'}, // 0x46, F
{{0x0E, 0x11, 0x10, 0x10, 0x13, 0x11, 0x0F}, 'G'}, // 0x47, G
{{0x11, 0x11, 0x11, 0x1F, 0x11, 0x11, 0x11}, 'H'}, // 0x48, H
{{0x0E, 0x04, 0x04, 0x04, 0x04, 0x04, 0x0E}, 'I'}, // 0x49, I
{{0x02, 0x04, 0x04, 0x04, 0x04, 0x04, 0x02}, 'J'}, // 0x4A, J
{{0x11, 0x11, 0x13, 0x15, 0x19, 0x11, 0x11}, 'K'}, // 0x4B, K
{{0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x1F}, 'L'}, // 0x4C, L
{{0x11, 0x1B, 0x1F, 0x1F, 0x11, 0x11, 0x11}, 'M'}, // 0x4D, M
{{0x11, 0x1B, 0x1F, 0x1F, 0x1B, 0x11, 0x11}, 'N'}, // 0x4E, N
{{0x0E, 0x11, 0x11, 0x11, 0x11, 0x11, 0x0E}, 'O'}, // 0x4F, O
{{0x1E, 0x11, 0x11, 0x1E, 0x10, 0x10, 0x10}, 'P'}, // 0x50, P
{{0x0E, 0x11, 0x11, 0x11, 0x11, 0x13, 0x0E}, 'Q'}, // 0x51, Q
{{0x1E, 0x11, 0x11, 0x1E, 0x14, 0x12, 0x11}, 'R'}, // 0x52, R
{{0x0E, 0x11, 0x10, 0x0E, 0x01, 0x11, 0x0E}, 'S'}, // 0x53, S
{{0x1F, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04}, 'T'}, // 0x54, T
{{0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x0E}, 'U'}, // 0x55, U
{{0x11, 0x11, 0x11, 0x11, 0x11, 0x0A, 0x04}, 'V'}, // 0x56, V
{{0x11, 0x11, 0x1F, 0x1F, 0x1F, 0x11, 0x11}, 'W'}, // 0x57, W
{{0x11, 0x11, 0x0A, 0x04, 0x04, 0x0A, 0x11}, 'X'}, // 0x58, X
{{0x11, 0x11, 0x11, 0x0A, 0x04, 0x04, 0x04}, 'Y'}, // 0x59, Y
{{0x1F, 0x01, 0x02, 0x04, 0x08, 0x10, 0x1F}, 'Z'}, // 0x5A, Z
{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, '['}, // 0x5B, [
{{0x10, 0x08, 0x04, 0x02, 0x01, 0x02, 0x04}, '\\'}, // 0x5C, \
{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, ']'}, // 0x5D, ]
{{0x00, 0x10, 0x08, 0x04, 0x02, 0x01, 0x01}, '^'}, // 0x5E, ^
{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1F}, '_'}, // 0x5F, _
{{0x01, 0x02, 0x04, 0x00, 0x00, 0x00, 0x00}, '`'}, // 0x60, `
{{0x00, 0x00, 0x0E, 0x01, 0x0F, 0x11, 0x0F}, 'a'}, // 0x61, a
{{0x10, 0x10, 0x16, 0x19, 0x11, 0x11, 0x0E}, 'b'}, // 0x62, b
{{0x00, 0x00, 0x0E, 0x10, 0x10, 0x10, 0x0E}, 'c'}, // 0x63, c
{{0x01, 0x01, 0x0D, 0x13, 0x11, 0x11, 0x0E}, 'd'}, // 0x64, d
{{0x00, 0x00, 0x0E, 0x11, 0x1F, 0x10, 0x0F}, 'e'}, // 0x65, e
{{0x06, 0x09, 0x08, 0x1C, 0x08, 0x08, 0x08}, 'f'}, // 0x66, f
{{0x00, 0x00, 0x0F, 0x11, 0x0F, 0x01, 0x1F}, 'g'}, // 0x67, g
{{0x10, 0x10, 0x16, 0x19, 0x11, 0x11, 0x11}, 'h'}, // 0x68, h
{{0x08, 0x00, 0x08, 0x18, 0x08, 0x08, 0x1C}, 'i'}, // 0x69, i
{{0x02, 0x00, 0x06, 0x02, 0x02, 0x12, 0x0C}, 'j'}, // 0x6A, j
{{0x10, 0x10, 0x12, 0x14, 0x18, 0x14, 0x12}, 'k'}, // 0x6B, k
{{0x18, 0x08, 0x08, 0x08, 0x08, 0x08, 0x1C}, 'l'}, // 0x6C, l
{{0x00, 0x00, 0x1A, 0x15, 0x15, 0x15, 0x15}, 'm'}, // 0x6D, m
{{0x00, 0x00, 0x16, 0x19, 0x11, 0x11, 0x11}, 'n'}, // 0x6E, n
{{0x00, 0x00, 0x0E, 0x11, 0x11, 0x11, 0x0E}, 'o'}, // 0x6F, o
{{0x00, 0x00, 0x1E, 0x11, 0x1E, 0x10, 0x10}, 'p'}, // 0x70, p
{{0x00, 0x00, 0x0D, 0x13, 0x0F, 0x01, 0x01}, 'q'}, // 0x71, q
{{0x00, 0x00, 0x16, 0x19, 0x10, 0x10, 0x10}, 'r'}, // 0x72, r
{{0x00, 0x00, 0x0E, 0x10, 0x0E, 0x01, 0x1E}, 's'}, // 0x73, s
{{0x08, 0x08, 0x1C, 0x08, 0x08, 0x09, 0x06}, 't'}, // 0x74, t
{{0x00, 0x00, 0x11, 0x11, 0x11, 0x13, 0x0D}, 'u'}, // 0x75, u
{{0x00, 0x00, 0x11, 0x11, 0x11, 0x0A, 0x04}, 'v'}, // 0x76, v
{{0x00, 0x00, 0x11, 0x11, 0x15, 0x15, 0x0A}, 'w'}, // 0x77, w
{{0x00, 0x00, 0x11, 0x0A, 0x04, 0x0A, 0x11}, 'x'}, // 0x78, x
{{0x00, 0x00, 0x11, 0x11, 0x0F, 0x01, 0x0E}, 'y'}, // 0x79, y
{{0x00, 0x00, 0x1F, 0x02, 0x04, 0x08, 0x1F}, 'z'}, // 0x7A, z
{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, '{'}, // 0x7B, {
{{0x01, 0x02, 0x04, 0x00, 0x04, 0x02, 0x01}, '|'}, // 0x7C, |
{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, '}'}, // 0x7D, }
{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, '~'}, // 0x7E, ~
};
// Convert a 5x7 bitmap to an ASCII character
// The bitmap (7 rows of 5 bits each) is compared row by row against the font_data.
// The comparison involves the 7 rows of the bitmap against the corresponding 7 rows in each font_data entry.
char BitmapToChar(const uint8_t *bitmap) {
// Iterate over the bitmap_characters array
for (int i = 0; i < sizeof(bitmap_characters) / sizeof(BitmapChar); i++) {
// Compare the input bitmap with the current character's bitmap
if (memcmp(bitmap, bitmap_characters[i].bitmap, FONT_HEIGHT) == 0) {
return bitmap_characters[i].ascii; // Return the matching ASCII character
}
}
// If no match is found, return '?'
return '?';
}
//******************************************************************************
// Each character on the display is encoded by a matrix of 40 bits packed
// into 5 consecutive bytes. 5x7=35 bits (S1-S35) define the pixel image of the character,
// 1 bit (S36) is the annunciator, 4 bits are not used. To optimize VFD PCB routing,
// the bit order in packets is shuffled:
//
// S17 S16 S15 S14 S13 S12 S11 S10
// S9 S8 S7 S6 S5 S4 S3 S2
// S1 S36 0 0 0 0 S35 S34
// S33 S32 S31 S30 S29 S28 S27 S26
// S25 S24 S23 S22 S21 S20 S19 S18
//
// The Packets_to_chars function sorts the character bitmap, extracts the annunciator
// flag, and stores the result in separate arrays chars[][] and flags[]
//
// 0 0 0 S1 S2 S3 S4 S5
// 0 0 0 S6 S7 S8 S9 S10
// 0 0 0 S11 S12 S13 S14 S15
// 0 0 0 S16 S17 S18 S19 S20
// 0 0 0 S21 S22 S23 S24 S25
// 0 0 0 S26 S27 S28 S29 S30
// 0 0 0 S31 S32 S33 S34 S35
//
void Packets_to_chars(void)
{
for (int i = 0; i < PACKET_COUNT; i++) {
uint8_t d0 = rx_buffer[i * PACKET_WIDTH + 0];
uint8_t d1 = rx_buffer[i * PACKET_WIDTH + 1];
uint8_t d2 = rx_buffer[i * PACKET_WIDTH + 2];
uint8_t d3 = rx_buffer[i * PACKET_WIDTH + 3];
uint8_t d4 = rx_buffer[i * PACKET_WIDTH + 4];
chars[Reorder[i]][0] = 0x1F & InverseByte((d1 << 4) | ((d2 & 0x80) >> 4));
chars[Reorder[i]][1] = 0x1F & InverseByte((d0 << 7) | ((d1 & 0xF0) >> 1));
chars[Reorder[i]][2] = 0x1F & InverseByte((d0 & 0xFE) << 2);
chars[Reorder[i]][3] = 0x1F & InverseByte(((d0 & 0xC0) >> 3) | (d4 << 5));
chars[Reorder[i]][4] = 0x1F & InverseByte(d4 & 0xF8);
chars[Reorder[i]][5] = 0x1F & InverseByte(d3 << 3);
chars[Reorder[i]][6] = 0x1F & InverseByte((d2 << 6) | ((d3 & 0xE0) >> 2));
flags[Reorder[i]] = (d2 & 0x40) == 0x40;
// Update annunciator boolean array for MAIN annunciators (G1 to G18)
if (i < 18) {
Annunciators[i] = flags[Reorder[i]];
}
}
// Null-terminate the main display line string
main_display_line[LINE1_LEN] = '\0';
}
void ReorderAnnunciators(void) {
// Map Annunciators[] to AnnunciatorsReordered[] for left-to-right order.
AnnunciatorsReordered[1] = Annunciators[8]; // G1
AnnunciatorsReordered[2] = Annunciators[7]; // G2
AnnunciatorsReordered[3] = Annunciators[6]; // G3
AnnunciatorsReordered[4] = Annunciators[5]; // G4
AnnunciatorsReordered[5] = Annunciators[4]; // G5
AnnunciatorsReordered[6] = Annunciators[3]; // G6
AnnunciatorsReordered[7] = Annunciators[2]; // G7
AnnunciatorsReordered[8] = Annunciators[1]; // G8
AnnunciatorsReordered[9] = Annunciators[0]; // G9
AnnunciatorsReordered[10] = Annunciators[17]; // G10
AnnunciatorsReordered[11] = Annunciators[16]; // G11
AnnunciatorsReordered[12] = Annunciators[15]; // G12
AnnunciatorsReordered[13] = Annunciators[14]; // G13
AnnunciatorsReordered[14] = Annunciators[13]; // G14
AnnunciatorsReordered[15] = Annunciators[12]; // G15
AnnunciatorsReordered[16] = Annunciators[11]; // G16
AnnunciatorsReordered[17] = Annunciators[10]; // G17
AnnunciatorsReordered[18] = Annunciators[9]; // G18
}
void Main_Aux_TFT_Display(void) {
// Clear LCD buffers
memset(LCD_buffer_packets, 0, sizeof(LCD_buffer_packets));
memset(LCD_buffer_bitmaps, 0, sizeof(LCD_buffer_bitmaps));
memset(LCD_buffer_chars, 0, sizeof(LCD_buffer_chars));
ReorderAnnunciators(); // re-order the annunciators so Annunnciator[1] is above G1
char annunciator_debug[256] = "Annunciators: "; // Buffer for annunciator state debug
for (int i = 0; i <= 17; i++) { // G1 to G18
// Use already-decoded data from Packets_to_chars
uint8_t *bitmap = chars[i]; // Get the bitmap for this character
char ascii_char = BitmapToChar(bitmap); // Convert bitmap to ASCII character
// MAIN Update individual variables
if (i == 0) G1 = ascii_char;
else if (i == 1) G2 = ascii_char;
else if (i == 2) G3 = ascii_char;
else if (i == 3) G4 = ascii_char;
else if (i == 4) G5 = ascii_char;
else if (i == 5) G6 = ascii_char;
else if (i == 6) G7 = ascii_char;
else if (i == 7) G8 = ascii_char;
else if (i == 8) G9 = ascii_char;
else if (i == 9) G10 = ascii_char;
else if (i == 10) G11 = ascii_char;
else if (i == 11) G12 = ascii_char;
else if (i == 12) G13 = ascii_char;
else if (i == 13) G14 = ascii_char;
else if (i == 14) G15 = ascii_char;
else if (i == 15) G16 = ascii_char;
else if (i == 16) G17 = ascii_char;
else if (i == 17) G18 = ascii_char;
// Append MAIN to debug buffers for additional debugging
snprintf(LCD_buffer_bitmaps + strlen(LCD_buffer_bitmaps),
sizeof(LCD_buffer_bitmaps) - strlen(LCD_buffer_bitmaps),
"%d : [%02X, %02X, %02X, %02X, %02X, %02X, %02X]\n",
i, bitmap[0], bitmap[1], bitmap[2], bitmap[3],
bitmap[4], bitmap[5], bitmap[6]);
// Append annunciator states to debug string
snprintf(annunciator_debug + strlen(annunciator_debug),
sizeof(annunciator_debug) - strlen(annunciator_debug),
"G%d=%s ", i + 1, Annunciators[i] ? "ON" : "OFF");
}
// Null-terminate the Main display debug string
main_display_debug[LINE1_LEN] = '\0';
for (int i = 18; i <= 46; i++) { // G19 to G47
// Use already-decoded data from Packets_to_chars
uint8_t *bitmap = chars[i]; // Get the bitmap for this character
char ascii_char = BitmapToChar(bitmap); // Convert bitmap to ASCII character
// AUX Update individual variables
if (i == 18) G19 = ascii_char;
else if (i == 19) G20 = ascii_char;
else if (i == 20) G21 = ascii_char;
else if (i == 21) G22 = ascii_char;
else if (i == 22) G23 = ascii_char;
else if (i == 23) G24 = ascii_char;
else if (i == 24) G25 = ascii_char;
else if (i == 25) G26 = ascii_char;
else if (i == 26) G27 = ascii_char;
else if (i == 27) G28 = ascii_char;
else if (i == 28) G29 = ascii_char;
else if (i == 29) G30 = ascii_char;
else if (i == 30) G31 = ascii_char;
else if (i == 31) G32 = ascii_char;
else if (i == 32) G33 = ascii_char;
else if (i == 33) G34 = ascii_char;
else if (i == 34) G35 = ascii_char;
else if (i == 35) G36 = ascii_char;
else if (i == 36) G37 = ascii_char;
else if (i == 37) G38 = ascii_char;
else if (i == 38) G39 = ascii_char;
else if (i == 39) G40 = ascii_char;
else if (i == 40) G41 = ascii_char;
else if (i == 41) G42 = ascii_char;
else if (i == 42) G43 = ascii_char;
else if (i == 43) G44 = ascii_char;
else if (i == 44) G45 = ascii_char;
else if (i == 45) G46 = ascii_char;
else if (i == 46) G47 = ascii_char;
// Append AUX to debug buffers for additional debugging
snprintf(LCD_buffer_bitmaps + strlen(LCD_buffer_bitmaps),
sizeof(LCD_buffer_bitmaps) - strlen(LCD_buffer_bitmaps),
"%d : [%02X, %02X, %02X, %02X, %02X, %02X, %02X]\n",
i, bitmap[0], bitmap[1], bitmap[2], bitmap[3],
bitmap[4], bitmap[5], bitmap[6]);
}
// Null-terminate the Aux display debug string
main_display_debug[LINE2_LEN] = '\0';
}
//************************************************************************************************************************************************************
// Main
int main(void)
{
// MCU Configuration--------------------------------------------------------
// Reset of all peripherals, Initializes the Flash interface and the Systick.
HAL_Init();
// Configure the system clock
SystemClock_Config();
// Initialize all configured peripherals
MX_GPIO_Init();
MX_DMA_Init();
MX_SPI1_Init();
MX_SPI2_Init();
/* Infinite loop */
SSD1322_API_init();
Init_Completed_flag = 1; // Now is a safe time to enable the EXTI interrupt handler
set_buffer_size(256, 64); // SSD1322 OLED size
while (1)
{
Packets_to_chars();
Main_Aux_TFT_Display();
fill_buffer(tx_buffer, 0); // Clearing OLED frame buffer
// Drawing (recoding, horizontal and vertical scaling) of the characters of the main display line
for (int i = 0; i < CHAR_HEIGHT; i++)
for (int j = 0; j < LINE1_LEN; j++)
for (int k = 0; k < 5; k++)
tx_buffer[2 + j * 7 + k + (LINE1_Y + i * 5 + 0) * 128] = tx_buffer[2 + j * 7 + k + (LINE1_Y + i * 5 + 1) * 128] =
tx_buffer[2 + j * 7 + k + (LINE1_Y + i * 5 + 2) * 128] = tx_buffer[2 + j * 7 + k + (LINE1_Y + i * 5 + 3) * 128] = Upscale1[chars[j][i]][k];
// Drawing (recoding and vertical scaling) of the characters of the auxiliary display line.
for (int i = 0; i < CHAR_HEIGHT; i++)
for (int j = 0; j < LINE2_LEN; j++)
for (int k = 0; k < 3; k++)
tx_buffer[7 + j * 4 + k + (LINE2_Y + i * 2 + 0) * 128] = tx_buffer[7 + j * 4 + k + (LINE2_Y + i * 2 + 1) * 128] = Upscale2[chars[j + LINE1_LEN][i]][k];
// Drawing of the annunciators
for (int k = 0; k < LINE1_LEN; k++)
if (flags[k])
for (int i = 0; i < 5; i++)
for (int j = 0; j < 7; j++)
tx_buffer[1 + j + k * 7 + i * 128] = Sprites[i][1 + k * 7 + j];
send_buffer_to_OLED(tx_buffer, 0, 0); // Send the frame buffer content to OLED
HAL_GPIO_TogglePin(GPIOC, TEST_OUT_Pin); // Test LED toggle
}
;
}
// System Clock Configuration
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = { 0 };
RCC_ClkInitTypeDef RCC_ClkInitStruct = { 0 };
//Initializes the RCC Oscillators according to the specified parameters in the RCC_OscInitTypeDef structure.
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
RCC_OscInitStruct.HSEState = RCC_HSE_ON;
RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;
RCC_OscInitStruct.HSIState = RCC_HSI_ON;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
Error_Handler();
}
// Initializes the CPU, AHB and APB buses clocks
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_SYSCLK
| RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)
{
Error_Handler();
}
}
// This function is executed in case of error occurrence.
void Error_Handler(void)
{
// User can add his own implementation to report the HAL error return state
__disable_irq();
while (1)
{
}
/* USER CODE END Error_Handler_Debug */
}
#ifdef USE_FULL_ASSERT
/**
* @brief Reports the name of the source file and the source line number
* where the assert_param error has occurred.
* @param file: pointer to the source file name
* @param line: assert_param error line source number
* @retval None
*/
void assert_failed(uint8_t *file, uint32_t line)
{
/* USER CODE BEGIN 6 */
/* User can add his own implementation to report the file name and line number,
ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
/* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */