prettyprint

2021年2月4日 星期四

Interface TFT display with STM32 in 16 bits parallel mode -- Display, Touch and Screenshot || STM32CubeIDE || HAL

 本實驗使用STM32F407VE(開發版STM32_F4VE)連接2.8inch 16BIT parallel-port Module(含 ILI9341、XPT2046與SPI SD card)。開發環境使用STM32CubeIDE與HAL library。

實驗目標:

  1. 顯示儲存在SD卡上JPEG image file, image width 無須小於TFT顯示寬度。
  2. Screenshot: 將TFT顯示內容儲存在SD卡上(RGB565 bitmap format)。
  3. TFT顯示方向改變時,修正Touch screen座標與TFT座標一致。
  4. pop-up message window。
  5. XPT2046與SPI SD Card使用同一個SPI介面,run time改變baud rate以適應慢速XPT2046與高速需求SD read/write。

模組圖片

  • 2.8 ' LCD Module:

  • 開發版STM32_F4VE

  • 線路連接:

使用FSMC、SPI2介面與LCD module連接。開發版TFT pin腳位如下:

TFT LCD header pins

#NameFunctionConnected to
1-GNDGround plane
2-RSTReset button
3-FSMC D15PD10
4-FSMC D14PD9
5-FSMC D13PD8
6-FSMC D12PE15
7-FSMC D11PE14
8-FSMC D10PE13
9-FSMC D9PE12
10-FSMC D8PE11
11-FSMC D7PE10
12-FSMC D6PE9
13-FSMC D5PE8
14-FSMC D4PE7
15-FSMC D3PD1
16-FSMC D2PD0
17-FSMC D1PD15
18-FSMC D0PD14
19-FSMC NOEPD4
20-FSMC NWEPD5
21-FSMC A18PD13
22-FSMC NE1PD7
23-Touch CLKPB13
24-Touch CSPB12
25-Touch MOSIPB15
26-Touch MISOPB14
27-Touch PENPC5
28-LCD BacklightPB1
29-VBATN.C.
30-GNDGround plane
31-3V3+3.3V rail
32-GNDGround plane

接線方式

STM32_F4VE Pin            LCD Module pin

31 3V3                            VDD
32 GND                          GND
19 NOE                           RD
20 NWE                          WR
21 A18                            RS
22 NE1                           CS
23 Touch CLK               CLK
24 Touch CS                  T_CS
25 Touch MOSI             MOSI
26 Touch MSIO             MISO
27 Touch PEN              PEN
28 LCD Backlight        BL
2 RST                            RST
D0~D15                        D0~D15

PC3                            SDCS

完成圖:



介面設定

1.FSMC: LCD Interface 16bit


2. SPI2介面:
  • Prescaler :128配合慢速Touch XPT2046,當要做SD  IO時再改變baud rate。
  • DMA,SPI2 pin與interrupt如下圖選擇。    

Middleware:使用FATFS與LIBJPEG。

FATFS與SPI SD存取檔案詳細運作,請參閱前篇

STM32微控制器(STM32F407VET6) SD-4bits、SD-SPI,FLASH等儲存設備管理


使用LIBJPEG library 處理jpeg image,但對於大的影像檔 LIBJPEG僅能縮小的倍數為1/1,1/2,1/4,1/8,所以當超過libjpeg最大縮小倍數時仍可能超過TFT最大寬度,因此需再二次處理。本實驗處理的照片檔如下:

程式碼說明:

  • 儲存TFT影像檔於SD卡(Screenshot):
因為LCD module 顯示為16bit RGB565模式,因此選擇儲存檔案的格式為Bitmap,RGB565 header設定,File Information header的bitCount:16 RGB565,Compression:BI_BITFIELDS=3,
RGB565 mask 為0x00f80000e00700001f000000。height為正數,因此從左下至右上依序讀取每個16bits pixel color value轉成low byte first寫入SD中。詳細程式碼請參閱bitmap.h, bitmap.c。
因為SPI SD讀寫需較高baud rate,更改baud rate prescale:
HAL_SPI_DeInit(&hspi2);
oldBraudRate = hspi2.Init.BaudRatePrescaler;
hspi2.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_2;
HAL_SPI_Init(&hspi2);

.
.
.

HAL_SPI_DeInit(&hspi2);
hspi2.Init.BaudRatePrescaler = oldBraudRate;
HAL_SPI_Init(&hspi2);


  • XPT2046 touch module
參考網址:https://github.com/taburyak/STM32-touchscreen-XPT2046-HAL-SPI。
改寫原來程式碼加入calibration與TFT螢幕轉向時touch point相對座標轉換。詳細程式碼請參閱XPT2046_touch.c, XPT2046_touch.h。
  • ILI9341 FSMC driver
參考網址:https://stm32withoutfear.blogspot.com/2019/09/stm32-ili9341-fsmc.html。
將ili9341, fontxx複製到src資料夾。

  • Display JPEG image files:
使用Middleware LIBJPEG,因開發板的SRAM只有192KiB,因此對於image width大於TFT display width的檔案先設定 
cinfo.scale_num 與cinfo.scale_denom調整縮小倍數,呼叫jpeg_read_scanlines才能使用較小量的memory,因為最大的縮小量為8倍,像本實驗05.jpg為4656X2620,縮小後仍大於TFT display width,為了能整個顯示,需再一次處理調整顯示再TFT display上的pixel。詳細程式碼請參閱jpeg_view.c,jpeg_view.h。
本實驗使用LCD module上的SPI介面SD,讀取速度較慢,因此對於7MB的05.jpg IO時間過長,較不具實用價值,因此在實用上須將SD改為開發版上SDIO介面。

  • screenshot demo
  • pop-up message window
浮動訊息視窗,設定顯示秒數,關閉視窗後,回存被視窗覆蓋的影像。詳細程式碼參閱showmessage.h,showmessage.c


完成測試影片





程式碼

bitmap.h
#ifndef INC_BITMAP_H_
#define INC_BITMAP_H_
enum compressionMethod {
	BI_RGB=0,
	BI_RLE8=1,
	BI_RLE4=2,
	BI_BITFIELDS=3,
	BI_JPEG=4,
	BI_PNG=5,
	BI_ALPHABITFIELDS=6
};
typedef struct tagBITMAPFILEHEADER
{
	uint16_t 	bfType;
	DWORD 		bfSize;	// file size
	uint16_t 	bfReserved1;
	uint16_t 	bfReserved2;
	DWORD 		bfOffBits;  //bitmap data offset
} BITMAPFILEHEADER;

