百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术文章 > 正文

ADC触摸屏编程_测试

itomcoil 2025-04-29 01:22 9 浏览


来源:百问网_嵌入式Linux wiki_jz2440 新1期视频维基教程 (视频文字版)

作者:韦东山

本文字数:5890,阅读时长:4.5分钟

第018课 ADC和触摸屏 第011节_触摸屏编程_测试

发现程序有bug,点击坐标一次,程序就完成执行,我们需要修改触摸屏文件tsib.c

static double g_kx;
static double g_ky;

static int g_ts_xc, g_ts_yc;
static int g_lcd_xc, g_lcd_yc;
static int g_ts_xy_swap = 0;


static unsigned int fb_base;
static int xres, yres, bpp;


void get_calibrate_point_data(int lcd_x, int lcd_y, int *px, int *py)
{
	int pressure;
	int x, y;
	
	fb_disp_cross(lcd_x, lcd_y, 0xffffff);

	/* 等待点击 */

	do {
//如果pressure一直是0的话我们丢掉这些数据
		ts_read_raw(&x, &y, &pressure); 
	} while (pressure == 0);

//让后再次读
	do {
		*px = x;
		*py = y;
		ts_read_raw(&x, &y, &pressure); 
		printf("get raw data: x = %08d, y = %08d\n\r", x, y);
	} while (pressure);

	printf("return raw data: x = %08d, y = %08d\n\r", *px, *py);

	/* 直到松开才返回 */
	fb_disp_cross(lcd_x, lcd_y, 0);
}

修改touchcreen.c文件添加压力值相关信息

//定义压力值全局变量
static int g_ts_pressure;
void report_ts_xy(int x, int y, int pressure);

//发现不能画线,修改 ts_read
//我们需要判断触摸笔是按下还是松开,需要添加压力值参数
/*
 * 读TS原始数据, 转换为LCD坐标
 * 我们需要加上压力值lcd_pressure
 */
int ts_read(int *lcd_x, int *lcd_y, int *lcd_pressure)
{
	int ts_x, ts_y, ts_pressure;
	int tmp_x, tmp_y;
//添加压力值参数	
	ts_read_raw(&ts_x, &ts_y, &ts_pressure);

	if (g_ts_xy_swap)
	{
		swap_xy(&ts_x, &ts_y);
	}

	/* 使用公式计算 */
	tmp_x = g_kx * (ts_x - g_ts_xc) + g_lcd_xc;
	tmp_y = g_ky * (ts_y - g_ts_yc) + g_lcd_yc;

//如果值超出了LCD范围返回-1
	if (tmp_x < 0 || tmp_x >= xres || tmp_y < 0 || tmp_y >= yres)
		return -1;
	
	*lcd_x = tmp_x;
	*lcd_y = tmp_y;
//压力值等于全局变量ts_pressure
	*lcd_pressure = ts_pressure;
	return 0;
}


void ts_read_raw(int *px, int *py, int *ppressure)
{
	while (g_ts_data_valid == 0);
	*px = g_ts_x;
	*py = g_ts_y;
	*ppressure = g_ts_pressure;
	g_ts_data_valid = 0;
}


//我们需要report上报压力值数据
void report_ts_xy(int x, int y, int pressure)
{
	//printf("x = %08d, y = %08d\n\r", x, y);

	if (g_ts_data_valid == 0)
	{
		g_ts_x = x;
		g_ts_y = y;
		g_ts_pressure = pressure;
		g_ts_data_valid = 1;
	}
}


void Isr_Tc(void)
{
	//printf("ADCUPDN = 0x%x, ADCDAT0 = 0x%x, ADCDAT1 = 0x%x, ADCTSC = 0x%x\n\r", ADCUPDN, ADCDAT0, ADCDAT1, ADCTSC);
	
	if (ADCDAT0 & (1<<15))
	{
		//printf("pen up\n\r");

		enter_wait_pen_down_mode();
//如果松开就上报数据xy坐标00 压力值0
		report_ts_xy(0, 0, 0);
		
	}
	else	
	{
		//printf("pen down\n\r");

		/* 进入"自动测量"模式 */
		enter_auto_measure_mode();

		/* 启动ADC */
		ADCCON |= (1<<0);
	}
}

