Chinaunix首页 | 论坛 | 博客
  • 博客访问: 374918
  • 博文数量: 57
  • 博客积分: 2299
  • 博客等级: 大尉
  • 技术积分: 1109
  • 用 户 组: 普通用户
  • 注册时间: 2008-05-27 23:12
文章分类
文章存档

2011年(4)

2010年(53)

分类: 嵌入式

2010-03-07 15:18:17


/*
*File Name : devadc.c
*Function : based on arm linu s3c2440 adc driver
*Type : standard char driver
*Time : 2010-3-8
*Author:CQUPT-SSW_feiyang
*/

#include <linux/module.h>     /*EXPORT_SYMBOL_GPL ())*/
#include <linux/moduleparam.h> /*module_param(variable, type, perm); */
#include <linux/init.h>/*module_init(init_function); module_exit(cleanup_function); */
#include <linux/kdev_t.h>/*dev_t*/
#include <linux/fs.h>/* everything... */
#include <linux/cdev.h>/*struct cdev *cdev_alloc(void); */
#include <linux/device.h>/*struct class*/
#include <asm/uaccess.h>    /* copy_*_user access_ok*/
#include <asm-arm/plat-s3c/regs-adc.h>
#include <linux/wait.h>
#include <asm/semaphore.h>
#include <asm-arm/arch-s3c2410/map.h> /*ADCCON就是S3C2410_PA_ADC*/
#include<asm/io.h>/*ioemap*/
#include <asm/hardware/clock.h>
#include <linux/sched.h>/*request_irq()*/
#include <linux/interrupt.h>/*irqreturn_t*/
#include <asm/signal.h>
#include<linux/ioctl.h>
#include "devadc.h"

struct adc_dev adc_dev;
static int adc_major = 0,adc_minor = 0;
static struct class *adc_class;
static void __iomem *adc_addr;/* 物理地址映射成虚拟地址后的基地址指针 */
static struct clk *adc_clock; /* 时钟 */

static irqreturn_t adc_irq(int irq, void *dev_id, struct pt_regs *sirq)
{
    /*保证了应用程序读取一次这里就读取AD转换的值一次,
  避免应用程序读取一次后发生多次中断多次读取AD转换值*/

  if(!adc_dev.ev_adc)
  {
  /*读取AD转换后的值保存到全局变量adc_data中,S3C2410_ADCDAT0定义在regs-adc.h中,
    这里为什么要与上一个0x3ff,很简单,因为AD转换后的数据是保存在ADCDAT0的第0-9位,
    所以与上0x3ff(即:1111111111)后就得到第0-9位的数据,多余的位就都为0*/

   adc_dev.data = ioread32(adc_addr + S3C2410_ADCDAT0) & 0x3ff;
// PDEBUG("I Am Here\n");

    /*将可读标识为1,并唤醒等待队列*/
    adc_dev.ev_adc = 1;
    wake_up_interruptible(&adc_dev.adcq);
  }
  return IRQ_HANDLED;
}
static int adc_open(struct inode *inode, struct file *filp)
{
    int ret;

    init_MUTEX(&adc_dev.sem);
    init_waitqueue_head(&adc_dev.adcq);
    adc_dev.channel = 0xff;
    adc_dev.data = 0;

    ret = request_irq(IRQ_ADC, adc_irq, SA_INTERRUPT, DEV_NAME, NULL);//申请ADC中断

    if (ret)
    {
      /*错误处理*/
      printk(KERN_WARNING "IRQ %d error %d\n", IRQ_ADC, ret);
      return -EINVAL;
    }
    adc_addr = ioremap(S3C2410_PA_ADC, 0x20);
    if(adc_addr == NULL)
    {
      printk(KERN_WARNING"Iomaped fail!\n");
      goto maped_err;
    }
    adc_clock = clk_get(NULL, "adc");
    if (!adc_clock) {
      PDEBUG("failed to get adc clock source\n");
      goto clk_err;
    }
    clk_enable(adc_clock);
    PDEBUG( "adc opened\n");
    return 0;
    clk_err:
        clk_disable(adc_clock);
        clk_put(adc_clock);
     maped_err:
         iounmap(adc_addr);
     return -1;
}
/*设置ADC控制寄存器,开启AD转换*/
static void start_adc(void)
{
  unsigned int tmp;
  tmp = (1<<14) | (255<<6) | (adc_dev.channel&(0x0038));/*选择通道和设置时钟*/

  writel(tmp, adc_addr + S3C2410_ADCCON); /*AD预分频器使能、模拟输入通道设为AIN0*/
  
  tmp = readl(adc_addr + S3C2410_ADCCON);
  tmp = tmp | (1 << 0); /* 0 1 00000011 000 0 0 1 */
  writel(tmp, adc_addr + S3C2410_ADCCON); /*AD转换开始*/
 // PDEBUG("I Am Here\n tmp = %d\n",tmp);

}
static int adc_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg)
{
  int err = 0;
  /*拒绝错误命令*/
  if (_IOC_TYPE(cmd) != ADC_IOC_MAGIC)
    return -ENOTTY;
  if (_IOC_NR(cmd) > ADC_IOC_MAXNR)
    return -ENOTTY;
/*
  * the direction is a bitmask, and VERIFY_WRITE catches R/W
  * transfers. `Type' is user-oriented, while
  * access_ok is kernel-oriented, so the concept of "read" and
  * "write" is reversed
 */

  if (_IOC_DIR(cmd) & _IOC_READ)
    err = !access_ok(VERIFY_WRITE, (void __user *)arg, _IOC_SIZE(cmd));
  else if (_IOC_DIR(cmd) & _IOC_WRITE)
    err = !access_ok(VERIFY_READ, (void __user *)arg, _IOC_SIZE(cmd));
  if (err)
    return -EFAULT;
  switch(cmd)
  {
    case ADC_IOCSCHANNEL :
      switch(arg)
      {
        case 0:
          adc_dev.channel = (0<<5) ;
          return 0;
        case 1:
          adc_dev.channel = (1<<3);
          return 0;
        case 2:
          adc_dev.channel = (1<<4);
          return 0;
        case 3:
          adc_dev.channel = (3<<3);
          return 0;
        default:
          return -EINVAL;
      }
      return 0;
    default:
      return -EINVAL;
  }
}

