2440上的触摸屏驱动移植

本文介绍了一个嵌入式Linux环境下针对特定开发板的ADC和触摸屏驱动的移植过程,包括所需源码的准备、驱动源码的部署、内核配置的修改以及驱动的测试验证。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

嵌入式Linux之我行,主要讲述和总结了本人在学习嵌入式linux中的每个步骤。一为总结经验,二希望能给想入门嵌入式Linux的朋友提供方便。如有错误之处,谢请指正。

一、移植环境

  • 主  机: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)
# define ADC_WRITE_GETPRE( data)     ( ( data) & 0xff)

# endif /* _S3C2410_ADC_H_ */


mini2440_adc.c

# include < linux/ errno . h>
# include < linux/ kernel. h>
# include < linux/ module. h>
# include < linux/ slab. h>
# include < linux/ input. h>
# include < linux/ init. h>
# include < linux/ serio. h>
# include < linux/ delay. h>
# include < linux/ clk. h>
# include < asm / io. h>
# include < asm / irq. h>
# include < asm / uaccess. h>
# include < mach/ regs- clock . h>
# include < plat/ regs- timer. h>
    
# include < plat/ regs- adc. h>
# include < mach/ regs- gpio. h>
# include < linux/ cdev. h>
# include < linux/ miscdevice. h>

# include "s3c24xx-adc.h"

# undef DEBUG
//#define DEBUG

# ifdef DEBUG
# define DPRINTK( x. . . ) { printk( __FUNCTION__ "(%d): " , __LINE__ ) ; printk( # # x) ; }
# else
# define DPRINTK( x. . . ) ( void ) ( 0)
# endif

# define DEVICE_NAME    "adc"

static void __iomem * base_addr;

typedef struct {
    wait_queue_head_t wait;
    int channel;
    int prescale;
} ADC_DEV;

DECLARE_MUTEX( ADC_LOCK) ;
static int OwnADC = 0;

static ADC_DEV adcdev;
static volatile int ev_adc = 0;
static int adc_data;

static struct clk    * adc_clock;

# define ADCCON ( * ( volatile unsigned long * ) ( base_addr + S3C2410_ADCCON) )     
//ADC control

# define ADCTSC ( * ( volatile unsigned long * ) ( base_addr + S3C2410_ADCTSC) )     
//ADC touch screen control

# define ADCDLY ( * ( volatile unsigned long * ) ( base_addr + S3C2410_ADCDLY) )     
//ADC start or Interval Delay

# define ADCDAT0 ( * ( volatile unsigned long * ) ( base_addr + S3C2410_ADCDAT0) )     
//ADC conversion data 0

# define ADCDAT1 ( * ( volatile unsigned long * ) ( base_addr + S3C2410_ADCDAT1) )     
//ADC conversion data 1

# define ADCUPDN ( * ( volatile unsigned long * ) ( base_addr + 0x14) )     
//Stylus Up/Down interrupt status

# define PRESCALE_DIS ( 0 < < 14)
# define PRESCALE_EN ( 1 < < 14)
# define PRSCVL( x) ( ( x) < < 6)
# define ADC_INPUT( x) ( ( x) < < 3)
# define ADC_START ( 1 < < 0)
# define ADC_ENDCVT ( 1 < < 15)

# define START_ADC_AIN( ch, prescale) /
    do { /
        ADCCON = PRESCALE_EN | PRSCVL( prescale) | ADC_INPUT( ( ch) ) ; /
        ADCCON | = ADC_START; /
    } while ( 0)

static irqreturn_t adcdone_int_handler( int irq, void * dev_id)
{
    if ( OwnADC) {
        adc_data = ADCDAT0 & 0x3ff;

        ev_adc = 1;
        wake_up_interruptible( & adcdev. wait) ;
    }

    return IRQ_HANDLED;
}

static ssize_t s3c2410_adc_read( struct file * filp, char * buffer, size_t count , loff_t * ppos)
{
    char str[ 20] ;
    int value;
    size_t len;
    if ( down_trylock( & ADC_LOCK) = = 0) {
        OwnADC = 1;
        START_ADC_AIN( adcdev. channel, adcdev. prescale) ;
        wait_event_interruptible( adcdev. wait, ev_adc) ;

        ev_adc = 0;

        DPRINTK( "AIN[%d] = 0x%04x, %d/n" , adcdev. channel, adc_data, ADCCON & 0x80 ? 1: 0) ;

        value = adc_data;
        sprintf ( str, "%5d" , adc_data) ;
        copy_to_user( buffer, ( char * ) & adc_data, sizeof ( adc_data) ) ;

        OwnADC = 0;
        up( & ADC_LOCK) ;
    } else {
        value = - 1;
    }

    len = sprintf ( str, "%d/n" , value) ;
    if ( count > = len) {
        int r = copy_to_user( buffer, str, len) ;
        return r ? r : len;
    } else {
        return - EINVAL;
    }
}

