// Driver afficheur ADA 1983/chip ILI9341 pour Kit STM32L476 IUT1 de Grenoble Dpt GEII // // Adaptations L476, et fusion libs : V. GRENNERAT, IUT1 de Grenoble, Dpt GEII // Correction bug coordonnees X et Y pour Draw_Char et Draw_Text : V. GRENNERAT // Ajout fonction LCD_Draw_Image_XY : V. GRENNERAT // Intégration des fonctions de configuration SPI3 et GPIO : V. GRENNERAT // Version 2 (1/12/2019) : // Suppression de l'utilisation de la HAL STM32 : V. GRENNERAT // Diverses optimisations, dans les burst images. // -------------------------------- // MIT License // // Copyright (c) 2017 Matej Artnak // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // /* Includes ------------------------------------------------------------------*/ #include "LCD_driver.h" #include "5x5_font.h" #include "stm32f1xx.h" /* Global Variables ------------------------------------------------------------------*/ volatile uint16_t LCD_HEIGHT = LCD_SCREEN_HEIGHT; volatile uint16_t LCD_WIDTH = LCD_SCREEN_WIDTH; // Set all 16 data pins (DB0-DB15) based on a 16-bit value void SetDataPins(uint16_t value) { GPIOA->BSRR = (value & (1 << 0) ? DB0_Pin : (DB0_Pin << 16)) | // DB0 (GPIOA) (value & (1 << 14) ? DB14_Pin : (DB14_Pin << 16)); // DB14 (GPIOA) GPIOB->BSRR = (value & (1 << 1) ? DB1_Pin : (DB1_Pin << 16)) | // DB1 (GPIOB) (value & (1 << 3) ? DB3_Pin : (DB3_Pin << 16)) | // DB3 (GPIOB) (value & (1 << 5) ? DB5_Pin : (DB5_Pin << 16)) | // DB5 (GPIOB) (value & (1 << 7) ? DB7_Pin : (DB7_Pin << 16)) | // DB7 (GPIOB) (value & (1 << 9) ? DB9_Pin : (DB9_Pin << 16)) | // DB9 (GPIOB) (value & (1 << 11) ? DB11_Pin : (DB11_Pin << 16)) | // DB11 (GPIOB) (value & (1 << 12) ? DB12_Pin : (DB12_Pin << 16)) | // DB12 (GPIOB) (value & (1 << 13) ? DB13_Pin : (DB13_Pin << 16)) | // DB13 (GPIOB) (value & (1 << 15) ? DB15_Pin : (DB15_Pin << 16)); // DB15 (GPIOB) GPIOD->BSRR = (value & (1 << 2) ? DB2_Pin : (DB2_Pin << 16)) | // DB2 (GPIOD) (value & (1 << 4) ? DB4_Pin : (DB4_Pin << 16)); // DB4 (GPIOD) GPIOC->BSRR = (value & (1 << 6) ? DB6_Pin : (DB6_Pin << 16)) | // DB6 (GPIOC) (value & (1 << 8) ? DB8_Pin : (DB8_Pin << 16)) | // DB8 (GPIOC) (value & (1 << 10) ? DB10_Pin : (DB10_Pin << 16)); // DB10 (GPIOC) } //void SetDataPins(uint16_t value) { // // Reset all data pins to 0 first (using BRR) // GPIOA->BRR = DB0_Pin | DB14_Pin; // Reset DB0, DB14 (GPIOA) // GPIOB->BRR = DB1_Pin | DB3_Pin | DB5_Pin | DB7_Pin | DB9_Pin | // DB11_Pin | DB12_Pin | DB13_Pin | DB15_Pin; // Reset DB1, DB3, DB5, DB7, DB9, DB11, DB12, DB13, DB15 (GPIOB) // GPIOD->BRR = DB2_Pin | DB4_Pin; // Reset DB2, DB4 (GPIOD) // GPIOC->BRR = DB6_Pin | DB8_Pin | DB10_Pin; // Reset DB6, DB8, DB10 (GPIOC) // // // Set pins to 1 if their corresponding bit in 'value' is 1 (using BSRR) // if (value & (1 << 0)) GPIOA->BSRR = DB0_Pin; // DB0 (GPIOA) // if (value & (1 << 1)) GPIOB->BSRR = DB1_Pin; // DB1 (GPIOB) // if (value & (1 << 2)) GPIOD->BSRR = DB2_Pin; // DB2 (GPIOD) // if (value & (1 << 3)) GPIOB->BSRR = DB3_Pin; // DB3 (GPIOB) // if (value & (1 << 4)) GPIOD->BSRR = DB4_Pin; // DB4 (GPIOD) // if (value & (1 << 5)) GPIOB->BSRR = DB5_Pin; // DB5 (GPIOB) // if (value & (1 << 6)) GPIOC->BSRR = DB6_Pin; // DB6 (GPIOC) // if (value & (1 << 7)) GPIOB->BSRR = DB7_Pin; // DB7 (GPIOB) // if (value & (1 << 8)) GPIOC->BSRR = DB8_Pin; // DB8 (GPIOC) // if (value & (1 << 9)) GPIOB->BSRR = DB9_Pin; // DB9 (GPIOB) // if (value & (1 << 10)) GPIOC->BSRR = DB10_Pin; // DB10 (GPIOC) // if (value & (1 << 11)) GPIOB->BSRR = DB11_Pin; // DB11 (GPIOB) // if (value & (1 << 12)) GPIOB->BSRR = DB12_Pin; // DB12 (GPIOB) // if (value & (1 << 13)) GPIOB->BSRR = DB13_Pin; // DB13 (GPIOB) // if (value & (1 << 14)) GPIOA->BSRR = DB14_Pin; // DB14 (GPIOA) // if (value & (1 << 15)) GPIOB->BSRR = DB15_Pin; // DB15 (GPIOB) //} //typedef struct { // GPIO_TypeDef* port; // uint16_t pin; //} DataPin; // //// Map each data bit (DB0-DB15) to its GPIO port and pin //const DataPin dataPins[16] = { // {DB0_GPIO_Port, DB0_Pin}, // DB0 // {DB1_GPIO_Port, DB1_Pin}, // DB1 // {DB2_GPIO_Port, DB2_Pin}, // DB2 // {DB3_GPIO_Port, DB3_Pin}, // DB3 // {DB4_GPIO_Port, DB4_Pin}, // DB4 // {DB5_GPIO_Port, DB5_Pin}, // DB5 // {DB6_GPIO_Port, DB6_Pin}, // DB6 // {DB7_GPIO_Port, DB7_Pin}, // DB7 // {DB8_GPIO_Port, DB8_Pin}, // DB8 // {DB9_GPIO_Port, DB9_Pin}, // DB9 // {DB10_GPIO_Port, DB10_Pin}, // DB10 // {DB11_GPIO_Port, DB11_Pin}, // DB11 // {DB12_GPIO_Port, DB12_Pin}, // DB12 // {DB13_GPIO_Port, DB13_Pin}, // DB13 // {DB14_GPIO_Port, DB14_Pin}, // DB14 // {DB15_GPIO_Port, DB15_Pin} // DB15 //}; // //void SetDataPins(uint16_t value) { // for (int i = 0; i < 16; i++) { // // Check if the i-th bit in 'value' is set // GPIO_PinState state = (value & (1 << i)) ? GPIO_PIN_SET : GPIO_PIN_RESET; // // // Write the state to the corresponding GPIO pin // HAL_GPIO_WritePin(dataPins[i].port, dataPins[i].pin, state); // } //} //void ScreenWriteCmd(uint16_t command) { // MCU_RS_LOW(); // MCU_CS_LOW(); // SetDataPins(command); // __NOP(); // // MCU_WR_LOW(); // __NOP(); __NOP(); __NOP(); __NOP(); // MCU_WR_HIGH(); // __NOP(); __NOP(); __NOP(); __NOP(); // // MCU_CS_HIGH(); //} //void ScreenWriteData(uint16_t data) { // MCU_RS_HIGH(); // MCU_CS_LOW(); // SetDataPins(data); // __NOP(); // // MCU_WR_LOW(); // __NOP(); __NOP(); __NOP(); __NOP(); // MCU_WR_HIGH(); // __NOP(); __NOP(); __NOP(); __NOP(); // // MCU_CS_HIGH(); //} // Write Command (optimized for timing) void ScreenWriteCmd(uint16_t command) { // 1. Set RS to command mode (t_AS = 10 ns) MCU_RS_LOW(); __NOP(); // 1 NOP = ~15.625 ns > 10 ns // 2. Assert CS (t_AS = 10 ns) MCU_CS_LOW(); __NOP(); // 1 NOP = ~15.625 ns > 10 ns // 3. Set data pins (t_DSW = 10 ns) SetDataPins(command); __NOP(); // 1 NOP = ~15.625 ns > 10 ns // 4. Pulse WR low (PWLW = 50 ns) MCU_WR_LOW(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); // 4 NOPs = ~62.5 ns > 50 ns __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); MCU_WR_HIGH(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); // 5. Hold time (t_H = 15 ns) __NOP(); __NOP(); __NOP(); __NOP(); // 4 NOPs // 6. Deassert CS (t_AH = 5 ns) MCU_CS_HIGH(); } // Write Data (same timing as command) void ScreenWriteData(uint16_t data) { // 1. Set RS to data mode (t_AS = 10 ns) MCU_RS_HIGH(); __NOP(); // 1 NOP // 2. Assert CS (t_AS = 10 ns) MCU_CS_LOW(); __NOP(); // 1 NOP // 3. Set data pins (t_DSW = 10 ns) SetDataPins(data); __NOP(); // 1 NOP // 4. Pulse WR low (PWLW = 50 ns) MCU_WR_LOW(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); // 4 NOPs __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); MCU_WR_HIGH(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); // 5. Hold time (t_H = 15 ns) __NOP(); __NOP(); __NOP(); __NOP(); // 4 NOPs // 6. Deassert CS (t_AH = 5 ns) MCU_CS_HIGH(); } /* Send command (char) to LCD via SPI bus */ void LCD_Write_Command(uint8_t Command) { while ((SPI1->SR & SPI_SR_BSY) != 0); CMD ; while ((SPI1->SR & SPI_SR_TXE) == 0); SPI1->DR = Command;// Cast sur pointeur, pour ecriture 8 bits. Sinon l'acces 16 bits provoque un tfert 16 bits } /* Send Data (char) to LCD via SPI bus */ void LCD_Write_Data(uint8_t Data) { while ((SPI1->SR & SPI_SR_BSY) != 0);//Attendre fin envoi trame DATA ; while ((SPI1->SR & SPI_SR_TXE) == 0);// Waiting for TX register to be available. SPI1->DR = Data; } void LCD_Write_Data16(uint16_t data) { while ((SPI1->SR & SPI_SR_BSY) != 0);// Wait until SPI is not busy DATA ; while ((SPI1->SR & SPI_SR_TXE) == 0);// Wait for TX buffer empty SPI1->DR = (data >> 8) & 0xFF;// Send MSB while ((SPI1->SR & SPI_SR_TXE) == 0);// Wait for TX buffer empty SPI1->DR = data & 0xFF;// Send LSB } /* Set the frame to draw into and sends a write into frame command */ void LCD_Set_Address(uint16_t X1, uint16_t Y1, uint16_t X2, uint16_t Y2) { ScreenWriteCmd(0x2A); ScreenWriteData(X1 + 10); ScreenWriteData(X2 + 10); ScreenWriteCmd(0x2B); ScreenWriteData(Y1 + 19); ScreenWriteData(Y2 + 19); ScreenWriteCmd(0x2C); } void LCD_HardwareReset() { HAL_GPIO_WritePin(DRESET_GPIO_Port, DRESET_Pin, GPIO_PIN_SET); HAL_Delay(1); HAL_GPIO_WritePin(DRESET_GPIO_Port, DRESET_Pin, GPIO_PIN_RESET); HAL_Delay(20); HAL_GPIO_WritePin(DRESET_GPIO_Port, DRESET_Pin, GPIO_PIN_SET); HAL_Delay(200); } void LCD_Init(void) { MCU_RD_HIGH(); LCD_HardwareReset(); // Software reset //ScreenWriteCmd(0x01); // 1 //HAL_Delay(150); // Color mode: 16bit/pixels ScreenWriteCmd(0x3A); ScreenWriteData(0x55); HAL_Delay(10); // Enable color inversion (INVON) //ScreenWriteCmd(0x21); // 0100001 // 1010001 // Configure orientation stuff //ScreenWriteCmd(0x36); //ScreenWriteData(0b10110100); // Exit sleep ScreenWriteCmd(0x11); HAL_Delay(120); // Turn on display ScreenWriteCmd(0x29); HAL_Delay(100); // Fill white LCD_Fill_Screen(RED); // Draw colors columns // LCD_Set_Address(30, 30, LCD_SCREEN_WIDTH - 30, LCD_SCREEN_HEIGHT - 30); // uint32_t size = (LCD_SCREEN_WIDTH - 59) * 20; // LCD_Draw_Colour_Burst(BLACK, size); // LCD_Draw_Colour_Burst(WHITE, size); // LCD_Draw_Colour_Burst(BLUE, size); // LCD_Draw_Colour_Burst(GREEN, size); // LCD_Draw_Colour_Burst(RED, size); // LCD_Draw_Colour_Burst(BLACK, 4 * size); // Draw rectangles in the angles // LCD_Draw_Rectangle(1, 1, 20, 20, RED); // LCD_Draw_Rectangle(LCD_SCREEN_WIDTH - 21, 1, 20, 20, GREEN); // LCD_Draw_Rectangle(LCD_SCREEN_WIDTH - 21, LCD_SCREEN_HEIGHT - 21, 20, 20, // MAGENTA); // LCD_Draw_Rectangle(1, LCD_SCREEN_HEIGHT - 21, 20, 20, BLUE); // STARTING ROTATION //LCD_Set_Rotation(SCREEN_HORIZONTAL_1); } //INTERNAL FUNCTION OF LIBRARY /*Sends block colour information to LCD*/ void LCD_Draw_Colour_Burst(uint16_t color, uint32_t size) { //while ((SPI1->SR & SPI_SR_BSY) != 0);// Wait until SPI is not busy // DATA; for (uint32_t j = 0; j < size; j++) { ScreenWriteData(color); // while ((SPI1->SR & SPI_SR_TXE) == 0);// Waiting for TX register to be available. // SPI1->DR = (color >> 8) & 0xFF; // while ((SPI1->SR & SPI_SR_TXE) == 0);// Waiting for TX register to be available. // SPI1->DR = color & 0xFF; } } //FILL THE ENTIRE SCREEN WITH SELECTED COLOUR (either #define-d ones or custom 16bit) /*Sets address (entire screen) and Sends Height*Width ammount of colour information to LCD*/ void LCD_Fill_Screen(uint16_t color) { LCD_Draw_Rectangle(0, 0, LCD_WIDTH, LCD_HEIGHT, color); } //DRAW PIXEL AT XY POSITION WITH SELECTED COLOUR // //Location is dependant on screen orientation. x0 and y0 locations change with orientations. //Using pixels to draw big simple structures is not recommended as it is really slow //Try using either rectangles or lines if possible // //void LCD_Draw_Pixel(uint16_t X, uint16_t Y, uint16_t Colour) { // if ((X >= LCD_WIDTH) || (Y >= LCD_HEIGHT)) return;//OUT OF BOUNDS! // ////ADDRESS // LCD_Write_Command(0x2A); // ////XDATA // DATA // ; // CS_ON; // SPI3->DR = (X >> 8) | (X << 8);//inversion MSB / LSB pour envoi des 2 mots 8 bits en 1W 16bits ////Pas d'att si FIFO full (TX buffer Empty=0) car juste 2 écriture 16 bits tiennent dans FIFO // SPI3->DR = ((X + 1) >> 8) | ((X + 1) << 8); // while ((SPI3->SR & SPI_SR_BSY) != 0);//Attendre fin envoi trame // CS_OFF; // ////ADDRESS // LCD_Write_Command(0x2B); // ////YDATA // DATA // ; // CS_ON; // SPI3->DR = (Y >> 8) | (Y << 8); // SPI3->DR = ((Y + 1) >> 8) | ((Y + 1) << 8); // while ((SPI3->SR & SPI_SR_BSY) != 0);//Attendre fin envoi trame // CS_OFF; // ////ADDRESS // LCD_Write_Command(0x2C); // ////COLOUR // DATA // ; // CS_ON; // SPI3->DR = (Colour >> 8) | (Colour << 8); // while ((SPI3->SR & SPI_SR_BSY) != 0);//Attendre fin envoi trame // CS_OFF; //} //DRAW RECTANGLE OF SET SIZE AND HEIGTH AT X and Y POSITION WITH CUSTOM COLOUR // //Rectangle is hollow. X and Y positions mark the upper left corner of rectangle //As with all other draw calls x0 and y0 locations dependant on screen orientation // void LCD_Draw_Rectangle( uint16_t X, uint16_t Y, uint16_t Width, uint16_t Height, uint16_t Colour) { if ((X >= LCD_WIDTH) || (Y >= LCD_HEIGHT)) return; if ((X + Width - 1) >= LCD_WIDTH) { Width = LCD_WIDTH - X; } if ((Y + Height - 1) >= LCD_HEIGHT) { Height = LCD_HEIGHT - Y; } LCD_Set_Address(X, Y, X + Width - 1, Y + Height - 1); LCD_Draw_Colour_Burst(Colour, Height * Width); } //DRAW LINE FROM X,Y LOCATION to X+Width,Y LOCATION void LCD_Draw_Horizontal_Line( uint16_t X, uint16_t Y, uint16_t Width, uint16_t Colour) { if ((X >= LCD_WIDTH) || (Y >= LCD_HEIGHT)) return; if ((X + Width - 1) >= LCD_WIDTH) { Width = LCD_WIDTH - X; } LCD_Set_Address(X, Y, X + Width - 1, Y); LCD_Draw_Colour_Burst(Colour, Width); } //DRAW LINE FROM X,Y LOCATION to X,Y+Height LOCATION void LCD_Draw_Vertical_Line( uint16_t X, uint16_t Y, uint16_t Height, uint16_t Colour) { if ((X >= LCD_WIDTH) || (Y >= LCD_HEIGHT)) return; if ((Y + Height - 1) >= LCD_HEIGHT) { Height = LCD_HEIGHT - Y; } LCD_Set_Address(X, Y, X, Y + Height - 1); LCD_Draw_Colour_Burst(Colour, Height); } /*********************Partie de la Lib issue de LCD_GFX**************************/ /*Draw hollow circle at X,Y location with specified radius and colour. X and Y represent circles center */ void LCD_Draw_Hollow_Circle( uint16_t X, uint16_t Y, uint16_t Radius, uint16_t Colour) { int x = Radius - 1; int y = 0; int dx = 1; int dy = 1; int err = dx - (Radius << 1); while (x >= y) { LCD_Draw_Pixel(X + x, Y + y, Colour); LCD_Draw_Pixel(X + y, Y + x, Colour); LCD_Draw_Pixel(X - y, Y + x, Colour); LCD_Draw_Pixel(X - x, Y + y, Colour); LCD_Draw_Pixel(X - x, Y - y, Colour); LCD_Draw_Pixel(X - y, Y - x, Colour); LCD_Draw_Pixel(X + y, Y - x, Colour); LCD_Draw_Pixel(X + x, Y - y, Colour); if (err <= 0) { y++; err += dy; dy += 2; } if (err > 0) { x--; dx += 2; err += (-Radius << 1) + dx; } } } /*Draw filled circle at X,Y location with specified radius and colour. X and Y represent circles center */ void LCD_Draw_Filled_Circle( uint16_t X, uint16_t Y, uint16_t Radius, uint16_t Colour) { int x = Radius; int y = 0; int xChange = 1 - (Radius << 1); int yChange = 0; int radiusError = 0; while (x >= y) { for (int i = X - x; i <= X + x; i++) { LCD_Draw_Pixel(i, Y + y, Colour); LCD_Draw_Pixel(i, Y - y, Colour); } for (int i = X - y; i <= X + y; i++) { LCD_Draw_Pixel(i, Y + x, Colour); LCD_Draw_Pixel(i, Y - x, Colour); } y++; radiusError += yChange; yChange += 2; if (((radiusError << 1) + xChange) > 0) { x--; radiusError += xChange; xChange += 2; } } //Really slow implementation, will require future overhaul //TODO: https://stackoverflow.com/questions/1201200/fast-algorithm-for-drawing-filled-circles } /*Draw a hollow rectangle between positions X0,Y0 and X1,Y1 with specified colour*/ void LCD_Draw_Hollow_Rectangle_Coord( uint16_t X0, uint16_t Y0, uint16_t X1, uint16_t Y1, uint16_t Colour) { uint16_t X_length = 0; uint16_t Y_length = 0; uint8_t Negative_X = 0; uint8_t Negative_Y = 0; float Calc_Negative = 0; Calc_Negative = X1 - X0; if (Calc_Negative < 0) Negative_X = 1; Calc_Negative = 0; Calc_Negative = Y1 - Y0; if (Calc_Negative < 0) Negative_Y = 1; //DRAW HORIZONTAL! if (!Negative_X) { X_length = X1 - X0; } else { X_length = X0 - X1; } LCD_Draw_Horizontal_Line(X0, Y0, X_length, Colour); LCD_Draw_Horizontal_Line(X0, Y1, X_length, Colour); //DRAW VERTICAL! if (!Negative_Y) { Y_length = Y1 - Y0; } else { Y_length = Y0 - Y1; } LCD_Draw_Vertical_Line(X0, Y0, Y_length, Colour); LCD_Draw_Vertical_Line(X1, Y0, Y_length, Colour); if ((X_length > 0) || (Y_length > 0)) { LCD_Draw_Pixel(X1, Y1, Colour); } } /*Draw a filled rectangle between positions X0,Y0 and X1,Y1 with specified colour*/ void LCD_Draw_Filled_Rectangle_Coord( uint16_t X0, uint16_t Y0, uint16_t X1, uint16_t Y1, uint16_t Colour) { uint16_t X_length = 0; uint16_t Y_length = 0; uint8_t Negative_X = 0; uint8_t Negative_Y = 0; int32_t Calc_Negative = 0; uint16_t X0_true = 0; uint16_t Y0_true = 0; Calc_Negative = X1 - X0; if (Calc_Negative < 0) Negative_X = 1; Calc_Negative = 0; Calc_Negative = Y1 - Y0; if (Calc_Negative < 0) Negative_Y = 1; //DRAW HORIZONTAL! if (!Negative_X) { X_length = X1 - X0; X0_true = X0; } else { X_length = X0 - X1; X0_true = X1; } //DRAW VERTICAL! if (!Negative_Y) { Y_length = Y1 - Y0; Y0_true = Y0; } else { Y_length = Y0 - Y1; Y0_true = Y1; } LCD_Draw_Rectangle(X0_true, Y0_true, X_length, Y_length, Colour); } /*Draws a character (fonts imported from fonts.h) at X,Y location with specified font colour, size and Background colour*/ /*See fonts.h implementation of font on what is required for changing to a different font when switching fonts libraries*/ void LCD_Draw_Char( char Character, uint16_t X, uint16_t Y, uint16_t Colour, uint16_t Size, uint16_t Background_Colour) { uint8_t function_char; uint8_t i, j; function_char = Character; if (function_char < ' ') { Character = 0; } else { function_char -= 32; } char temp[CHAR_WIDTH]; for (uint8_t k = 0; k < CHAR_WIDTH; k++) { temp[k] = font[function_char][k]; } // Draw pixels LCD_Draw_Rectangle(X, Y, CHAR_WIDTH * Size, CHAR_HEIGHT * Size, Background_Colour); for (j = 0; j < CHAR_WIDTH; j++) { for (i = 0; i < CHAR_HEIGHT; i++) { if (temp[j] & (1 << i)) { if (Size == 1) { LCD_Draw_Pixel(X + j, Y + i, Colour); } else { LCD_Draw_Rectangle(X + (j * Size), Y + (i * Size), Size, Size, Colour); } } } } } /*Draws an array of characters (fonts imported from fonts.h) at X,Y location with specified font colour, size and Background colour*/ /*See fonts.h implementation of font on what is required for changing to a different font when switching fonts libraries*/ void LCD_Draw_Text( const char *Text, uint16_t X, uint16_t Y, uint16_t Colour, uint16_t Size, uint16_t Background_Colour) { while (*Text) { LCD_Draw_Char(*Text++, X, Y, Colour, Size, Background_Colour); X += CHAR_WIDTH * Size; } } /*Dessine une image dans une zone de l'ecran, aux coordonnées X et Y*/ //CONVERTISSEUR: http://www.digole.com/tools/PicturetoC_Hex_converter.php //65K colour (2Bytes / Pixel) //void LCD_Draw_Image_XY( // const char *Image_Array, // uint16_t X, // uint16_t Y, // uint16_t Width, // uint16_t Height) { // LCD_Set_Address(X, Y, X + Width - 1, Y + Height - 1); // // DATA // ; // CS_ON; // // for (uint32_t i = 0; i < Width * Height * 2; i += 2) { // while ((SPI3->SR & SPI_SR_TXE) == 0);//Si FIFO full (TX buffer Empty=0), on attend //// on utilise l'ecriture 16 bits dans DR, pour des envois 8 bits //// Le LSB doit etre place ds le MSB : // SPI3->DR = ((short) Image_Array[i + 1]) << 8 | Image_Array[i]; // } // // while ((SPI3->SR & SPI_SR_BSY) != 0);//Attendre fin envoi trame // CS_OFF; //} /*Draws a full screen picture from flash. Image converted from RGB .jpeg/other to C array using online converter*/ //USING CONVERTER: http://www.digole.com/tools/PicturetoC_Hex_converter.php //65K colour (2Bytes / Pixel) //void LCD_Draw_Image_Full(const char *Image_Array, uint8_t Orientation) { // switch (Orientation) { // case SCREEN_HORIZONTAL_1: // LCD_Set_Rotation(SCREEN_HORIZONTAL_1); // LCD_Set_Address(0, 0, LCD_SCREEN_WIDTH, LCD_SCREEN_HEIGHT); // break; // // case SCREEN_HORIZONTAL_2: // LCD_Set_Rotation(SCREEN_HORIZONTAL_2); // LCD_Set_Address(0, 0, LCD_SCREEN_WIDTH, LCD_SCREEN_HEIGHT); // break; // // case SCREEN_VERTICAL_1: // LCD_Set_Rotation(SCREEN_VERTICAL_1); // LCD_Set_Address(0, 0, LCD_SCREEN_HEIGHT, LCD_SCREEN_WIDTH); // break; // // case SCREEN_VERTICAL_2: // LCD_Set_Rotation(SCREEN_VERTICAL_2); // LCD_Set_Address(0, 0, LCD_SCREEN_HEIGHT, LCD_SCREEN_WIDTH); // break; // } // // DATA // ; // CS_ON; // // for (uint32_t i = 0; i < LCD_SCREEN_WIDTH * LCD_SCREEN_HEIGHT * 2; i += 2) { // while ((SPI3->SR & SPI_SR_TXE) == 0);//Si FIFO full (TX buffer Empty=0), on attend //// on utilise l'ecriture 16 bits dans DR, pour des envois 8 bits //// Le LSB doit etre place ds le MSB : // SPI3->DR = ((short) Image_Array[i + 1]) << 8 | Image_Array[i]; // } // // while ((SPI3->SR & SPI_SR_BSY) != 0);//Attendre fin envoi trame // CS_OFF; //}