static ssize_t adc_read(struct file *filp, char __user *buffer, size_t count, loff_t *ppos)
{
  ssize_t retval;
  /*试着获取信号量(即:加锁)*/
  if (down_trylock(&adc_dev.sem))
  {
    return -EBUSY;
  }
  if(!adc_dev.ev_adc)
  {
    if(filp->f_flags & O_NONBLOCK)
    {
      /*应用程序若采用非阻塞方式读取则返回错误*/
      return -EAGAIN;
    }
    else/*以阻塞方式进行读取*/
    {
      /*设置ADC控制寄存器,开启AD转换*/
      start_adc();
      /*使等待队列进入睡眠*/
      wait_event_interruptible(adc_dev.adcq, adc_dev.ev_adc);
    }
  }
  adc_dev.ev_adc = 0;//中断标识//第一次把这个忘了清零,故不能进入中断程序
  /*将读取到的AD转换后的值发往到上层应用程序*/
  if(copy_to_user(buffer, (char *)&adc_dev.data, sizeof(adc_dev.data)))
  {
    retval = -EFAULT;
    goto out;
  }
  up(&adc_dev.sem);
  return sizeof(adc_dev.data);//昨晚上犯的错误,居然return 0

  out:
      up(&adc_dev.sem);
  return retval;
}
/*ADC设备驱动的关闭接口函数*/
static int adc_release(struct inode *inode, struct file *filp)
{
  free_irq(IRQ_ADC, NULL); /*释放中断 特别注意中断应该在文件关闭时释放,即当进程结束时,应该释放中断,不该再拥有,否则再次读文件时会发生中断错误!huanggang大师的没有考虑到此*/
  PDEBUG("File Closed!\n");
  return 0;
}
static struct file_operations adc_fops = {
    .owner = THIS_MODULE,
    .open =     adc_open,
    .read = adc_read,
    .release      =     adc_release,
    .ioctl = adc_ioctl,
};
static int __init adc_init(void)    //模块初始化

