ADC触摸屏编程_完善_ab触摸屏


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

作者:韦东山

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

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

触摸屏编程_完善

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

问题

  1. 我们第一次点击触摸屏会出现两个点
  2. 长按,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;

//定义测试数据16
static 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

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

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

  1. ...

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

视频的要点在于

修改要点

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

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

原文链接:,转发请注明来源!