一、ADC和触摸屏硬件介绍及使用
S3C2410 ADC和触摸屏接口概述
S3C2410的CMOS模数转换器(ADC,Analog to Digital Converter)可以接收8个通道的模拟信号输入,并将它们转换为10位的二进制数据。在2.5MHz的A/D转换时钟下,最大的转化速率可达500KSPS(SPS:每秒采样次数)。
S3C2410/S3C2440都提供触摸屏的接口,不过它们有所不同。S3C2410的触摸屏接口向外提供4个控制信号引脚(nYPON,YMON,nXPON,XMON)和2个模拟信号输入引脚(AIN[7],AIN[5]),这6个引脚通过4个晶体管与触摸屏的4个引脚相连。而S3C2440提供了与触摸屏直接相连的4个引脚,不需要外接晶体管。
S3C2410 ADC和触摸屏接口有如下特征
- 分辨率:10位
- 微分线性度误差:±1.0LSB
- 积分线性度误差:±2.0LSB
- 最大转换速率:500KSPS
- 低功耗
- 供电电压:3.3V
- 输出模拟电压范围:0~3.3V
- 片上采样保持功能(On-chip sample-and-hold function).
- 普通转换模式
- 分离的x/y轴坐标转换模式
- 自动(连续)x/y轴坐标转换模式
- 等待中断模式
ADC和触摸屏接口结构图
上图为S3C2410,下图为S3C2440触摸屏

其中,芯片中只有一个A/D转换器,可以通过设置寄存器来选择对那路模拟信号进行采样。图中有两个中断信号:INT_ADC,INT_TC,前者表示A/D转换器已经转换完毕,后者表示触摸屏被按下了。
对于S3C2410,在使用触摸屏时,AIN[7]和AIN[5]被用来测量XP,YP的电平。只剩下AIN[6],AIN[4:0]工6个引脚用于一般得ADC输入。对于S3C2440,在使用触摸屏时,引脚XP,YP,XM,YM被用于和触摸屏直接相连,只剩下AIN[3:0]共4个引脚用于一般得ADC输入;当不使用触摸屏时,XP,XM,YP,YM这4个引脚也可以用于一般ADC输入。
S3C2410与触摸屏得连接比S3C2440复杂,需要增加几个外接晶体管。
2 使用ADC
2.1 ADC寄存器
ADCCON寄存器 主要使用的功能如下
/*
[15] ECFLG 、 1 表示转换结束
[14] PRSCEN 预分频使能
[13:6] PRSCVL 预分频值 adcclk = pclk / (pscvl + 1)
[5:3] SEL_MUX 多路选择器 000 AIN 0
[2] STDBM 0 连续转换模式 1 单次转换模式
[0] ENABLE_START 1 启动ADC转换
*/


2.2 ADCDAT0 寄存器
在仅使用ADC时,我们只需要读取其中数据即可。

2.3 电压值与ADCDAT0中的值如何转换?

可以理解为,调节该电阻的阻值,所输出的电压不同,电压最大为3.3V,即对应0X3FF(在ADCDAT0中,只有低10位表示数据)
volt = (double)(val * 3.3) / 1023;
即可计算出对应电压。
2.4 代码编写
并没有使用中断,而是通过ADCCON的第15位来观测ADC转换是否结束。
adc.c
#include "../s3c2440_soc.h"
#include "adc.h"
void adc_init(void)
{
// Initialize ADC
/*
[15] ECFLG 、 1 表示转换结束
[14] PRSCEN 预分频使能
[13:6] PRSCVL 预分频值 adcclk = pclk / (pscvl + 1)
[5:3] SEL_MUX 多路选择器 000 AIN 0
[2] STDBM 0 连续转换模式 1 单次转换模式
[0] ENABLE_START 1 启动ADC转换
*/
ADCCON = (1 << 14) | (49 << 6) | (0 << 3);
ADCDLY = 0xff; // 延时 0 微秒
}
int adc_read_ain0(void)
{
// 启动ADC
ADCCON |= (1 << 0);
while (!(ADCCON & (1 << 15)))
;
return (ADCDAT0 & 0x3ff); // 10 位 ADC 数据
}
测试程序adc_test.c
#include "adc.h"
void adc_test(void)
{
int val;
double volt;
int m; // 整数部分
int n; // 小数部分
adc_init();
while (1)
{
val = adc_read_ain0();
volt = (double)(val * 3.3) / 1023;
m = (int)val;
val = val - m;
n = val * 1000;
// 串口打印
printf("ADC Value: %d.%03d V\r\n", m, n);
// lcd打印
}
}
3 触摸屏
电阻触摸屏原理
触摸屏已经在现实生活中大量使用,比如银行的自动存/取款机等。触摸屏的种类有很多,比如超声波触摸屏,红外触摸屏,电容触摸屏,电阻触摸屏等。电阻触摸屏由于造价低廉,在电气上可以直接接入用户的系统中而得到大量使用。电阻触摸屏有几种类型,比如“四线”,“八线”线越多,精度也就越高,温度偏移也越少,但基本的操作都是一样的。它本质上是一个电阻分压器,将矩形区域中触摸点(X,Y)的物理位置转换为代表x坐标和y坐标的电压。
S3C2410的触摸屏接口可以驱动四线电阻触摸屏,四线电阻触摸屏的等效电路如下所示

