基于Smart ZYNQ (SP/SP2/SL 版) 的PS实验 二十三 5寸RGB屏实验: 电容触摸部分DEMO

本节作为前两节的补充,介绍如何驱动我们5寸屏上的电容触摸功能。

  • 此章节内容适用于Smart ZYNQ SP SP2和 SL 版的板子 ( 不包含Smart ZYNQ 标准版 ),如是标准版或本站其他板子请看对应板子目录
  • 本文在 vivado2018.3版本上演示

本次实验需要外接本站的5寸IPS 屏幕模块,有关本次实验的 RGB屏的原理图手册以及其他例程资料可以参看下面汇总贴:5寸IPS 800X480 RGB接口LCD屏幕模块

触摸屏接口部分入下图所示,可以看出触摸部分的硬件包含一路I2C,一路reset复位信号,和一路INT中断信号

实验内容:

本次实验将采集触摸屏的触摸数据,并利用VDMA将触摸点的内容实时绘制到LCD屏幕上。每次触摸点的坐标通过VDMA高效地从内存传输到屏幕,实现了流畅的触摸数据显示和实时更新。

本文的工程将会在前一章的工程上直接修改添加触摸功能,关于VDMA LCD部分可以参考前一章的内容,基于Smart ZYNQ (SP/SP2/SL 版) 的PS实验二十一 5寸RGB屏实验:用VDMA模块来缓存图像并在LCD上显示(一)PS端彩条纹的显示

一、VIVADO工程的修改

  • VIVADO的工程我们需要增加下面几个地方:
    • 增加一路IIC(EMIO方式)用于连接触摸屏的 TOUCH_SDA,TOUCH_SCL
    • 增加两路GPIO(EMIO方式)用于连接触摸屏的 TOUCH_INT 和TOUCH_RESET
    • 增加一路GPIO(EMIO方式)用于连接按键,用来清屏用

1) 对Block Design 部分进行修改

a) 使能I2C 0 并选择EMIO

b) 在GPIO的地方添加3路EMIO GPIO

c) 之后回到 Block Design 界面, 分别右键 GPIO_0 和 IIC_0 选中Make External 引出这两组信号接口

d) 完成之后点选保存

2 ) 对顶层文件TOP_MODULE进行修改(增加 I2C 和3路EMIO增加的内容)

