【ARM裸板】S3C2440下的ADC操作和触摸屏的详解 && 根据寄存器编程编程

001 ADC硬件原理

硬件特性

ADC特性

s3c2440的ADC硬件原理图s3c2440的ADC硬件原理图

s3c2440的ADC框图

在这里插入图片描述

  • 怎么设置:
    • a.设置8:1mux,选中要测量哪个引脚
    • b.设置工作时钟
      [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FpI2iF6S-1581491700386)(en-resource://database/2384:1)]
    • c.启动
    • d.读状态,判断ADC成功
    • e.读数据

002 ADC编程

  • 目标:
  • a.初始化ADC
  • b.读数据
  • c.在串口上显示出来
  • d.在LCD上显示出来

设置ADC寄存器

ADCCON

ADCCON

ADCTSC

ADCTSC

ADCDLY

在这里插入图片描述
在这里插入图片描述

ADCDATA0

在这里插入图片描述

void adc_init(void)
{
	/* [15]  :ECFLG        ,1 = End of A/D conversion
	 * [14]  : PRSCEN      ,1 = A/D converter prescaler enable
	 * [13:6]: PRSCVL      ,adc clk = PCK / (PRSCVL + 1)
	 * [5:3] : SEL_MUX     ,000 = AIN 0
	 * [2]   : STDBM       ,1 = Standby mode
	 * [1]   : READ_ START ,0 = Disable start by read operation
	 * [0]   : ENABLE_START,1 = A/D conversion starts and this bit is cleared after the startup.
	*/
	ADCCON = (1<<14) | (49<<6) | (0<<3) ;

	ADCDLY = 0xff;
}

int adc_read_ain0(void)
{
	/* 启动adc */
	ADCCON |= (1<<0);
	
	while(ADCCON & (1<<15));	/* 等待ADC结束 */

	return ADCDAT0 & 0x3ff;
}

003 电阻触摸屏硬件原理

触摸屏示意图

在这里插入图片描述

测量触点X座标 测量触点Y座标
Xp接3.3v,Xm接地,Yp、Ym不接电源 Yp接3.3v,Ym接地,Xp、Xm不接电源
测Yp电压(就是X座标) 测Xp电压(就是Y座标)

使用触摸屏流程

  • 1.按下触摸屏,产生触摸中断
  • 2.在触摸屏中断程序中,启动ADC(目的:获得x,y座标)
  • 3.ADC完成,产生中断
  • 4.在ADC中断中,读取x,y座标
  • 5.启动定时器
  • 6.定时器中断发生,判断触摸屏仍被按下,回到第2步。
  • 7.松开,结束。

003 S3C2440触摸屏接口

编程要点

ADC转化中断时,可以通过中断或者查询来得到数据

  • 使用中断模式时,从AD转换开始到得到数据可能有些延迟,因为中断的进入和退出需要浪费一些时间
  • 如果像要立刻得到数据,可以使用ADC的查询方式,查询ADCCON[15],判断转化是否结束 。

ADCTSC设置与触摸屏等效电路图

在这里插入图片描述

  • 触摸屏等待按下接口程序
void enter_wait_pen_dowm_mode(void)
{
	ADCTSC = PULLUP_ENABLE | YM_ENABLE | YP_DISABLE | XP_DISABLE | XP_DISABLE | WAIT_INT_MODE ;
}

ADC/TS与中断的控制流程

在这里插入图片描述

怎么编程

  • 1.初始化ADC/TS接口
  • 2.设置TS处于等到中断
  • 3.设置中断
  • 4.按下,进去TS中断
    • a.进入自动采集模式
    • b.启动ADC
  • 5.ADC中断
    • a.读数据
    • b.再次进入”等待中断模式“
    • c.启动定时器
  • 6.定时器中断
    • a.若松开,结束
    • b.若按下,进入4.b

005触摸屏编程_按下松开检测

  • 状态转换图
    在这里插入图片描述
  • 注意:adc和触摸屏公用一个中断,利用SUBSRCPND中的位,判断是哪种中断

在这里插入图片描述

void AdcTsIntHandle(int irq)
{
	if(SUBSRCPND & (1<<ADC_INT_BIT))	/* 如果是触摸屏中断 */
		Tsr_Tc();
	if(SUBSRCPND & (1<<TC_INT_BIT))	/* ADC中断 */
		Isr_Adc();
}

判断按下松开

在这里插入图片描述

void Tsr_Tc(void)
{
	if(ADCUPDN & (1<<1))
		printf("pen up\n\r");
	if(ADCUPDN & (1<<0))
		printf("pen dowm\n\r");
}

006ADC中断

007定时器程序优化

在timer.c中,在中断服务函数中,应该让他从某个数组中,把需要定时器处理的函数依次执行。

  • 函数指针数组:往数组中写函数。
typedef void(*timer_func)(void);

typedef struct timer_desc{
	char *name;
	timer_func fp;
}timer_desc,*p_timer_desc;

timer_desc timer_array[TIMER_NUM];

int register_timer(char *name, timer_func fp)
{
	int i;
	for(i = 0; i < TIMER_NUM ; i++)
	{
		if(!timer_array[i].fp)
		{
			timer_array[i].name = name;
			timer_array[i].fp   = fp;
			return 0;
		}
	}
	return -1;
}

void register_timer(char *name)
{
	int i;
	for(i = 0; i < TIMER_NUM ; i++)
	{
		if(!strcmp(timer_array[i].name,name))
		{
			timer_array[i].name = NULL;
			timer_array[i].fp   = NULL;
			return 0;
		}
	}
	return -1; 
}

#define NULL ((void *)0)

009校准原理

  • 问题:得到触点的(x,y)怎么换算出LCD的(x,y)
  • 1.利用比例

在这里插入图片描述

  • 2.但是由于边界点不准,因此选点

在这里插入图片描述

  • 3.由于制作工艺不均匀,上下左右都有差异
    在这里插入图片描述

010校准与画线

程序思路:

    • 1.在A点显示“+”
fb_disp_cross(int x, int y)
  • 2.客户点击“+”
ts_read_raw()//读到原始数据
  • 3.记录触摸屏座标
  • 二在B、C、D、E、上循环操作:显示、点击、读取操作
  • 三、根据数据,确定公式
ts_calibrate()//构造公式
  • 四、以后得到TS触点时,可以转换出LCD座标
ts_read()//

  • 1.根据思路写出基本函数

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;


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

	/* 等待点击 */

	ts_read_raw(px,  py);
}

