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"
2021-01-09 12:14:14 +00:00
2021-01-19 20:37:54 +00:00
# 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-18 22:34:50 +00:00
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
2021-01-19 20:37:54 +00:00
# include <SPI.h>
2020-07-15 07:21:17 +00:00
2020-07-06 11:04:22 +00:00
# define PIN_RST 8
# define PIN_DC 9
2020-07-15 14:19:57 +00:00
# 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-02-02 15:02:32 +00:00
driveIndicator [ 0 ] = driveIndicator [ 1 ] = true ; // assume on so they will redraw immediately the first time
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
2021-01-19 20:37:54 +00:00
void TeensyDisplay : : drawImageOfSizeAt ( const uint8_t * img ,
2021-01-21 02:16:32 +00:00
uint16_t sizex , uint16_t sizey ,
uint16_t wherex , uint16_t wherey )
2017-02-20 18:55:16 +00:00
{
2021-01-19 20:37:54 +00:00
uint8_t r , g , b ;
2022-01-30 21:03:38 +00:00
uint8_t * p = img ;
2021-01-21 02:16:32 +00:00
for ( uint16_t y = 0 ; y < sizey ; y + + ) {
2021-01-19 20:37:54 +00:00
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 ) ;
2021-01-09 12:14:14 +00:00
}
}
2017-02-20 18:55:16 +00:00
}
2020-07-07 01:31:37 +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
2022-01-20 13:19:16 +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 15:02:32 +00:00
if ( use8875 ) {
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
2022-02-02 15:02:32 +00:00
} else {
drawString ( M_SELECTDISABLED , 1 , ILI9341_HEIGHT - ( 16 + 12 ) , overlayMessage ) ;
}
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
}
}
}
2021-01-19 20:37:54 +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 ) ;
2021-01-19 20:37:54 +00:00
}
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
}
2021-01-19 20:37:54 +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
}
2018-02-18 01:44:04 +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 ] ) ;
2021-01-19 20:37:54 +00:00
}
// "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 ) ) ] ;
2020-07-15 07:21:17 +00:00
2022-01-31 01:26:12 +00:00
tft - > cacheDoubleWideApplePixel ( x , y , color16 ) ;
2021-01-19 20:37:54 +00:00
}
2020-07-15 07:21:17 +00:00
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 ( ) ;
2020-07-15 07:21:17 +00:00
}