Linux驱动:s3c2410/s3c2440 ts驱动分析

发布者:shmilyde最新更新时间:2022-11-16 来源: zhihu关键字:Linux驱动  s3c2410  s3c2440 手机看文章 扫描二维码
随时随地手机看文章

前言

前面结合“平台总线-设备-驱动”模型分析了ts(触摸)驱动的加载过程,现在进入驱动程序分析下其具体的实现。涉及到输入子系统、s3c2440的ADC转换和触摸控制器

涉及的寄存器

image.png

调用probe函数

根据上一篇的分析,驱动层通过platform_driver_register注册后,会调用到该驱动层的probe函数。

s3c2410ts_probe函数分析

1. 硬件寄存器设置

获取设备参数

struct s3c2410_ts_mach_info *info;


    info = ( struct s3c2410_ts_mach_info *)pdev->dev.platform_data;


    /*

    info

        .delay = 10000,   // ADC conversion start delay value

        .presc = 49,      // ADC clk

        .oversampling_shift = 2, // 采样精度

    */


    if (!info)

    {

        printk(KERN_ERR "Hm... too bad : no platform data for tsn");

        return -EINVAL;

    }

使能ADC

adc_clock = clk_get(NULL, "adc");

if (!adc_clock) {

    printk(KERN_ERR "failed to get adc clock sourcen");

    return -ENOENT;

}

clk_enable(adc_clock);

获取ADC & TOUCH SCREEN 寄存器

static inline void s3c2410_ts_connect(void)

{

    s3c2410_gpio_cfgpin(S3C2410_GPG12, S3C2410_GPG12_XMON);

    s3c2410_gpio_cfgpin(S3C2410_GPG13, S3C2410_GPG13_nXPON);

    s3c2410_gpio_cfgpin(S3C2410_GPG14, S3C2410_GPG14_YMON);

    s3c2410_gpio_cfgpin(S3C2410_GPG15, S3C2410_GPG15_nYPON);

}


{

    .....

    base_addr=ioremap(S3C2410_PA_ADC,0x20);  //#define S3C2410_PA_ADC      (0x58000000)

    if (base_addr == NULL) {

        printk(KERN_ERR "Failed to remap register blockn");

        return -ENOMEM;

    }


    /* Configure GPIOs */

    s3c2410_ts_connect(); // 设置GPIO功能

    .....

}

设置ADC转换频率

if ((info->presc&0xff) > 0)

        iowrite32(S3C2410_ADCCON_PRSCEN | S3C2410_ADCCON_PRSCVL(info->presc&0xFF),

                 base_addr+S3C2410_ADCCON);    

// 设置ADC clk 

// A/D converter freq = PCLK/(info->presc+1)  

// 一次adc转换所需时间Conversion time = 1/(A/D converter freq / 5cycles)

设置ADC转换开始的延时时间

因为ADC中断产生后的一段时间内电压还未稳定,如果立即进行ADC转换,那么转换值会有误差,这时就可以通过设置延时时间,待电压稳定后再执行转换以确保数值的准确性。


// 设置ADC conversion start delay value

if ((info->delay&0xffff) > 0)

        iowrite32(info->delay & 0xffff,  base_addr+S3C2410_ADCDLY);

进入等待触摸按下模式

iowrite32(WAIT4INT(0), base_addr+S3C2410_ADCTSC);  // 等待触摸按下中断模式

自此,ADC & TOUCH SCREEN 寄存器初始设置完成。

2. 注册ADC和TC中断

注册TC中断,监测触摸屏的按下和抬起;注册ADC中断,进行ADC转换。


/* Get irqs */

if (request_irq(IRQ_ADC, stylus_action, IRQF_SAMPLE_RANDOM | SA_SHIRQ,

                "s3c2410_action", ts.dev)) {

    printk(KERN_ERR "s3c2410_ts.c: Could not allocate ts IRQ_ADC !n");

    iounmap(base_addr);

    return -EIO;

}

if (request_irq(IRQ_TC, stylus_updown, IRQF_SAMPLE_RANDOM,

                "s3c2410_action", ts.dev)) {

    printk(KERN_ERR "s3c2410_ts.c: Could not allocate ts IRQ_TC !n");

    iounmap(base_addr);

    return -EIO;

}

TC中断处理函数 stylus_updown

static irqreturn_t stylus_updown(int irq, void *dev_id)