static int s3c2410_adc_open( struct inode * inode, struct file * filp)
{
    init_waitqueue_head( & ( adcdev. wait) ) ;

    adcdev. channel= 0;
    adcdev. prescale= 0xff;

    DPRINTK( "adc opened/n" ) ;
    return 0;
}

static int s3c2410_adc_release( struct inode * inode, struct file * filp)
{
    DPRINTK( "adc closed/n" ) ;
    return 0;
}

static struct file_operations dev_fops = {
    owner:     THIS_MODULE,
    open :     s3c2410_adc_open,
    read :     s3c2410_adc_read,     
    release:     s3c2410_adc_release,
} ;

static struct miscdevice misc = {
    . minor = MISC_DYNAMIC_MINOR,
    . name = DEVICE_NAME,
    . fops = & dev_fops,
} ;

static int __init dev_init( void )
{
    int ret;

    base_addr= ioremap( S3C2410_PA_ADC, 0x20) ;
    if ( base_addr = = NULL ) {
        printk( KERN_ERR "Failed to remap register block/n" ) ;
        return - ENOMEM;
    }

    adc_clock = clk_get( NULL , "adc" ) ;
    if ( ! adc_clock) {
        printk( KERN_ERR "failed to get adc clock source/n" ) ;
        return - ENOENT;
    }
    clk_enable( adc_clock) ;
    
    /* normal ADC */
    ADCTSC = 0;

    ret = request_irq( IRQ_ADC, adcdone_int_handler, IRQF_SHARED, DEVICE_NAME, & adcdev) ;
    if ( ret) {
        iounmap( base_addr) ;
        return ret;
    }

    ret = misc_register( & misc) ;

    printk ( DEVICE_NAME"/tinitialized/n" ) ;
    return ret;
}

static void __exit dev_exit( void )
{
    free_irq( IRQ_ADC, & adcdev) ;
    iounmap( base_addr) ;

    if ( adc_clock) {
        clk_disable( adc_clock) ;
        clk_put( adc_clock) ;
        adc_clock = NULL ;
    }

    misc_deregister( & misc) ;
}

EXPORT_SYMBOL( ADC_LOCK) ;
module_init( dev_init) ;
module_exit( dev_exit) ;
MODULE_LICENSE( "GPL" ) ;
MODULE_AUTHOR( "FriendlyARM Inc." ) ;

s3c2410_ts.c

# include < linux/ errno . h>
# include < linux/ kernel. h>
# include < linux/ module. h>
# include < linux/ slab. h>
# include < linux/ input. h>
# include < linux/ init. h>
# include < linux/ serio. h>
# include < linux/ delay. h>
# include < linux/ platform_device. h>
# include < linux/ clk. h>
# include < asm / io. h>
# include < asm / irq. h>

# include < plat/ regs- adc. h>
# include < mach/ regs- gpio. h>

/* For ts.dev.id.version */
# define S3C2410TSVERSION    0x0101

# define WAIT4INT( x) ( ( ( x) < < 8) | /
         S3C2410_ADCTSC_YM_SEN | S3C2410_ADCTSC_YP_SEN | S3C2410_ADCTSC_XP_SEN | /
         S3C2410_ADCTSC_XY_PST( 3) )

# define AUTOPST     ( S3C2410_ADCTSC_YM_SEN | S3C2410_ADCTSC_YP_SEN | S3C2410_ADCTSC_XP_SEN | /
         S3C2410_ADCTSC_AUTO_PST | S3C2410_ADCTSC_XY_PST( 0) )

static char * s3c2410ts_name = "s3c2410 TouchScreen" ;

static     struct input_dev * dev;
static     long xp;
static     long yp;
static     int count ;

extern struct semaphore ADC_LOCK;
static int OwnADC = 0;

