【STemWin】STM32F103VE单片机用FSMC驱动ILI9341彩色触摸屏(触控芯片XPT2046),并裸机移植STemWin图形库(采用LCDConf_FlexColor.c模板)

本程序采用的是STM32F103VE单片机,外部晶振的大小为8MHz,使用HAL库编写程序。

程序下载地址:https://pan.baidu.com/s/1-Q4LX3DkMjDcLod1m3r1oQ(提取码:mcji)
(程序里面注释FSMC_D16=>RS写错了,应该是A16才对,D没有16)

去ST官网下载STM32F1的Cube包,文件名称为en.stm32cubef1.zip,STemWin图形库就位于STM32Cube_FW_F1_V1.8.0/Middlewares/ST/STemWin文件夹中。

将整个inc文件夹复制到工程中,然后复制Config文件夹下的GUIConf.c、GUIConf.h、LCDConf_FlexColor.c和LCDConf_FlexColor.h文件(去掉了文件名中的_Template)。再复制GUI_X.c和STemWin_CM3_wc16.a就可以了。

【STemWin】STM32F103VE单片机用FSMC驱动ILI9341彩色触摸屏(触控芯片XPT2046),并裸机移植STemWin图形库(采用LCDConf_FlexColor.c模板)_第1张图片

【STemWin】STM32F103VE单片机用FSMC驱动ILI9341彩色触摸屏(触控芯片XPT2046),并裸机移植STemWin图形库(采用LCDConf_FlexColor.c模板)_第2张图片

STemWin库本身是支持ILI9341的,因此移植过程非常简单。

在GUIConf.c中,我们只需要将GUI_NUMBYTES宏的值调小就行了。因为默认的值太大了,单片机根本没有这么多内存可分配,可以改成32768,也就是只分配32KB的内存给STemWin:

//
// Define the available number of bytes available for the GUI
//
#define GUI_NUMBYTES  32768

 对于LCDConf_FlexColor.c,我们只需要实现读写液晶屏命令和数据的函数就行了。先包含STM32头文件:

#include 
#include "../ILI9341.h"

 实现命令和数据读写函数:

/********************************************************************
*
*       LcdWriteReg
*
* Function description:
*   Sets display register
*/
static void LcdWriteReg(U16 Data) {
  // ... TBD by user
  ILI9341_CMD = Data; // 写命令
}

/********************************************************************
*
*       LcdWriteData
*
* Function description:
*   Writes a value to a display register
*/
static void LcdWriteData(U16 Data) {
  // ... TBD by user
  ILI9341_DATA = Data; // 写数据
}

/********************************************************************
*
*       LcdWriteDataMultiple
*
* Function description:
*   Writes multiple values to a display register.
*/
static void LcdWriteDataMultiple(U16 * pData, int NumItems) {
  while (NumItems--) {
    // ... TBD by user
    ILI9341_DATA = *pData++; // 写多个数据
  }
}

/********************************************************************
*
*       LcdReadDataMultiple
*
* Function description:
*   Reads multiple values from a display register.
*/
static void LcdReadDataMultiple(U16 * pData, int NumItems) {
  while (NumItems--) {
    // ... TBD by user
    *pData++ = ILI9341_DATA; // 读多个数据
  }
}

其中ILI9341_CMD和ILI9341_DATA是两个FSMC内存地址,是定义在我们的ILI9341.h中的。

#define ILI9341_CMD (*(volatile uint16_t *)0x60000000)
#define ILI9341_DATA (*(volatile uint16_t *)0x60020000)

接下来在LCD_X_Config中,我们需要调用STemWin中内置的ILI9341的驱动程序,把第三个参数改成F66709:

GUIDRV_FlexColor_SetFunc(pDevice, &PortAPI, GUIDRV_FLEXCOLOR_F66709, GUIDRV_FLEXCOLOR_M16C0B16);
// 根据StemWin手册, 驱动Ilitek ILI9335的屏幕, 第三个参数应该选择66708
// Ilitek ILI9338, ILI9340, ILI9341, ILI9342应该选择66709
// Samsung S6E63D6应该选择66719

可以在STemWin的手册里面看到ILI9341对应的是F66709:

【STemWin】STM32F103VE单片机用FSMC驱动ILI9341彩色触摸屏(触控芯片XPT2046),并裸机移植STemWin图形库(采用LCDConf_FlexColor.c模板)_第3张图片

在本程序中,我们不需要颠倒X和Y坐标轴,也不需要翻转Y轴,于是注释掉:

//Config.Orientation = GUI_SWAP_XY | GUI_MIRROR_Y; // 是否交换XY方向

这样的话,X轴就是短边,Y轴就是长边了。

接下来,将STemWin的头文件目录添加到工程属性里面去:

【STemWin】STM32F103VE单片机用FSMC驱动ILI9341彩色触摸屏(触控芯片XPT2046),并裸机移植STemWin图形库(采用LCDConf_FlexColor.c模板)_第4张图片

STemWin_CM3_wc16.a的文件类型要修改为Library file:

【STemWin】STM32F103VE单片机用FSMC驱动ILI9341彩色触摸屏(触控芯片XPT2046),并裸机移植STemWin图形库(采用LCDConf_FlexColor.c模板)_第5张图片

OK,就这么简单,移植完成了,现在可以编写主程序运行一下试试看了!

注意:调用GUI_Init函数初始化STemWin图形库前必须要在RCC中打开STM32的CRC外设的时钟!!!

很奇怪今天晚上液晶屏触摸突然好了,X坐标和Y坐标都能读了。触控程序没有改过,之前测的时候按下去X坐标(0xd0寄存器)一直是0,Y坐标(0x90寄存器)的范围是2950~3050(从屏幕上方滑到下方)。松开时X和Y都是4095。也就是说只能读Y坐标不能读X坐标。而且,触摸中断一直为高电平,按下屏幕没有任何反应。
今天晚上弄着弄着就X和Y都能读了,触摸中断(PC4低电平)也好使了。感觉很可能是屏幕本身的问题,同样程序在其他屏幕上运行就没有问题,之前偏偏在这块屏幕上有问题。

#include 
#include 
#include 
#include "common.h"
#include "images.h"
#include "Keyboard.h"
#include "ILI9341.h"
#include "XPT2046.h"

// 矩阵键盘按键处理
static void key_handler(int key, void *arg)
{
  char str[30];
  GUI_RECT rect;
  
  GUI_SetBkColor(GUI_LIGHTBLUE);
  rect.x0 = 0;
  rect.y0 = 0;
  rect.x1 = 239;
  rect.y1 = 99;
  GUI_ClearRectEx(&rect);
  
  if (key != -1)
  {
    snprintf(str, sizeof(str), "You pressed KEY%d!", key);
    GUI_SetColor(GUI_RED);
  }
  else
  {
    snprintf(str, sizeof(str), "You released the key!");
    GUI_SetColor(GUI_GREEN);
  }
  GUI_DispStringInRectWrap(str, &rect, GUI_TA_HCENTER | GUI_TA_VCENTER, GUI_WRAPMODE_WORD);
  //GUI_InvertRect(rect.x0, rect.y0, rect.x1, rect.y1); // 反色
}

// 截取一部分位图并显示
static void copy_part_of_bitmap(int x, int y, const GUI_BITMAP *bmp, int x0, int y0, int width, int height)
{
  GUI_RECT rect;
 
  rect.x0 = x;
  rect.y0 = y;
  rect.x1 = x + width - 1;
  rect.y1 = y + height - 1;
  GUI_SetClipRect(&rect);
  GUI_DrawBitmap(bmp, x - x0, y - y0);
  GUI_SetClipRect(NULL);
}

static void display_images(void)
{
  GUI_BITMAP bmp;
  
  bmp.BitsPerPixel = 16;
  bmp.BytesPerLine = sizeof(image1[0]);
  bmp.pData = (const uint8_t *)image1;
  bmp.pMethods = GUI_DRAW_BMPM565;
  bmp.pPal = NULL;
  bmp.XSize = GUI_COUNTOF(image1[0]);
  bmp.YSize = GUI_COUNTOF(image1);
  //GUI_DrawBitmap(&bmp, 0, 36); // 显示完整的图片
  
  // 截取图片的一部分并显示
  copy_part_of_bitmap(20, 120, &bmp, 60, 125, 131, 123);
  //GUI_Delay(1000);
  copy_part_of_bitmap(180, 120, &bmp, 127, 32, 16, 64);
}

// 显示触控坐标
void display_touch(int x, int y)
{
  char str[30];
  GUI_RECT rect;
  
  GUI_SetBkColor(GUI_LIGHTGREEN);
  rect.x0 = 0;
  rect.y0 = 263;
  rect.x1 = 239;
  rect.y1 = 319;
  GUI_ClearRectEx(&rect);
  
  snprintf(str, sizeof(str), "(%d,%d)\n", x, y);
  GUI_SetColor(GUI_ORANGE);
  GUI_DispStringInRectWrap(str, &rect, GUI_TA_HCENTER | GUI_TA_VCENTER, GUI_WRAPMODE_WORD);
}

