• TQ210搭载Android4.0.3系统构建之ADC从驱动到HAL到JNI到应用程序(驱动篇)


          ADC的驱动也采用platform设备驱动的方式进行编写,platform_device为platform_driver提供ADC控制器/ADC数据存储器/ADC延时器的地址,在platform_driver的probe函数在进行ioremap的映射,进而操作相应的寄存器。

         对于ADC寄存器的操作,可分为三步:

        1.使用clk_get获取adc时钟,接着使用clk_enable使能adc时钟

        2.设置ADCCON的工作方式,预分频比之类的

        3.当开始读取数据值时,开启ADC转换,判断是否开始转换,判断是否转换完成,最终读取数据,返回给用户空间

    硬件电路图:

           ADCIN1的模拟量输入端接一个滑动变阻器,通过滑动变阻器,提供不同的模拟量输入端。

    adc平台设备代码

    adc_under_device.c

    #include <linux/init.h>
    #include <linux/module.h>
    #include <linux/printk.h>
    #include <linux/platform_device.h>
    
    
    static struct resource adc_under_resource[]=  //adc的资源
    {
    	[0]=  //adc_con0
    		{
    		.start=0xE1700000,
    		.end=0xE1700000+0x3,
    		.flags=IORESOURCE_MEM,
    		},
    	[1]=//adc_delay0
    		{
    		.start=0xE1700000+0x8,
    		.end=0xE1700000+0xB,
    		.flags=IORESOURCE_MEM,
    		},
    	[2]=//adc_data0
    		{
    		.start=0xE1700000+0xC,
    		.end=0xE1700000+0xF,
    		.flags=IORESOURCE_MEM,
    		},
    	[3]=//adc_con1
    		{
    		.start=0xE1701000,
    		.end=0xE1701000+0x3,
    		.flags=IORESOURCE_MEM,
    		},
    	[4]=//adc_delay1
    		{
    		.start=0xE1701000+0x8,
    		.end=0xE1701000+0xB,
    		.flags=IORESOURCE_MEM,
    		},
    	[5]=//adc_data1
    		{
    		.start=0xE1701000+0xC,
    		.end=0xE1701000+0xF,
    		.flags=IORESOURCE_MEM,
    		},
    };
    
    
    static void adc_under_release(struct device *dev)  //释放adc设备函数
    {
    	
    }
    
    static struct platform_device adc_under_platform_device=  //adc的平台函数
    {
    	.name="adc_under",  //用于与驱动进行匹配的名称
    	.id=-1,
    	.num_resources=sizeof(adc_under_resource)/sizeof(adc_under_resource[0]),
    	.resource=adc_under_resource,
    	.dev=
    		{
    			.release=adc_under_release,
    	       },
    	
    	
    };
    
    
    static int __init adc_under_init(void)  //adc模块初始化函数
    {
    	int ret=platform_device_register(&adc_under_platform_device); //注册adc平台设备,为adc驱动提供资源
    	if(ret==0)  printk(KERN_INFO "adc_under_platform_device register success.
    ");
    	else printk(KERN_INFO "adc_under_platform_device register failed.
    ");
    	return ret;
    }
    
    static void __exit adc_under_exit(void) //adc的模块卸载函数
    {
    	 platform_device_unregister(&adc_under_platform_device); //卸载adc平台设备
    	 printk(KERN_INFO "adc_under_platform_device unregister success.
    ");
    }
    
    module_init(adc_under_init);
    module_exit(adc_under_exit);
    MODULE_LICENSE("GPL");
    


     

    编译文件 Makefile

    obj-m	:=adc_under_device.o
    KERNELDIR	:=~/java/Kernel_3.0.8_TQ210_for_Android_v1.0/
    PWD			:=$(shell pwd)
    
    build:kernel_module
    kernel_module:
    	make  -C  $(KERNELDIR) M=$(PWD) modules
    clean:
    	make -C $(KERNELDIR)	  M=$(PWD) clean
    
    


     

    adc平台驱动文件

    adc_under_driver.c

    #include <linux/module.h>
    #include <linux/init.h>
    #include <linux/fs.h>
    #include <linux/printk.h>
    #include <linux/clk.h>
    #include <asm/io.h>
    #include <asm/uaccess.h>  //包含copy_to_user...
    #include <linux/delay.h>   //包含mdelay...
    #include <linux/platform_device.h>
    
    #define ADC_DEVICE_NAME "adc_unders"   //adc的设备名称
    
    
    static struct class *cls;  //类指针
    static void __iomem *adccon1,*adcdata1,*adcdly1; //adc控制器/数据存储器/延时器
    //__iomem表示指向一个I/O的内存空间,目的为了开发通用的驱动,编译器不会对其进行检查
    static struct clk *adc_clk;//adc时钟指针
    
    #define ADCCON (*(volatile unsigned long *)(adccon1))  //adc控制器宏
    #define ADCDATA (*(volatile unsigned long *)(adcdata1))//将__iomem指针转为unsigned long *型指针
    #define ADCDLY (*(volatile unsigned long *)(adcdly1)) //volatile表示变量是易变的,总是需要从内存地址中读取,而不是从寄存器中读取,对该变量不进行优化
    
    int major;  //存储设备主设备号
    
    
    static int adc_under_open(struct inode *inode,struct file *file) //adc打开函数
    {
           printk(KERN_INFO "adc_under_open.
    ");
    	return 0;
    }
    
    static int adc_under_close(struct platform_device *pdev)  //adc关闭函数,将申请的资源释放
    {
    	unregister_chrdev(major, ADC_DEVICE_NAME);  //从chrdevs指针数组中删掉adc字符设备
    	device_destroy(cls, MKDEV(major,0));    //从subsystem中将其删除,并将其引用减一
    	class_destroy(cls);  //删除cls类
    	iounmap(adccon1);  //取消物理地址到虚拟地址的映射
    	iounmap(adcdata1);
    	iounmap(adcdly1);
    	printk(KERN_INFO "adc_under_close success.
    ");
    	return 0;
    }
    
    
    static int  adc_under_read(struct file *file,char __user *buf,size_t count,loff_t *fops)  //adc读取转换后的数字量
    {
    	int adc_val,n,con;
    	ADCCON |=0x01;  //开启ADC的转换
    	while(ADCCON&0x1);  //判断是否开始转换
    	while(!ADCCON&0x8000); //判断是否转换完成
    	con=ADCCON&0xffff;
    	adc_val=(ADCDATA&0x03ff);  //获取到转换后的结果值
    	mdelay(100);
    	n=copy_to_user(buf, &adc_val, sizeof(adc_val));  //将adc_val的值从内核空间拷到用户空间
    	printk(KERN_INFO "adc_under_read,adc_val=%d,con=0x%x
    ",adc_val,con);
    	return n;
    }
    
    struct file_operations adc_under_fops=  //adc文件操作
    {
    	.owner=THIS_MODULE,
    	.open=adc_under_open,
    	.read=adc_under_read,
    };
    
    static void adc_reg_init(void)  //adc的初始化函数
    {
    	int con;
    	adc_clk=clk_get(NULL, "adc");  //获取到adc的时钟,在S5PV210中,进行ADC转换的时候,使用的是PCLK(Max 66MHz) is used
    	clk_enable(adc_clk); //使能clk时钟
    	ADCDLY=100;  //adc采样转换的延迟时间,影响stylus down,x-conversion,y-conversion时间
    	ADCCON=(0<<0) | //没有操作
    		(0<<1) |  //disable start by read operation
    		(0<<2) |  //正常操作模式
    		(65<<6) |  //分频值为65 ,因为PCLK=66 所以prscvl=65 即 a/d转换频率为1MHz ,Conversion time=5us
    		(1<<14) ;// 使能预分频值
    	con=ADCCON&0xffff;  //调试信息
    	printk(KERN_INFO "adc_reg_init.con=0x%x
    ",con);
    }
    
    static int adc_under_probe(struct platform_device *pdev)  //adc的探针函数,与platform_device匹配后调用,用于初始化platform_driver
    {
           struct resource *rescon1,*resdata1,*resdly1;  //资源指针
    	 rescon1=platform_get_resource(pdev,IORESOURCE_MEM, 3);  //获取到ADCCON1的资源,详情见adc_under_device.c
    	 if(rescon1==NULL) { printk(KERN_INFO "can not find rescon1.
    "); return -1;}  //判断是否获得资源
    	 adccon1=ioremap(rescon1->start, rescon1->end-rescon1->start+1);  //物理地址映射为虚拟地址
    	if(adccon1==NULL) { printk(KERN_INFO "no mem to adccon1.
    "); return -1;}  //判断映射是否成功
    	 
    	  resdata1=platform_get_resource(pdev,IORESOURCE_MEM, 5);//获取到ADCDATA1的资源,详情见adc_under_device.c
    	  if(resdata1==NULL) { printk(KERN_INFO "cann not find resdata1->
    "); return -1;} //判断是否获得资源
    	  adcdata1=ioremap(resdata1->start, resdata1->end-resdata1->start+1);//物理地址转为虚拟地址
    	if(adcdata1==NULL) { printk(KERN_INFO "no mem to adcdata1.
    "); return -1;}//判断映射是否成功
    	  
    	  resdly1=platform_get_resource(pdev,IORESOURCE_MEM, 4); //获取到ADCDLY1的资源,详情见adc_under_device.c
    	  if(resdly1==NULL) { printk(KERN_INFO "can not find resdly1.
    "); return -1; } //判断是否获取资源
    	 adcdly1=ioremap(resdly1->start, resdly1->end-resdly1->start+1); //物理地址映射为虚拟地址
    	if(adcdly1==NULL) { printk(KERN_INFO "no mem to adcdly1.
    "); return -1;}//判断映射是否成功
    
    	 adc_reg_init();  //adc寄存器的初始化
    
    	 major=register_chrdev(0, ADC_DEVICE_NAME,&adc_under_fops); //创建并注册字符设备
    	 cls=class_create(THIS_MODULE, ADC_DEVICE_NAME);  //创建类
    	 device_create(cls,NULL, MKDEV(major,0), NULL, ADC_DEVICE_NAME); //建立/dev/adc_unders设备文件,利用udev策略
    	 printk(KERN_INFO "adc_under_probe success.
    ");
    	return 0;
    }
    
    
    static struct platform_driver adc_under_platform_driver=  //adc的平台驱动
    {
    	.probe=adc_under_probe,
    	.remove=adc_under_close,
    	.driver=
    		{
    			.name="adc_under",  //用于与platform_device匹配的名称
    		},
    };
    
    static int __init adc_driver_init(void)  //adc模块初始化函数
    {
    	int ret=platform_driver_register(&adc_under_platform_driver);  //注册平台驱动
    	if(ret==0) printk(KERN_INFO "adc_under_platform_driver success.
    ");
    	else printk(KERN_INFO "adc_under_platform_driver failed.
    ");
    	return ret;
    }
    
    static void __exit adc_driver_exit(void) //模块卸载函数
    {
      	platform_driver_unregister(&adc_under_platform_driver);  //卸载平台驱动
    }
    
    
    module_init(adc_driver_init);
    module_exit(adc_driver_exit);
    MODULE_LICENSE("GPL");
    


    编译文件 Makefile

    obj-m	:=adc_under_driver.o
    KERNELDIR	:=~/java/Kernel_3.0.8_TQ210_for_Android_v1.0/
    PWD			:=$(shell pwd)
    
    build:kernel_module
    kernel_module:
    	make  -C  $(KERNELDIR) M=$(PWD) modules
    clean:
    	make -C $(KERNELDIR)	  M=$(PWD) clean
    


     

    最后的测试文件

    adc_under_test.c

    #include <fcntl.h>
    #include <stdio.h>
    #include <stdlib.h>
    
    
    #define DEVICE_NAME "/dev/adc_unders"
    
    int main(void)
    {
    	int fd,ret=0,data,count=0;
    	fd=open(DEVICE_NAME, O_RDWR);
    	if(fd<0) {printf("can not open fd=%s
    ",DEVICE_NAME); return -1; }
    	while(count<10){
    		ret=read(fd,&data,sizeof(data));
    		if(ret<0)
    				printf("read data is error 
    ");
    		printf("adc is %f V
    ",(float)data*3.3/1024);
    		sleep(2);
    		count++;
    	}
    
    	close(fd);
    	return ret;
    }
    
    


    测试文件的Android.mk

    LOCAL_PATH	:=$(call my-dir)
    include $(CLEAR_VARS)
    LOCAL_MODULE_TAGS		:=eng
    LOCAL_SRC_FILES		:=adc_under_test.c
    LOCAL_MODULE	:=adc_test
    LOCAL_MODULE_PATH		:=$(LOCAL_PATH)
    include $(BUILD_EXECUTABLE)
    
    
    
    
    

        对于这个adc的驱动,我在测试的时候出现了一个很奇怪的现象,我在adc驱动测试代码中总共是读取了10次adc的值,偶合会有一两次的值是错的,就是3,3V,即adc没开始转换的值,嗯,对于这个问题暂时还不清楚,是不是我的adc硬件上有问题,待后续研究。

  • 相关阅读:
    第八次作业
    微信用户体验
    •设计一款给爸爸妈妈用的手机
    对类的继承
    必应词典
    第二次作业二
    第二次作业
    我想搞的软工
    数字签名
    C++的学习心得
  • 原文地址:https://www.cnblogs.com/liangxinzhi/p/4275624.html
Copyright © 2020-2023  润新知