static void __iomem * base_addr;

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) ;
}

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) ) ;

     if ( updown) {
         if ( count ! = 0) {
            long tmp;
                                                                                                 
            tmp = xp;
            xp = yp;
            yp = tmp;
                                                                                                 
            xp > > = 2;
            yp > > = 2;

# ifdef CONFIG_TOUCHSCREEN_MY2440_DEBUG
            struct timeval tv;
            do_gettimeofday( & tv) ;
            printk( KERN_DEBUG "T: %06d, X: %03ld, Y: %03ld/n" , ( int ) tv. tv_usec, xp, yp) ;
# endif

             input_report_abs( dev, ABS_X, xp) ;
             input_report_abs( dev, ABS_Y, yp) ;

             input_report_key( dev, BTN_TOUCH, 1) ;
             input_report_abs( dev, ABS_PRESSURE, 1) ;
             input_sync( dev) ;
         }

         xp = 0;
         yp = 0;
         count = 0;

         iowrite32( S3C2410_ADCTSC_PULL_UP_DISABLE | AUTOPST, base_addr+ S3C2410_ADCTSC) ;
         iowrite32( ioread32( base_addr+ S3C2410_ADCCON) | S3C2410_ADCCON_ENABLE_START, base_addr+ S3C2410_ADCCON) ;
     } else {
         count = 0;

         input_report_key( dev, BTN_TOUCH, 0) ;
         input_report_abs( dev, ABS_PRESSURE, 0) ;
         input_sync( dev) ;

         iowrite32( WAIT4INT( 0) , base_addr+ S3C2410_ADCTSC) ;
        if ( OwnADC) {
            OwnADC = 0;
            up( & ADC_LOCK) ;
        }
     }
}

static struct timer_list touch_timer =
        TIMER_INITIALIZER( touch_timer_fire, 0, 0) ;

static irqreturn_t stylus_updown( int irq, void * dev_id)
{
    unsigned long data0;
    unsigned long data1;
    int updown;

    if ( down_trylock( & ADC_LOCK) = = 0) {
        OwnADC = 1;
        data0 = ioread32( base_addr+ S3C2410_ADCDAT0) ;
        data1 = ioread32( base_addr+ S3C2410_ADCDAT1) ;

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

        if ( updown) {
            touch_timer_fire( 0) ;
        } else {
            OwnADC = 0;
            up( & ADC_LOCK) ;
        }
    }

    return IRQ_HANDLED;
}

static irqreturn_t stylus_action( int irq, void * dev_id)
{
    unsigned long data0;
    unsigned long data1;

    if ( OwnADC) {
        data0 = ioread32( base_addr+ S3C2410_ADCDAT0) ;
        data1 = ioread32( base_addr+ S3C2410_ADCDAT1) ;

        xp + = data0 & S3C2410_ADCDAT0_XPDATA_MASK;
        yp + = data1 & S3C2410_ADCDAT1_YPDATA_MASK;
        count + + ;

     if ( count < ( 1< < 2) ) {
            iowrite32( S3C2410_ADCTSC_PULL_UP_DISABLE | AUTOPST, base_addr+ S3C2410_ADCTSC) ;
            iowrite32( ioread32( base_addr+ S3C2410_ADCCON) | S3C2410_ADCCON_ENABLE_START, base_addr+ S3C2410_ADCCON) ;
        } else {
            mod_timer( & touch_timer, jiffies+ 1) ;
            iowrite32( WAIT4INT( 1) , base_addr+ S3C2410_ADCTSC) ;
        }
    }

    return IRQ_HANDLED;
}

static struct clk    * adc_clock;

