【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库本身是支持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 userILI9341_CMD = Data; // 写命令
}/********************************************************************
*
* LcdWriteData
*
* Function description:
* Writes a value to a display register
*/
static void LcdWriteData(U16 Data) {// ... TBD by userILI9341_DATA = Data; // 写数据
}/********************************************************************
*
* LcdWriteDataMultiple
*
* Function description:
* Writes multiple values to a display register.
*/
static void LcdWriteDataMultiple(U16 * pData, int NumItems) {while (NumItems--) {// ... TBD by userILI9341_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 readwhile (i < count){// 读两个像素, 每个像素3字节// 每字节表示一个分量, 分量在字节中是左对齐的data[0] = ILI9341_DATA; // 0xr1g1 (高字节为第一个像素的红色分量, 低字节为第一个像素的绿色分量)data[1] = ILI9341_DATA; // 0xb1r2 (高字节为第一个像素的蓝色分量, 低字节为第二个像素的红色分量)data[2] = ILI9341_DATA; // 0xg2b2 (高字节为第二个像素的绿色分量, 低字节为第二个像素的蓝色分量)// 转换成RGB888rgb888[0] = (data[0] << 8) | (data[1] >> 8);rgb888[1] = ((data[1] & 0xff) << 16) | data[2];//printf("#%06X #%06X => ", rgb888[0], rgb888[1]);// 再转换成RGB565rgb565[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.comWe appreciate your understanding and fairness.
----------------------------------------------------------------------
File : LCDConf_FlexColor_Template.c
Purpose : Display controller configuration (single layer)
---------------------------END-OF-HEADER------------------------------
*//********************************************************************************* @attention** <h2><center>© 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 userILI9341_CMD = Data; // 写命令
}/********************************************************************
*
* LcdWriteData
*
* Function description:
* Writes a value to a display register
*/
static void LcdWriteData(U16 Data) {// ... TBD by userILI9341_DATA = Data; // 写数据
}/********************************************************************
*
* LcdWriteDataMultiple
*
* Function description:
* Writes multiple values to a display register.
*/
static void LcdWriteDataMultiple(U16 * pData, int NumItems) {while (NumItems--) {// ... TBD by userILI9341_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应该选择66719LCD_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触控中断有反应。看来这个屏幕真的有问题。。。
【STemWin】STM32F103VE单片机用FSMC驱动ILI9341彩色触摸屏(触控芯片XPT2046),并裸机移植STemWin图形库(采用LCDConf_FlexColor.c模板)相关推荐
- 【STemWin】STM32F429IG单片机用LTDC驱动正点原子7寸RGB彩色触摸屏,并裸机移植STemWin图形库
[器件型号] 单片机采用STM32F429IG,运行频率为180MHz,外部晶振HSE的频率为25MHz. 开发板采用外部32MB的SDRAM内存作显存.显示屏分辨率为800×480,颜色格式为RGB ...
- 【程序】STM32F407VE单片机通过FSMC驱动Ilitek ILI9325液晶屏以及XPT2046触控芯片
本程序使用的单片机为STM32F407VE,晶振大小为8MHz,用10厘米长的杜邦线和彩屏相连,彩屏为微雪(Waveshare)的3.2inch 320x240 ILI9325 Touch LCD ( ...
- ST7701芯片820*320屏幕移植stemwin
简要说明 如题,公司最近更换820*320屏幕,移植stemwin,使用3线spi通讯+16RGB,这屏幕工作流程为:首先通过3线spi发送初始化指令,之后通过16bitRGB发送像素点. 移植过程参 ...
- 100脚的STM32F103VE单片机通过FSMC接口读写DS12C887时钟芯片中的寄存器
STM32F1系列的单片机本身自带的RTC实时时钟外设只是一个单纯的32位计数器,没有分立为年月日.小时.分钟.秒等寄存器,使用起来不是很方便.这时可以考虑使用外部RTC芯片,比如使用SPI接口的双向 ...
- 联想小新触摸板驱动_如何下载并安装触控板驱动
操作步骤: 一.Lenovo系列电脑 1.点击这里打开联想官方网站,在示例的位置输入主机编号后,点击"搜索"按钮,查找本机所带的驱动: 2.在操作系统列表中选择对应的操作系统: 3 ...
- 五、TDDI 触控、显示驱动一体化技术
TDDI(触控.显示驱动一体化技术)即触控与显示驱动器集成(Touch and Display Driver Integration ).目前智能手机的触控和显示功能都由两块芯片独立控制,而TDDI最 ...
- GC9A01-TFT屏幕驱动(整理有stm32/51单片机/arduino等驱动代码)
GC9A01-TFT屏幕驱动 & 整理有stm32/51单片机/arduino等驱动代码 前言 关于GC9A01 stm32驱动 引脚接线 代码移植 文件复制 端口修改 显示函数 中文汉字数组 ...
- FSMC驱动TFTLCD原理,时序和寄存器介绍
一,FSMC简介 FSMC:灵活的静态存储控制器 能够与同步或异步存储器和16位PC存储器卡连接 STM32的FSMC接口支持包括SRAM.NAND FLASH.NOR FLASH和PSRAM等存储器 ...
- ST7735-TFT屏幕驱动(整理有stm32/51单片机/arduino等驱动代码)
ST7735-TFT屏幕驱动 & 整理有stm32/51单片机/arduino等驱动代码 前言 关于ST7735 stm32驱动 引脚接线 代码移植 文件复制 端口修改 显示函数 中文汉字数组 ...
最新文章
- QuartusII和NiosII,FPGA板之间的关系
- 通过JConsole查看本地远程虚拟机
- 数学图形之SineSurface与粽子曲面
- BaseAdapter的抽取
- 深度学习-吴恩达-笔记-1-深度学习引言
- [转]MSSQL CURSOR (游标) 笔记
- 用servlet编写下载程序
- Debug JDK源码没变量值怎么办?
- 计算机二级与c语言有什么关系,计算机二级c和c++区别?
- java.lang.NoClassDefFoundError: org/springframework/core/metrics/ApplicationStartup
- ruby 之watir
- 少男杀手dodolook签约酷6网原创红人阵营
- index+match函数/一对多查找匹配,可以代替Vlookup函数的使用。
- 宁可编译和链接时出错,也不要运行时出错
- Tracert与Traceroute[转]
- 谷歌浏览器播放h.265_Google删除H.264
- inductive bias:归纳偏置
- 福布斯:区块链科技从边缘到主流的…
- 用C++封装一个简单的英汉词典
- (保姆级)利用ffmpeg将flv批量转mp4
热门文章
- 学习yade的日常犯错2019.4.7
- PCL 库的安装与应用
- 视频教程-机器学习数学基础--概率论与数理统计视频教学-机器学习
- Linux source 命令的作用
- ubuntu 搜狗输入法只能打出繁体字
- 让错的程序看得出错(简体中文)(Making Wrong Code Look Wrong)--让错误代码显得错误
- 优维科技王津银:SaaS模式是中小企业实现数字化转型的“利器”
- 初识机器学习-理论篇
- 深度学习+心脏医学图像分割——自动心脏诊断挑战赛(ACDC)项目的代码学习记录
- 电脑商情报LUCENE.CN中文搜索CLUB聚会