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

16 篇文章 7 订阅

本程序采用的是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库本身是支持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 <stm32f1xx.h>
#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:

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

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

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

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

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

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 <stdio.h>
#include <stm32f1xx.h>
#include <GUI.h>
#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);
  }
}

程序运行效果:

可以发现已经可以正常绘制图片,矩形,以及显示文字了。
但是现在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:  support@segger.com    *
*                                                                    *
**********************************************************************

** 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
  *
  * <h2><center>&copy; Copyright (c) 2018 STMicroelectronics. 
  * All rights reserved.</center></h2>
  *
  * 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 <stm32f1xx.h>
#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矩阵键盘和彩屏排座的电路图:

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

  • 5
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
您好!对于驱动ILI9800液晶屏,您可以使用STM32F4系列的FSMC(Flexible Static Memory Controller)来进行驱动。首先,确保您已经正确配置并初始化了FSMC,包括设置相应的时序参数和引脚映射。 下面是一个简单的示例代码,演示如何使用FSMC驱动ILI9800液晶屏: ```c // 定义FSMC寄存器地址 #define FSMC_BANK1_BASE_ADDR 0x60000000 #define FSMC_BANK1_RS_ADDR (FSMC_BANK1_BASE_ADDR + 0x0000) // RS引脚地址 #define FSMC_BANK1_RW_ADDR (FSMC_BANK1_BASE_ADDR + 0x0800) // RW引脚地址 #define FSMC_BANK1_DATA_ADDR (FSMC_BANK1_BASE_ADDR + 0x1000) // 数据引脚地址 // 写命令到ILI9800 void ILI9800_WriteCommand(uint16_t command) { *(volatile uint16_t*)FSMC_BANK1_RS_ADDR = 0; // RS引脚置低表示写命令 *(volatile uint16_t*)FSMC_BANK1_RW_ADDR = 0; // RW引脚置低表示写入 *(volatile uint16_t*)FSMC_BANK1_DATA_ADDR = command; // 写入命令数据 } // 写数据到ILI9800 void ILI9800_WriteData(uint16_t data) { *(volatile uint16_t*)FSMC_BANK1_RS_ADDR = 1; // RS引脚置高表示写数据 *(volatile uint16_t*)FSMC_BANK1_RW_ADDR = 0; // RW引脚置低表示写入 *(volatile uint16_t*)FSMC_BANK1_DATA_ADDR = data; // 写入数据 } // 初始化ILI9800 void ILI9800_Init() { // 进行ILI9800的初始化操作,具体根据液晶屏规格进行配置 } int main() { // 进行FSMC的初始化配置 // 初始化ILI9800 ILI9800_Init(); while (1) { // 更新ILI9800显示内容 // 进行其他操作 } } ``` 请注意,以上代码仅为示例,具体的液晶屏初始化和显示操作需要根据您使用的ILI9800液晶屏具体规格和驱动要求进行配置。 希望这可以帮助到您!如果您还有其他问题,请随时提问。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

巨大八爪鱼

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值