typedef struct tagBITMAPINFOHEADER
{
	DWORD biSize;    // info header size
	LONG biWidth;
	LONG biHeight;
	WORD biPlanes;
	WORD biBitCount;	//1, 4, 8, 16, 24 or 32
	DWORD biCompression; //0:BI_RGB, 1:BI_RLE8, 2: BI_RLE4, 3:BI_BITFIELDS, 4:BI_JPEG, 5:BI_PNG
	DWORD biSizeImage;   // if BI_RGB, can set be 0
	LONG biXPelsPerMeter;
	LONG biYPelsPerMeter;
	DWORD biClrUsed;
	DWORD biClrImportant;
} BITMAPINFOHEADER;
FRESULT saveScreenBigmap(FIL *fp);
uint8_t saveScreen(TCHAR* path);
FRESULT bitmapHeader(FIL* fp, BITMAPFILEHEADER *fh, BITMAPINFOHEADER *ih);



#endif /* INC_BITMAP_H_ */
bitmap.c
#include "fatfs.h"
#include "string.h"
#include "bitmap.h"
#include "ili9341.h"
#include "string.h"
#include "stdio.h"
extern SPI_HandleTypeDef hspi2;

FRESULT LSBFirstWrite(FIL *fp,  DWORD value, uint8_t len)
{
	UINT bw;
	char lsb[len];
	for (int i = 0; i < len; i++)
	{
		lsb[i] = (value>>(i*8))&0xFF;
	}

	return f_write(fp, lsb, len, &bw);

}

FRESULT saveScreenBigmap(FIL *fp)
{
	FRESULT res;

	uint8_t RGBMask[12] = {0x00,0xf8,0x00,0x00,0xe0,0x07,0x00,0x00,0x1f,0x00,0x00,0x00};
	UINT bw;
	DWORD tempValue;
	uint16_t w,h;
	w=lcdGetWidth();
	h=lcdGetHeight();
	uint16_t pixvalue;
	char buff[w*2];

	//write file header
	// type: BM
	res = f_write(fp, "BM", 2, &bw); if (res!=FR_OK) return res;
	// size: width*height*2(bytes,16bits)+14(file header)+40(info header)+12(RGB mask)
	tempValue = lcdGetWidth()*lcdGetHeight()*2+66;
	res = LSBFirstWrite(fp, tempValue, 4); if (res!=FR_OK) return res;
	// Reserved1, Reserved2: 0,0
	memset(buff,0,4);
	res = f_write(fp, buff, 4, &bw); if (res!=FR_OK) return res;
	// bitmap offset: 14+40+12=66
	res = LSBFirstWrite(fp, 66, 4); if (res!=FR_OK) return res;
	// File Information Header
	// info header size:40
	res = LSBFirstWrite(fp, 40, 4); if (res!=FR_OK) return res;
	//width: lcd width
	res = LSBFirstWrite(fp, (DWORD)w, 4); if (res!=FR_OK) return res;
	//height: lcd height
	res = LSBFirstWrite(fp, (DWORD)h, 4); if (res!=FR_OK) return res;
	//Planes:1
	res = LSBFirstWrite(fp, 1, 2); if (res!=FR_OK) return res;
	//bitCount:16 RGB565
	res = LSBFirstWrite(fp, 16, 2); if (res!=FR_OK) return res;
	//Compression:BI_BITFIELDS=3,
	res = LSBFirstWrite(fp, 3, 4); if (res!=FR_OK) return res;
	//sizeImage:0, do not care
	res = LSBFirstWrite(fp, tempValue, 4); if (res!=FR_OK) return res;
	//x: 72 dpi=2835 PelsPerMeter
	res = LSBFirstWrite(fp, 2835, 4); if (res!=FR_OK) return res;
	//y: 72 dpi=2835 PelsPerMeter
	res = LSBFirstWrite(fp, 2835, 4); if (res!=FR_OK) return res;
	//color use:0
	res = LSBFirstWrite(fp, 0, 4); if (res!=FR_OK) return res;
	//color important:0
	res = LSBFirstWrite(fp, 0, 4); if (res!=FR_OK) return res;
	// RGB565 mask
	res = f_write(fp, RGBMask, 12, &bw); if (res!=FR_OK) return res;

	//write image pixel from LB to RT
	for (int y = h-1; y >= 0; y--)
	{
		memset(buff,0, w*2);
		for (int x = 0; x <=w-1; x++)
		{
			pixvalue=0;
			pixvalue = lcdReadPixel((uint16_t)x, (uint16_t)y);
			buff[x*2+1] = ((pixvalue>>8) & 0xFF);
			buff[x*2] = (pixvalue & 0xFF);

		}
		res = f_write(fp, buff,w*2,&bw); if (res!=FR_OK) return res;
	}


	return res;


}

uint8_t saveScreen(char* path)
{
	FATFS fs;
	FIL file;
	FRESULT res;
	uint32_t oldBraudRate;
	char fn[256];

	HAL_SPI_DeInit(&hspi2);
	oldBraudRate = hspi2.Init.BaudRatePrescaler;
	hspi2.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_2;
	HAL_SPI_Init(&hspi2);

	res = f_mount(&fs, path, 1);
	if (res==FR_OK)
	{
		sprintf(fn,"%sScreen%ld.bmp",path,get_fattime()&0xFFFF);
		res = f_open(&file, fn, FA_CREATE_ALWAYS|FA_WRITE);
		if (res==FR_OK)
		{
			lcdSetWindow(0, 0, lcdGetWidth()-1, lcdGetHeight()-1);
			res=saveScreenBigmap(&file);
			f_close(&file);
		}
		f_mount(&fs, "", 0);
	}



	HAL_SPI_DeInit(&hspi2);
	hspi2.Init.BaudRatePrescaler = oldBraudRate;
	HAL_SPI_Init(&hspi2);
	return res;

}

FRESULT bitmapHeader(FIL* fp, BITMAPFILEHEADER *fh, BITMAPINFOHEADER *ih)
{
	FRESULT r;
	uint8_t fbuff[14], ibuff[40],maskbuf[12];
	UINT br;
	if ((r=f_read(fp, fbuff, 14, &br)) != FR_OK) return r;
	fh->bfType=fbuff[0] << 8 | fbuff[1];
	fh->bfSize = fbuff[5] << 24 | fbuff[4] << 16 |fbuff[3] << 8 | fbuff[2];
	fh->bfReserved1 =fbuff[7] << 8 |fbuff[6];
	fh->bfReserved2 =fbuff[9] << 8 |fbuff[8];
	fh->bfOffBits = fbuff[13] << 24 | fbuff[12] << 16 |fbuff[11] << 8 | fbuff[10];

	if ((r=f_read(fp, ibuff, 40, &br)) != FR_OK) return r;
	ih->biSize = ibuff[3] << 24 | ibuff[2] << 16 |ibuff[1] << 8 | ibuff[0];
	ih->biWidth = ibuff[7] << 24 | ibuff[6] << 16 |ibuff[5] << 8 | ibuff[4];
	ih->biHeight = ibuff[11] << 24 | ibuff[10] << 16 |ibuff[9] << 8 | ibuff[8];
	ih->biPlanes = ibuff[13] << 8 | ibuff[12];
	ih->biBitCount = ibuff[15] << 8 | ibuff[14];
	ih->biCompression = ibuff[19] << 24 | ibuff[18] << 16 |ibuff[17] << 8 | ibuff[16];
	ih->biSizeImage =ibuff[23] << 24 | ibuff[22] << 16 |ibuff[21] << 8 | ibuff[20];
	ih->biXPelsPerMeter = ibuff[27] << 24 | ibuff[26] << 16 |ibuff[25] << 8 | ibuff[24];
	ih->biYPelsPerMeter =ibuff[31] << 24 | ibuff[30] << 16 |ibuff[29] << 8 | ibuff[28];
	ih->biClrUsed = ibuff[35] << 24 | ibuff[34] << 16 |ibuff[33] << 8 | ibuff[32];
	ih->biClrImportant = ibuff[39] << 24 | ibuff[38] << 16 |ibuff[37] << 8 | ibuff[36];

	if ((r=f_read(fp, maskbuf, 12, &br)) != FR_OK) return r;

	f_close(fp);

	return FR_OK;
}