void Isr_Adc(void)
{
	int x = ADCDAT0;
	int y = ADCDAT1;

	static int adc_cnt = 0;
	static int adc_x = 0;
	static int adc_y = 0;

	if (!(x & (1<<15))) /* 如果仍然按下才打印 */
	{
#if 0		
		x &= 0x3ff;
		y &= 0x3ff;
		
		//printf("x = %08d, y = %08d\n\r", x, y);
//当我得到触电数据以后,如果当前仍是按下状态,会上报XY坐标值并且上报压力值,压力值等于1
		report_ts_xy(x, y, 1);

		/* 启动定时器以再次读取数据 */
		ts_timer_enable();
#endif
//防止数据在最后出现很大的误差
		/* 第1次启动ADC后:
		 *   a. 要连续启动N次, 获得N个数据, 求平均值并上报
		 *   b. 得到N次数据后, 再启动TIMER 
		 */
//我们直接累加
		adc_x += (x & 0x3ff);
		adc_y += (y & 0x3ff);
		adc_cnt++;
//我们取16的话右移4位比较容易操作
		if (adc_cnt == 16)
		{
//右移4位
			adc_x >>= 4;
			adc_y >>= 4;
//上报
			report_ts_xy(adc_x, adc_y, 1);

//恢复到初始值0
			adc_cnt = 0;
			adc_x = 0;
			adc_y = 0;
			
			/* 启动定时器以再次读取数据 */
			ts_timer_enable();
		}
		else
		{
			/* 否则再次启动ADC */
			/* 进入"自动测量"模式 */
			enter_auto_measure_mode();
			
			/* 启动ADC */
			ADCCON |= (1<<0);
		}
		
	}
	else
	{
		adc_cnt = 0;
		adc_x = 0;
		adc_y = 0;

		ts_timer_disable();
		enter_wait_pen_down_mode();
//如果数据转换完之前再次松开,这里我也会上报数据,0,0,0
		report_ts_xy(0, 0, 0);
	}

	enter_wait_pen_up_mode();
}

那么我们的tslib.c

void get_calibrate_point_data(int lcd_x, int lcd_y, int *px, int *py)
{
	int pressure;
	int x, y;
	
	fb_disp_cross(lcd_x, lcd_y, 0xffffff);

	/* 等待点击 */

	do {
//读取ts坐标值也加上压力值
		ts_read_raw(&x, &y, &pressure); 
//直到压力等于0的时候才返回
	} while (pressure == 0);


	do {
		*px = x;
		*py = y;
//压力值存在的情况下表示按下状态,就一直读取数据
		ts_read_raw(&x, &y, &pressure); 
		printf("get raw data: x = %08d, y = %08d\n\r", x, y);
	} while (pressure);
//打印坐标值
	printf("return raw data: x = %08d, y = %08d\n\r", *px, *py);

	/* 直到松开才返回 */
//我们操作完成后消除 +  
	fb_disp_cross(lcd_x, lcd_y, 0);
}

//我们确定校准为什么不对,我们校准涉及 原始数据 校准公式

//我们把公式使用函数来表示
int get_lcd_x_frm_ts_x(int ts_x)
{
	return g_kx * (ts_x - g_ts_xc) + g_lcd_xc;
}

int get_lcd_y_frm_ts_y(int ts_y)
{
	return g_ky * (ts_y - g_ts_yc) + g_lcd_yc;
}