int main(void)
{
  int x, y;
  
  HAL_Init();
  
  clock_init();
  usart_init(115200);
  printf("STM32F103VE FSMC ILI9341\n");
  printf("SystemCoreClock=%u\n", SystemCoreClock);
  
  Keyboard_Init();
  ILI9341_Init();
  XPT2046_Init();
  
  __HAL_RCC_CRC_CLK_ENABLE();
  GUI_Init();
  GUI_SetBkColor(GUI_LIGHTRED);
  GUI_Clear(); // 清屏
  ILI9341_Enable(1); // 开显示
  
  GUI_SetFont(GUI_FONT_32B_1);
  GUI_SetTextMode(GUI_TM_TRANS); // 绘制文字时无背景颜色
  
  display_images();
  while (1)
  {
    if (XPT2046_GetITStatus())
    {
      XPT2046_ReadPosition(&x, &y);
      printf("(%d,%d)\n", x, y);
      display_touch(x, y);
    }
    
    Keyboard_Process(key_handler, NULL);
  }
}

程序运行效果:

【STemWin】STM32F103VE单片机用FSMC驱动ILI9341彩色触摸屏(触控芯片XPT2046),并裸机移植STemWin图形库(采用LCDConf_FlexColor.c模板)_第6张图片

【STemWin】STM32F103VE单片机用FSMC驱动ILI9341彩色触摸屏(触控芯片XPT2046),并裸机移植STemWin图形库(采用LCDConf_FlexColor.c模板)_第7张图片

【STemWin】STM32F103VE单片机用FSMC驱动ILI9341彩色触摸屏(触控芯片XPT2046),并裸机移植STemWin图形库(采用LCDConf_FlexColor.c模板)_第8张图片

可以发现已经可以正常绘制图片,矩形,以及显示文字了。
但是现在GUI_Delay无法延时,一调用就会陷入死循环。而且GUI_InvertRect函数也无法正常工作,原因是因为我们还没有正确配置好STemWin读取屏幕像素点的功能,凡是需要读取像素点的(例如GUI_DM_XOR方式绘制)函数都无法正常工作。

首先解决GUI_Delay的问题,只需要将GUI_X.c里面GUI_X_GetTime和GUI_X_Delay分别与HAL库中的HAL_GetTick和HAL_Delay函数绑定就可以了:

GUI_TIMER_TIME GUI_X_GetTime(void) { 
  return HAL_GetTick();
}

void GUI_X_Delay(int ms) { 
  HAL_Delay(ms);
}

实现读取屏幕像素点需要修改LCD_X_Config函数。查阅手册可知,STemWin自带了三种读取屏幕像素点的方式,一一测试:

GUIDRV_FlexColor_SetReadFunc66709_B16(pDevice, GUIDRV_FLEXCOLOR_READ_FUNC_I);
GUIDRV_FlexColor_SetReadFunc66709_B16(pDevice, GUIDRV_FLEXCOLOR_READ_FUNC_II);
GUIDRV_FlexColor_SetReadFunc66709_B16(pDevice, GUIDRV_FLEXCOLOR_READ_FUNC_III);

 发现前两个都不行,第三个会导致HardFault错误,所以用StemWin自带的没有希望了。我们可以自己编写函数来实现读取像素点,然后和STemWin绑定:

LCD_SetDevFunc(0, LCD_DEVFUNC_READPIXEL, (void (*)(void))LcdReadPixel);
LCD_SetDevFunc(0, LCD_DEVFUNC_READMPIXELS, (void (*)(void))LcdReadMPixels);

第一个函数是读单个点,第二个函数是读多个点,函数的实现如下:

static U16 LcdReadPixel(int LayerIndex)
{
  U16 color;
  
  ILI9341_GetPixels(&color, 1);
  return color;
}

static void LcdReadMPixels(int LayerIndex, U16 *pBuffer, U32 NumPixels)
{
  ILI9341_GetPixels(pBuffer, NumPixels);
}

注意这两个函数都只传入了LayerIndex(图层号)参数,没有传入X坐标和Y坐标,这是因为STemWin已经帮我们设置好了坐标了,我们只需要去读取颜色值就行了:

/* 读取屏幕显示内容 */
void ILI9341_GetPixels(uint16_t *pixels, int count)
{
  int i = 0;
  uint16_t data[3];
  uint16_t rgb565[2];
  uint32_t rgb888[2];
  
  ILI9341_CMD = 0x2e;
  ILI9341_DATA; // dummy read
  while (i < count)
  {
    // 读两个像素, 每个像素3字节
    // 每字节表示一个分量, 分量在字节中是左对齐的
    data[0] = ILI9341_DATA; // 0xr1g1 (高字节为第一个像素的红色分量, 低字节为第一个像素的绿色分量)
    data[1] = ILI9341_DATA; // 0xb1r2 (高字节为第一个像素的蓝色分量, 低字节为第二个像素的红色分量)
    data[2] = ILI9341_DATA; // 0xg2b2 (高字节为第二个像素的绿色分量, 低字节为第二个像素的蓝色分量)
    
    // 转换成RGB888
    rgb888[0] = (data[0] << 8) | (data[1] >> 8);
    rgb888[1] = ((data[1] & 0xff) << 16) | data[2];
    //printf("#%06X #%06X => ", rgb888[0], rgb888[1]);
    
    // 再转换成RGB565
    rgb565[0] = ILI9341_RGB888TO565(rgb888[0]);
    rgb565[1] = ILI9341_RGB888TO565(rgb888[1]);
    //printf("0x%04x 0x%04x\n", rgb565[0], rgb565[1]);
    
    // 保存颜色值
    pixels[i++] = rgb565[0];
    if (i < count)
      pixels[i++] = rgb565[1];
  }
}

下面附上完整的LCDConf_FlexColor.c的文件内容:

/*********************************************************************
*                SEGGER Microcontroller GmbH & Co. KG                *
*        Solutions for real time microcontroller applications        *
**********************************************************************
*                                                                    *
*        (c) 1996 - 2017  SEGGER Microcontroller GmbH & Co. KG       *
*                                                                    *
*        Internet: www.segger.com    Support:  [email protected]    *
*                                                                    *
**********************************************************************

** emWin V5.44 - Graphical user interface for embedded applications **
All  Intellectual Property rights  in the Software belongs to  SEGGER.
emWin is protected by  international copyright laws.  Knowledge of the
source code may not be used to write a similar product.  This file may
only be used in accordance with the following terms:

The  software has  been licensed  to STMicroelectronics International
N.V. a Dutch company with a Swiss branch and its headquarters in Plan-
les-Ouates, Geneva, 39 Chemin du Champ des Filles, Switzerland for the
purposes of creating libraries for ARM Cortex-M-based 32-bit microcon_
troller products commercialized by Licensee only, sublicensed and dis_
tributed under the terms and conditions of the End User License Agree_
ment supplied by STMicroelectronics International N.V.
Full source code is available at: www.segger.com

We appreciate your understanding and fairness.
----------------------------------------------------------------------
File        : LCDConf_FlexColor_Template.c
Purpose     : Display controller configuration (single layer)
---------------------------END-OF-HEADER------------------------------
*/