showmessage.h
#ifndef INC_SHOWMESSAGE_H_
#define INC_SHOWMESSAGE_H_
#include "stm32f4xx_hal.h"
#include "integer.h"

void showMessage(char* msg, uint8_t sec);

#endif /* INC_SHOWMESSAGE_H_ */

showmessage.c
#include "showmessage.h"
#include "ili9341.h"
#include "string.h"
#include "stdlib.h"

void showMessage(char* msg, uint8_t sec)
{
	UINT px,py;
	UINT x,y;
	uint8_t fw,fh;
	uint16_t *msgBuff;

	px = (lcdGetWidth()-210)/2;
	py = (lcdGetHeight()-110)/2;
	msgBuff = malloc(sizeof(uint16_t)*(210*110));
	lcdSetWindow(0, 0, lcdGetWidth(), lcdGetHeight());
	for (x=px; x < 210+px; x++)
	{
		for (y=py; y < 110+py;y++)
		{
		 msgBuff[(x-px)*110+y-py] = lcdReadPixel(x, y);
		}
	}
	lcdFillRect(px, py, 209, 109, COLOR_BLUE);
	lcdFillRect(px+5, py+5, 199, 99, COLOR_WHITE);
	lcdSetTextColor(COLOR_BLUE, COLOR_WHITE);
	lcdSetTextFont(&Font16);
	fw=Font16.Width;
	fh = Font16.Height;
	for (int posx=0, posy=0, i=0; i < strlen(msg);i++,posx++)
	{
		if (msg[i] == '\n' || (posx+1)*(fw) > 200-fw)
		{
			posy++;
			posx=0;
		}
		if (msg[i] == '\n') i++;
		lcdDrawChar(px+12+posx*fw, py+12+posy*(fh+2), msg[i], COLOR_BLUE, COLOR_WHITE);
	}

	HAL_Delay(sec*1000);

	lcdSetWindow(0, 0, lcdGetWidth(), lcdGetHeight());
	for (x=px; x < 210+px; x++)
	{
		for (y=py; y < 110+py; y++)
		{
		 lcdDrawPixel(x, y, msgBuff[(x-px)*110+y-py]);
		}
	}
	free(msgBuff);
	HAL_Delay(10);
}

XPT2046_touch,h
#ifndef XPT2046_TOUCH_H_
#define XPT2046_TOUCH_H_

#include "main.h"
#include <stdbool.h>

/*** Redefine if necessary ***/

// Warning! Use SPI bus with < 2.5 Mbit speed, better ~650 Kbit to be save.
#define XPT2046_SPI_PORT hspi2
extern SPI_HandleTypeDef XPT2046_SPI_PORT;

#define XPT2046_IRQ_Pin       T_IRQ_Pin
#define XPT2046_IRQ_GPIO_Port T_IRQ_GPIO_Port
#define XPT2046_CS_Pin        T_CS_Pin
#define XPT2046_CS_GPIO_Port  T_CS_GPIO_Port

// change depending on screen orientation
#define XPT2046_SCALE_X 240
#define XPT2046_SCALE_Y 320

// to calibrate uncomment UART_Printf line in ili9341_touch.c
//#define XPT2046_MIN_RAW_X 3400
//#define XPT2046_MAX_RAW_X 29000
//#define XPT2046_MIN_RAW_Y 3300
//#define XPT2046_MAX_RAW_Y 30000

#define XPT2046_MIN_RAW_X 2000
#define XPT2046_MAX_RAW_X 30000
#define XPT2046_MIN_RAW_Y 1500
#define XPT2046_MAX_RAW_Y 29000

#define XTP2046_CALI_DIFF	600

// call before initializing any SPI devices
void XPT2046_TouchUnselect(void);
bool XPT2046_TouchPressed(void);
bool XPT2046_TouchGetCoordinates(uint16_t* x, uint16_t* y);
bool XPT2046_TouchGetRawCoordinates(uint32_t* raw_, uint32_t* raw_y);
bool XPT2046_TouchCalibration(void);

#endif /* XPT2046_TOUCH_H_ */
XPT2046_touch.c
#include <stdio.h>
#include <stdlib.h>
#include <XPT2046_touch.h>
#include "ili9341.h"

#define READ_X 0xD0
#define READ_Y 0x90


uint16_t cRawX_min = XPT2046_MIN_RAW_X;
uint16_t cRawX_max = XPT2046_MAX_RAW_X;
uint16_t cRawY_min = XPT2046_MIN_RAW_Y;
uint16_t cRawY_max = XPT2046_MAX_RAW_Y;


static void XPT2046_TouchSelect()
{
    HAL_GPIO_WritePin(XPT2046_CS_GPIO_Port, XPT2046_CS_Pin, GPIO_PIN_RESET);
}

void XPT2046_TouchUnselect()
{
    HAL_GPIO_WritePin(XPT2046_CS_GPIO_Port, XPT2046_CS_Pin, GPIO_PIN_SET);
}

bool XPT2046_TouchPressed()
{
    return HAL_GPIO_ReadPin(XPT2046_IRQ_GPIO_Port, XPT2046_IRQ_Pin) == GPIO_PIN_RESET;
}