当没有点击屏幕时,如右图所示:S4S5闭合,S1S2S3断开,即YM接地,XP上拉。
Y_ADC为高电平,当点击屏幕后,XY由于按压而导通,Y_ADC连接到地而变为低电平,此时这个信号可以作为DOWN信号,CPU发生“PEN Down"事件,称为等待中断模式。
采样X_ADC电压,得到x坐标,等效电路如14.6:
S4,S5断开,即XP接上电源、XM接地、YP作为模拟输入(对CPU而言)、YM高阻,XP禁止上拉。这时,YP即X_ADC就是x轴的分压点,进行A/D转换后就得到x坐标
采样Y_ADC电压,得到y坐标,等效电路如上图
S2,S4闭合,S1,S3,S5断开,即YP接上电源,YM接地,XP作为模拟信号输入,XM高阻,XP禁止上拉。这时,XP即Y_ADC就是y轴的分压点,进行A/D转换后就得到y坐标。

S3C2410触摸屏接口
与上面描述的触摸屏工作过程3个步骤对应,触摸屏控制器也有4个工作模式。
等待中断模式
设置ADCTSC寄存器为0xD3即可令触摸屏控制器处于这种模式。这时,它在等待触摸屏被按下。当触摸屏被按下时,触摸屏控制器将发出INT_TC中断信号,这时触摸屏控制器要转入以下两种工作模式中的一种,以读取x,y坐标。
对于S3C2410,当触摸屏被按下(Pen DOWN)或松开(Pen Up)时,都产生INT_TC中断信号。
对于S3C2440,可以设置ADCTSC寄存器的位[8]为0或1时,表示等待Pen Down中断或Pen Up中断。
分离的x/y轴坐标转换模式
这分别对应上述触摸屏工作过程的第2,3步骤。设置ADCTSC寄存器为0x69进入x轴坐标转换模式,x坐标值转换完毕后被写入ADCDAT0,然后发出INT_ADC中断;相似地,设置ADCTSC寄存器为0x9A进入y轴坐标转换模式,y坐标值转换完毕后被写入ADCDAT1,然后发出INT_ADC中断。
自动(连续)x/y轴坐标转换模式
上述触摸屏工作过程的第2,3步骤可以合成一个步骤,设置ADCTSC寄存器为0x0C,进入自动(连续)x/y轴坐标转换模式,触摸屏控制器就会自动转换接触点x,y坐标值,并分别写入ADCDAT0,ADCDAT1寄存器中,然后发出INT_ADC中断。
普通转换模式
不使用触摸屏时,触摸屏控制器处于这种模式。在这种模式下,可以通过设置ADCCON寄存器启动普通A/D转换,转换完成时数据被写入ADCDAT0寄存器中
4:触摸屏寄存器


