ADC及触摸屏操作

一、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();
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值