aiie/teensy/teensy-display.cpp

263 lines
8.6 KiB
C++
Raw Normal View History

2017-02-20 18:55:16 +00:00
#include <ctype.h> // isgraph
2020-07-06 20:46:14 +00:00
2017-02-20 18:55:16 +00:00
#include "teensy-display.h"
2021-01-19 23:40:13 +00:00
#include "iocompat.h"
2017-02-20 18:55:16 +00:00
2018-01-07 19:43:17 +00:00
#include "appleui.h"
#include "globals.h"
#include "applevm.h"
2017-02-20 18:55:16 +00:00
2022-01-26 18:49:28 +00:00
#include "RA8875_t4.h"
#include "ILI9341_wrap.h"
2022-01-26 18:49:28 +00:00
#include "images.h"
2022-01-18 12:42:57 +00:00
2022-01-31 01:26:12 +00:00
uint8_t *dmaBuffer = NULL;
uint16_t *dmaBuffer16 = NULL;
2022-01-19 23:40:10 +00:00
#include <SPI.h>
2020-07-06 11:04:22 +00:00
#define PIN_RST 8
#define PIN_DC 9
#define PIN_CS 0
#define PIN_MOSI 26
#define PIN_MISO 1
#define PIN_SCK 27
2017-02-20 18:55:16 +00:00
2022-02-02 12:17:05 +00:00
const uint16_t loresPixelColors[16] = { 0x0000, // 0 black
0xC006, // 1 magenta
0x0010, // 2 dark blue
0xA1B5, // 3 purple
0x0480, // 4 dark green
0x6B4D, // 5 dark grey
0x1B9F, // 6 med blue
0x0DFD, // 7 light blue
0x92A5, // 8 brown
0xF8C5, // 9 orange
0x9555, // 10 light gray
0xFCF2, // 11 pink
0x07E0, // 12 green
0xFFE0, // 13 yellow
0x87F0, // 14 aqua
0xFFFF // 15 white
};
2017-02-20 18:55:16 +00:00
TeensyDisplay::TeensyDisplay()
{
2022-01-21 20:37:06 +00:00
driveIndicator[0] = driveIndicator[1] = false;
2022-01-26 18:49:28 +00:00
shellImage = NULL;
d1OpenImage = d1ClosedImage = d2OpenImage = d2ClosedImage = NULL;
appleImage = NULL;
// FIXME abstract pin number, don't hard code it
2022-02-02 13:14:58 +00:00
pinMode(11, INPUT_PULLUP);
delay(10); // let it rise before reading it
2022-02-02 12:17:38 +00:00
if (digitalRead(11)) {
2022-01-31 01:26:12 +00:00
// Default: use older, smaller but faster, ILI display if pin 11 is not connected to ground
2022-01-30 20:30:11 +00:00
Serial.println(" using ILI9341 display");
2022-02-02 13:14:58 +00:00
use8875 = false;
2022-01-31 01:26:12 +00:00
2022-02-02 12:17:05 +00:00
dmaBuffer16 = (uint16_t *)malloc((320*240)*2+32); // malloc() happens in the DMAMEM area (RAM2)
// And we have to be sure dmaBuffer16 is 32-byte aligned for DMA purposes
// so we intentionally alloc'd an extra 32 bytes in order to shift here
dmaBuffer16 = (uint16_t *)(((uintptr_t)dmaBuffer16 + 32) &
~((uintptr_t)(31)));
2022-01-31 01:26:12 +00:00
2022-01-26 18:49:28 +00:00
tft = new ILI9341_Wrap(PIN_CS, PIN_RST, PIN_MOSI, PIN_SCK, PIN_MISO, PIN_DC);
// Load the 9341 images
getImageInfoAndData(IMG_9341_SHELL, &shellWidth, &shellHeight, &shellImage);
getImageInfoAndData(IMG_9341_D1OPEN, &driveWidth, &driveHeight, &d1OpenImage);
getImageInfoAndData(IMG_9341_D1CLOSED, &driveWidth, &driveHeight, &d1ClosedImage);
getImageInfoAndData(IMG_9341_D2OPEN, &driveWidth, &driveHeight, &d2OpenImage);
getImageInfoAndData(IMG_9341_D2CLOSED, &driveWidth, &driveHeight, &d2ClosedImage);
getImageInfoAndData(IMG_9341_APPLEBATTERY, &appleImageWidth, &appleImageHeight, &appleImage);
2022-01-30 20:30:11 +00:00
tft->begin(50000000u);
2022-01-31 01:26:12 +00:00
tft->setFrameBuffer((uint8_t *)dmaBuffer16);
2022-01-26 18:49:28 +00:00
} else {
// If someone grounded pin 11, then use the new RA8875 display
2022-01-30 20:30:11 +00:00
Serial.println(" using RA8875 display");
2022-02-02 13:14:58 +00:00
use8875 = true;
2022-01-31 01:26:12 +00:00
2022-02-02 12:17:05 +00:00
dmaBuffer = (uint8_t *)malloc(800*480+32); // malloc() happens in the DMAMEM area (RAM2)
// And we have to be sure dmaBuffer is 32-byte aligned for DMA purposes
// so we intentionally alloc'd an extra 32 bytes in order to shift here
dmaBuffer = (uint8_t *)(((uintptr_t)dmaBuffer + 32) &
~((uintptr_t)(31)));
2022-01-30 20:30:11 +00:00
tft = new RA8875_t4(PIN_CS, PIN_RST, PIN_MOSI, PIN_SCK, PIN_MISO);
2022-01-26 18:49:28 +00:00
// Load the 8875 images
getImageInfoAndData(IMG_8875_SHELL, &shellWidth, &shellHeight, &shellImage);
getImageInfoAndData(IMG_8875_D1OPEN, &driveWidth, &driveHeight, &d1OpenImage);
getImageInfoAndData(IMG_8875_D1CLOSED, &driveWidth, &driveHeight, &d1ClosedImage);
getImageInfoAndData(IMG_8875_D2OPEN, &driveWidth, &driveHeight, &d2OpenImage);
getImageInfoAndData(IMG_8875_D2CLOSED, &driveWidth, &driveHeight, &d2ClosedImage);
getImageInfoAndData(IMG_8875_APPLEBATTERY, &appleImageWidth, &appleImageHeight, &appleImage);
2022-01-30 20:30:11 +00:00
// 30MHz: solid performance, 9 FPS
// 57.5MHz: solid performance, 14/15 FPS
// 60MHz: unexpected palatte shifts & (may be audio overruns, needs checking since bumping up buffer sizes) 17 FPS
// And I can't get the SPI bus working at 80MHz or higher. Not sure why yet...
tft->begin(57500000);
2022-01-31 01:26:12 +00:00
tft->setFrameBuffer((uint8_t *)dmaBuffer);
2022-01-26 18:49:28 +00:00
}
2022-01-30 20:30:11 +00:00
2022-01-26 18:49:28 +00:00
tft->fillWindow();
2017-02-20 18:55:16 +00:00
}
TeensyDisplay::~TeensyDisplay()
{
2022-02-02 12:17:05 +00:00
/* FIXME: we mucked with these after alloc to align them, so we can't free them from their offset addresses; need to keep track of the original malloc'd address instead
if (dmaBuffer)
free(dmaBuffer);
if (dmaBuffer16)
free(dmaBuffer16);
*/
2017-02-20 18:55:16 +00:00
}
2022-01-26 18:49:28 +00:00
// Take one of the abstracted image constants, figure out which one it
// is based on the current display, and display it via
// drawImageOfSizeAt.
void TeensyDisplay::drawUIImage(uint8_t imageIdx)
{
switch (imageIdx) {
case IMG_SHELL:
drawImageOfSizeAt(shellImage, shellWidth, shellHeight, 0, 0);
break;
case IMG_D1OPEN:
2022-02-02 13:14:58 +00:00
drawImageOfSizeAt(d1OpenImage, driveWidth, driveHeight,
use8875 ? 4 : 55,
use8875 ? 67 : 216);
2022-01-26 18:49:28 +00:00
break;
case IMG_D1CLOSED:
2022-02-02 13:14:58 +00:00
drawImageOfSizeAt(d1ClosedImage, driveWidth, driveHeight,
use8875 ? 4 : 55,
use8875 ? 67 : 216);
2022-01-26 18:49:28 +00:00
break;
case IMG_D2OPEN:
2022-02-02 13:14:58 +00:00
drawImageOfSizeAt(d2OpenImage, driveWidth, driveHeight,
use8875 ? 4 : 189,
use8875 ? 116 : 216);
2022-01-26 18:49:28 +00:00
break;
case IMG_D2CLOSED:
2022-02-02 13:14:58 +00:00
drawImageOfSizeAt(d2ClosedImage, driveWidth, driveHeight,
use8875 ? 4 : 189,
use8875 ? 116 : 216);
2022-01-26 18:49:28 +00:00
break;
case IMG_APPLEBATTERY:
// FIXME ***
break;
}
}
2022-02-02 13:14:58 +00:00
void TeensyDisplay::drawDriveActivity(bool drive0, bool drive1)
{
2022-02-02 13:57:06 +00:00
// FIXME this could be much more efficient; it's doing a lot of checking use8875 in the middle of a loop
2022-02-02 13:14:58 +00:00
if (drive0 != driveIndicator[0]) {
2022-02-02 13:57:06 +00:00
for (int y=0; y<(use8875 ? LED_HEIGHT_8875 : LED_HEIGHT_9341); y++) {
for (int x=0; x<(use8875 ? LED_WIDTH_8875 : LED_WIDTH_9341); x++) {
drawPixel(x+(use8875 ? LED1_X_8875 : LED1_X_9341), y+(use8875 ? LED1_Y_8875 : LED1_Y_9341), drive0 ? 0xFA00 : 0x0000);
2022-02-02 13:14:58 +00:00
}
}
driveIndicator[0] = drive0;
}
if (drive1 != driveIndicator[1]) {
2022-02-02 13:57:06 +00:00
for (int y=0; y<(use8875 ? LED_HEIGHT_8875 : LED_HEIGHT_9341); y++) {
for (int x=0; x<(use8875 ? LED_WIDTH_8875 : LED_WIDTH_9341); x++) {
drawPixel(x+(use8875 ? LED2_X_8875 : LED2_X_9341), y+(use8875 ? LED2_Y_8875 : LED2_Y_9341), drive0 ? 0xFA00 : 0x0000);
2022-02-02 13:14:58 +00:00
}
}
2022-02-02 13:57:06 +00:00
2022-02-02 13:14:58 +00:00
driveIndicator[1] = drive1;
}
}
2022-01-26 18:49:28 +00:00
// *** this probably needs to be private now FIXME
void TeensyDisplay::drawImageOfSizeAt(const uint8_t *img,
uint16_t sizex, uint16_t sizey,
uint16_t wherex, uint16_t wherey)
2017-02-20 18:55:16 +00:00
{
uint8_t r, g, b;
2022-01-30 21:03:38 +00:00
uint8_t *p = img;
for (uint16_t y=0; y<sizey; y++) {
for (uint16_t x=0; x<sizex; x++) {
2022-01-30 21:03:38 +00:00
uint16_t v = pgm_read_byte(p++);
v <<= 8;
v |= pgm_read_byte(p++);
2022-01-31 01:26:12 +00:00
tft->drawPixel(x+wherex, y+wherey, v);
}
}
2017-02-20 18:55:16 +00:00
}
void TeensyDisplay::blit()
2020-07-06 20:46:14 +00:00
{
2022-01-31 01:26:12 +00:00
if (!tft)
return;
2022-01-19 23:40:10 +00:00
// Start updates, if they're not running already
2022-01-26 18:49:28 +00:00
if (!tft->asyncUpdateActive())
tft->updateScreenAsync(true);
2022-01-21 20:37:06 +00:00
static uint32_t ctr = 0;
2022-01-19 23:40:10 +00:00
2020-07-06 20:46:14 +00:00
// draw overlay, if any, occasionally
{
static uint32_t nextMessageTime = 0;
if (millis() >= nextMessageTime) {
if (overlayMessage[0]) {
2022-02-02 13:14:58 +00:00
/// FIXME This position needs updating for each display differently
// drawString(M_SELECTDISABLED, 1, (RA8875_HEIGHT - 18)/2, overlayMessage); // FIXME this /2 is clunky b/c drawString winds up doubling
2017-02-20 18:55:16 +00:00
}
2022-01-21 20:37:06 +00:00
nextMessageTime = millis() + 1000;
2017-02-20 18:55:16 +00:00
}
}
}
void TeensyDisplay::drawPixel(uint16_t x, uint16_t y, uint16_t color)
{
2022-01-31 01:26:12 +00:00
tft->drawPixel(x,y,color);
}
void TeensyDisplay::drawPixel(uint16_t x, uint16_t y, uint8_t r, uint8_t g, uint8_t b)
{
uint16_t color16 = ((r & 0xF8) << 8) | ((g & 0xFC) << 3) | ((b & 0xF8) >> 3);
2022-01-31 01:26:12 +00:00
tft->drawPixel(x,y,color16);
2017-02-20 18:55:16 +00:00
}
void TeensyDisplay::clrScr(uint8_t coloridx)
2017-02-20 18:55:16 +00:00
{
2022-01-31 01:26:12 +00:00
uint16_t color16 = loresPixelColors[coloridx];
tft->fillWindow(color16);
2017-02-20 18:55:16 +00:00
}
void TeensyDisplay::cachePixel(uint16_t x, uint16_t y, uint8_t color)
{
2022-01-31 01:26:12 +00:00
tft->cacheApplePixel(x,y,loresPixelColors[color]);
}
// "DoubleWide" means "please double the X because I'm in low-res
// width mode".
void TeensyDisplay::cacheDoubleWidePixel(uint16_t x, uint16_t y, uint8_t color)
{
uint16_t color16;
color16 = loresPixelColors[(( color & 0x0F ) )];
2022-01-31 01:26:12 +00:00
tft->cacheDoubleWideApplePixel(x, y, color16);
}
uint32_t TeensyDisplay::frameCount()
{
2022-01-31 01:26:12 +00:00
if (!tft)
return 0;
2022-01-26 18:49:28 +00:00
return tft->frameCount();
}