You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

261 lines
9.5 KiB

3 years ago
  1. // MCUFRIEND UNO shields have microSD on pins 10, 11, 12, 13
  2. // The official <SD.h> library only works on the hardware SPI pins
  3. // e.g. 11, 12, 13 on a Uno
  4. // e.g. 50, 51, 52 on a Mega2560
  5. // e.g. 74, 75, 76 on a Due
  6. //
  7. // if you are not using a UNO, you must use Software SPI:
  8. //
  9. // install v1.0.1 of the <SdFat.h> library with the Arduino Library Manager.
  10. // edit the src/SdFatConfig.h file to #define ENABLE_SOFTWARE_SPI_CLASS 1
  11. //
  12. // copy all your BMP files to the root directory on the microSD with your PC
  13. // (or another directory)
  14. #include <SPI.h> // f.k. for Arduino-1.5.2
  15. #define USE_SDFAT
  16. #include <SdFat.h> // Use the SdFat library
  17. SdFatSoftSpi<12, 11, 13> SD; //Bit-Bang on the Shield pins
  18. #include <Adafruit_GFX.h> // Hardware-specific library
  19. #include <MCUFRIEND_kbv.h>
  20. MCUFRIEND_kbv tft;
  21. #define SD_CS 10
  22. #define NAMEMATCH "" // "" matches any name
  23. //#define NAMEMATCH "tiger" // *tiger*.bmp
  24. #define PALETTEDEPTH 8 // support 256-colour Palette
  25. char namebuf[32] = "/"; //BMP files in root directory
  26. //char namebuf[32] = "/bitmaps/"; //BMP directory e.g. files in /bitmaps/*.bmp
  27. File root;
  28. int pathlen;
  29. void setup()
  30. {
  31. uint16_t ID;
  32. Serial.begin(9600);
  33. Serial.print("Show BMP files on TFT with ID:0x");
  34. ID = tft.readID();
  35. Serial.println(ID, HEX);
  36. if (ID == 0x0D3D3) ID = 0x9481;
  37. tft.begin(ID);
  38. tft.fillScreen(0x001F);
  39. tft.setTextColor(0xFFFF, 0x0000);
  40. bool good = SD.begin(SD_CS);
  41. if (!good) {
  42. Serial.print(F("cannot start SD"));
  43. while (1);
  44. }
  45. root = SD.open(namebuf);
  46. pathlen = strlen(namebuf);
  47. }
  48. void loop()
  49. {
  50. tft.setRotation(1); //Added in to use horizontal rotation
  51. char *nm = namebuf + pathlen;
  52. File f = root.openNextFile();
  53. uint8_t ret;
  54. uint32_t start;
  55. if (f != NULL) {
  56. #ifdef USE_SDFAT
  57. f.getName(nm, 32 - pathlen);
  58. #else
  59. strcpy(nm, (char *)f.name());
  60. #endif
  61. f.close();
  62. strlwr(nm);
  63. if (strstr(nm, ".bmp") != NULL && strstr(nm, NAMEMATCH) != NULL) {
  64. Serial.print(namebuf);
  65. Serial.print(F(" - "));
  66. tft.fillScreen(0);
  67. start = millis();
  68. ret = showBMP(namebuf, 5, 5);
  69. switch (ret) {
  70. case 0:
  71. Serial.print(millis() - start);
  72. Serial.println(F("ms"));
  73. delay(5000);
  74. break;
  75. case 1:
  76. Serial.println(F("bad position"));
  77. break;
  78. case 2:
  79. Serial.println(F("bad BMP ID"));
  80. break;
  81. case 3:
  82. Serial.println(F("wrong number of planes"));
  83. break;
  84. case 4:
  85. Serial.println(F("unsupported BMP format"));
  86. break;
  87. case 5:
  88. Serial.println(F("unsupported palette"));
  89. break;
  90. default:
  91. Serial.println(F("unknown"));
  92. break;
  93. }
  94. }
  95. }
  96. else root.rewindDirectory();
  97. }
  98. #define BMPIMAGEOFFSET 54
  99. #define BUFFPIXEL 20
  100. uint16_t read16(File& f) {
  101. uint16_t result; // read little-endian
  102. f.read(&result, sizeof(result));
  103. return result;
  104. }
  105. uint32_t read32(File& f) {
  106. uint32_t result;
  107. f.read(&result, sizeof(result));
  108. return result;
  109. }
  110. uint8_t showBMP(char *nm, int x, int y)
  111. {
  112. File bmpFile;
  113. int bmpWidth, bmpHeight; // W+H in pixels
  114. uint8_t bmpDepth; // Bit depth (currently must be 24, 16, 8, 4, 1)
  115. uint32_t bmpImageoffset; // Start of image data in file
  116. uint32_t rowSize; // Not always = bmpWidth; may have padding
  117. uint8_t sdbuffer[3 * BUFFPIXEL]; // pixel in buffer (R+G+B per pixel)
  118. uint16_t lcdbuffer[(1 << PALETTEDEPTH) + BUFFPIXEL], *palette = NULL;
  119. uint8_t bitmask, bitshift;
  120. boolean flip = true; // BMP is stored bottom-to-top
  121. int w, h, row, col, lcdbufsiz = (1 << PALETTEDEPTH) + BUFFPIXEL, buffidx;
  122. uint32_t pos; // seek position
  123. boolean is565 = false; //
  124. uint16_t bmpID;
  125. uint16_t n; // blocks read
  126. uint8_t ret;
  127. if ((x >= tft.width()) || (y >= tft.height()))
  128. return 1; // off screen
  129. bmpFile = SD.open(nm); // Parse BMP header
  130. bmpID = read16(bmpFile); // BMP signature
  131. (void) read32(bmpFile); // Read & ignore file size
  132. (void) read32(bmpFile); // Read & ignore creator bytes
  133. bmpImageoffset = read32(bmpFile); // Start of image data
  134. (void) read32(bmpFile); // Read & ignore DIB header size
  135. bmpWidth = read32(bmpFile);
  136. bmpHeight = read32(bmpFile);
  137. n = read16(bmpFile); // # planes -- must be '1'
  138. bmpDepth = read16(bmpFile); // bits per pixel
  139. pos = read32(bmpFile); // format
  140. if (bmpID != 0x4D42) ret = 2; // bad ID
  141. else if (n != 1) ret = 3; // too many planes
  142. else if (pos != 0 && pos != 3) ret = 4; // format: 0 = uncompressed, 3 = 565
  143. else if (bmpDepth < 16 && bmpDepth > PALETTEDEPTH) ret = 5; // palette
  144. else {
  145. bool first = true;
  146. is565 = (pos == 3); // ?already in 16-bit format
  147. // BMP rows are padded (if needed) to 4-byte boundary
  148. rowSize = (bmpWidth * bmpDepth / 8 + 3) & ~3;
  149. if (bmpHeight < 0) { // If negative, image is in top-down order.
  150. bmpHeight = -bmpHeight;
  151. flip = false;
  152. }
  153. w = bmpWidth;
  154. h = bmpHeight;
  155. if ((x + w) >= tft.width()) // Crop area to be loaded
  156. w = tft.width() - x;
  157. if ((y + h) >= tft.height()) //
  158. h = tft.height() - y;
  159. if (bmpDepth <= PALETTEDEPTH) { // these modes have separate palette
  160. bmpFile.seek(BMPIMAGEOFFSET); //palette is always @ 54
  161. bitmask = 0xFF;
  162. if (bmpDepth < 8)
  163. bitmask >>= bmpDepth;
  164. bitshift = 8 - bmpDepth;
  165. n = 1 << bmpDepth;
  166. lcdbufsiz -= n;
  167. palette = lcdbuffer + lcdbufsiz;
  168. for (col = 0; col < n; col++) {
  169. pos = read32(bmpFile); //map palette to 5-6-5
  170. palette[col] = ((pos & 0x0000F8) >> 3) | ((pos & 0x00FC00) >> 5) | ((pos & 0xF80000) >> 8);
  171. }
  172. }
  173. // Set TFT address window to clipped image bounds
  174. tft.setAddrWindow(x, y, x + w - 1, y + h - 1);
  175. for (row = 0; row < h; row++) { // For each scanline...
  176. // Seek to start of scan line. It might seem labor-
  177. // intensive to be doing this on every line, but this
  178. // method covers a lot of gritty details like cropping
  179. // and scanline padding. Also, the seek only takes
  180. // place if the file position actually needs to change
  181. // (avoids a lot of cluster math in SD library).
  182. uint8_t r, g, b, *sdptr;
  183. int lcdidx, lcdleft;
  184. if (flip) // Bitmap is stored bottom-to-top order (normal BMP)
  185. pos = bmpImageoffset + (bmpHeight - 1 - row) * rowSize;
  186. else // Bitmap is stored top-to-bottom
  187. pos = bmpImageoffset + row * rowSize;
  188. if (bmpFile.position() != pos) { // Need seek?
  189. bmpFile.seek(pos);
  190. buffidx = sizeof(sdbuffer); // Force buffer reload
  191. }
  192. for (col = 0; col < w; ) { //pixels in row
  193. lcdleft = w - col;
  194. if (lcdleft > lcdbufsiz) lcdleft = lcdbufsiz;
  195. for (lcdidx = 0; lcdidx < lcdleft; lcdidx++) { // buffer at a time
  196. uint16_t color;
  197. // Time to read more pixel data?
  198. if (buffidx >= sizeof(sdbuffer)) { // Indeed
  199. bmpFile.read(sdbuffer, sizeof(sdbuffer));
  200. buffidx = 0; // Set index to beginning
  201. r = 0;
  202. }
  203. switch (bmpDepth) { // Convert pixel from BMP to TFT format
  204. case 24:
  205. b = sdbuffer[buffidx++];
  206. g = sdbuffer[buffidx++];
  207. r = sdbuffer[buffidx++];
  208. color = tft.color565(r, g, b);
  209. break;
  210. case 16:
  211. b = sdbuffer[buffidx++];
  212. r = sdbuffer[buffidx++];
  213. if (is565)
  214. color = (r << 8) | (b);
  215. else
  216. color = (r << 9) | ((b & 0xE0) << 1) | (b & 0x1F);
  217. break;
  218. case 1:
  219. case 4:
  220. case 8:
  221. if (r == 0)
  222. b = sdbuffer[buffidx++], r = 8;
  223. color = palette[(b >> bitshift) & bitmask];
  224. r -= bmpDepth;
  225. b <<= bmpDepth;
  226. break;
  227. }
  228. lcdbuffer[lcdidx] = color;
  229. }
  230. tft.pushColors(lcdbuffer, lcdidx, first);
  231. first = false;
  232. col += lcdidx;
  233. } // end cols
  234. } // end rows
  235. tft.setAddrWindow(0, 0, tft.width() - 1, tft.height() - 1); //restore full screen
  236. ret = 0; // good render
  237. }
  238. bmpFile.close();
  239. return (ret);
  240. }