Chinaunix首页 | 论坛 | 博客
  • 博客访问: 32392
  • 博文数量: 12
  • 博客积分: 1400
  • 博客等级: 上尉
  • 技术积分: 130
  • 用 户 组: 普通用户
  • 注册时间: 2009-12-22 20:05
文章分类
文章存档

2010年(4)

2009年(8)

我的朋友

分类: 嵌入式

2010-01-09 13:57:37

一、开发平台

 机:VMWare--Fedora 8

开发板:utu2440--64MB Nand / linux-2.6.29.1

编译器:arm-linux-gcc-4.3.2

 

二、移植步骤

1.修改driver/char/mini2440_adc.c,

之前移植touchscreen驱动时是参考友善的方法,分析代码发现mini2440_adc.c中没有提供ioctl命令来设置adc输入通道,而utu2440上有两个adc测量通道,因此按具体情况将友善的adc驱动做相应修改, 修改后adc驱动代码如下:

/* mini2440_adc.c */

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

        

#include

#include

#include

#include

 

// #include "s3c24xx-adc.h" // driver/char/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"

#define ADC_CHANNEL 1

#define ADC_PRESCALE 2 // adjust prescale command, reserved for future use

 

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

len = sprintf(str, "%d", value); // 这个‘\n’在后面的测试程序中会显得多余

       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 int s3c2410_adc_ioctl(

       struct inode *inode,

       struct file *file,

       unsigned int cmd,

       unsigned long arg)

{

       switch(cmd) {

       case ADC_CHANNEL:

              adcdev.channel = (int)arg & 0x3;        

              break;

       case ADC_PRESCALE:

              adcdev.prescale = (int)arg & 0xff;

              break;

       default:

              return -EINVAL;

              break;    

       }

      

       return 0;

}

 

static struct file_operations dev_fops = {

       owner:    THIS_MODULE,

       open:      s3c2410_adc_open,

       read:       s3c2410_adc_read,

       release:    s3c2410_adc_release,

       ioctl:       s3c2410_adc_ioctl,

};

 

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

 

2. 内核配置选项不变,重新编译内核,并下载到开发板

 

3. adc测试应用程序如下

/* test_adc.c */

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

 

#define ADC_CHANNEL 1

#define ADC_PRESCALE 2 //reserved for future use

 

void usage(char *argv0)

{

       printf("Usage: %s [channel]\n", argv0);

       printf("channel: 0 - 1\n");

}

 

int main(int argc, char *argv[])

{

       int fd;

       int args;

       int n;

       float voltage;

       char buf[20];

 

       if (argc != 2)

       {

              usage(argv[0]);

              return -1;

       }    

 

       args = atoi(argv[1]);

       if (args != 0 && args != 1)

       {

              usage(argv[0]);

              return -1;

       }

 

       fd = open("/dev/adc", O_RDONLY);

       if (fd < 0)

       {

              printf("can't open adc\n");

             

              return -1;

       }

 

       if (ioctl(fd, ADC_CHANNEL, args))

       {

              printf("ioctl ADC_CHANNEL failed\n");

              return -1;

       }

 

       while (1)

       {

              memset(buf, 0, sizeof(buf));

              n = read(fd, buf, sizeof(buf));

              if (n > 0)

              {

                     voltage = (atoi(buf) * 3.3) / 1024;

                     printf("channel[%d]: %.3fV\n", args, voltage);

              }

              sleep(2);

       }

 

       close(fd);

       return 0;

}

 

启动开发板后测试adc,调整两个输入通道的电位器输出会在0V3.3V之间变化:

[root@liuzg]# ./test_adc

Usage: ./test_adc [channel]

channel: 0 - 1

[root@liuzg]# ./test_adc 0

channel[0]: 1.895V

channel[0]: 1.895V

channel[0]: 1.895V

channel[0]: 1.895V

channel[0]: 1.895V

^C

[root@liuzg]# ./test_adc 1

channel[1]: 3.294V

channel[1]: 3.297V

channel[1]: 3.297V

 

阅读(1252) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~