#include "ili9341.h" volatile uint16_t LCD_W = ILI9341_TFTWIDTH; volatile uint16_t LCD_H = ILI9341_TFTHEIGHT; const uint16_t ili9341InitCodes[80] = { 0x01CB, 0x39, 0x2C, 0x00, 0x34, 0x02, //Power Control A 0x01CF, 0x00, 0xC1, 0x30, //Power Control B 0x01E8, 0x85, 0x00, 0x78, //Driver Timing Control A 0x01EA, 0x00, 0x00, //Driver Timing Control B 0x01ED, 0x64, 0x03, 0x12, 0x81, //Power on Sequence Control 0x01F7, 0x20, //Pump Ratio Control 0x01C0, 0x23, //Power Control VRH 0x01C1, 0x10, //Power Control SAP, BT 0x01C5, 0x3E, 0x28, //VCM Control A 0x01C7, 0x86, //VCM Control B 0x0136, 0x48, //SRAM Memory Access Control 0x013A, 0x55, //Pixel Format 0x01B1, 0x00, 0x18, //Frameration Control Normal Mode, 16 Bit Full Colors 0x01B6, 0x08, 0x82, 0x27, //Display Function Control 0x01F2, 0x00, //Disable 3gamma Function 0x0126, 0x01, //Gamma Curve Selected 0x01E0, 0x0F, 0x31, 0x2B, 0x0C, 0x0E, 0x08, 0x4E, 0xF1, 0x37, 0x07, 0x10, 0x03, 0x0E, 0x09, 0x00, //Gamma Positive Correction 0x01E1, 0x00, 0x0E, 0x14, 0x03, 0x11, 0x07, 0x31, 0xC1, 0x48, 0x08, 0x0F, 0x0C, 0x31, 0x36, 0x0F //Gamma Negative Correction }; #ifndef _swap_int16_t #define _swap_int16_t(a, b) \ { \ int16_t t = a; \ a = b; \ b = t; \ } #endif bool dcHigh; uint8_t ili9341_frameBuffer[(ILI9341_TFTWIDTH * ILI9341_TFTHEIGHT) * 2]; uint16_t ili9341_frameBufferWide[ILI9341_TFTWIDTH * ILI9341_TFTHEIGHT]; //For text functions int16_t cursorX; int16_t cursorY; uint16_t textColor; uint16_t textBgColor; uint8_t textSize_X; uint8_t textSize_Y; bool textWrap; bool _cp437; GFXfont* font; //Local functions void ili9341_writeCommand(uint8_t cmd); void ili9341_writeDataByte(uint8_t data); void ili9341_writeDataWord(uint16_t data); void ili9341_writeDataWords(uint32_t num); void ili9341_writeDataDWord(uint32_t data); void ili9341_fillCircleHelper(uint16_t x, uint16_t y, uint16_t radius, uint8_t cornername, uint16_t delta, uint16_t color); //Initializes the ILI9341 and SPI and GPIO of the BCM2835 void ili9341_init(void) { uint16_t i, tmp; cursorX = 0; cursorY = 0; textSize_X = 1; textSize_Y = 1; textColor = WHITE; textBgColor = BLACK; textWrap = true; _cp437 = false; font = NULL; bcm2835_gpio_fsel(DC_PIN , BCM2835_GPIO_FSEL_OUTP); bcm2835_gpio_fsel(RST_PIN, BCM2835_GPIO_FSEL_OUTP); ili9341_reset(); bcm2835_gpio_write(DC_PIN, LOW); dcHigh = false; ili9341_writeCommand(0x01); for (i = 0; i < (sizeof(ili9341InitCodes) / 2); i++) { tmp = ili9341InitCodes[i]; if (tmp & 0x100) { ili9341_writeCommand(tmp & 0xFF); } else { ili9341_writeDataByte(tmp); } } //Exit sleep mode ili9341_writeCommand(0x11); bcm2835_delay(120); //Turn Display On ili9341_writeCommand(0x29); ili9341_setRotation(3); ili9341_clear(BLACK); } //Resets the ILI9341 via the Reset pin void ili9341_reset(void) { bcm2835_gpio_write(RST_PIN, HIGH); bcm2835_delay(200); bcm2835_gpio_write(RST_PIN, LOW); bcm2835_delay(200); bcm2835_gpio_write(RST_PIN, HIGH); bcm2835_delay(200); } //Pulls the D/C line Low if not already Low. Sends a byte(8-Bit) to the ILI9341 via SPI void ili9341_writeCommand(uint8_t cmd) { if (dcHigh) { bcm2835_gpio_write(DC_PIN, LOW); dcHigh = false; } bcm2835_aux_spi_setClockDivider(bcm2835_aux_spi_CalcClockDivider(ILI_CLOCK)); bcm2835_aux_spi_setCS(2); bcm2835_aux_spi_transfer(cmd); } //Pulls the D/C line High if not already High. Sends a byte(8-Bit) to the ILI9341 via SPI void ili9341_writeDataByte(uint8_t data) { if (!dcHigh) { bcm2835_gpio_write(DC_PIN, HIGH); dcHigh = true; } bcm2835_aux_spi_setClockDivider(bcm2835_aux_spi_CalcClockDivider(ILI_CLOCK)); bcm2835_aux_spi_setCS(2); bcm2835_aux_spi_transfer(data); } //Pulls the D/C line High if not already High. Sends a word(16-Bit) to the ILI9341 via SPI void ili9341_writeDataWord(uint16_t data) { if (!dcHigh) { bcm2835_gpio_write(DC_PIN, HIGH); dcHigh = true; } bcm2835_aux_spi_setClockDivider(bcm2835_aux_spi_CalcClockDivider(ILI_CLOCK)); bcm2835_aux_spi_setCS(2); bcm2835_aux_spi_write(data); } //Pulls the D/C line High if not already High. Sends a dword(32-Bit) to the ILI9341 via SPI void ili9341_writeDataDWord(uint32_t data) { if (!dcHigh) { bcm2835_gpio_write(DC_PIN, HIGH); dcHigh = true; } bcm2835_aux_spi_setClockDivider(bcm2835_aux_spi_CalcClockDivider(ILI_CLOCK)); bcm2835_aux_spi_setCS(2); bcm2835_aux_spi_write_dword(data); } uint8_t ili9341_readByte(void) { if (!dcHigh) { bcm2835_gpio_write(DC_PIN, HIGH); dcHigh = true; } bcm2835_aux_spi_setClockDivider(bcm2835_aux_spi_CalcClockDivider(ILI_CLOCK)); bcm2835_aux_spi_setCS(2); return bcm2835_aux_spi_transfer(0x00); //Sends a NOP } //Pulls the D/C line High if not already High. Sends "num" words(16-bit) to the ILI9341 via SPI from the ili9341_frameBuffer void ili9341_writeDataWords(uint32_t num) { if (num <= 1) return; if (!dcHigh) { bcm2835_gpio_write(DC_PIN, HIGH); dcHigh = true; } bcm2835_aux_spi_setClockDivider(bcm2835_aux_spi_CalcClockDivider(ILI_CLOCK)); bcm2835_aux_spi_setCS(2); bcm2835_aux_spi_writenb(ili9341_frameBuffer, num); } //Reads the pixel data from X,Y in the ILI9341 graphics RAM buffer void ili9341_readPixel(uint16_t x, uint16_t y) { bcm2835_gpio_fsel(RPI_BPLUS_GPIO_J8_36, BCM2835_GPIO_FSEL_OUTP); //Manual CS control - To keep CS continues low during the operation bcm2835_gpio_write(RPI_BPLUS_GPIO_J8_36, LOW); //CS Low //Set read address ili9341_writeCommand(0x2A); ili9341_writeDataDWord((x << 16) + (x + 1 - 1)); //32-Bit ili9341_writeCommand(0x2B); ili9341_writeDataDWord((y << 16) + (y + 1 - 1)); //32-Bit ili9341_writeCommand(0x2E); //Read from GRAM //Write D/C pin High bcm2835_gpio_write(DC_PIN, HIGH); dcHigh = true; //Setup SPI clock to 8MHz bcm2835_aux_spi_setClockDivider(bcm2835_aux_spi_CalcClockDivider(ILI_CLOCK)); bcm2835_aux_spi_setCS(2); (void)bcm2835_aux_spi_transfer(0x00); //Dummy read dont care about it char rxBuf[3] = { 0x00, 0x00, 0x00 }; bcm2835_aux_spi_transfern(rxBuf, sizeof(rxBuf)); bcm2835_gpio_write(RPI_BPLUS_GPIO_J8_36, HIGH); //CS High to end transmission bcm2835_gpio_fsel(RPI_BPLUS_GPIO_J8_36, BCM2835_GPIO_FSEL_ALT4); //Back to auto CS printf("Read Pixel from X:%d Y:%d - Red: %02X - Green: %02X - Blue: %02X - 565: %04X\r\n", x, y, rxBuf[0], rxBuf[1], rxBuf[2], ((rxBuf[0] & 0xF8) << 8) | ((rxBuf[1] & 0xFC) << 3) | (rxBuf[2] >> 3)); } void ili9341_readPixels(uint16_t x, uint16_t y, uint16_t width, uint16_t height) { uint16_t* pixels = malloc(sizeof(uint16_t) * (width * height)); //Allocate memory uint16_t offset = 0; if (pixels == NULL) { printf("malloc in ili9341_readPixels() failed!\r\n"); return; } //Setup SPI clock to 8MHz bcm2835_aux_spi_setClockDivider(bcm2835_aux_spi_CalcClockDivider(ILI_CLOCK)); bcm2835_aux_spi_setCS(2); bcm2835_gpio_fsel(RPI_BPLUS_GPIO_J8_36, BCM2835_GPIO_FSEL_OUTP); //Manual CS control - To keep CS continues low during the operation bcm2835_gpio_write(RPI_BPLUS_GPIO_J8_36, LOW); //CS Low //Set read address ili9341_writeCommand(0x2A); ili9341_writeDataDWord((x << 16) + (x + width - 1)); //32-Bit ili9341_writeCommand(0x2B); ili9341_writeDataDWord((y << 16) + (y + height - 1)); //32-Bit ili9341_writeCommand(0x2E); //Read from GRAM //Write D/C pin High for data bcm2835_gpio_write(DC_PIN, HIGH); dcHigh = true; printf("Reading a rectangle of pixels from X: %d Y: %d with a with of %d and height of %d - Data:\r\n", x, y, width, height); (void)bcm2835_aux_spi_transfer(0x00); //Dummy read dont care about it for (int i = 0; i < (width * height); i++) { char rxBuf[3] = { 0x00, 0x00, 0x00 }; //rxBuf[0] is red. rxBuf[1] is green. rxBuf[2] is blue bcm2835_aux_spi_transfern(rxBuf, sizeof(rxBuf)); //Convert color to 565 uint16_t color565 = ((rxBuf[0] & 0xF8) << 8) | ((rxBuf[1] & 0xFC) << 3) | (rxBuf[2] >> 3); pixels[offset++] = color565; } //Print pixels to serial monitor for (int i = 0; i < offset; i++) { if ((i != 0) && (i % width) == 0) printf("\r\n"); //new line after a complete width of pixels printf("%04X ", pixels[i]); } printf("\r\n"); free(pixels); bcm2835_gpio_write(RPI_BPLUS_GPIO_J8_36, HIGH); //CS High to end transmission bcm2835_gpio_fsel(RPI_BPLUS_GPIO_J8_36, BCM2835_GPIO_FSEL_ALT4); //Back to auto CS } void ili9341_readRegister(uint8_t cmd, uint8_t idx) { /* bcm2835_gpio_fsel(RPI_BPLUS_GPIO_J8_36, BCM2835_GPIO_FSEL_OUTP); //Manual CS control bcm2835_gpio_write(RPI_BPLUS_GPIO_J8_36, LOW); //CS Low ili9341_writeCommand(0xD3); //Command 0xD3 read ID4 ili9341_readByte(); //Dummy read ili9341_readByte(); //First data byte all zeros uint8_t id1 = ili9341_readByte(); //ID part 0 (should be 0x93) uint8_t id2 = ili9341_readByte(); //ID part 1 (should be 0x41) bcm2835_gpio_write(RPI_BPLUS_GPIO_J8_36, HIGH); //CS High bcm2835_gpio_fsel(RPI_BPLUS_GPIO_J8_36, BCM2835_GPIO_FSEL_ALT4); //Back to auto CS printf("ID is: %X %X\r\n", id1, id2);*/ bcm2835_gpio_fsel(RPI_BPLUS_GPIO_J8_36, BCM2835_GPIO_FSEL_OUTP); //Manual CS control bcm2835_gpio_write(RPI_BPLUS_GPIO_J8_36, LOW); //CS Low uint8_t index = 0x10 + (idx & 0x0F); ili9341_writeCommand(0xD9); //Geheimer Befehl?? ili9341_writeDataByte(index); //Teil des Geheimen Befehls bcm2835_gpio_write(RPI_BPLUS_GPIO_J8_36, HIGH); //CS HIGH bcm2835_gpio_write(RPI_BPLUS_GPIO_J8_36, LOW); //CS Low ili9341_writeCommand(cmd); uint8_t data = ili9341_readByte(); bcm2835_gpio_write(RPI_BPLUS_GPIO_J8_36, HIGH); //CS High bcm2835_gpio_fsel(RPI_BPLUS_GPIO_J8_36, BCM2835_GPIO_FSEL_ALT4); //Back to auto CS printf("Val of Reg %02X is: %X\r\n", cmd, data); } //Sets the screen rotation for the ILI9341 display mode. Possible values are 0/90/180/270 void ili9341_setRotation(uint8_t rotation) { uint8_t data; ili9341_writeCommand(0x36); rotation %= 4; switch (rotation) { case 0: data = (0x40 | 0x08); LCD_W = 240; LCD_H = 320; break; case 1: data = (0x20 | 0x08); LCD_W = 320; LCD_H = 240; break; case 2: data = (0x80 | 0x08); LCD_W = 240; LCD_H = 320; break; case 3: data = (0x40 | 0x80 | 0x20 | 0x08); LCD_W = 320; LCD_H = 240; break; } ili9341_writeDataByte(data); } //Sets the address window where we want to operate on the ILI9341 display screen. void ili9341_setAddress(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2) { ili9341_writeCommand(0x2A); ili9341_writeDataDWord((x1 << 16) + x2); //32-Bit ili9341_writeCommand(0x2B); ili9341_writeDataDWord((y1 << 16) + y2); //32-Bit ili9341_writeCommand(0x2C); //Now cames the data bcm2835_gpio_write(DC_PIN, HIGH); dcHigh = true; } //Draws a filled rectangle with the given color at X,Y void ili9341_fillRect(uint16_t x, uint16_t y, uint16_t width, uint16_t height, uint16_t color) { if ((x >= LCD_W) || (y >= LCD_H)) return; if ((x + width - 1) >= LCD_W) width = LCD_W - x; if ((y + height - 1) >= LCD_H) height = LCD_H - y; ili9341_setAddress(x, y, x + width - 1, y + height - 1); uint32_t num = (width * height); for (uint32_t i = 0; i <= num; i++) { ili9341_frameBufferWide[i] = color; } bcm2835_aux_spi_setClockDivider(bcm2835_aux_spi_CalcClockDivider(ILI_CLOCK)); bcm2835_aux_spi_setCS(2); bcm2835_aux_test(num); //Writes the ili9341_frameBufferWide to the SPI bus in 16-Bit mode } //Clears the complete screen with the given color void ili9341_clear(uint16_t color) { ili9341_fillRect(0, 0, ILI9341_TFTWIDTH, ILI9341_TFTHEIGHT, color); } //Draws a simple line from X,Y to X2,Y2 with the given color void ili9341_drawLine(uint16_t x, uint16_t y, uint16_t x2, uint16_t y2, uint16_t color) { if ((x >= LCD_W) || (y >= LCD_H) || (x2 >= LCD_W) || (y2 >= LCD_H)) return; int16_t steep = abs(y2 - y) > abs(x2 - x); if (steep) { _swap_int16_t(x, y); _swap_int16_t(x2, y2); } if (x > x2) { _swap_int16_t(x, x2); _swap_int16_t(y, y2); } int16_t dx, dy; dx = x2 - x; dy = abs(y2 - y); int16_t err = dx / 2; int16_t ystep; if (y < y2) { ystep = 1; } else { ystep = -1; } for (; x <= x2; x++) { if (steep) { ili9341_drawPixel(y, x, color); } else { ili9341_drawPixel(x, y, color); } err -= dy; if (err < 0) { y += ystep; err += dx; } } } //Draws a horizontal line starting from X,Y to X+width,Y with the given color void ili9341_drawHorizontalLine(uint16_t x, uint16_t y, uint16_t width, uint16_t color) { if ((x >= LCD_W) || (y >= LCD_H)) return; if ((x + width - 1) >= LCD_W) width = LCD_W - x; ili9341_setAddress(x, y, x + width - 1, y); for (uint16_t i = 0; i <= width; i++) { ili9341_frameBufferWide[i] = color; } bcm2835_aux_spi_setClockDivider(bcm2835_aux_spi_CalcClockDivider(ILI_CLOCK)); bcm2835_aux_spi_setCS(2); bcm2835_aux_test(width); } //Draws a vertical line starting from X,Y to X+height,Y with the given color void ili9341_drawVerticalLine(uint16_t x, uint16_t y, uint16_t height, uint16_t color) { if ((x >= LCD_W) || (y >= LCD_H)) return; if ((y + height - 1) >= LCD_H) height = LCD_H - y; ili9341_setAddress(x, y, x, y + height - 1); for (uint16_t i = 0; i <= height; i++) { ili9341_frameBufferWide[i] = color; } bcm2835_aux_spi_setClockDivider(bcm2835_aux_spi_CalcClockDivider(ILI_CLOCK)); bcm2835_aux_spi_setCS(2); bcm2835_aux_test(height); } //Draws a single pixel at the given corrdinates X,Y with the given color void ili9341_drawPixel(uint16_t x, uint16_t y, uint16_t color) { if ((x < 0) || (x >= LCD_W) || (y < 0) || (y >= LCD_H)) return; ili9341_setAddress(x, y, x + 1, y + 1); ili9341_writeDataWord(color); } //Draws a rectangle with the given color at X,Y void ili9341_drawRect(uint16_t x, uint16_t y, uint16_t width, uint16_t height, uint16_t color) { ili9341_drawHorizontalLine(x, y, width, color); ili9341_drawHorizontalLine(x, y + height - 1, width, color); ili9341_drawVerticalLine(x, y, height, color); ili9341_drawVerticalLine(x + width - 1, y, height, color); } //Draws a circle with the given color at X,Y and radius void ili9341_drawCircle(uint16_t x, uint16_t y, uint16_t radius, uint16_t color) { int16_t f = 1 - radius; int16_t ddF_x = 1; int16_t ddF_y = -2 * radius; int16_t x1 = 0; int16_t y1 = radius; ili9341_drawPixel(x, y + radius, color); ili9341_drawPixel(x, y - radius, color); ili9341_drawPixel(x + radius, y, color); ili9341_drawPixel(x - radius, y, color); while (x1 < y1) { if (f >= 0) { y1--; ddF_y += 2; f += ddF_y; } x1++; ddF_x += 2; f += ddF_x; ili9341_drawPixel(x + x1, y + y1, color); ili9341_drawPixel(x - x1, y + y1, color); ili9341_drawPixel(x + x1, y - y1, color); ili9341_drawPixel(x - x1, y - y1, color); ili9341_drawPixel(x + y1, y + x1, color); ili9341_drawPixel(x - y1, y + x1, color); ili9341_drawPixel(x + y1, y - x1, color); ili9341_drawPixel(x - y1, y - x1, color); } } //Draws a filled circle with the given color at X,Y and radius void ili9341_fillCircle(uint16_t x, uint16_t y, uint16_t radius, uint16_t color) { ili9341_drawVerticalLine(x, y - radius, 2 * radius + 1, color); ili9341_fillCircleHelper(x, y, radius, 3, 0, color); } //Circle helper function to draw the filled circle void ili9341_fillCircleHelper(uint16_t x, uint16_t y, uint16_t radius, uint8_t cornername, uint16_t delta, uint16_t color) { int16_t f = 1 - radius; int16_t ddF_x = 1; int16_t ddF_y = -2 * radius; int16_t x1 = 0; int16_t y1 = radius; while (x1 < y1) { if (f >= 0) { y1--; ddF_y += 2; f += ddF_y; } x1++; ddF_x += 2; f += ddF_x; if (cornername & 0x1) { ili9341_drawVerticalLine(x + x1, y - y1, 2 * y1 + 1 + delta, color); ili9341_drawVerticalLine(x + y1, y - x1, 2 * x1 + 1 + delta, color); } if (cornername & 0x2) { ili9341_drawVerticalLine(x - x1, y - y1, 2 * y1 + 1 + delta, color); ili9341_drawVerticalLine(x - y1, y - x1, 2 * x1 + 1 + delta, color); } } } //Draws a filled triangle from xLeft,yLeft to xTop, yTop and to xRight, yRight with given color void ili9341_fillTriangle(int16_t x0, int16_t y0, int16_t x1, int16_t y1, int16_t x2, int16_t y2, uint16_t color) { int16_t a, b, y, last; // Sort coordinates by Y order (y2 >= y1 >= y0) if (y0 > y1) { _swap_int16_t(y0, y1); _swap_int16_t(x0, x1); } if (y1 > y2) { _swap_int16_t(y2, y1); _swap_int16_t(x2, x1); } if (y0 > y1) { _swap_int16_t(y0, y1); _swap_int16_t(x0, x1); } if (y0 == y2) { // Handle awkward all-on-same-line case as its own thing a = b = x0; if (x1 < a) a = x1; else if (x1 > b) b = x1; if (x2 < a) a = x2; else if (x2 > b) b = x2; ili9341_drawHorizontalLine(a, y0, b - a + 1, color); return; } int16_t dx01 = x1 - x0, dy01 = y1 - y0, dx02 = x2 - x0, dy02 = y2 - y0, dx12 = x2 - x1, dy12 = y2 - y1; int32_t sa = 0, sb = 0; // For upper part of triangle, find scanline crossings for segments // 0-1 and 0-2. If y1=y2 (flat-bottomed triangle), the scanline y1 // is included here (and second loop will be skipped, avoiding a /0 // error there), otherwise scanline y1 is skipped here and handled // in the second loop...which also avoids a /0 error here if y0=y1 // (flat-topped triangle). if (y1 == y2) last = y1; // Include y1 scanline else last = y1 - 1; // Skip it for (y = y0; y <= last; y++) { a = x0 + sa / dy01; b = x0 + sb / dy02; sa += dx01; sb += dx02; /* longhand: a = x0 + (x1 - x0) * (y - y0) / (y1 - y0); b = x0 + (x2 - x0) * (y - y0) / (y2 - y0); */ if (a > b) _swap_int16_t(a, b); ili9341_drawHorizontalLine(a, y, b - a + 1, color); } // For lower part of triangle, find scanline crossings for segments // 0-2 and 1-2. This loop is skipped if y1=y2. sa = (int32_t)dx12 * (y - y1); sb = (int32_t)dx02 * (y - y0); for (; y <= y2; y++) { a = x1 + sa / dy12; b = x0 + sb / dy02; sa += dx12; sb += dx02; /* longhand: a = x1 + (x2 - x1) * (y - y1) / (y2 - y1); b = x0 + (x2 - x0) * (y - y0) / (y2 - y0); */ if (a > b) _swap_int16_t(a, b); ili9341_drawHorizontalLine(a, y, b - a + 1, color); } } /*Loads a bitmap file(.bmp) from the filesystem. Path must be absolut like / home / / file.bmp. Draws the bitmap at the given coordinates X, Y Return values: >0 = Returns a positive number of how many pixels are readed and procceded to the display -1 = File not found -2 = File is not a bitmap file(.bmp) -3 = Invalid bit depth format (Must be 16-bit 5-6-5 color format) -4 = Readed pixel from the bitmap file is incorrect to calculated pixel size of the bitmap parameters -5 = Failed to allocated memory in HEAP for the pixel data */ int32_t ili9341_drawBitmapFromFs(uint16_t x, uint16_t y, const char* fileName) { uint32_t image_data_address; int32_t width; int32_t height; uint32_t pixelCount; uint16_t bit_depth; uint8_t byte_depth; uint16_t* pixels; uint32_t biCompression, imgSize; uint32_t redMask, greenMask, blueMask, alphaMask; uint8_t padding; FILE* file = fopen(fileName, "rb"); if (file) { if (fgetc(file) == 'B' && fgetc(file) == 'M') { fseek(file, 8, SEEK_CUR); fread(&image_data_address, 4, 1, file); fseek(file, 4, SEEK_CUR); fread(&width, 4, 1, file); fread(&height, 4, 1, file); fseek(file, 2, SEEK_CUR); fread(&bit_depth, 2, 1, file); fread(&biCompression, 4, 1, file); fread(&imgSize, 4, 1, file); fseek(file, 16, SEEK_CUR); fread(&redMask, 4, 1, file); fread(&greenMask, 4, 1, file); fread(&blueMask, 4, 1, file); fread(&alphaMask, 4, 1, file); if (bit_depth == 16) { //Only continue if a 16-bit bitmap is confirm byte_depth = bit_depth / 8; //Calculate the byte depth. Usually it is 2 byte on a 16 bit bitmap. But to be sure lets calculate it padding = width % byte_depth; //Check if an extra padding byte(filling byte) is needed. Is needed if width is odd pixelCount = (width * height) + (padding * height); //Calculate pixelCount. Including the extra padding byte for odd widths /* Notice to the image size and width & height. * If you calculate width * height to get the pixel Count is represent only the real pixel data. * but if padding bytes are used with followed at the end of a width row then you shift the * complete image! * The imagesize from the header is the pixel data array including the padding bytes. So if there * is no extra padding byte needs then (width * height * 2) is equals to image size. If padding * bytes are needed then image size - (width * height * 2) = padding bytes * * So you can allocate memory using the image size instead of pixelCount * byte_depth because image * size repesent all information from the pixel array which we needed directly in bytes! */ if (LOAD_BMP_DEBUG) printf("Cal pixelCount: %d\r\n", pixelCount); pixels = malloc(pixelCount * byte_depth); //malloc(pixelCount * byte_depth); //Allocated memory //If malloc falls it returns null if (pixels) { fseek(file, image_data_address, SEEK_SET); uint32_t pixels_read = fread(pixels, byte_depth, pixelCount, file); if (LOAD_BMP_DEBUG) { printf("pixels_read : %d\r\n", pixels_read); printf("img_size : %d\r\n", imgSize); } if (pixels_read == pixelCount) { //Only for debug if (LOAD_BMP_DEBUG) { printf("Width : %d\r\n", width); printf("Height : %d\r\n", height); printf("Image Size : %d\r\n", imgSize); printf("Pixelcount : %d\r\n", pixelCount); printf("Bi-Compression: %d\r\n", biCompression); printf("Red mask : %X\r\n", redMask); printf("Green mask : %X\r\n", greenMask); printf("Blue mask : %X\r\n", blueMask); printf("Alpha mask : %X\r\n", alphaMask); printf("Padding Byte : %s(%d)\r\n", padding ? "Yes" : "No", padding); } //Address must set before the width gets may excced by one because of padding ili9341_setAddress(x, y, x + width - 1, y + height - 1); //Set addresss for display uint32_t offset = 0; if (padding) width++; if (LOAD_BMP_DEBUG) { printf(" "); for (int i = 0; i < width; i++) { printf("%02d ", i); } printf("\r\n00 "); } memset(ili9341_frameBufferWide, 0, sizeof(ili9341_frameBufferWide)); //Reserve pixel array for (int h = 0; h < height; h++) { for (int w = 0; w < width; w++) { uint16_t pixelOffset = (((height - h) * width) - (width - w)); //Offset in pixel array without loading the paddings in correct order if (padding && w == (width - 1)) continue; //Skip the padding byte in the loop ili9341_frameBufferWide[offset++] = pixels[pixelOffset]; if(LOAD_BMP_DEBUG) printf("%04X ", ili9341_frameBufferWide[offset - 1]); } if (LOAD_BMP_DEBUG) printf("\r\n%02d " , (h + 1)); } if (LOAD_BMP_DEBUG) printf("Pixels procceded: %d\r\n", offset); bcm2835_aux_spi_setClockDivider(bcm2835_aux_spi_CalcClockDivider(ILI_CLOCK)); bcm2835_aux_spi_setCS(2); bcm2835_aux_test(offset); free(pixels); //Free allocated memory return pixels_read; } else { return INCORRECT_PIXEL_SIZE; } } else { return FAILED_TO_ALLOC_MEM; } } else { return INVALID_BIT_DEPTH; } } else { return FILE_IS_NOT_BITMAP; } fclose(file); } else { return BITMAP_NOT_FOUND; } } //Writes an string (str) to the display at X,Y with the given color and background color and size void ili9341_text(int16_t x, int16_t y, uint16_t color, uint16_t bgColor, uint8_t size, const char* str) { ili9341_setCursor(x, y); ili9341_setTextColor(color, bgColor); ili9341_setTextSize(size); uint8_t len = strlen(str); for (uint8_t i = 0; i < len; i++) { ili9341_writeChar(str[i]); } } //Writes an string (str) to the display at X,Y with the given color and background color and size. Applies the given font. Sets the font after writting back to NULL void ili9341_textFont(int16_t x, int16_t y, uint16_t color, uint16_t bgColor, const GFXfont* font, uint8_t size, const char* str) { ili9341_setFont(font); ili9341_text(x, y, color, bgColor, size, str); ili9341_setFont(NULL); } //Writes an string (str) to the display at X,Y with the given color and background color and size. Sets the font to NULL. void ili9341_textNoFont(int16_t x, int16_t y, uint16_t color, uint16_t bgColor, uint8_t size, const char* str) { ili9341_setFont(NULL); ili9341_text(x, y, color, bgColor, size, str); } //Writes a single char to the display automatically increases the cursor. size_t ili9341_writeChar(uint8_t c) { if (!font) { //"Classic" build in font if (c == '\n') { //New line cursorX = 0; //Reset X to zero cursorY += textSize_Y * 8; //Advance y one line } else if (c != '\r') { //Ignore carriage returns if (textWrap && ((cursorX + textSize_X * 6) > LCD_W)) { cursorX = 0; //Reset X to zero cursorY += textSize_Y * 8; //Advance y one line } ili9341_drawChar(cursorX, cursorY, c, textColor, textBgColor, textSize_X, textSize_Y); cursorX += textSize_X * 6; //Advance x one char } } else { //Custom font if (c == '\n') { cursorX = 0; cursorY += (int16_t)textSize_Y * font->yAdvance; } else if (c != '\r') { uint8_t first = font->first; if ((c >= first) && (c <= font->last)) { GFXglyph* glyph = font->glyph + c - first; uint8_t w = glyph->width; uint8_t h = glyph->height; if ((w > 0) && (h > 0)) { //Is there an associated bitmap? int16_t xo = (int8_t)glyph->xOffset; //sic if (textWrap && ((cursorX + textSize_X * (xo + w)) > LCD_W)) { cursorX = 0; cursorY += (int16_t)textSize_Y * (uint8_t)font->yAdvance; } ili9341_drawChar(cursorX, cursorY, c, textColor, textBgColor, textSize_X, textSize_Y); } cursorX += (uint8_t)glyph->xAdvance * (int16_t)textSize_X; } } } return 1; } //Writes an single char at X,Y with the given color and background color. Size_X,Y determines it size on the screen void ili9341_drawChar(int16_t x, int16_t y, unsigned char c, uint16_t color, uint16_t bgColor, uint8_t size_x, uint8_t size_y) { if (!font) { //Use "Classic" build-in font if ((x >= LCD_W) ||(y >= LCD_H) || ((x + 6 * size_x - 1) < 0) || ((y + 8 * size_y - 1) < 0)) return; //Check for CP437 charset if (!_cp437 && (c >= 176)) { c++; //Handle classic charset behavior } //startWrite() for (int8_t i = 0; i < 5; i++) { //Char bitmap = 5 colmns uint8_t line = classicFont[c * 5 + i]; for (int8_t j = 0; j < 8; j++, line >>= 1) { if (line & 1) { if (size_x == 1 && size_y == 1) { ili9341_drawPixel(x + i, y + j, color); } else { ili9341_fillRect(x + i * size_x, y + j * size_y, size_x, size_y, color); } } else if (bgColor != color) { if (size_x == 1 && size_y == 1) { ili9341_drawPixel(x + i, y + j, bgColor); } else { ili9341_fillRect(x + i * size_x, y + j * size_y, size_x, size_y, bgColor); } } } } if (bgColor != color) { // If opaque, draw vertical line for last column if (size_x == 1 && size_y == 1) { ili9341_drawVerticalLine(x + 5, y, 8, bgColor); } else { ili9341_fillRect(x + 5 * size_x, y, size_x, 8 * size_y, bgColor); } } //endWrite() } else { //Custom font c -= (uint8_t)font->first; GFXglyph* glyph = font->glyph + c; uint8_t* bitmap = font->bitmap; uint16_t bo = glyph->bitmapOffset; uint8_t w = glyph->width; uint8_t h = glyph->height; int8_t xo = glyph->xOffset; int8_t yo = glyph->yOffset; uint8_t xx, yy, bits = 0, bit = 0; int16_t xo16 = 0, yo16 = 0; if (size_x > 1 || size_y > 1) { xo16 = xo; yo16 = yo; } //startWrite() for (yy = 0; yy < h; yy++) { for (xx = 0; xx < w; xx++) { if (!(bit++ & 7)) { bits = bitmap[bo++]; } if (bits & 0x80) { if (size_x == 1 && size_y == 1) { ili9341_drawPixel(x + xo + xx, y + yo + yy, color); } else { ili9341_fillRect(x + (xo16 + xx) * size_x, y + (yo16 + yy) * size_y, size_x, size_y, color); } } bits <<= 1; } } //endWrite() } } //Sets the textsize for the ili9341_drawChar function. Minimum size is 1 void ili9341_setTextSize(uint8_t size) { textSize_X = (size > 0) ? size : 1; textSize_Y = (size > 0) ? size : 1; } //Sets a custom font for the ili9341_drawChar function. Pass NULL to use the classic build-in font. void ili9341_setFont(const GFXfont* f) { if (f) { if (!font) { cursorY += 6; } } else { cursorY -= 6; } font = (GFXfont*)f; } //Sets the text and background color for the ili9341_drawChar function void ili9341_setTextColor(uint16_t color, uint16_t bgColor) { textColor = color; textBgColor = bgColor; } //Enables the text wrap style. If a text reaches the end of the screen it will wrap to the new line void ili9341_setTextWrap(bool w) { textWrap = w; } //Sets the cursor on the screen to X,Y to draw a char with ili9341_drawChar void ili9341_setCursor(int16_t x, int16_t y) { cursorX = x; cursorY = y; } //Sets the IBM CP437 charset void ili9341_setCP437(bool x) { _cp437 = x; } //Returns the current cursor X position on the screen int16_t ili9341_getCursorX(void) { return cursorX; } //Returns the current cursor Y position on the screen int16_t ili9341_getCursorY(void) { return cursorY; } //Returns the current text color uint16_t ili9341_getTextColor(void) { return textColor; } //Returns the current text background color uint16_t ili9341_getTextBgColor(void) { return textBgColor; } //Returns the current text size uint8_t ili9341_getTextSize(void) { return textSize_X; }