// 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. // #include "LCD_driver.h" void LCD_Write_Command(uint8_t Command) { while ((SPI1->SR & SPI_SR_BSY) != 0);// Wait that everything is sent before changing the RS pin CMD ; while ((SPI1->SR & SPI_SR_TXE) == 0); SPI1->DR = Command; } /* Send Data (char) to LCD via SPI bus */ void LCD_Write_Data(uint8_t Data) { while ((SPI1->SR & SPI_SR_BSY) != 0);// Wait that everything is sent before changing the RS pin DATA ; while ((SPI1->SR & SPI_SR_TXE) == 0); SPI1->DR = Data; } void LCD_Write_Data16(uint16_t data) { while ((SPI1->SR & SPI_SR_BSY) != 0);// Wait that everything is sent before changing the RS pin DATA ; while ((SPI1->SR & SPI_SR_TXE) == 0); SPI1->DR = (data >> 8) & 0xFF;// Send MSB while ((SPI1->SR & SPI_SR_TXE) == 0); 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) { LCD_Write_Command(0x2A); LCD_Write_Data16(X1); LCD_Write_Data16(X2); LCD_Write_Command(0x2B); LCD_Write_Data16(Y1 + 19); LCD_Write_Data16(Y2 + 19); LCD_Write_Command(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(150); } void LCD_Init(void) { LCD_HardwareReset(); // enable spi1 SPI1->CR1 |= SPI_CR1_SPE;// NSS (CS) pin is automatically pulled low HAL_Delay(300); // Software reset LCD_Write_Command(0x01); HAL_Delay(150); // Color mode: 16bit/pixels LCD_Write_Command(0x3A); LCD_Write_Data(0x55); HAL_Delay(150); // Enable color inversion (INVON) LCD_Write_Command(0x21); // Configure orientation stuff LCD_Write_Command(0x36); LCD_Write_Data(0b00111100); // Exit sleep LCD_Write_Command(0x11); HAL_Delay(150); // Turn on display LCD_Write_Command(0x29); HAL_Delay(400); // Fill white LCD_Fill_Screen(WHITE); // Draw colors columns LCD_Set_Address(30, 30, LCD_WIDTH - 30, LCD_HEIGHT - 30); uint32_t size = (LCD_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_WIDTH - 21, 1, 20, 20, GREEN); LCD_Draw_Rectangle(LCD_WIDTH - 21, LCD_HEIGHT - 21, 20, 20, BLACK); LCD_Draw_Rectangle(1, LCD_HEIGHT - 21, 20, 20, BLUE); } //INTERNAL FUNCTIONS OF THE LIBRARY void LCD_Draw_Colour_Burst(uint16_t color, uint32_t size) { while ((SPI1->SR & SPI_SR_BSY) != 0);// Wait that everything is sent before changing the RS pin DATA ; for (uint32_t j = 0; j < size; j++) { while ((SPI1->SR & SPI_SR_TXE) == 0); SPI1->DR = (color >> 8) & 0xFF; while ((SPI1->SR & SPI_SR_TXE) == 0); SPI1->DR = color & 0xFF; } } void LCD_Fill_Screen(uint16_t color) { LCD_Draw_Rectangle(0, 0, LCD_WIDTH, LCD_HEIGHT, color); } void LCD_Draw_Pixel(uint16_t x, uint16_t y, uint16_t color) { if ((x >= LCD_WIDTH) || (y >= LCD_HEIGHT)) return; LCD_Set_Address(x, y, x, y); LCD_Draw_Colour_Burst(color, 1); } 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); } 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); } 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); } 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; } } } 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 } 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); } } 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); } /*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; //}