//www.hellofpga.com
`timescale 1ns / 1ps
module Top_Module(
output [7:0] LCD_R,
output [7:0] LCD_G,
output [7:0] LCD_B,
output LCD_HS,
output LCD_VS,
output LCD_DE,
output LCD_CLK,
output LCD_BL,
inout KEY,
inout TOUCH_RESET,
inout TOUCH_INT,
inout TOUCH_SDA,
inout TOUCH_SCL


);

assign LCD_BL=1'b1;

ZYNQ_CORE_wrapper u1(
.lcd_clk(LCD_CLK),
.lcd_data({LCD_R,LCD_G,LCD_B}),
.lcd_de(LCD_DE),
.lcd_hsync(LCD_HS),
.lcd_vsync(LCD_VS),
.GPIO_0_0_tri_io({KEY,TOUCH_RESET,TOUCH_INT}),
.IIC_0_0_scl_io(TOUCH_SCL),
.IIC_0_0_sda_io(TOUCH_SDA)

);


endmodule

3) 对管脚约束部分进行修改,增加上述信号

set_property PACKAGE_PIN U22 [get_ports LCD_VS]
set_property PACKAGE_PIN T22 [get_ports LCD_DE]
set_property PACKAGE_PIN W22 [get_ports LCD_CLK]
set_property PACKAGE_PIN V22 [get_ports LCD_HS]
set_property PACKAGE_PIN Y16 [get_ports LCD_BL]
set_property PACKAGE_PIN Y20 [get_ports {LCD_B[7]}]
set_property PACKAGE_PIN Y21 [get_ports {LCD_B[6]}]
set_property PACKAGE_PIN AA22 [get_ports {LCD_B[5]}]
set_property PACKAGE_PIN AB22 [get_ports {LCD_B[4]}]
set_property PACKAGE_PIN AA21 [get_ports {LCD_B[3]}]
set_property PACKAGE_PIN AB21 [get_ports {LCD_B[2]}]
set_property PACKAGE_PIN AB20 [get_ports {LCD_B[1]}]
set_property PACKAGE_PIN AB19 [get_ports {LCD_B[0]}]
set_property PACKAGE_PIN Y19 [get_ports {LCD_G[7]}]
set_property PACKAGE_PIN AA19 [get_ports {LCD_G[6]}]
set_property PACKAGE_PIN AA16 [get_ports {LCD_G[5]}]
set_property PACKAGE_PIN AB16 [get_ports {LCD_G[4]}]
set_property PACKAGE_PIN AA18 [get_ports {LCD_G[3]}]
set_property PACKAGE_PIN Y18 [get_ports {LCD_G[2]}]
set_property PACKAGE_PIN AB15 [get_ports {LCD_G[1]}]
set_property PACKAGE_PIN AB14 [get_ports {LCD_G[0]}]
set_property PACKAGE_PIN AA13 [get_ports {LCD_R[7]}]
set_property PACKAGE_PIN Y13 [get_ports {LCD_R[6]}]
set_property PACKAGE_PIN W13 [get_ports {LCD_R[5]}]
set_property PACKAGE_PIN V13 [get_ports {LCD_R[4]}]
set_property PACKAGE_PIN W17 [get_ports {LCD_R[3]}]
set_property PACKAGE_PIN W18 [get_ports {LCD_R[2]}]
set_property PACKAGE_PIN AB17 [get_ports {LCD_R[1]}]
set_property PACKAGE_PIN AA17 [get_ports {LCD_R[0]}]

set_property IOSTANDARD LVCMOS33 [get_ports LCD_VS]
set_property IOSTANDARD LVCMOS33 [get_ports LCD_HS]
set_property IOSTANDARD LVCMOS33 [get_ports LCD_DE]
set_property IOSTANDARD LVCMOS33 [get_ports LCD_CLK]
set_property IOSTANDARD LVCMOS33 [get_ports LCD_BL]
set_property IOSTANDARD LVCMOS33 [get_ports {LCD_R[7]}]
set_property IOSTANDARD LVCMOS33 [get_ports {LCD_R[6]}]
set_property IOSTANDARD LVCMOS33 [get_ports {LCD_R[5]}]
set_property IOSTANDARD LVCMOS33 [get_ports {LCD_R[4]}]
set_property IOSTANDARD LVCMOS33 [get_ports {LCD_R[3]}]
set_property IOSTANDARD LVCMOS33 [get_ports {LCD_R[1]}]
set_property IOSTANDARD LVCMOS33 [get_ports {LCD_R[2]}]
set_property IOSTANDARD LVCMOS33 [get_ports {LCD_R[0]}]
set_property IOSTANDARD LVCMOS33 [get_ports {LCD_G[7]}]
set_property IOSTANDARD LVCMOS33 [get_ports {LCD_G[6]}]
set_property IOSTANDARD LVCMOS33 [get_ports {LCD_G[5]}]
set_property IOSTANDARD LVCMOS33 [get_ports {LCD_G[4]}]
set_property IOSTANDARD LVCMOS33 [get_ports {LCD_G[3]}]
set_property IOSTANDARD LVCMOS33 [get_ports {LCD_G[2]}]
set_property IOSTANDARD LVCMOS33 [get_ports {LCD_G[1]}]
set_property IOSTANDARD LVCMOS33 [get_ports {LCD_G[0]}]
set_property IOSTANDARD LVCMOS33 [get_ports {LCD_B[7]}]
set_property IOSTANDARD LVCMOS33 [get_ports {LCD_B[6]}]
set_property IOSTANDARD LVCMOS33 [get_ports {LCD_B[5]}]
set_property IOSTANDARD LVCMOS33 [get_ports {LCD_B[4]}]
set_property IOSTANDARD LVCMOS33 [get_ports {LCD_B[3]}]
set_property IOSTANDARD LVCMOS33 [get_ports {LCD_B[2]}]
set_property IOSTANDARD LVCMOS33 [get_ports {LCD_B[1]}]
set_property IOSTANDARD LVCMOS33 [get_ports {LCD_B[0]}]

set_property IOSTANDARD LVCMOS33 [get_ports TOUCH_RESET]
set_property IOSTANDARD LVCMOS33 [get_ports TOUCH_SDA]
set_property IOSTANDARD LVCMOS33 [get_ports TOUCH_SCL]
set_property IOSTANDARD LVCMOS33 [get_ports KEY]
set_property IOSTANDARD LVCMOS33 [get_ports TOUCH_INT]
set_property PACKAGE_PIN W16 [get_ports TOUCH_RESET]
set_property PACKAGE_PIN AA14 [get_ports TOUCH_SDA]
set_property PACKAGE_PIN Y14 [get_ports TOUCH_SCL]
set_property PACKAGE_PIN V14 [get_ports TOUCH_INT]
set_property PACKAGE_PIN J20 [get_ports KEY]

4) 之后对工程进行再次的编译和综合, 随后重新Export hardware (详细操作可参考之前LCD的 VDMA章节内容)(最好对SDK下的bsp都右键refresh一下)

二、SDK部分的修改

1 ) 在原先SRC目录下添加 gt911.c 和gt911.h内容分别如下:

gt911.c: (包含触摸屏初始化,以及触摸屏寄存器的读写等操作)


//www.hellofpga.com
#include "xgpiops.h"
#include "xiicps.h"
#include "sleep.h"

#define RESET_PIN 55
#define INT_PIN 54

#define Slave_Address (0xBA>>1)

static XIicPs i2c;
extern XGpioPs Gpio;

int gt911_read_reg(uint16_t addr, uint8_t *buf, int len) {
uint8_t send_buf[2];
int status;

// Set the high and low bytes of the register address
send_buf[0] = (uint8_t)(addr >> 8);
send_buf[1] = (uint8_t)(addr & 0xFF);

// Send the read command
status = XIicPs_MasterSendPolled(&i2c, send_buf, 2, Slave_Address);
if (status != XST_SUCCESS) {
return status;
}

// Wait for the I2C bus to be idle
while (XIicPs_BusIsBusy(&i2c));

// Receive data from the I2C bus
status = XIicPs_MasterRecvPolled(&i2c, buf, len, Slave_Address);
if (status != XST_SUCCESS) {
return status;
}

// Wait for the I2C bus to be idle again
while (XIicPs_BusIsBusy(&i2c));

return XST_SUCCESS;
}

int gt911_write_reg(uint16_t addr, uint8_t *buf, int len) {
uint8_t send_buf[256] = {0};
int status;

// Set the high and low bytes of the register address
send_buf[0] = (uint8_t)(addr >> 8);
send_buf[1] = (uint8_t)(addr & 0xFF);

// Copy the data from the buffer into send_buf starting at index 2
memcpy(&send_buf[2], buf, len);

// Send the write command
status = XIicPs_MasterSendPolled(&i2c, send_buf, len + 2, Slave_Address);
if (status != XST_SUCCESS) {
return status;
}

// Wait for the I2C bus to be idle
while (XIicPs_BusIsBusy(&i2c));

return XST_SUCCESS;
}

int gt911_init(void)
{
XIicPs_Config *i2cConfig;
uint8_t buffer[5] = {0};

i2cConfig = XIicPs_LookupConfig(XPAR_XIICPS_0_DEVICE_ID);
XIicPs_CfgInitialize(&i2c, i2cConfig, i2cConfig->BaseAddress);
XIicPs_SetSClk(&i2c, 200000);

XGpioPs_SetDirectionPin(&Gpio, RESET_PIN, 1);
XGpioPs_SetDirectionPin(&Gpio, INT_PIN, 1);
XGpioPs_SetOutputEnablePin(&Gpio, RESET_PIN, 1);
XGpioPs_SetOutputEnablePin(&Gpio, INT_PIN, 1);

//SET 0XBA/0XBB
XGpioPs_WritePin(&Gpio, RESET_PIN, 0);
XGpioPs_WritePin(&Gpio, INT_PIN, 0);
usleep(200); //200us
XGpioPs_WritePin(&Gpio, RESET_PIN, 1);
usleep(55000); //55ms
XGpioPs_SetDirectionPin(&Gpio, INT_PIN, 0);//input_floating

//READ ID
gt911_read_reg(0x8140, buffer, 4);
buffer[3]='\0';
//xil_printf("CTP ID:%s\r\n", buffer);

if(strcmp((char*)buffer,"911")==0){
return 0;
}
return 1;
}


int gt911_read(int16_t *x, int16_t *y)
{
uint8_t buf[7] = {0};

// Read coordinate data
gt911_read_reg(0x814E, buf, 6);

// Clear the touch state
buf[6] = 0x0;
gt911_write_reg(0x814E, &buf[6], 1);

// Check if a touch event is detected
// The high bit of buf[0] (0x80) indicates touch validity,
// and the lower four bits (0x0F) indicate the number of touch points
if ((buf[0] & 0x80) && (buf[0] & 0x0F)) {
// Extract coordinates (16-bit)
*x = (buf[3] << 8) | buf[2]; // X coordinate
*y = (buf[5] << 8) | buf[4]; // Y coordinate
return 1; // Successfully read coordinates
}
return 0; // No touch detected
}

gt911.h

//www.hellofpga.com
#ifndef __GT911_H
#define __GT911_H

int gt911_read(int16_t *x, int16_t *y);
int gt911_init();

#endif

2 )修改main.c的内容

//www.hellofpga.com
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "xil_types.h"
#include "xil_cache.h"
#include "xparameters.h"
#include "xaxivdma.h"
#include "xaxivdma_i.h"
#include "sleep.h"
#include "xgpiops.h"
#include "gt911.h"

#define WIDTH 800
#define HEIGHT 480

#define VDMA_BASEADDR XPAR_AXI_VDMA_0_BASEADDR
#define VIDEO_BASEADDR0 0x01000000
uint8_t *frame_buffer0 = (uint8_t *)VIDEO_BASEADDR0;

#define GPIO_DEVICE_ID XPAR_XGPIOPS_0_DEVICE_ID
XGpioPs Gpio;

#define KEY 56


void Gpio_Init(){
XGpioPs_Config *ConfigPtr;
ConfigPtr = XGpioPs_LookupConfig(GPIO_DEVICE_ID);
XGpioPs_CfgInitialize(&Gpio, ConfigPtr,ConfigPtr->BaseAddr);
XGpioPs_SetDirectionPin(&Gpio, KEY, 0);
}


void VDMA_init(){
Xil_Out32((VDMA_BASEADDR + 0x000), 0x00000001);
Xil_Out32((VDMA_BASEADDR + 0x05c), VIDEO_BASEADDR0);
Xil_Out32((VDMA_BASEADDR + 0x058), (WIDTH*3));
Xil_Out32((VDMA_BASEADDR + 0x054), (WIDTH*3));
Xil_Out32((VDMA_BASEADDR + 0x050), HEIGHT);
Xil_Out32((VDMA_BASEADDR + 0x028), 0X00000000);
}


void clear_screen(void) {
unsigned int i, j;
for (i = 0; i < WIDTH; i++) {
for (j = 0; j < HEIGHT; j++) {
frame_buffer0[(j * WIDTH + i) * 3 + 2] = 0x00;
frame_buffer0[(j * WIDTH + i) * 3 + 1] = 0x00;
frame_buffer0[(j * WIDTH + i) * 3 + 0] = 0x00;
}
}
Xil_DCacheFlush();
}

void write_pixel_to_frame_buffer(int16_t pos_x, int16_t pos_y) {
if (pos_x >= 0 && pos_x < WIDTH && pos_y >= 0 && pos_y < HEIGHT) {
frame_buffer0[(pos_y * WIDTH + pos_x) * 3 + 2] = 0xFF;
frame_buffer0[(pos_y * WIDTH + pos_x) * 3 + 1] = 0xFF;
frame_buffer0[(pos_y * WIDTH + pos_x) * 3 + 0] = 0xFF;
Xil_DCacheFlush();
}
}


int main(void) {
int16_t pos_x, pos_y;

Gpio_Init();
VDMA_init();
gt911_init();
clear_screen();

while (1) {
if (XGpioPs_ReadPin(&Gpio, KEY) == 0)
clear_screen();
else if (gt911_read(&pos_x, &pos_y)) {
write_pixel_to_frame_buffer(pos_x, pos_y);
}
usleep(1000); // 1ms
}
}

3 ) 程序解读

a ) 首先是VDMA_init(),这个在前两章内容中有提到,这里主要是设置了缓存区的地址为 VIDEO_BASEADDR0,(备注虽然我们的vdma设置了三帧缓存,但是本次工程我们实际只用了一帧进行演示),这里我们通过配置028h的4-0bit来让vdma停留在我们指定的缓存帧0上(前文中已将vdma 配置在非循环状态下,Park Mode)

b ) write_pixel_to_frame_buffer:根据传入的坐标(pos_x, pos_y)将该位置的像素颜色设置为白色。

c) clear_screen:清空屏幕(将每个像素的颜色设置为黑色)。每个像素使用 3 字节表示 RGB 颜色,每次循环将每个像素的 RGB 值都设置为 0x00

d) main函数: 先初始化GPIO、VDMA、GT911触摸屏控制器,并通过 clear_screen() 清空屏幕。

e ) 在主循环中,检查按键状态,如果按键按下则清空屏幕;如果触摸屏有触摸事件,则获取触摸坐标并更新显示屏上的像素。这里的延时必须加,否则会因为不停的扫描反而造成扫描卡住的情况

  while (1) {
if (XGpioPs_ReadPin(&Gpio, KEY) == 0)
clear_screen();
else if (gt911_read(&pos_x, &pos_y)) {
write_pixel_to_frame_buffer(pos_x, pos_y);
}
usleep(1000); // 1ms
}

三、实机调试:

回到SDK界面,对程序进行正常编译,并debug,这时候屏幕会显示黑色背景,此时我们用手指划过屏幕,可以在屏幕上看到我们划过的轨迹都会被白色的点覆盖, 证明我们的程序运行成功。(当我们按下主板上的KEY2键, 图像会被重新清空)

写在后面:

GT911其实可以支持5点同时触控的,这里的程序只演示了一个点的读取,大家如果对5个点的触摸方式感兴趣可以自行尝试修改。

  • 本文的完整工程下载:23_VDMA_5INCH_LCD_TOUCH_PANEL_DEMO
  • VIVADO的版本:2018.3
  • 工程创建目录: E:\Smart_ZYNQ_SP_SL\SDK\23_VDMA_5INCH_LCD_TOUCH_PANEL_DEMO
  • 工程适用主板: Smart ZYNQ (SP / SP2 / SL) (不适用于Smart ZYNQ 标准版 

“基于Smart ZYNQ (SP/SP2/SL 版) 的PS实验 二十三 5寸RGB屏实验: 电容触摸部分DEMO”的一个回复

  1. 哈喽,贴主你好呀,我按照您的教程成功移植了LVGL例程画面,但是美中不足的是屏幕的触屏似乎用不了,我用的是GT911的芯片,像是IIC地址那些我看了您上面的评论应该是不相干的。请问接下来我应该从哪些方面下手调试?

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注