注:处于等待中断模式时,XP_SEN必须处于1;PULL_UP设为0
AUTO_PST设为1时,必须处于自动X/Y模式下。
4.2 代码逻辑
首先设置为 希望点击状态,(等待中断)。
一旦点击屏幕,就会进入中断ADCTS_IRQHandler,并判断是 触摸屏中断 还是 ADC中断
如果是触摸屏中断,跳转Isr_TC(); 打印up 或 down 。
如果是down状态,启动自动测量。进入中断函数ADCTS_IRQHandler,此时为ADC中断
在ADC中断中 打印XY时,
4.3 代码编写
这个代码主要实现的功能是 点击屏幕 打印”pen down“,抬起笔 打印 ”pen up “
void touchcream_init(void)
{
// 设置触摸屏接口寄存器
adc_ts_reg_init();
// printf("ADCUPDN = 0x%x SUBSRCPND = 0x%x , SRCPND = 0x%x\n\r", ADCUPDN, SUBSRCPND, SRCPND);
// 设置中断
adc_ts_init();
// 让触摸屏控制器进入等待中断模式
enter_wait_pen_down_mode();
}
// 设置触摸屏接口寄存器
adc_ts_reg_init();
void adc_ts_reg_init()
{
// Initialize ADC
/*
[15] ECFLG 1 表示转换结束
[14] PRSCEN 预分频使能
[13:6] PRSCVL 预分频值 adcclk = pclk / (pscvl + 1)
[5:3] SEL_MUX 多路选择器 000 AIN 0
[2] STDBM 0 连续转换模式 1 单次转换模式
[0] ENABLE_START 1 启动ADC转换
*/
ADCCON = (1 << 14) | (49 << 6) | (0 << 3);
// 按下触摸屏,延时一会儿 发出TC中断
// 延时 = ADCDLY * 晶振周期 = adcdly * 1 /12000000 =10MS
ADCDLY = 120000; // 延时 0 微秒
}
// 设置中断
adc_ts_init();
void adc_ts_init(void)
{
// 注册中断处理函数
INTMSK &= ~(1 << 31);
ADCTS_IRQHandler();
// 使能中断
INTSUBMSK &= ~((1 << ADC_INT_BIT) | (1 << TC_INT_BIT));
// INTMSK &= ~(1<<INT_ADC_TC);
}
中断处理函数
void ADCTS_IRQHandler(int irq)
{
if (SUBSRCPND & (1 << 9)) // 如果是触摸屏中断, 调用ISRTC
Isr_TC();
if (SUBSRCPND & (1 << 10)) // 如果是ADC中断
ISR_A();
//清除中断标志位
SUBSRCPND = ((1 << 9) | (1 << 10));
}
触摸屏中断函数编写,这里我们主要是检测 笔点击和抬起来的动作
void Isr_TC(void)
{
if (ADCDAT0 & (1 << 15))
{
//当我的笔抬起来时,希望下一次检测 按下中断
printf("pen up\n\r");
enter_wait_pen_down_mode();
}
else
{
printf("pen down\n\r");
//当我的笔点击时,希望下一次检测 抬起 中断
enter_wait_pen_UP_mode();
}
}
下面代码的作用是
在ADCTSC寄存器第八位,我们可以设置 希望 检测什么类型的中断
点击中断 和 抬起中断
我们分别设计两个函数
结合上述代码 实现 //当我的笔抬起来时,希望下一次检测 按下中断 //当我的笔点击时,希望下一次检测 抬起 中断 这个功能。
void enter_wait_pen_down_mode(void)
{
ADCTSC = WAIT_PEN_DOWM | 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;
}
4.4 打印行列坐标
什么类型的中断可以打印 行列坐标
ADC中断下,ADCDAT0[9:0]表示行坐标 ADCDAT1【9:0】表示列坐标。
那么当我们检测到笔 点击屏幕时候,也就是在打印”pen down“ ”pen up “,时,我们可以在进入pen down时,开启自动测量XY模式,并启动ADC,这时硬件会自动将XY坐标值写入寄存器,我们只需要读取数据即可
ISR_TC代码修改:
void Isr_TC(void)
{
if (ADCDAT0 & (1 << 15))
{
//当我的笔抬起来时,希望下一次检测 按下中断
printf("pen up\n\r");
enter_wait_pen_down_mode();
}
else
{
printf("pen down\n\r");
// enter_wait_pen_UP_mode();
// 进入自动测量模式
enter_auto_measure_mode();
// 启动ADC
ADCCON |= (1 << 0);
}
}
自动测量 寄存器位修改
void enter_auto_measure_mode(void)
{
ADCTSC = AUTO_PST | NO_OPR_MODE;
}
当产生了ADC中断时,进入我们之前编写的void ADCTS_IRQHandler(int irq)函数中
进入ISR_A函数,打印XY坐标,并设置 希望 检测 笔抬起中断。
void ISR_A(void)
{
int x = ADCDAT0 & 0x3ff;
int y = ADCDAT1 & 0x3ff;
if (!(ADCDAT0 & (1 << 15)))
printf("x = %08d, y = %08d\n\r", x, y);
enter_wait_pen_UP_mode();
}
4.5 代码所定义的类型
#define ADC_INT_BIT (10)
#define TC_INT_BIT (9)
#define INT_ADC_TC (31)
// ADCTSC
#define WAIT_PEN_DOWM (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)
问题
在上述代码中,实现了点击屏幕打印所点击的XY坐标,如果我们一直点击一个点不妨,那么它也会只点击一次,无法实现画线操作。
实现 长点击打印XY
在上述代码中,我们是的逻辑是:
首先设置为 希望点击状态,(等待中断)。
一旦点击屏幕,就会进入中断ADCTS_IRQHandler,并判断是 触摸屏中断 还是 ADC中断
如果是触摸屏中断,跳转Isr_TC(); 打印up 或 down 。
如果是down状态,启动自动测量。进入中断函数ADCTS_IRQHandler,此时为ADC中断
在ADC中断中 打印XY时,如果此时我们开启定时器中断10ms,那么会进入到定时器中断中touchscreen_timer_irq,并在此时检测 笔的状态。
如果此时笔抬起状态,我们则 关闭定时器,并希望下一次为点击状态。 如果笔为 点击状态,那么启动ADC转换XY。 直到笔抬起。
此处为实现上述逻辑的全部代码。
#include "../s3c2440_soc.h"
#define ADC_INT_BIT (10)
#define TC_INT_BIT (9)
#define INT_ADC_TC (31)
// ADCTSC
#define WAIT_PEN_DOWM (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;
void enter_wait_pen_down_mode(void)
{
ADCTSC = WAIT_PEN_DOWM | 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;
}
void Isr_TC(void)
{
if (ADCDAT0 & (1 << 15))
{
// 当我的笔抬起来时,希望下一次检测 按下中断
printf("pen up\n\r");
enter_wait_pen_down_mode();
}
else
{
printf("pen down\n\r");
// enter_wait_pen_UP_mode();
// 进入自动测量模式
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;
}
// 每10ms该函数被调用一次
void touchscreen_timer_irq(void)
{
// 如果触摸屏任被按下,启动自动测量模式 启动AADC
if (get_status_of_ts_timer() == 0)
return;
if (ADCDAT0 & (1 << 15)) // 马上松开
{
ts_timer_disable();
enter_wait_pen_down_mode();
return;
}
else // 按下启动下一次测量
{
// 进入自动测量模式
enter_auto_measure_mode();
// 启动ADC
ADCCON |= (1 << 0);
}
}
void ISR_A(void)
{
int x = ADCDAT0;
int y = ADCDAT1;
if (!(ADCDAT0 & (1 << 15)))
{
x &= 0x3FF;
y &= 0x3FF;
printf("x = %08d, y = %08d\n\r", x, y);
// 启动定时器 以再次读取数据
ts_timer_enable();
}
else
{
ts_timer_disable();
enter_wait_pen_down_mode();
}
enter_wait_pen_UP_mode();
}
void ADCTS_IRQHandler(int irq)
{
if (SUBSRCPND & (1 << TC_INT_BIT)) // 如果是触摸屏中断, 调用ISRTC
Isr_TC();
if (SUBSRCPND & (1 << ADC_INT_BIT)) // 如果是ADC中断
ISR_A();
SUBSRCPND = ((1 << TC_INT_BIT) | (1 << ADC_INT_BIT));
}
void adc_ts_init(void)
{
// 注册中断处理函数
register_irq(31, ADCTS_IRQHandler);
// 使能中断
INTSUBMSK &= ~((1 << ADC_INT_BIT) | (1 << TC_INT_BIT));
// INTMSK &= ~(1<<INT_ADC_TC);
}
void adc_ts_reg_init()
{
// Initialize ADC
/*
[15] ECFLG 1 表示转换结束
[14] PRSCEN 预分频使能
[13:6] PRSCVL 预分频值 adcclk = pclk / (pscvl + 1)
[5:3] SEL_MUX 多路选择器 000 AIN 0
[2] STDBM 0 连续转换模式 1 单次转换模式
[0] ENABLE_START 1 启动ADC转换
*/
ADCCON = (1 << 14) | (49 << 6) | (0 << 3);
// 按下触摸屏,延时一会儿 发出TC中断
// 延时 = ADCDLY * 晶振周期 = adcdly * 1 /12000000 =10MS
ADCDLY = 120000; // 延时 0 微秒
}
void touchcream_init(void)
{
// 设置触摸屏接口寄存器
adc_ts_reg_init();
// printf("ADCUPDN = 0x%x SUBSRCPND = 0x%x , SRCPND = 0x%x\n\r", ADCUPDN, SUBSRCPND, SRCPND);
// 设置中断
adc_ts_init();
// 注册定时器处理函数
register_timer("touchscreen", touchscreen_timer_irq);
// 让触摸屏控制器进入等待中断模式
enter_wait_pen_down_mode();
}



6009

被折叠的 条评论
为什么被折叠?



