触摸屏接口设置-洋桃百科
电路设计
触摸屏的组成:
-
PCB板层:是电子设备中用于支撑和连接电子元件的基板。
-
显示层:是LCD触摸屏的核心部分,负责生成视觉图像。它由以下部分组成:
- 液晶层:包含液晶材料,通过改变电场来控制光线的通过,从而显示图像。
- 偏振片:贴在液晶层的两侧,与液晶材料一起工作,控制光线的偏振状态,以实现图像的明暗和色彩变化。
- 彩色滤光片:位于液晶层上方,包含红、绿、蓝三种颜色的滤光点,与液晶层配合产生全彩显示。
- 背光单元:通常位于LCD面板的背面,提供均匀的光源,使图像可见。背光可以是LED、CCFL或其他类型的光源。
-
触摸层:位于显示层的最上方,允许用户通过触摸与设备交互。
触摸层的类型和结构因技术而异:
- 电阻式触摸层:由两层导电材料组成,当用户触摸屏幕时,两层接触,形成电压变化,检测触摸位置。
- 电容式触摸层:利用人体电场与电容的变化来检测触摸。这层通常由导电材料(如ITO,氧化铟锡)制成,分布在保护玻璃的内侧。
- 触摸控制器:一个专用的集成电路或微控制器,处理触摸信号并转换为位置数据,然后发送给主控制系统。
- 传感器矩阵:在电容式触摸屏中,传感器矩阵由多个水平和垂直传感器组成,以高精度检测触摸点。
对显示层原件的详细介绍:
-
反光板:其作用是将光线反射到屏幕正面,增加可见度。
-
棱镜板:棱镜板通过微小的棱镜结构来聚集光线,使光线更集中地射向观看者,这样可以提高屏幕的亮度和清晰度。
-
黑色液晶板:是液晶屏的显示区域,液晶层是LCD屏幕的核心部分,由液晶材料和电极组成。液晶材料可以根据电场的变化改变其分子排列,从而控制光线的通过。
-
背光灯:紧贴导光板,形成均匀白色背光。通过提供均匀的白色背景光来照亮屏幕,使得图像可见。通常使用LED作为背光灯,因为它们比传统的冷阴极荧光灯(CCFL)更节能、更薄,并且寿命更长。
-
导光板:导光板位于背光灯上方,其作用是将背光灯的光线均匀分布到整个屏幕。它通常由透明的塑料材料制成,并通过特殊的纹理或结构设计来散射和引导光线。
技术理论
触摸屏接口定义
显示部分
触摸部分
手指的位置数据是需要单片机主动询问触摸屏,才会得到数据,如果一直询问就会占用大量资源,为了解决占用资源问题,才设置了INT引脚。
在没有触摸的时候,INT引脚始终是高电平,有触摸的时候变成低电平。
需要把INT引脚接到单片机的EXTI接口:
背光
FSMC接口
TFT_LCD本身就是一个1152KB的存储器。
这里用LCD接口接到FSMC接口上面,操作FSMC就可以控制LCD,显示屏的显示区域就变成单片机的内部区域了。
通信协议
FSMC
FSMC简述
FSMC(Flexible Static Memory Controller)可以驱动LCD,主要是因为LCD的显存(GRAM)操作与外部静态存储器的操作类似,都是基于地址和数据的读写。FSMC能够模拟与LCD通信所需的接口时序,特别是对于使用8080或类似接口的LCD。以下是FSMC驱动LCD的基本步骤和原理:
-
地址映射:首先,需要将LCD的显存地址映射到STM32的地址空间中。这样,通过访问特定的地址,就可以直接与LCD的显存交互。
-
接口信号配置:FSMC需要配置相应的信号引脚,包括数据线(D0-D15)、地址线(A0-A25)、控制信号(如片选NE、读使能NOE、写使能NWE等)。
-
时序配置:FSMC需要根据LCD的数据手册配置正确的时序参数,包括地址建立时间、数据建立时间、总线转换时间等。这些参数确保数据在读写操作中能够正确同步。
-
模式选择:FSMC可以工作在不同的模式下,对于LCD,通常使用模式A或模式B。模式A适用于异步SRAM,模式B适用于异步NOR Flash,但也可以用于LCD,特别是当LCD使用与NOR Flash类似的8080接口时。
-
信号极性配置:需要根据LCD的要求配置信号的极性,例如,读使能和写使能的高电平或低电平有效。
-
初始化代码编写:编写初始化代码来配置FSMC的控制寄存器,包括FSMC_BCR(Bank Control Register)和FSMC_BTR(Bank Timing Register)等。
-
读写函数实现:实现读写函数,使用FSMC的地址映射和控制信号来向LCD发送命令和数据。
-
模拟8080接口:如果LCD使用8080接口,FSMC可以通过以下方式模拟此接口:
- 使用片选信号(NE)作为LCD的CS(Chip Select)信号。
- 使用读使能(NOE)和写使能(NWE)作为LCD的RD(Read)和WR(Write)信号。
- 使用地址线(A0-A25)来发送命令和数据。
- 使用数据线(D0-D15)进行数据传输。
-
D/CX信号处理:对于8080接口的LCD,D/CX(Data/Command Select)信号用于区分当前传输的是命令还是数据。这可以通过将D/CX连接到FSMC的某个地址线(如A0)来实现,通过设置该地址线的电平来控制传输类型。
-
背光和复位控制:LCD的背光和复位通常由GPIO控制,需要额外配置GPIO引脚,并在初始化过程中控制这些引脚以正确设置LCD。
通过上述步骤,FSMC可以有效地驱动LCD,实现图形显示和其他视觉输出功能。在实际应用中,可能还需要考虑其他因素,如LCD的初始化过程、电源管理、中断处理等。
显示板掉电后就会丢失数据,所以属于SRAM存储器。
LCD屏接口类型:
FSMC参数设置
这里举例,物联网实战项目用到的:
因为bank1是PSRAM区域,所以选择bank1
设置FSMC为高电平,提高通讯的稳定性:
FSMC函数介绍
这里对应的:
所以FSMC的数据操作地址是算法是按上面的方法来算,洋桃电子视频里面讲的就是A18:
I2C接口
I2C读写地址。
RST
INT
BL
- 第一种模式,只开关背光,就只用设置GPIO接口
-
第二种调节背光亮度,设置BL为TIM3_CH2,进入定时器设置PWM
-
PWM相关函数:
TFT触屏手册
LCD
-
分辨率信息
-
色彩模式
-
接口
-
内部结构图
-
引脚定义
给模块制造商看的
-
接口设置
选择驱动方式
-
色彩方式
因为我们是16bit数据,所以选择这个:D23-D16是灰色数据。
65K-color:只用发送一次16bit数据,就可以把一个像素点的色彩全部显示出来。
262k-color:通过发送三次16bit数据,可以包含两个像素点的6位RGB数据。
16.7M-color:通过发送三次16bit数据,可以包含两个像素点的8位RGB数据。
-
显示模式方式
TOUCH IC
-
协议
一般是I2C协议的话,就带有地址,给出七位地址的计算方法:
也有SPI协议方式
-
寄存器表:
一般会读取ID编号,触摸状态寄存器、同一时间最多手指数量、触摸点位置寄存器
读取坐标就只有两步:
- 读出触摸点数量
- 根据数量,去读触摸点X,Y位置,有一个就只用读一个触摸点
触摸画图板开发
背光调节
while (1)
{
if(KEY_1()){
__HAL_TIM_SetCompare(&htim3,TIM_CHANNEL_2,0);//设置占空比函数(参数3是PWM比值,范围0~ARR计数周期)
}
if(KEY_2()){
__HAL_TIM_SetCompare(&htim3,TIM_CHANNEL_2,100);//设置占空比函数(参数3是PWM比值,范围0~ARR计数周期)
}
if(KEY_3()){
__HAL_TIM_SetCompare(&htim3,TIM_CHANNEL_2,200);//设置占空比函数(参数3是PWM比值,范围0~ARR计数周期)
}
if(KEY_4()){
__HAL_TIM_SetCompare(&htim3,TIM_CHANNEL_2,499);//设置占空比函数(参数3是PWM比值,范围0~ARR计数周期)
}
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
驱动触摸IC
-
touch.h
#ifndef __TOUCH_H__ #define __TOUCH_H__ #include "stm32f4xx_hal.h" //HAL库文件声明 #include "main.h" #include "lcd.h" #include "stdlib.h" #include "math.h" #include "lcd.h" extern I2C_HandleTypeDef hi2c2;//声明I2C2的HAL库结构体 #define TOUCH_GT9xxx_ADD 0x28 //GT9147/GT911芯片的I2C器件地址 #define TOUCH_MAX 5 //电容屏支持的点数,固定为5点 #define Module_Switch1 0x35 //模块配置1(其中低2位控制INT输出方式 //INT输出方式(0x34上升沿触发,0x35下降沿触发,0x36低电平查询,0x37高电平查询) extern uint8_t TOUCH_ID[5];//触摸芯片ID码(TOUCH_Init初始化后可使用,以ASCII码方式的“9147”) extern uint16_t TOUCH_X[TOUCH_MAX];//当前触发时的X坐标 extern uint16_t TOUCH_Y[TOUCH_MAX];//当前触发时的Y坐标 extern uint8_t TOUCH_STA;//触摸屏当前触发状态 //TOUCH_STA低BIT0-BIT3位保存触发点数量,BIT6触摸状态(1触发,0放开),BIT7数据更新状态(1数据已更新,0数据未准备好) //扫描方向定义(原始方向是以竖屏左上角为基点X坐标=0,Y坐标=0) #define Portrait 0 //原始竖屏坐标(竖屏时的左上角向右是X坐标,向下是Y坐标) #define Landscape 1 //横屏坐标(竖屏时的右上角向下是X坐标,向左是Y坐标,即横屏)(横屏适用于洋桃2号开发板) #define Portrait_reversal 2 //竖屏坐标反转180度 #define Landscape_reversal 3 //横屏坐标反转180度 //-----------------【GT9147/GT911子地址表】-----------------------// // #define GT9xxx_COM 0X8040 //指令控制。操作内容如下: //【GT9xxx_COM说明】 //0:读坐标状态 //1、2:差值原始值 //3:基准更新 //4:基准校准//5:关屏 //6:进入充电模式 7:退出充电模式8:切换手势唤醒固件 //0x20:进入从机接近检测模式 //0x21:进入主机接近检测模式 //0x22:进入数据传输模式 //0x23:进入主机传输模式//0x28:退出从机检测模式 //0x29:退出主机接近检测模式 //0x2A:退出数据传输模式//0xAA:ESD 保护机制使用,由驱动定时写入0xAA 并定时读取检查 //其余值无效 #define GT9xxx_Ver 0X8047 //配置文件版本号 #define GT9xxx_CHKSUM 0X80FF //配置信息校验(0x8047到0x80FE之字节和的补码) #define GT9xxx_PID 0X8140 //产品ID(从0X8140~0X8143) #define GT9xxx_STA 0X814E //触摸屏状态总标志位 #define GT9xxx_TP1 0X8150 //GT9147/GT911芯片第1个触摸点坐标地址 #define GT9xxx_TP2 0X8158 //GT9147/GT911芯片第2个触摸点坐标地址 #define GT9xxx_TP3 0X8160 //GT9147/GT911芯片第3个触摸点坐标地址 #define GT9xxx_TP4 0X8168 //GT9147/GT911芯片第4个触摸点坐标地址 #define GT9xxx_TP5 0X8170 //GT9147/GT911芯片第5个触摸点坐标地址 //GT9147数据手册有错误,第1个触摸点坐标地址是0X8150(以此类推) void GT9xxx_Write_Config(uint8_t save); uint8_t TOUCH_Init(void);//触摸屏初始化(驱动芯片GT9xxx)(返回1成功,0失败) void TOUCH_Read(uint8_t dir);//读取触摸屏当前状态,有触发则改变TOUCH_STA,读出5个触发点坐标在TOUCH_X和TOUCH_Y数组中 #endif /********************************************************************************************* * 洋桃电子 www.doyoung.net * 部分程序代码复制自网络开源资料 如有侵权请联系我们处理 * 洋桃电子原创程序代码部分均未声明版权 可自由复制使用 我们不对代码做任何担保 *********************************************************************************************/
-
touch.c
/* * oled.c * * Created on: Jun 11, 2022 * Author: Administrator */ /* //杜洋工作室出品 //洋桃系列开发板应用程序 //关注微信公众号:洋桃电子 //洋桃开发板资料下载 www.DoYoung.net //即可免费看所有教学视频,下载技术资料,技术疑难提问 //更多内容尽在 杜洋工作室主页 www.doyoung.net */ #include "touch.h" //本驱动程序可支持GT9147和GT911两款芯片(其电路与寄存器地址基本兼容) uint8_t TOUCH_ID[5]={0};//触摸芯片ID码(TOUCH_Init初始化后可使用,以ASCII码方式的“9147”或“911”) uint16_t TOUCH_X[TOUCH_MAX];//当前触发时的X坐标 uint16_t TOUCH_Y[TOUCH_MAX];//当前触发时的Y坐标 uint8_t TOUCH_STA;//触摸屏当前触发状态 const uint16_t GT9xxx_TP_DAT[6]={GT9xxx_TP1,GT9xxx_TP2,GT9xxx_TP3,GT9xxx_TP4,GT9xxx_TP5};//5个触摸点坐标的I2C子地址(包括GT911和GT9xxx地址有错位) const uint8_t GT9xxx_Config[]={//GT9xxx配置数据(从设置寄存器0x8047地址开始写入,详见《GT9147编程指南》) 0X60,0XE0,0X01,0X20,0X03,0X05,Module_Switch1,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 }; void GT9xxx_Write_Config(uint8_t save){//发送GT9xxx_Config配置数据(初始化时使用)(参数:1保存数据到GT9xxx芯片,0不保存) uint8_t buf[2]; uint8_t i=0; buf[0]=0; buf[1]=save;//控制是否将配置数据保存到GT9147/GT911芯片中(0不保存,1保存) for(i=0;i<sizeof(GT9xxx_Config);i++) buf[0]+=GT9xxx_Config[i];//求得配置数据的校验和 buf[0]=(~buf[0])+1; HAL_I2C_Mem_Write(&hi2c2,TOUCH_GT9xxx_ADD,GT9xxx_COM,I2C_MEMADD_SIZE_16BIT,(uint8_t*)GT9xxx_Config,sizeof(GT9xxx_Config),1000);//发送配置数据 HAL_I2C_Mem_Write(&hi2c2,TOUCH_GT9xxx_ADD,GT9xxx_CHKSUM,I2C_MEMADD_SIZE_16BIT,buf,2,1000);//发送校验和结果 } uint8_t TOUCH_Init(void){//触摸屏初始化(驱动芯片GT9xxx)(返回1成功,0失败) uint8_t t; GPIO_InitTypeDef GPIO_InitStruct = {0}; HAL_GPIO_WritePin(TOUTH_RST_GPIO_Port,TOUTH_RST_Pin, GPIO_PIN_RESET);//向RST复位接口输出10毫秒低电平脉冲 HAL_Delay(5); HAL_GPIO_WritePin(TOUTH_RST_GPIO_Port,TOUTH_RST_Pin, GPIO_PIN_SET);// HAL_Delay(10); //器件地址设置完成后,将INT设置为高阻输入 GPIO_InitStruct.Pin = TOUCH_INT_Pin; GPIO_InitStruct.Mode = GPIO_MODE_INPUT; GPIO_InitStruct.Pull = GPIO_NOPULL; HAL_GPIO_Init(GPIOG, &GPIO_InitStruct);//写入IO设置 HAL_Delay(100); TOUCH_STA = HAL_I2C_Mem_Read(&hi2c2,TOUCH_GT9xxx_ADD,GT9xxx_PID,I2C_MEMADD_SIZE_16BIT,TOUCH_ID,4,1000);//读出产品ID if(strcmp((char*)TOUCH_ID,"9147")==0 || strcmp((char*)TOUCH_ID,"911")==0){//判断ID是"9147"或“911”(ASCII码方式的ID) t=0X02; HAL_I2C_Mem_Write(&hi2c2,TOUCH_GT9xxx_ADD,GT9xxx_COM,I2C_MEMADD_SIZE_16BIT,&t,1,1000);//写指令控制:复位芯片 HAL_I2C_Mem_Read(&hi2c2,TOUCH_GT9xxx_ADD,GT9xxx_Ver,I2C_MEMADD_SIZE_16BIT,&t,1,1000); //读取配置文件版本号 if(t<0X60)GT9xxx_Write_Config(1);//判断版本号过小时//写入配置文件(参数:1保存数据到GT9xxx芯片,0不保存) HAL_Delay(10);//延时 t=0;//GT9xxx_COM赋值0:读坐标状态 HAL_I2C_Mem_Write(&hi2c2,TOUCH_GT9xxx_ADD,GT9xxx_COM,I2C_MEMADD_SIZE_16BIT,&t,1,1000);//写入指令控制,进入读坐标的工作状态 return 1;//返回成功 } return 0;//返回失败 } void TOUCH_Read(uint8_t dir){//读取触摸屏状态(有触发则改变TOUCH_STA,读出5个触发点坐标在TOUCH_X[]和TOUCH_Y[]数组中) //TOUCH_STA低BIT0-BIT3位保存触发点数量,BIT4触摸状态(1触发,0放开),BIT7数据更新状态(1数据已更新,0数据未准备好) //参数:竖屏或横屏的方向(详见touch.h文件) uint8_t buf[4]; uint8_t i=0,STA=0; if(HAL_GPIO_ReadPin(GPIOG,TOUCH_INT_Pin)==GPIO_PIN_RESET){//读INT接口的电平(必须将Module_Switch1设置为0x35下降沿触发) HAL_I2C_Mem_Read(&hi2c2,TOUCH_GT9xxx_ADD,GT9xxx_STA,I2C_MEMADD_SIZE_16BIT,&STA,1,1000); //读取触摸屏状态总标志位 if((STA&0X80) && ((STA&0x0F)<=TOUCH_MAX)){//判断BIT7的就绪标志位是不是1,同时BIT0-3的触发点数量要小于最大点数 TOUCH_STA = STA;//将总标志位中的数据放入TOUCH_STA全局标志位(可在主函数中使用TOUCH_STA) for(i=0;i<5;i++){//写入坐标之前先清0 TOUCH_X[i]=0;TOUCH_Y[i]=0;//寄存器清0 } for(i=0;i<(STA&0x0F);i++){//循环读出5个坐标值 HAL_I2C_Mem_Read(&hi2c2,TOUCH_GT9xxx_ADD,GT9xxx_TP_DAT[i],I2C_MEMADD_SIZE_16BIT,buf,4,1000); //读取XY坐标值 if(dir==Portrait){//判断是横屏还是竖屏(0是竖屏,1是横屏) TOUCH_X[i]=((uint16_t)buf[1]<<8)+buf[0];//写入坐标值到全局X坐标数组(竖屏坐标写入) TOUCH_Y[i]=((uint16_t)buf[3]<<8)+buf[2];//写入坐标值到全局Y坐标数组(竖屏坐标写入) }else if(dir==Landscape){ TOUCH_Y[i]=LCD_Width-(((uint16_t)buf[1]<<8)+buf[0]);//写入坐标值到全局X坐标数组(横屏坐标写入) TOUCH_X[i]=((uint16_t)buf[3]<<8)+buf[2];//写入坐标值到全局Y坐标数组(横屏坐标写入) }else if(dir==Portrait_reversal){ TOUCH_X[i]=LCD_Width-(((uint16_t)buf[1]<<8)+buf[0]);//写入坐标值到全局X坐标数组(竖屏坐标写入) TOUCH_Y[i]=LCD_Height-(((uint16_t)buf[3]<<8)+buf[2]);//写入坐标值到全局Y坐标数组(竖屏坐标写入) }else if(dir==Landscape_reversal){ TOUCH_Y[i]=((uint16_t)buf[1]<<8)+buf[0];//写入坐标值到全局X坐标数组(横屏坐标写入) TOUCH_X[i]=LCD_Height-(((uint16_t)buf[3]<<8)+buf[2]);//写入坐标值到全局Y坐标数组(横屏坐标写入) } } i=0; HAL_I2C_Mem_Write(&hi2c2,TOUCH_GT9xxx_ADD,GT9xxx_STA,I2C_MEMADD_SIZE_16BIT,&i,1,1000);//清除触发标志 } } } /********************************************************************************************* * 洋桃电子 www.doyoung.net * 部分程序代码复制自网络开源资料 如有侵权请联系我们处理 * 洋桃电子原创程序代码部分均未声明版权 可自由复制使用 我们不对代码做任何担保 *********************************************************************************************/
-
main.c:不同手指数量刷新不同颜色
TOUCH_Init();//触摸显示部分初始化函数 /* USER CODE END 2 */ /* Infinite loop */ /* USER CODE BEGIN WHILE */ while (1) { TOUCH_Read(Landscape);//读出手指数量和X、Y轴坐标(参数是屏幕方向Portrait纵向,Landscape横向) if(TOUCH_STA&0x90){//判断有没有触摸屏触发 if((TOUCH_STA&0x0F) == 1)LCD_CLEAR(White);判断触摸手指数量,清屏(单色背景) else if((TOUCH_STA&0x0F) == 2)LCD_CLEAR(Purple); else if((TOUCH_STA&0x0F) == 3)LCD_CLEAR(Red); else if((TOUCH_STA&0x0F) == 4)LCD_CLEAR(Green); else if((TOUCH_STA&0x0F) == 5)LCD_CLEAR(Yellow); else LCD_CLEAR(Blue);//清屏(单色背景) } TOUCH_STA=0;//标志位清0 /* USER CODE END WHILE */ MX_USB_HOST_Process(); /* USER CODE BEGIN 3 */ }
位图和矢量图
位图:
- 每个像素点需要占用空间
- 图片是由许多个不同颜色和亮度的像素点拼凑而成
矢量图:
-
用的几何数学原理,在屏幕上用点和线绘制在特定的数学公式下实时绘制出图形
-
矢量图中的每个数据都是由数学公式产生的
-
只需要存放数学公式,在应用中传入公式参数就可以运用了
位图,20kb:
矢量图:
只占用十个字节
关于矢量图函数的解读:
NT35510的LCD函数详解01(洋桃电子-触摸屏开发者笔记)
NT35510的LCD函数详解02(洋桃电子-触摸屏开发者笔记)
图片取模
void LCD_DISPLAY_BMP(uint16_t sx,uint16_t sy,uint16_t *COLOR){//绘制BMP位图(参数:左上角X,左上角Y坐标,BMP位图数组)
uint16_t i,j,h,w;
w= COLOR[1];//得出绘图宽度(通过Image2lcd软件生成数组时带有的图片头数据得到)
h= COLOR[2];//得出绘图高度
for(i=0;i<h;i++){//循环写入行数(高度h)
LCD_Write_Cursor(sx,sy+i);//设置光标位置
LCD_Write_COM(SET_GRAM);//开始写入GRAM
for(j=0;j<w;j++)//循环写入列出(宽度w)
HAL_SRAM_Write_16b(&hsram1,LCD_DAT,&COLOR[i*w+j+4],1);//向LCD写16位数据(句柄,指令COM/数据DAT,存放寄存器,数量)
}
}
16bit,就是一次性读取两个字节,所以COLOR[0]是读取的scan和gray值。COLOR[3]是颜色模式,COLOR[4]才是颜色数,所以从这里开始。
文字显示
单个字符显示
单个字符显示函数可以显示单个字符,传入‘0’,带引号的就是传入ASCII值。
void LCD_DISPLAY_ASCII(uint16_t x,uint16_t y,uint8_t adcii,uint8_t size,uint8_t overlay){//在屏上显示一个字符
//参数:X坐标,Y坐标,字符内容,字号(12/16/24/32/48),叠加/覆盖(1叠加,0覆盖)
uint8_t b,r;//b存储当前处理的字节数据,r用作内部循环的索引,表示在当前字节中处理的行
uint16_t t,z=y;//t用作外部循环的索引,表示字符数据中的字节位置,z用于存储字符显示的起始Y坐标,用于在一行字符绘制完毕后重置Y坐标
uint16_t csize=(size/8)*(size/2);//得出一个字符所用到的字节数量
adcii=adcii-' ';//得到偏移后的值(ASCII字库是从空格开始取模,所以-' '就是对应字符的字库)
for(t=0;t<csize;t++){
if(size==12)b=ASCII_1206[adcii][t]; //调用1206字体
else if(size==16)b=ASCII_1608[adcii][t]; //调用1608字体
else if(size==24)b=ASCII_2412[adcii][t]; //调用2412字体
else if(size==32)b=ASCII_3216[adcii][t]; //调用3216字体
else if(size==48)b=ASCII_4824[adcii][t]; //调用4824字体
else b=ASCII_4824[adcii][t]; //如输入字体值错误,则调用4824字体
for(r=0;r<8;r++){
if(b&0x80)LCD_Vector_Point2(x,y,ForeColor);
else if(overlay==0)LCD_Vector_Point2(x,y,BackColor);
b<<=1;y++;
if(y>=SET_Height)return;//判断Y方向超出显示区域
if((y-z)==size){
y=z;x++;
if(x>=SET_Width)return;//判断X方向超出显示区域
break;
}
}
}
}
-
x
和y
:这两个uint16_t
类型的变量分别代表字符在LCD屏幕上显示的X(水平)和Y(垂直)坐标。 -
adcii
:这是一个uint8_t
类型的变量,表示要显示的ASCII字符。字符是通过减去空格字符的ASCII码(即' '
)来获取字库中的偏移值。 -
size
:这也是一个uint8_t
类型的变量,表示字符的字号大小,常见的大小有12、16、24、32和48。字号大小决定了字符的像素高度和宽度。 -
overlay
:一个uint8_t
类型的变量,用于决定字符的显示方式。值为1时表示叠加(overlay),即在现有像素上显示字符;值为0时表示覆盖(overwrite),即用字符覆盖现有像素。 -
b
:这是一个uint8_t
类型的局部变量,用于存储当前处理的字节数据,即字符的某一部分。 -
r
:这是另一个uint8_t
类型的局部变量,用作内部循环的索引,表示在当前字节中处理的行。 -
t
:uint16_t
类型的局部变量,用作外部循环的索引,表示字符数据中的字节位置。 -
z
:uint16_t
类型的局部变量,用于存储字符显示的起始Y坐标,用于在一行字符绘制完毕后重置Y坐标。 -
csize
:uint16_t
类型的局部变量,表示一个字符所用到的字节数量,计算公式为(size/8)*(size/2)
,这个值取决于字号大小。
在ASCII字符字库中,每个字符通常都是等宽的,这意味着每个字符占用的像素宽度是相同的。在这段代码中,size
变量代表了字符的字号大小,也就是字符的像素高度。由于字符是等宽的,字符的宽度通常是高度的一半(对于常见的字号大小,如12、16、24、32等)。
这里的 size/2
实际上是用来计算每个字符的宽度(以像素为单位)。例如,如果字符大小是16x16像素,那么每个字符的高度是16像素,宽度则是8像素。因此,size/2
就是用来得到这个宽度值。
然后,csize
的计算公式 (size/8)*(size/2)
表示的是:
-
size/8
计算出每个字符一行所需的字节数。因为每个字节包含8位,所以如果字符是8像素宽,那么一行字符所需的字节数就是size/8
。 -
(size/2)
计算出字符的行数,也就是字符的高度(以字节为单位)。
将这两个值相乘,就得到了每个字符所需的总字节数。这个公式适用于等宽字体,其中每个字符的宽度是固定的,高度与字号大小成正比。
例如,对于16x16像素的字符:
- 宽度(以字节为单位):
16 / 8 = 2
字节(因为8像素宽,每像素1位,所以是2字节)。 - 高度(以字节为单位):
16 / 2 = 8
行(因为16像素高,宽度是8像素,所以是8行)。
所以,每个16x16像素的字符所需的字节数是 2 * 8 = 16
字节。这个计算确保了无论字符的字号大小如何,都能正确计算出所需的字节数。