Using and esp32-s3 16mb
I have a struct array where one of the members is an unsigned short array allocating 4096 bytes that will hold a 64x64 RBG565 sprite. Currently, I can read the sprite off of the SD-Card and display it on the screen. I'm trying to speed up the display refresh times and avoid constantly reading off of the SD-Card every time I want to display each sprite. I can get it to work if I make the struct array a global variable. But, I'm trying to code "properly" and pass the struct to a function to load the sprite into memory. I've tried passing the struct array with and without pointer references, passing just a pointer to the member to be updated and other various combinations and am still unable to figure out exactly what I'm missing. Many times the code will compile with no errors, yet causes the arduino to continually reset upon reaching the Init_Sprite_File function.
Here is the working code using global variables:
// ######################### Includes ##############################
#include <TFT_eSPI.h>
#include <SPI.h> // Library for SPI functions
#include <SD.h> // Library for SD Card
// ######################## Defines ################################
// LCD Display control signals using Hardware SPI
#define TFT_S1_CS_PIN 14 // Screen 1 Chip Select
#define TFT_S2_CS_PIN 8 // Screen 2 Chip Select
// These are defined in the TFT_eSPI User_Setup.h file
// #define TFT_MISO 13 // Master In - Slave Out
// #define TFT_MOSI 11 // Master Out - Slave In
// #define TFT_CLK 12 // Clock (40Mhz)
// #define TFT_DC 10 // Data/Command
// #define TFT_RST -1 // Reset, connected to Arduino reset pin
// #define TFT_LED -1 // Not using LED control
// SD Card control signals using Software SPI
#define SDC_CS_PIN 39 // SD Card Chip Select
#define SDC_CLK_PIN 36 // SD Card Clock
#define SDC_MOSI_PIN 35 // SD Card Master Out/Slave In
#define SDC_MISO_PIN 37 // SD Card Master In/Slave Out
#define BG_COLOR 0xF9D7 // Background Color as Transparent
#define DOUBLEBUFFER_X 160 // Double Buffer x = screen x
#define DOUBLEBUFFER_Y 128 // Double Buffer y = screen y
#define BACKGROUND_X 160 // Background x
#define BACKGROUND_Y 128 // Background y
#define NUM_BACKGROUNDS 4 // Number of background images
#define SPRITE_X 64 // Sprite x size
#define SPRITE_Y 64 // Sprite y size
#define NUM_SPRITES 12 // Nunber of sprites
#define MAX_PLAYERS 4
// ######################## Structures #############################
struct st_Person {
unsigned short sprite[4096]; // Actual Sprite graphic pre-loaded
unsigned char sector, sector_x, sector_y;
};
// ######################## Function Prototypes #####################
void Initialize_Screens(void);
void Put_Graphics(uint16_t x_loc, uint16_t y_loc, uint16_t x_size, uint16_t y_size, char *image_file);
void Init_Buffer(int res_x, int res_y, unsigned short color, unsigned short buffer2init[]);
void Plot_Pixel(unsigned int x, unsigned int y, unsigned short color);
void Push_Buffer(unsigned short x_res, unsigned short y_res, unsigned short buffer_to_show[], unsigned short screen_cs);
void Activate_Screen(unsigned short screen_cs);
void De_Activate_Screen(unsigned short screen_cs);
void Init_Sprite_File(unsigned short ref_number, char *file_to_open);
// ######################## Global Variables #########################
TFT_eSPI tft = TFT_eSPI(); // Create instance for TFT Screen(s) -> 1 instance for 2 screens w/ soft /CS
SPIClass SDsoftSPI; // Create instance for Software SPI
unsigned short double_buffer[20480]; // Behind the scenes buffer for flicker free animation 160x128 screen
byte screen_width, screen_height; // Self explanatory
st_Person person[MAX_PLAYERS]; // Struct that holds person information
// ######################## Setup ###################################
void setup() {
Serial.begin(115200); // Turn on Serial Monitor for debugging
Serial.println();
Initialize_Screens(); // Setup screen for use and soft /CS
screen_width = tft.getViewportWidth(); // Get the screen width and height
screen_height = tft.getViewportHeight();
Serial.print("Screen Resolution : "); // Verify to serial monitor the screen resolution
Serial.print(screen_width);
Serial.print("x");
Serial.println(screen_height);
Serial.print("Initalizing SCD... ");
SDsoftSPI.begin(SDC_CLK_PIN, SDC_MISO_PIN, SDC_MOSI_PIN, SDC_CS_PIN); // Initialized the secondary SPI Bus, used for slow devices (SD Card...)
if (!SD.begin(SDC_CS_PIN, SDsoftSPI)) // Initialize the SD Card and test if working
Serial.println("Initialization Failed");
else
Serial.println("done");
}
// ############################# Main ############################
void loop() {
int bg_index, sprite_index, pin;
char image_file[13]; // Holds image filename
unsigned int dummy;
unsigned int x, y;
bg_index=0;
sprite_index=0;
pin = TFT_S1_CS_PIN;
strcpy(image_file, "/SpWiYl.raw");
Init_Sprite_File(0, image_file);
dummy=0;
for(y=0; y<SPRITE_Y; y++) {
for(x=0; x<SPRITE_X; x++) {
Plot_Pixel(x, y, person[0].sprite[dummy]);
dummy++;
}
}
Push_Buffer(DOUBLEBUFFER_X, DOUBLEBUFFER_Y, double_buffer, TFT_S2_CS_PIN);
while(1);
}
// ############################################# FUNCTIONS #################################################
// ################################################################
// # Function: Initialize_Screens
// # Args: None
// # Description: Initialize the 2 screens used in this program
// ################################################################
void Initialize_Screens(void) {
pinMode(TFT_S1_CS_PIN, OUTPUT); // Screen 1 chip select set to output
pinMode(TFT_S2_CS_PIN, OUTPUT); // Screen 2 chip select set to output
digitalWrite(TFT_S1_CS_PIN, HIGH); // Initial /CS setting
digitalWrite(TFT_S2_CS_PIN, HIGH); // Initial /CS setting
digitalWrite(TFT_S1_CS_PIN, LOW); // Select Screen 1
digitalWrite(TFT_S2_CS_PIN, LOW); // Select Screen 2
Serial.print("Initalizing Both LCDs... ");
tft.init(); // Initialize Screen(s)
digitalWrite(TFT_S2_CS_PIN, HIGH); // De-Select Screen 2
tft.setRotation(1); // Set rotation of screen 1
tft.fillScreen(TFT_BLACK);
digitalWrite(TFT_S1_CS_PIN, HIGH); // De-Select Screen 1
digitalWrite(TFT_S2_CS_PIN, LOW); // Select Screen 2
tft.setRotation(1);
tft.fillScreen(TFT_BLACK); // Set rotation of screen 2
digitalWrite(TFT_S2_CS_PIN, HIGH); // De-Select Screen 2
Serial.println("done");
}
// ################################################################
// # Function: Put_Graphics
// # Args: uint16_t x_loc, uint16_t y_loc, uint16_t x_size, uint16_t y_size, char *file_to_open
// # Description: Reads a file off of the SD Card and places it on
// # the double buffer at location x_loc, y_loc
// # x_size, y_size is the size of the image file
// ################################################################
void Put_Graphics(uint16_t x_loc, uint16_t y_loc, uint16_t x_size, uint16_t y_size, char *file_to_open) {
File image_file;
uint16_t total_size;
uint16_t dummy, x, y;
uint8_t pixello, pixelhi;
uint16_t pixel_built;
if (!(image_file = SD.open(file_to_open, FILE_READ))) { // Check if file exists
Serial.println(F("-> File not found")); // If not, Error message
return; // and break
}
x=0; // Reset x, y coords
y=0;
while (image_file.available()) { // Parse through entire image file
pixelhi = image_file.read(); // Pixel color is kept in 2 bytes
pixello = image_file.read();
if(!((pixello+(pixelhi<<8)) == BG_COLOR)) // If not the Background color
Plot_Pixel(x_loc+x, y_loc+y, (pixello+(pixelhi<<8))); // Put it on the screen
x++; // Next pixel
if(x>=x_size) { // If width of image is reached
x=0; // go to next line
y++;
}
}
image_file.close(); // Close the file
}
// ################################################################
// # Function: Init_Buffer
// # Args: int res_x, res_y - Dimensions of the buffer,
// # 160x128 screen
// # Description: Fills the buffer with given color
// # For some reason library is inverting color bytes
// # flip bytes before rendering to buffer
// ################################################################
void Init_Buffer(int res_x, int res_y, unsigned short color, unsigned short buffer2init[]) {
int dummy;
unsigned short shiftbyte = 0x0000; // Needed for byte shifting, clear shiftbyte
shiftbyte = ((color & 0x00FF) << 8); // Mask off upper bytes and shift lower bytes left 8
color = ((color >> 8 ) | shiftbyte); // Shift upper bytes right 8 and OR shifted lower bytes
for(dummy=0; dummy<(res_x * res_y); dummy++) {
buffer2init[dummy]=color;
}
}
// ################################################################
// # Function: Plot_Pixel
// # Args: int x, int y, int color
// # Description: x, y are coords to plot the pixel on a 160x128 screen
// # color is color of pixel using RGB 565 color scheme
// # For some reason library is inverting color bytes
// # flip bytes before rendering to buffer
// # Screen resolution is 160x128
// # double_buffer[160*y + x]=color;
// # 160*y = y*128 + y*32 === Optimized, bit shifting & adding is much faster than muliplying
// # y<<7 + y<<5
// ################################################################
void Plot_Pixel(unsigned int x, unsigned int y, unsigned short color) {
unsigned short shiftbyte = 0x0000; // Needed for byte shifting, clear shiftbyte
shiftbyte = ((color & 0x00FF) << 8); // Mask off upper bytes and shift lower bytes left 8
color = ((color >> 8 ) | shiftbyte); // Shift upper bytes right 8 and OR shifted lower bytes
double_buffer[((y<<7) + (y<<5)) + x] = color;
}
// ################################################################
// # Function: Push_Image
// # Args: unsigned short x_res, unsigned short y_res, unsigned short buffer[], unsigned short screen
// # Description: This function is a helper to tft.pushImage to accomodate more that 1 screen.
// # x_res, y_res - resolution of the screen being used.
// # buffer - double buffer to push
// # screen_cs - chip select for screen to activate
// ################################################################
void Push_Buffer(unsigned short x_res, unsigned short y_res, unsigned short *buffer, unsigned short screen_cs) {
digitalWrite(screen_cs, LOW); // /CS is active LOW
tft.pushImage(0, 0, x_res, y_res, buffer);
digitalWrite(screen_cs, HIGH); // /CS is active LOW
}
// ################################################################
// ################################################################
void Init_Sprite_File(unsigned short ref_number, char *file_to_open) {
File image_file;
uint16_t total_size;
uint16_t dummy, x, y;
uint8_t pixello, pixelhi;
uint16_t pixel_built;
if (!(image_file = SD.open(file_to_open, FILE_READ))) { // Check if file exists
Serial.println(F("-> File not found")); // If not, Error message
return; // and break
}
else {
Serial.println(F("File w/in Function open"));
}
x=0; // Reset x, y coords
while (image_file.available()) { // Parse through entire image file
pixelhi = image_file.read(); // Pixel color is kept in 2 bytes
pixello = image_file.read();
person[ref_number].sprite[x] = (pixello+(pixelhi<<8));
Serial.printf("%04x\n", person[0].sprite[x]);
x++; // Next pixel
}
image_file.close(); // Close the file
Serial.printf("X value in init function %d\n", x);
}
And... the not working code trying to pass the struct to a function:
// ######################### Includes ##############################
#include <TFT_eSPI.h>
#include <SPI.h> // Library for SPI functions
#include <SD.h> // Library for SD Card
// ######################## Defines ################################
// LCD Display control signals using Hardware SPI
#define TFT_S1_CS_PIN 14 // Screen 1 Chip Select
#define TFT_S2_CS_PIN 8 // Screen 2 Chip Select
// These are defined in the TFT_eSPI User_Setup.h file
// #define TFT_MISO 13 // Master In - Slave Out
// #define TFT_MOSI 11 // Master Out - Slave In
// #define TFT_CLK 12 // Clock (40Mhz)
// #define TFT_DC 10 // Data/Command
// #define TFT_RST -1 // Reset, connected to Arduino reset pin
// #define TFT_LED -1 // Not using LED control
// SD Card control signals using Software SPI
#define SDC_CS_PIN 39 // SD Card Chip Select
#define SDC_CLK_PIN 36 // SD Card Clock
#define SDC_MOSI_PIN 35 // SD Card Master Out/Slave In
#define SDC_MISO_PIN 37 // SD Card Master In/Slave Out
#define BG_COLOR 0xF9D7 // Background Color as Transparent
#define DOUBLEBUFFER_X 160 // Double Buffer x = screen x
#define DOUBLEBUFFER_Y 128 // Double Buffer y = screen y
#define BACKGROUND_X 160 // Background x
#define BACKGROUND_Y 128 // Background y
#define NUM_BACKGROUNDS 4 // Number of background images
#define SPRITE_X 64 // Sprite x size
#define SPRITE_Y 64 // Sprite y size
#define NUM_SPRITES 12 // Nunber of sprites
#define MAX_PEOPLE 4
// ######################## Structures #############################
struct st_Person {
unsigned short sprite[4096]; // Actual Sprite graphic pre-loaded
unsigned char sector, sector_x, sector_y;
};
// ######################## Function Prototypes #####################
void Initialize_Screens(void);
void Put_Graphics(uint16_t x_loc, uint16_t y_loc, uint16_t x_size, uint16_t y_size, char *image_file);
void Init_Buffer(int res_x, int res_y, unsigned short color, unsigned short buffer2init[]);
void Plot_Pixel(unsigned int x, unsigned int y, unsigned short color);
void Push_Buffer(unsigned short x_res, unsigned short y_res, unsigned short buffer_to_show[], unsigned short screen_cs);
void Activate_Screen(unsigned short screen_cs);
void De_Activate_Screen(unsigned short screen_cs);
void Init_Sprite_File(struct st_Person st_passed[], unsigned short ref_number, char *file_to_open);
// ######################## Global Variables #########################
TFT_eSPI tft = TFT_eSPI(); // Create instance for TFT Screen(s) -> 1 instance for 2 screens w/ soft /CS
SPIClass SDsoftSPI; // Create instance for Software SPI
unsigned short double_buffer[20480]; // Behind the scenes buffer for flicker free animation 160x128 screen
byte screen_width, screen_height; // Self explanatory
// ######################## Setup ###################################
void setup() {
Serial.begin(115200); // Turn on Serial Monitor for debugging
Serial.println();
Initialize_Screens(); // Setup screen for use and soft /CS
screen_width = tft.getViewportWidth(); // Get the screen width and height
screen_height = tft.getViewportHeight();
Serial.print("Screen Resolution : "); // Verify to serial monitor the screen resolution
Serial.print(screen_width);
Serial.print("x");
Serial.println(screen_height);
Serial.print("Initalizing SCD... ");
SDsoftSPI.begin(SDC_CLK_PIN, SDC_MISO_PIN, SDC_MOSI_PIN, SDC_CS_PIN); // Initialized the secondary SPI Bus, used for slow devices (SD Card...)
if (!SD.begin(SDC_CS_PIN, SDsoftSPI)) // Initialize the SD Card and test if working
Serial.println("Initialization Failed");
else
Serial.println("done");
}
// ############################# Main ############################
void loop() {
st_Person person[MAX_PEOPLE]; // Struct that holds person information
int bg_index, sprite_index, pin;
char image_file[13]; // Holds image filename
unsigned int dummy;
unsigned int x, y;
bg_index=0;
sprite_index=0;
pin = TFT_S1_CS_PIN;
strcpy(image_file, "/SpWiYl.raw");
Init_Sprite_File(person, 0, image_file);
dummy=0;
for(y=0; y<SPRITE_Y; y++) {
for(x=0; x<SPRITE_X; x++) {
Plot_Pixel(x, y, person[0].sprite[dummy]);
dummy++;
}
}
Push_Buffer(DOUBLEBUFFER_X, DOUBLEBUFFER_Y, double_buffer, TFT_S2_CS_PIN);
while(1);
}
// ############################################# FUNCTIONS #################################################
// ################################################################
// # Function: Initialize_Screens
// # Args: None
// # Description: Initialize the 2 screens used in this program
// ################################################################
void Initialize_Screens(void) {
pinMode(TFT_S1_CS_PIN, OUTPUT); // Screen 1 chip select set to output
pinMode(TFT_S2_CS_PIN, OUTPUT); // Screen 2 chip select set to output
digitalWrite(TFT_S1_CS_PIN, HIGH); // Initial /CS setting
digitalWrite(TFT_S2_CS_PIN, HIGH); // Initial /CS setting
digitalWrite(TFT_S1_CS_PIN, LOW); // Select Screen 1
digitalWrite(TFT_S2_CS_PIN, LOW); // Select Screen 2
Serial.print("Initalizing Both LCDs... ");
tft.init(); // Initialize Screen(s)
digitalWrite(TFT_S2_CS_PIN, HIGH); // De-Select Screen 2
tft.setRotation(1); // Set rotation of screen 1
tft.fillScreen(TFT_BLACK);
digitalWrite(TFT_S1_CS_PIN, HIGH); // De-Select Screen 1
digitalWrite(TFT_S2_CS_PIN, LOW); // Select Screen 2
tft.setRotation(1);
tft.fillScreen(TFT_BLACK); // Set rotation of screen 2
digitalWrite(TFT_S2_CS_PIN, HIGH); // De-Select Screen 2
Serial.println("done");
}
// ################################################################
// # Function: Put_Graphics
// # Args: uint16_t x_loc, uint16_t y_loc, uint16_t x_size, uint16_t y_size, char *file_to_open
// # Description: Reads a file off of the SD Card and places it on
// # the double buffer at location x_loc, y_loc
// # x_size, y_size is the size of the image file
// ################################################################
void Put_Graphics(uint16_t x_loc, uint16_t y_loc, uint16_t x_size, uint16_t y_size, char *file_to_open) {
File image_file;
uint16_t total_size;
uint16_t dummy, x, y;
uint8_t pixello, pixelhi;
uint16_t pixel_built;
if (!(image_file = SD.open(file_to_open, FILE_READ))) { // Check if file exists
Serial.println(F("-> File not found")); // If not, Error message
return; // and break
}
x=0; // Reset x, y coords
y=0;
while (image_file.available()) { // Parse through entire image file
pixelhi = image_file.read(); // Pixel color is kept in 2 bytes
pixello = image_file.read();
if(!((pixello+(pixelhi<<8)) == BG_COLOR)) // If not the Background color
Plot_Pixel(x_loc+x, y_loc+y, (pixello+(pixelhi<<8))); // Put it on the screen
x++; // Next pixel
if(x>=x_size) { // If width of image is reached
x=0; // go to next line
y++;
}
}
image_file.close(); // Close the file
}
// ################################################################
// # Function: Init_Buffer
// # Args: int res_x, res_y - Dimensions of the buffer,
// # 160x128 screen
// # Description: Fills the buffer with given color
// # For some reason library is inverting color bytes
// # flip bytes before rendering to buffer
// ################################################################
void Init_Buffer(int res_x, int res_y, unsigned short color, unsigned short buffer2init[]) {
int dummy;
unsigned short shiftbyte = 0x0000; // Needed for byte shifting, clear shiftbyte
shiftbyte = ((color & 0x00FF) << 8); // Mask off upper bytes and shift lower bytes left 8
color = ((color >> 8 ) | shiftbyte); // Shift upper bytes right 8 and OR shifted lower bytes
for(dummy=0; dummy<(res_x * res_y); dummy++) {
buffer2init[dummy]=color;
}
}
// ################################################################
// # Function: Plot_Pixel
// # Args: int x, int y, int color
// # Description: x, y are coords to plot the pixel on a 160x128 screen
// # color is color of pixel using RGB 565 color scheme
// # For some reason library is inverting color bytes
// # flip bytes before rendering to buffer
// # Screen resolution is 160x128
// # double_buffer[160*y + x]=color;
// # 160*y = y*128 + y*32 === Optimized, bit shifting & adding is much faster than muliplying
// # y<<7 + y<<5
// ################################################################
void Plot_Pixel(unsigned int x, unsigned int y, unsigned short color) {
unsigned short shiftbyte = 0x0000; // Needed for byte shifting, clear shiftbyte
shiftbyte = ((color & 0x00FF) << 8); // Mask off upper bytes and shift lower bytes left 8
color = ((color >> 8 ) | shiftbyte); // Shift upper bytes right 8 and OR shifted lower bytes
double_buffer[((y<<7) + (y<<5)) + x] = color;
}
// ################################################################
// # Function: Push_Image
// # Args: unsigned short x_res, unsigned short y_res, unsigned short buffer[], unsigned short screen
// # Description: This function is a helper to tft.pushImage to accomodate more that 1 screen.
// # x_res, y_res - resolution of the screen being used.
// # buffer - double buffer to push
// # screen_cs - chip select for screen to activate
// ################################################################
void Push_Buffer(unsigned short x_res, unsigned short y_res, unsigned short *buffer, unsigned short screen_cs) {
digitalWrite(screen_cs, LOW); // /CS is active LOW
tft.pushImage(0, 0, x_res, y_res, buffer);
digitalWrite(screen_cs, HIGH); // /CS is active LOW
}
// ################################################################
// ################################################################
void Init_Sprite_File(struct st_Person st_passed[], unsigned short ref_number, char *file_to_open) {
File image_file;
uint16_t total_size;
uint16_t dummy, x, y;
uint8_t pixello, pixelhi;
uint16_t pixel_built;
if (!(image_file = SD.open(file_to_open, FILE_READ))) { // Check if file exists
Serial.println(F("-> File not found")); // If not, Error message
return; // and break
}
else {
Serial.println(F("File w/in Function open"));
}
x=0;
while (image_file.available()) { // Parse through entire image file
pixelhi = image_file.read(); // Pixel color is kept in 2 bytes
pixello = image_file.read();
st_passed[ref_number].sprite[x] = (pixello+(pixelhi<<8));
Serial.printf("%04x\n", st_passed[ref_number].sprite[x]);
x++; // Next pixel
}
image_file.close(); // Close the file
Serial.printf("X value in init function %d\n", x);
}
There are some #defines and variables that are not used, I realize that. I isolated the code from my larger sketch, to try to just get this to work w/o other problems. Thank you in advance.