{

    unsigned long data0;

    unsigned long data1;

    int updown;


    // 读取寄存器DATA0和DATA1获取x,y轴的ADC转换值

    data0 = ioread32(base_addr+S3C2410_ADCDAT0);

    data1 = ioread32(base_addr+S3C2410_ADCDAT1);


    // ADCDAT0 bit[15] 0 为按下,1 为松开 即updown 为true 则按下,为false 则松开

    updown = (!(data0 & S3C2410_ADCDAT0_UPDOWN)) && (!(data1 & S3C2410_ADCDAT0_UPDOWN));


    /* TODO we should never get an interrupt with updown set while

     * the timer is running, but maybe we ought to verify that the

     * timer isn't running anyways. */


    if (updown)

    {

        // 如果为按下状态

        touch_timer_fire(0);

    }


    return IRQ_HANDLED;

}

ADC中断处理函数 stylus_action

static irqreturn_t stylus_action(int irq, void *dev_id)

{

    unsigned long data0;

    unsigned long data1;


//  if (bADCForTS) {


        data0 = ioread32(base_addr+S3C2410_ADCDAT0);

        data1 = ioread32(base_addr+S3C2410_ADCDAT1);


        ts.xp += data0 & S3C2410_ADCDAT0_XPDATA_MASK;  // 为四次adc转换值的累加

        ts.yp += data1 & S3C2410_ADCDAT1_YPDATA_MASK;  // 为四次adc转换值的累加

        ts.count++;


//      bADCForTS = 0;

//      up(&gADClock);

        //  ts.count < 4 ,即四次adc转换值为一次按下的结果                             

        if (ts.count < (1<//              if (!down_trylock(&gADClock)) {

//                  bADCForTS = 1;

                    iowrite32(S3C2410_ADCTSC_PULL_UP_DISABLE | AUTOPST, base_addr+S3C2410_ADCTSC); // 进入 等待adc 转换模式 

                    iowrite32(ioread32(base_addr+S3C2410_ADCCON) | S3C2410_ADCCON_ENABLE_START, base_addr+S3C2410_ADCCON); // 开启adc转换,转换在info->delay后进行,转换完成后会产生一个ADC中断

//          }

        } else {


            mod_timer(&touch_timer, jiffies+1); // 启动一个定时器,一个jiffies(系统滴答时间) 后进入定时器处理函数 touch_timer_fire

            iowrite32(WAIT4INT(1), base_addr+S3C2410_ADCTSC); // 同时等待 触摸抬起中断

        }

//  }

    return IRQ_HANDLED;

}

定时器处理函数 touch_timer_fire

static struct timer_list touch_timer =

        TIMER_INITIALIZER(touch_timer_fire, 0, 0);


static void touch_timer_fire(unsigned long data)

{

    unsigned long data0;

    unsigned long data1;

    int updown;


    data0 = ioread32(base_addr+S3C2410_ADCDAT0);

    data1 = ioread32(base_addr+S3C2410_ADCDAT1);


    updown = (!(data0 & S3C2410_ADCDAT0_UPDOWN)) && (!(data1 & S3C2410_ADCDAT0_UPDOWN));

    // updown 为true 则按下,为false 则松开


    if (updown) {

        // 处于按下状态的处理


        // 首次按下 产生了四次ADC转换后,才去处理x,y值

        if (ts.count != 0) {

            long tmp;  


            // 横纵坐标转换

            tmp = ts.xp;

            ts.xp = ts.yp;

            ts.yp = tmp;


            ts.xp >>= ts.shift; // 四次adc转换值的平均值为一次按下的结果

            ts.yp >>= ts.shift; // 四次adc转换值的平均值为一次按下的结果


#ifdef CONFIG_TOUCHSCREEN_S3C2410_DEBUG

            {

                struct timeval tv;

                do_gettimeofday(&tv);

                printk(DEBUG_LVL "T: %06d, X: %03ld, Y: %03ldn", (int)tv.tv_usec, ts.xp, ts.yp);

            }

#endif


            input_report_abs(ts.dev, ABS_X, ts.xp);   // 上报x的坐标

            input_report_abs(ts.dev, ABS_Y, ts.yp);   // 上报y的坐标


            input_report_key(ts.dev, BTN_TOUCH, 1);     // 上报BIN_TOUCH 按下

            input_report_abs(ts.dev, ABS_PRESSURE, 1);  // 上报ABS_PRESSURE 按下

            input_sync(ts.dev);                         // 上报事件完成

        }


        ts.xp = 0;

        ts.yp = 0;

        ts.count = 0;


//      if (!down_trylock(&gADClock)) {

//          bADCForTS = 1;

        iowrite32(S3C2410_ADCTSC_PULL_UP_DISABLE | AUTOPST, base_addr+S3C2410_ADCTSC);  // 进入 等待adc 转换模式 

        iowrite32(ioread32(base_addr+S3C2410_ADCCON) | S3C2410_ADCCON_ENABLE_START, base_addr+S3C2410_ADCCON); // 开启adc转换,完成后会产生一个ADC中断

//      }

    } else {

        // 松开的处理


        ts.count = 0;


        input_report_key(ts.dev, BTN_TOUCH, 0);   // 上报BIN_TOUCH 松开

        input_report_abs(ts.dev, ABS_PRESSURE, 0);// 上报ABS_PRESSURE 松开

        input_sync(ts.dev);                       // 上报事件完成


        iowrite32(WAIT4INT(0), base_addr+S3C2410_ADCTSC);  // 进入等待触摸按下中断

    }

}

3. 注册到输入子系统

申请input_dev

input_dev = input_allocate_device();


if (!input_dev) {

    printk(KERN_ERR "Unable to allocate the input device !!n");

    return -ENOMEM;

}

设置input_dev

ts.dev = input_dev;

// 设置产生的事件类型,同步类事件、按键类事件和绝对位移事件

ts.dev->evbit[0] = BIT(EV_SYN) | BIT(EV_KEY) | BIT(EV_ABS);

// 设置某类事件类型中的具体事件

// 按键类事件中的触摸事件

ts.dev->keybit[LONG(BTN_TOUCH)] = BIT(BTN_TOUCH);

// 绝对位移类事件中的ABS_X、ABS_Y、压力值的值范围,0x3ff是该ADC转换器是10bit精度的,最大为0x3fff

input_set_abs_params(ts.dev, ABS_X, 0, 0x3FF, 0, 0);

input_set_abs_params(ts.dev, ABS_Y, 0, 0x3FF, 0, 0);

input_set_abs_params(ts.dev, ABS_PRESSURE, 0, 1, 0, 0); // 只设置有按下和抬起(0,1),没有具体的压力值


ts.dev->private = &ts;

ts.dev->name = s3c2410ts_name;

ts.dev->id.bustype = BUS_RS232;

ts.dev->id.vendor = 0xDEAD;

[1] [2]
关键字:Linux驱动  s3c2410  s3c2440 引用地址:Linux驱动:s3c2410/s3c2440 ts驱动分析

上一篇:Linux驱动:s3c2410/s3c2440 ts驱动分析 之二
下一篇:linux驱动:s3c2410_ts/s3c2440_ts模块加载流程

推荐阅读最新更新时间:2024-11-02 09:12

关于s3c2410的Watchdog
1. 看门狗原理 Watchdog原理上就是一个定时器。定时器timer对时钟进行计数,当定时器溢出时,产生复位信号,使得整个系统复位。在程序或嵌入式系统中,需要定期的对看门狗timer进行复位重新计数,定时器不会溢出复位系统,从而保证系统的正常运行。当某种原因(例如干扰)引起程序跑飞或者进入死循环时,程序不能定期的复位看门狗timer, 计数溢出产生复位信号,导致系统复位。 设本系统程序完整运行一周期的时间是T1,看门狗的定时周期为T2,T2 T1,在程序运行一周期后就修改定时器的计数值,只要程序正常运行,定时器就不会溢出,若因为程序“跑飞”或者进入死循环,那么系统不能在T1时刻修改时间的计数值,一直计数直到T2时刻溢出,引发
[单片机]
关于<font color='red'>s3c2410</font>的Watchdog
ARM Linux内核驱动异常定位方法分析反汇编方式
最近在搞Atmel 的SAM9x25平台,Linux系统,用于工业设备。这也是我首次参与工业设备的研发。在调试Atmel SAM9x25的Linux串口设备的时候,发现无论是读还是写,都会产生异常。相关的异常信息如下: ================================================================================================================== Unable to handle kernel NULL pointer dereference at virtual address 00000000 pgd = c0004000 *
[单片机]
基于ARM920T处理器核的图像采集系统设计
  引言   随着微电子技术和软件技术的发展,嵌入式技术也有了长足的进步。因此,基于嵌入式技术的图像数据采集与存储监控系统以其直观、方便、信息内容丰富的特点,广泛应用于许多场合。   处理器性能的提高,接口传输数据能力的增强,特别是未来高容量存储器的出现,图像监控系统的小型化、多功能化是易于实现的,嵌入式技术引入图像监控系统后,两个问题需要解决,一是灵活的图像监控系统结构调整,二是适合监控规范、集图像和信号检测与控制一体的交互式软件的设计。   1 系统平台的搭建   1.1 硬件平台的搭建   ARM(AdvancedRISCMachines),既可以认为是一个公司的名字,也可以认为是对一类微处理器的统称,还可以认为
[单片机]
基于ARM920T处理器核的图像采集系统设计
s3c2440串口接收中断(OK2440III)
#include uart2440.h #include def.h #include 2440addr.h #include led2440.h // 引入led #define UART_CLK 50000000 // UART0的时钟源设为PCLK #define UART_BRUD(baud) { rUBRDIV0 = ((UART_CLK / (baud * 16)) - 1) } void __irq uart_irq_recv(void) { uint8 buf_recv; uint8 buf = 接收中断发生...rn ; uart_send(buf); buf_recv
[单片机]
Linux-2.6.30.4在2440上的移植之触摸屏驱动
一、移植环境 主 机:VMWare--Fedora 9 开发板:Mini2440--64MB Nand 编译器:arm-linux-gcc-4.3.2 二、移植步骤 1. 准备驱动源码。因为linux-2.6.30.4内核中没有提供合适的ADC驱动和触摸屏驱动,所以这里就直接用友善提供的驱动 s3c24xx-adc.h #ifndef _S3C2410_ADC_H_ #define _S3C2410_ADC_H_ #define ADC_WRITE(ch, prescale) ((ch) 16|(prescale)) #define ADC_WRITE_GETCH(data) (((data) 16)&0x7)
[单片机]
<font color='red'>Linux</font>-2.6.30.4在2440上的移植之触摸屏<font color='red'>驱动</font>
S3C2440时钟体系及设置方法
这几天一直忙着研究移植U-boot,移植U-boot的过程中有一步很重要,就是要设置s3c2440的时钟,什么 Fin,Fclk,Hclk,Pclk,Mpll,Upll等时钟信号,让初学者看得一头雾水,各种信号混淆不清,本人特意花了一点时间,把这些东东理了理,现整理如下: 首先得弄清一个大的框架,s3c2440 cpu的默认工作主频有两种12MHz和16.9344MHz,也就是我们的晶振的频率,但一般12MHz的晶振用的比较多,Fin就是指我们接的晶振频率。大家都知道s3c2440上电正常工作后频率是远远大于12MHz和16.9344MHz的,我们的s3c2440的cpu正常工作时的频率就是405MHz,因此这就需要一个电路来
[单片机]
<font color='red'>S3C2440</font>时钟体系及设置方法
ARM处理器s3c2410的中断异常处理
    在进入正题之前,我想先把ARM920T的异常向量表(Exception Vectors)做一个简短的介绍。     ARM920T的异常向量表有两种存放方式,一种是低端存放(从0x00000000处开始存放),另一种是高端存放(从0xfff000000处开始存放)。关于为什么要分两种方式进行存放这点我将在介绍MMU的文章中进行说明,本文采用低端模式。ARM920T能处理有8个异常,他们分别是: Reset,Undefined instruction,Software Interrupt,Abort (prefetch),Abort (data),Reserved,IRQ,FIQ     下面是某个采用低
[嵌入式]
Linux设备驱动开发 - 混杂设备驱动
混杂设备共享一个主设备号MISC_MAJOR(10),次设备号不同。 混杂设备本质上仍然是一个字符设备,所以混杂设备的操作跟字符设备基本相同。 在字符设备中,描述字符设备的结构体为struct cdev,在混杂设备中同样有一个描述混杂设备的结构体struct miscdevice。 1 struct miscdevice { 2 int minor; /* 次设备号 */ 3 const char *name; /* 设备名 */ 4 const struct file_operations *fops; /* 文件操作 */ 5 struct list
[单片机]
<font color='red'>Linux</font>设备<font color='red'>驱动</font>开发 - 混杂设备<font color='red'>驱动</font>
小广播
设计资源 培训 开发板 精华推荐

最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

换一换 更多 相关热搜器件
随便看看

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved