龙空技术网

ADC触摸屏编程_完善

韦东山嵌入式Linux 120

前言:

现在大家对“linux触摸屏校准”都比较关怀,大家都想要学习一些“linux触摸屏校准”的相关资讯。那么小编在网络上汇集了一些关于“linux触摸屏校准””的相关内容,希望兄弟们能喜欢,我们一起来了解一下吧!

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

作者:韦东山

本文字数:2725,阅读时长:3.5分钟

第018课 ADC和触摸屏 第012节_触摸屏编程_完善

触摸屏编程_完善我们触摸屏校准虽然可以正常运行,但是有些问题,比如在触摸屏上点一个点,同时屏幕上面会显示另一个点,我们按住屏幕不动的同时将其转换成LCD坐标并且描点,就表明数值不大稳定

问题

我们第一次点击触摸屏会出现两个点长按,LCD上的点会越来越大根源在于我们得到的LCD坐标值不稳定,根源ADC转换出来的xy坐标值不稳定

我们打开touchscreen.c问题出现在这里面

#include "../s3c2440_soc.h"#define ADC_INT_BIT (10)#define TC_INT_BIT  (9)#define INT_ADC_TC   (31)/* ADCTSC's bits */#define WAIT_PEN_DOWN    (0<<8)#define WAIT_PEN_UP      (1<<8)#define YM_ENABLE        (1<<7)#define YM_DISABLE       (0<<7)#define YP_ENABLE        (0<<6)#define YP_DISABLE       (1<<6)#define XM_ENABLE        (1<<5)#define XM_DISABLE       (0<<5)#define XP_ENABLE        (0<<4)#define XP_DISABLE       (1<<4)#define PULLUP_ENABLE    (0<<3)#define PULLUP_DISABLE   (1<<3)#define AUTO_PST         (1<<2)#define WAIT_INT_MODE    (3)#define NO_OPR_MODE      (0)static volatile int g_ts_timer_enable = 0;static int g_ts_x;static int g_ts_y;static int g_ts_pressure;static int g_ts_data_valid = 0;//定义测试数据16static int test_x_array[16];static int test_y_array[16];void report_ts_xy(int x, int y, int pressure);void enter_wait_pen_down_mode(void){	ADCTSC = WAIT_PEN_DOWN | PULLUP_ENABLE | YM_ENABLE | YP_DISABLE | XP_DISABLE | XM_DISABLE | WAIT_INT_MODE;}void enter_wait_pen_up_mode(void){	ADCTSC = WAIT_PEN_UP | PULLUP_ENABLE | YM_ENABLE | YP_DISABLE | XP_DISABLE | XM_DISABLE | WAIT_INT_MODE;}void enter_auto_measure_mode(void){	ADCTSC = AUTO_PST | NO_OPR_MODE;}int is_in_auto_mode(void){	return ADCTSC & AUTO_PST;}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))	{//按下状态启用触摸屏//启动测量模式,转换结束产生adc中断		//printf("pen up\n\r");		enter_wait_pen_down_mode();		report_ts_xy(0, 0, 0);			}	else		{		//printf("pen down\n\r");		/* 进入"自动测量"模式 */		enter_auto_measure_mode();		/* 启动ADC */		ADCCON |= (1<<0);	}}static void ts_timer_enable(void){	g_ts_timer_enable = 1;}static void ts_timer_disable(void){	g_ts_timer_enable = 0;}static int get_status_of_ts_timer(void){	return g_ts_timer_enable;}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 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;}/* 每10ms该函数被调用一次  */void touchscreen_timer_irq(void){	/* 如果触摸屏仍被按下, 进入"自动测量模式", 启动ADC */	if (get_status_of_ts_timer() == 0)		return;	if (is_in_auto_mode())		return;	/* 只有在"等待中断模式"下才可以使用ADCDAT0'BIT 15来判断触摸笔状态 */	if (ADCDAT0 & (1<<15)) /* 如果松开 */	{		printf("timer set pen down\n\r");		ts_timer_disable();		enter_wait_pen_down_mode();		report_ts_xy(0, 0, 0);		return;	}	else  /* 按下状态 */	{		/* 进入"自动测量"模式 */		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;	/* 进入ADC中断时, TS处于"自动测量模式" */	/* 只有在"等待中断模式"下才可以使用ADCDAT0'BIT 15来判断触摸笔状态 */	enter_wait_pen_up_mode();	if (!(ADCDAT0 & (1<<15))) /* 如果仍然按下才打印 */	{#if 0				x &= 0x3ff;		y &= 0x3ff;				//printf("x =   %08d, y = %08d\n\r", x, y);		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);//定义一个函数把这些值打印出来		test_x_array[adc_cnt] = (x & 0x3ff);		test_y_array[adc_cnt] = (y & 0x3ff);				adc_cnt++;		if (adc_cnt == 16)		{			adc_x >>= 4;			adc_y >>= 4;			report_ts_xy(adc_x, adc_y, 1);			adc_cnt = 0;			adc_x = 0;			adc_y = 0;						/* 启动定时器以再次读取数据 */			/* 先设置TS进入"等待中断模式" *///有按下就会有松开			enter_wait_pen_up_mode();			ts_timer_enable();		}		else		{			/* 再次启动ADC */			/* 进入"自动测量"模式 */			enter_auto_measure_mode();						/* 启动ADC */			ADCCON |= (1<<0);		}			}	else	{		adc_cnt = 0;		adc_x = 0;		adc_y = 0;		printf("adc report pen down\n\r");		ts_timer_disable();		enter_wait_pen_down_mode();		report_ts_xy(0, 0, 0);	}	//enter_wait_pen_up_mode();  /* 启动ADC时不应该进入这个模式, 它会影响数据 */}void AdcTsIntHandle(int irq){	if (SUBSRCPND & (1<<TC_INT_BIT))  /* 如果是触摸屏中断 */		Isr_Tc();	if (SUBSRCPND & (1<<ADC_INT_BIT))  /* ADC中断 */		Isr_Adc();	SUBSRCPND = (1<<TC_INT_BIT) | (1<<ADC_INT_BIT);}void adc_ts_int_init(void){	SUBSRCPND = (1<<TC_INT_BIT) | (1<<ADC_INT_BIT);	/* 注册中断处理函数 */	register_irq(31, AdcTsIntHandle);		/* 使能中断 */	INTSUBMSK &= ~((1<<ADC_INT_BIT) | (1<<TC_INT_BIT));	//INTMSK    &= ~(1<<INT_ADC_TC);}void adc_ts_reg_init(void){	/* [15] : ECFLG,  1 = End of A/D conversion	 * [14] : PRSCEN, 1 = A/D converter prescaler enable	 * [13:6]: PRSCVL, adc clk = PCLK / (PRSCVL + 1)	 * [5:3] : SEL_MUX, 000 = AIN 0	 * [2]   : STDBM	 * [0]   : 1 = A/D conversion starts and this bit is cleared after the startup.	 */	ADCCON = (1<<14) | (49<<6) | (0<<3);	/*  按下触摸屏, 延时一会再发出TC中断	 *  延时时间 = ADCDLY * 晶振周期 = ADCDLY * 1 / 12000000 = 5ms	 */	ADCDLY = 60000;	}void touchscreen_init(void){	/* 设置触摸屏接口:寄存器 */	adc_ts_reg_init();	printf("ADCUPDN = 0x%x, SUBSRCPND = 0x%x, SRCPND = 0x%x\n\r", ADCUPDN, SUBSRCPND, SRCPND);	/* 设置中断 */	adc_ts_int_init();	/* 注册定时器处理函数 */	register_timer("touchscreen", touchscreen_timer_irq);	/* 让触摸屏控制器进入"等待中断模式" */	enter_wait_pen_down_mode();}//打印数组定义的那些值void print_test_array(void){	int i;	printf("test array x : ");	for (i = 0; i < 16; i++)		printf("%08d ", test_x_array[i]);	printf("\n\r");	printf("test array y : ");	for (i = 0; i < 16; i++)		printf("%08d ", test_y_array[i]);	printf("\n\r");}void ts_read_raw_test(int *px, int *py, int *ppressure){	while (g_ts_data_valid == 0);	*px = g_ts_x;	*py = g_ts_y;	*ppressure = g_ts_pressure;	print_test_array();	g_ts_data_valid = 0;}

