Chinaunix首页 | 论坛 | 博客
  • 博客访问: 30940
  • 博文数量: 7
  • 博客积分: 252
  • 博客等级: 二等列兵
  • 技术积分: 100
  • 用 户 组: 普通用户
  • 注册时间: 2010-12-17 15:34
文章分类
文章存档

2011年(6)

2010年(1)

我的朋友

分类: LINUX

2011-01-11 22:47:10

中断方式的驱动:
/*============================s3c2410-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_ */
/*==========================s3c2410-adc.c===============================*/
#include
#include
#include
#include
#include
#include
#include    /* printk() */
#include    /* kmalloc() */
#include        /* everything... */
#include     /* error codes */
#include     /* size_t */
#include
#include
#include
#include
//#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "s3c2410-adc.h"
 
#define DEVICE_NAME "adc"
#define ADCRAW_MINOR 0
#define ADC_INPUT(x) ((x)<<3)
#define PRSCVL(x) ((x)<<6)
#define ADCCON 0x58000000
#define ADCDAT0 0x5800000c
#define CLKCON 0x4c00000c
static int adc_major = 244;   //在0~255之间自由分配
static volatile unsigned int *adccon;
static volatile unsigned int *adcdat0;
static volatile unsigned int *clkcon;
typedef struct
{
       struct semaphore lock; //声明一个信号量
       wait_queue_head_t wait; //声明一个等待队列头
       int channel;//选择哪一路AD转换器
       int prescale;//预分频值
}ADC_DEV;
 
static ADC_DEV adcdev;
 
/*==中断处理函数==*/
static irqreturn_t adcdone_int_handler(int irq,void *dev_id,struct pt_regs *regs)
{
       wake_up(&adcdev.wait);//唤醒等待队列
       return IRQ_HANDLED ;
}
 
/*==打开设备==*/

static int s3c2410_adc_open(struct inode *inode, struct file *filp)

      int ret;
      printk("in adc open");
      ret = request_irq(IRQ_ADC, adcdone_int_handler, SA_INTERRUPT, DEVICE_NAME, NULL);//注册中断例程
      if (ret)
      {
          return ret;
       }
      init_MUTEX(&adcdev.lock);//初始化一个互斥的信号量,并设置为1
      init_waitqueue_head(&(adcdev.wait));//初始化等待队列
      adcdev.channel=0;        //缺省为0通道
      adcdev.prescale=0xff;   //预分频的缺省为 0xff
      printk(KERN_INFO"adc opened\n");
      return 0;
}

/*==对设备进行写操作,buffer一定是用户空间的==*/
static ssize_t s3c2410_adc_write(struct file *file, const char *buffer, size_t count, loff_t * ppos)
{
      int data;
      if(count!=sizeof(data))
      {
          printk(KERN_INFO"the size of  input data must be %d\n", sizeof(data));
          return 0;
      }
      copy_from_user(&data, buffer, count);   //从用户空间拷贝数据到内核空间
      adcdev.channel=ADC_WRITE_GETCH(data);   //得到哪一路AD转换器
      adcdev.prescale=ADC_WRITE_GETPRE(data); //得到预分频值
//    printk(KERN_INFO "adcdev.channel=%d\n",adcdev.channel);
//    printk(KERN_INFO"set adc channel=%d, prescale=0x%x\n", adcdev.channel, adcdev.prescale);
      return count;
}

/*==对设备进行读操作,buffer一定是用户空间的==*/
static ssize_t s3c2410_adc_read(struct file *filp, char *buffer, size_t count, loff_t *ppos)
{
      int ret = 0;
      if (down_interruptible(&adcdev.lock))//获得信号量
              return -ERESTARTSYS;
      writel(readl(adccon) &(~1), adccon);//对AD控制寄存器进行操作,具体参看s3c2410 datasheet
      writel( (1<<14) | (255<<6) |(1<<0)|(1<<0)| ADC_INPUT(adcdev.channel), adccon);
      sleep_on( &adcdev.wait );
      ret = readl(adcdat0);
      ret &= 0x3ff;
//    printk(KERN_INFO "%d\n",adcdev.channel);
//    printk(KERN_INFO"AIN[%d] = 0x%04x, %d\n", adcdev.channel, ret, readl(S3C2410_ADCCON) & 0x80 ? 1:0);
      copy_to_user(buffer, (char *)&ret, sizeof(ret));//拷贝内核数据到用户空间
      up(&adcdev.lock);
      return sizeof(ret);
}

/*==关闭设备==*/
static int s3c2410_adc_release(struct inode *inode, struct file *filp)
{
      free_irq(IRQ_ADC, NULL);//释放中断资源
      printk(KERN_INFO"adc closed\n");
      return 0;
}
 