int is_ts_xy_swap(int a_ts_x, int a_ts_y, int b_ts_x, int b_ts_y)
{
	int dx = b_ts_x - a_ts_x;
	int dy = b_ts_y - b_ts_x;

	if(dx < 0)
		dx = 0 - dx;
	if(dy < 0)
		dy = 0 - dy;
	
	if(dx > dy)
		return 0;	/* xym没有反转 */
	else 
		return 1;	/* xy反了 */
}

void swap_xy(int *px, int *py)
{
	int tmp = *px;
	*px = *py;
	*py = tmp;
}

/*
   ---------------------------------
   |                               |
   |   +(A)                  +(B)  |
   |                               |
   |              +(E)             |
   |                               |
   |   +(C)                  +(D)  |
   |                               |
   ---------------------------------
*/

void ts_calibrate(void)
{
	unsigned int fb_base;
	int xres, yres, bpp;

	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的参数 */
	get_lcd_params(&fb_base,&xres,&yres,&bpp);
	
	/* 对于ABCDE,循环:显示“+"、点击、读ts原始值 */
	/* A(50, 50) */
	get_calibrate_point_data(50, 50, &a_ts_x, &a_ts_x);
	
	/* B(xres-50, 50) */
	get_calibrate_point_data(xres-50, 50, &b_ts_x, &b_ts_x);

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

	/* D(50, yres-50) */
	get_calibrate_point_data(50,  yres-50, &d_ts_x, &d_ts_x);

	/* E(xres/2, yres/2) */
	get_calibrate_point_data(xres/2, yres/2, &e_ts_x, &e_ts_x);

	/* 确定触摸屏数据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;
}
/*
 *读TS原始数据,转换为LCD座标
*/
void ts_read(int *lcd_x,int *lcd_y)
{
	int ts_x,ts_y;

	ts_read_raw(&ts_x, &ts_y);

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

	/* 使用公式计算 */
	*lcd_x = g_kx * (ts_x - g_ts_xc) + g_lcd_xc;
	*lcd_y = g_kx * (ts_y - g_ts_yc) + g_lcd_yc;
	
}



  • 2.第二个问题:触摸屏上报的x轴y轴相反,
int is_ts_xy_swap(int a_ts_x, int a_ts_y, int b_ts_x, int b_ts_y)
{
	int dx = b_ts_x - a_ts_x;
	int dy = b_ts_y - b_ts_x;

	if(dx < 0)
		dx = 0 - dx;
	if(dy < 0)
		dy = 0 - dy;
	
	if(dx > dy)
		return 0;	/* xym没有反转 */
	else 
		return 1;	/* xy反了 */
}


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

  • 3.第三个问题:如何对调XY的座标–对调函数
void swap_xy(int *px, int *py)
{
	int tmp = *px;
	*px = *py;
	*py = tmp;
}
/* 确定触摸屏数据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);
	}
    
  • 总结:
  • a.对于触摸屏要多次测量求平均值
  • b.要丢弃非法值(以lcd分辨率作为判断条件)
  • c.校准时一定要点准

012 编程完善

  • 问题:
  • 第一次点击触摸屏,会出现2个点
  • 长按,LCD上的点越来越大。

根源:使用ADC得到的座标值不稳定。

  • 方法
  • 参考tslib :
    • a.使用矩阵进行校准,适用性更强
    • b.使用多种方法消除误差:多次测量,判断相邻点的距离,如果突然变化很大,可能就是错误值。
  • 修改要点:
  • 1.启动ADC时不用改进入等待中断模式,它会影响数据
    1. 只有在”等待中断"模式下才可以使用ADCDATA0’bit15来判断触摸笔状态
  • 3.校准非常重要,多以在程序中多次测量求平均值你(不仅是在ADC中断中求平均值)。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章