This commit is contained in:
Roelandts_Gert
2023-11-20 12:54:53 +01:00
22 changed files with 40139 additions and 3186 deletions

3
.gitignore vendored
View File

@@ -57,4 +57,5 @@ project/project.launch
project/Scripts
Scripts/
project/project\ Debug.launch
project/project\ Debug.launch

View File

@@ -58,6 +58,7 @@ If your part needs documentation (e.g. how to use tcp cmd interface), add a mark
This folder contains the following documents:
- [llfs.md](docs/llfs.md): Linked List File System
- [lcd_api.md](docs/lcd_api.md): LCD API
- [logger.md](docs/logger.md): Logging and Debugging Messages
- [mkllfs.md](docs/mkllfs.md): Make Linked List File System
- [style_guide.md](docs/style_guide.md): Style Guide
- [style_guide.md](docs/style_guide.md): Style Guide

View File

@@ -2,12 +2,45 @@
## Introduction
The LCD API can be used to display BMP images or text onto the LCD screen.
At the moment of writing, only BMP's in C array's are supported.
The LCD API can be used to display text, BMP images and GIF animations onto the LCD screen.
Supported color schemes for BMP's are ARGB8888, RGB565, RGB888.
Displayed text that is exceeds the LCD width size, will be wrapped onto the next line and a '-' character will be injected if the wrap occurs within a word.
Displayed text that exceeds the LCD width will be wrapped onto the next line,
and a '-' character will be injected if the wrap occurs within a word.
## Table of contents
- [Introduction](#introduction)
- [Table of contents](#table-of-contents)
- [Usage of LCD API](#usage-of-lcd-api)
- [Generating BMP C header files](#generating-bmp-c-header-files)
- [Resizing images](#resizing-images)
- [Generating the C array](#generating-the-c-array)
- [Using the LCD API](#using-the-lcd-api)
- [Initialization of LCD API](#initialization-of-lcd-api)
- [The lcd task](#the-lcd-task)
- [Drawing text on the screen](#drawing-text-on-the-screen)
- [Drawing a 'raw' image onto the screen](#drawing-a-raw-image-onto-the-screen)
- [Drawing a BMP image onto the screen](#drawing-a-bmp-image-onto-the-screen)
- [Drawing a BMP from the filesystem to the LCD](#drawing-a-bmp-from-the-filesystem-to-the-lcd)
- [Drawing a GIF image/animation](#drawing-a-gif-imageanimation)
- [Stopping a GIF animation](#stopping-a-gif-animation)
- [Checking if a GIF is still running](#checking-if-a-gif-is-still-running)
- [Clearing the LCD screen](#clearing-the-lcd-screen)
## Usage of LCD API
### Generating BMP images
The LCD API supports BMP3 images in the following color-formats: ARGB8888, RGB888, RGB565.
* RLE is not supported
* Pixel data should be stored from the bottom up
* Non-square pixels are not supported
* Use of a color palette/table is not supported
To convert an image to BMP3, one can use an image editor such as Photoshop, GIMP or ImageMagick.
For example, using ImageMagick:
```bash
magick in.png -resize 50 BMP3:out.bmp # Resize to a width of 50 pixels
```
See the [Imagick documentation](https://imagemagick.org/script/convert.php) for more information.
### Generating BMP C header files
#### Resizing images
To resize an image to the desired size (width and height), one can use an image editor such as Photoshop, GIMP, ImageMagick, ... .
@@ -26,7 +59,7 @@ The BMP3 option is used to convert the PNG to Bitmap version 3.0 (note that the
#### Generating the C array
To easily generate a BMP C array (in the desired color scheme) from an image, [lv_img_conv](https://github.com/lvgl/lv_img_conv) can be used.
See the installation instructions on Github or use the [online version](https://lvgl.io/tools/imageconverter).
See the installation instructions on GitHub or use the [online version](https://lvgl.io/tools/imageconverter).
Example:
```bash
@@ -53,6 +86,21 @@ void main(void) {
}
```
#### The lcd task
The `lcd_task()` function should be called in the main loop of the application. This function is responsible for drawing the GIF animations.
```c
#include "lcd_api.h"
void main(void) {
...
lcd_init(true);
...
for(;;) {
lcd_task();
}
}
```
#### Drawing text on the screen
```c
@@ -73,9 +121,9 @@ void main(void) {
```
Display text on the LCD screen in a certain color. When text width exceeds BSP_LCD_GetXSize(), a text wrap will be performed. If the text wrap is between two will be injected.
#### Drawing a BMP (C-array) onto the screen
#### Drawing a 'raw' image onto the screen
```c
void lcd_draw_bmp(const void* p_src, uint32_t x_pos, uint32_t y_pos, uint32_t x_size, uint32_t y_size, uint32_t color_mode);
void lcd_draw_raw_img(const void* p_src, uint32_t x_pos, uint32_t y_pos, uint32_t x_size, uint32_t y_size, uint32_t color_mode);
```
```c
@@ -86,7 +134,121 @@ void main(void) {
...
lcd_init(true);
...
lcd_draw_bmp(bmp_array, 0, 0, 50, 50, LCD_ARGB8888);
lcd_draw_raw_img(raw_img_array, 0, 0, 50, 50, LCD_ARGB8888);
}
```
Draw BMP image from C array to the LCD screen at position X, Y. In color mode ARGB8888, RGB888, RGB565 or ARGB1555 Supports ARGB8888, RGB565, RGB888 (see LCD_* defines in header file).
Draw raw image from a C array to the LCD screen at position X, Y. In color mode ARGB8888, RGB888, RGB565 or ARGB1555 Supports ARGB8888, RGB565, RGB888 (see LCD_* defines in header file).
Note that when an image exceeds the X or Y size of the LCD display, the image will go out of the visible LCD area.
#### Drawing a BMP image onto the screen
```c
void lcd_draw_bmp_img(uint8_t* bmp_buff, uint32_t x_pos, uint32_t y_pos);
```
```c
#include "lcd_api.h"
#include "test_image.h"
void main(void) {
...
lcd_init(true);
...
lcd_draw_bmp_img(bmp_array, 0, 0);
lcd_draw_bmp_img(file->data, 50, 50);
}
```
Drawing a BMP image (either from filesystem or a C-array) to the LCD screen at position X, Y. The image size and color scheme do not have to be passed as arguments like in the `lcd_draw_raw_img(...)` function, these values are present in the BMP header.
Note that when an image exceeds the X or Y size of the LCD display, the image will go out of the visible LCD area.
#### Drawing a BMP from the filesystem to the LCD
```c
void lcd_draw_img_from_fs(const char* name, uint32_t x_pos, uint32_t y_pos);
```
```c
#include "lcd_api.h"
void main(void) {
...
lcd_init(true);
...
lcd_draw_img_from_fs("st.bmp", 0, 0);
}
```
This function expects the name of the BMP image as argument, together with the X and Y coordinates.
Note that this function only works for BMP images and not raw images and when an image exceeds the X or Y size of the LCD display, the image will go out of the visible LCD area.
#### Drawing a GIF image/animation
```c
lcd_gif_t* lcd_draw_gif(uint8_t* src, size_t size, uint32_t x_pos, uint32_t y_pos);
lcd_gif_t* lcd_draw_gif_from_fs(const char* name, uint32_t x_pos, uint32_t y_pos);
```
```c
#include "lcd_api.h"
void main(void) {
...
lcd_init(true);
...
// From a C-array
lcd_gif_t* gif = lcd_draw_gif(gif_array, gif_size, 0, 0);
// From the filesystem
lcd_gif_t* gif = lcd_draw_gif_from_fs("st.gif", 0, 0);
if (gif == NULL) {
LOG_WARNING("GIF could not be drawn");
}
...
for(;;) {
lcd_task();
}
}
```
This function expects a GIF image as argument, together with the X and Y coordinates.
The size argument is the size of the GIF image in bytes.
The function returns a handle for the GIF image, which can be used to stop the animation.
If the GIF has a loop count specified,
it will stop after the specified number of loops and the lcd_gif_t handle will be invalid for further use.
Otherwise, the GIF will loop indefinitely.
Before drawing over the GIF, the GIF should be stopped by calling `lcd_gif_stop(gif)`.
#### Stopping a GIF animation
```c
void lcd_stop_gif(lcd_gif_t* gif);
```
This function stops the GIF animation and frees the memory allocated for the GIF.
Call this function before drawing over the GIF.
This function should not be called on a GIF that has already been stopped (GIFs with a loop count will stop automatically).
It is possible that the handler has been assigned to a new GIF, so it would stop the new GIF instead.
#### Checking if a GIF is still running
```c
bool lcd_gif_is_playing(lcd_gif_t* gif);
```
NOTE: It is possible that the GIF has stopped playing, but another GIF has taken its slot and is still playing.
#### Clearing the LCD screen
```c
void lcd_clear(uint32_t color);
```
```c
#include "lcd_api.h"
void main(void) {
...
lcd_init(true);
...
lcd_clear(LCD_BLACK);
}
```
Clears the LCD screen to the specified color.

View File

@@ -7,6 +7,8 @@ The llfs filesystem is a flat filesystem, meaning that it does not support direc
The mkllfs utilit can be used to generate the `llfs_data.c` file. The `llfs_data.c` file from a directory with files.
A pre-compiled version can be download: [mkllfs.exe](https://github.com/Sani7/2023-Webservices_And_Applications/releases/tag/v0.2.0)
## Usage
The mkllfs utility can be used as follows:
```bash
@@ -22,6 +24,10 @@ Available options:
-v, --version print the version of mkllfs and the llfs library that it generates for
```
### Using the batch file
The `mkllfs.bat` file can be used to generate the `llfs_data.c` file from the data in the `llfs-data` directory.
Before using this script, place mkllfs.exe in the same directory as the batch file.
## Building
The mkllfs utility can be built using the following command:
```bash
@@ -30,4 +36,4 @@ gcc -o mkllfs mkllfs.c
For windows, you can use the following command:
```bash
gcc -o mkllfs.exe mkllfs.c
```
```

BIN
llfs-data/color_bar.bmp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

BIN
llfs-data/pacman.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

BIN
llfs-data/panda.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 522 KiB

BIN
llfs-data/st.bmp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

1
llfs-data/test.txt Normal file
View File

@@ -0,0 +1 @@
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer penatibus lorem nunc semper venenatis vestibulum urna. Hac elit inceptos mattis integer sollicitudin pellentesque parturient. Est fermentum laoreet class egestas odio tellus condimentum.

1
llfs-data/test2.txt Normal file
View File

@@ -0,0 +1 @@
Aliquet pellentesque morbi fermentum dolor fermentum auctor lacinia. Montes euismod habitasse fringilla praesent sodales metus interdum. Lacinia vel duis cum auctor class commodo adipiscing. Elementum condimentum taciti iaculis metus ornare duis curae. Risus consectetur urna convallis metus fusce arcu class. Nisl himenaeos vel praesent laoreet taciti himenaeos lacus. Duis pharetra aptent phasellus sodales imperdiet aptent elit.

2
mkllfs.cmd Normal file
View File

@@ -0,0 +1,2 @@
mkllfs.exe llfs-data project/Core/Src/llfs_data.c
pause

View File

@@ -1,7 +1,7 @@
/**
* @file main.c
* @brief Converts files to a C file that can be used by llfs (linked list file system).
* @version 0.1.0
* @version 0.2.0
* @author Lorenz C.
*/
@@ -10,8 +10,8 @@
#include <string.h>
#include "tinydir.h"
#define VERSION "0.1.0"
#define LLFS_VERSION "0.1.0"
#define VERSION "0.2.0"
#define LLFS_VERSION "0.1.1"
#define MAX_PATH_LEN 256
static void file_name_to_llfs_name(char* llfs_name, const char* file_name);
@@ -99,6 +99,7 @@ int main(int argc, char** argv) {
// Write the file data to the output file
fprintf(out_file, "// File: %s\n", file.name);
fprintf(out_file, "__attribute__((section(\".ext_qspi_flash\")))\n");
fprintf(out_file, "const uint8_t %s_data[] = {\n", llfs_name);
for (int i = 0, c; (c = fgetc(src_file)) != EOF; i++) {
file_size++;

View File

@@ -94,9 +94,9 @@
<fileInfo id="com.st.stm32cube.ide.mcu.gnu.managedbuild.config.exe.debug.1923792012.2015795402" name="test_data.h" rcbsApplicability="disable" resourcePath="Core/Inc/test_data.h" toolsToInvoke=""/>
<sourceEntries>
<entry excluding="Inc/fsdata_custom.c" flags="VALUE_WORKSPACE_PATH|RESOLVED" kind="sourcePath" name="Core"/>
<entry flags="VALUE_WORKSPACE_PATH|RESOLVED" kind="sourcePath" name="Drivers"/>
<entry flags="VALUE_WORKSPACE_PATH|RESOLVED" kind="sourcePath" name="LWIP"/>
<entry flags="VALUE_WORKSPACE_PATH|RESOLVED" kind="sourcePath" name="Middlewares"/>
<entry flags="VALUE_WORKSPACE_PATH|RESOLVED" kind="sourcePath" name="Drivers"/>
</sourceEntries>
</configuration>
</storageModule>

224
project/Core/Inc/gifdec.h Normal file
View File

@@ -0,0 +1,224 @@
// Copyright 2020 BitBank Software, Inc. All Rights Reserved.
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//===========================================================================
#ifndef __ANIMATEDGIF__
#define __ANIMATEDGIF__
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <stdlib.h>
#define memcpy_P memcpy
#define PROGMEM
//
// GIF Animator
// Written by Larry Bank
// Copyright (c) 2020 BitBank Software, Inc.
// bitbank@pobox.com
//
// Designed to decode images up to 480x320
// using less than 22K of RAM
//
/* GIF Defines and variables */
#define MAX_CHUNK_SIZE 255
//
// These 2 macros can be changed to limit the amount of RAM
// required by the decoder. For example, decoding 1-bit images to
// a 128x32 display will not need a max code size of 12 nor a palette
// with 256 entries
//
#define MAX_CODE_SIZE 12
#define MAX_COLORS 256
#define LZW_BUF_SIZE (6*MAX_CHUNK_SIZE)
#define LZW_HIGHWATER (4*MAX_CHUNK_SIZE)
#ifdef __LINUX__
#define MAX_WIDTH 2048
#else
#define MAX_WIDTH 320
#endif // __LINUX__
// This buffer is used to store the pixel sequence in reverse order
// it needs to be large enough to hold the longest possible
// sequence (1<<MAX_CODE_SIZE)
#define FILE_BUF_SIZE (1<<MAX_CODE_SIZE)
#define PIXEL_FIRST 0
#define PIXEL_LAST (1<<MAX_CODE_SIZE)
#define LINK_UNUSED 5911 // 0x1717 to use memset
#define LINK_END 5912
#define MAX_HASH 5003
enum {
GIF_PALETTE_RGB565_LE = 0, // little endian (default)
GIF_PALETTE_RGB565_BE, // big endian
GIF_PALETTE_RGB888 // original 24-bpp entries
};
// for compatibility with older code
#define LITTLE_ENDIAN_PIXELS GIF_PALETTE_RGB565_LE
#define BIG_ENDIAN_PIXELS GIF_PALETTE_RGB565_BE
//
// Draw callback pixel type
// RAW = 8-bit palettized pixels requiring transparent pixel handling
// COOKED = 16 or 24-bpp fully rendered pixels ready for display
//
enum {
GIF_DRAW_RAW = 0,
GIF_DRAW_COOKED
};
enum {
GIF_SUCCESS = 0,
GIF_DECODE_ERROR,
GIF_TOO_WIDE,
GIF_INVALID_PARAMETER,
GIF_UNSUPPORTED_FEATURE,
GIF_FILE_NOT_OPEN,
GIF_EARLY_EOF,
GIF_EMPTY_FRAME,
GIF_BAD_FILE,
GIF_ERROR_MEMORY
};
typedef struct gif_file_tag
{
int32_t iPos; // current file position
int32_t iSize; // file size
uint8_t *pData; // memory file pointer
void * fHandle; // class pointer to File/SdFat or whatever you want
} GIFFILE;
typedef struct gif_info_tag
{
int32_t iFrameCount; // total frames in file
int32_t iDuration; // duration of animation in milliseconds
int32_t iMaxDelay; // maximum frame delay
int32_t iMinDelay; // minimum frame delay
} GIFINFO;
typedef struct gif_draw_tag
{
int iX, iY; // Corner offset of this frame on the canvas
int y; // current line being drawn (0 = top line of image)
int iWidth, iHeight; // size of this frame
void *pUser; // user supplied pointer
uint8_t *pPixels; // 8-bit source pixels for this line
uint16_t *pPalette; // little or big-endian RGB565 palette entries (default)
uint8_t *pPalette24; // RGB888 palette (optional)
uint8_t ucTransparent; // transparent color
uint8_t ucHasTransparency; // flag indicating the transparent color is in use
uint8_t ucDisposalMethod; // frame disposal method
uint8_t ucBackground; // background color
uint8_t ucIsGlobalPalette; // Flag to indicate that a global palette, rather than a local palette is being used
} GIFDRAW;
// Callback function prototypes
typedef int32_t (GIF_READ_CALLBACK)(GIFFILE *pFile, uint8_t *pBuf, int32_t iLen);
typedef int32_t (GIF_SEEK_CALLBACK)(GIFFILE *pFile, int32_t iPosition);
typedef void (GIF_DRAW_CALLBACK)(GIFDRAW *pDraw);
typedef void * (GIF_OPEN_CALLBACK)(const char *szFilename, int32_t *pFileSize);
typedef void (GIF_CLOSE_CALLBACK)(void *pHandle);
typedef void * (GIF_ALLOC_CALLBACK)(uint32_t iSize);
typedef void (GIF_FREE_CALLBACK)(void *buffer);
//
// our private structure to hold a GIF image decode state
//
typedef struct gif_image_tag
{
int iWidth, iHeight, iCanvasWidth, iCanvasHeight;
int iX, iY; // GIF corner offset
int iBpp;
int iError; // last error
int iFrameDelay; // delay in milliseconds for this frame
int iRepeatCount; // NETSCAPE animation repeat count. 0=forever
int iXCount, iYCount; // decoding position in image (countdown values)
int iLZWOff; // current LZW data offset
int iLZWSize; // current quantity of data in the LZW buffer
int iCommentPos; // file offset of start of comment data
short sCommentLen; // length of comment
GIF_READ_CALLBACK *pfnRead;
GIF_SEEK_CALLBACK *pfnSeek;
GIF_DRAW_CALLBACK *pfnDraw;
GIF_OPEN_CALLBACK *pfnOpen;
GIF_CLOSE_CALLBACK *pfnClose;
GIFFILE GIFFile;
void *pUser;
unsigned char *pFrameBuffer;
unsigned char *pPixels, *pOldPixels;
unsigned char ucLineBuf[MAX_WIDTH]; // current line
unsigned char ucFileBuf[FILE_BUF_SIZE]; // holds temp data and pixel stack
unsigned short pPalette[(MAX_COLORS * 3)/2]; // can hold RGB565 or RGB888 - set in begin()
unsigned short pLocalPalette[(MAX_COLORS * 3)/2]; // color palettes for GIF images
unsigned char ucLZW[LZW_BUF_SIZE]; // holds 6 chunks (6x255) of GIF LZW data packed together
unsigned short usGIFTable[1<<MAX_CODE_SIZE];
unsigned char ucGIFPixels[(PIXEL_LAST*2)];
unsigned char bEndOfFrame;
unsigned char ucGIFBits, ucBackground, ucTransparent, ucCodeStart, ucMap, bUseLocalPalette;
unsigned char ucPaletteType; // RGB565 or RGB888
unsigned char ucDrawType; // RAW or COOKED
} GIFIMAGE;
#ifdef __cplusplus
//
// The GIF class wraps portable C code which does the actual work
//
class AnimatedGIF
{
public:
int open(uint8_t *pData, int iDataSize, GIF_DRAW_CALLBACK *pfnDraw);
int openFLASH(uint8_t *pData, int iDataSize, GIF_DRAW_CALLBACK *pfnDraw);
int open(const char *szFilename, GIF_OPEN_CALLBACK *pfnOpen, GIF_CLOSE_CALLBACK *pfnClose, GIF_READ_CALLBACK *pfnRead, GIF_SEEK_CALLBACK *pfnSeek, GIF_DRAW_CALLBACK *pfnDraw);
void close();
void reset();
void begin(unsigned char ucPaletteType = GIF_PALETTE_RGB565_LE);
void begin(int iEndian, unsigned char ucPaletteType) { begin(ucPaletteType); };
int playFrame(bool bSync, int *delayMilliseconds, void *pUser = NULL);
int getCanvasWidth();
int allocFrameBuf(GIF_ALLOC_CALLBACK *pfnAlloc);
int setDrawType(int iType);
int freeFrameBuf(GIF_FREE_CALLBACK *pfnFree);
uint8_t *getFrameBuf();
int getCanvasHeight();
int getLoopCount();
int getInfo(GIFINFO *pInfo);
int getLastError();
int getComment(char *destBuffer);
private:
GIFIMAGE _gif;
};
#else
// C interface
int GIF_openRAM(GIFIMAGE *pGIF, uint8_t *pData, int iDataSize, GIF_DRAW_CALLBACK *pfnDraw);
int GIF_openFile(GIFIMAGE *pGIF, const char *szFilename, GIF_DRAW_CALLBACK *pfnDraw);
void GIF_close(GIFIMAGE *pGIF);
void GIF_begin(GIFIMAGE *pGIF, unsigned char ucPaletteType);
void GIF_reset(GIFIMAGE *pGIF);
int GIF_playFrame(GIFIMAGE *pGIF, int *delayMilliseconds, void *pUser);
int GIF_getCanvasWidth(GIFIMAGE *pGIF);
int GIF_getCanvasHeight(GIFIMAGE *pGIF);
int GIF_getComment(GIFIMAGE *pGIF, char *destBuffer);
int GIF_getInfo(GIFIMAGE *pGIF, GIFINFO *pInfo);
int GIF_getLastError(GIFIMAGE *pGIF);
int GIF_getLoopCount(GIFIMAGE *pGIF);
#endif // __cplusplus
// Due to unaligned memory causing an exception, we have to do these macros the slow way
#define INTELSHORT(p) ((*p) + (*(p+1)<<8))
#define INTELLONG(p) ((*p) + (*(p+1)<<8) + (*(p+2)<<16) + (*(p+3)<<24))
#define MOTOSHORT(p) (((*(p))<<8) + (*(p+1)))
#define MOTOLONG(p) (((*p)<<24) + ((*(p+1))<<16) + ((*(p+2))<<8) + (*(p+3)))
// Must be a 32-bit target processor
#define REGISTER_WIDTH 32
#endif // __ANIMATEDGIF__

View File

@@ -2,6 +2,7 @@
* @file lcd_api.h
* @brief API for LCD functionality
* @author Tim S.
* @author Lorenz C.
*/
#ifndef INC_LCD_API_H_
@@ -13,6 +14,14 @@
#define LOGGER_LEVEL_ALL
#include "log.h"
#include "../../Drivers/BSP/STM32746G-Discovery/stm32746g_discovery_lcd.h"
#include "llfs.h"
#include "gifdec.h"
/**
* @brief The maximum amount of GIFs that can be displayed at the same time
* @note This can't be higher than 255 (uint8_t)
*/
#define LCD_MAX_GIFS 5
#define LCD_BLUE LCD_COLOR_BLUE
#define LCD_GREEN LCD_COLOR_GREEN
@@ -46,15 +55,26 @@
#define LCD_RGB565 0x00000002U
#define LCD_ARGB1555 0x00000003U
#define LCD_FONT8 &Font8
#define LCD_FONT12 &Font12
#define LCD_FONT16 &Font16
#define LCD_FONT20 &Font20
#define LCD_FONT24 &Font24
#define LCD_FONT8 (&Font8)
#define LCD_FONT12 (&Font12)
#define LCD_FONT16 (&Font16)
#define LCD_FONT20 (&Font20)
#define LCD_FONT24 (&Font24)
extern LTDC_HandleTypeDef hLtdcHandler;
typedef struct {
GIFIMAGE gif;
uint8_t* src;
uint32_t x_pos;
uint32_t y_pos;
uint32_t last_frame_time;
int loop_count;
int cur_loop;
int frame_delay;
} lcd_gif_t;
/**
* @brief Initialise LCD
* Initialise the LCD screen with BackLight on or not
@@ -64,6 +84,12 @@ extern LTDC_HandleTypeDef hLtdcHandler;
*/
void lcd_init(bool bl_on);
/**
* @brief LCD task
* Task to be called in the main loop to update the LCD screen
*/
void lcd_task(void);
/**
* @brief Display text
* Display text on the LCD screen in a certain color. When text width exceeds BSP_LCD_GetXSize(),
@@ -93,6 +119,83 @@ void lcd_display_text(uint8_t* text, uint16_t x_pos, uint16_t y_pos, uint32_t co
* @param[in] y_size Height of image
* @param[in] color_mode Color mode (see defined color modes above in file)
*/
void lcd_draw_bmp(const void* p_src, uint32_t x_pos, uint32_t y_pos, uint32_t x_size, uint32_t y_size, uint32_t color_mode);
void lcd_draw_raw_img(const void* p_src, uint32_t x_pos, uint32_t y_pos, uint32_t x_size, uint32_t y_size, uint32_t color_mode);
/**
* @brief Draw BMP image on screen
* Draw BMP image from C array to the LCD screen at position X, Y. In color mode ARGB8888, RGB888, RGB565 or ARGB1555
* Supports ARGB8888, RGB565, RGB888
*
* @param[in] bmp_buff BMP file data
* @param[in] x_pos X-position
* @param[in] y_pos Y-position
*/
void lcd_draw_bmp_img(uint8_t* bmp_buff, uint32_t x_pos, uint32_t y_pos);
/**
* @brief Draw BMP image on screen by specifying the name, the BMP image has in the file system
* Draw BMP image from C array to the LCD screen at position X, Y by specifying the BMP image name on the filesystem
* Supports ARGB8888, RGB565, RGB888
*
* @param[in] name Name of image on filesystem
* @param[in] x_pos X-position
* @param[in] y_pos Y-position
*/
void lcd_draw_img_from_fs(const char* name, uint32_t x_pos, uint32_t y_pos);
/**
* @brief Clear LCD screen
* Clears the whole LCD screen to the desired color
*
*@param[in] color Color to which the LCD should be cleared
*/
void lcd_clear(uint32_t color);
/**
* @brief Draw GIF image on screen from memory
* Draw GIF image from memory to the LCD screen at position X, Y
* @warning If the GIF has a loop count specified, it will stop after the specified amount of loops and the lcd_gif_t handle will be invalid for further use
* @note Before drawing over a GIF, make sure to call lcd_stop_gif(), otherwise the GIF will keep overwriting the screen
*
* @param src Pointer to the GIF image data
* @param size The size of the GIF image data
* @param x_pos The X position on the screen
* @param y_pos The Y position on the screen
* @return A handle to the GIF image, NULL if the GIF could not be opened @ref lcd_gif_t
*/
lcd_gif_t* lcd_draw_gif(uint8_t* src, size_t size, uint32_t x_pos, uint32_t y_pos);
/**
* @brief Draw GIF image on screen from filesystem
* Draw GIF image from filesystem to the LCD screen at position X, Y
* @warning If the GIF has a loop count specified, it will stop after the specified amount of loops and the lcd_git_t handle will be invalidated
* @note Before drawing over a GIF, make sure to call lcd_stop_gif(), otherwise the GIF will keep overwriting the screen
*
* @param name The filename of the GIF image
* @param x_pos The X position on the screen
* @param y_pos The Y position on the screen
* @return A handle to the GIF image, NULL if the GIF could not be opened @ref lcd_gif_t
*/
lcd_gif_t* lcd_draw_gif_from_fs(const char* name, uint32_t x_pos, uint32_t y_pos);
/**
* @brief Stop a GIF from playing
* Frees the GIF slot and stops the GIF from playing
* Call this function before trying to draw over a GIF
* @warning Make sure the GIF is still playing, otherwise it might stop another GIF
*
* @param gif The handle to the GIF image @ref lcd_gif_t
*/
void lcd_stop_gif(lcd_gif_t* gif);
/**
* @brief Check if a GIF is still playing
* @note It is possible that the GIF has stopped playing, but another GIF has taken its slot and is still playing
*
* @param gif The handle to the GIF image @ref lcd_gif_t
* @return True if the GIF is still playing, false if not
*/
bool lcd_gif_is_playing(lcd_gif_t* gif);
#endif /* INC_LCD_API_H_ */

1014
project/Core/Src/gifdec.c Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -2,20 +2,27 @@
* @file lcd_api.c
* @brief LCD API implementation
* @author Tim S.
* @todo Implement function to read images from fs
* @author Lorenz C.
*/
#include "lcd_api.h"
static const char* TAG = "lcd_api";
static DMA2D_HandleTypeDef hDma2dHandler2;
static lcd_gif_t gifs[LCD_MAX_GIFS]; // Array of GIF slots
static lcd_gif_t* get_free_gif_slot(void);
static void gif_draw_cb(GIFDRAW* pDraw);
static inline void free_gif_slot(lcd_gif_t* gif);
void lcd_init(bool bl_on) {
LOG_INFO(TAG, "Init LCD");
BSP_LCD_Init();
BSP_LCD_LayerDefaultInit(1, LCD_FB_START_ADDRESS);
BSP_LCD_LayerDefaultInit(0, LCD_FB_START_ADDRESS + (BSP_LCD_GetXSize()*BSP_LCD_GetYSize()*4));
BSP_LCD_LayerDefaultInit(0, LCD_FB_START_ADDRESS + (BSP_LCD_GetXSize() * BSP_LCD_GetYSize() * 4));
BSP_LCD_SelectLayer(0);
BSP_LCD_Clear(LCD_COLOR_BLACK);
BSP_LCD_SelectLayer(1);
@@ -23,13 +30,57 @@ void lcd_init(bool bl_on) {
if (bl_on) {
HAL_GPIO_WritePin(GPIOK, GPIO_PIN_3, GPIO_PIN_SET);
HAL_GPIO_WritePin(GPIOI, GPIO_PIN_12, GPIO_PIN_SET);
} else {
HAL_GPIO_WritePin(GPIOK, GPIO_PIN_3, GPIO_PIN_RESET);
HAL_GPIO_WritePin(GPIOI, GPIO_PIN_12, GPIO_PIN_RESET);
return;
}
HAL_GPIO_WritePin(GPIOK, GPIO_PIN_3, GPIO_PIN_RESET);
HAL_GPIO_WritePin(GPIOI, GPIO_PIN_12, GPIO_PIN_RESET);
}
void lcd_task(void) {
// Play the next frame of each GIF if it's time
for (uint8_t i = 0; i < LCD_MAX_GIFS; i++) {
int ret;
// Check if the GIF is in use
if (gifs[i].src == NULL) {
continue;
}
// Check if it's time to play the next frame
if (HAL_GetTick() - gifs[i].last_frame_time < gifs[i].frame_delay) {
continue;
}
// Play the next frame
ret = GIF_playFrame(&gifs[i].gif, &gifs[i].frame_delay, &gifs[i].gif);
if (ret == 0) { // No more frames
// Infinite loop
if (gifs[i].loop_count <= 0) {
GIF_reset(&gifs[i].gif);
continue;
}
// Loop again
if (gifs[i].cur_loop < gifs[i].loop_count) {
GIF_reset(&gifs[i].gif);
gifs[i].cur_loop++;
continue;
}
// No more loops, free slot
LOG_DEBUG(TAG, "GIF finished, freeing slot");
free_gif_slot(&gifs[i]);
} else if (ret == -1) { // Error
LOG_WARN(TAG, "GIF_playFrame error, freeing slot");
free_gif_slot(&gifs[i]);
}
// Update the last frame time
gifs[i].last_frame_time = HAL_GetTick();
}
}
void lcd_display_text(uint8_t* text, uint16_t x_pos, uint16_t y_pos, uint32_t color, uint32_t bg_color, sFONT *font) {
void lcd_display_text(uint8_t* text, uint16_t x_pos, uint16_t y_pos, uint32_t color, uint32_t bg_color, sFONT* font) {
LOG_INFO(TAG, "Display text: %s @x=%d,y=%d", text, x_pos, y_pos);
uint16_t tot_length = x_pos + (strlen(text) * font->Width);
@@ -43,33 +94,31 @@ void lcd_display_text(uint8_t* text, uint16_t x_pos, uint16_t y_pos, uint32_t co
if (tot_length > BSP_LCD_GetXSize()) {
for (int i = 0; i < strlen(text); i++) {
if ((x_pos) > BSP_LCD_GetXSize() - (font->Width)*2) {
if (isalpha(text[i-1]) && isalpha(text[i])) {
if ((x_pos) > BSP_LCD_GetXSize() - (font->Width) * 2) {
if (isalpha(text[i - 1]) && isalpha(text[i])) {
BSP_LCD_DisplayChar(x_pos, y_pos, '-');
i -= 1;
} else {
BSP_LCD_DisplayChar(x_pos, y_pos, text[i]);
}
x_pos = 0;
y_pos += font->Height;
} else {
BSP_LCD_DisplayChar(x_pos, y_pos, text[i]);
x_pos += font->Width;
continue;
}
BSP_LCD_DisplayChar(x_pos, y_pos, text[i]);
x_pos += font->Width;
}
} else {
BSP_LCD_DisplayStringAt(x_pos, y_pos, text, LEFT_MODE);
return;
}
BSP_LCD_DisplayStringAt(x_pos, y_pos, text, LEFT_MODE);
}
void lcd_draw_bmp(const void* p_src, uint32_t x_pos, uint32_t y_pos, uint32_t x_size, uint32_t y_size, uint32_t color_mode) {
void lcd_draw_raw_img(const void* p_src, uint32_t x_pos, uint32_t y_pos, uint32_t x_size, uint32_t y_size, uint32_t color_mode) {
uint32_t address = hLtdcHandler.LayerCfg[1].FBStartAdress + (((BSP_LCD_GetXSize() * y_pos) + x_pos) * (4));
void* p_dst = (void*)address;
uint32_t address = hLtdcHandler.LayerCfg[1].FBStartAdress + (((BSP_LCD_GetXSize()*y_pos) + x_pos)*(4));
void *p_dst = (void *)address;
hDma2dHandler2.Init.Mode = DMA2D_M2M_PFC;
hDma2dHandler2.Init.ColorMode = DMA2D_ARGB8888;
hDma2dHandler2.Init.OutputOffset = BSP_LCD_GetXSize()-x_size;
hDma2dHandler2.Init.Mode = DMA2D_M2M_PFC;
hDma2dHandler2.Init.ColorMode = DMA2D_ARGB8888;
hDma2dHandler2.Init.OutputOffset = BSP_LCD_GetXSize() - x_size;
hDma2dHandler2.LayerCfg[1].AlphaMode = DMA2D_NO_MODIF_ALPHA;
hDma2dHandler2.LayerCfg[1].InputAlpha = 0xFF;
@@ -84,7 +133,7 @@ void lcd_draw_bmp(const void* p_src, uint32_t x_pos, uint32_t y_pos, uint32_t x_
}
LOG_INFO(TAG, "DMA2D config layer");
if (HAL_DMA2D_ConfigLayer(&hDma2dHandler2, 1) != HAL_OK) {
LOG_CRIT(TAG, "HAL_DMA2D_ConfigLayer error");
LOG_CRIT(TAG, "HAL_DMA2D_ConfigLayer error");
return;
}
LOG_INFO(TAG, "DMA2D start");
@@ -95,3 +144,145 @@ void lcd_draw_bmp(const void* p_src, uint32_t x_pos, uint32_t y_pos, uint32_t x_
LOG_INFO(TAG, "DMA2D poll");
HAL_DMA2D_PollForTransfer(&hDma2dHandler2, 10);
}
void lcd_draw_bmp_img(uint8_t* bmp_buff, uint32_t x_pos, uint32_t y_pos) {
BSP_LCD_DrawBitmap(x_pos, y_pos, bmp_buff);
}
void lcd_draw_img_from_fs(const char* name, uint32_t x_pos, uint32_t y_pos) {
llfs_file_t* file = llfs_file_open(name);
if (file != NULL) {
BSP_LCD_DrawBitmap(x_pos, y_pos, (uint8_t*)file->data);
return;
}
LOG_WARN(TAG, "File \"%s\" not found", file->name);
}
void lcd_clear(uint32_t color) {
BSP_LCD_Clear(color);
}
lcd_gif_t* lcd_draw_gif(uint8_t* src, size_t size, uint32_t x_pos, uint32_t y_pos) {
lcd_gif_t* gif;
// Get a free GIF slot
if ((gif = get_free_gif_slot()) == NULL) {
LOG_WARN(TAG, "No free GIF slots");
return NULL;
}
// Open the GIF and reset slot values
GIF_begin(&(gif->gif), GIF_PALETTE_RGB888);
if (GIF_openRAM(&(gif->gif), src, (int)size, gif_draw_cb)) {
gif->src = src;
gif->x_pos = x_pos;
gif->y_pos = y_pos;
gif->last_frame_time = 0;
gif->loop_count = GIF_getLoopCount(&(gif->gif));
gif->cur_loop = 1;
gif->frame_delay = 0;
gif->gif.ucDrawType = GIF_DRAW_RAW;
gif->gif.ucPaletteType = GIF_PALETTE_RGB888;
return gif;
}
// Failed to open GIF
LOG_WARN(TAG, "GIF_openRAM failed");
free_gif_slot(gif);
return NULL;
}
lcd_gif_t* lcd_draw_gif_from_fs(const char* name, uint32_t x_pos, uint32_t y_pos) {
lcd_gif_t* gif;
llfs_file_t* file;
// Get the file from llfs
file = llfs_file_open(name);
if (file == NULL) {
LOG_WARN(TAG, "File \"%s\" not found", name);
return NULL;
}
// Draw the GIF using the file data
gif = lcd_draw_gif((uint8_t*)file->data, file->len, x_pos, y_pos);
return gif;
}
void lcd_stop_gif(lcd_gif_t* gif) {
free_gif_slot(gif);
}
bool lcd_gif_is_playing(lcd_gif_t* gif) {
return gif->src != NULL;
}
/**
* @brief Get a free GIF slot
* Get the next GIF slot that is not in use
*
* @return Pointer to the GIF slot, NULL if none are available
*/
static lcd_gif_t* get_free_gif_slot(void) {
for (int i = 0; i < 5; i++) {
if (gifs[i].src == NULL) {
return &gifs[i];
}
}
return NULL;
}
/**
* @brief Free a GIF slot
* Release a GIF slot, and free the memory
*
* @param gif Pointer to the GIF slot to free
*/
static inline void free_gif_slot(lcd_gif_t* gif) {
gif->src = NULL;
GIF_close(&gif->gif);
}
/**
* @brief Callback function used by the GIF decoder
* This function is called by the GIF decoder to draw a new line of pixels
* @todo See if it's possible to use DMA2D for this
*
* @param pDraw Pointer to the GIFDRAW struct
*/
static void gif_draw_cb(GIFDRAW* pDraw) {
lcd_gif_t* gif = (lcd_gif_t*)pDraw->pUser;
uint8_t* palette = pDraw->pPalette24; // The RGB888 color palette
uint8_t* p_src = pDraw->pPixels; // Source pixel pointer
int y = pDraw->iY + pDraw->y; // Current line being drawn
// Calculate the destination address of the first pixel in the line
uint32_t address = hLtdcHandler.LayerCfg[1].FBStartAdress + (((BSP_LCD_GetXSize() * gif->y_pos) + gif->x_pos) * 4)
+ (y * (BSP_LCD_GetXSize() * 4));
// Restore the background
if (pDraw->ucDisposalMethod == 2) {
for (int x = 0; x < pDraw->iWidth; x++) {
if (p_src[x] == pDraw->ucTransparent) {
p_src[x] = pDraw->ucBackground;
}
}
pDraw->ucHasTransparency = 0;
}
// Draw each pixel in the line
for (int x = 0; x < pDraw->iWidth; x++) {
uint8_t pixel = *p_src++;
// Skip transparent pixels
if (pDraw->ucHasTransparency && pixel == pDraw->ucTransparent) {
continue;
}
// Get the color from the palette and convert it to ARGB8888
uint8_t *p = palette + (pixel * 3);
uint32_t color = (0xFF << 24) | (p[0] << 16) | (p[1] << 8) | p[2];
// Draw the pixel
((uint32_t *)address)[x] = color;
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -23,9 +23,10 @@
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#define LOGGER_LEVEL_ALL
#include "../../Drivers/BSP/STM32746G-Discovery/stm32746g_discovery_qspi.h"
#include "../../Drivers/BSP/STM32746G-Discovery/stm32746g_discovery_lcd.h"
#include "log.h"
#include "llfs.h"
#include "../../Drivers/BSP/STM32746G-Discovery/stm32746g_discovery_lcd.h"
#include "lcd_api.h"
/* USER CODE END Includes */
@@ -113,11 +114,23 @@ int main(void)
MX_LWIP_Init();
MX_QUADSPI_Init();
/* USER CODE BEGIN 2 */
/* Initialize QSPI */
BSP_QSPI_Init();
BSP_QSPI_MemoryMappedMode();
WRITE_REG(QUADSPI->LPTR, 0xFFF);
// Clear terminal
printf(CLEAR_SCREEN);
/* Initialize the LCD */
lcd_init(true);
/* Initialize the filesystem */
llfs_init();
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
@@ -126,6 +139,7 @@ int main(void)
/* USER CODE BEGIN 3 */
MX_LWIP_Process();
lcd_task();
}
/* USER CODE END 3 */
}

View File

@@ -93,8 +93,6 @@
#define LWIP_HTTPD_SSI 1
/*----- Default Value for LWIP_HTTPD_SSI_RAW: 0 ---*/
#define LWIP_HTTPD_SSI_RAW 1
/*----- Default Value for LWIP_HTTPD_SUPPORT_POST: 0 ---*/
#define LWIP_HTTPD_SUPPORT_POST 1
/*----- Default Value for LWIP_HTTPD_CUSTOM_FILES: 0 ---*/
#define LWIP_HTTPD_CUSTOM_FILES 1
/*----- Value in opt.h for HTTPD_USE_CUSTOM_FSDATA: 0 -----*/

View File

@@ -46,6 +46,7 @@ MEMORY
{
RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 320K
FLASH (rx) : ORIGIN = 0x8000000, LENGTH = 1024K
QSPI (xrw) : ORIGIN = 0x90000000, LENGTH = 16M
}
/* Sections */
@@ -182,4 +183,9 @@ SECTIONS
}
.ARM.attributes 0 : { *(.ARM.attributes) }
.ext_qspi_flash :
{
*(.ext_qspi_flash)
} >QSPI
}

View File

@@ -43,7 +43,7 @@ LWIP.LWIP_HTTPD_CGI_SSI=1
LWIP.LWIP_HTTPD_CUSTOM_FILES=1
LWIP.LWIP_HTTPD_SSI=1
LWIP.LWIP_HTTPD_SSI_RAW=1
LWIP.LWIP_HTTPD_SUPPORT_POST=1
LWIP.LWIP_HTTPD_SUPPORT_POST=0
LWIP.LWIP_IGMP=1
LWIP.LWIP_TFTP=1
LWIP.MEMP_MEM_MALLOC=1