bool XPT2046_TouchGetCoordinates(uint16_t* x, uint16_t* y)
{
	bool ret_value=false;
	uint16_t tx,ty;
	uint32_t raw_x;
	uint32_t raw_y;
	if (XPT2046_TouchGetRawCoordinates(&raw_x, &raw_y))
	{
		if(raw_x < cRawX_min) raw_x = cRawX_min;
		if(raw_x > cRawX_max) raw_x = cRawX_max;

		if(raw_y < cRawY_min) raw_y = cRawY_min;
		if(raw_y > cRawY_max) raw_y = cRawY_max;

		tx = (raw_x - cRawX_min) * XPT2046_SCALE_X / (cRawX_max - cRawX_min);
		ty = (raw_y - cRawY_min) * XPT2046_SCALE_Y / (cRawY_max - cRawY_min);

		lcdOrientationTypeDef lot = lcdGetOrientation();
		switch (lot)
		{
		case LCD_ORIENTATION_PORTRAIT:
			*x=tx;
			*y=ty;
			break;
		case LCD_ORIENTATION_LANDSCAPE:
			*x=ty;
			*y=ILI9341_PIXEL_WIDTH-tx;
					break;
		case LCD_ORIENTATION_PORTRAIT_MIRROR:
			*x=ILI9341_PIXEL_WIDTH-tx;
			*y=ILI9341_PIXEL_HEIGHT-ty;
					break;
		case LCD_ORIENTATION_LANDSCAPE_MIRROR:
			*x=ILI9341_PIXEL_HEIGHT-ty;
			*y=tx;
			break;
		}
		ret_value =true;
    }
	return ret_value;

}
bool XPT2046_TouchGetRawCoordinates(uint32_t* raw_x, uint32_t* raw_y)
{
    static const uint8_t cmd_read_x[] = { READ_X };
    static const uint8_t cmd_read_y[] = { READ_Y };
    static const uint8_t zeroes_tx[] = { 0x00, 0x00 };
    static const uint8_t SAMPLES=16;

    XPT2046_TouchSelect();

    uint32_t avg_x = 0;
    uint32_t avg_y = 0;
    uint8_t nsamples = 0;

    for(uint8_t i = 0; i < SAMPLES; i++)
    {
        if(!XPT2046_TouchPressed())
            break;

        nsamples++;

        HAL_SPI_Transmit(&XPT2046_SPI_PORT, (uint8_t*)cmd_read_y, sizeof(cmd_read_y), HAL_MAX_DELAY);
        uint8_t y_raw[2];
        HAL_SPI_TransmitReceive(&XPT2046_SPI_PORT, (uint8_t*)zeroes_tx, y_raw, sizeof(y_raw), HAL_MAX_DELAY);

        HAL_SPI_Transmit(&XPT2046_SPI_PORT, (uint8_t*)cmd_read_x, sizeof(cmd_read_x), HAL_MAX_DELAY);
        uint8_t x_raw[2];
        HAL_SPI_TransmitReceive(&XPT2046_SPI_PORT, (uint8_t*)zeroes_tx, x_raw, sizeof(x_raw), HAL_MAX_DELAY);

        avg_x += (((uint16_t)x_raw[0]) << 8) | ((uint16_t)x_raw[1]);
        avg_y += (((uint16_t)y_raw[0]) << 8) | ((uint16_t)y_raw[1]);
    }

    XPT2046_TouchUnselect();

    if(nsamples < SAMPLES)
        return false;

    *raw_x = (avg_x / SAMPLES);
    *raw_y = (avg_y / SAMPLES);
    return true;

}

bool XPT2046_TouchCalibration()
{
	uint32_t x0=0,y0=0,x1=0,y1=0,x2=0,y2=0,x3=0,y3=0;
	bool correct=true;
	lcdOrientationTypeDef lot = lcdGetOrientation();
	lcdSetOrientation(LCD_ORIENTATION_PORTRAIT);

	lcdFillRGB(COLOR_BLACK);
	lcdDrawRect(0, 0, 6, 6, COLOR_YELLOW);
	//lcdSetCursor(20, 100);
	while(!XPT2046_TouchPressed()) ;
	if (!XPT2046_TouchGetRawCoordinates(&x0, &y0))
	{
		//lcdPrintf("Calibration Error!");
		lcdSetOrientation(lot);
		return false;
	}
	//lcdPrintf("Release Pen");
	lcdFillRGB(COLOR_BLACK);
	lcdDrawRect(0, lcdGetHeight()-6, 6, 6, COLOR_YELLOW);
	while(XPT2046_TouchPressed());
	HAL_Delay(1);

	//lcdSetCursor(20, 100);
	while(!XPT2046_TouchPressed());
	if(!XPT2046_TouchGetRawCoordinates(&x1, &y1))
	{
		//lcdPrintf("Calibration Error!");
		lcdSetOrientation(lot);
		return false;
	}
	//lcdPrintf("Release Pen");
	lcdFillRGB(COLOR_BLACK);
	lcdDrawRect(lcdGetWidth()-6, lcdGetHeight()-6, 6, 6, COLOR_YELLOW);
	while(XPT2046_TouchPressed());
	HAL_Delay(1);


	//lcdSetCursor(20, 100);
	while(!XPT2046_TouchPressed());
	if (!XPT2046_TouchGetRawCoordinates(&x2, &y2))
	{
		//lcdPrintf("Calibration Error!");
		lcdSetOrientation(lot);
		return false;
	}
	//lcdPrintf("Release Pen");
	lcdFillRGB(COLOR_BLACK);
	lcdDrawRect(lcdGetWidth()-6, 0, 6, 6, COLOR_YELLOW);
	while(XPT2046_TouchPressed());
	HAL_Delay(1);

	//lcdSetCursor(20, 100);
	while(!XPT2046_TouchPressed());
	if (!XPT2046_TouchGetRawCoordinates(&x3, &y3))
	{
		//lcdPrintf("Calibration Error!");
		lcdSetOrientation(lot);
		return false;
	}
	//lcdPrintf("Release Pen");
	while(XPT2046_TouchPressed());

	if ( (int)(x0-x1) < -1*XTP2046_CALI_DIFF  || (int)(x0-x1) > XTP2046_CALI_DIFF) correct=false;
	if ( (int)(y1-y2) < -1*XTP2046_CALI_DIFF  || (int)(y1-y2) > XTP2046_CALI_DIFF) correct=false;
	if ( (int)(x2-x3) < -1*XTP2046_CALI_DIFF  || (int)(x2-x3) > XTP2046_CALI_DIFF) correct=false;
	if ( (int)(y0-y3) < -1*XTP2046_CALI_DIFF  || (int)(y0-y3) > XTP2046_CALI_DIFF) correct=false;

	if (correct) {
		cRawX_min = (x0+x1)/2;
		cRawX_max = (x2+x3)/2;
		cRawY_min = (y0+y3)/2;
		cRawY_max = (y1+y2)/2;
	}

	lcdFillRGB(COLOR_BLACK);
	//lcdSetCursor(20, 100);
	//lcdPrintf("Calibration OK:\r\nx_min=%04x,\r\ny_min=%04x,\r\nx_max=%04x,\r\ny_max=%04x",cRawX_min,cRawY_min,cRawX_max,cRawY_max);
	lcdSetOrientation(lot);
	return correct;
}


jpeg_view.h
#ifndef INC_JPEG_VIEW_H_
#define INC_JPEG_VIEW_H_

void jpeg_screen_view(char* path, char* fn, int px, int py, UINT *iw, UINT *ih);


#endif /* INC_JPEG_VIEW_H_ */

jpeg_view.c
#define JPEG_INTERNALS
#include "jinclude.h"
#include "jpeglib.h"
#include "ili9341.h"
#include "showmessage.h"

