141 lines
4.9 KiB
C++
141 lines
4.9 KiB
C++
// An adaption of the "UncannyEyes" sketch (see eye_functions tab)
|
|
// for the TFT_eSPI library. As written the sketch is for driving
|
|
// one (240x320 minimum) TFT display, showing 2 eyes. See example
|
|
// Animated_Eyes_2 for a dual 128x128 TFT display configued sketch.
|
|
|
|
// The number of displays and chip selects used are defined in the
|
|
// config.h tab. The display count can be set to 1. If using one
|
|
// TFT and the chip select for that display is already defined in
|
|
// the TFT_eSPI library then change the chip select pins to -1 in the
|
|
// "config.h" tab.
|
|
|
|
// The wiring for 2 TFT displays to an ESP32 is described in the
|
|
// "wiring" tab of this sketch.
|
|
|
|
// Configuration settings for the eye, eye style, display count,
|
|
// chip selects and x offsets can be defined in the sketch "config.h" tab.
|
|
|
|
// Performance (frames per second = fps) can be improved by using
|
|
// DMA (for SPI displays only) on ESP32 and STM32 processors. Use
|
|
// as high a SPI clock rate as is supported by the display. 27MHz
|
|
// minimum, some diplays can be operated at higher clock rates in
|
|
// the range 40-80MHz.
|
|
|
|
// Single defaultEye performance for different processors
|
|
// No DMA With DMA
|
|
// ESP8266 (160MHz CPU) 40MHz SPI 36 fps
|
|
// ESP32 27MHz SPI 53 fps 85 fps
|
|
// ESP32 40MHz SPI 67 fps 102 fps
|
|
// ESP32 80MHz SPI 82 fps 116 fps // Note: Few displays work reliably at 80MHz
|
|
// STM32F401 55MHz SPI 44 fps 90 fps
|
|
// STM32F446 55MHz SPI 83 fps 155 fps
|
|
// STM32F767 55MHz SPI 136 fps 197 fps
|
|
|
|
// DMA can be used with STM32 and ESP32 processors when the interface
|
|
// is SPI, uncomment the next line:
|
|
#define USE_DMA
|
|
|
|
// Load TFT driver library
|
|
#include <SPI.h>
|
|
#include <TFT_eSPI.h>
|
|
TFT_eSPI tft; // A single instance is used for 1 or 2 displays
|
|
|
|
// A pixel buffer is used during eye rendering
|
|
#define BUFFER_SIZE 1024 // 128 to 1024 seems optimum
|
|
|
|
#ifdef USE_DMA
|
|
#define BUFFERS 2 // 2 toggle buffers with DMA
|
|
#else
|
|
#define BUFFERS 1 // 1 buffer for no DMA
|
|
#endif
|
|
|
|
uint16_t pbuffer[BUFFERS][BUFFER_SIZE]; // Pixel rendering buffer
|
|
bool dmaBuf = 0; // DMA buffer selection
|
|
|
|
// This struct is populated in config.h
|
|
typedef struct { // Struct is defined before including config.h --
|
|
int8_t select; // pin numbers for each eye's screen select line
|
|
int8_t wink; // and wink button (or -1 if none) specified there,
|
|
uint8_t rotation; // also display rotation and the x offset
|
|
int16_t xposition; // position of eye on the screen
|
|
} eyeInfo_t;
|
|
|
|
#include "config.h" // ****** CONFIGURATION IS DONE IN HERE ******
|
|
|
|
extern void user_setup(void); // Functions in the user*.cpp files
|
|
extern void user_loop(void);
|
|
|
|
#define SCREEN_X_START 0
|
|
#define SCREEN_X_END SCREEN_WIDTH // Badly named, actually the "eye" width!
|
|
#define SCREEN_Y_START 0
|
|
#define SCREEN_Y_END SCREEN_HEIGHT // Actually "eye" height
|
|
|
|
// A simple state machine is used to control eye blinks/winks:
|
|
#define NOBLINK 0 // Not currently engaged in a blink
|
|
#define ENBLINK 1 // Eyelid is currently closing
|
|
#define DEBLINK 2 // Eyelid is currently opening
|
|
typedef struct {
|
|
uint8_t state; // NOBLINK/ENBLINK/DEBLINK
|
|
uint32_t duration; // Duration of blink state (micros)
|
|
uint32_t startTime; // Time (micros) of last state change
|
|
} eyeBlink;
|
|
|
|
struct { // One-per-eye structure
|
|
int16_t tft_cs; // Chip select pin for each display
|
|
eyeBlink blink; // Current blink/wink state
|
|
int16_t xposition; // x position of eye image
|
|
} eye[NUM_EYES];
|
|
|
|
uint32_t startTime; // For FPS indicator
|
|
|
|
// INITIALIZATION -- runs once at startup ----------------------------------
|
|
void setup(void) {
|
|
Serial.begin(115200);
|
|
//while (!Serial);
|
|
Serial.println("Starting");
|
|
|
|
#if defined(DISPLAY_BACKLIGHT) && (DISPLAY_BACKLIGHT >= 0)
|
|
// Enable backlight pin, initially off
|
|
Serial.println("Backlight turned off");
|
|
pinMode(DISPLAY_BACKLIGHT, OUTPUT);
|
|
digitalWrite(DISPLAY_BACKLIGHT, LOW);
|
|
#endif
|
|
|
|
// User call for additional features
|
|
user_setup();
|
|
|
|
// Initialiase the eye(s), this will set all chip selects low for the tft.init()
|
|
initEyes();
|
|
|
|
// Initialise TFT
|
|
Serial.println("Initialising displays");
|
|
tft.init();
|
|
|
|
#ifdef USE_DMA
|
|
tft.initDMA();
|
|
#endif
|
|
|
|
// Raise chip select(s) so that displays can be individually configured
|
|
digitalWrite(eye[0].tft_cs, HIGH);
|
|
if (NUM_EYES > 1) digitalWrite(eye[1].tft_cs, HIGH);
|
|
|
|
for (uint8_t e = 0; e < NUM_EYES; e++) {
|
|
digitalWrite(eye[e].tft_cs, LOW);
|
|
tft.setRotation(eyeInfo[e].rotation);
|
|
tft.fillScreen(TFT_BLACK);
|
|
digitalWrite(eye[e].tft_cs, HIGH);
|
|
}
|
|
|
|
#if defined(DISPLAY_BACKLIGHT) && (DISPLAY_BACKLIGHT >= 0)
|
|
Serial.println("Backlight now on!");
|
|
analogWrite(DISPLAY_BACKLIGHT, BACKLIGHT_MAX);
|
|
#endif
|
|
|
|
startTime = millis(); // For frame-rate calculation
|
|
}
|
|
|
|
// MAIN LOOP -- runs continuously after setup() ----------------------------
|
|
void loop() {
|
|
updateEye();
|
|
}
|