Linux-4.9.2内核在mini2440上的移植(十)——ADC驱动移植

本篇目的:移植ADC驱动,并测试。

本篇参考:http://singleboy.blog.163.com/blog/static/54900194201152284152419/

 

说明:

1、本文源码参考链接的源码修改,在linux-4.9.2下移植驱动请务必使用本源码

2、源码为了方便粘贴,部分注释已经去掉,需要注释请查看参考网址


10.1 添加ADC驱动源码文件

(1)添加头文件

root@ubuntu:~/linux-4.9.2# vim drivers/misc/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_ */

 

保存退出

 

(2)添加mini2440_adc.c文件

root@ubuntu:~/linux-4.9.2# vim drivers/misc/mini2440_adc.c

 

添加如下源码

 

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
 
#include "s3c24xx-adc.h"
 
#undef DEBUG
#ifdef DEBUG
#define DPRINTK(x...) {printk("%s%d\n",__FUNCTION__,__LINE__);}
#else
#define DPRINTK(x...) (void)(0)
#endif
 
#define DEVICE_NAME "adc"
 
static void __iomem *adc_base; /*定义了一个用来保存经过虚拟映射后的内存地址*/
 
typedef struct {
          wait_queue_head_t wait;
           int channel;
            int prescale;
}ADC_DEV;
static ADC_DEV adcdev;
 
DEFINE_SEMAPHORE(ADC_LOCK);
 
/*用于标识AD转换后的数据是否可以读取,0表示不可读取*/
static volatile int ev_adc = 0;
 
/*用于保存读取的AD转换后的值,该值在ADC中断中读取*/
static int adc_data;
 
/*保存从平台时钟队列中获取ADC的时钟*/
static struct clk *adc_clk;
 
#define ADCCON (*(volatile unsigned long *)(adc_base +S3C2410_ADCCON))
#define ADCTSC (*(volatile unsigned long *)(adc_base +S3C2410_ADCTSC))
#define ADCDLY (*(volatile unsigned long *)(adc_base +S3C2410_ADCDLY))
#define ADCDAT0 (*(volatile unsigned long *)(adc_base +S3C2410_ADCDAT0))
#define ADCDAT1 (*(volatile unsigned long *)(adc_base +S3C2410_ADCDAT1))
#define ADCUPDN (*(volatile unsigned long *)(adc_base + 0x14))
#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(ch, prescale) \
         do{ \
                    ADCCON = PRESCALE_EN | PRSCVL(prescale) |ADC_INPUT((ch)) ; \
                    ADCCON |= ADC_START; \
         }while(0)
 
static irqreturn_t adc_irq(int irq, void *dev_id)
{
         if (!ev_adc)
         {
                   /*读取AD转换后的值保存到全局变量adc_data中,S3C2410_ADCDAT0定义在regs-adc.h中,
              *            这里为什么要与上一个0x3ff,很简单,因为AD转换后的数据是保存在ADCDAT0的第0-9位,
     *                       所以与上0x3ff(即:1111111111)后就得到第0-9位的数据,多余的位就都为0*/
             adc_data = ADCDAT0 & 0x3ff;
               /*将可读标识为1,并唤醒等待队列*/                         
             ev_adc = 1;
            
             wake_up_interruptible(&adcdev.wait);
         }
         return IRQ_HANDLED;
}
 
static ssize_t adc_read(struct file *filp, char *buffer, size_tcount, loff_t *ppos)
{
 
          /*试着获取信号量(即:加锁)*/
          if (down_trylock(&ADC_LOCK))
          {
                    return -EBUSY;
          }
          if(!ev_adc) /*表示还没有AD转换后的数据,不可读取*/
          {
                    if(filp->f_flags & O_NONBLOCK)
                    {
                             /*应用程序若采用非阻塞方式读取则返回错误*/
                             return -EAGAIN;
                    }
                    else /*以阻塞方式进行读取*/
                    {
                             /*设置ADC控制寄存器,开启AD转换*/
                             start_adc(adcdev.channel, adcdev.prescale);
                             /*使等待队列进入睡眠*/
                             wait_event_interruptible(adcdev.wait, ev_adc);
                    }
          }
          /*能到这里就表示已有AD转换后的数据,则标识清0,给下一次读做判断用*/
          ev_adc = 0;
          
          /*将读取到的AD转换后的值发往到上层应用程序*/
          copy_to_user(buffer, (char *)&adc_data,sizeof(adc_data));
          
          /*释放获取的信号量(即:解锁)*/
          up(&ADC_LOCK);
          
          return sizeof(adc_data);
          
}
 