{
    int result;
    dev_t dev;
    PDEBUG("Debug Opend!\n"); //我写来测试调试是否打开了的

    if (adc_major) {
        dev = MKDEV(adc_major,adc_minor);
        result = register_chrdev_region(dev, 1, DEV_NAME);//此处为静态分配主设备号

    } else {
        result = alloc_chrdev_region(&dev, adc_minor, 1, DEV_NAME);//动态分配主设备号,此设备号为0

        adc_major = MAJOR(dev);//获取主设备号的宏

    }    
    if (result < 0) {
        printk(KERN_WARNING "ADC: can't get major %d\n", adc_major);
        return result;
    }else {
        printk(KERN_WARNING"Registered ok \nADC_major = %d\n", adc_major);
    }
    
    cdev_init(&adc_dev.cdev, &adc_fops) ;//初始化最先定义的字符设备结构

    (adc_dev.cdev).owner = THIS_MODULE;
    (adc_dev.cdev).ops = &adc_fops;
    result = cdev_add(&adc_dev.cdev, MKDEV(adc_major,adc_minor), 1);//通知内核有该结构注册,注意考虑失败
    if(result < 0){
        printk(KERN_WARNING"cdev_add failed!\n");
        goto setup_err;
    }
    adc_class = class_create(THIS_MODULE, DEV_NAME);//creat自己的设备节点

    if(IS_ERR(adc_class))
    {
        printk(KERN_WARNING"Err:faile in adc_class!\n");
        return -1;
    }
    /*创建设备节点,名字为DEVICE_NAME ,主设备号用上面动态生成的dev*/
    class_device_create(adc_class, NULL, MKDEV(adc_major,adc_minor), NULL, DEV_NAME);
    printk(KERN_WARNING"Leds Module Initialed!\n");
    return 0;
    setup_err:
            unregister_chrdev_region(MKDEV(adc_major,adc_minor), 1);
    return -1;
}
static void __exit adc_exit(void)
{
    unregister_chrdev_region(MKDEV(adc_major, adc_minor ), 1);//销毁设备号
    cdev_del(&adc_dev.cdev);//销毁字符设备结构

    class_device_destroy(adc_class, MKDEV(adc_major, adc_minor )); //销毁注册的类

    class_destroy(adc_class);
    if(adc_clock)//释放时钟和io内存映射
    {
      clk_disable(adc_clock);
      clk_put(adc_clock);
      iounmap(adc_addr);
    }
    printk(KERN_WARNING"ADC Module exit!\n");
}
module_init(adc_init);
module_exit(adc_exit);
MODULE_LICENSE("Dual BSD/GPL");
MODULE_DESCRIPTION("ADC DRVER");
MODULE_AUTHOR("gufeiyang@2010-03-5");


/*
*File Name : devadc.h
*Function : based on arm linu s3c2440 adc driver
*Type : standard char driver
*Time : 2010-3-8
*Author:CQUPT-SSW_feiyang
*/

#ifndef _LEDS_H_
#define _LEDS_H_

#define ADC_DEBUG

#define DEV_NAME "gfy_adc"
struct adc_dev {
    struct semaphore sem;
    struct cdev cdev;
    wait_queue_head_t adcq;

    int channel;
    volatile    int data;
    int ev_adc;
};

//* Macros to help debugging */

#undef PDEBUG /* undef it, just in case */
#ifdef ADC_DEBUG
# ifdef __KERNEL__
     /* This one if debugging is on, and kernel space */
# define PDEBUG(fmt, args...) printk( KERN_DEBUG "[kernel]: " fmt, ## args)
# else /* This one for user space */
# define PDEBUG(fmt, args...) fprintf(stderr, fmt, ## args)
# endif
#else
# define PDEBUG(fmt, args...) /* not debugging: nothing */
#endif

#undef PDEBUGG
#define PDEBUGG(fmt, args...) /* nothing: it's a placeholder */

#define ADC_IOC_MAGIC 'k'

#define ADC_IOCSCHANNEL _IOW(ADC_IOC_MAGIC, 0, int)
#define ADC_IOC_MAXNR 1
#endif


/*
*File Name : adc.c
*Function : app adc driver
*Time : 2010-3-8
*Author:CQUPT-SSW_feiyang
*/

#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include<linux/ioctl.h>
#include <errno.h>
#define CHANNEL 2//通道2

#define ADC_IOC_MAGIC 'k'
#define ADC_IOCSCHANNEL _IOW(ADC_IOC_MAGIC, 0, int)
#define ADC_IOC_MAXNR 1
int main(int argc, char **argv)
{
  int fd,i=3;
  int ret;
  int data;
    //以阻塞方式打开设备文件,非阻塞时flags=O_NONBLOCK

  if((fd = open("/dev/gfy_adc",O_RDWR)) == -1)
  {
    perror("Open");
    return -1;
  } else {
    printf("File Opened!\n");
  }
  if(ioctl(fd,ADC_IOCSCHANNEL,CHANNEL)) {
    perror("Ioctl");
  }else {
    printf("Set Channel %d\n",CHANNEL);
  }
  while(1)
  {
    sleep(1);  //读设备
    ret = read(fd, &data, sizeof(data));
    if(ret != sizeof(data))
    {
 // printf("data = %d sizeof(data) = %d\n",data,sizeof(data));
// printf("ret = %d sizeof(ret) = %d\n",ret,sizeof(ret));
      if(errno != EAGAIN)
      {
        printf("Read ADC Device Faild!\n");
      }
      continue;
    }
    else
    {
      printf("ADC DATA = %d\n", data);
    }
  }
  close(fd);
  return 0;
}

测试结果


 

[root@gfy-S3C2440 /tmp]# ./adc
File Opened!
Set Channel 2
ADC DATA = 705
ADC DATA = 703
ADC DATA = 705
ADC DATA = 707
ADC DATA = 705
ADC DATA = 709
ADC DATA = 686
ADC DATA = 673
ADC DATA = 658
ADC DATA = 657
ADC DATA = 658

源码:

文件: adc.tar.gz
大小: 20KB
下载: 下载
阅读(2043) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~