来源:百问网_嵌入式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;
//定义测试数据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
- 使用矩阵进行校准,适用性更强
- 使用多种方法消除误差,多次测量求平均值
判断相邻点的距离,如果突然变化很大,就有可能是错误值
- ...
第一期的视频在于裸机基本操作
视频的要点在于
修改要点
- 启动ADC时不应该进入等待中断模式,它会影响数据
- 只有在"等待中断模式"下才可以使用ADCDAT0'BIT 15来判断触摸笔状态
- 校准非常重要,所以在程序中多次测量求平均值(不仅仅是在adc中断种求平均值)