├── README.md ├── LICENSE └── ST7735_SDMediaPlayer.ino /README.md: -------------------------------------------------------------------------------- 1 | # ST7735 SD Media Player 2 | SD File Browser and Viewer 3 | 4 | Smooth video playback from SD on STM32 and ST7735 1.8" 128x160 using DMA and fast SPI 36Mbps interface 5 | 6 | Achieved 41 fps using fast SD card. 160x128 pixel JPEG image decoding takes 100 ms or less. 7 | 8 | YouTube videos: 9 | 10 | https://youtu.be/6Uh5Iu-erO0 11 | 12 | https://youtu.be/o3AqITHf0mo 13 | 14 | https://youtu.be/4PwaX-zusPM 15 | 16 | 17 | More ST7735 and STM32 videos: 18 | 19 | https://www.youtube.com/watch?v=o3AqITHf0mo&list=PLxb1losWErZ6y6GombzvtwRZ2l7brPv1s 20 | 21 | 22 | ## Connections (header at the top): 23 | 24 | |LCD pin|LCD pin name|STM32| 25 | |--|--|--| 26 | |#01| LED| 3.3V| 27 | |#02| SCK |PA5/SCK| 28 | |#03| SCA |PA7/MOSI| 29 | |#04| A0/DC|PA1 or any digital 30 | |#05| RESET|PA0 or any digital| 31 | |#06| CS|PA2 or any digital| 32 | |#07| GND | GND| 33 | |#08| VCC | 3.3V| 34 | 35 | |SD pin|SD pin name|STM32| 36 | |--|--|--| 37 | |#01| SD_SCK| PA5| 38 | |#02| SD_MISO |PA6| 39 | |#03| SD_MOSI |PA7| 40 | |#04| SD_CS |PA4| 41 | 42 | 43 | ## Features: 44 | - SD file browser with one button 45 | - Short click for next file/switch stat mode 46 | - Long click to show file or exit the viewer 47 | - Semi-transparent progress bar 48 | - Long file names (up to 23 characters fit on the screen) and file size displayed 49 | - RAW 160x128 video files supported @ 41fps 50 | - BMP pictures in 24-bit and 8/4-bit with palette 51 | - basic text files viewer 52 | - JPEG photos support (even high resolution) thanks to JpgDecode_STM library 53 | 54 | ## Comments: 55 | - SD uses faster STM32 SPI1 interface which supports 36 Mbps 56 | - SPI1 is shared between LCD and SD card 57 | - Not all SD cards work at 36Mbps 58 | - Fast card at 36Mbps gives 41fps for 160x128 video 59 | - SdFat library uses DMA for SPI transfer 60 | - Big buffer in RAM is used to speed up SPI/DMA transfer 61 | - Developed and tested with stm32duino and Arduino IDE 1.6.5 62 | - Requires Arduino_ST7735_STM, SdFat, JpgDecoder_STM and RREFont libraries and stm32duino 63 | 64 | If you find it useful and you want to buy me a coffee or a beer: 65 | 66 | https://www.paypal.me/cbm80amiga 67 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU LESSER GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | 9 | This version of the GNU Lesser General Public License incorporates 10 | the terms and conditions of version 3 of the GNU General Public 11 | License, supplemented by the additional permissions listed below. 12 | 13 | 0. Additional Definitions. 14 | 15 | As used herein, "this License" refers to version 3 of the GNU Lesser 16 | General Public License, and the "GNU GPL" refers to version 3 of the GNU 17 | General Public License. 18 | 19 | "The Library" refers to a covered work governed by this License, 20 | other than an Application or a Combined Work as defined below. 21 | 22 | An "Application" is any work that makes use of an interface provided 23 | by the Library, but which is not otherwise based on the Library. 24 | Defining a subclass of a class defined by the Library is deemed a mode 25 | of using an interface provided by the Library. 26 | 27 | A "Combined Work" is a work produced by combining or linking an 28 | Application with the Library. The particular version of the Library 29 | with which the Combined Work was made is also called the "Linked 30 | Version". 31 | 32 | The "Minimal Corresponding Source" for a Combined Work means the 33 | Corresponding Source for the Combined Work, excluding any source code 34 | for portions of the Combined Work that, considered in isolation, are 35 | based on the Application, and not on the Linked Version. 36 | 37 | The "Corresponding Application Code" for a Combined Work means the 38 | object code and/or source code for the Application, including any data 39 | and utility programs needed for reproducing the Combined Work from the 40 | Application, but excluding the System Libraries of the Combined Work. 41 | 42 | 1. Exception to Section 3 of the GNU GPL. 43 | 44 | You may convey a covered work under sections 3 and 4 of this License 45 | without being bound by section 3 of the GNU GPL. 46 | 47 | 2. Conveying Modified Versions. 48 | 49 | If you modify a copy of the Library, and, in your modifications, a 50 | facility refers to a function or data to be supplied by an Application 51 | that uses the facility (other than as an argument passed when the 52 | facility is invoked), then you may convey a copy of the modified 53 | version: 54 | 55 | a) under this License, provided that you make a good faith effort to 56 | ensure that, in the event an Application does not supply the 57 | function or data, the facility still operates, and performs 58 | whatever part of its purpose remains meaningful, or 59 | 60 | b) under the GNU GPL, with none of the additional permissions of 61 | this License applicable to that copy. 62 | 63 | 3. Object Code Incorporating Material from Library Header Files. 64 | 65 | The object code form of an Application may incorporate material from 66 | a header file that is part of the Library. You may convey such object 67 | code under terms of your choice, provided that, if the incorporated 68 | material is not limited to numerical parameters, data structure 69 | layouts and accessors, or small macros, inline functions and templates 70 | (ten or fewer lines in length), you do both of the following: 71 | 72 | a) Give prominent notice with each copy of the object code that the 73 | Library is used in it and that the Library and its use are 74 | covered by this License. 75 | 76 | b) Accompany the object code with a copy of the GNU GPL and this license 77 | document. 78 | 79 | 4. Combined Works. 80 | 81 | You may convey a Combined Work under terms of your choice that, 82 | taken together, effectively do not restrict modification of the 83 | portions of the Library contained in the Combined Work and reverse 84 | engineering for debugging such modifications, if you also do each of 85 | the following: 86 | 87 | a) Give prominent notice with each copy of the Combined Work that 88 | the Library is used in it and that the Library and its use are 89 | covered by this License. 90 | 91 | b) Accompany the Combined Work with a copy of the GNU GPL and this license 92 | document. 93 | 94 | c) For a Combined Work that displays copyright notices during 95 | execution, include the copyright notice for the Library among 96 | these notices, as well as a reference directing the user to the 97 | copies of the GNU GPL and this license document. 98 | 99 | d) Do one of the following: 100 | 101 | 0) Convey the Minimal Corresponding Source under the terms of this 102 | License, and the Corresponding Application Code in a form 103 | suitable for, and under terms that permit, the user to 104 | recombine or relink the Application with a modified version of 105 | the Linked Version to produce a modified Combined Work, in the 106 | manner specified by section 6 of the GNU GPL for conveying 107 | Corresponding Source. 108 | 109 | 1) Use a suitable shared library mechanism for linking with the 110 | Library. A suitable mechanism is one that (a) uses at run time 111 | a copy of the Library already present on the user's computer 112 | system, and (b) will operate properly with a modified version 113 | of the Library that is interface-compatible with the Linked 114 | Version. 115 | 116 | e) Provide Installation Information, but only if you would otherwise 117 | be required to provide such information under section 6 of the 118 | GNU GPL, and only to the extent that such information is 119 | necessary to install and execute a modified version of the 120 | Combined Work produced by recombining or relinking the 121 | Application with a modified version of the Linked Version. (If 122 | you use option 4d0, the Installation Information must accompany 123 | the Minimal Corresponding Source and Corresponding Application 124 | Code. If you use option 4d1, you must provide the Installation 125 | Information in the manner specified by section 6 of the GNU GPL 126 | for conveying Corresponding Source.) 127 | 128 | 5. Combined Libraries. 129 | 130 | You may place library facilities that are a work based on the 131 | Library side by side in a single library together with other library 132 | facilities that are not Applications and are not covered by this 133 | License, and convey such a combined library under terms of your 134 | choice, if you do both of the following: 135 | 136 | a) Accompany the combined library with a copy of the same work based 137 | on the Library, uncombined with any other library facilities, 138 | conveyed under the terms of this License. 139 | 140 | b) Give prominent notice with the combined library that part of it 141 | is a work based on the Library, and explaining where to find the 142 | accompanying uncombined form of the same work. 143 | 144 | 6. Revised Versions of the GNU Lesser General Public License. 145 | 146 | The Free Software Foundation may publish revised and/or new versions 147 | of the GNU Lesser General Public License from time to time. Such new 148 | versions will be similar in spirit to the present version, but may 149 | differ in detail to address new problems or concerns. 150 | 151 | Each version is given a distinguishing version number. If the 152 | Library as you received it specifies that a certain numbered version 153 | of the GNU Lesser General Public License "or any later version" 154 | applies to it, you have the option of following the terms and 155 | conditions either of that published version or of any later version 156 | published by the Free Software Foundation. If the Library as you 157 | received it does not specify a version number of the GNU Lesser 158 | General Public License, you may choose any version of the GNU Lesser 159 | General Public License ever published by the Free Software Foundation. 160 | 161 | If the Library as you received it specifies that a proxy can decide 162 | whether future versions of the GNU Lesser General Public License shall 163 | apply, that proxy's public statement of acceptance of any version is 164 | permanent authorization for you to choose that version for the 165 | Library. 166 | -------------------------------------------------------------------------------- /ST7735_SDMediaPlayer.ino: -------------------------------------------------------------------------------- 1 | // ST7735 library example 2 | // SD Media Player/File Viewer with File Browser 3 | // Requires Arduino_ST7735_STM, SdFat, JpgDecoder_STM, RREFont libraries and stm32duino 4 | // (C)2019-20 Pawel A. Hernik 5 | // YouTube videos: 6 | // https://youtu.be/6Uh5Iu-erO0 7 | // https://youtu.be/o3AqITHf0mo 8 | // https://youtu.be/4PwaX-zusPM 9 | 10 | /* 11 | ST7735 128x160 1.8" LCD pinout (header at the top, from left): 12 | #1 LED -> 3.3V 13 | #2 SCK -> SCL/D13/PA5 14 | #3 SDA -> MOSI/D11/PA7 15 | #4 A0/DC -> D8/PA1 or any digital 16 | #5 RESET -> D9/PA0 or any digital 17 | #6 CS -> D10/PA2 or any digital 18 | #7 GND -> GND 19 | #8 VCC -> 3.3V 20 | 21 | SPI2/SPI1 22 | SD_SCK - PB13/PA5 23 | SD_MISO - PB14/PA6 24 | SD_MOSI - PB15/PA7 25 | SD_CS - PB12/PA4 26 | */ 27 | 28 | /* 29 | STM32 SPI1/SPI2 pins: 30 | 31 | SPI1 MOSI PA7 32 | SPI1 MISO PA6 33 | SPI1 SCK PA5 34 | SPI1 CS PA4 35 | 36 | SPI2 MOSI PB15 37 | SPI2 MISO PB14 38 | SPI2 SCK PB13 39 | SPI2 CS PB12 40 | */ 41 | 42 | /* 43 | Features: 44 | - SD file browser with one button 45 | - Short click for next file/switch stat mode 46 | - Long click to show file or exit the viewer 47 | - Semi-transparent progress bar 48 | - Long file names (up to 23 characters fit on the screen) and file size displayed 49 | - Supported file types: RAW 565 video, BMP, JPEG, TXT (very simple) 50 | 51 | Comments: 52 | - SD uses faster STM32 SPI1 interface which supports 36 Mbps 53 | - SPI1 is shared between LCD and SD card 54 | - Not all SD cards work at 36MBps 55 | - Fast card at 36Mbps gives 41fps for 160x128 video 56 | - SdFat library uses DMA for SPI transfer 57 | - Big buffer in RAM is used to speed up SPI/DMA transfer 58 | - fast JPEG decoding - for 160x128 pixel images - 100 ms or less 59 | */ 60 | 61 | #include 62 | #include 63 | 64 | #if (__STM32F1__) // bluepill 65 | #define TFT_CS PA2 66 | #define TFT_DC PA1 67 | #define TFT_RST PA0 68 | #include 69 | #else 70 | #define TFT_CS 10 71 | #define TFT_DC 8 72 | #define TFT_RST 9 73 | //#include 74 | #endif 75 | 76 | #define SCR_WD 160 77 | #define SCR_HT 128 78 | Arduino_ST7735 lcd = Arduino_ST7735(TFT_DC, TFT_RST, TFT_CS); 79 | 80 | #define NLINES 32 81 | #define BUF_WD 160 82 | uint16_t buf[BUF_WD*NLINES] __attribute__((aligned(4))); 83 | char txt[30]; 84 | 85 | #include "RREFont.h" 86 | #include "rre_5x8.h" 87 | RREFont font; 88 | 89 | // ------------------------- 90 | // renders directly to LCD 91 | void customRect(int x, int y, int w, int h, int c) { lcd.fillRect(x, y, w, h, c); } 92 | // ------------------------- 93 | // renders to the buffer 94 | void customRectBuf(int x, int y, int w, int h, int c) 95 | { 96 | if(y>NLINES) return; 97 | if(y+h>NLINES) h=NLINES-y; 98 | for(int j=0;j 123 | 124 | // the callback function below renders decoded JPEG on the LCD 125 | bool renderLCD(int16_t x, int16_t y, uint16_t w, uint16_t h, uint16_t* bitmap) 126 | { 127 | lcdSPI(); 128 | if(y>=lcd.height()) return 0; // 0 - to stop decoding 129 | lcd.drawImage(x,y, w,h, bitmap); 130 | return 1; // 1 - to decode next block 131 | } 132 | 133 | // align to a 32 bit boundary for the best performance 134 | #define DECODE_BUFFER_LEN 3100 135 | //uint8_t decodeBuffer[DECODE_BUFFER_LEN] __attribute__((aligned(4))); 136 | JpgDecoder jpeg(renderLCD,(uint8_t*)buf,DECODE_BUFFER_LEN); 137 | 138 | // ----------------------------------------------- 139 | #define BUTTON PB9 140 | int buttonState; 141 | int prevState = HIGH; 142 | long btDebounce = 30; 143 | long btMultiClick = 600; 144 | long btLongClick = 500; 145 | long btLongerClick = 2000; 146 | long btTime = 0, btTime2 = 0; 147 | int clickCnt = 1; 148 | 149 | // 0=idle, 1,2,3=click, -1,-2=longclick 150 | int checkButton() 151 | { 152 | int state = digitalRead(BUTTON); 153 | if( state == LOW && prevState == HIGH ) { btTime = millis(); prevState = state; return 0; } // button just pressed 154 | if( state == HIGH && prevState == LOW ) { // button just released 155 | prevState = state; 156 | if( millis()-btTime >= btDebounce && millis()-btTime < btLongClick ) { 157 | if( millis()-btTime2= btLongerClick ) { prevState = state; return -2; } 163 | if( state == LOW && millis()-btTime >= btLongClick ) { prevState = state; return -1; } 164 | return 0; 165 | } 166 | 167 | int prevButtonState=0; 168 | 169 | int handleButton() 170 | { 171 | prevButtonState = buttonState; 172 | buttonState = checkButton(); 173 | return buttonState; 174 | } 175 | 176 | // -------------------------------------------------------------------------- 177 | void error(char *err, uint16_t col, int8 halt=1) 178 | { 179 | lcdSPI(); lcd.fillScreen(col); 180 | font.setColor(YELLOW); 181 | font.setFillRectFun(customRect); 182 | font.setBold(1); 183 | font.setSpacingY(4); 184 | font.printStr(4,4,err); 185 | font.setBold(0); 186 | if(halt) sd.errorHalt(err); else Serial.println(err); 187 | } 188 | // -------------------------------------------------------------------------- 189 | int statMode=1; // 0-0ff, 1-progress, 2-full, 3-fps 190 | 191 | void darken(uint16_t *p) 192 | { 193 | uint16_t c = *p; 194 | int r,g,b; 195 | r = (c>>8)&0xf8; 196 | g = (c>>3)&0xfc; 197 | b = (c<<3)&0xf8; 198 | //*p = RGBto565(r/2,g/2,b/2); // blend to black 199 | *p = RGBto565(r+(60-r)/2,g+(60-g)/2,b+(60-b)/2); // blend to dark grey 200 | } 201 | 202 | void drawProgress(int y, int p) 203 | { 204 | int i; 205 | for(i=0; i=8 && statMode>0) { 244 | if(statMode==1) drawProgress(NLINES-8,140*(file.curPosition()/1000)/(file.fileSize()/1000)); else 245 | if(statMode>1) { 246 | font.setFillRectFun(customRectBuf); 247 | font.setColor(BLACK); 248 | font.printStr(1,NLINES-6,txt); 249 | font.printStr(2,NLINES-6,txt); 250 | font.printStr(3,NLINES-6,txt); 251 | font.printStr(1,NLINES-8,txt); 252 | font.printStr(2,NLINES-8,txt); 253 | font.printStr(3,NLINES-8,txt); 254 | font.printStr(1,NLINES-7,txt); 255 | font.printStr(3,NLINES-7,txt); 256 | font.setColor(YELLOW); 257 | font.printStr(2,NLINES-7,txt); 258 | } 259 | } 260 | statTime += millis()-statStartTime; 261 | 262 | lcdSPI(); 263 | lcd.drawImage(0,i*nl,lcd.width(),nl,buf); 264 | } 265 | frTime = millis()-frTime-statTime; 266 | lcdTime = frTime-sdTime; 267 | if(buttonState>0) { 268 | if(++statMode>3) statMode=0; 269 | } 270 | if(statMode==2) snprintf(txt,30,"Fr/SD/LCD: %2ld/%2ld/%2ld FPS:%2d",frTime,sdTime,lcdTime,1000/frTime); 271 | if(statMode==3) snprintf(txt,30,"%2ld fps",1000/frTime); 272 | 273 | if(skipFr>0) file.seekCur(wd*ht*2*skipFr); 274 | if(handleButton()<0 && prevButtonState==0) break; 275 | } 276 | file.close(); 277 | while(handleButton()==0); 278 | return 1; 279 | } 280 | // -------------------------------------------------------------------------- 281 | // Limited to LCD resolution 282 | int showBMP(char *filename) 283 | { 284 | int bmpWd, bmpHt, bmpBits, bmpNumCols, y=0; 285 | uint16_t pal[256]; 286 | 287 | sdSPI(); 288 | if(!file.open(filename, O_CREAT | O_RDONLY)) { 289 | lcdSPI(); lcd.fillScreen(YELLOW); 290 | Serial.print(F("Cannot open ")); 291 | Serial.println(filename); 292 | delay(1000); 293 | //return -1; 294 | } 295 | file.seekSet(0); 296 | file.read(buf,54); 297 | uint8_t *buf8 = (uint8_t *)buf; 298 | bmpWd = buf8[18]+buf8[19]*256; 299 | bmpHt = buf8[22]+buf8[23]*256; 300 | bmpBits = buf8[28]; 301 | bmpNumCols = buf8[46]+buf8[47]*256; 302 | //Serial.print(bmpWd); Serial.print(" x "); Serial.print(bmpHt); Serial.print(" x "); Serial.print(bmpBits); Serial.print(" bpp"); 303 | //if(bmpBits<=8) { Serial.print(" / "); Serial.print(bmpNumCols); Serial.print(" colors"); } 304 | //Serial.println(); 305 | if(bmpBits<=8) { 306 | file.read(buf,bmpNumCols*4); 307 | for(int i=0;i>4]; 315 | buf[i*2+1] = pal[buf8[i]&0xf]; 316 | } 317 | } else 318 | if(bmpBits==8) { 319 | file.read(buf8,bmpWd); 320 | for(int i=0;i=15) sc=3; else // 15/2 339 | if(2*jht/lcd.height()>=7) sc=2; else // 7/2 340 | if(2*jht/lcd.height()>=3) sc=1; // 3/2 341 | int x=(lcd.width()-(jwd>>sc))/2; 342 | int y=(lcd.height()-(jht>>sc))/2; 343 | if(x<0) x=0; 344 | if(y<0) y=0; 345 | snprintf(txt,50,"%4d x %4d sc=%d [%s]",jwd,jht,sc,filename); 346 | Serial.println(txt); 347 | jpeg.show(x,y,filename,sc); 348 | while(handleButton()==0 || prevButtonState!=0); 349 | return 1; 350 | } 351 | // -------------------------------------------------------------------------- 352 | int showTxt(char *filename) 353 | { 354 | sdSPI(); 355 | if(!file.open(filename, O_CREAT | O_RDONLY)) { 356 | lcdSPI(); lcd.fillScreen(YELLOW); 357 | Serial.print(F("Cannot open ")); 358 | Serial.println(filename); 359 | delay(1000); 360 | //return -1; 361 | } 362 | file.seekSet(0); 363 | while(file.available()) { 364 | int rd = file.read(buf,NLINES*BUF_WD*2); 365 | char *txt = (char*)buf; 366 | txt[rd-1]=0; 367 | lcdSPI(); 368 | lcd.fillScreen(RGBto565(0,0,100)); 369 | font.setColor(YELLOW); 370 | font.setSpacingY(1); 371 | font.setCR(1); 372 | font.printStr(0,0,txt); 373 | } 374 | file.close(); 375 | while(handleButton()==0 || prevButtonState!=0); 376 | return 1; 377 | } 378 | 379 | // -------------------------------------------------------------------------- 380 | char *getExt(char *filename) 381 | { 382 | int len = strlen(filename); 383 | return len>3 ? filename+len-3 : filename; 384 | } 385 | 386 | int checkExt(char *filename, char *ext) 387 | { 388 | return strcmp(getExt(filename),ext)==0; 389 | } 390 | // -------------------------------------------------------------------------- 391 | // SD file browser data 392 | #define MAX_NAME_LEN 23 393 | #define MAX_DIR_LEN 50 394 | #define MAX_SIZE_LEN 5 395 | const int charWd = 6; 396 | const int lineHt = 12; 397 | const int numScreenFilesMax = 9; 398 | int numScreenFiles = 0; 399 | char filesList[numScreenFilesMax][MAX_NAME_LEN+1]; // last char filesList[MAX_NAME_LEN] is used as file/dir mode 400 | char filesSize[numScreenFilesMax][MAX_SIZE_LEN]; 401 | int selFile = 0; 402 | int fileAvailable = 1; 403 | uint32_t dirPos; 404 | bool rootDir = true; 405 | char curDir[MAX_DIR_LEN]; 406 | int ys = 14, xs = 2; 407 | 408 | void fileList(int rewind=0) 409 | { 410 | numScreenFiles = 0; 411 | selFile = 0; 412 | sdSPI(); 413 | if(rewind || !fileAvailable) { 414 | dirPos = 0; 415 | if(!rootDir) { 416 | strcpy(filesList[numScreenFiles],".."); 417 | filesList[numScreenFiles][MAX_NAME_LEN] = 2; 418 | numScreenFiles++; 419 | } 420 | } 421 | sd.vwd()->seekSet(dirPos); 422 | while(numScreenFiles1000*1000*1000) snprintf(filesSize[numScreenFiles],MAX_SIZE_LEN,"%dG",fsize>>30); 439 | else if(fsize>1000*1000) snprintf(filesSize[numScreenFiles],MAX_SIZE_LEN,"%dM",fsize>>20); 440 | else if(fsize>1999) snprintf(filesSize[numScreenFiles],MAX_SIZE_LEN,"%dK",fsize>>10); 441 | else snprintf(filesSize[numScreenFiles],MAX_SIZE_LEN,"%dB",fsize); 442 | filesSize[numScreenFiles][MAX_SIZE_LEN-1] = 0; 443 | //Serial.print("\t\t"); Serial.println(filesSize[numScreenFiles]); 444 | } 445 | numScreenFiles++; 446 | file.close(); 447 | } 448 | dirPos = sd.vwd()->curPosition(); 449 | if(numScreenFiles>0) fileListShow(); 450 | } 451 | 452 | // -------------------------------------------------------------------------- 453 | uint16_t bgCol(int i) 454 | { 455 | return i&1 ? RGBto565(30,30,30) : RGBto565(50,50,50); 456 | } 457 | 458 | void selFrame(int i, uint16_t c) 459 | { 460 | lcdSPI(); 461 | lcd.drawRect(0,ys+i*lineHt,(MAX_NAME_LEN-1)*charWd+4,lineHt,c); 462 | } 463 | 464 | void selFrameActive() 465 | { 466 | selFrame(selFile,filesList[selFile][MAX_NAME_LEN] ? GREEN : RED); 467 | } 468 | 469 | void fileListShow() 470 | { 471 | lcdSPI(); lcd.fillScreen(BLACK); 472 | font.setFillRectFun(customRect); 473 | lcd.drawFastHLine(0,0,lcd.width(),RGBto565(0,0,180)); 474 | lcd.drawFastHLine(0,ys-1,lcd.width(),RGBto565(0,0,180)); 475 | lcd.drawFastHLine(0,1,lcd.width(),RGBto565(0,0,200)); 476 | lcd.drawFastHLine(0,ys-2,lcd.width(),RGBto565(0,0,200)); 477 | lcd.drawFastHLine(0,2,lcd.width(),RGBto565(0,0,220)); 478 | lcd.drawFastHLine(0,ys-3,lcd.width(),RGBto565(0,0,220)); 479 | lcd.fillRect(0,3,lcd.width(),ys-6,BLUE); 480 | font.setColor(YELLOW); 481 | font.printStr(xs,3,curDir); 482 | for(int i=0;i0) { 540 | selFrame(selFile,bgCol(selFile)); 541 | if(++selFile>=numScreenFiles) { 542 | fileList(); 543 | if(numScreenFiles==0) fileList(1); 544 | } 545 | selFrameActive(); 546 | } 547 | if(buttonState<0 && prevButtonState==0) { 548 | if(filesList[selFile][MAX_NAME_LEN]==2) { 549 | if(filesList[selFile][0]=='.') { 550 | char *last = strrchr(curDir,'/'); 551 | if(last && last!=curDir) *last=0; 552 | last = strrchr(curDir,'/'); 553 | if(last==curDir) rootDir = true; 554 | } else { 555 | rootDir = false; 556 | if(strlen(curDir)+strlen(filesList[selFile])+2