tslib.c使用了ts_read_raw函数

void get_calibrate_point_data(int lcd_x, int lcd_y, int *px, int *py){	int pressure;	int x, y;	int sum_x = 0, sum_y = 0;	int cnt = 0;		fb_disp_cross(lcd_x, lcd_y, 0xffffff);	/* 等待点击 */	do {		//ts_read_raw(&x, &y, &pressure); //我们使用测试程序去读这些值		ts_read_raw_test(&x, &y, &pressure);	} while (pressure == 0);	do {		if (cnt < 128)		{			sum_x += x;			sum_y += y;			cnt++;		}		//ts_read_raw(&x, &y, &pressure);		//ts_read_raw_test(&x, &y, &pressure);		printf("get raw data: x = %08d, y = %08d, cnt = %d\n\r", x, y, cnt);	} while (pressure);	*px = sum_x / cnt;	*py = sum_y / cnt;	printf("return raw data: x = %08d, y = %08d\n\r", *px, *py);	/* 直到松开才返回 */	fb_disp_cross(lcd_x, lcd_y, 0);}

我们来看点击一下是不是得到了距离非常远的两个值

对于同一个点得到的是255 945 945 944

发现 945 944经常出现

我们查一下原因,进入touchscreen.c中

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;	/* 进入ADC中断时, TS处于"自动测量模式" */	/* 只有在"等待中断模式"下才可以使用ADCDAT0'BIT 15来判断触摸笔状态 */	enter_wait_pen_up_mode();	if (!(ADCDAT0 & (1<<15))) /* 如果仍然按下才打印 */	{#if 0				x &= 0x3ff;		y &= 0x3ff;				//printf("x = %08d, y = %08d\n\r", x, y);		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);		test_x_array[adc_cnt] = (x & 0x3ff);		test_y_array[adc_cnt] = (y & 0x3ff);				adc_cnt++;		if (adc_cnt == 16)		{			adc_x >>= 4;			adc_y >>= 4;			report_ts_xy(adc_x, adc_y, 1);			adc_cnt = 0;			adc_x = 0;			adc_y = 0;						/* 启动定时器以再次读取数据 */			/* 先设置TS进入"等待中断模式" */			enter_wait_pen_up_mode();			ts_timer_enable();		}		else		{			/* 再次启动ADC */			/* 进入"自动测量"模式 */			enter_auto_measure_mode();						/* 启动ADC */			ADCCON |= (1<<0);		}			}	else	{		adc_cnt = 0;		adc_x = 0;		adc_y = 0;		printf("adc report pen down\n\r");		ts_timer_disable();		enter_wait_pen_down_mode();		report_ts_xy(0, 0, 0);	}//启动ADC后又再次进入enter_wait_pen_up_mode电阻上拉,多次一举	//enter_wait_pen_up_mode();  /* 启动ADC时不应该进入这个模式, 它会影响数据 */}

发现这些值中还有944,我需要继续查找原因, 在touchscreen.c时钟处理函数中添加打印信息

/* 每10ms该函数被调用一次  */void touchscreen_timer_irq(void){	/* 如果触摸屏仍被按下, 进入"自动测量模式", 启动ADC */	if (get_status_of_ts_timer() == 0)		return;	if (is_in_auto_mode())		return;	/* 只有在"等待中断模式"下才可以使用ADCDAT0'BIT 15来判断触摸笔状态 */	if (ADCDAT0 & (1<<15)) /* 如果松开 */	{//添加打印信息		printf("timer set pen down\n\r");		ts_timer_disable();//进入enter_wait_pen_down_mode,来判断触摸笔是按下还是松开		enter_wait_pen_down_mode();		report_ts_xy(0, 0, 0);		return;	}	else  /* 按下状态 */	{		/* 进入"自动测量"模式 */		enter_auto_measure_mode();		/* 启动ADC */		ADCCON |= (1<<0);	}}

非常频繁打印timer set pen down

我们的判断有问题

打开芯片手册,搜索这个寄存器,Bit15确实是判断按下或者松开

只有在中断模式下,这一位才可以正确反应是按下还是松开的状态,修改touchscreen_timer_irq函数

/* 每10ms该函数被调用一次  */void touchscreen_timer_irq(void){	/* 如果触摸屏仍被按下, 进入"自动测量模式", 启动ADC */	if (get_status_of_ts_timer() == 0)		return;//如果定时器中断在ADC中间产生,应该立刻返回啥都不做,不需要timer去做任何操作//我们需要写出这个函数//如果不是自动模式,那么就是等待中断模式	if (is_in_auto_mode())		return;//中断处理函数,	/* 只有在"等待中断模式"下才可以使用ADCDAT0'BIT 15来判断触摸笔状态 */	if (ADCDAT0 & (1<<15)) /* 如果松开 */	{		printf("timer set pen down\n\r");		ts_timer_disable();		enter_wait_pen_down_mode();		report_ts_xy(0, 0, 0);		return;	}	else  /* 按下状态 */	{		/* 进入"自动测量"模式 */		enter_auto_measure_mode();		/* 启动ADC */		ADCCON |= (1<<0);	}}//判断是否是模式模式的函数int is_in_auto_mode(void){	return ADCTSC & AUTO_PST;}

我们接着实验

发现并没有捕捉到笔的松开模式 , 修改tslib,取消ts_read_raw_test 重新进行测试.

void get_calibrate_point_data(int lcd_x, int lcd_y, int *px, int *py){	int pressure;	int x, y;	int sum_x = 0, sum_y = 0;	int cnt = 0;		fb_disp_cross(lcd_x, lcd_y, 0xffffff);	/* 等待点击 */	do {		ts_read_raw(&x, &y, &pressure); 		//ts_read_raw_test(&x, &y, &pressure);	} while (pressure == 0);	do {		if (cnt < 128)		{			sum_x += x;			sum_y += y;			cnt++;		}		ts_read_raw(&x, &y, &pressure); 		//ts_read_raw_test(&x, &y, &pressure);		printf("get raw data: x = %08d, y = %08d, cnt = %d\n\r", x, y, cnt);	} while (pressure);	*px = sum_x / cnt;	*py = sum_y / cnt;	printf("return raw data: x = %08d, y = %08d\n\r", *px, *py);	/* 直到松开才返回 */	fb_disp_cross(lcd_x, lcd_y, 0);}

现在发现点点不准确

发现校准的值和我们之前的不一样,修改我们的tslib校准程序

void get_calibrate_point_data(int lcd_x, int lcd_y, int *px, int *py){	int pressure;	int x, y;	int sum_x = 0, sum_y = 0;	int cnt = 0;		fb_disp_cross(lcd_x, lcd_y, 0xffffff);	/* 等待点击 */	do {		ts_read_raw(&x, &y, &pressure); 		//ts_read_raw_test(&x, &y, &pressure);	} while (pressure == 0);	do {//我们把求和的次数限制为128次		if (cnt < 128)		{			sum_x += x;			sum_y += y;			cnt++;		}		ts_read_raw(&x, &y, &pressure); 		//ts_read_raw_test(&x, &y, &pressure);		printf("get raw data: x = %08d, y = %08d, cnt = %d\n\r", x, y, cnt);	} while (pressure);	*px = sum_x / cnt;	*py = sum_y / cnt;	printf("return raw data: x = %08d, y = %08d\n\r", *px, *py);	/* 直到松开才返回 */	fb_disp_cross(lcd_x, lcd_y, 0);}

我们可以参考tslib

使用矩阵进行校准,适用性更强使用多种方法消除误差,多次测量求平均值

判断相邻点的距离,如果突然变化很大,就有可能是错误值

...

第一期的视频在于裸机基本操作

视频的要点在于

修改要点

启动ADC时不应该进入等待中断模式,它会影响数据只有在"等待中断模式"下才可以使用ADCDAT0'BIT 15来判断触摸笔状态校准非常重要,所以在程序中多次测量求平均值(不仅仅是在adc中断种求平均值)

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

标签: #linux触摸屏校准