/* * ili9341Driver.c * * Created on: 10.08.2021 @ 17:53:30 * Author: $[AUTHOR] * License: None * * ATTENTION!: This Display Driver is currently designed and * tested under the STM32F103C8T6 Processor and the SPI2 * Peripheral which is clocked at 36MHz so a max SPI SCK * Speed from 18 MHz. If you use this on a other SPI Peripheral * Bus then you have might to change the __NOP() Instuctions in * the 8Bit and 16Bit Functions */ #include "ili9341/ili9341Driver.h" //Screen Size volatile uint16_t LCD_WIDTH = ILI9341_TFTWIDTH; volatile uint16_t LCD_HEIGHT = ILI9341_TFTHEIGHT; //Initializion Codes for ILI9341 Driver const uint16_t ili9341InitCodes[80] = { 0x0128, //Display Off 0x013A, 0x55, //Pixel Format 0x01C0, 0x0D, 0x0D, //Power Control VRH 0x01C1, 0x43, 0x00, //Power Control SAP, BT 0x01C2, 0x00, //Power Control 3 0x01C5, 0x00, 0x48, 0x00, 0x48, //VCM Control A 0x01B4, 0x00, //Disable Z-Zoom 0x01B6, 0x02, 0x02, 0x3B, //Display Function Control 0x0126, 0x01, //Disable 3gamma Function 0x01E0, 0x0F, 0x21, 0x1C, 0x0B, 0x0E, 0x08, 0x49, 0x98, 0x38, 0x09, 0x11, 0x03, 0x14, 0x10, 0x00, //Gamma Positive Correction 0x01E1, 0x0F, 0x2F, 0x2B, 0x0C, 0x0E, 0x06, 0x47, 0x76, 0x37, 0x07, 0x11, 0x04, 0x23, 0x1E, 0x00 //Gamma Neagtive Correction }; //Setup GPIO Pins for Display void ili9341_initGPIOs(void) { RCC->APB2ENR |= RCC_APB2ENR_IOPAEN; //Enable Clock for Port A RCC->APB2ENR |= RCC_APB2ENR_IOPBEN; //Enable Clock for Port B //Set RESET, D/C, WR, RD, CS as Outputs max 50 MHz GPIOA->CRL |= (GPIO_CRL_MODE0 | GPIO_CRL_MODE1 | GPIO_CRL_MODE2 | GPIO_CRL_MODE3 | GPIO_CRL_MODE4); GPIOA->CRL &= ~(GPIO_CRL_CNF0 | GPIO_CRL_CNF1 | GPIO_CRL_CNF2 | GPIO_CRL_CNF3 | GPIO_CRL_CNF4); //Set LCD Data 0 to LCD Data 7 as Output max 50 MHz GPIOA->CRL |= (GPIO_CRL_MODE5 | GPIO_CRL_MODE6 | GPIO_CRL_MODE7); //D0, D1, D2 GPIOA->CRL &= ~(GPIO_CRL_CNF5 | GPIO_CRL_CNF6 | GPIO_CRL_CNF7); GPIOB->CRL |= (GPIO_CRL_MODE0 | GPIO_CRL_MODE1); //D3, D4 GPIOB->CRL &= ~(GPIO_CRL_CNF0 | GPIO_CRL_CNF1); GPIOB->CRH |= (GPIO_CRH_MODE10 | GPIO_CRH_MODE11 | GPIO_CRH_MODE12); //D5, D6, D7 GPIOB->CRH &= ~(GPIO_CRH_CNF10 | GPIO_CRH_CNF11 | GPIO_CRH_CNF12); CS_IDLE; //CS idle(3,3V) WR_IDLE; //WR idle(3,3V) RD_IDLE; //RD idle(3,3V) DC_DATA; //DC Command(0V) RST_IDLE; //RST idle(3,3V) //Data Lines to Ground DX_LOW(GPIOA, ILI_D0); DX_LOW(GPIOA, ILI_D1); DX_LOW(GPIOA, ILI_D2); DX_LOW(GPIOB, ILI_D3); DX_LOW(GPIOB, ILI_D4); DX_LOW(GPIOB, ILI_D5); DX_LOW(GPIOB, ILI_D6); DX_LOW(GPIOB, ILI_D7); } //Perform an Hardware Reset over the RESET Pin of the LED void ili9341_hw_rst(void) { CS_IDLE; RD_IDLE; WR_IDLE; RST_IDLE; delayMs(50); RST_ACTIVE; delayMs(100); RST_IDLE; delayMs(100); } //Initialize the ILI9341 Driver void ili9341_init(void) { ili9341_initGPIOs(); //Initialize all GPIO Pins for the Display: CS, D/C, RESET ili9341_hw_rst(); //Reset the Display CS_ACTIVE; //Enable Communication DC_COMMAND; //Sending Command ili9341_send8Bit(0x01); //Software Reset //Sending all Initialitzion Codes for(uint16_t i = 0; i < (sizeof(ili9341InitCodes) / 2); i++) { if(ili9341InitCodes[i] & 0x100) { DC_COMMAND; ili9341_send8Bit(ili9341InitCodes[i] & 0xFF); }else{ DC_DATA; ili9341_send8Bit(ili9341InitCodes[i]); } } //Exit Sleep Mode DC_COMMAND; ili9341_send8Bit(0x11); delayMs(120); //Turn Display On ili9341_send8Bit(0x29); DC_DATA; CS_IDLE; //Set Display Rotation ili9341_setRotation(3); //Clear Screen Black ili9341_clear(BLACK); } //Sends an 8 Bit Value to the Display. void ili9341_send8Bit(uint8_t data) { GPIOA->ODR = data & 0xFF; WR_ACTIVE; WR_IDLE; } //Sends an 16Bit Value to the Display void ili9341_send16Bit(uint16_t data) { ili9341_send8Bit(data >> 8); ili9341_send8Bit(data); } //Clears the complete Screen void ili9341_clear(uint16_t color) { ili9341_fillrect(0, 0, ILI9341_TFTWIDTH, ILI9341_TFTHEIGHT, color); } //Sets the Display Rotation. void ili9341_setRotation(uint8_t rot) { uint8_t rotation, data; CS_ACTIVE; DC_COMMAND; ili9341_send8Bit(0x36); rotation = rot % 4; switch (rotation) { case 0: data = (0x40 | 0x08); LCD_WIDTH = 320; LCD_HEIGHT = 480; break; case 1: data = (0x20 | 0x08); LCD_WIDTH = 480; LCD_HEIGHT = 320; break; case 2: data = (0x80 | 0x08); LCD_WIDTH = 320; LCD_HEIGHT = 480; break; case 3: data = (0x40 | 0x80 | 0x20 | 0x08); LCD_WIDTH = 480; LCD_HEIGHT = 320; break; } DC_DATA; ili9341_send8Bit(data); CS_IDLE; } //Sets the Address inside the Display where to draw void ili9341_setAddress(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2) { CS_ACTIVE; DC_COMMAND; ili9341_send8Bit(0x2A); DC_DATA; ili9341_send16Bit(x1); ili9341_send16Bit(x2); DC_COMMAND; ili9341_send8Bit(0x2B); DC_DATA; ili9341_send16Bit(y1); ili9341_send16Bit(y2); DC_COMMAND; ili9341_send8Bit(0x2C); DC_DATA; } //Fills an Area void ili9341_fillrect(uint16_t x, uint16_t y, uint16_t w, uint16_t h, uint16_t color) { if((x >=LCD_WIDTH) || (y >=LCD_HEIGHT)) return; if((x+w-1)>=LCD_WIDTH) w=LCD_WIDTH-x; if((y+h-1)>=LCD_HEIGHT) h=LCD_HEIGHT-y; ili9341_setAddress(x, y, x+w-1, y+h-1); for(y = h; y > 0; y--) { for(x = w; x > 0; x--) { ili9341_pushColor(color); } } CS_IDLE; } //Sends the Color to the Display void ili9341_pushColor(uint16_t color) { ili9341_send16Bit(color); } //Fills an Circle void ili9341_fillCircle(uint16_t x, uint16_t y, uint16_t r, uint16_t color) { ili9341_drawVline(x, y - r, 2 * r + 1, color); ili9341_fillCircleHelper(x, y, r, 3, 0, color); } //Draws an Rect void ili9341_drawRect(uint16_t x, uint16_t y, uint16_t w, uint16_t h, uint16_t color) { ili9341_drawHline(x, y + h - 1, w, color); ili9341_drawHline(x, y, w, color); ili9341_drawVline(x, y, h, color); ili9341_drawVline(x + w - 1, y, h, color); } //Draws an Circle void ili9341_drawCircle(uint16_t x, uint16_t y, uint16_t r, uint16_t color) { int16_t f = 1 - r; int16_t ddF_x = 1; int16_t ddF_y = -2 * r; int16_t x1 = 0; int16_t y1 = r; ili9341_drawpixel(x , y + r, color); ili9341_drawpixel(x , y - r, color); ili9341_drawpixel(x + r, y , color); ili9341_drawpixel(x - r, 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 an Veritcal Line void ili9341_drawVline(uint16_t x, uint16_t y, uint16_t h, uint16_t colour) { if((x >= LCD_WIDTH) || (y >= LCD_HEIGHT)) return; if((y + h - 1) >= LCD_HEIGHT) h = LCD_HEIGHT - y; ili9341_setAddress(x, y, x, (y + h - 1)); uint8_t high = colour >> 8, low = colour; while(h--) { ili9341_send8Bit(high); ili9341_send8Bit(low); } CS_IDLE; } //Draws an Horziontal Line void ili9341_drawHline(uint16_t x, uint16_t y, uint16_t w, uint16_t colour) { if((x >= LCD_WIDTH) || (y >= LCD_HEIGHT)) return; if((x + w - 1) >= LCD_WIDTH) w = LCD_WIDTH - x; ili9341_setAddress(x, y, x + w - 1, y); uint8_t high = colour >> 8, low = colour; while(w--) { ili9341_send8Bit(high); ili9341_send8Bit(low); } CS_IDLE; } //Draws an Single Pixel void ili9341_drawpixel(uint16_t x, uint16_t y, uint16_t colour) { if((x < 0) || (x >= LCD_WIDTH) || (y < 0) || (y >= LCD_WIDTH)) return; ili9341_setAddress(x, y, (x + 1), (y + 1)); ili9341_send8Bit(colour >> 8); ili9341_send8Bit(colour); CS_IDLE; } //Helper to fill an Circle void ili9341_fillCircleHelper(uint16_t x, uint16_t y, uint16_t r, uint8_t cornername, uint16_t delta, uint16_t color) { int16_t f = 1 - r; int16_t ddF_x = 1; int16_t ddF_y = -2 * r; int16_t x1 = 0; int16_t y1 = r; while (x1 < y1) { if (f >= 0) { y1--; ddF_y += 2; f += ddF_y; } x1++; ddF_x += 2; f += ddF_x; if (cornername & 0x1) { ili9341_drawVline(x + x1, y - y1, 2 * y1 + 1 + delta, color); ili9341_drawVline(x + y1, y - x1, 2 * x1 + 1 + delta, color); } if (cornername & 0x2) { ili9341_drawVline(x - x1, y - y1, 2 * y1 + 1 + delta, color); ili9341_drawVline(x - y1, y - x1, 2 * x1 + 1 + delta, color); } } } void ili9341_fillTriangle(int16_t x0, int16_t y0, int16_t x1, int16_t y1, int16_t x2, int16_t y2, uint16_t c) { int16_t a, b, y, last; if(y0 > y1) { swap(y0, y1); swap(x0, x1); } if(y1 > y2) { swap(y2, y1); swap(x2, x1); } if(y0 > y1) { swap(y0, y1); swap(x0, x1); } if(y0 == y2) { 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_drawHline(a, y0, b - a + 1, c); return; } int16_t dx01 = x1 - x0, dy01 = y1 - y0, dx02 = x2 - x0, dy02 = y2 - y0, dx12 = x2 - x1, dy12 = y2 - y1, sa = 0, sb = 0; 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; if(a > b) swap(a, b); ili9341_drawHline(a, y, b - a + 1, c); } sa = dx12 * (y - y1); sb = dx02 * (y - y0); for(; y <= y2; y++) { a = x1 + sa / dy12; b = x0 + sb / dy02; sa += dx12, sb += dx02; if(a > b) swap(a, b); ili9341_drawHline(a, y, b - a + 1, c); } } //This Function draws an Analouge Meter like Speedometer. uint16_t ili9341_drawAnalougeMeter(uint16_t value, uint16_t minValue, uint16_t maxValue, uint16_t positionX, uint16_t positionY, uint8_t meterRadius, char *displayName, uint16_t colorScheme, bool directColor, uint8_t ringFactor) { //Calculate Coordinates for the Centre Ring positionX += meterRadius; positionY += meterRadius; //Get Outer Ring uint16_t outerRing = meterRadius / ringFactor; int16_t angle = 150; int16_t v = map(value, minValue, maxValue, -angle, angle); uint8_t segments = 5; uint8_t increases = 5; for(int16_t i = -angle; i < angle; i += increases) { uint16_t colour = 0; if(directColor) { colour = colorScheme; }else{ switch(colorScheme) { case 0: colour = rainbow(map(i, -angle, angle, 0, 127)); break; case 1: colour = rainbow(map(i, -angle, angle, 63, 127)); break; case 2: colour = rainbow(map(i, -angle, angle, 127, 63)); break; default: colour = WHITE; break; } } //Calculate pair of coordinates for segment start float sx = cos((i - 90) * 0.0174532925f); float sy = sin((i - 90) * 0.0174532925f); int16_t x0 = sx * (meterRadius - outerRing) + positionX; int16_t y0 = sy * (meterRadius - outerRing) + positionY; int16_t x1 = sx * meterRadius + positionX; int16_t y1 = sy * meterRadius + positionY; //Calculate pair of coordinates for segment end float sx2 = cos((i + segments - 90) * 0.0174532925f); float sy2 = sin((i + segments - 90) * 0.0174532925f); int16_t x2 = sx2 * (meterRadius - outerRing) + positionX; int16_t y2 = sy2 * (meterRadius - outerRing) + positionY; int16_t x3 = sx2 * meterRadius + positionX; int16_t y3 = sy2 * meterRadius + positionY; if(i < v) { ili9341_fillTriangle(x0, y0, x1, y1, x2, y2, colour); ili9341_fillTriangle(x1, y1, x2, y2, x3, y3, colour); }else{ ili9341_fillTriangle(x0, y0, x1, y1, x2, y2, GRAY); ili9341_fillTriangle(x1, y1, x2, y2, x3, y3, GRAY); } } return positionX + meterRadius; } long map(long x, long in_min, long in_max, long out_min, long out_max) { return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min; } // Return a 16 bit rainbow colour uint32_t rainbow(uint8_t value) { // Value is expected to be in range 0-127 // The value is converted to a spectrum colour from 0 = blue through to 127 = red uint8_t red = 0; // Red is the top 5 bits of a 16 bit colour value uint8_t green = 0;// Green is the middle 6 bits uint8_t blue = 0; // Blue is the bottom 5 bits uint8_t quadrant = value / 32; if (quadrant == 0) { blue = 31; green = 2 * (value % 32); red = 0; } if (quadrant == 1) { blue = 31 - (value % 32); green = 63; red = 0; } if (quadrant == 2) { blue = 0; green = 63; red = value % 32; } if (quadrant == 3) { blue = 0; green = 63 - 2 * (value % 32); red = 31; } return (red << 11) + (green << 5) + blue; }