/*==初始化并添加结构提struct cdev到系统之中==*/
static void adc_setup_cdev(struct cdev *dev, int minor,struct file_operations *fops)
{
     int err, devno = MKDEV(adc_major, minor);
     cdev_init(dev, fops);         //初始化结构体struct cdev
     dev->owner = THIS_MODULE;
     dev->ops = fops;   //给结构体里的ops成员赋初值,这里是对设备操作的具体的实现函数
     err = cdev_add (dev, devno, 1);//将结构提struct cdev添加到系统之中
 /* Fail gracefully if need be */
     if (err)
            printk (KERN_NOTICE "Error %d adding adc %d", err, minor);
}
static struct cdev AdcDevs;
 
/*==定义一个file_operations结构体,来实现对设备的具体操作的功能==*/
static struct file_operations adc_remap_ops = {
                owner: THIS_MODULE,
                open: s3c2410_adc_open,
                read: s3c2410_adc_read, 
                write: s3c2410_adc_write,
                release: s3c2410_adc_release,
                  };

/*==初始化设备驱动模块,主要完成对字符设备结构体的初始化和添加到系统中,并得到一个设备的设备号==*/
static int  adc_init(void)
{
// writel(0,S3C2410_ADCTSC); //XP_PST(NOP_MODE);
        int result;
        dev_t dev = MKDEV(adc_major, 0);//将主设备号和次设备号定义到一个dev_t数据类型的结构体之中
 /* Figure out our device number. */
        if (adc_major)
            result = register_chrdev_region(dev, 1, "adc");//静态注册一个设备,设备号先前指定好,并得到一个设备名,cat /proc/device来查看信息
        else
        {
              result = alloc_chrdev_region(&dev, 0, 1, "adc");//如果主设备号被占用,则由系统提供一个主设备号给设备驱动程序
              adc_major = MAJOR(dev);//得到主设备号
         }
        if (result < 0)
        {
              return result;
         }
        if (adc_major == 0)                         
              adc_major = result;//如果静态分配失败。把动态非配的设备号给设备驱动程序
        adc_setup_cdev(&AdcDevs, 0, &adc_remap_ops);//初始化和添加结构体struct cdev到系统之中
 //do ioremap
        adccon = ioremap(ADCCON, 0x4);
        adcdat0 = ioremap(ADCDAT0, 0x4);
        clkcon = ioremap(CLKCON, 0x4);
        printk(KERN_INFO"adc clock = %d\n", *clkcon & (0x1<<15));
        *clkcon |= 0x1 << 15; //open clock for adc
        printk(KERN_INFO"adc device installed, with major %d\n", adc_major);
        return 0;
}
/*==卸载驱动模块==*/
static void adc_cleanup(void)
{
       iounmap(adccon);
       iounmap(adcdat0);
       cdev_del(&AdcDevs);//删除结构体struct cdev
       unregister_chrdev_region(MKDEV(adc_major, 0), 1);//卸载设备驱动所占有的资源
       printk(KERN_INFO"adc device uninstalled\n");
}
 
module_init(adc_init);//初始化设备驱动程序的入口
module_exit(adc_cleanup);//卸载设备驱动程序的入口

MODULE_LICENSE("Dual BSD/GPL");//模块应该指定代码所使用的许可证
 
/*============================adc_test.c===============================*/
#include
#include
#include
#include
#include
#include
#include
#include "s3c2410-adc.h"
#define ADC_DEV  "/dev/adc"
static int adc_fd = -1;
 
static int init_ADdevice(void)
{          
      printf(ADC_DEV);
      if((adc_fd=open(ADC_DEV,O_RDWR))<0)//打开设备
      {
           printf("Error opening %d adc device\n", adc_fd);
           return -1;
      }
}
static int GetADresult(int channel)
{
      int PRESCALE=0XFF;
      int data=ADC_WRITE(channel, PRESCALE);
      write(adc_fd, &data, sizeof(data));//对设备进行读操作
      read(adc_fd, &data, sizeof(data));//对设备进行写操作
      return data;
}
int main(void)
{
     int i;
     float d;
     if(init_ADdevice()<0)
           return -1;
     while( 1 )
      {
           d=((float)GetADresult(0)*3.0)/1024.0;
           printf("%8f\t",d);
           printf("\n");    
           sleep(1);
           printf("\r");
       }
      close(adc_fd);//关闭设备
      return 0;
}
轮询方式的驱动:
/*====*/
 
 
 
 
 
 
阅读(3356) | 评论(0) | 转发(1) |
给主人留下些什么吧!~~