extern SPI_HandleTypeDef hspi2;

struct jpeg_decompress_struct cinfo;
typedef struct RGB
{
  uint8_t B;
  uint8_t G;
  uint8_t R;
}RGB_typedef;
struct jpeg_error_mgr jerr;

RGB_typedef *RGB_matrix;

uint16_t RGB16PixelColor;
static uint8_t *rowBuff;

static uint8_t jpeg_decode(JFILE *file, uint8_t *rowBuff, int posx, int posy, UINT *iw, UINT *ih)
{
	uint32_t line_counter = 0;
	uint32_t i = 0, xc=0, ratio;
	uint8_t offset=1;
	JSAMPROW buffer[2] = {0};

  UINT lcdWidth, lcdHeight;


  buffer[0] = rowBuff;
  lcdWidth = lcdGetWidth();
  lcdHeight = lcdGetHeight();

  cinfo.err = jpeg_std_error(&jerr);

  jpeg_create_decompress(&cinfo);

  jpeg_stdio_src (&cinfo, file);


  jpeg_read_header(&cinfo, TRUE);
  if (cinfo.image_width > 6000)
  {
	  showMessage("Image width exceeds 6000!!!", 5);
	  return 0;
  }

  if (cinfo.image_width > lcdWidth)
    {
  	  ratio=cinfo.image_width/lcdWidth;
  	  cinfo.scale_num=1;
  	  if (ratio <= 8) {
		  cinfo.scale_denom=1;
		  for(int s = 0x8; s > 0x01; s /=2)
		  {
			  if (ratio & s)
			  {
				  cinfo.scale_denom=s;
				  break;
			  }
		  }
  	  }
  	  else
  	  {
  		cinfo.scale_denom=8;
  	  }
    }

  cinfo.dct_method = JDCT_IFAST;

    jpeg_start_decompress(&cinfo);
    if (cinfo.output_width > lcdWidth)
    {
    	offset = cinfo.output_width / lcdWidth;
    	if (cinfo.output_width % lcdWidth > lcdWidth/4) offset++;
    }

    if (posx <0 || posy < 0)
    {
    	posx=(lcdWidth-(cinfo.output_width*(offset-1)/offset))/2;
    	posy=(lcdHeight-(cinfo.output_height*(offset-1)/offset))/2;
    }
    *iw = cinfo.image_width;
    *ih = cinfo.image_height;

    lcdFillRGB(COLOR_BLACK);

    if (posx > 0  && cinfo.output_width/offset < lcdWidth)
	{
    	lcdDrawRect(posx-1, posy-1, cinfo.output_width/offset+2, cinfo.output_height/offset+2, COLOR_WHITE);
	}

    while (cinfo.output_scanline < cinfo.output_height && line_counter < lcdHeight-posy)
    {
    	(void) jpeg_read_scanlines(&cinfo, buffer, 1);
      RGB_matrix=(RGB_typedef*)buffer[0];
      for(i = 0, xc=0; i < cinfo.output_width && xc < (lcdWidth -posx); i+=offset, xc++)
	  {
    	RGB16PixelColor = (uint16_t)
	    (
	     ((RGB_matrix[i].R & 0x00F8) >> 3)|
	     ((RGB_matrix[i].G & 0x00FC) << 3)|
	     ((RGB_matrix[i].B & 0x00F8) << 8)
	    );
	    lcdDrawPixel(xc+posx, line_counter+posy, RGB16PixelColor);

	  }
      for(i=0; i < offset-1 && cinfo.output_scanline < cinfo.output_height; i++)
    	  (void) jpeg_read_scanlines(&cinfo, buffer, 1);


      line_counter++;

    }

    jpeg_finish_decompress(&cinfo);

  jpeg_destroy_decompress(&cinfo);

  return 1;
}

void jpeg_screen_view(char* path, char* fn, int px, int py, UINT *iw, UINT *ih)
{
  FIL file;
  FATFS fs;

  uint32_t oldBaudRate;

  char sf[256];

  	HAL_SPI_DeInit(&hspi2);
  	oldBaudRate = hspi2.Init.BaudRatePrescaler;
  	hspi2.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_2;
  	HAL_SPI_Init(&hspi2);

  	rowBuff = JMALLOC(2048);

  sprintf(sf, "%s%s", path, fn);
  if (f_mount(&fs, path, 0) != FR_OK) {
	  JFREE(rowBuff);
	  return;
  }
  if(f_open(&file, sf, FA_READ) == FR_OK)
  {
    jpeg_decode(&file,rowBuff,px,py, iw, ih);
    f_close(&file);
  }
  else
  {
	  sprintf(sf, "%s\nFile open Error!!", sf);
	  showMessage(sf, 5);
  }
  f_mount(&fs, "", 0);
  JFREE(rowBuff);
  HAL_SPI_DeInit(&hspi2);
	hspi2.Init.BaudRatePrescaler = oldBaudRate;
	HAL_SPI_Init(&hspi2);

}



main.c
/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * @file           : main.c
  * @brief          : Main program body
  ******************************************************************************
  * @attention
  *
  * <h2><center>&copy; Copyright (c) 2020 STMicroelectronics.
  * All rights reserved.</center></h2>
  *
  * This software component is licensed by ST under BSD 3-Clause license,
  * the "License"; You may not use this file except in compliance with the
  * License. You may obtain a copy of the License at:
  *                        opensource.org/licenses/BSD-3-Clause
  *
  ******************************************************************************
  */
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "fatfs.h"
#include "libjpeg.h"

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "ili9341.h"
#include "XPT2046_touch.h"
#include "lcdDemo.h"
#include "menu.h"
#include "string.h"
#include "bitmap.h"
#include "jpeg_view.h"
#include "showmessage.h"
/* USER CODE END Includes */

/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */

/* USER CODE END PTD */

/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */

/* USER CODE END PD */

/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */

/* USER CODE END PM */

/* Private variables ---------------------------------------------------------*/
RTC_HandleTypeDef hrtc;

SPI_HandleTypeDef hspi2;
DMA_HandleTypeDef hdma_spi2_rx;
DMA_HandleTypeDef hdma_spi2_tx;

SRAM_HandleTypeDef hsram1;

/* USER CODE BEGIN PV */

uint8_t isDraw=0;

uint8_t penWidth=1;

uint8_t tempIgnoralEXTI=0;

RTC_TimeTypeDef sTime;
RTC_DateTypeDef sDate;
uint16_t cx = 0, cy = 0;

/* USER CODE END PV */

/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_DMA_Init(void);
static void MX_FSMC_Init(void);
static void MX_SPI2_Init(void);
static void MX_RTC_Init(void);
/* USER CODE BEGIN PFP */


/* USER CODE END PFP */

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
uint8_t isCalbriating,demo;

