一、环境描述及效果展示
硬件环境:TQ2440开发板
内核版本:linux-2.6.32
文件系统:busybox-1.13.1
交叉工具:arm-2009q1,codesource出品
效果如下
二、code
//ad.c TQ2440,
- #include <linux/module.h>
- #include <linux/kernel.h>
- #include <linux/init.h>
- #include <linux/sched.h>
- #include <linux/poll.h>
- #include <linux/timer.h>
- #include <linux/irq.h>
- #include <linux/cdev.h>
- #include <linux/clk.h>
- #include <linux/wait.h>//waitqueue
- #include <linux/errno.h>
- #include <linux/interrupt.h>//irq
- #include <linux/io.h>
- #include <linux/types.h>
- #include <linux/delay.h>
- #include <linux/semaphore.h>//sem
- #include <linux/fs.h>
- #include <linux/platform_device.h>//
- #include <linux/miscdevice.h>
- #include <linux/device.h>
- #include <linux/workqueue.h>//workqueue
- #include <asm/uaccess.h>
- #include <asm/irq.h>
- //soc相关
- #include <mach/gpio.h>
- #include <mach/regs-clock.h>
- #include <mach/regs-gpio.h>
- #include <mach/hardware.h>
- #include <mach/irqs.h>//IRQ_ADC
- #include <plat/regs-adc.h>//这个头文件,包含了ADC
- #include <plat/map.h>
- //取消调试信息,则只需要#define _DEBUG_ 0
- #define _DEBUG_ 1
- #define DEV_NAME "usr_adc"
- #if _DEBUG_
- #define debug(format,msg...) printk(format,##msg)
- #else
- #define debug(format,msg...) (void *)(0)
- #endif
- char * iok="ok";
- char *ino="no";
- char * msg;
- //ADCCON
- static void __iomem * VIR_ADC_BASE;
- #define base_addr VIR_ADC_BASE
- //clk
- static struct clk *adc_clk;
- //这个值是全局变量,是用来存放采样值
- unsigned short volatile value;
- //这个值用来标识转换完成
- unsigned int volatile end_of_conv;
- DECLARE_WAIT_QUEUE_HEAD(wq);
- DECLARE_MUTEX(lock);
- struct
- {
- int ch;
- int prescale;
- }adc_dev;
- //相关头文件plat/regs-adc.h
- //下面开始书写启动ADC的函数
- //这个函数很关键
- /*
- 15 14 13-6 5-3 2 1 0
- ecflg prscen prscvl sel_mux stdbm read_start enable_start
- */
- static int start_adc(int ch,int prescale)
- {
- //使用256分频|使用AIN2|使能ADC
- unsigned int temp=0;
- //0111,1111,1101,0001
- temp |= S3C2410_ADCCON_PRSCEN |S3C2410_ADCCON_PRSCVL(prescale)|S3C2410_ADCCON_SELMUX(ch);
- debug("in %s,writel temp=%02x--------->[ok]\n",__func__,temp);
- writel(temp,(VIR_ADC_BASE+S3C2410_ADCCON));
- debug("in %s,writel temp=%02x--------->[ok]\n",__func__,temp);
- temp |= S3C2410_ADCCON_ENABLE_START;
- writel(temp,(VIR_ADC_BASE+S3C2410_ADCCON));
- debug("in %s,writel temp=%02x--------->[ok]\n",__func__,temp);
- return 0;
- }
- //irq proc
- //ADC中断处理程序
- static irqreturn_t irq_proc(int irq, void *dev_id)
- {
- value=readl(VIR_ADC_BASE +S3C2410_ADCDAT0)&0x3ff;
- end_of_conv=1;
-
-
- debug("in %s,readl,value=%d\n",__func__,value);
- wake_up_interruptible(&wq);
-
-
- debug("in %s,readl,avale=%d\n",__func__,value);
- return IRQ_RETVAL(IRQ_HANDLED);
- }
- //打开设备
- static int dev_open(struct inode *nodep,struct file *filep)
- {
-
- int ret=0;
- if(down_trylock(&lock))
- {
- ret= -EBUSY;
- goto out;
- }
-
-
- //value初始化为0或者初始化为0xff
- //end_of_conv结束标志初始化为0
- //初始化AD通道为2.分频0xff
- value=0x0000;
- end_of_conv=0;
- adc_dev.ch=2;
- adc_dev.prescale=0xff;
-
- //进行 ioremap
-
- VIR_ADC_BASE =ioremap(S3C2410_PA_ADC,0x20);
- msg=(VIR_ADC_BASE==NULL ? iok : ino);
- debug("in %s,ioremap VIR_ADC_BASE=%p--------->[%s]\n",__func__,VIR_ADC_BASE,msg);
- if(VIR_ADC_BASE==NULL)
- {
- ret =-ENOMEM;
- goto out;
- }
-
-
- //writel(0,base_addr+S3C2410_ADCTSC);
- ret=request_irq(IRQ_ADC,irq_proc,IRQF_SHARED,DEV_NAME,&adc_dev);
- msg=(ret==0 ? iok : ino);
- debug("in %s,request_irq--------->[%s]\n",__func__,msg);
- if (ret)
- goto out;
-
-
-
- //获得时钟源plat-s3c/clock.c
- adc_clk = clk_get(NULL,"adc");
- msg=(adc_clk==NULL ? iok : ino);
- debug("in %s,clk_get--------->[%s]\n",__func__,msg);
- if(adc_clk==NULL)
- {
-
- ret =-ENOENT;
- goto out;
- };
- clk_enable(adc_clk);
-
-
- out:
- return ret;
- }
- //read
- static int dev_read(struct file* filep,char * buf, ssize_t count,loff_t *ops)
- {
- int ret=0;;
-
- /*
- 注意这个结构
- if(end_of_conv==0){}
- else{}
- 为什么会有这个结构?
-
- 考虑应用程序的结构
- int fd=open();
- ...............
- do{
- ...
- i=read();
- ...
- }while(i>0);
- close(fd);
-
- ...............
-
- //如果数据转换完成,
- //则啥也不做直接进入下一步
-
- }
- put_user(value,buf);
- 不能用这个,那样返回的数据是不正确的
- */
-
- if(end_of_conv==0)
- {
- start_adc(adc_dev.ch,adc_dev.prescale);
- //非阻塞读
- if(filep->f_flags & O_NONBLOCK)
- {
- ret=-EAGAIN;
- debug("in %s,filep->f_flags & O_NONBLOCK------>[return]\n",__func__);
- goto out;
- }
- else//阻塞读
- {
- debug("in %s,wait_event end_of_conv=1------------->[start]\n",__func__);
- //等待事件end_of_conv==1,或者等待2s,
- wait_event_interruptible_timeout(wq,end_of_conv==1,200);
- debug("in %s,finish wait_event end_of_conv=1------------->[finish]\n",__func__);
- }
-
- }
- else
- {}
- copy_to_user(buf,(char *)&value,sizeof(value));
- end_of_conv=0;
- value=0;
- out:
- return ret;
-
-
- }
- //dev_release
- static int dev_release(struct inode *nodep,struct file *filep)
- {
- free_irq(IRQ_ADC,&adc_dev);
-
- //释放ioremap
- VIR_ADC_BASE==NULL? 0 : iounmap(VIR_ADC_BASE);
-
-
- //释放adc_clk
- if(adc_clk!=NULL)
- {
- clk_disable(adc_clk);
- clk_put(adc_clk);
- adc_clk=NULL;
- }
- up(&lock);
- return 0;
- }
- static int dev_write(struct file* filep,char * buf, ssize_t count,loff_t *ops)
- {
- debug("\nsorry,this dev can't be write\n");
- return 0;
- }
- //字符设备关键结构体
- struct file_operations file_ops=
- {
- .owner =THIS_MODULE,
- .open = dev_open,
- .read =dev_read,
- .write =dev_write,
- .release =dev_release,
- };
- //我们将这个注册为混杂设备
- struct miscdevice adc_fops=
- {
- .minor =MISC_DYNAMIC_MINOR,
- .name =DEV_NAME,
- .fops= &file_ops,
- };
- //dev_init
- static __init int dev_init(void)
- {
- int ret;
- ret=misc_register(&adc_fops);
- msg=(ret==0?iok : ino);
- debug("in %s,misc_register--------->[%s]\n",__func__,msg);
- return ret;
- }
- //dev_exit
- static __exit int dev_exit(void)
- {
- int ret;
- ret=misc_deregister(&adc_fops);
- msg=(ret==0?iok : ino);
- debug("in %s,misc_deregister--------->[%s]\n",__func__,msg);
- return ret;
- }
- MODULE_AUTHOR("keytounix");
- MODULE_LICENSE("GPL");
- MODULE_DESCRIPTION("driver adc for TQ2440");
- module_init(dev_init);
- module_exit(dev_exit);
三、 Makefile
- TARGET=ad
- #这个对应ad.c
- #ad.c是上面那个驱动文件
- #生成目标文件ad.ko
- #-W是显示警告信息
- #-w不显示警告信息
- CC=arm-linux-gcc -w
- #ad.ko
- obj-m :=$(TARGET).o
- #这个是内核路径
- KERNELDIR :=/home/kernel/linux-2.6.32
- default:
- make -C $(KERNELDIR) M=$(shell pwd) modules
- clean:
- rm -rf module* *.symvers $(TARGET).o $(TARGET).mod.c $(TARGET).mod.o $(TARGET).ko $(TARGET).tmp_versions
四 、测试应用程序read
//read.c
- #include <stdio.h>
- #include <stdlib.h>
- #include <errno.h>
- #include <fcntl.h>
- int main(int argc, char *argv[])
- {
- int fd;
- if(argc==1)
- {
- //./test 设备名 是否阻塞读取 是否循环读取
- printf("./test /dev/usr_adc 1 1\n");
- printf("./test dev_name BLOCK LOOP\n");
- return 0;
- }
- //以阻塞方式打开设备文件,非阻塞时flags=O_NONBLOCK
- if(argc==3 && argv[2][0]=='0')
- fd = open(argv[1], O_NONBLOCK|O_RDWR);
- else
- fd = open(argv[1], 0|O_RDWR);
-
- if(fd < 0)
- {
- printf("Open %s Device Faild!\n",argv[1]);
- exit(1);
- }
- else
- {
- printf("open %s ok\n",argv[1]) ;
- }
-
-
- int ret;
- int data;
-
- do
- {
- ret = read(fd, &data, sizeof(data));
- printf("Read %s value is: %d\n", argv[1],data);
- }while(argc==4 && argv[3][0]=='1');
- close(fd);
- return 0;
- }
- //arm-linux-gcc -march=armv4t read.c -o read
- //使用方法 #./read /dev/usr_adc 1 0
阅读(2053) | 评论(1) | 转发(0) |