gcc version 4.3.3 (Sourcery G++ Lite 2009q1-176)
文件系统: busybox 1.13.1+yaffs
开发板内核版本:linux2.6.32
效果图1 :默认参数
效果图2:自定义参数
一. 代码
//adc.c
- #include <linux/input.h>
- #include <linux/module.h>
- #include <linux/init.h>
- #include <asm/irq.h>
- #include <asm/io.h>
- #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 DEV_NAME "adc_as_input"
- #define DEV_PHYS "usr_adc/input0"
- #define rADCCON (adc_base_addr)
- #define rADCDATA (adc_base_addr +S3C2410_ADCDAT0)
- char *adc_dev_name =DEV_NAME;
- char *msg=NULL;
- char *iok="ok";
- char *ino="no";
- //取消调试信息,则只需要
- #define _DEBUG_ 0
- #if _DEBUG_==1
- #define debug(format,msg...) printk(format,##msg)
- #else
- #define debug(format,msg...) do{}while(0)
- #endif
- static struct input_dev *adc_dev=NULL;
- unsigned int adc_ch =2;
- //这个是通道选择,TQ2440上的AD 电阻接在AIN2上
- unsigned int adc_pre=0xff;
- //这个是预分频,我们设置他默认为0xff
- unsigned int adc_rate =0;
- //默认的分别率,意思时说 数据变化<adc_rate 则不上报
- static struct clk *adc_clk=NULL;
- //时钟,因为AD采样过程是需要时钟参与的,所以需要clk
- static unsigned int volatile value=0;
- //这个保存实时采样的值
- static unsigned int volatile old_value=0;
- //这个保存上一次采样后认定的值,所谓认定的值,是和实时的值相区别的
- static void __iomem * adc_base_addr=NULL;
- //这个,是为了端口 映射,ioremap操作,为啥?因为这是在linux中,有MMU功能的,不能直接操作端口
- /*
- 如下展示了 AD控制字寄存器
- 15 14 13-6 5-3 2 1 0
- ecflg prscen prscvl sel_mux stdbm read_start enable_start
- ecflg: 0 AD转换中,1: ad转换结束
- prscen: :0 关闭 分频,1 :使能分频
- prscvl: 分频的值
- sel_mux: 0-7 依次对应 0-7通道
- stdbm: 0; 标准操作 1: 备用操作
- read_start: 0 关闭 1 使能
- enable_start:0 无操作 1 开始 转换
- ad采样的时钟频率由ADCPSR决定
- 加入CPU时钟频率 66M 完成一次转换至少需要16个时钟周期
- 则采样频率如下
- f=66MHz/(2*(20+1))/16=98.2kHz=10.2us
- 由于 2410内部没有 采样保持电路,
- 因此信号的输入频率最好低于100HZ
- */
- /*
- start_adc:
- 以adc_con_value为控制字,开启ADC设备
- 设置adc_con_value 的函数 为 set_adc
- */
- static unsigned int adc_con_value=0;
- static int start_adc(void)
- {
- //使用256分频|使用AIN2|使能ADC
- adc_con_value |= S3C2410_ADCCON_ENABLE_START;
- writel(adc_con_value,(rADCCON));
- debug("in %s,writel adc_con_value=%02x--------->[ok]\n",__func__,adc_con_value);
- return 0;
- };
- /*
- set_adc(int ch,int prescale)
- 设置adc_con_value;
- ch:通道
- prescale:分频
- */
- static int set_adc(int ch,int prescale)
- {
- //使用adc_pre分频|使用adc_ch|使能ADC
- adc_con_value=0;
-
- //0111,1111,1101,0001
- adc_con_value |= S3C2410_ADCCON_PRSCEN |S3C2410_ADCCON_PRSCVL(prescale)|S3C2410_ADCCON_SELMUX(ch);
- debug("in %s,write temp=%02x--------->[ing]\n",__func__,adc_con_value);
- writel(adc_con_value,(rADCCON));
- debug("in %s,writel temp=%02x--------->[ok]\n",__func__,adc_con_value);
- return 0;
- }
- /*
- run_adc:
- 以ch,prescale参数运行set_adc
- 并且启动adc
- */
- static int run_adc(int ch,int prescale)
- {
- set_adc(ch,prescale) ;
- start_adc();
- return 0;
- }
- /*
- irq_proc:中断处理函数
- 流程是
- 首先读取一次DATA0寄存器的值,存为value
- 接着与上一次保存的值(old_value)比较,
- if
- 二者之间的差值在分别率 adc_rate内
- 则上报EV_ABS事件,
- 事件的值ev_var由3部分组成
- 实时adc的值(0-9)
- ad通道adc_ch的值(10-12)
- 分别率的值adc_rate(13-15),这个是我们自己添加的,意味着最大值为00000111=7
- 分频值adc_pre(16-31)//,
- 保存value到old_value
- end if
- 最后再次start adc
- 注意这里我们没有采用中断的底半部机制,是有待提高的
- 具体可以见按键驱动
- */
- static irqreturn_t irq_proc(int irq, void *dummy)
- {
- value=readl(rADCDATA)&0x3ff;
- if(((old_value -value) >=adc_rate)||((value-old_value -value ) >=adc_rate))
- {
- value |= (adc_ch & 0x0f)<<10;
- value |=(adc_rate &0x0f)<<13;
- value |= (adc_pre & 0xff) << 16;
- input_report_abs(adc_dev, ABS_X,value);
- //AB_X代表的是 (value &0x03ff) | (ch << 10) | adc_rate <<13 |(pre <<16)
- debug("in %s,value=%02x\n",__func__,value);
- input_sync(adc_dev);
- old_value=value;
- }
-
- start_adc();
-
- return IRQ_RETVAL(IRQ_HANDLED);
- }
- /*
- 这个是回调事件处理函数,作用是什么呢?
- 一般来说我们感兴趣的信息方向是这样的
- 用户程序<--....设备,即读取设备信息,比如读取按键信息,则按键信息主动上报给用户
- 但是有些设备,我们需要给他一个信息,利用这个信息操作硬件,比如我们想给她一个LED控制信息
- 让这个信息控制LED,就要用到这个函数了
- type=EV_LED,
- code=LED_NUML| LED_CAPSL|LED_SCROLLL| LED_COMPOSE|LED_KANA中的一个
- var =xy这个是我们感兴趣的
- //pre,rate,ch,var
- //16,3,3,10
- */
- int adc_event(struct input_dev *dev, unsigned int type, unsigned int code, int var)
- {
- if (type == EV_LED)
- {
- //AB_X代表的是 (value &0x03ff) | (ch << 10) | adc_rate <<13 |(pre <<16)
- adc_ch=(var >>10) &0x07;
- adc_rate =(var >>13) & 0x07;
- adc_pre=((unsigned int)var >>16) & 0xffff;
- if(adc_pre==0)
- adc_pre=1;
- debug("in %s->%s():%02x,%02x,%02x,%02x\n",__FILE__,__func__,adc_pre,adc_rate,adc_ch,var & 0x3ff);
- return 0;
- }
- return -1;
- }
- /*
- adcinit过程如下
- 1. input_allocate_device
- 2. ioremap
- 3. irq:request_irq
- 4. clk:clk_get,clk_enable
- 5. input_register_device
- 6. run_adc
- 其中 必须先进行ioremap,再进行request_irq,否则会出现oops错误
- */
- static int __init adc_init(void)
- {
- int ret=0;
- //申请 内存
- adc_dev = input_allocate_device();
- debug("%s: input_allocate_device() adc_dev=%p\n" ,__FILE__,adc_dev);
- if (adc_dev==NULL) {
- printk(KERN_ERR "%s: Not enough memory\n" ,__FILE__);
- return -ENOMEM;
-
- }
- debug("%s: input_allocate_device() adc_dev=%p\n" ,__FILE__,adc_dev);
- //ioremap操作
- adc_base_addr=ioremap(S3C2410_PA_ADC,0x20);
- debug("in %s,ioremap VIR_adc_base_addr=%p--------->[%s]\n",__func__,adc_base_addr,adc_base_addr==NULL?ino:iok);
- if(adc_base_addr==NULL)
- {
- printk(KERN_ERR "%s:%s Can't ioremap %d\n", __FILE__,__func__,S3C2410_PA_ADC);
- ret =-EIO;
- goto err_free_dev;
- }
-
- //free_irq最后一个参数必须和request_irq最后一个参数一致,都为adc_dev
- if (request_irq(IRQ_ADC,irq_proc, IRQF_SHARED|IRQF_SAMPLE_RANDOM, adc_dev_name, adc_dev)) {
- printk(KERN_ERR "%s:%s: Can't allocate irq %d\n", __FILE__,__func__,IRQ_ADC);
- ret = -EIO;
- goto err_free_map;
- }
-
- debug("%s: request_irq()\n" ,__FILE__);
-
- //clk操作
- adc_clk = clk_get(NULL,"adc");
- if(adc_clk==NULL)
- {
- printk(KERN_ERR "%s:%s: Failed to clk_get\n",__FILE__,__func__);
- ret =-ENOENT;
- goto err_free_irq;
- };
- clk_enable(adc_clk);
- //adc_dev相关的设置
- adc_dev->name=DEV_NAME;
- adc_dev->phys=DEV_PHYS;
- adc_dev->id.bustype=BUS_VIRTUAL;
-
- adc_dev->id.vendor=0x0001;
- adc_dev->id.product=0x0001;
- adc_dev->id.version=0x0001;
-
- adc_dev->event=adc_event;
- //设置事件支持
- set_bit(EV_ABS ,adc_dev->evbit);
- set_bit(EV_LED,adc_dev->evbit);
- adc_dev->ledbit[0]=(LED_NUML| LED_CAPSL|LED_SCROLLL| LED_COMPOSE|LED_KANA);
-
- input_set_abs_params(adc_dev, ABS_X, 0, 0xFFFFFFFF, 1, 0);
- ////AB_X代表的是 (value &0x0fff) | (ch << 16) | adc_rate <<20 |(pre <<24)
- debug("%s:%s input_register_device()\n" ,__FILE__,__func__);
- ret = input_register_device(adc_dev);
- if (ret) {
- printk(KERN_ERR "%s:%s: Failed to register device\n",__FILE__,__func__);
- goto err_free_irq;
- }
- //最后 run_adc
- run_adc(adc_ch,adc_pre);
- printk("%s->%s(),%s has init!\nadc_ch=0x%02x,adc_rate=0x%02x,adc_pre=0x%02x.\n",__FILE__,__func__,DEV_NAME,adc_ch,adc_rate,adc_pre);
- return 0;
- err_free_irq:
- //Trying to free already-free IRQ 4
- // free_irq最后一个参数必须和request_irq最后一个参数一致
- //否则出现 Trying to free already-free IRQ 80错误
-
- free_irq(IRQ_ADC, adc_dev);
-
- err_free_map:
- if(adc_base_addr!=NULL) iounmap(adc_base_addr);
- err_free_dev:
-
- input_free_device(adc_dev);
-
- return ret;
- }
- /*
- adc_exit过程
- 1. input_unregister_device
- 2. free_irq(IRQ_ADC, adc_dev);
- 3. iounmap
- 4. clk_disable;clk_put
- 其中必须先free_irq再进行iounmap
- */
- static void __exit adc_exit(void)
- {
-
- if(adc_dev!=NULL)
- {
- input_unregister_device(adc_dev);
- //Trying to free already-free IRQ 4
- // free_irq最后一个参数必须和request_irq最后一个参数一致
- //否则出现 Trying to free already-free IRQ 80错误
- free_irq(IRQ_ADC, adc_dev);
-
- }
- if(adc_base_addr!=NULL) iounmap(adc_base_addr);
- if(adc_clk!=NULL)
- {
- clk_disable(adc_clk);
- clk_put(adc_clk);
- adc_clk=NULL;
- }
- printk("%s->%s(),%s has exit!\n",__FILE__,__func__,DEV_NAME);
- }
- /*
- 参考
- http://blog.csdn.net/ralph_sqd/article/details/5933388
- http://blog.csdn.net/wangyunqian6/article/details/6563921
- */
- module_param(adc_ch,int,0644);
- MODULE_PARM_DESC(adc_ch,"set ad channel");
- module_param(adc_pre,int,0644);
- MODULE_PARM_DESC(adc_pre,"set adc prescale");
- module_param(adc_rate,int,0644);
- MODULE_PARM_DESC(adc_rate,"set adc's distinguishability");
- MODULE_DESCRIPTION("driver adc for TQ2440");
- MODULE_AUTHOR("keytounix");
- MODULE_LICENSE("GPL");
- module_init(adc_init);
- module_exit(adc_exit);
二. 测试文件
- //test.c
- #include <string.h>
- #include <stdio.h>
- #include <sys/types.h>
- #include <sys/stat.h>
- #include <fcntl.h>
- #include <linux/input.h>
- #include <linux/uinput.h>
- #include <stdio.h>
- #include <sys/time.h>
- #include <sys/types.h>
- #include <unistd.h>
- /*
- #define EV_SYN 0x00
-
- #define EV_KEY 0x01 //按键
-
- #define EV_REL 0x02 //相对坐标(轨迹球)
-
- #define EV_ABS 0x03 //绝对坐标
-
- #define EV_MSC 0x04 //其他
-
- #define EV_SW 0x05
-
- #define EV_LED 0x11 //LED
-
- #define EV_SND 0x12//声音
-
- #define EV_REP 0x14//repeat
-
- #define EV_FF 0x15
-
- #define EV_PWR 0x16
-
- #define EV_FF_STATUS 0x17
-
- #define EV_MAX 0x1f
-
- #define EV_CNT (EV_MAX+1)
- struct input_event {
-
- struct timeval time; //按键时间
-
- __u16 type; //类型,在下面有定义
-
- __u16 code; //要模拟成什么按键
-
- __s32 value;//是按下还是释放
-
- };
- */
- #ifdef DEBUG
- #define debug(format,msg...) printf(format,##msg)
- #else
- #define debug(format,msg...) do{}while(0)
- #endif
- #define EVEN_NAME "/dev/event0"
- int main(int argn,char * argv[])
- {
- int fd;
- fd_set rfds;
- struct timeval tv;
- struct input_event event;
- unsigned int var=0;
- fd = open(EVEN_NAME,O_RDWR|O_NONBLOCK);
- if(fd<=0){
- printf("error open %s\n",EVEN_NAME);
- return -1;
- }
- int i = 1;
- while(1)
- {
- //轮寻 直到 fd 可读 或者 5s超时时间
- FD_ZERO(&rfds);
- FD_SET(fd, &rfds);
- tv.tv_sec = 5;
- tv.tv_usec = 0;
- select(fd+1, &rfds, NULL, NULL, &tv);
-
- /*AB_X事件格式
- (value &0x0fff) | (ch << 16) | adc_rate <<20 |(pre <<24)
- pre,rate,ch,var
- 16,3,3,10
- */
- read(fd,&event,sizeof(struct input_event)) ;
- if(event.type==EV_ABS)
- {
- var=event.value;
- debug("in %s->%s():%02x,%02x,%02x,%02x\n",__FILE__,__func__,(var>>16)&0xffff,(var>>13)&0x07,(var>>10)&0x07,var & 0x03ff);
- }
- sleep(1);
-
- /*
- 如下 是 操作 LED事件的
- */
- if(event.type!=EV_SYN)
- {
- event.type = EV_LED;
- i++;
- i%=0xff;
- event.value = event.value &(~0x3ff);
- event.code = 0;
- gettimeofday(&event.time,0);
- write(fd,&event,sizeof(event));
- }
- }
- close(fd);
- }
三. Makefile
A=arm-linux-ar
ARCH=arm
CC=arm-linux-gcc
TESTFILE=test.c
MYFLAGS= -DDEBUG -g
obj-m:=adc.o
KDIR=/opt/linux/kernel/linux-2.6.32
all:
$(MAKE) -C $(KDIR) M=$(PWD) modules
$(CC) $(TESTFILE) $(MYFLAGS) -o test
clean:
rm -f *.mod.c *.mod.o *.ko *.o *.tmp_versions Module.symvers modules.order test
阅读(2791) | 评论(0) | 转发(0) |