void HAL_GPIO_EXTI_Callback (uint16_t GPIO_Pin)
{
 if (GPIO_Pin == T_IRQ_Pin)
 {
	 if (tempIgnoralEXTI || isCalbriating) return;
	 if (activedMenuItem == SAVEITEM || activedMenuItem == JPEGITEM) return;

	  if(XPT2046_TouchPressed())
	  {
		  tempIgnoralEXTI=1;
		  XPT2046_TouchGetCoordinates(&cx, &cy);

		  if (menuActived)
		  {
			  uint8_t i=getTouchMenItem(cx, cy);
			  if (i > 0)
			  {
				 showMenu(i);
				  switch (i)
				  {
				   case DRAWINGITEM:
					  lcdFillRect(1, 26, lcdGetWidth()-2, lcdGetHeight()-2, COLOR_BLACK);
					  lcdDrawRect(1, 26, lcdGetWidth()-2, lcdGetHeight()-2, COLOR_WHITE);
					  break;
				  case LCDORIITEM:
					  menuActived=0;
					  break;
				  }
			  }
		  }
		  if (activedMenuItem == DRAWINGITEM)
		  {
			  if (cx > 1 && cx < lcdGetWidth()-1 && cy > 26 && cy < lcdGetHeight()-1 )
								  lcdFillCircle(cx, cy, penWidth, COLOR_GREENYELLOW);
		  }
		  tempIgnoralEXTI=0;
	  }

 }
}



void loadJpegFileTest()
{
	uint32_t stick,etick;

	lcdOrientationTypeDef lcdori;
	UINT iw, ih;

	lcdori=lcdGetOrientation();

	lcdSetOrientation(LCD_ORIENTATION_LANDSCAPE);
	lcdSetTextFont(&Font16);
	lcdSetTextColor(COLOR_WHITE, COLOR_BLACK);


	stick = HAL_GetTick();
	jpeg_screen_view("0:/", "jpg/01.jpg", 0, 0, &iw, &ih);
	etick = HAL_GetTick();

	lcdSetCursor(0, lcdGetHeight() - 2*lcdGetTextFont()->Height - 1);
	lcdPrintf("Resolution:%dX%d, \nTime: %4lu ms", iw, ih, etick-stick);
	HAL_Delay(5000);


	jpeg_screen_view("0:/", "jpg/nofile.jpg", 0, 0, &iw, &ih);
	HAL_Delay(1000);

	lcdSetTextFont(&Font16);
	lcdSetTextColor(COLOR_WHITE, COLOR_BLACK);

	stick = HAL_GetTick();
	jpeg_screen_view("0:/", "jpg/02.jpg", 0, 0, &iw, &ih);
	etick = HAL_GetTick();

	lcdSetCursor(0, lcdGetHeight() - 2*lcdGetTextFont()->Height - 1);
	lcdPrintf("Resolution:%dX%d, \nTime: %4lu ms", iw, ih, etick-stick);
	HAL_Delay(5000);

	stick = HAL_GetTick();
	jpeg_screen_view("0:/", "jpg/03.jpg", -1, 0, &iw, &ih);
	etick = HAL_GetTick();

	lcdSetCursor(0, lcdGetHeight() - 2*lcdGetTextFont()->Height - 1);
	lcdPrintf("Resolution:%dX%d, \nTime: %4lu ms", iw, ih, etick-stick);
	HAL_Delay(5000);

	stick = HAL_GetTick();
	jpeg_screen_view("0:/", "jpg/04.jpg", -1, 0, &iw, &ih);
	etick = HAL_GetTick();

	lcdSetCursor(0, lcdGetHeight() - 2*lcdGetTextFont()->Height - 1);
	lcdPrintf("Resolution:%dX%d, \nTime: %4lu ms", iw, ih, etick-stick);
	HAL_Delay(5000);

	stick = HAL_GetTick();
	jpeg_screen_view("0:/", "jpg/05.jpg", -1, 0, &iw, &ih);
	etick = HAL_GetTick();

	lcdSetCursor(0, lcdGetHeight() - 2*lcdGetTextFont()->Height - 1);
	lcdPrintf("Resolution:%dX%d, \nTime: %4lu ms", iw, ih, etick-stick);
	HAL_Delay(5000);

	lcdSetOrientation(lcdori);

}

/* USER CODE END 0 */

/**
  * @brief  The application entry point.
  * @retval int
  */
int main(void)
{
  /* USER CODE BEGIN 1 */
	activedMenuItem=0;
	 menuActived=0;
	 FATFS fs;
  /* USER CODE END 1 */

  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_DMA_Init();
  MX_FSMC_Init();
  MX_SPI2_Init();
  MX_FATFS_Init();
  MX_RTC_Init();
  MX_LIBJPEG_Init();
  /* USER CODE BEGIN 2 */

  lcdBacklightOn();
  lcdInit();
  lcdFillRGB(COLOR_BLACK);
  //lcdSetOrientation(LCD_ORIENTATION_LANDSCAPE);
  demo=false;

  lcdSetOrientation(LCD_ORIENTATION_PORTRAIT);
  //start Calibrating
  isCalbriating=true;
  showMessage("Start Calibration",2);
  //HAL_Delay(2000);
  while(!XPT2046_TouchCalibration())
  {
	showMessage("Calibration Error!!!\n Try again", 2);
   }
    isCalbriating=false;
    showMessage("Calibrate\n successfully",2);
    //HAL_Delay(2000);
  // end Calibrating
    menuActived=1;

    showMenu(0);

  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */

  while (1)
  {
	  switch(activedMenuItem)
	  {
	  case SAVEITEM:
		  saveScreen(USERPath);
		  showMenu(0);
		  break;
	  case JPEGITEM:
		  loadJpegFileTest();
		  showMenu(0);
		  break;
	  case DEMOITEM:
		  demoLCD();
		  showMenu(0);
		  break;
	  case DRAWINGITEM:

		  break;

	  case LCDORIITEM:
		  tempIgnoralEXTI=1;
		  lcdFillRGB(COLOR_BLACK);
		  lcdSetOrientation((lcdGetOrientation()+1)%4);
		  showMenu(0);
		  tempIgnoralEXTI=0;
		  menuActived=1;
		  break;

	  }

    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}

/**
  * @brief System Clock Configuration
  * @retval None
  */
void SystemClock_Config(void)
{
  RCC_OscInitTypeDef RCC_OscInitStruct = {0};
  RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
  RCC_PeriphCLKInitTypeDef PeriphClkInitStruct = {0};

  /** Configure the main internal regulator output voltage
  */
  __HAL_RCC_PWR_CLK_ENABLE();
  __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);
  /** Initializes the RCC Oscillators according to the specified parameters
  * in the RCC_OscInitTypeDef structure.
  */
  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_LSI|RCC_OSCILLATORTYPE_HSE;
  RCC_OscInitStruct.HSEState = RCC_HSE_ON;
  RCC_OscInitStruct.LSIState = RCC_LSI_ON;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
  RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
  RCC_OscInitStruct.PLL.PLLM = 4;
  RCC_OscInitStruct.PLL.PLLN = 168;
  RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;
  RCC_OscInitStruct.PLL.PLLQ = 7;
  if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
  {
    Error_Handler();
  }
  /** Initializes the CPU, AHB and APB buses clocks
  */
  RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
                              |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV4;
  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV2;

  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_5) != HAL_OK)
  {
    Error_Handler();
  }
  PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_RTC;
  PeriphClkInitStruct.RTCClockSelection = RCC_RTCCLKSOURCE_LSI;
  if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct) != HAL_OK)
  {
    Error_Handler();
  }
}