static int __init s3c2410ts_init( void )
{
    struct input_dev * input_dev;

    adc_clock = clk_get( NULL , "adc" ) ;
    if ( ! adc_clock) {
        printk( KERN_ERR "failed to get adc clock source/n" ) ;
        return - ENOENT;
    }
    clk_enable( adc_clock) ;

    base_addr= ioremap( S3C2410_PA_ADC, 0x20) ;
    if ( base_addr = = NULL ) {
        printk( KERN_ERR "Failed to remap register block/n" ) ;
        return - ENOMEM;
    }

    /* Configure GPIOs */
    s3c2410_ts_connect( ) ;

    iowrite32( S3C2410_ADCCON_PRSCEN | S3C2410_ADCCON_PRSCVL( 0xFF) , /
         base_addr+ S3C2410_ADCCON) ;
    iowrite32( 0xffff, base_addr+ S3C2410_ADCDLY) ;
    iowrite32( WAIT4INT( 0) , base_addr+ S3C2410_ADCTSC) ;

    /* Initialise input stuff */
    input_dev = input_allocate_device( ) ;

    if ( ! input_dev) {
        printk( KERN_ERR "Unable to allocate the input device !!/n" ) ;
        return - ENOMEM;
    }

    dev = input_dev;
    dev- > evbit[ 0] = BIT( EV_SYN) | BIT( EV_KEY) | BIT( EV_ABS) ;
    dev- > keybit[ BITS_TO_LONGS( BTN_TOUCH) ] = BIT( BTN_TOUCH) ;
    input_set_abs_params( dev, ABS_X, 0, 0x3FF, 0, 0) ;
    input_set_abs_params( dev, ABS_Y, 0, 0x3FF, 0, 0) ;
    input_set_abs_params( dev, ABS_PRESSURE, 0, 1, 0, 0) ;

    dev- > name = s3c2410ts_name;
    dev- > id. bustype = BUS_RS232;
    dev- > id. vendor = 0xDEAD;
    dev- > id. product = 0xBEEF;
    dev- > id. version = S3C2410TSVERSION;

    /* Get irqs */
    if ( request_irq( IRQ_ADC, stylus_action, IRQF_SHARED| IRQF_SAMPLE_RANDOM,
        "s3c2410_action" , 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" , dev) ) {
        printk( KERN_ERR "s3c2410_ts.c: Could not allocate ts IRQ_TC !/n" ) ;
        iounmap( base_addr) ;
        return - EIO;
    }

    printk( KERN_INFO "%s successfully loaded/n" , s3c2410ts_name) ;

    /* All went ok, so register to the input system */
    input_register_device( dev) ;

    return 0;
}

static void __exit s3c2410ts_exit( void )
{
    disable_irq( IRQ_ADC) ;
    disable_irq( IRQ_TC) ;
    free_irq( IRQ_TC, dev) ;
    free_irq( IRQ_ADC, dev) ;

    if ( adc_clock) {
        clk_disable( adc_clock) ;
        clk_put( adc_clock) ;
        adc_clock = NULL ;
    }

    input_unregister_device( dev) ;
    iounmap( base_addr) ;
}

module_init( s3c2410ts_init) ;
module_exit( s3c2410ts_exit) ;

2. 部署驱动源代码到内核中

#cp -f s3c24xx-adc.h mini2440_adc.c /linux-2.6.30.4/drivers/char/

#cp -f s3c2410_ts.c /linux-2.6.30.4/drivers/input/touchscreen/

 

3. 将驱动模块添加到内核配置文件中
 

#gedit linux-2.6.30.4/drivers/char/Kconfig  //添加如下内容

config MY2440_ADC
    bool "ADC device for my2440"
    default y

 

#gedit linux-2.6.30.4/drivers/char/Makefile  //添加如下内容

obj-$(CONFIG_MY2440_ADC)  += mini2440_adc.o

#gedit linux-2.6.30.4/drivers/input/touchscreen/ Kconfig  //添加如下内容

config TOUCHSCREEN_MY2440
    tristate "MY2440 touchscreens input driver"
    default y
 
config TOUCHSCREEN_MY2440_DEBUG
    boolean "MY2440 touchscreens input driver debug messages"
    depends on TOUCHSCREEN_MY2440
    help
        Select this if you want debug messages

#gedit linux-2.6.30.4/drivers/input/touchscreen/Makefile   //添加如下内容

obj-$(CONFIG_TOUCHSCREEN_MY2440) += s3c2410_ts.o

4. 修改内核配置选项
 

Device Drivers --->
    Input device support --->
        (240) Horizontal screen resolution
        (320) Vertical screen resolution
        [*] Touchscreens --->
            <*> MY2440 touchscreens input driver (NEW)
            [*] MY2440 touchscreens input driver debug messages
    Character devices --->
        [*] ADC device for my2440 (NEW)


5. 编译内核并下载到开发板上,从启动信息可以看到触摸屏驱动加载成功

6. 创建触摸屏设备节点并测试。由启动信息得知触摸屏设备为输入类设备,如下图操作可以得知输入类设备的主设备号为13,又查看总线的具体输入设备可得知该设 备的详细信息,根据这些信息我们建立好设备节点,设置内核调试级别为8,然后打开触摸屏设备,点击触摸屏,此时可以看到打印的坐标信息,即触摸屏可正常使 用了

 

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值