static int adc_open(struct inode *inode, struct file *filp)
{
         int ret;
         /* normal ADC */
         ADCTSC = 0;
 
         init_waitqueue_head(&(adcdev.wait));
         adcdev.channel=0;
         adcdev.prescale=0xff;
         /* 申请ADC中断服务,这里使用的是共享中断:IRQF_SHARED,为什么要使用共享中断,因为在触摸屏驱动中
          *          *      也使用了这个中断号。中断服务程序为:adc_irq在下面实现,IRQ_ADC是ADC的中断号,这里注意:
          *                   *             申请中断函数的最后一个参数一定不能为NULL,否则中断申请会失败,这里传入的是ADC_DEV类型的变量*/
         ret = request_irq(IRQ_ADC,adc_irq, IRQF_SHARED, DEVICE_NAME, &adcdev);
         if (ret)
         {
                   /*错误处理*/
                   printk(KERN_ERR"IRQ%d error %d\n", IRQ_ADC, ret);
                   return-EINVAL;
         }
        
         DPRINTK( "adcopened\n");
         return 0;
        
}
 
static int adc_release(struct inode *inode, struct file *filp)
{
          DPRINTK( "adc closed\n");
           return 0;
}
 
static struct file_operations dev_fops = {
         owner: THIS_MODULE,
          open: adc_open,
            read: adc_read,
      release: adc_release,
};
 
static struct miscdevice adc_miscdev = {
          .minor = MISC_DYNAMIC_MINOR,
          .name = DEVICE_NAME,
          .fops = &dev_fops,
};
 
static int __init dev_init(void)
{
         int ret;
         /*  1,从平台时钟队列中获取ADC的时钟,这里为什么要取得这个时钟,因为ADC的转换频率跟时钟有关。
            *      系统的一些时钟定义在arch/arm/plat-s3c24xx/s3c2410-clock.c中*/
         adc_clk = clk_get(NULL,"adc");
        
         clk_prepare_enable(adc_clk);
        
         if (!adc_clk) {
                   printk(KERN_ERR"failed to get adc clock source\n");
                   return-ENOENT;
         }
        
 
           /*时钟获取后要使能后才可以使用,clk_enable定义在arch/arm/plat-s3c/clock.c中*/
         clk_enable(adc_clk);
 
 
         /*  2,将ADC的IO端口占用的这段IO空间映射到内存的虚拟地址,ioremap定义在io.h中。
         *        注意:IO空间要映射后才能使用,以后对虚拟地址的操作就是对IO空间的操作,
         *           S3C2410_PA_ADC是ADC控制器的基地址,定义在mach-s3c2410/include/mach/map.h中,0x20是虚拟地址长度大小*/
         adc_base=ioremap(S3C2410_PA_ADC,0x20);
         if (adc_base == NULL){
                   printk(KERN_ERR"Failed to remap register block\n");
                   ret =-EINVAL;
                   gotoerr_noclk;
         }
 
 
         /*   3,把看ADC注册成为misc设备,misc_register定义在miscdevice.h中
          adc_miscdev结构体定义及内部接口函数在第2步中讲,MISC_DYNAMIC_MINOR是次设备号,定义在miscdevice.h中*/
         ret =misc_register(&adc_miscdev);
         if (ret)                                
         {
                   /*错误处理*/
                   printk(KERN_ERR"Cannot register miscdev on minor=%d (%d)\n", MISC_DYNAMIC_MINOR,ret);
                   gotoerr_nomap;
         }
        
         printk(DEVICE_NAME"\tinitialized!\n");
        
         return 0;
        
err_noclk:
         clk_disable(adc_clk);
         clk_put(adc_clk);
 
err_nomap:
         iounmap(adc_base);
        
         return ret;
}
 
static void __exit dev_exit(void)
{       
         free_irq(IRQ_ADC,&adcdev);
         iounmap(adc_base); /*释放虚拟地址映射空间*/
         if (adc_clk)  /*屏蔽和销毁时钟*/
         {
                   clk_disable(adc_clk);
                   clk_put(adc_clk);
                   adc_clk =NULL;
         }
         misc_deregister(&adc_miscdev);
}
 