/**
  * @brief RTC Initialization Function
  * @param None
  * @retval None
  */
static void MX_RTC_Init(void)
{

  /* USER CODE BEGIN RTC_Init 0 */

  /* USER CODE END RTC_Init 0 */

  RTC_TimeTypeDef sTime = {0};
  RTC_DateTypeDef sDate = {0};

  /* USER CODE BEGIN RTC_Init 1 */

  /* USER CODE END RTC_Init 1 */
  /** Initialize RTC Only
  */
  hrtc.Instance = RTC;
  hrtc.Init.HourFormat = RTC_HOURFORMAT_24;
  hrtc.Init.AsynchPrediv = 127;
  hrtc.Init.SynchPrediv = 255;
  hrtc.Init.OutPut = RTC_OUTPUT_DISABLE;
  hrtc.Init.OutPutPolarity = RTC_OUTPUT_POLARITY_HIGH;
  hrtc.Init.OutPutType = RTC_OUTPUT_TYPE_OPENDRAIN;
  if (HAL_RTC_Init(&hrtc) != HAL_OK)
  {
    Error_Handler();
  }

  /* USER CODE BEGIN Check_RTC_BKUP */

  /* USER CODE END Check_RTC_BKUP */

  /** Initialize RTC and set the Time and Date
  */
  sTime.Hours = 12;
  sTime.Minutes = 10;
  sTime.Seconds = 0;
  sTime.DayLightSaving = RTC_DAYLIGHTSAVING_NONE;
  sTime.StoreOperation = RTC_STOREOPERATION_RESET;
  if (HAL_RTC_SetTime(&hrtc, &sTime, RTC_FORMAT_BIN) != HAL_OK)
  {
    Error_Handler();
  }
  sDate.WeekDay = RTC_WEEKDAY_MONDAY;
  sDate.Month = RTC_MONTH_JANUARY;
  sDate.Date = 1;
  sDate.Year = 21;

  if (HAL_RTC_SetDate(&hrtc, &sDate, RTC_FORMAT_BIN) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN RTC_Init 2 */

  /* USER CODE END RTC_Init 2 */

}

/**
  * @brief SPI2 Initialization Function
  * @param None
  * @retval None
  */
static void MX_SPI2_Init(void)
{

  /* USER CODE BEGIN SPI2_Init 0 */

  /* USER CODE END SPI2_Init 0 */

  /* USER CODE BEGIN SPI2_Init 1 */

  /* USER CODE END SPI2_Init 1 */
  /* SPI2 parameter configuration*/
  hspi2.Instance = SPI2;
  hspi2.Init.Mode = SPI_MODE_MASTER;
  hspi2.Init.Direction = SPI_DIRECTION_2LINES;
  hspi2.Init.DataSize = SPI_DATASIZE_8BIT;
  hspi2.Init.CLKPolarity = SPI_POLARITY_LOW;
  hspi2.Init.CLKPhase = SPI_PHASE_1EDGE;
  hspi2.Init.NSS = SPI_NSS_SOFT;
  hspi2.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_128;
  hspi2.Init.FirstBit = SPI_FIRSTBIT_MSB;
  hspi2.Init.TIMode = SPI_TIMODE_DISABLE;
  hspi2.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
  hspi2.Init.CRCPolynomial = 10;
  if (HAL_SPI_Init(&hspi2) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN SPI2_Init 2 */

  /* USER CODE END SPI2_Init 2 */

}

/**
  * Enable DMA controller clock
  */
static void MX_DMA_Init(void)
{

  /* DMA controller clock enable */
  __HAL_RCC_DMA1_CLK_ENABLE();

  /* DMA interrupt init */
  /* DMA1_Stream3_IRQn interrupt configuration */
  HAL_NVIC_SetPriority(DMA1_Stream3_IRQn, 0, 0);
  HAL_NVIC_EnableIRQ(DMA1_Stream3_IRQn);
  /* DMA1_Stream4_IRQn interrupt configuration */
  HAL_NVIC_SetPriority(DMA1_Stream4_IRQn, 0, 0);
  HAL_NVIC_EnableIRQ(DMA1_Stream4_IRQn);

}

/**
  * @brief GPIO Initialization Function
  * @param None
  * @retval None
  */
static void MX_GPIO_Init(void)
{
  GPIO_InitTypeDef GPIO_InitStruct = {0};

  /* GPIO Ports Clock Enable */
  __HAL_RCC_GPIOH_CLK_ENABLE();
  __HAL_RCC_GPIOC_CLK_ENABLE();
  __HAL_RCC_GPIOB_CLK_ENABLE();
  __HAL_RCC_GPIOE_CLK_ENABLE();
  __HAL_RCC_GPIOD_CLK_ENABLE();
  __HAL_RCC_GPIOA_CLK_ENABLE();

  /*Configure GPIO pin Output Level */
  HAL_GPIO_WritePin(SD_CS_GPIO_Port, SD_CS_Pin, GPIO_PIN_SET);

  /*Configure GPIO pin Output Level */
  HAL_GPIO_WritePin(GPIOB, LCD_BL_Pin|T_CS_Pin, GPIO_PIN_SET);

  /*Configure GPIO pin : SD_CS_Pin */
  GPIO_InitStruct.Pin = SD_CS_Pin;
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
  GPIO_InitStruct.Pull = GPIO_PULLUP;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
  HAL_GPIO_Init(SD_CS_GPIO_Port, &GPIO_InitStruct);

  /*Configure GPIO pin : T_IRQ_Pin */
  GPIO_InitStruct.Pin = T_IRQ_Pin;
  GPIO_InitStruct.Mode = GPIO_MODE_IT_FALLING;
  GPIO_InitStruct.Pull = GPIO_PULLUP;
  HAL_GPIO_Init(T_IRQ_GPIO_Port, &GPIO_InitStruct);

  /*Configure GPIO pins : LCD_BL_Pin T_CS_Pin */
  GPIO_InitStruct.Pin = LCD_BL_Pin|T_CS_Pin;
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
  GPIO_InitStruct.Pull = GPIO_PULLUP;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
  HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);

  /* EXTI interrupt init*/
  HAL_NVIC_SetPriority(EXTI9_5_IRQn, 0, 0);
  HAL_NVIC_EnableIRQ(EXTI9_5_IRQn);

}

/* FSMC initialization function */
static void MX_FSMC_Init(void)
{

  /* USER CODE BEGIN FSMC_Init 0 */

  /* USER CODE END FSMC_Init 0 */

  FSMC_NORSRAM_TimingTypeDef Timing = {0};

  /* USER CODE BEGIN FSMC_Init 1 */

  /* USER CODE END FSMC_Init 1 */

  /** Perform the SRAM1 memory initialization sequence
  */
  hsram1.Instance = FSMC_NORSRAM_DEVICE;
  hsram1.Extended = FSMC_NORSRAM_EXTENDED_DEVICE;
  /* hsram1.Init */
  hsram1.Init.NSBank = FSMC_NORSRAM_BANK1;
  hsram1.Init.DataAddressMux = FSMC_DATA_ADDRESS_MUX_DISABLE;
  hsram1.Init.MemoryType = FSMC_MEMORY_TYPE_SRAM;
  hsram1.Init.MemoryDataWidth = FSMC_NORSRAM_MEM_BUS_WIDTH_16;
  hsram1.Init.BurstAccessMode = FSMC_BURST_ACCESS_MODE_DISABLE;
  hsram1.Init.WaitSignalPolarity = FSMC_WAIT_SIGNAL_POLARITY_LOW;
  hsram1.Init.WrapMode = FSMC_WRAP_MODE_DISABLE;
  hsram1.Init.WaitSignalActive = FSMC_WAIT_TIMING_BEFORE_WS;
  hsram1.Init.WriteOperation = FSMC_WRITE_OPERATION_ENABLE;
  hsram1.Init.WaitSignal = FSMC_WAIT_SIGNAL_DISABLE;
  hsram1.Init.ExtendedMode = FSMC_EXTENDED_MODE_DISABLE;
  hsram1.Init.AsynchronousWait = FSMC_ASYNCHRONOUS_WAIT_DISABLE;
  hsram1.Init.WriteBurst = FSMC_WRITE_BURST_DISABLE;
  hsram1.Init.PageSize = FSMC_PAGE_SIZE_NONE;
  /* Timing */
  Timing.AddressSetupTime = 1;
  Timing.AddressHoldTime = 15;
  Timing.DataSetupTime = 5;
  Timing.BusTurnAroundDuration = 0;
  Timing.CLKDivision = 16;
  Timing.DataLatency = 17;
  Timing.AccessMode = FSMC_ACCESS_MODE_A;
  /* ExtTiming */

  if (HAL_SRAM_Init(&hsram1, &Timing, NULL) != HAL_OK)
  {
    Error_Handler( );
  }

  /* USER CODE BEGIN FSMC_Init 2 */

  /* USER CODE END FSMC_Init 2 */
}

/* USER CODE BEGIN 4 */

/* USER CODE END 4 */

/**
  * @brief  This function is executed in case of error occurrence.
  * @retval None
  */
void Error_Handler(void)
{
  /* USER CODE BEGIN Error_Handler_Debug */
  /* User can add his own implementation to report the HAL error return state */
  __disable_irq();
  while (1)
  {
  }
  /* USER CODE END Error_Handler_Debug */
}

#ifdef  USE_FULL_ASSERT
/**
  * @brief  Reports the name of the source file and the source line number
  *         where the assert_param error has occurred.
  * @param  file: pointer to the source file name
  * @param  line: assert_param error line source number
  * @retval None
  */
void assert_failed(uint8_t *file, uint32_t line)
{
  /* USER CODE BEGIN 6 */
  /* User can add his own implementation to report the file name and line number,
     ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
  /* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */

/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/


menu.h
#include "stm32f4xx_hal.h"
#include "ili9341.h"

#define MENU_ITEMS	5
#define JPEGITEM 	1
#define DEMOITEM 	2
#define DRAWINGITEM	3
#define SAVEITEM	5
#define LCDORIITEM	4

uint8_t activedMenuItem;
uint8_t menuActived;

void showMenu(uint8_t selectedIndex);
void hideMenu(void);
uint8_t getTouchMenItem(uint16_t x, uint16_t y);

menu.c
#include "menu.h"

uint16_t menuColordinate[][4]={{5,5,44,20},{52, 5, 44, 20},{99, 5, 44, 20},{146, 5, 25, 20}, {174,5,44,20}};
char *menuText[]={"JPEG", "DEMO","DRAW", "R", "SAVE"};
uint16_t menuBGColor[]={COLOR_BLUE,COLOR_BLUE,COLOR_BLUE,COLOR_BLUE,COLOR_BLUE};
uint16_t selmenuBGColor=COLOR_GREEN;



void hideMenu()
{
	lcdFillRect(0, 0,lcdGetWidth(), 25, COLOR_BLACK);
}

void showMenu(uint8_t selectedIndex)
{
	lcdSetTextFont(&Font12);
	if (selectedIndex > 0 && selectedIndex <= MENU_ITEMS)
	{
		if (activedMenuItem > 0 && activedMenuItem <= MENU_ITEMS)
		{
			int ti=activedMenuItem-1;
			lcdSetTextColor(COLOR_WHITE, menuBGColor[ti]);
			lcdFillRect(menuColordinate[ti][0], menuColordinate[ti][1], menuColordinate[ti][2], menuColordinate[ti][3], menuBGColor[ti]);
			lcdSetCursor(menuColordinate[ti][0]+6, menuColordinate[ti][1]+4);
			lcdPrintf(menuText[ti]);
		}
		activedMenuItem=selectedIndex;
		selectedIndex--;
		lcdSetTextColor(COLOR_WHITE, selmenuBGColor);
		lcdFillRect(menuColordinate[selectedIndex][0], menuColordinate[selectedIndex][1], menuColordinate[selectedIndex][2], menuColordinate[selectedIndex][3], selmenuBGColor);
		lcdSetCursor(menuColordinate[selectedIndex][0]+6, menuColordinate[selectedIndex][1]+4);
		lcdPrintf(menuText[selectedIndex]);
	}
	else
	{
		hideMenu();
		activedMenuItem=0;
		for (int i=0;i<MENU_ITEMS;i++)
		{
			lcdSetTextColor(COLOR_WHITE, menuBGColor[i]);
			lcdFillRect(menuColordinate[i][0], menuColordinate[i][1], menuColordinate[i][2], menuColordinate[i][3], menuBGColor[i]);
			lcdSetCursor(menuColordinate[i][0]+6, menuColordinate[i][1]+4);
			lcdPrintf(menuText[i]);
		}
	}
}
uint8_t getTouchMenItem(uint16_t x, uint16_t y)
{
	uint8_t ret = 0;

	for (int i=0;i<MENU_ITEMS;i++)
	{
		if (x > menuColordinate[i][0] && x < menuColordinate[i][0]+menuColordinate[i][2] &&
				y > menuColordinate[i][1] && y < menuColordinate[i][1]+menuColordinate[i][3])
		{
			ret = i+1;
			break;
		}
	}
	return ret;
}