分享

实验 A/D驱动程序编写与测试

 武溪嵌人 2012-03-28

实验 A/D驱动程序编写与测试

【实验内容】

编写一个字符设备驱动程序,驱动FS_S5PC100开发板上的可调电阻,采集AD转换过来的电压值

可调电阻位于开发板第二个串口(COM2)的正下方

【实验目的】

了解AD的工作原理,掌握驱动里操作AD控制器的方法,巩固字符设备驱动程序编写方法

【实验平台】

主机:Ubuntu 10.10
        目标板:FS_S5PC100
        目标内核版本:2.6.29
        交叉编译器版本:arm-unknown-linux-gnueabi-gcc-4.2.2

【实验步骤】

在主机ubuntu环境下:

1、将文件夹s5pc100_adc复制到linux环境中,如:/home/linux/test
        $ cp s5pc100_adc /home/linux/test –a
        2、$ cd /home/linux/test/s5pc100_adc
        3、$ make //编译设备驱动,生成adc.ko模块
        4、$ arm-linux-gcc adc_test.c -o adc_test //编译应用程序
        5、$ sudo cp adc.ko adc_test /source/rootfs

在开发板的串口终端控制台下:

6、通过insmod命令将模块加入内核

# insmod adc.ko //由于驱动采用的是动态获取主设备号的方式,所以根据打印信息建立设备结点
        major number of adc is 252

7、 创建设备结点

# mknod /dev/adc c 252 0

9、运行应用程序

# ./adc_test

调整可调电阻上的旋钮,发现输出值也在发生变化

val = 630
        val = 623
        val = 60b
        val = 5fb
        val = 913
        val = 925
        val = 916
        val = 911

10、修改应用程序,将直接从驱动获得的数字量转换为实际的电压值(可调电阻可以承受的电压范围为: 0 —— 3.3V)

附:

1、 驱动程序代码:

#include <linux/input.h>
        #include <linux/module.h>
        #include <linux/init.h>
        #include <linux/interrupt.h>
        #include <linux/fs.h>

#include <asm/uaccess.h> // copy_to_user, copy_from_user
        #include <asm/irq.h>
        #include <asm/io.h>
        #include <plat/irqs.h>
        #include <plat/regs-adc.h>

MODULE_AUTHOR("farsight");
        MODULE_LICENSE("Dual BSD/GPL");

#define ADC_BASE_ADDR 0xF3000000
        #define MAP_SIZE 36

static int adc_major = 0;
        static volatile void *adc_base;

#if 0
        static irqreturn_t ad_interrupt(int irq, void *dummy)
        {
                //input_report_key(ad_dev, BTN_0, inb(BUTTON_PORT) & 1);
                //input_sync(ad_dev);
                return IRQ_HANDLED;
        }
        #endif

static int adc_open(struct inode *inode, struct file *filp)
        {
                //map physical address to virtual address
                adc_base = ioremap(ADC_BASE_ADDR, MAP_SIZE);

                //ADC output resolution seletion : 12-bit A/D conversion
                writel(readl(adc_base+S3C_ADCCON) | S3C_ADCCON_RESSEL_12BIT, adc_base+S3C_ADCCON);
                //enable A/D converter prescale and set prescaler as 0xFF
                writel(readl(adc_base+S3C_ADCCON) | S3C2410_ADCCON_PRSCEN, adc_base+S3C_ADCCON);
                //set prescaler as 0xFF
                writel((readl(adc_base+S3C_ADCCON) & ~S3C2410_ADCCON_PRSCVLMASK) | S3C2410_ADCCON_PRSCVL(0xff), adc_base+S3C_ADCCON);
                //select normal operation mode
                writel(readl(adc_base+S3C_ADCCON) & ~S3C2410_ADCCON_STDBM, adc_base+S3C_ADCCON);

        //select channel as AIN0
                writel(readl(adc_base+S3C_ADCMUX) & ~S3C_ADCMUX_MASK, adc_base+S3C_ADCMUX);

#if 0
                if (request_irq(IRQ_ADC, ad_interrupt, 0, "adc", NULL)) {
                        printk(KERN_ERR "%s: Can't allocate irq %d\n", __FUNCTION__, IRQ_ADC);
                        return -EBUSY;
                }
        #endif

        //enable A/D conversion
                //writel(readl(adc_base+S3C_ADCCON) | S3C2410_ADCCON_ENABLE_START, adc_base+S3C_ADCCON);
                writel(readl(adc_base+S3C_ADCCON) | S3C_ADCCON_READ_START, adc_base+S3C_ADCCON);

                return 0;
        }

static int adc_close( struct inode *inode, struct file *filp )
        {
                //free_irq(IRQ_ADC, ad_interrupt);
                return 0;
        }

static ssize_t adc_read( struct file *filp, char __user *buffer, size_t count, loff_t *offset )
        {
                int val;

        //read A/D conversion data from ADCDAT0
                val = readl(adc_base+S3C_ADCDAT0) & 0xfff;
                printk(KERN_INFO "val = %x\n", val);

        if( copy_to_user(buffer, &val, sizeof(val)) != 0 ) {
                        printk( KERN_ERR "copy to user failed\n" );
                        return -1;
                }
                return sizeof(val);
        }

static struct file_operations adc_fops = {
                .owner = THIS_MODULE,
                .open = adc_open,
                .release = adc_close,
                .read = adc_read,
        };

static int __init adc_init( void )
        {
                int res;
                res = register_chrdev( adc_major, "adc", &adc_fops );
                if ( res < 0 ) {
                        printk( KERN_ERR "%s failed.\n", __FUNCTION__ );
                        return res;
                }
                else {
                        if ( adc_major )
                                printk ( KERN_INFO "major number of adc is %d\n", adc_major );
                        else {
                                adc_major = res;
                                printk ( KERN_INFO "major number of adc is %d\n", adc_major );
                        }
                }

        return 0;
        }

static void __exit adc_cleanup( void )
        {
                unregister_chrdev( adc_major, "adc" );
        }

module_init(adc_init);
        module_exit(adc_cleanup);

2、 测试程序代码

#include <stdio.h>
        #include <fcntl.h>

int
        main( int argc, char *argv[] )
        {
                int fd = open( "/dev/adc", O_RDWR );
                if( fd < 0 ) {
                        perror( "open /dev/adc error" );
                        return -1;
                }

                int nread = 0;
                int adc_val = 0;
                while( 1 ) {
                        nread = read( fd, (void *)&adc_val, sizeof(adc_val) );
                        if( nread < 0 ) {
                                perror( "read adc" );
                                return -1;
                        }
                        sleep( 1 );
                }

        close( fd );
                return 0;
        }

3、 AD转换接口原理图:


    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多