void ts_calibrate(void)
{

	int a_ts_x, a_ts_y;
	int b_ts_x, b_ts_y;
	int c_ts_x, c_ts_y;
	int d_ts_x, d_ts_y;
	int e_ts_x, e_ts_y;

	/* X轴方向 */
	int ts_s1, ts_s2;
	int lcd_s;

	/* Y轴方向 */
	int ts_d1, ts_d2;
	int lcd_d;

	/* 获得LCD的参数: fb_base, xres, yres, bpp */
	get_lcd_params(&fb_base, &xres, &yres, &bpp);

	/* 对于ABCDE, 循环: 显示"+"、点击、读ts原始值 */
	/* A(50, 50) */
	get_calibrate_point_data(50, 50, &a_ts_x, &a_ts_y);	

	/* B(xres-50, 50) */
	get_calibrate_point_data(xres-50, 50, &b_ts_x, &b_ts_y);

	/* C(xres-50, yres-50) */
	get_calibrate_point_data(xres-50, yres-50, &c_ts_x, &c_ts_y);

	/* D(50, yres-50) */
	get_calibrate_point_data(50, yres-50, &d_ts_x, &d_ts_y);
	
	/* E(xres/2, yres/2) */
	get_calibrate_point_data(xres/2, yres/2, &e_ts_x, &e_ts_y);

	/* 确定触摸屏数据XY是否反转 */
	g_ts_xy_swap = is_ts_xy_swap(a_ts_x, a_ts_y, b_ts_x, b_ts_y);

	if (g_ts_xy_swap)
	{
		/* 对调所有点的XY坐标 */
		swap_xy(&a_ts_x, &a_ts_y);
		swap_xy(&b_ts_x, &b_ts_y);
		swap_xy(&c_ts_x, &c_ts_y);
		swap_xy(&d_ts_x, &d_ts_y);
		swap_xy(&e_ts_x, &e_ts_y);
	}

	/* 确定公式的参数并保存 */
	ts_s1 = b_ts_x - a_ts_x;
	ts_s2 = c_ts_x - d_ts_x;
	lcd_s = xres-50 - 50;

	ts_d1 = d_ts_y - a_ts_y;
	ts_d2 = c_ts_y - b_ts_y;
	lcd_d = yres-50-50;

	g_kx = ((double)(2*lcd_s)) / (ts_s1 + ts_s2);
	g_ky = ((double)(2*lcd_d)) / (ts_d1 + ts_d2);

	g_ts_xc = e_ts_x;
	g_ts_yc = e_ts_y;

	g_lcd_xc = xres/2;
	g_lcd_yc = yres/2;
//打印ABCDE的坐标值
	printf("A lcd_x = %08d, lcd_y = %08d\n\r", get_lcd_x_frm_ts_x(a_ts_x), get_lcd_y_frm_ts_y(a_ts_y));
	printf("B lcd_x = %08d, lcd_y = %08d\n\r", get_lcd_x_frm_ts_x(b_ts_x), get_lcd_y_frm_ts_y(b_ts_y));
	printf("C lcd_x = %08d, lcd_y = %08d\n\r", get_lcd_x_frm_ts_x(c_ts_x), get_lcd_y_frm_ts_y(c_ts_y));
	printf("D lcd_x = %08d, lcd_y = %08d\n\r", get_lcd_x_frm_ts_x(d_ts_x), get_lcd_y_frm_ts_y(d_ts_y));
	printf("E lcd_x = %08d, lcd_y = %08d\n\r", get_lcd_x_frm_ts_x(e_ts_x), get_lcd_y_frm_ts_y(e_ts_y));
}
  • 我们转换出的XY坐标值不是特别稳定
/* 每10ms该函数被调用一次 
 */
void touchscreen_timer_irq(void)
{
	/* 如果触摸屏仍被按下, 进入"自动测量模式", 启动ADC */
	if (get_status_of_ts_timer() == 0)
		return;

	if (ADCDAT0 & (1<<15)) /* 如果松开 */
	{
		ts_timer_disable();
		enter_wait_pen_down_mode();
		//report_ts_xy(0, 0, 0);
		return;
	}
	else  /* 按下状态 */
	{
		/* 进入"自动测量"模式 */
		enter_auto_measure_mode();

		/* 启动ADC */
		ADCCON |= (1<<0);
	}
}
  • 修改touchscreen-test.c文件
void touchscreen_test(void)
{
	unsigned int fb_base;
	int xres, yres, bpp;

	int x, y, pressure;

	/* 获得LCD的参数: fb_base, xres, yres, bpp */
	get_lcd_params(&fb_base, &xres, &yres, &bpp);

	touchscreen_init();

	/* 清屏 */
	clear_screen(0);

	/* 显示文字提示较准 */
	fb_print_string(70, 70, "Touc cross to calibrate touchscreen", 0xffffff);
	ts_calibrate();

	/* 显示文字提示绘画 */
	fb_print_string(70, yres - 70, "OK! To draw!", 0xffffff);

	while (1)
	{
//如果结果=0则继续执行下面操作
		if (ts_read(&x, &y, &pressure) == 0)
		{
			printf(" x = %d, y = %d\n\r", x, y);
//如果是按下状态,才会描点
			if (pressure)
			{

				fb_put_pixel(x, y, 0xff00);
			}
		}
	}
}

把LCD的电压值,成功转化成屏幕的坐标 要点

  1. 对于触摸屏要多次测量,求平均值
  2. 要丢弃非法值(以LCD分辨率作为判断标准)
  3. 校准时一定要点准

