Cubemx_STM32H743 触摸屏
1. 触摸屏的分类
1.1 电阻式触摸屏
在 Iphone 面世之前,几乎清一色的都是使用电阻式触摸屏,电阻式触摸屏利用压力感应进行触点检测控制,需要直接应力接触,通过检测电阻来定位触摸位置。
ALIENTEK 2.4/2.8/3.5 寸 LCD 模块自带的触摸屏都属于电阻式触摸屏,下面简单介绍下电阻式触摸屏的原理。电阻触摸屏的主要部分是一块与显示器表面非常配合的电阻薄膜屏,这是一种多层的复合薄膜,它以一层玻璃或硬塑料平板作为基层,表面涂有一层透明氧化金属(透明的导电电阻)导电层,上面再盖有一层外表面硬化处理、光滑防擦的塑料层、它的内表面也涂有一层涂层、
在他们之间有许多细小的(小于 1/1000 英寸)的透明隔离点把两层导电层隔开绝缘。 当手指触摸屏幕时,两层导电层在触摸点位置就有了接触,电阻发生变化,在 X 和 Y 两个方向上产生信号,然后送触摸屏控制器。控制器侦测到这一接触并计算出(X,Y)的位置,再根据获得的位置模拟鼠标的方式运作。这就是电阻技术触摸屏的最基本的原理。
电阻触摸屏的优点:精度高、价格便宜、抗干扰能力强、稳定性好。
电阻触摸屏的缺点:容易被划伤、透光性不太好、不支持多点触摸。
从以上介绍可知,触摸屏都需要一个 AD 转换器,一般来说是需要一个控制器的。ALIENTEK LCD 模块选择的是四线电阻式触摸屏,这种触摸屏的控制芯片有很多,包括:
ADS7843、ADS7846、TSC2046、XPT2046 和 AK4182 等。这几款芯片的驱动基本上是一样的,也就是你只要写出了 ADS7843 的驱动,这个驱动对其他几个芯片也是有效的。而且封装也有一样的,完全 PIN TO PIN 兼容。所以在替换起来,很方便。
ALIENTEK LCD 模块自带的触摸屏控制芯片为 XPT2046。XPT2046 是一款 4 导线制触摸屏控制器,内含 12 位分辨率 125KHz 转换速率逐步逼近型 A/D 转换器。XPT2046 支持从 1.5V到 5.25V 的低电压 I/O 接口。XPT2046 能通过执行两次 A/D 转换查出被按的屏幕位置, 除此之外,还可以测量加在触摸屏上的压力。内部自带 2.5V 参考电压可以作为辅助输入、温度测量和电池监测模式之用,电池监测的电压范围可以从 0V 到 6V。XPT2046 片内集成有一个温度传感器。在 2.7V 的典型工作状态下,关闭参考电压,功耗可小于 0.75mW。XPT2046 采用微小的封装形式:TSSOP-16,QFN-16(0.75mm 厚度)和 VFBGA-48。工作温度范围为-40℃~+85℃。
该芯片完全是兼容 ADS7843 和 ADS7846 的,关于这个芯片的详细使用,可以参考这两个芯片的 datasheet。电阻式触摸屏就介绍到这里。
1.2 电容式触摸屏
现在几乎所有智能手机,包括平板电脑都是采用电容屏作为触摸屏,电容屏是利用人体感应进行触点检测控制,不需要直接接触或只需要轻微接触,通过检测感应电流来定位触摸坐标。
ALIENTEK 4.3/7 寸 LCD 模块自带的触摸屏采用的是电容式触摸屏,下面简单介绍下电容
式触摸屏的原理。
电容式触摸屏主要分为两种:
1、 表面电容式电容触摸屏。
表面电容式触摸屏技术是利用 ITO(铟锡氧化 物,是一种透明的导电材料)导电膜,通过电 场感应方式感测屏幕表面的触摸行为进行。 但是表面电容式触摸屏有一些局限性,它只 能识别一个手指或者一次触摸。
2、 投射式电容触摸屏。
投射电容式触摸屏是传感器利用触摸屏电极 发射出静电场线。一般用于投射电容传感技 术的电容类型有两种:自我电容和交互电容。
自我电容又称绝对电容,是最广为采用的一种方法,自我电容通常是指扫描电极与地构成的电容。在玻璃表面有用 ITO 制成的横向与纵向的扫描电极,这些电极和地之间就构成一个电容的两极。当用手或触摸笔触摸的时候就会并联一个电容到电路中去,从而使在该条扫描线上的总体的电容量有所改变。在扫描的时候,控制 IC 依次扫描纵向和横向电极,并根据扫描前后的电容变化来确定触摸点坐标位置。笔记本电脑触摸输入板就是采用的这种方式,笔记本电脑的输入板采用 X* Y 的传感电极阵列形成一个传感格子,当手指靠近触摸输入板时,在手指和传感电极之间产生一个小量电荷。采用特定的运算法则处理来自行、列传感器的信号来确定手指的位置。
交互电容又叫做跨越电容,它是在玻璃表面的横向和纵向的 ITO 电极的交叉处形成电容。交互电容的扫描方式就是扫描每个交叉处的电容变化,来判定触摸点的位置。当触摸的时候就会影响到相邻电极的耦合,从而改变交叉处的电容量,交互电容的扫面方法可以侦测到每个交叉点的电容值和触摸后电容变化,因而它需要的扫描时间与自我电容的扫描方式相比要长一些,需要扫描检测 X 乘以Y 根电极。目前智能手机/平板电脑等的触摸屏,都是采用交互电容技术。
ALIENTEK 所选择的电容触摸屏,也是采用的是投射式电容屏(交互电容类型),所以后面仅以投射式电容屏作为介绍。
透射式电容触摸屏采用纵横两列电极组成感应矩阵,来感应触摸。以两个交叉的电极矩阵,即: X 轴电极和 Y 轴电极,来检测每一格感应单元的电容变化,如图 36.1.2.1 所示:
示意图中的电极,实际是透明的,这里是为了方便大家理解。图中,X、Y 轴的透明电极电容屏的精度、分辨率与 X、Y 轴的通道数有关,通道数越多,精度越高。以上就是电容触摸屏的基本原理,接下来看看电容触摸屏的优缺点:
电容触摸屏的优点:手感好、无需校准、支持多点触摸、透光性好。
电容触摸屏的缺点:成本高、精度不高、抗干扰能力差。
这里特别提醒大家电容触摸屏对工作环境的要求是比较高的,在潮湿、多尘、高低温环境下面,都是不适合使用电容屏的。
电容触摸屏一般都需要一个驱动 IC 来检测电容触摸,且一般是通过 IIC 接口输出触摸数据的。ALIENTEK 7’LCD 模块的电容触摸屏,使用 FT5206/FT5426 做为驱动 IC,采用的是 1528
的驱动结构(15 个感应通道,28 个驱动通道)。ALIENTEK 4.3’LCD 模块则使用
GT9147/OTT2001A 作为驱动 IC,采用 1710 的驱动结构(10 个感应通道,17 个驱动通道)。
这两个模块都只支持最多 5 点触摸,本例程除 CPLD 方案的 V1 版本 7 寸屏模块不支持外,其他所有 ALIENTEK 的 LCD 模块都支持,电容触摸驱动 IC,这里只介绍 FT5426 的驱动,OTT2001A、FT5206 和 FT5426 的驱动同 GT9147 类似,大家可以参考着学习即可。
1.3 FT5426驱动芯片介绍
1.3.1 接口说明
下面我们简单介绍下 FT5426,该芯片是一颗电容触摸屏驱动 IC ,实际上正点原子7寸屏幕,触摸屏驱动芯片是CST340,资料基本上找不到,但是兼容FT5426.
FT5426与 MCU 连接是通过 4 根线:SDA、SCL、RST 和 INT。其中:SDA 和 SCL 是 IIC
通信用的,RST 是复位脚(低电平有效),INT 是中断输出信号,关于 IIC 我们就不详细介绍了,请参考前面文章
FT5426的 IIC 地址,写0x70,读0x71。采用标准IIC通讯,最大通讯速率为400KHZ。
写读操作如下:
1.3.2 寄存器介绍
1、主要介绍0X00,0XA4,0X80,0X88,0XA1,0XA2,0X02,0X03-0X06,0X09-0X0C,0X0F-0X12,0x15-0X18,0X1B-0X1E等.
1.3.3 FT5206初始化流程
FT5206初始化流程非常简单,首先通过CT_RST引脚对FT5206进行一次复位,让FT5206进入正常模式。然后设置工作模式、中断状态、触摸阈值和激活周期等参数就可以对FT5206的初始化。
初始化完成便可以读取触摸坐标数据了,先读取0x02寄存器,判断有多少个有效触点,然后读取0x03-0x1E,便可以获得触摸坐标数据了。
1.3 特征
电容屏不需要校准、电阻屏需要校准 。
2 Cubemx及驱动
直接使用的端口模拟I2C。
直接赋上正点原子的代码吧,比较乱,而且用的芯片CST340得用FT5206替代。
ft5206.c
#include "ft5206.h"
#include "touch.h"
#include "ctiic.h"
#include "usart.h"
#include "pub_delay.h"
#include "string.h"
#include "lcd.h"
#include "gt9147.h"
//
//本程序只供学习使用,未经作者许可,不得用于其它任何用途
//ALIENTEK STM32开发板
//7寸电容触摸屏-FT5206 驱动代码
//正点原子@ALIENTEK
//技术论坛:www.openedv.com
//创建日期:2015/12/28
//版本:V1.0
//版权所有,盗版必究。
//Copyright(C) 广州市星翼电子科技有限公司 2014-2024
//All rights reserved
//
//向FT5206写入一次数据
//reg:起始寄存器地址
//buf:数据缓缓存区
//len:写数据长度
//返回值:0,成功;1,失败.
u8 FT5206_WR_Reg(u16 reg,u8 *buf,u8 len)
{
u8 i;
u8 ret=0;
CT_IIC_Start();
CT_IIC_Send_Byte(FT_CMD_WR); //发送写命令
CT_IIC_Wait_Ack();
CT_IIC_Send_Byte(reg&0XFF); //发送低8位地址
CT_IIC_Wait_Ack();
for(i=0;i<len;i++)
{
CT_IIC_Send_Byte(buf[i]); //发数据
ret=CT_IIC_Wait_Ack();
if(ret)break;
}
CT_IIC_Stop(); //产生一个停止条件
return ret;
}
//从FT5206读出一次数据
//reg:起始寄存器地址
//buf:数据缓缓存区
//len:读数据长度
void FT5206_RD_Reg(u16 reg,u8 *buf,u8 len)
{
u8 i;
CT_IIC_Start();
CT_IIC_Send_Byte(FT_CMD_WR); //发送写命令
CT_IIC_Wait_Ack();
CT_IIC_Send_Byte(reg&0XFF); //发送低8位地址
CT_IIC_Wait_Ack();
CT_IIC_Start();
CT_IIC_Send_Byte(FT_CMD_RD); //发送读命令
CT_IIC_Wait_Ack();
for(i=0;i<len;i++)
{
buf[i]=CT_IIC_Read_Byte(i==(len-1)?0:1); //发数据
}
CT_IIC_Stop();//产生一个停止条件
}
u8 CIP[5]; //用来存放触摸IC-GT911
//初始化FT5206触摸屏
//返回值:0,初始化成功;1,初始化失败
u8 FT5206_Init(void)
{
/*
PG3 ---- T_MISO ----
PI3 ---- T_MOSI ---- CT_SDA
PH6 ---- T_SCK ---- CT_SCL
PH7 ---- T_PEN ---- CT_INT
PI8 ---- T_CS ---- CT_RST
*/
u8 temp[5];
u8 res=1;
// GPIO_InitTypeDef GPIO_Initure;
// __HAL_RCC_GPIOH_CLK_ENABLE(); //开启GPIOH时钟
// __HAL_RCC_GPIOI_CLK_ENABLE(); //开启GPIOI时钟
//
// //PH7
// GPIO_Initure.Pin=GPIO_PIN_7; //PH7
// GPIO_Initure.Mode=GPIO_MODE_INPUT; //输入
// GPIO_Initure.Pull=GPIO_PULLUP; //上拉
// GPIO_Initure.Speed=GPIO_SPEED_FREQ_VERY_HIGH; //高速
// HAL_GPIO_Init(GPIOH,&GPIO_Initure); //初始化
//
// //PI8
// GPIO_Initure.Pin=GPIO_PIN_8; //PI8
// GPIO_Initure.Mode=GPIO_MODE_OUTPUT_PP; //推挽输出
// HAL_GPIO_Init(GPIOI,&GPIO_Initure); //初始化
CT_IIC_Init(); //初始化电容屏的I2C总线
FT_RST(0); //复位
HAL_Delay(20);
FT_RST(1); //释放复位
HAL_Delay(50);
temp[0]=0;
//读取版本号,参考值:0x3003
FT5206_RD_Reg(FT_ID_G_LIB_VERSION,&temp[0],2);
if((temp[0]==0X30&&temp[1]==0X03)||temp[1]==0X01||temp[1]==0X02)//版本:0X3003/0X0001/0X0002
{
printf("CTP ID:%x\r\n",((u16)temp[0]<<8)+temp[1]);
FT5206_WR_Reg(FT_DEVIDE_MODE,temp,1); //进入正常操作模式
FT5206_WR_Reg(FT_ID_G_MODE,temp,1); //查询模式
temp[0]=22; //触摸有效值,22,越小越灵敏
FT5206_WR_Reg(FT_ID_G_THGROUP,temp,1); //设置触摸有效值
temp[0]=12; //激活周期,不能小于12,最大14
FT5206_WR_Reg(FT_ID_G_PERIODACTIVE,temp,1);
res = 0;
}
else
{
GT9147_RD_Reg(GT_PID_REG,temp,4); //读取产品ID
temp[4]=0;
printf("CTP ID:%s\r\n", temp); //打印ID
memcpy(CIP,temp, sizeof(u8)*4);
temp[0]=0X02;
GT9147_WR_Reg(GT_CTRL_REG,temp,1); //软复位GT9XXX
GT9147_RD_Reg(GT_CFGS_REG,temp,1); //读取GT_CFGS_REG寄存器
HAL_Delay(10);
temp[0]=0X00;
GT9147_WR_Reg(GT_CTRL_REG,temp,1); //结束复位, 进入读坐标状态
res = 0;
}
return res;
}
const u16 FT5206_TPX_TBL[5]={FT_TP1_REG,FT_TP2_REG,FT_TP3_REG,FT_TP4_REG,FT_TP5_REG};
//GT911属于GT9xx系列,所以直接调用gt9147的相关宏定义和调用相关函数
const u16 GT911_TPX_TBL[5]={GT_TP1_REG,GT_TP2_REG,GT_TP3_REG,GT_TP4_REG,GT_TP5_REG};
u8 g_gt_tnum=5; //默认支持的触摸屏点数(5点触摸)
//扫描触摸屏(采用查询方式)
//mode:0,正常扫描.
//返回值:当前触屏状态.
//0,触屏无触摸;1,触屏有触摸
u8 FT5206_Scan(u8 mode)
{
u8 buf[4];
u8 i=0;
u8 res=0;
u8 temp;
u16 tempsta;
static u8 t=0;//控制查询间隔,从而降低CPU占用率
t++;
if((t%10)==0||t<10)//空闲时,每进入10次CTP_Scan函数才检测1次,从而节省CPU使用率
{
FT5206_RD_Reg(FT_REG_NUM_FINGER,&mode,1);//读取触摸点的状态
if((mode&0XF)&&((mode&0XF)<=g_gt_tnum))
{
temp=0XFF<<(mode&0XF);//将点的个数转换为1的位数,匹配tp_dev.sta定义
tempsta=tp_dev.sta; //保存当前的tp_dev.sta值
tp_dev.sta=(~temp)|TP_PRES_DOWN|TP_CATH_PRES;
tp_dev.x[g_gt_tnum-1]=tp_dev.x[0];//保存触点0的数据,保存在最后一个上
tp_dev.y[g_gt_tnum-1]=tp_dev.y[0];
for(i=0;i<g_gt_tnum;i++)
{
if(tp_dev.sta&(1<<i)) //触摸有效?
{
if(strcmp((char *)CIP,"911")==0) //触摸IC 911
{
GT9147_RD_Reg(GT911_TPX_TBL[i],buf,4); //读取XY坐标值
if(tp_dev.touchtype&0X01) //横屏
{
tp_dev.y[i]=lcddev.height-(((u16)buf[3]<<8)+buf[2]);
tp_dev.x[i]=lcddev.width-(((u16)buf[1]<<8)+buf[0]);
}
else
{
tp_dev.x[i]=((u16)buf[3]<<8)+buf[2];
tp_dev.y[i]=lcddev.height-(((u16)buf[1]<<8)+buf[0]);
}
}
else
{
FT5206_RD_Reg(FT5206_TPX_TBL[i],buf,4); //读取XY坐标值
if(tp_dev.touchtype&0X01)//横屏
{
tp_dev.y[i]=((u16)(buf[0]&0X0F)<<8)+buf[1];
tp_dev.x[i]=((u16)(buf[2]&0X0F)<<8)+buf[3];
}else
{
tp_dev.x[i]=lcddev.width-(((u16)(buf[0]&0X0F)<<8)+buf[1]);
tp_dev.y[i]=((u16)(buf[2]&0X0F)<<8)+buf[3];
}
}
//printf("x[%d]:%d,y[%d]:%d\r\n",i,tp_dev.x[i],i,tp_dev.y[i]);
}
}
res=1;
if(tp_dev.x[0]>lcddev.width||tp_dev.y[0]>lcddev.height) //非法数据(坐标超出了)
{
if((mode&0XF)>1) // 有其他点有数据,则复第二个触点的数据到第一个触点.
{
tp_dev.x[0]=tp_dev.x[1];
tp_dev.y[0]=tp_dev.y[1];
t=0; // 触发一次,则会最少连续监测10次,从而提高命中率
}
else // 非法数据,则忽略此次数据(还原原来的)
{
tp_dev.x[0]=tp_dev.x[g_gt_tnum-1];
tp_dev.y[0]=tp_dev.y[g_gt_tnum-1];
mode=0X80;
tp_dev.sta=tempsta; // 恢复tp_dev.sta
}
}
else t=0; // 触发一次,则会最少连续监测10次,从而提高命中率
}
}
if((mode&0X1F)==0)//无触摸点按下
{
if(tp_dev.sta&TP_PRES_DOWN) //之前是被按下的
{
tp_dev.sta&=~TP_PRES_DOWN; //标记按键松开
}else //之前就没有被按下
{
tp_dev.x[0]=0xffff;
tp_dev.y[0]=0xffff;
tp_dev.sta&=0XE0; //清除点有效标记
}
}
if(t>240)t=10;//重新从10开始计数
return res;
}
ft5206.h
#ifndef __FT5206_H
#define __FT5206_H
#include "sys.h"
//
//本程序只供学习使用,未经作者许可,不得用于其它任何用途
//ALIENTEK STM32开发板
//7寸电容触摸屏-FT5206 驱动代码
//正点原子@ALIENTEK
//技术论坛:www.openedv.com
//创建日期:2015/12/28
//版本:V1.0
//版权所有,盗版必究。
//Copyright(C) 广州市星翼电子科技有限公司 2014-2024
//All rights reserved
//
//与电容触摸屏连接的芯片引脚(未包含IIC引脚)
//IO操作函数
#define FT_RST(n) (n?HAL_GPIO_WritePin(GPIOI,GPIO_PIN_8,GPIO_PIN_SET):HAL_GPIO_WritePin(GPIOI,GPIO_PIN_8,GPIO_PIN_RESET))//FT5206复位引脚
#define FT_INT HAL_GPIO_ReadPin(GPIOH,GPIO_PIN_7) //FT5206断引脚
//I2C读写命令
#define FT_CMD_WR 0X70 //写命令
#define FT_CMD_RD 0X71 //读命令
//FT5206 部分寄存器定义
#define FT_DEVIDE_MODE 0x00 //FT5206模式控制寄存器
#define FT_REG_NUM_FINGER 0x02 //触摸状态寄存器
#define FT_TP1_REG 0X03 //第一个触摸点数据地址
#define FT_TP2_REG 0X09 //第二个触摸点数据地址
#define FT_TP3_REG 0X0F //第三个触摸点数据地址
#define FT_TP4_REG 0X15 //第四个触摸点数据地址
#define FT_TP5_REG 0X1B //第五个触摸点数据地址
#define FT_ID_G_LIB_VERSION 0xA1 //版本
#define FT_ID_G_MODE 0xA4 //FT5206中断模式控制寄存器
#define FT_ID_G_THGROUP 0x80 //触摸有效值设置寄存器
#define FT_ID_G_PERIODACTIVE 0x88 //激活状态周期设置寄存器
u8 FT5206_WR_Reg(u16 reg,u8 *buf,u8 len);
void FT5206_RD_Reg(u16 reg,u8 *buf,u8 len);
u8 FT5206_Init(void);
u8 FT5206_Scan(u8 mode);
#endif
touch.c
#include "touch.h"
#include "lcd.h"
#include "pub_delay.h"
#include "stdlib.h"
#include "math.h"
#include "24cxx.h"
#include "gt9147.h"
//
//本程序只供学习使用,未经作者许可,不得用于其它任何用途
//ALIENTEK STM32开发板
//触摸屏驱动(支持ADS7843/7846/UH7843/7846/XPT2046/TSC2046/OTT2001A等) 代码
//正点原子@ALIENTEK
//技术论坛:www.openedv.com
//创建日期:2015/12/30
//版本:V1.0
//版权所有,盗版必究。
//Copyright(C) 广州市星翼电子科技有限公司 2014-2024
//All rights reserved
//********************************************************************************
//修改说明
//无
//
_m_tp_dev tp_dev=
{
TP_Init,
TP_Scan,
TP_Adjust,
0,
0,
0,
0,
0,
0,
0,
0,
};
//默认为touchtype=0的数据.
u8 CMD_RDX=0XD0;
u8 CMD_RDY=0X90;
//SPI写数据
//向触摸屏IC写入1byte数据
//num:要写入的数据
void TP_Write_Byte(u8 num)
{
u8 count=0;
for(count=0;count<8;count++)
{
if(num&0x80)TDIN(1);
else TDIN(0);
num<<=1;
TCLK(0);
delay_us(1);
TCLK(1); //上升沿有效
}
}
//SPI读数据
//从触摸屏IC读取adc值
//CMD:指令
//返回值:读到的数据
u16 TP_Read_AD(u8 CMD)
{
u8 count=0;
u16 Num=0;
TCLK(0); //先拉低时钟
TDIN(0); //拉低数据线
TCS(0); //选中触摸屏IC
TP_Write_Byte(CMD);//发送命令字
delay_us(6);//ADS7846的转换时间最长为6us
TCLK(0);
delay_us(1);
TCLK(1); //给1个时钟,清除BUSY
delay_us(1);
TCLK(0);
for(count=0;count<16;count++)//读出16位数据,只有高12位有效
{
Num<<=1;
TCLK(0); //下降沿有效
delay_us(1);
TCLK(1);
if(DOUT)Num++;
}
Num>>=4; //只有高12位有效.
TCS(1); //释放片选
return(Num);
}
//读取一个坐标值(x或者y)
//连续读取READ_TIMES次数据,对这些数据升序排列,
//然后去掉最低和最高LOST_VAL个数,取平均值
//xy:指令(CMD_RDX/CMD_RDY)
//返回值:读到的数据
#define READ_TIMES 5 //读取次数
#define LOST_VAL 1 //丢弃值
u16 TP_Read_XOY(u8 xy)
{
u16 i, j;
u16 buf[READ_TIMES];
u16 sum=0;
u16 temp;
for(i=0;i<READ_TIMES;i++)buf[i]=TP_Read_AD(xy);
for(i=0;i<READ_TIMES-1; i++)//排序
{
for(j=i+1;j<READ_TIMES;j++)
{
if(buf[i]>buf[j])//升序排列
{
temp=buf[i];
buf[i]=buf[j];
buf[j]=temp;
}
}
}
sum=0;
for(i=LOST_VAL;i<READ_TIMES-LOST_VAL;i++)sum+=buf[i];
temp=sum/(READ_TIMES-2*LOST_VAL);
return temp;
}
//读取x,y坐标
//最小值不能少于100.
//x,y:读取到的坐标值
//返回值:0,失败;1,成功。
u8 TP_Read_XY(u16 *x,u16 *y)
{
u16 xtemp,ytemp;
xtemp=TP_Read_XOY(CMD_RDX);
ytemp=TP_Read_XOY(CMD_RDY);
//if(xtemp<100||ytemp<100)return 0;//读数失败
*x=xtemp;
*y=ytemp;
return 1;//读数成功
}
//连续2次读取触摸屏IC,且这两次的偏差不能超过
//ERR_RANGE,满足条件,则认为读数正确,否则读数错误.
//该函数能大大提高准确度
//x,y:读取到的坐标值
//返回值:0,失败;1,成功。
#define ERR_RANGE 50 //误差范围
u8 TP_Read_XY2(u16 *x,u16 *y)
{
u16 x1,y1;
u16 x2,y2;
u8 flag;
flag=TP_Read_XY(&x1,&y1);
if(flag==0)return(0);
flag=TP_Read_XY(&x2,&y2);
if(flag==0)return(0);
if(((x2<=x1&&x1<x2+ERR_RANGE)||(x1<=x2&&x2<x1+ERR_RANGE))//前后两次采样在+-50内
&&((y2<=y1&&y1<y2+ERR_RANGE)||(y1<=y2&&y2<y1+ERR_RANGE)))
{
*x=(x1+x2)/2;
*y=(y1+y2)/2;
return 1;
}else return 0;
}
//
//与LCD部分有关的函数
//画一个触摸点
//用来校准用的
//x,y:坐标
//color:颜色
void TP_Drow_Touch_Point(u16 x,u16 y,u16 color)
{
POINT_COLOR=color;
LCD_DrawLine(x-12,y,x+13,y);//横线
LCD_DrawLine(x,y-12,x,y+13);//竖线
LCD_DrawPoint(x+1,y+1);
LCD_DrawPoint(x-1,y+1);
LCD_DrawPoint(x+1,y-1);
LCD_DrawPoint(x-1,y-1);
LCD_Draw_Circle(x,y,6);//画中心圈
}
//画一个大点(2*2的点)
//x,y:坐标
//color:颜色
void TP_Draw_Big_Point(u16 x,u16 y,u16 color)
{
POINT_COLOR=color;
LCD_DrawPoint(x,y);//中心点
LCD_DrawPoint(x+1,y);
LCD_DrawPoint(x,y+1);
LCD_DrawPoint(x+1,y+1);
}
//
//触摸按键扫描
//tp:0,屏幕坐标;1,物理坐标(校准等特殊场合用)
//返回值:当前触屏状态.
//0,触屏无触摸;1,触屏有触摸
u8 TP_Scan(u8 tp)
{
if(PEN==0)//有按键按下
{
if(tp)TP_Read_XY2(&tp_dev.x[0],&tp_dev.y[0]);//读取物理坐标
else if(TP_Read_XY2(&tp_dev.x[0],&tp_dev.y[0]))//读取屏幕坐标
{
tp_dev.x[0]=tp_dev.xfac*tp_dev.x[0]+tp_dev.xoff;//将结果转换为屏幕坐标
tp_dev.y[0]=tp_dev.yfac*tp_dev.y[0]+tp_dev.yoff;
}
if((tp_dev.sta&TP_PRES_DOWN)==0)//之前没有被按下
{
tp_dev.sta=TP_PRES_DOWN|TP_CATH_PRES;//按键按下
tp_dev.x[4]=tp_dev.x[0];//记录第一次按下时的坐标
tp_dev.y[4]=tp_dev.y[0];
}
}else
{
if(tp_dev.sta&TP_PRES_DOWN)//之前是被按下的
{
tp_dev.sta&=~(1<<7);//标记按键松开
}else//之前就没有被按下
{
tp_dev.x[4]=0;
tp_dev.y[4]=0;
tp_dev.x[0]=0xffff;
tp_dev.y[0]=0xffff;
}
}
return tp_dev.sta&TP_PRES_DOWN;//返回当前的触屏状态
}
//
//保存在EEPROM里面的地址区间基址,占用13个字节(RANGE:SAVE_ADDR_BASE~SAVE_ADDR_BASE+12)
#define SAVE_ADDR_BASE 40
//保存校准参数
void TP_Save_Adjdata(void)
{
int temp;
//保存校正结果!
temp=tp_dev.xfac*100000000;//保存x校正因素
AT24CXX_WriteLenByte(SAVE_ADDR_BASE,temp,4);
temp=tp_dev.yfac*100000000;//保存y校正因素
AT24CXX_WriteLenByte(SAVE_ADDR_BASE+4,temp,4);
//保存x偏移量
AT24CXX_WriteLenByte(SAVE_ADDR_BASE+8,tp_dev.xoff,2);
//保存y偏移量
AT24CXX_WriteLenByte(SAVE_ADDR_BASE+10,tp_dev.yoff,2);
//保存触屏类型
AT24CXX_WriteOneByte(SAVE_ADDR_BASE+12,tp_dev.touchtype);
temp=0X0A;//标记校准过了
AT24CXX_WriteOneByte(SAVE_ADDR_BASE+13,temp);
}
//得到保存在EEPROM里面的校准值
//返回值:1,成功获取数据
// 0,获取失败,要重新校准
u8 TP_Get_Adjdata(void)
{
int tempfac;
tempfac=AT24CXX_ReadOneByte(SAVE_ADDR_BASE+13);//读取标记字,看是否校准过!
if(tempfac==0X0A)//触摸屏已经校准过了
{
tempfac=AT24CXX_ReadLenByte(SAVE_ADDR_BASE,4);
tp_dev.xfac=(float)tempfac/100000000;//得到x校准参数
tempfac=AT24CXX_ReadLenByte(SAVE_ADDR_BASE+4,4);
tp_dev.yfac=(float)tempfac/100000000;//得到y校准参数
//得到x偏移量
tp_dev.xoff=AT24CXX_ReadLenByte(SAVE_ADDR_BASE+8,2);
//得到y偏移量
tp_dev.yoff=AT24CXX_ReadLenByte(SAVE_ADDR_BASE+10,2);
tp_dev.touchtype=AT24CXX_ReadOneByte(SAVE_ADDR_BASE+12);//读取触屏类型标记
if(tp_dev.touchtype)//X,Y方向与屏幕相反
{
CMD_RDX=0X90;
CMD_RDY=0XD0;
}else //X,Y方向与屏幕相同
{
CMD_RDX=0XD0;
CMD_RDY=0X90;
}
return 1;
}
return 0;
}
//提示字符串
u8* const TP_REMIND_MSG_TBL="Please use the stylus click the cross on the screen.The cross will always move until the screen adjustment is completed.";
//提示校准结果(各个参数)
void TP_Adj_Info_Show(u16 x0,u16 y0,u16 x1,u16 y1,u16 x2,u16 y2,u16 x3,u16 y3,u16 fac)
{
POINT_COLOR=RED;
LCD_ShowString(40,160,lcddev.width,lcddev.height,16,"x1:");
LCD_ShowString(40+80,160,lcddev.width,lcddev.height,16,"y1:");
LCD_ShowString(40,180,lcddev.width,lcddev.height,16,"x2:");
LCD_ShowString(40+80,180,lcddev.width,lcddev.height,16,"y2:");
LCD_ShowString(40,200,lcddev.width,lcddev.height,16,"x3:");
LCD_ShowString(40+80,200,lcddev.width,lcddev.height,16,"y3:");
LCD_ShowString(40,220,lcddev.width,lcddev.height,16,"x4:");
LCD_ShowString(40+80,220,lcddev.width,lcddev.height,16,"y4:");
LCD_ShowString(40,240,lcddev.width,lcddev.height,16,"fac is:");
LCD_ShowNum(40+24,160,x0,4,16); //显示数值
LCD_ShowNum(40+24+80,160,y0,4,16); //显示数值
LCD_ShowNum(40+24,180,x1,4,16); //显示数值
LCD_ShowNum(40+24+80,180,y1,4,16); //显示数值
LCD_ShowNum(40+24,200,x2,4,16); //显示数值
LCD_ShowNum(40+24+80,200,y2,4,16); //显示数值
LCD_ShowNum(40+24,220,x3,4,16); //显示数值
LCD_ShowNum(40+24+80,220,y3,4,16); //显示数值
LCD_ShowNum(40+56,240,fac,3,16); //显示数值,该数值必须在95~105范围之内.
}
//触摸屏校准代码
//得到四个校准参数
void TP_Adjust(void)
{
u16 pos_temp[4][2];//坐标缓存值
u8 cnt=0;
u16 d1,d2;
u32 tem1,tem2;
double fac;
u16 outtime=0;
cnt=0;
POINT_COLOR=BLUE;
BACK_COLOR =WHITE;
LCD_Clear(WHITE);//清屏
POINT_COLOR=RED;//红色
LCD_Clear(WHITE);//清屏
POINT_COLOR=BLACK;
LCD_ShowString(40,40,160,100,16,(u8*)TP_REMIND_MSG_TBL);//显示提示信息
TP_Drow_Touch_Point(20,20,RED);//画点1
tp_dev.sta=0;//消除触发信号
tp_dev.xfac=0;//xfac用来标记是否校准过,所以校准之前必须清掉!以免错误
while(1)//如果连续10秒钟没有按下,则自动退出
{
tp_dev.scan(1);//扫描物理坐标
if((tp_dev.sta&0xc0)==TP_CATH_PRES)//按键按下了一次(此时按键松开了.)
{
outtime=0;
tp_dev.sta&=~(1<<6);//标记按键已经被处理过了.
pos_temp[cnt][0]=tp_dev.x[0];
pos_temp[cnt][1]=tp_dev.y[0];
cnt++;
switch(cnt)
{
case 1:
TP_Drow_Touch_Point(20,20,WHITE); //清除点1
TP_Drow_Touch_Point(lcddev.width-20,20,RED); //画点2
break;
case 2:
TP_Drow_Touch_Point(lcddev.width-20,20,WHITE); //清除点2
TP_Drow_Touch_Point(20,lcddev.height-20,RED); //画点3
break;
case 3:
TP_Drow_Touch_Point(20,lcddev.height-20,WHITE); //清除点3
TP_Drow_Touch_Point(lcddev.width-20,lcddev.height-20,RED); //画点4
break;
case 4: //全部四个点已经得到
//对边相等
tem1=abs(pos_temp[0][0]-pos_temp[1][0]);//x1-x2
tem2=abs(pos_temp[0][1]-pos_temp[1][1]);//y1-y2
tem1*=tem1;
tem2*=tem2;
d1=sqrt(tem1+tem2);//得到1,2的距离
tem1=abs(pos_temp[2][0]-pos_temp[3][0]);//x3-x4
tem2=abs(pos_temp[2][1]-pos_temp[3][1]);//y3-y4
tem1*=tem1;
tem2*=tem2;
d2=sqrt(tem1+tem2);//得到3,4的距离
fac=(float)d1/d2;
if(fac<0.95||fac>1.05||d1==0||d2==0)//不合格
{
cnt=0;
TP_Drow_Touch_Point(lcddev.width-20,lcddev.height-20,WHITE); //清除点4
TP_Drow_Touch_Point(20,20,RED); //画点1
TP_Adj_Info_Show(pos_temp[0][0],pos_temp[0][1],pos_temp[1][0],pos_temp[1][1],pos_temp[2][0],pos_temp[2][1],pos_temp[3][0],pos_temp[3][1],fac*100);//显示数据
continue;
}
tem1=abs(pos_temp[0][0]-pos_temp[2][0]);//x1-x3
tem2=abs(pos_temp[0][1]-pos_temp[2][1]);//y1-y3
tem1*=tem1;
tem2*=tem2;
d1=sqrt(tem1+tem2);//得到1,3的距离
tem1=abs(pos_temp[1][0]-pos_temp[3][0]);//x2-x4
tem2=abs(pos_temp[1][1]-pos_temp[3][1]);//y2-y4
tem1*=tem1;
tem2*=tem2;
d2=sqrt(tem1+tem2);//得到2,4的距离
fac=(float)d1/d2;
if(fac<0.95||fac>1.05)//不合格
{
cnt=0;
TP_Drow_Touch_Point(lcddev.width-20,lcddev.height-20,WHITE); //清除点4
TP_Drow_Touch_Point(20,20,RED); //画点1
TP_Adj_Info_Show(pos_temp[0][0],pos_temp[0][1],pos_temp[1][0],pos_temp[1][1],pos_temp[2][0],pos_temp[2][1],pos_temp[3][0],pos_temp[3][1],fac*100);//显示数据
continue;
}//正确了
//对角线相等
tem1=abs(pos_temp[1][0]-pos_temp[2][0]);//x1-x3
tem2=abs(pos_temp[1][1]-pos_temp[2][1]);//y1-y3
tem1*=tem1;
tem2*=tem2;
d1=sqrt(tem1+tem2);//得到1,4的距离
tem1=abs(pos_temp[0][0]-pos_temp[3][0]);//x2-x4
tem2=abs(pos_temp[0][1]-pos_temp[3][1]);//y2-y4
tem1*=tem1;
tem2*=tem2;
d2=sqrt(tem1+tem2);//得到2,3的距离
fac=(float)d1/d2;
if(fac<0.95||fac>1.05)//不合格
{
cnt=0;
TP_Drow_Touch_Point(lcddev.width-20,lcddev.height-20,WHITE); //清除点4
TP_Drow_Touch_Point(20,20,RED); //画点1
TP_Adj_Info_Show(pos_temp[0][0],pos_temp[0][1],pos_temp[1][0],pos_temp[1][1],pos_temp[2][0],pos_temp[2][1],pos_temp[3][0],pos_temp[3][1],fac*100);//显示数据
continue;
}//正确了
//计算结果
tp_dev.xfac=(float)(lcddev.width-40)/(pos_temp[1][0]-pos_temp[0][0]);//得到xfac
tp_dev.xoff=(lcddev.width-tp_dev.xfac*(pos_temp[1][0]+pos_temp[0][0]))/2;//得到xoff
tp_dev.yfac=(float)(lcddev.height-40)/(pos_temp[2][1]-pos_temp[0][1]);//得到yfac
tp_dev.yoff=(lcddev.height-tp_dev.yfac*(pos_temp[2][1]+pos_temp[0][1]))/2;//得到yoff
if(abs(tp_dev.xfac)>2||abs(tp_dev.yfac)>2)//触屏和预设的相反了.
{
cnt=0;
TP_Drow_Touch_Point(lcddev.width-20,lcddev.height-20,WHITE); //清除点4
TP_Drow_Touch_Point(20,20,RED); //画点1
LCD_ShowString(40,26,lcddev.width,lcddev.height,16,"TP Need readjust!");
tp_dev.touchtype=!tp_dev.touchtype;//修改触屏类型.
if(tp_dev.touchtype)//X,Y方向与屏幕相反
{
CMD_RDX=0X90;
CMD_RDY=0XD0;
}else //X,Y方向与屏幕相同
{
CMD_RDX=0XD0;
CMD_RDY=0X90;
}
continue;
}
POINT_COLOR=BLUE;
LCD_Clear(WHITE);//清屏
LCD_ShowString(35,110,lcddev.width,lcddev.height,16,"Touch Screen Adjust OK!");//校正完成
HAL_Delay(1000);
TP_Save_Adjdata();
LCD_Clear(WHITE);//清屏
return;//校正完成
}
}
HAL_Delay(10);
outtime++;
if(outtime>1000)
{
TP_Get_Adjdata();
break;
}
}
}
//触摸屏初始化
//返回值:0,没有进行校准
// 1,进行过校准
u8 TP_Init(void)
{
if(lcddev.id==0X1963||lcddev.id==0X7084||lcddev.id==0X7016)//SSD1963 7寸屏或者 7寸800*480/1024*600 RGB屏
{
FT5206_Init();
tp_dev.scan=FT5206_Scan; //扫描函数指向GT9147触摸屏扫描
tp_dev.touchtype|=0X80; //电容屏
tp_dev.touchtype|=lcddev.dir&0X01;//横屏还是竖屏
return 0;
}
return 1;
}
touch.h
#ifndef __TOUCH_H__
#define __TOUCH_H__
#include "sys.h"
//#include "ott2001a.h"
//#include "gt9147.h"
#include "ft5206.h"
//
//本程序只供学习使用,未经作者许可,不得用于其它任何用途
//ALIENTEK STM32开发板
//触摸屏驱动(支持ADS7843/7846/UH7843/7846/XPT2046/TSC2046/OTT2001A等) 代码
//正点原子@ALIENTEK
//技术论坛:www.openedv.com
//创建日期:2015/12/28
//版本:V1.0
//版权所有,盗版必究。
//Copyright(C) 广州市星翼电子科技有限公司 2014-2024
//All rights reserved
//********************************************************************************
//修改说明
//无
//
#define TP_PRES_DOWN 0x80 //触屏被按下
#define TP_CATH_PRES 0x40 //有按键按下了
#define CT_MAX_TOUCH 5 //电容屏支持的点数,固定为5点
//触摸屏控制器
typedef struct
{
u8 (*init)(void); //初始化触摸屏控制器
u8 (*scan)(u8); //扫描触摸屏.0,屏幕扫描;1,物理坐标;
void (*adjust)(void); //触摸屏校准
u16 x[CT_MAX_TOUCH]; //当前坐标
u16 y[CT_MAX_TOUCH]; //电容屏有最多5组坐标,电阻屏则用x[0],y[0]代表:此次扫描时,触屏的坐标,用
//x[4],y[4]存储第一次按下时的坐标.
u8 sta; //笔的状态
//b7:按下1/松开0;
//b6:0,没有按键按下;1,有按键按下.
//b5:保留
b4~b0:电容触摸屏按下的点数(0,表示未按下,1表示按下),比如5个点全部按下0001 1111 b
/触摸屏校准参数(电容屏不需要校准)//
float xfac;
float yfac;
short xoff;
short yoff;
//新增的参数,当触摸屏的左右上下完全颠倒时需要用到.
//b0:0,竖屏(适合左右为X坐标,上下为Y坐标的TP)
// 1,横屏(适合左右为Y坐标,上下为X坐标的TP)
//b1~6:保留.
//b7:0,电阻屏
// 1,电容屏
u8 touchtype;
}_m_tp_dev;
extern _m_tp_dev tp_dev; //触屏控制器在touch.c里面定义
//电阻屏芯片连接引脚
#define PEN HAL_GPIO_ReadPin(GPIOH,GPIO_PIN_7) //T_PEN
#define DOUT HAL_GPIO_ReadPin(GPIOG,GPIO_PIN_3) //T_MISO
#define TDIN(n) (n?HAL_GPIO_WritePin(GPIOI,GPIO_PIN_3,GPIO_PIN_SET):HAL_GPIO_WritePin(GPIOI,GPIO_PIN_3,GPIO_PIN_RESET))//T_MOSI
#define TCLK(n) (n?HAL_GPIO_WritePin(GPIOH,GPIO_PIN_6,GPIO_PIN_SET):HAL_GPIO_WritePin(GPIOH,GPIO_PIN_6,GPIO_PIN_RESET))//T_SCK
#define TCS(n) (n?HAL_GPIO_WritePin(GPIOI,GPIO_PIN_8,GPIO_PIN_SET):HAL_GPIO_WritePin(GPIOI,GPIO_PIN_8,GPIO_PIN_RESET))//T_CS
//电阻屏函数
void TP_Write_Byte(u8 num); //向控制芯片写入一个数据
u16 TP_Read_AD(u8 CMD); //读取AD转换值
u16 TP_Read_XOY(u8 xy); //带滤波的坐标读取(X/Y)
u8 TP_Read_XY(u16 *x,u16 *y); //双方向读取(X+Y)
u8 TP_Read_XY2(u16 *x,u16 *y); //带加强滤波的双方向坐标读取
void TP_Drow_Touch_Point(u16 x,u16 y,u16 color);//画一个坐标校准点
void TP_Draw_Big_Point(u16 x,u16 y,u16 color); //画一个大点
void TP_Save_Adjdata(void); //保存校准参数
u8 TP_Get_Adjdata(void); //读取校准参数
void TP_Adjust(void); //触摸屏校准
void TP_Adj_Info_Show(u16 x0,u16 y0,u16 x1,u16 y1,u16 x2,u16 y2,u16 x3,u16 y3,u16 fac);//显示校准信息
//电阻屏/电容屏 共用函数
u8 TP_Scan(u8 tp); //扫描
u8 TP_Init(void); //初始化
#endif
ctiic.c
#include "ctiic.h"
#include "pub_delay.h"
//
//本程序只供学习使用,未经作者许可,不得用于其它任何用途
//ALIENTEK STM32开发板
//电容触摸屏-IIC 驱动代码
//正点原子@ALIENTEK
//技术论坛:www.openedv.com
//创建日期:2015/12/28
//版本:V1.0
//版权所有,盗版必究。
//Copyright(C) 广州市星翼电子科技有限公司 2014-2024
//All rights reserved
//********************************************************************************
//无
//
//控制I2C速度的延时
void CT_Delay(void)
{
delay_us(2);
}
//电容触摸芯片IIC接口初始化
void CT_IIC_Init(void)
{
GPIO_InitTypeDef GPIO_Initure;
// __HAL_RCC_GPIOH_CLK_ENABLE(); //开启GPIOH时钟
__HAL_RCC_GPIOI_CLK_ENABLE(); //开启GPIOI时钟
// GPIO_Initure.Pin=GPIO_PIN_6; //PH6
// GPIO_Initure.Mode=GPIO_MODE_OUTPUT_OD; //开漏输出
// GPIO_Initure.Pull=GPIO_PULLUP; //上拉
// GPIO_Initure.Speed=GPIO_SPEED_FREQ_VERY_HIGH; //高速
// HAL_GPIO_Init(GPIOH,&GPIO_Initure); //初始化
GPIO_Initure.Pin=GPIO_PIN_3; //PI3
HAL_GPIO_Init(GPIOI,&GPIO_Initure); //初始化
}
//产生IIC起始信号
void CT_IIC_Start(void)
{
CT_SDA_OUT();
CT_IIC_SDA(1);
CT_IIC_SCL(1);
CT_Delay();
CT_IIC_SDA(0);//START:when CLK is high,DATA change form high to low
CT_Delay();
CT_IIC_SCL(0);//钳住I2C总线,准备发送或接收数据
CT_Delay();
}
//产生IIC停止信号
void CT_IIC_Stop(void)
{
CT_SDA_OUT();
CT_IIC_SCL(0);
CT_IIC_SDA(0);
CT_Delay();
CT_IIC_SCL(1);
CT_IIC_SDA(1);//STOP:when CLK is high DATA change form low to high
CT_Delay();
}
//等待应答信号到来
//返回值:1,接收应答失败
// 0,接收应答成功
u8 CT_IIC_Wait_Ack(void)
{
u8 ucErrTime=0;
u8 rack = 0;
CT_SDA_IN();
CT_IIC_SDA(1);
CT_Delay();
CT_IIC_SCL(1);
CT_Delay();
while(CT_READ_SDA)
{
ucErrTime++;
if(ucErrTime>250)
{
CT_IIC_Stop();
rack = 1;
break;
}
}
CT_IIC_SCL(0);//时钟输出0
CT_Delay();
return rack;
}
//产生ACK应答
void CT_IIC_Ack(void)
{
CT_SDA_OUT();
CT_IIC_SDA(0);
CT_Delay();
CT_IIC_SCL(1);
CT_Delay();
CT_IIC_SCL(0);
CT_Delay();
CT_IIC_SDA(1);
CT_Delay();
}
//不产生ACK应答
void CT_IIC_NAck(void)
{
CT_SDA_OUT();
CT_IIC_SDA(1);
CT_Delay();
CT_IIC_SCL(1);
CT_Delay();
CT_IIC_SCL(0);
CT_Delay();
}
//IIC发送一个字节
//返回从机有无应答
//1,有应答
//0,无应答
void CT_IIC_Send_Byte(u8 txd)
{
u8 t;
CT_SDA_OUT();
for(t=0;t<8;t++)
{
CT_IIC_SDA((txd&0x80)>>7);
CT_Delay();
CT_IIC_SCL(1);
CT_Delay();
CT_IIC_SCL(0);
txd<<=1;
}
CT_IIC_SDA(1);
}
//读1个字节,ack=1时,发送ACK,ack=0,发送nACK
u8 CT_IIC_Read_Byte(unsigned char ack)
{
u8 i,receive=0;
CT_SDA_IN();
for(i=0;i<8;i++ )
{
receive<<=1;
CT_IIC_SCL(1);
CT_Delay();
if(CT_READ_SDA)receive++;
CT_IIC_SCL(0);
CT_Delay();
}
if (!ack)
CT_IIC_NAck();//发送nACK
else
CT_IIC_Ack(); //发送ACK
return receive;
}
ctiic.h
#ifndef __MYCT_IIC_H
#define __MYCT_IIC_H
#include "sys.h"
//
//本程序只供学习使用,未经作者许可,不得用于其它任何用途
//ALIENTEK STM32开发板
//电容触摸屏-IIC 驱动代码
//正点原子@ALIENTEK
//技术论坛:www.openedv.com
//创建日期:2015/12/30
//版本:V1.0
//版权所有,盗版必究。
//Copyright(C) 广州市星翼电子科技有限公司 2014-2024
//All rights reserved
//********************************************************************************
//无
//
//IO方向设置
#define CT_SDA_IN() {GPIOI->MODER&=~(3<<(3*2));GPIOI->MODER|=0<<3*2;} //PI3输入模式
#define CT_SDA_OUT() {GPIOI->MODER&=~(3<<(3*2));GPIOI->MODER|=1<<3*2;} //PI3输出模式
//IO操作函数
#define CT_IIC_SCL(n) (n?HAL_GPIO_WritePin(GPIOH,GPIO_PIN_6,GPIO_PIN_SET):HAL_GPIO_WritePin(GPIOH,GPIO_PIN_6,GPIO_PIN_RESET))//SCL
#define CT_IIC_SDA(n) (n?HAL_GPIO_WritePin(GPIOI,GPIO_PIN_3,GPIO_PIN_SET):HAL_GPIO_WritePin(GPIOI,GPIO_PIN_3,GPIO_PIN_RESET))//SDA
#define CT_READ_SDA HAL_GPIO_ReadPin(GPIOI,GPIO_PIN_3)//输入SDA
//IIC所有操作函数
void CT_IIC_Init(void); //初始化IIC的IO口
void CT_IIC_Start(void); //发送IIC开始信号
void CT_IIC_Stop(void); //发送IIC停止信号
void CT_IIC_Send_Byte(u8 txd); //IIC发送一个字节
u8 CT_IIC_Read_Byte(unsigned char ack); //IIC读取一个字节
u8 CT_IIC_Wait_Ack(void); //IIC等待ACK信号
void CT_IIC_Ack(void); //IIC发送ACK信号
void CT_IIC_NAck(void); //IIC不发送ACK信号
#endif
gt9147.c
#include "gt9147.h"
#include "touch.h"
#include "ctiic.h"
#include "usart.h"
#include "pub_delay.h"
#include "string.h"
#include "lcd.h"
//
//本程序只供学习使用,未经作者许可,不得用于其它任何用途
//ALIENTEK STM32开发板
//4.3寸电容触摸屏-GT9147 驱动代码
//正点原子@ALIENTEK
//技术论坛:www.openedv.com
//创建日期:2015/12/28
//版本:V1.0
//版权所有,盗版必究。
//Copyright(C) 广州市星翼电子科技有限公司 2014-2024
//All rights reserved
//
//GT9147配置参数表
//第一个字节为版本号(0X60),必须保证新的版本号大于等于GT9147内部
//flash原有版本号,才会更新配置.
const u8 GT9147_CFG_TBL[]=
{
0X60,0XE0,0X01,0X20,0X03,0X05,0X35,0X00,0X02,0X08,
0X1E,0X08,0X50,0X3C,0X0F,0X05,0X00,0X00,0XFF,0X67,
0X50,0X00,0X00,0X18,0X1A,0X1E,0X14,0X89,0X28,0X0A,
0X30,0X2E,0XBB,0X0A,0X03,0X00,0X00,0X02,0X33,0X1D,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X32,0X00,0X00,
0X2A,0X1C,0X5A,0X94,0XC5,0X02,0X07,0X00,0X00,0X00,
0XB5,0X1F,0X00,0X90,0X28,0X00,0X77,0X32,0X00,0X62,
0X3F,0X00,0X52,0X50,0X00,0X52,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X0F,
0X0F,0X03,0X06,0X10,0X42,0XF8,0X0F,0X14,0X00,0X00,
0X00,0X00,0X1A,0X18,0X16,0X14,0X12,0X10,0X0E,0X0C,
0X0A,0X08,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X29,0X28,0X24,0X22,0X20,0X1F,0X1E,0X1D,
0X0E,0X0C,0X0A,0X08,0X06,0X05,0X04,0X02,0X00,0XFF,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,
0XFF,0XFF,0XFF,0XFF,
};
//发送GT9147配置参数
//mode:0,参数不保存到flash
// 1,参数保存到flash
u8 GT9147_Send_Cfg(u8 mode)
{
u8 buf[2];
u8 i=0;
buf[0]=0;
buf[1]=mode; //是否写入到GT9147 FLASH? 即是否掉电保存
for(i=0;i<sizeof(GT9147_CFG_TBL);i++)buf[0]+=GT9147_CFG_TBL[i];//计算校验和
buf[0]=(~buf[0])+1;
GT9147_WR_Reg(GT_CFGS_REG,(u8*)GT9147_CFG_TBL,sizeof(GT9147_CFG_TBL));//发送寄存器配置
GT9147_WR_Reg(GT_CHECK_REG,buf,2);//写入校验和,和配置更新标记
return 0;
}
//向GT9147写入一次数据
//reg:起始寄存器地址
//buf:数据缓缓存区
//len:写数据长度
//返回值:0,成功;1,失败.
u8 GT9147_WR_Reg(u16 reg,u8 *buf,u8 len)
{
u8 i;
u8 ret=0;
CT_IIC_Start();
CT_IIC_Send_Byte(GT_CMD_WR); //发送写命令
CT_IIC_Wait_Ack();
CT_IIC_Send_Byte(reg>>8); //发送高8位地址
CT_IIC_Wait_Ack();
CT_IIC_Send_Byte(reg&0XFF); //发送低8位地址
CT_IIC_Wait_Ack();
for(i=0;i<len;i++)
{
CT_IIC_Send_Byte(buf[i]); //发数据
ret=CT_IIC_Wait_Ack();
if(ret)break;
}
CT_IIC_Stop(); //产生一个停止条件
return ret;
}
//从GT9147读出一次数据
//reg:起始寄存器地址
//buf:数据缓缓存区
//len:读数据长度
void GT9147_RD_Reg(u16 reg,u8 *buf,u8 len)
{
u8 i;
CT_IIC_Start();
CT_IIC_Send_Byte(GT_CMD_WR); //发送写命令
CT_IIC_Wait_Ack();
CT_IIC_Send_Byte(reg>>8); //发送高8位地址
CT_IIC_Wait_Ack();
CT_IIC_Send_Byte(reg&0XFF); //发送低8位地址
CT_IIC_Wait_Ack();
CT_IIC_Start();
CT_IIC_Send_Byte(GT_CMD_RD); //发送读命令
CT_IIC_Wait_Ack();
for(i=0;i<len;i++)
{
buf[i]=CT_IIC_Read_Byte(i==(len-1)?0:1); //发数据
}
CT_IIC_Stop();//产生一个停止条件
}
//初始化GT9147触摸屏
//返回值:0,初始化成功;1,初始化失败
u8 GT9147_Init(void)
{
u8 temp[5];
GPIO_InitTypeDef GPIO_Initure;
__HAL_RCC_GPIOH_CLK_ENABLE(); //开启GPIOH时钟
__HAL_RCC_GPIOI_CLK_ENABLE(); //开启GPIOI时钟
//PH7
GPIO_Initure.Pin=GPIO_PIN_7; //PH7
GPIO_Initure.Mode=GPIO_MODE_INPUT; //输入
GPIO_Initure.Pull=GPIO_PULLUP; //上拉
GPIO_Initure.Speed=GPIO_SPEED_FREQ_VERY_HIGH; //高速
HAL_GPIO_Init(GPIOH,&GPIO_Initure); //初始化
//PI8
GPIO_Initure.Pin=GPIO_PIN_8; //PI8
GPIO_Initure.Mode=GPIO_MODE_OUTPUT_PP; //推挽输出
HAL_GPIO_Init(GPIOI,&GPIO_Initure); //初始化
CT_IIC_Init(); //初始化电容屏的I2C总线
GT_RST(0); //复位
HAL_Delay(10);
GT_RST(1); //释放复位
HAL_Delay(10);
GPIO_Initure.Pin=GPIO_PIN_7; //PH7
GPIO_Initure.Mode=GPIO_MODE_INPUT; //输入
GPIO_Initure.Pull=GPIO_NOPULL; //不带上下拉,浮空输入
GPIO_Initure.Speed=GPIO_SPEED_FREQ_VERY_HIGH; //高速
HAL_GPIO_Init(GPIOH,&GPIO_Initure); //初始化
HAL_Delay(100);
GT9147_RD_Reg(GT_PID_REG,temp,4);//读取产品ID
temp[4]=0;
printf("CTP ID:%s\r\n",temp); //打印ID
if(strcmp((char*)temp,"9147")==0)//ID==9147
{
temp[0]=0X02;
GT9147_WR_Reg(GT_CTRL_REG,temp,1);//软复位GT9147
GT9147_RD_Reg(GT_CFGS_REG,temp,1);//读取GT_CFGS_REG寄存器
if(temp[0]<0X60)//默认版本比较低,需要更新flash配置
{
printf("Default Ver:%d\r\n",temp[0]);
if(lcddev.id==0X5510)GT9147_Send_Cfg(1);//更新并保存配置
}
HAL_Delay(10);
temp[0]=0X00;
GT9147_WR_Reg(GT_CTRL_REG,temp,1);//结束复位
return 0;
}
return 0;
}
const u16 GT9147_TPX_TBL[5]={GT_TP1_REG,GT_TP2_REG,GT_TP3_REG,GT_TP4_REG,GT_TP5_REG};
//扫描触摸屏(采用查询方式)
//mode:0,正常扫描.
//返回值:当前触屏状态.
//0,触屏无触摸;1,触屏有触摸
u8 GT9147_Scan(u8 mode)
{
u8 buf[4];
u8 i=0;
u8 res=0;
u8 temp;
u8 tempsta;
static u8 t=0;//控制查询间隔,从而降低CPU占用率
t++;
if((t%10)==0||t<10)//空闲时,每进入10次CTP_Scan函数才检测1次,从而节省CPU使用率
{
GT9147_RD_Reg(GT_GSTID_REG,&mode,1); //读取触摸点的状态
if(mode&0X80&&((mode&0XF)<6))
{
temp=0;
GT9147_WR_Reg(GT_GSTID_REG,&temp,1);//清标志
}
if((mode&0XF)&&((mode&0XF)<6))
{
temp=0XFF<<(mode&0XF); //将点的个数转换为1的位数,匹配tp_dev.sta定义
tempsta=tp_dev.sta; //保存当前的tp_dev.sta值
tp_dev.sta=(~temp)|TP_PRES_DOWN|TP_CATH_PRES;
tp_dev.x[4]=tp_dev.x[0]; //保存触点0的数据
tp_dev.y[4]=tp_dev.y[0];
for(i=0;i<5;i++)
{
if(tp_dev.sta&(1<<i)) //触摸有效?
{
GT9147_RD_Reg(GT9147_TPX_TBL[i],buf,4); //读取XY坐标值
if(lcddev.id==0X5510) //4.3寸800*480 MCU屏
{
if(tp_dev.touchtype&0X01)//横屏
{
tp_dev.y[i]=((u16)buf[1]<<8)+buf[0];
tp_dev.x[i]=800-(((u16)buf[3]<<8)+buf[2]);
}else
{
tp_dev.x[i]=((u16)buf[1]<<8)+buf[0];
tp_dev.y[i]=((u16)buf[3]<<8)+buf[2];
}
}else if(lcddev.id==0X4342) //4.3寸480*272 RGB屏
{
if(tp_dev.touchtype&0X01)//横屏
{
tp_dev.x[i]=(((u16)buf[1]<<8)+buf[0]);
tp_dev.y[i]=(((u16)buf[3]<<8)+buf[2]);
}else
{
tp_dev.y[i]=((u16)buf[1]<<8)+buf[0];
tp_dev.x[i]=272-(((u16)buf[3]<<8)+buf[2]);
}
}else if(lcddev.id==0X4384) //4.3寸800*480 RGB屏
{
if(tp_dev.touchtype&0X01)//横屏
{
tp_dev.x[i]=(((u16)buf[1]<<8)+buf[0]);
tp_dev.y[i]=(((u16)buf[3]<<8)+buf[2]);
}else
{
tp_dev.y[i]=((u16)buf[1]<<8)+buf[0];
tp_dev.x[i]=480-(((u16)buf[3]<<8)+buf[2]);
}
}
}
}
res=1;
if(tp_dev.x[0]>lcddev.width||tp_dev.y[0]>lcddev.height)//非法数据(坐标超出了)
{
if((mode&0XF)>1) //有其他点有数据,则复第二个触点的数据到第一个触点.
{
tp_dev.x[0]=tp_dev.x[1];
tp_dev.y[0]=tp_dev.y[1];
t=0; //触发一次,则会最少连续监测10次,从而提高命中率
}else //非法数据,则忽略此次数据(还原原来的)
{
tp_dev.x[0]=tp_dev.x[4];
tp_dev.y[0]=tp_dev.y[4];
mode=0X80;
tp_dev.sta=tempsta; //恢复tp_dev.sta
}
}else t=0; //触发一次,则会最少连续监测10次,从而提高命中率
}
}
if((mode&0X8F)==0X80)//无触摸点按下
{
if(tp_dev.sta&TP_PRES_DOWN) //之前是被按下的
{
tp_dev.sta&=~(1<<7); //标记按键松开
}else //之前就没有被按下
{
tp_dev.x[0]=0xffff;
tp_dev.y[0]=0xffff;
tp_dev.sta&=0XE0; //清除点有效标记
}
}
if(t>240)t=10;//重新从10开始计数
return res;
}
gt9147.h
#ifndef __GT9147_H
#define __GT9147_H
#include "sys.h"
//
//本程序只供学习使用,未经作者许可,不得用于其它任何用途
//ALIENTEK STM32开发板
//4.3寸电容触摸屏-GT9147 驱动代码
//正点原子@ALIENTEK
//技术论坛:www.openedv.com
//创建日期:2015/12/28
//版本:V1.0
//版权所有,盗版必究。
//Copyright(C) 广州市星翼电子科技有限公司 2014-2024
//All rights reserved
//
//IO操作函数
#define GT_RST(n) (n?HAL_GPIO_WritePin(GPIOI,GPIO_PIN_8,GPIO_PIN_SET):HAL_GPIO_WritePin(GPIOI,GPIO_PIN_8,GPIO_PIN_RESET))//GT9147复位引脚
#define GT_INT HAL_GPIO_ReadPin(GPIOH,GPIO_PIN_7) //GT9147中断引脚
//I2C读写命令
#define GT_CMD_WR 0X28 //写命令
#define GT_CMD_RD 0X29 //读命令
//GT9147 部分寄存器定义
#define GT_CTRL_REG 0X8040 //GT9147控制寄存器
#define GT_CFGS_REG 0X8047 //GT9147配置起始地址寄存器
#define GT_CHECK_REG 0X80FF //GT9147校验和寄存器
#define GT_PID_REG 0X8140 //GT9147产品ID寄存器
#define GT_GSTID_REG 0X814E //GT9147当前检测到的触摸情况
#define GT_TP1_REG 0X8150 //第一个触摸点数据地址
#define GT_TP2_REG 0X8158 //第二个触摸点数据地址
#define GT_TP3_REG 0X8160 //第三个触摸点数据地址
#define GT_TP4_REG 0X8168 //第四个触摸点数据地址
#define GT_TP5_REG 0X8170 //第五个触摸点数据地址
u8 GT9147_Send_Cfg(u8 mode);
u8 GT9147_WR_Reg(u16 reg,u8 *buf,u8 len);
void GT9147_RD_Reg(u16 reg,u8 *buf,u8 len);
u8 GT9147_Init(void);
u8 GT9147_Scan(u8 mode);
#endif
24cxx.c
#include "24cxx.h"
#include "pub_delay.h"
//
//本程序只供学习使用,未经作者许可,不得用于其它任何用途
//ALIENTEK STM32F7开发板
//24CXX驱动代码
//正点原子@ALIENTEK
//技术论坛:www.openedv.com
//创建日期:2015/12/28
//版本:V1.0
//版权所有,盗版必究。
//Copyright(C) 广州市星翼电子科技有限公司 2014-2024
//All rights reserved
//
//初始化IIC接口
void AT24CXX_Init(void)
{
IIC_Init();//IIC初始化
}
//在AT24CXX指定地址读出一个数据
//ReadAddr:开始读数的地址
//返回值 :读到的数据
u8 AT24CXX_ReadOneByte(u16 ReadAddr)
{
u8 temp=0;
IIC_Start();
if(EE_TYPE>AT24C16)
{
IIC_Send_Byte(0XA0); //发送写命令
IIC_Wait_Ack();
IIC_Send_Byte(ReadAddr>>8);//发送高地址
}else IIC_Send_Byte(0XA0+((ReadAddr/256)<<1)); //发送器件地址0XA0,写数据
IIC_Wait_Ack();
IIC_Send_Byte(ReadAddr%256); //发送低地址
IIC_Wait_Ack();
IIC_Start();
IIC_Send_Byte(0XA1); //进入接收模式
IIC_Wait_Ack();
temp=IIC_Read_Byte(0);
IIC_Stop();//产生一个停止条件
return temp;
}
//在AT24CXX指定地址写入一个数据
//WriteAddr :写入数据的目的地址
//DataToWrite:要写入的数据
void AT24CXX_WriteOneByte(u16 WriteAddr,u8 DataToWrite)
{
IIC_Start();
if(EE_TYPE>AT24C16)
{
IIC_Send_Byte(0XA0); //发送写命令
IIC_Wait_Ack();
IIC_Send_Byte(WriteAddr>>8);//发送高地址
}else IIC_Send_Byte(0XA0+((WriteAddr/256)<<1)); //发送器件地址0XA0,写数据
IIC_Wait_Ack();
IIC_Send_Byte(WriteAddr%256); //发送低地址
IIC_Wait_Ack();
IIC_Send_Byte(DataToWrite); //发送字节
IIC_Wait_Ack();
IIC_Stop();//产生一个停止条件
HAL_Delay(10);
}
//在AT24CXX里面的指定地址开始写入长度为Len的数据
//该函数用于写入16bit或者32bit的数据.
//WriteAddr :开始写入的地址
//DataToWrite:数据数组首地址
//Len :要写入数据的长度2,4
void AT24CXX_WriteLenByte(u16 WriteAddr,u32 DataToWrite,u8 Len)
{
u8 t;
for(t=0;t<Len;t++)
{
AT24CXX_WriteOneByte(WriteAddr+t,(DataToWrite>>(8*t))&0xff);
}
}
//在AT24CXX里面的指定地址开始读出长度为Len的数据
//该函数用于读出16bit或者32bit的数据.
//ReadAddr :开始读出的地址
//返回值 :数据
//Len :要读出数据的长度2,4
u32 AT24CXX_ReadLenByte(u16 ReadAddr,u8 Len)
{
u8 t;
u32 temp=0;
for(t=0;t<Len;t++)
{
temp<<=8;
temp+=AT24CXX_ReadOneByte(ReadAddr+Len-t-1);
}
return temp;
}
//检查AT24CXX是否正常
//这里用了24XX的最后一个地址(255)来存储标志字.
//如果用其他24C系列,这个地址要修改
//返回1:检测失败
//返回0:检测成功
u8 AT24CXX_Check(void)
{
u8 temp;
temp=AT24CXX_ReadOneByte(255);//避免每次开机都写AT24CXX
if(temp==0X55)return 0;
else//排除第一次初始化的情况
{
AT24CXX_WriteOneByte(255,0X55);
temp=AT24CXX_ReadOneByte(255);
if(temp==0X55)return 0;
}
return 1;
}
//在AT24CXX里面的指定地址开始读出指定个数的数据
//ReadAddr :开始读出的地址 对24c02为0~255
//pBuffer :数据数组首地址
//NumToRead:要读出数据的个数
void AT24CXX_Read(u16 ReadAddr,u8 *pBuffer,u16 NumToRead)
{
while(NumToRead)
{
*pBuffer++=AT24CXX_ReadOneByte(ReadAddr++);
NumToRead--;
}
}
//在AT24CXX里面的指定地址开始写入指定个数的数据
//WriteAddr :开始写入的地址 对24c02为0~255
//pBuffer :数据数组首地址
//NumToWrite:要写入数据的个数
void AT24CXX_Write(u16 WriteAddr,u8 *pBuffer,u16 NumToWrite)
{
while(NumToWrite--)
{
AT24CXX_WriteOneByte(WriteAddr,*pBuffer);
WriteAddr++;
pBuffer++;
}
}
24cxx.h
#ifndef _24CXX_H
#define _24CXX_H
#include "sys.h"
#include "myiic.h"
//
//本程序只供学习使用,未经作者许可,不得用于其它任何用途
//ALIENTEK STM32F7开发板
//SDRAM驱动代码
//正点原子@ALIENTEK
//技术论坛:www.openedv.com
//创建日期:2015/12/28
//版本:V1.0
//版权所有,盗版必究。
//Copyright(C) 广州市星翼电子科技有限公司 2014-2024
//All rights reserved
//
#define AT24C01 127
#define AT24C02 255
#define AT24C04 511
#define AT24C08 1023
#define AT24C16 2047
#define AT24C32 4095
#define AT24C64 8191
#define AT24C128 16383
#define AT24C256 32767
//STM32 F746开发板使用的是24c02,所以定义EE_TYPE为AT24C02
#define EE_TYPE AT24C02
u8 AT24CXX_ReadOneByte(u16 ReadAddr); //指定地址读取一个字节
void AT24CXX_WriteOneByte(u16 WriteAddr,u8 DataToWrite); //指定地址写入一个字节
void AT24CXX_WriteLenByte(u16 WriteAddr,u32 DataToWrite,u8 Len);//指定地址开始写入指定长度的数据
u32 AT24CXX_ReadLenByte(u16 ReadAddr,u8 Len); //指定地址开始读取指定长度数据
void AT24CXX_Write(u16 WriteAddr,u8 *pBuffer,u16 NumToWrite); //从指定地址开始写入指定长度的数据
void AT24CXX_Read(u16 ReadAddr,u8 *pBuffer,u16 NumToRead); //从指定地址开始读出指定长度的数据
u8 AT24CXX_Check(void); //检查器件
void AT24CXX_Init(void); //初始化IIC
#endif
myiic.c
#include "myiic.h"
#include "pub_delay.h"
//
//本程序只供学习使用,未经作者许可,不得用于其它任何用途
//ALIENTEK STM32F7开发板
//IIC驱动代码
//正点原子@ALIENTEK
//技术论坛:www.openedv.com
//创建日期:2015/12/28
//版本:V1.0
//版权所有,盗版必究。
//Copyright(C) 广州市星翼电子科技有限公司 2014-2024
//All rights reserved
//
//IIC初始化
void IIC_Init(void)
{
GPIO_InitTypeDef GPIO_Initure;
__HAL_RCC_GPIOH_CLK_ENABLE(); //使能GPIOH时钟
//PH4,5初始化设置
GPIO_Initure.Pin=GPIO_PIN_4|GPIO_PIN_5;
GPIO_Initure.Mode=GPIO_MODE_OUTPUT_PP; //推挽输出
GPIO_Initure.Pull=GPIO_PULLUP; //上拉
GPIO_Initure.Speed=GPIO_SPEED_FREQ_VERY_HIGH; //快速
HAL_GPIO_Init(GPIOH,&GPIO_Initure);
IIC_SDA(1);
IIC_SCL(1);
}
//产生IIC起始信号
void IIC_Start(void)
{
SDA_OUT(); //sda线输出
IIC_SDA(1);
IIC_SCL(1);
delay_us(4);
IIC_SDA(0);//START:when CLK is high,DATA change form high to low
delay_us(4);
IIC_SCL(0);//钳住I2C总线,准备发送或接收数据
}
//产生IIC停止信号
void IIC_Stop(void)
{
SDA_OUT();//sda线输出
IIC_SCL(0);
IIC_SDA(0);//STOP:when CLK is high DATA change form low to high
delay_us(4);
IIC_SCL(1);
IIC_SDA(1);//发送I2C总线结束信号
delay_us(4);
}
//等待应答信号到来
//返回值:1,接收应答失败
// 0,接收应答成功
u8 IIC_Wait_Ack(void)
{
u8 ucErrTime=0;
SDA_IN(); //SDA设置为输入
IIC_SDA(1);delay_us(1);
IIC_SCL(1);delay_us(1);
while(READ_SDA)
{
ucErrTime++;
if(ucErrTime>250)
{
IIC_Stop();
return 1;
}
}
IIC_SCL(0);//时钟输出0
return 0;
}
//产生ACK应答
void IIC_Ack(void)
{
IIC_SCL(0);
SDA_OUT();
IIC_SDA(0);
delay_us(2);
IIC_SCL(1);
delay_us(2);
IIC_SCL(0);
}
//不产生ACK应答
void IIC_NAck(void)
{
IIC_SCL(0);
SDA_OUT();
IIC_SDA(1);
delay_us(2);
IIC_SCL(1);
delay_us(2);
IIC_SCL(0);
}
//IIC发送一个字节
//返回从机有无应答
//1,有应答
//0,无应答
void IIC_Send_Byte(u8 txd)
{
u8 t;
SDA_OUT();
IIC_SCL(0);//拉低时钟开始数据传输
for(t=0;t<8;t++)
{
IIC_SDA((txd&0x80)>>7);
txd<<=1;
delay_us(2); //对TEA5767这三个延时都是必须的
IIC_SCL(1);
delay_us(2);
IIC_SCL(0);
delay_us(2);
}
}
//读1个字节,ack=1时,发送ACK,ack=0,发送nACK
u8 IIC_Read_Byte(unsigned char ack)
{
unsigned char i,receive=0;
SDA_IN();//SDA设置为输入
for(i=0;i<8;i++ )
{
IIC_SCL(0);
delay_us(2);
IIC_SCL(1);
receive<<=1;
if(READ_SDA)receive++;
delay_us(1);
}
if (!ack)
IIC_NAck();//发送nACK
else
IIC_Ack(); //发送ACK
return receive;
}
myiic.h
#ifndef _MYIIC_H
#define _MYIIC_H
#include "sys.h"
//
//本程序只供学习使用,未经作者许可,不得用于其它任何用途
//ALIENTEK STM32F7开发板
//IIC驱动代码
//正点原子@ALIENTEK
//技术论坛:www.openedv.com
//创建日期:2015/11/30
//版本:V1.0
//版权所有,盗版必究。
//Copyright(C) 广州市星翼电子科技有限公司 2014-2024
//All rights reserved
//
//IO方向设置
#define SDA_IN() {GPIOH->MODER&=~(3<<(5*2));GPIOH->MODER|=0<<5*2;} //PH5输入模式
#define SDA_OUT() {GPIOH->MODER&=~(3<<(5*2));GPIOH->MODER|=1<<5*2;} //PH5输出模式
//IO操作
#define IIC_SCL(n) (n?HAL_GPIO_WritePin(GPIOH,GPIO_PIN_4,GPIO_PIN_SET):HAL_GPIO_WritePin(GPIOH,GPIO_PIN_4,GPIO_PIN_RESET)) //SCL
#define IIC_SDA(n) (n?HAL_GPIO_WritePin(GPIOH,GPIO_PIN_5,GPIO_PIN_SET):HAL_GPIO_WritePin(GPIOH,GPIO_PIN_5,GPIO_PIN_RESET)) //SDA
#define READ_SDA HAL_GPIO_ReadPin(GPIOH,GPIO_PIN_5) //输入SDA
//IIC所有操作函数
void IIC_Init(void); //初始化IIC的IO口
void IIC_Start(void); //发送IIC开始信号
void IIC_Stop(void); //发送IIC停止信号
void IIC_Send_Byte(u8 txd); //IIC发送一个字节
u8 IIC_Read_Byte(unsigned char ack);//IIC读取一个字节
u8 IIC_Wait_Ack(void); //IIC等待ACK信号
void IIC_Ack(void); //IIC发送ACK信号
void IIC_NAck(void); //IIC不发送ACK信号
void IIC_Write_One_Byte(u8 daddr,u8 addr,u8 data);
u8 IIC_Read_One_Byte(u8 daddr,u8 addr);
#endif
main.c测试代码
/*TOUCH_TEST*/
#if 1
tp_dev.init(); //触摸屏初始化
POINT_COLOR=RED;
LCD_ShowString(30,50,200,16,16,"Apollo STM32H7");
LCD_ShowString(30,70,200,16,16,"TOUCH TEST");
LCD_ShowString(30,90,200,16,16,"ATOM@ALIENTEK");
LCD_ShowString(30,110,200,16,16,"2017/8/14");
HAL_Delay(1500);
Load_Drow_Dialog();
if(tp_dev.touchtype&0X80)
ctp_test();//电容屏测试
else
rtp_test(); //电阻屏测试
#endif
ltdc.c
/**
******************************************************************************
* @file ltdc.c
* @brief This file provides code for the configuration
* of the LTDC instances.
******************************************************************************
* @attention
*
* <h2><center>© Copyright (c) 2021 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
*
******************************************************************************
*/
/* Includes ------------------------------------------------------------------*/
#include "ltdc.h"
/* USER CODE BEGIN 0 */
#include "bsp_led.h"
#include "touch.h"
#include "sys.h"
LTDC_HandleTypeDef LTDC_Handler; //LTDC句柄
uint32_t *ltdc_framebuf[2]; //LTDC LCD帧缓存数组指针,必须指向对应大小的内存区域
_ltdc_dev lcdltdc; //管理LCD LTDC的重要参数
uint16_t ltdc_lcd_framebuf[1280][800] __attribute__((at(LCD_FRAME_BUF_ADDR))); //定义最大屏分辨率时,LCD所需的帧缓存数组大小
/* USER CODE END 0 */
LTDC_HandleTypeDef hltdc;
/* LTDC init function */
void MX_LTDC_Init(void)
{
/* USER CODE BEGIN LTDC_Init 0 */
/* USER CODE END LTDC_Init 0 */
LTDC_LayerCfgTypeDef pLayerCfg = {0};
LTDC_LayerCfgTypeDef pLayerCfg1 = {0};
/* USER CODE BEGIN LTDC_Init 1 */
/* USER CODE END LTDC_Init 1 */
hltdc.Instance = LTDC;
hltdc.Init.HSPolarity = LTDC_HSPOLARITY_AL;
hltdc.Init.VSPolarity = LTDC_VSPOLARITY_AL;
hltdc.Init.DEPolarity = LTDC_DEPOLARITY_AL;
hltdc.Init.PCPolarity = LTDC_PCPOLARITY_IPC;
hltdc.Init.HorizontalSync = 19;
hltdc.Init.VerticalSync = 2;
hltdc.Init.AccumulatedHBP = 159;
hltdc.Init.AccumulatedVBP = 22;
hltdc.Init.AccumulatedActiveW = 1183;
hltdc.Init.AccumulatedActiveH = 622;
hltdc.Init.TotalWidth = 1343;
hltdc.Init.TotalHeigh = 634;
hltdc.Init.Backcolor.Blue = 0;
hltdc.Init.Backcolor.Green = 0;
hltdc.Init.Backcolor.Red = 0;
if (HAL_LTDC_Init(&hltdc) != HAL_OK)
{
Error_Handler();
}
pLayerCfg.WindowX0 = 0;
pLayerCfg.WindowX1 = 1024;
pLayerCfg.WindowY0 = 0;
pLayerCfg.WindowY1 = 600;
pLayerCfg.PixelFormat = LTDC_PIXEL_FORMAT_RGB565;
pLayerCfg.Alpha = 255;
pLayerCfg.Alpha0 = 0;
pLayerCfg.BlendingFactor1 = LTDC_BLENDING_FACTOR1_CA;
pLayerCfg.BlendingFactor2 = LTDC_BLENDING_FACTOR2_CA;
pLayerCfg.FBStartAdress = 0xC0000000;
pLayerCfg.ImageWidth = 1024;
pLayerCfg.ImageHeight = 600;
pLayerCfg.Backcolor.Blue = 0;
pLayerCfg.Backcolor.Green = 0;
pLayerCfg.Backcolor.Red = 0;
if (HAL_LTDC_ConfigLayer(&hltdc, &pLayerCfg, 0) != HAL_OK)
{
Error_Handler();
}
pLayerCfg1.WindowX0 = 0;
pLayerCfg1.WindowX1 = 0;
pLayerCfg1.WindowY0 = 0;
pLayerCfg1.WindowY1 = 0;
pLayerCfg1.PixelFormat = LTDC_PIXEL_FORMAT_RGB565;
pLayerCfg1.Alpha = 0;
pLayerCfg1.Alpha0 = 0;
pLayerCfg1.BlendingFactor1 = LTDC_BLENDING_FACTOR1_CA;
pLayerCfg1.BlendingFactor2 = LTDC_BLENDING_FACTOR2_CA;
pLayerCfg1.FBStartAdress = 0;
pLayerCfg1.ImageWidth = 0;
pLayerCfg1.ImageHeight = 0;
pLayerCfg1.Backcolor.Blue = 0;
pLayerCfg1.Backcolor.Green = 0;
pLayerCfg1.Backcolor.Red = 0;
if (HAL_LTDC_ConfigLayer(&hltdc, &pLayerCfg1, 1) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN LTDC_Init 2 */
/* USER CODE END LTDC_Init 2 */
}
void HAL_LTDC_MspInit(LTDC_HandleTypeDef* ltdcHandle)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
RCC_PeriphCLKInitTypeDef PeriphClkInitStruct = {0};
if(ltdcHandle->Instance==LTDC)
{
/* USER CODE BEGIN LTDC_MspInit 0 */
/* USER CODE END LTDC_MspInit 0 */
/** Initializes the peripherals clock
*/
PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_LTDC;
PeriphClkInitStruct.PLL3.PLL3M = 32;
PeriphClkInitStruct.PLL3.PLL3N = 192;
PeriphClkInitStruct.PLL3.PLL3P = 2;
PeriphClkInitStruct.PLL3.PLL3Q = 2;
PeriphClkInitStruct.PLL3.PLL3R = 2;
PeriphClkInitStruct.PLL3.PLL3RGE = RCC_PLL3VCIRANGE_3;
PeriphClkInitStruct.PLL3.PLL3VCOSEL = RCC_PLL3VCOMEDIUM;
PeriphClkInitStruct.PLL3.PLL3FRACN = 0;
if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct) != HAL_OK)
{
Error_Handler();
}
/* LTDC clock enable */
__HAL_RCC_LTDC_CLK_ENABLE();
__HAL_RCC_GPIOI_CLK_ENABLE();
__HAL_RCC_GPIOF_CLK_ENABLE();
__HAL_RCC_GPIOH_CLK_ENABLE();
__HAL_RCC_GPIOG_CLK_ENABLE();
/**LTDC GPIO Configuration
PI9 ------> LTDC_VSYNC
PI10 ------> LTDC_HSYNC
PF10 ------> LTDC_DE
PH9 ------> LTDC_R3
PH10 ------> LTDC_R4
PH11 ------> LTDC_R5
PH12 ------> LTDC_R6
PG6 ------> LTDC_R7
PG7 ------> LTDC_CLK
PH13 ------> LTDC_G2
PH14 ------> LTDC_G3
PH15 ------> LTDC_G4
PI0 ------> LTDC_G5
PI1 ------> LTDC_G6
PI2 ------> LTDC_G7
PG11 ------> LTDC_B3
PI4 ------> LTDC_B4
PI5 ------> LTDC_B5
PI6 ------> LTDC_B6
PI7 ------> LTDC_B7
*/
GPIO_InitStruct.Pin = GPIO_PIN_9|GPIO_PIN_10|GPIO_PIN_0|GPIO_PIN_1
|GPIO_PIN_2|GPIO_PIN_4|GPIO_PIN_5|GPIO_PIN_6
|GPIO_PIN_7;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
GPIO_InitStruct.Alternate = GPIO_AF14_LTDC;
HAL_GPIO_Init(GPIOI, &GPIO_InitStruct);
GPIO_InitStruct.Pin = GPIO_PIN_10;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
GPIO_InitStruct.Alternate = GPIO_AF14_LTDC;
HAL_GPIO_Init(GPIOF, &GPIO_InitStruct);
GPIO_InitStruct.Pin = GPIO_PIN_9|GPIO_PIN_10|GPIO_PIN_11|GPIO_PIN_12
|GPIO_PIN_13|GPIO_PIN_14|GPIO_PIN_15;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
GPIO_InitStruct.Alternate = GPIO_AF14_LTDC;
HAL_GPIO_Init(GPIOH, &GPIO_InitStruct);
GPIO_InitStruct.Pin = GPIO_PIN_6|GPIO_PIN_7|GPIO_PIN_11;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
GPIO_InitStruct.Alternate = GPIO_AF14_LTDC;
HAL_GPIO_Init(GPIOG, &GPIO_InitStruct);
/* USER CODE BEGIN LTDC_MspInit 1 */
/* USER CODE END LTDC_MspInit 1 */
}
}
void HAL_LTDC_MspDeInit(LTDC_HandleTypeDef* ltdcHandle)
{
if(ltdcHandle->Instance==LTDC)
{
/* USER CODE BEGIN LTDC_MspDeInit 0 */
/* USER CODE END LTDC_MspDeInit 0 */
/* Peripheral clock disable */
__HAL_RCC_LTDC_CLK_DISABLE();
/**LTDC GPIO Configuration
PI9 ------> LTDC_VSYNC
PI10 ------> LTDC_HSYNC
PF10 ------> LTDC_DE
PH9 ------> LTDC_R3
PH10 ------> LTDC_R4
PH11 ------> LTDC_R5
PH12 ------> LTDC_R6
PG6 ------> LTDC_R7
PG7 ------> LTDC_CLK
PH13 ------> LTDC_G2
PH14 ------> LTDC_G3
PH15 ------> LTDC_G4
PI0 ------> LTDC_G5
PI1 ------> LTDC_G6
PI2 ------> LTDC_G7
PG11 ------> LTDC_B3
PI4 ------> LTDC_B4
PI5 ------> LTDC_B5
PI6 ------> LTDC_B6
PI7 ------> LTDC_B7
*/
HAL_GPIO_DeInit(GPIOI, GPIO_PIN_9|GPIO_PIN_10|GPIO_PIN_0|GPIO_PIN_1
|GPIO_PIN_2|GPIO_PIN_4|GPIO_PIN_5|GPIO_PIN_6
|GPIO_PIN_7);
HAL_GPIO_DeInit(GPIOF, GPIO_PIN_10);
HAL_GPIO_DeInit(GPIOH, GPIO_PIN_9|GPIO_PIN_10|GPIO_PIN_11|GPIO_PIN_12
|GPIO_PIN_13|GPIO_PIN_14|GPIO_PIN_15);
HAL_GPIO_DeInit(GPIOG, GPIO_PIN_6|GPIO_PIN_7|GPIO_PIN_11);
/* USER CODE BEGIN LTDC_MspDeInit 1 */
/* USER CODE END LTDC_MspDeInit 1 */
}
}
/* USER CODE BEGIN 1 */
//打开LCD开关
//lcd_switch:1 打开,0,关闭
void LTDC_Switch(u8 sw)
{
if(sw==1) __HAL_LTDC_ENABLE(<DC_Handler);
else if(sw==0)__HAL_LTDC_DISABLE(<DC_Handler);
}
//开关指定层
//layerx:层号,0,第一层; 1,第二层
//sw:1 打开;0关闭
void LTDC_Layer_Switch(u8 layerx,u8 sw)
{
if(sw==1) __HAL_LTDC_LAYER_ENABLE(<DC_Handler,layerx);
else if(sw==0) __HAL_LTDC_LAYER_DISABLE(<DC_Handler,layerx);
__HAL_LTDC_RELOAD_CONFIG(<DC_Handler);
}
//选择层
//layerx:层号;0,第一层;1,第二层;
void LTDC_Select_Layer(u8 layerx)
{
lcdltdc.activelayer=layerx;
}
//设置LCD显示方向
//dir:0,竖屏;1,横屏
void LTDC_Display_Dir(u8 dir)
{
lcdltdc.dir=dir; //显示方向
if(dir==0) //竖屏
{
lcdltdc.width=lcdltdc.pheight;
lcdltdc.height=lcdltdc.pwidth;
}else if(dir==1) //横屏
{
lcdltdc.width=lcdltdc.pwidth;
lcdltdc.height=lcdltdc.pheight;
}
}
//画点函数
//x,y:坐标
//color:颜色值
void LTDC_Draw_Point(u16 x,u16 y,u32 color)
{
#if LCD_PIXFORMAT==LCD_PIXFORMAT_ARGB8888||LCD_PIXFORMAT==LCD_PIXFORMAT_RGB888
if(lcdltdc.dir) //横屏
{
*(u32*)((u32)ltdc_framebuf[lcdltdc.activelayer]+lcdltdc.pixsize*(lcdltdc.pwidth*y+x))=color;
}else //竖屏
{
*(u32*)((u32)ltdc_framebuf[lcdltdc.activelayer]+lcdltdc.pixsize*(lcdltdc.pwidth*(lcdltdc.pheight-x-1)+y))=color;
}
#else
if(lcdltdc.dir) //横屏
{
*(u16*)((u32)ltdc_framebuf[lcdltdc.activelayer]+lcdltdc.pixsize*(lcdltdc.pwidth*y+x))=color;
}else //竖屏
{
*(u16*)((u32)ltdc_framebuf[lcdltdc.activelayer]+lcdltdc.pixsize*(lcdltdc.pwidth*(lcdltdc.pheight-x-1)+y))=color;
}
#endif
}
//读点函数
//返回值:颜色值
u32 LTDC_Read_Point(u16 x,u16 y)
{
#if LCD_PIXFORMAT==LCD_PIXFORMAT_ARGB8888||LCD_PIXFORMAT==LCD_PIXFORMAT_RGB888
if(lcdltdc.dir) //横屏
{
return *(u32*)((u32)ltdc_framebuf[lcdltdc.activelayer]+lcdltdc.pixsize*(lcdltdc.pwidth*y+x));
}else //竖屏
{
return *(u32*)((u32)ltdc_framebuf[lcdltdc.activelayer]+lcdltdc.pixsize*(lcdltdc.pwidth*(lcdltdc.pheight-x-1)+y));
}
#else
if(lcdltdc.dir) //横屏
{
return *(u16*)((u32)ltdc_framebuf[lcdltdc.activelayer]+lcdltdc.pixsize*(lcdltdc.pwidth*y+x));
}else //竖屏
{
return *(u16*)((u32)ltdc_framebuf[lcdltdc.activelayer]+lcdltdc.pixsize*(lcdltdc.pwidth*(lcdltdc.pheight-x-1)+y));
}
#endif
}
//LTDC填充矩形,DMA2D填充
//(sx,sy),(ex,ey):填充矩形对角坐标,区域大小为:(ex-sx+1)*(ey-sy+1)
//注意:sx,ex,不能大于lcddev.width-1;sy,ey,不能大于lcddev.height-1!!!
//color:要填充的颜色
void LTDC_Fill(u16 sx,u16 sy,u16 ex,u16 ey,u32 color)
{
u32 psx,psy,pex,pey; //以LCD面板为基准的坐标系,不随横竖屏变化而变化
u32 timeout=0;
u16 offline;
u32 addr;
//坐标系转换
if(lcdltdc.dir) //横屏
{
psx=sx;psy=sy;
pex=ex;pey=ey;
}else //竖屏
{
psx=sy;psy=lcdltdc.pheight-ex-1;
pex=ey;pey=lcdltdc.pheight-sx-1;
}
offline=lcdltdc.pwidth-(pex-psx+1);
addr=((u32)ltdc_framebuf[lcdltdc.activelayer]+lcdltdc.pixsize*(lcdltdc.pwidth*psy+psx));
RCC->AHB1ENR|=1<<23; //使能DM2D时钟
DMA2D->CR=3<<16; //寄存器到存储器模式
DMA2D->OPFCCR=LCD_PIXFORMAT; //设置颜色格式
DMA2D->OOR=offline; //设置行偏移
DMA2D->CR&=~(1<<0); //先停止DMA2D
DMA2D->OMAR=addr; //输出存储器地址
DMA2D->NLR=(pey-psy+1)|((pex-psx+1)<<16); //设定行数寄存器
DMA2D->OCOLR=color; //设定输出颜色寄存器
DMA2D->CR|=1<<0; //启动DMA2D
while((DMA2D->ISR&(1<<1))==0) //等待传输完成
{
timeout++;
if(timeout>0X1FFFFF)break; //超时退出
}
DMA2D->IFCR|=1<<1; //清除传输完成标志
}
//在指定区域内填充单个颜色
//(sx,sy),(ex,ey):填充矩形对角坐标,区域大小为:(ex-sx+1)*(ey-sy+1)
//color:要填充的颜色
//void LTDC_Fill(u16 sx,u16 sy,u16 ex,u16 ey,u32 color)
//{
// u32 psx,psy,pex,pey; //以LCD面板为基准的坐标系,不随横竖屏变化而变化
// u32 timeout=0;
// u16 offline;
// u32 addr;
// if(ex>=lcdltdc.width)ex=lcdltdc.width-1;
// if(ey>=lcdltdc.height)ey=lcdltdc.height-1;
// //坐标系转换
// if(lcdltdc.dir) //横屏
// {
// psx=sx;psy=sy;
// pex=ex;pey=ey;
// }else //竖屏
// {
// psx=sy;psy=lcdltdc.pheight-ex-1;
// pex=ey;pey=lcdltdc.pheight-sx-1;
// }
// offline=lcdltdc.pwidth-(pex-psx+1);
// addr=((u32)ltdc_framebuf[lcdltdc.activelayer]+lcdltdc.pixsize*(lcdltdc.pwidth*psy+psx));
// if(LCD_PIXFORMAT==LCD_PIXEL_FORMAT_RGB565) //如果是RGB565格式的话需要进行颜色转换,将16bit转换为32bit的
// {
// color=((color&0X0000F800)<<8)|((color&0X000007E0)<<5)|((color&0X0000001F)<<3);
// }
// //配置DMA2D的模式
// DMA2D_Handler.Instance=DMA2D;
// DMA2D_Handler.Init.Mode=DMA2D_R2M; //内存到存储器模式
// DMA2D_Handler.Init.ColorMode=LCD_PIXFORMAT; //设置颜色格式
// DMA2D_Handler.Init.OutputOffset=offline; //输出偏移
// HAL_DMA2D_Init(&DMA2D_Handler); //初始化DMA2D
// HAL_DMA2D_ConfigLayer(&DMA2D_Handler,lcdltdc.activelayer); //层配置
// HAL_DMA2D_Start(&DMA2D_Handler,color,(u32)addr,pex-psx+1,pey-psy+1);//开启传输
// HAL_DMA2D_PollForTransfer(&DMA2D_Handler,1000);//传输数据
// while((__HAL_DMA2D_GET_FLAG(&DMA2D_Handler,DMA2D_FLAG_TC)==0)&&(timeout<0X5000))//等待DMA2D完成
// {
// timeout++;
// }
// __HAL_DMA2D_CLEAR_FLAG(&DMA2D_Handler,DMA2D_FLAG_TC); //清除传输完成标志
//}
//在指定区域内填充指定颜色块,DMA2D填充
//此函数仅支持u16,RGB565格式的颜色数组填充.
//(sx,sy),(ex,ey):填充矩形对角坐标,区域大小为:(ex-sx+1)*(ey-sy+1)
//注意:sx,ex,不能大于lcddev.width-1;sy,ey,不能大于lcddev.height-1!!!
//color:要填充的颜色数组首地址
void LTDC_Color_Fill(u16 sx,u16 sy,u16 ex,u16 ey,u16 *color)
{
u32 psx,psy,pex,pey; //以LCD面板为基准的坐标系,不随横竖屏变化而变化
u32 timeout=0;
u16 offline;
u32 addr;
//坐标系转换
if(lcdltdc.dir) //横屏
{
psx=sx;psy=sy;
pex=ex;pey=ey;
}else //竖屏
{
psx=sy;psy=lcdltdc.pheight-ex-1;
pex=ey;pey=lcdltdc.pheight-sx-1;
}
offline=lcdltdc.pwidth-(pex-psx+1);
addr=((u32)ltdc_framebuf[lcdltdc.activelayer]+lcdltdc.pixsize*(lcdltdc.pwidth*psy+psx));
RCC->AHB1ENR|=1<<23; //使能DM2D时钟
DMA2D->CR=0<<16; //存储器到存储器模式
DMA2D->FGPFCCR=LCD_PIXFORMAT; //设置颜色格式
DMA2D->FGOR=0; //前景层行偏移为0
DMA2D->OOR=offline; //设置行偏移
DMA2D->CR&=~(1<<0); //先停止DMA2D
DMA2D->FGMAR=(u32)color; //源地址
DMA2D->OMAR=addr; //输出存储器地址
DMA2D->NLR=(pey-psy+1)|((pex-psx+1)<<16); //设定行数寄存器
DMA2D->CR|=1<<0; //启动DMA2D
while((DMA2D->ISR&(1<<1))==0) //等待传输完成
{
timeout++;
if(timeout>0X1FFFFF)break; //超时退出
}
DMA2D->IFCR|=1<<1; //清除传输完成标志
}
//LCD清屏
//color:颜色值
void LTDC_Clear(u32 color)
{
LTDC_Fill(0,0,lcdltdc.width-1,lcdltdc.height-1,color);
}
//LTDC时钟(Fdclk)设置函数
//PLL3_VCO Input=HSE_VALUE/PLL3M
//PLL3_VCO Output=PLL3_VCO Input * PLL3N
//PLLLCDCLK = PLL3_VCO Output/PLL3R
//假如HSE_VALUE=25MHz,PLL3M=5,PLL3N=160,PLL3R=88
//LTDCLK=PLLLCDCLK=25/5*160/88=9MHz
//返回值:0,成功;1,失败。
u8 LTDC_Clk_Set(u32 pll3m,u32 pll3n,u32 pll3r)
{
RCC_PeriphCLKInitTypeDef PeriphClkIniture;
PeriphClkIniture.PeriphClockSelection = RCC_PERIPHCLK_LTDC;
PeriphClkIniture.PLL3.PLL3M = pll3m;
PeriphClkIniture.PLL3.PLL3N = pll3n;
PeriphClkIniture.PLL3.PLL3P = 2;
PeriphClkIniture.PLL3.PLL3Q = 2;
PeriphClkIniture.PLL3.PLL3R = pll3r;
if(HAL_RCCEx_PeriphCLKConfig(&PeriphClkIniture)==HAL_OK) //配置像素时钟,这里配置为时钟为18.75MHZ
{
return 0; //成功
}
else return 1; //失败
}
//LTDC,层颜窗口设置,窗口以LCD面板坐标系为基准
//注意:此函数必须在LTDC_Layer_Parameter_Config之后再设置.
//layerx:层值,0/1.
//sx,sy:起始坐标
//width,height:宽度和高度
void LTDC_Layer_Window_Config(u8 layerx,u16 sx,u16 sy,u16 width,u16 height)
{
HAL_LTDC_SetWindowPosition(<DC_Handler,sx,sy,layerx); //设置窗口的位置
HAL_LTDC_SetWindowSize(<DC_Handler,width,height,layerx);//设置窗口大小
}
//LTDC,基本参数设置.
//注意:此函数,必须在LTDC_Layer_Window_Config之前设置.
//layerx:层值,0/1.
//bufaddr:层颜色帧缓存起始地址
//pixformat:颜色格式.0,ARGB8888;1,RGB888;2,RGB565;3,ARGB1555;4,ARGB4444;5,L8;6;AL44;7;AL88
//alpha:层颜色Alpha值,0,全透明;255,不透明
//alpha0:默认颜色Alpha值,0,全透明;255,不透明
//bfac1:混合系数1,4(100),恒定的Alpha;6(101),像素Alpha*恒定Alpha
//bfac2:混合系数2,5(101),恒定的Alpha;7(111),像素Alpha*恒定Alpha
//bkcolor:层默认颜色,32位,低24位有效,RGB888格式
//返回值:无
void LTDC_Layer_Parameter_Config(u8 layerx,u32 bufaddr,u8 pixformat,u8 alpha,u8 alpha0,u8 bfac1,u8 bfac2,u32 bkcolor)
{
LTDC_LayerCfgTypeDef pLayerCfg;
pLayerCfg.WindowX0=0; //窗口起始X坐标
pLayerCfg.WindowY0=0; //窗口起始Y坐标
pLayerCfg.WindowX1=lcdltdc.pwidth; //窗口终止X坐标
pLayerCfg.WindowY1=lcdltdc.pheight; //窗口终止Y坐标
pLayerCfg.PixelFormat=pixformat; //像素格式
pLayerCfg.Alpha=alpha; //Alpha值设置,0~255,255为完全不透明
pLayerCfg.Alpha0=alpha0; //默认Alpha值
pLayerCfg.BlendingFactor1=(u32)bfac1<<8; //设置层混合系数
pLayerCfg.BlendingFactor2=(u32)bfac2<<8; //设置层混合系数
pLayerCfg.FBStartAdress=bufaddr; //设置层颜色帧缓存起始地址
pLayerCfg.ImageWidth=lcdltdc.pwidth; //设置颜色帧缓冲区的宽度
pLayerCfg.ImageHeight=lcdltdc.pheight; //设置颜色帧缓冲区的高度
pLayerCfg.Backcolor.Red=(u8)(bkcolor&0X00FF0000)>>16; //背景颜色红色部分
pLayerCfg.Backcolor.Green=(u8)(bkcolor&0X0000FF00)>>8; //背景颜色绿色部分
pLayerCfg.Backcolor.Blue=(u8)bkcolor&0X000000FF; //背景颜色蓝色部分
HAL_LTDC_ConfigLayer(<DC_Handler,&pLayerCfg,layerx); //设置所选中的层
}
//读取面板参数,新增了判断悬空全一的排查
//PG6=R7(M0);PI2=G7(M1);PI7=B7(M2);
//M2:M1:M0
//0 :0 :0 //4.3寸480*272 RGB屏,ID=0X4342
//0 :0 :1 //7寸800*480 RGB屏,ID=0X7084
//0 :1 :0 //7寸1024*600 RGB屏,ID=0X7016
//0 :1 :1 //7寸1280*800 RGB屏,ID=0X7018
//1 :0 :0 //4.3寸800*480 RGB屏,ID=0X4384
//1 :0 :1 //10.1寸1280*800,RGB屏,ID=0X1018
//1 :1 :1 //VGA显示器
//返回值:LCD ID:0,非法;其他值,ID;
u16 LTDC_PanelID_Read(void)
{
/*
u8 idx=0;
GPIO_InitTypeDef GPIO_Initure;
__HAL_RCC_GPIOG_CLK_ENABLE(); //使能GPIOG时钟
__HAL_RCC_GPIOI_CLK_ENABLE(); //使能GPIOI时钟
GPIO_Initure.Pin=GPIO_PIN_6; //PG6
GPIO_Initure.Mode=GPIO_MODE_INPUT; //输入
GPIO_Initure.Pull=GPIO_PULLUP; //上拉
GPIO_Initure.Speed=GPIO_SPEED_FREQ_VERY_HIGH; //高速
HAL_GPIO_Init(GPIOG,&GPIO_Initure); //初始化
GPIO_Initure.Pin=GPIO_PIN_2|GPIO_PIN_7; //PI2,7
HAL_GPIO_Init(GPIOI,&GPIO_Initure); //初始化
idx=(u8)HAL_GPIO_ReadPin(GPIOG,GPIO_PIN_6); //读取M0
idx|=(u8)HAL_GPIO_ReadPin(GPIOI,GPIO_PIN_2)<<1;//读取M1
idx|=(u8)HAL_GPIO_ReadPin(GPIOI,GPIO_PIN_7)<<2;//读取M2
if(idx==0) return 0X4342; //4.3寸屏,480*272分辨率
else if(idx==1)return 0X7084; //7寸屏,800*480分辨率
else if(idx==2)return 0X7016; //7寸屏,1024*600分辨率
else if(idx==3)return 0X7018; //7寸屏,1280*800分辨率
else if(idx==4)return 0X4384; //4.3寸屏,800*480分辨率
else if(idx==5)return 0X1018; //10.1寸屏,1280*800分辨率
else return 0;
*/
return 0X7016;
}
void LTDC_Init(void)
{
u32 lcdid;
lcdid=LTDC_PanelID_Read(); //读取LCD面板ID
if(lcdid==0X7016)
{
tp_dev.touchtype|=0X80; //电容屏
lcddev.id = 0X7016;
lcdltdc.pwidth=1024; //面板宽度,单位:像素
lcdltdc.pheight=600; //面板高度,单位:像素
lcdltdc.hsw=20; //水平同步宽度
lcdltdc.vsw=3; //垂直同步宽度
lcdltdc.hbp=140; //水平后廊
lcdltdc.vbp=20; //垂直后廊
lcdltdc.hfp=160; //水平前廊
lcdltdc.vfp=12; //垂直前廊
}
#if LCD_PIXFORMAT==LCD_PIXFORMAT_ARGB8888||LCD_PIXFORMAT==LCD_PIXFORMAT_RGB888
ltdc_framebuf[0]=(u32*)<dc_lcd_framebuf;
lcdltdc.pixsize=4; //每个像素占4个字节
#else
lcdltdc.pixsize=2; //每个像素占2个字节
ltdc_framebuf[0]=(u32*)<dc_lcd_framebuf;
#endif
//LTDC配置
LTDC_Handler.Instance=LTDC;
LTDC_Handler.Init.HSPolarity=LTDC_HSPOLARITY_AL; //水平同步极性
LTDC_Handler.Init.VSPolarity=LTDC_VSPOLARITY_AL; //垂直同步极性
LTDC_Handler.Init.DEPolarity=LTDC_DEPOLARITY_AL; //数据使能极性
LTDC_Handler.Init.PCPolarity=LTDC_PCPOLARITY_IPC; //像素时钟极性
LTDC_Handler.Init.HorizontalSync=lcdltdc.hsw-1; //水平同步宽度
LTDC_Handler.Init.VerticalSync=lcdltdc.vsw-1; //垂直同步宽度
LTDC_Handler.Init.AccumulatedHBP=lcdltdc.hsw+lcdltdc.hbp-1; //水平同步后沿宽度
LTDC_Handler.Init.AccumulatedVBP=lcdltdc.vsw+lcdltdc.vbp-1; //垂直同步后沿高度
LTDC_Handler.Init.AccumulatedActiveW=lcdltdc.hsw+lcdltdc.hbp+lcdltdc.pwidth-1;//有效宽度
LTDC_Handler.Init.AccumulatedActiveH=lcdltdc.vsw+lcdltdc.vbp+lcdltdc.pheight-1;//有效高度
LTDC_Handler.Init.TotalWidth=lcdltdc.hsw+lcdltdc.hbp+lcdltdc.pwidth+lcdltdc.hfp-1; //总宽度
LTDC_Handler.Init.TotalHeigh=lcdltdc.vsw+lcdltdc.vbp+lcdltdc.pheight+lcdltdc.vfp-1; //总高度
LTDC_Handler.Init.Backcolor.Red=0; //屏幕背景层红色部分
LTDC_Handler.Init.Backcolor.Green=0; //屏幕背景层绿色部分
LTDC_Handler.Init.Backcolor.Blue=0; //屏幕背景色蓝色部分
HAL_LTDC_Init(<DC_Handler);
//层配置
LTDC_Layer_Parameter_Config(0,(u32)ltdc_framebuf[0],LCD_PIXFORMAT,255,0,6,7,0X000000);//层参数配置
LTDC_Layer_Window_Config(0,0,0,lcdltdc.pwidth,lcdltdc.pheight); //层窗口配置,以LCD面板坐标系为基准,不要随便修改!
//LTDC_Display_Dir(0); //默认竖屏
//LTDC_Display_Dir(1); //横屏
LTDC_Select_Layer(0); //选择第1层
LCD_LED(1); //点亮背光
LTDC_Clear(0XFFFFFFFF); //清屏
}
//清空屏幕并在右上角显示"RST"
void Load_Drow_Dialog(void)
{
LCD_Clear(WHITE);//清屏
POINT_COLOR=BLUE;//设置字体为蓝色
LCD_ShowString(lcddev.width-24,0,200,16,16,"RST");//显示清屏区域
POINT_COLOR=RED;//设置画笔蓝色
}
//电容触摸屏专有部分
//画水平线
//x0,y0:坐标
//len:线长度
//color:颜色
void gui_draw_hline(u16 x0,u16 y0,u16 len,u16 color)
{
if(len==0)return;
if((x0+len-1)>=lcddev.width)x0=lcddev.width-len-1; //限制坐标范围
if(y0>=lcddev.height)y0=lcddev.height-1; //限制坐标范围
LCD_Fill(x0,y0,x0+len-1,y0,color);
}
//画实心圆
//x0,y0:坐标
//r:半径
//color:颜色
void gui_fill_circle(u16 x0,u16 y0,u16 r,u16 color)
{
u32 i;
u32 imax = ((u32)r*707)/1000+1;
u32 sqmax = (u32)r*(u32)r+(u32)r/2;
u32 x=r;
gui_draw_hline(x0-r,y0,2*r,color);
for (i=1;i<=imax;i++)
{
if ((i*i+x*x)>sqmax)// draw lines from outside
{
if (x>imax)
{
gui_draw_hline (x0-i+1,y0+x,2*(i-1),color);
gui_draw_hline (x0-i+1,y0-x,2*(i-1),color);
}
x--;
}
// draw lines from inside (center)
gui_draw_hline(x0-x,y0+i,2*x,color);
gui_draw_hline(x0-x,y0-i,2*x,color);
}
}
//两个数之差的绝对值
//x1,x2:需取差值的两个数
//返回值:|x1-x2|
u16 my_abs(u16 x1,u16 x2)
{
if(x1>x2)return x1-x2;
else return x2-x1;
}
//画一条粗线
//(x1,y1),(x2,y2):线条的起始坐标
//size:线条的粗细程度
//color:线条的颜色
void lcd_draw_bline(u16 x1, u16 y1, u16 x2, u16 y2,u8 size,u16 color)
{
u16 t;
int xerr=0,yerr=0,delta_x,delta_y,distance;
int incx,incy,uRow,uCol;
if(x1<size|| x2<size||y1<size|| y2<size)return;
delta_x=x2-x1; //计算坐标增量
delta_y=y2-y1;
uRow=x1;
uCol=y1;
if(delta_x>0)incx=1; //设置单步方向
else if(delta_x==0)incx=0;//垂直线
else {incx=-1;delta_x=-delta_x;}
if(delta_y>0)incy=1;
else if(delta_y==0)incy=0;//水平线
else{incy=-1;delta_y=-delta_y;}
if( delta_x>delta_y)distance=delta_x; //选取基本增量坐标轴
else distance=delta_y;
for(t=0;t<=distance+1;t++ )//画线输出
{
gui_fill_circle(uRow,uCol,size,color);//画点
xerr+=delta_x ;
yerr+=delta_y ;
if(xerr>distance)
{
xerr-=distance;
uRow+=incx;
}
if(yerr>distance)
{
yerr-=distance;
uCol+=incy;
}
}
}
//5个触控点的颜色(电容触摸屏用)
const u16 POINT_COLOR_TBL[5]={RED,GREEN,BLUE,BROWN,GRED};
//电阻触摸屏测试函数
void rtp_test(void)
{
// u8 key;
// u8 i=0;
// while(1)
// {
// key=KEY_Scan(0);
// tp_dev.scan(0);
// if(tp_dev.sta&TP_PRES_DOWN) //触摸屏被按下
// {
// if(tp_dev.x[0]<lcddev.width&&tp_dev.y[0]<lcddev.height)
// {
// if(tp_dev.x[0]>(lcddev.width-24)&&tp_dev.y[0]<16)Load_Drow_Dialog();//清除
// else TP_Draw_Big_Point(tp_dev.x[0],tp_dev.y[0],RED); //画图
// }
// }
// else
// delay_ms(10); //没有按键按下的时候
// if(key==KEY0_PRES) //KEY0按下,则执行校准程序
// {
// LCD_Clear(WHITE); //清屏
// TP_Adjust(); //屏幕校准
// TP_Save_Adjdata();
// Load_Drow_Dialog();
// }
// i++;
// if(i%20==0){LED1_TOGGLE;LED0_TOGGLE;};
// }
}
//电容触摸屏测试函数
void ctp_test(void)
{
u8 t=0;
u8 i=0;
u16 lastpos[5][2]; //最后一次的数据
while(1)
{
tp_dev.scan(0);
for(t=0;t<5;t++)
{
if((tp_dev.sta)&(1<<t))
{
//printf("X坐标:%d,Y坐标:%d\r\n",tp_dev.x[0],tp_dev.y[0]);
if(tp_dev.x[t]<lcddev.width&&tp_dev.y[t]<lcddev.height)
{
if(lastpos[t][0]==0XFFFF)
{
lastpos[t][0] = tp_dev.x[t];
lastpos[t][1] = tp_dev.y[t];
}
lcd_draw_bline(lastpos[t][0],lastpos[t][1],tp_dev.x[t],tp_dev.y[t],2,POINT_COLOR_TBL[t]);//画线
lastpos[t][0]=tp_dev.x[t];
lastpos[t][1]=tp_dev.y[t];
if(tp_dev.x[t]>(lcddev.width-24)&&tp_dev.y[t]<20)
{
Load_Drow_Dialog();//清除
}
}
}else lastpos[t][0]=0XFFFF;
}
delay_ms(5);i++;
if(i%20==0){LED1_TOGGLE;LED0_TOGGLE;};
}
}
/* USER CODE END 1 */
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
ltdc.h
/**
******************************************************************************
* @file ltdc.h
* @brief This file contains all the function prototypes for
* the ltdc.c file
******************************************************************************
* @attention
*
* <h2><center>© Copyright (c) 2021 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
*
******************************************************************************
*/
/* Define to prevent recursive inclusion -------------------------------------*/
#ifndef __LTDC_H__
#define __LTDC_H__
#ifdef __cplusplus
extern "C" {
#endif
/* Includes ------------------------------------------------------------------*/
#include "main.h"
/* USER CODE BEGIN Includes */
#include "lcd.h"
/* USER CODE END Includes */
extern LTDC_HandleTypeDef hltdc;
/* USER CODE BEGIN Private defines */
#define PIXELS_W 1024
#define PIXELS_H 600
extern LTDC_HandleTypeDef hltdc;
/* USER CODE BEGIN Private defines */
//定义颜色像素格式,一般用RGB565
#define LCD_PIXFORMAT 0X02
//定义默认背景层颜色
#define LTDC_BACKLAYERCOLOR 0X00000000
//LCD帧缓冲区首地址,这里定义在SDRAM里面.
#define LCD_FRAME_BUF_ADDR 0XC0000000
//LCD LTDC重要参数集
typedef struct
{
uint32_t pwidth; //LCD面板的宽度,固定参数,不随显示方向改变,如果为0,说明没有任何RGB屏接入
uint32_t pheight; //LCD面板的高度,固定参数,不随显示方向改变
uint16_t hsw; //水平同步宽度
uint16_t vsw; //垂直同步宽度
uint16_t hbp; //水平后廊
uint16_t vbp; //垂直后廊
uint16_t hfp; //水平前廊
uint16_t vfp; //垂直前廊
uint8_t activelayer; //当前层编号:0/1
uint8_t dir; //0,竖屏;1,横屏;
uint16_t width; //LCD宽度
uint16_t height; //LCD高度
uint32_t pixsize; //每个像素所占字节数
}_ltdc_dev;
/* USER CODE END Private defines */
void MX_LTDC_Init(void);
/* USER CODE BEGIN Prototypes */
void LTDC_Switch(uint8_t sw); //LTDC开关
void LTDC_Layer_Switch(uint8_t layerx,uint8_t sw); //层开关
void LTDC_Select_Layer(uint8_t layerx); //层选择
void LTDC_Display_Dir(uint8_t dir); //显示方向控制
void LTDC_Draw_Point(uint16_t x,uint16_t y,uint32_t color);//画点函数
uint32_t LTDC_Read_Point(uint16_t x,uint16_t y); //读点函数
void LTDC_Fill(uint16_t sx,uint16_t sy,uint16_t ex,uint16_t ey,uint32_t color); //矩形单色填充函数
void LTDC_Color_Fill(uint16_t sx,uint16_t sy,uint16_t ex,uint16_t ey,uint16_t *color); //矩形彩色填充函数
void LTDC_Clear(uint32_t color); //清屏函数
uint16_t LTDC_PanelID_Read(void);
void LTDC_Init(void);
void LTDC_Layer_Window_Config(u8 layerx,u16 sx,u16 sy,u16 width,u16 height);
void LTDC_Layer_Parameter_Config(u8 layerx,u32 bufaddr,u8 pixformat,u8 alpha,u8 alpha0,u8 bfac1,u8 bfac2,u32 bkcolor);
u16 LTDC_PanelID_Read(void);
void LTDC_Init(void);
void Load_Drow_Dialog(void);
void gui_draw_hline(u16 x0,u16 y0,u16 len,u16 color);
void gui_fill_circle(u16 x0,u16 y0,u16 r,u16 color);
void lcd_draw_bline(u16 x1, u16 y1, u16 x2, u16 y2,u8 size,u16 color);
void rtp_test(void);
void ctp_test(void);
/* USER CODE END Private defines */
void MX_LTDC_Init(void);
/* USER CODE BEGIN Prototypes */
/* USER CODE END Prototypes */
#ifdef __cplusplus
}
#endif
#endif /* __LTDC_H__ */
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/