EXPORT_SYMBOL(ADC_LOCK);
 
module_init(dev_init);
module_exit(dev_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("singleboy");
MODULE_DESCRIPTION("Mini2440 ADC Driver");

 

(3)编辑Makefile

root@ubuntu:~/linux-4.9.2# vim drivers/misc/Makefile

 

在第35行添加红色部分

 

obj-$(CONFIG_TI_DAC7512)       += ti_dac7512.o

obj-$(CONFIG_C2PORT)           += c2port/

obj-$(CONFIG_MINI2440_ADC) += mini2440_adc.o

obj-$(CONFIG_HMC6352)          += hmc6352.o

obj-y                          += eeprom/

 

(4)编辑Kconfig

root@ubuntu:~/linux-4.9.2# vim drivers/misc/Kconfig

 

在第87行添加红色部分

 

        help

          Some chips providemore than one TC block, so you have the

          choice of which oneto use for the clock framework.  Theother

          TC can be used forother purposes, such as PWM generation and

          interval timing.

 

config MINI2440_ADC

 bool"ADC driver for FriendlyARM Mini2440 development boards"

 depends onMACH_MINI2440

 default yif MACH_MINI2440

 help

  this isADC driver for FriendlyARM Mini2440 development boards

  Notes: thetouch-screen-driver required this option

 

config DUMMY_IRQ

        tristate "DummyIRQ handler"

        default n

        ---help---

 

(5)在menuconfig中添加ADC驱动支持

root@ubuntu:~/linux-4.9.2# make menuconfig

 

Device Drivers --->

      [*] Misc devices  ---> 

Linux-4.9.2内核在mini2440上的移植(十)——ADC驱动移植_第1张图片

 

10.2 编译、测试

(1)编译源码

root@ubuntu:~/linux-4.9.2# make

root@ubuntu:~/linux-4.9.2# ./mkuImage.sh

 

(2)重启测试

重启开发板

 

(3)编写测试文件

root@ubuntu:~/linux-4.9.2# vim ../NFS/rootfs/home/plg/adc_test.c

 

加入如下代码

#include 
#include 
#include 
#include 
 
int main(int argc, char **argv)
{
        int fd;
 
        fprintf(stderr,"press Ctrl-C to stop\n");
 
        fd =open("/dev/adc", 0);
 
        if(fd < 0)
        {
               printf("Open ADC Device Faild!\n");
                exit(1);
        }
 
        while(1)
        {
                int ret;
                int data;
 
               fprintf(stderr, "read adc\n");
                ret = read(fd, &data, sizeof(data));
 
               fprintf(stderr, "read adc1\n");
 
                if(ret !=sizeof(data))
                {
                       fprintf(stderr, "read adc2\n");
 
                       if(errno != EAGAIN)
                        {
                               printf("Read ADC Device Faild!\n");
                        }
 
                       continue;
                }
                else
                {
                       printf("Read ADC value is: %d\n", data);
}
               fprintf(stderr, "read adc3\n");
 
                 sleep(1);
 
        }
 
        close(fd);
 
        return 0;
}


(4)编译测试源码

root@ubuntu:~/linux-4.9.2# cd ../NFS/rootfs/home/plg/

root@ubuntu:~/NFS/rootfs/home/plg# arm-linux-gcc -o adc_testadc_test.c

 

(5)测试源码

打开secrueCRT,进入console

[root@FriendlyARM /]# cd /home/plg/

[root@FriendlyARM plg]# ./adc_test

可以看到:

read adc1

Read ADC value is: 838

read adc3

read adc

read adc1

Read ADC value is: 833

read adc3

read adc

read adc1

Read ADC value is: 884

read adc3

read adc

read adc1

Read ADC value is: 884

read adc3

read adc

read adc1

Read ADC value is: 959

read adc3

read adc

read adc1

Read ADC value is: 967

read adc3

read adc

read adc1

Read ADC value is: 968

read adc3

read adc

read adc1

Read ADC value is: 968

read adc3

read adc

read adc1

Read ADC value is: 967

 

说明ADC驱动成功。

你可能感兴趣的:(Linux-4.9.2内核在mini2440上的移植(十)——ADC驱动移植)