/**
  ******************************************************************************
  * @attention
  *
  * 

© Copyright (c) 2018 STMicroelectronics. * All rights reserved.

* * This software component is licensed by ST under Ultimate Liberty license SLA0044, * 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.st.com/SLA0044 * ****************************************************************************** */ #include "GUI.h" #include "GUIDRV_FlexColor.h" #include #include "../ILI9341.h" /********************************************************************* * * Layer configuration (to be modified) * ********************************************************************** */ // // Physical display size // #define XSIZE_PHYS 240 // To be adapted to x-screen size #define YSIZE_PHYS 320 // To be adapted to y-screen size /********************************************************************* * * Configuration checking * ********************************************************************** */ #ifndef VXSIZE_PHYS #define VXSIZE_PHYS XSIZE_PHYS #endif #ifndef VYSIZE_PHYS #define VYSIZE_PHYS YSIZE_PHYS #endif #ifndef XSIZE_PHYS #error Physical X size of display is not defined! #endif #ifndef YSIZE_PHYS #error Physical Y size of display is not defined! #endif #ifndef GUICC_565 #error Color conversion not defined! #endif #ifndef GUIDRV_FLEXCOLOR #error No display driver defined! #endif /********************************************************************* * * Local functions * ********************************************************************** */ /******************************************************************** * * LcdWriteReg * * Function description: * Sets display register */ static void LcdWriteReg(U16 Data) { // ... TBD by user ILI9341_CMD = Data; // 写命令 } /******************************************************************** * * LcdWriteData * * Function description: * Writes a value to a display register */ static void LcdWriteData(U16 Data) { // ... TBD by user ILI9341_DATA = Data; // 写数据 } /******************************************************************** * * LcdWriteDataMultiple * * Function description: * Writes multiple values to a display register. */ static void LcdWriteDataMultiple(U16 * pData, int NumItems) { while (NumItems--) { // ... TBD by user ILI9341_DATA = *pData++; // 写多个数据 } } /******************************************************************** * * LcdReadDataMultiple * * Function description: * Reads multiple values from a display register. */ static void LcdReadDataMultiple(U16 * pData, int NumItems) { while (NumItems--) { // ... TBD by user *pData++ = ILI9341_DATA; // 读多个数据 } } static U16 LcdReadPixel(int LayerIndex) { U16 color; ILI9341_GetPixels(&color, 1); return color; } static void LcdReadMPixels(int LayerIndex, U16 *pBuffer, U32 NumPixels) { ILI9341_GetPixels(pBuffer, NumPixels); } /********************************************************************* * * Public functions * ********************************************************************** */ /********************************************************************* * * LCD_X_Config * * Function description: * Called during the initialization process in order to set up the * display driver configuration. * */ void LCD_X_Config(void) { GUI_DEVICE * pDevice; CONFIG_FLEXCOLOR Config = {0}; GUI_PORT_API PortAPI = {0}; // // Set display driver and color conversion // pDevice = GUI_DEVICE_CreateAndLink(GUIDRV_FLEXCOLOR, GUICC_565, 0, 0); // // Display driver configuration, required for Lin-driver // LCD_SetSizeEx (0, XSIZE_PHYS , YSIZE_PHYS); LCD_SetVSizeEx(0, VXSIZE_PHYS, VYSIZE_PHYS); // // Orientation // //Config.Orientation = GUI_SWAP_XY | GUI_MIRROR_Y; // 是否交换XY方向 GUIDRV_FlexColor_Config(pDevice, &Config); // // Set controller and operation mode // PortAPI.pfWrite16_A0 = LcdWriteReg; PortAPI.pfWrite16_A1 = LcdWriteData; PortAPI.pfWriteM16_A1 = LcdWriteDataMultiple; PortAPI.pfReadM16_A1 = LcdReadDataMultiple; GUIDRV_FlexColor_SetFunc(pDevice, &PortAPI, GUIDRV_FLEXCOLOR_F66709, GUIDRV_FLEXCOLOR_M16C0B16); // 根据StemWin手册, 驱动Ilitek ILI9335的屏幕, 第三个参数应该选择66708 // Ilitek ILI9338, ILI9340, ILI9341, ILI9342应该选择66709 // Samsung S6E63D6应该选择66719 LCD_SetDevFunc(0, LCD_DEVFUNC_READPIXEL, (void (*)(void))LcdReadPixel); LCD_SetDevFunc(0, LCD_DEVFUNC_READMPIXELS, (void (*)(void))LcdReadMPixels); //GUIDRV_FlexColor_SetReadFunc66709_B16(pDevice, GUIDRV_FLEXCOLOR_READ_FUNC_III); } /********************************************************************* * * LCD_X_DisplayDriver * * Function description: * This function is called by the display driver for several purposes. * To support the according task the routine needs to be adapted to * the display controller. Please note that the commands marked with * 'optional' are not cogently required and should only be adapted if * the display controller supports these features. * * Parameter: * LayerIndex - Index of layer to be configured * Cmd - Please refer to the details in the switch statement below * pData - Pointer to a LCD_X_DATA structure * * Return Value: * < -1 - Error * -1 - Command not handled * 0 - Ok */ int LCD_X_DisplayDriver(unsigned LayerIndex, unsigned Cmd, void * pData) { int r; (void) LayerIndex; (void) pData; switch (Cmd) { case LCD_X_INITCONTROLLER: { // // Called during the initialization process in order to set up the // display controller and put it into operation. If the display // controller is not initialized by any external routine this needs // to be adapted by the customer... // // ... return 0; } default: r = -1; } return r; } /*************************** End of file ****************************/

以及4x4矩阵键盘和彩屏排座的电路图:

【STemWin】STM32F103VE单片机用FSMC驱动ILI9341彩色触摸屏(触控芯片XPT2046),并裸机移植STemWin图形库(采用LCDConf_FlexColor.c模板)_第9张图片

连续运行了一天触控一直都好使。晚上关机,第二天中午再开,屏幕触控就没有任何反应了,触控中断无信号。通电大概二十分钟过后,触控又好使了(屏幕透明膜之前已经揭下来了),PC4触控中断有反应。看来这个屏幕真的有问题。。。

你可能感兴趣的:(CubeMX,STM32)