参考tslib库,

「新品首发」STM32MP157开发板火爆预售!首批仅300套

相关推荐

点过的网页会变色?没错,这玩意把你的浏览记录漏光了

提起隐私泄露这事儿,托尼其实早就麻了。。。平时网购、换手机号、注册各种账号之类的都会咔咔泄露,根本就防不住。但托尼真是没想到,浏览器里会有一个看起来完全人畜无害的功能,也在偷偷泄露我们的个人隐私,而且...

Axure教程:高保真数据可视化原型

本文将介绍如何制作Axure高保真数据可视化原型,供大家参考和学习。高保真数据可视化原型设计,称得上是Axure高阶水平。数据可视化在原型设计中是一个重要的分支,但是对于Axure使用者具有一定要求。...

Flutter web开发中禁用浏览器后退按钮

路由采用的go-router路由框架:finalrootNavigatorKey=GlobalKey<NavigatorState>();finalGoRouterrouter...

jQuery 控制属性和样式

标记的属性each()遍历元素:each(callback)方法主要用于对选择器进行遍历,它接受一个函数为参数,该函数接受一个参数,指代元素的序号。对于标记的属性而言,可以利用each()方法配合th...

微信小程序入门教程之二:页面样式

这个系列的上一篇教程,教大家写了一个最简单的Helloworld微信小程序。但是,那只是一个裸页面,并不好看。今天接着往下讲,如何为这个页面添加样式,使它看上去更美观,教大家写出实际可以使用的页...

如何在Windows11的任务栏中禁用和删除天气小部件图标?

微软该公司已在Windows11的任务栏中添加了一个天气小部件图标,作为小部件的入口点。这个功能与之前Win10上的新闻与资讯功能相同,但是有的用户不喜欢想要关闭,不知道如何操作,下面小编为大家带来...

CSS伪类选择器大全:提升网页交互与样式的神奇工具

CSS伪类选择器是前端开发中不可或缺的强大工具,它们允许我们根据元素的状态、位置或用户行为动态地应用样式。本文将全面介绍常用的伪类选择器,并通过代码示例展示其实际应用场景。一、基础交互伪类1.超链接...

7个Axure使用小技巧

编辑导读:对于Axure原型工具,很少有产品经过系统学习,一般都是直接上手,边摸索边学习,这直接导致很多快捷操作被忽视。笔者在日常工作中总结出以下小技巧,希望对各位有帮助。之前整理了2期Axure的...

JavaScript黑暗技巧:禁止浏览器点击“后退”按钮

浏览网页时,当从A页面点击跳转到B页面后,一般情况下,可以点击浏览器上的“后退”按钮返回A页面。如果进入B页面后,B页面想让访问者留下,禁止返回,是否可以实现呢?这简直是要控制浏览器的行为,虽然有些邪...

对齐PyTorch,一文详解OneFlow的DataLoader实现

撰文|赵露阳在最新的OneFlowv0.5.0版本中,我们增加了许多新特性,比如:新增动态图特性:OneFlow默认以动态图模式(eager)运行,与静态图模式(graph)相比,更容易搭建网...

Python计算机视觉编程 第一章 基本的图像操作和处理

以下是使用Python进行基本图像操作和处理的示例代码:使用PIL库加载图像:fromPILimportImageimage=Image.open("image.jpg"...

PyTorch 深度学习实战(31):可解释性AI与特征可视化

在上一篇文章中,我们探讨了模型压缩与量化部署技术。本文将深入可解释性AI与特征可视化领域,揭示深度学习模型的决策机制,帮助开发者理解和解释模型的内部工作原理。一、可解释性AI基础1.核心概念特征重要...

学习编程第177天 python编程 富文本框text控件的使用

今天学习的是刘金玉老师零基础Python教程第72期,主要内容是python编程富文本框text控件。一、知识点1.tag_config方法:利用某个别名作为标签,具体的对应标签的属性功能配置在后面参...

用Python讓電腦攝像頭實現掃二維碼

importsys#系統模組,用來存取命令列參數與系統功能importcv2#OpenCV,處理影像與相機操作importnumpyasnp#Numpy,用來處理數值與...

使用Transformer来做物体检测

作者:JacobBriones编译:ronghuaiyang导读这是一个Facebook的目标检测Transformer(DETR)的完整指南。介绍DEtectionTRansformer(D...