免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 1451 | 回复: 0
打印 上一主题 下一主题

输入子系统具体应用---ADC驱动 [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2011-12-23 01:19 |只看该作者 |倒序浏览
gcc version 4.3.3 (Sourcery G++ Lite 2009q1-176)
文件系统: busybox 1.13.1+yaffs
开发板内核版本:linux2.6.32
效果图1 :默认参数
效果图2:自定义参数
 
一.  代码
//adc.c
  1. #include <linux/input.h>
  2. #include <linux/module.h>
  3. #include <linux/init.h>
  4. #include <asm/irq.h>
  5. #include <asm/io.h>
  6. #include <linux/module.h>
  7. #include <linux/kernel.h>
  8. #include <linux/init.h>
  9. #include <linux/sched.h>
  10. #include <linux/poll.h>
  11. #include <linux/timer.h>
  12. #include <linux/irq.h>
  13. #include <linux/cdev.h>
  14. #include <linux/clk.h>
  15. #include <linux/wait.h>//waitqueue
  16. #include <linux/errno.h>
  17. #include <linux/interrupt.h>//irq
  18. #include <linux/io.h>
  19. #include <linux/types.h>
  20. #include <linux/delay.h>
  21. #include <linux/semaphore.h>//sem
  22. #include <linux/fs.h>
  23. #include <linux/platform_device.h>//
  24. #include <linux/miscdevice.h>
  25. #include <linux/device.h>
  26. #include <linux/workqueue.h>//workqueue
  27. #include <asm/uaccess.h>
  28. #include <asm/irq.h>
  29. //soc相关
  30. #include <mach/gpio.h>
  31. #include <mach/regs-clock.h>
  32. #include <mach/regs-gpio.h>
  33. #include <mach/hardware.h>
  34. #include <mach/irqs.h>
  35. //IRQ_ADC

  36. #include <plat/regs-adc.h>
  37. //这个头文件,包含了ADC

  38. #include <plat/map.h>
  39. //取消调试信息,则只需要#define _DEBUG_ 0
  40. #define DEV_NAME "adc_as_input"
  41. #define DEV_PHYS "usr_adc/input0"
  42. #define rADCCON (adc_base_addr)
  43. #define rADCDATA (adc_base_addr +S3C2410_ADCDAT0)
  44. char *adc_dev_name =DEV_NAME;
  45. char *msg=NULL;
  46. char *iok="ok";
  47. char *ino="no";

  48. //取消调试信息,则只需要
  49. #define _DEBUG_ 0
  50. #if _DEBUG_==1
  51. #define debug(format,msg...) printk(format,##msg)
  52. #else
  53. #define debug(format,msg...) do{}while(0)
  54. #endif

  55. static struct input_dev *adc_dev=NULL;
  56. unsigned int adc_ch =2;
  57. //这个是通道选择,TQ2440上的AD 电阻接在AIN2上
  58. unsigned int adc_pre=0xff;
  59. //这个是预分频,我们设置他默认为0xff
  60. unsigned int adc_rate =0;
  61. //默认的分别率,意思时说 数据变化<adc_rate 则不上报
  62. static struct clk *adc_clk=NULL;
  63. //时钟,因为AD采样过程是需要时钟参与的,所以需要clk
  64. static unsigned int volatile value=0;
  65. //这个保存实时采样的值
  66. static unsigned int volatile old_value=0;
  67. //这个保存上一次采样后认定的值,所谓认定的值,是和实时的值相区别的
  68. static void __iomem * adc_base_addr=NULL;
  69. //这个,是为了端口 映射,ioremap操作,为啥?因为这是在linux中,有MMU功能的,不能直接操作端口

  70. /*
  71. 如下展示了 AD控制字寄存器
  72. 15 14 13-6 5-3 2 1 0
  73. ecflg prscen prscvl sel_mux stdbm read_start enable_start

  74. ecflg: 0 AD转换中,1: ad转换结束
  75. prscen: :0 关闭 分频,1 :使能分频
  76. prscvl: 分频的值
  77. sel_mux: 0-7 依次对应 0-7通道
  78. stdbm: 0; 标准操作 1: 备用操作
  79. read_start: 0 关闭 1 使能
  80. enable_start:0 无操作 1 开始 转换
  81. ad采样的时钟频率由ADCPSR决定
  82. 加入CPU时钟频率 66M 完成一次转换至少需要16个时钟周期
  83. 则采样频率如下
  84.     f=66MHz/(2*(20+1))/16=98.2kHz=10.2us
  85. 由于 2410内部没有 采样保持电路,
  86. 因此信号的输入频率最好低于100HZ
  87. */
  88. /*
  89. start_adc:
  90. 以adc_con_value为控制字,开启ADC设备
  91. 设置adc_con_value 的函数 为 set_adc
  92. */
  93. static unsigned int adc_con_value=0;
  94. static int start_adc(void)
  95. {
  96.     //使用256分频|使用AIN2|使能ADC
  97.     adc_con_value |= S3C2410_ADCCON_ENABLE_START;
  98.     writel(adc_con_value,(rADCCON));
  99.     debug("in %s,writel adc_con_value=%02x--------->[ok]\n",__func__,adc_con_value);
  100.     return 0;
  101. };
  102. /*
  103. set_adc(int ch,int prescale)
  104. 设置adc_con_value;
  105. ch:通道
  106. prescale:分频
  107. */
  108. static int set_adc(int ch,int prescale)
  109. {
  110.     //使用adc_pre分频|使用adc_ch|使能ADC
  111.     adc_con_value=0;
  112.     
  113.     //0111,1111,1101,0001
  114.     adc_con_value |= S3C2410_ADCCON_PRSCEN |S3C2410_ADCCON_PRSCVL(prescale)|S3C2410_ADCCON_SELMUX(ch);
  115.        debug("in %s,write temp=%02x--------->[ing]\n",__func__,adc_con_value);
  116.     writel(adc_con_value,(rADCCON));
  117.     debug("in %s,writel temp=%02x--------->[ok]\n",__func__,adc_con_value);

  118.     return 0;
  119. }
  120. /*
  121. run_adc:
  122. 以ch,prescale参数运行set_adc
  123. 并且启动adc
  124. */
  125. static int run_adc(int ch,int prescale)
  126. {
  127.     set_adc(ch,prescale) ;
  128.     start_adc();
  129.     return 0;
  130. }

  131. /*
  132. irq_proc:中断处理函数
  133. 流程是
  134. 首先读取一次DATA0寄存器的值,存为value
  135. 接着与上一次保存的值(old_value)比较,
  136. if
  137. 二者之间的差值在分别率 adc_rate内
  138. 则上报EV_ABS事件,
  139. 事件的值ev_var由3部分组成
  140. 实时adc的值(0-9)
  141. ad通道adc_ch的值(10-12)
  142. 分别率的值adc_rate(13-15),这个是我们自己添加的,意味着最大值为00000111=7
  143. 分频值adc_pre(16-31)//,
  144. 保存value到old_value
  145. end if
  146. 最后再次start adc
  147. 注意这里我们没有采用中断的底半部机制,是有待提高的
  148. 具体可以见按键驱动
  149. */
  150. static irqreturn_t irq_proc(int irq, void *dummy)
  151. {
  152.          value=readl(rADCDATA)&0x3ff;
  153.     if(((old_value -value) >=adc_rate)||((value-old_value -value ) >=adc_rate))
  154.      {
  155.         value |= (adc_ch & 0x0f)<<10;
  156.         value |=(adc_rate &0x0f)<<13;
  157.         value |= (adc_pre & 0xff) << 16;
  158.         input_report_abs(adc_dev, ABS_X,value);
  159.         //AB_X代表的是 (value &0x03ff) | (ch << 10) | adc_rate <<13 |(pre <<16)
  160.         debug("in %s,value=%02x\n",__func__,value);
  161.         input_sync(adc_dev);
  162.         old_value=value;
  163.      }
  164.         
  165.     start_adc();
  166.     
  167.      return IRQ_RETVAL(IRQ_HANDLED);
  168. }

  169. /*
  170. 这个是回调事件处理函数,作用是什么呢?
  171. 一般来说我们感兴趣的信息方向是这样的
  172. 用户程序<--....设备,即读取设备信息,比如读取按键信息,则按键信息主动上报给用户
  173. 但是有些设备,我们需要给他一个信息,利用这个信息操作硬件,比如我们想给她一个LED控制信息
  174. 让这个信息控制LED,就要用到这个函数了
  175. type=EV_LED,
  176. code=LED_NUML| LED_CAPSL|LED_SCROLLL| LED_COMPOSE|LED_KANA中的一个
  177. var =xy这个是我们感兴趣的
  178. //pre,rate,ch,var
  179. //16,3,3,10
  180. */
  181. int adc_event(struct input_dev *dev, unsigned int type, unsigned int code, int var)
  182. {
  183.     if (type == EV_LED)
  184.     {
  185.         //AB_X代表的是 (value &0x03ff) | (ch << 10) | adc_rate <<13 |(pre <<16)
  186.         adc_ch=(var >>10) &0x07;
  187.         adc_rate =(var >>13) & 0x07;
  188.         adc_pre=((unsigned int)var >>16) & 0xffff;
  189.         if(adc_pre==0)
  190.             adc_pre=1;
  191.         debug("in %s->%s():%02x,%02x,%02x,%02x\n",__FILE__,__func__,adc_pre,adc_rate,adc_ch,var & 0x3ff);
  192.         return 0;
  193.     }
  194.     return -1;
  195. }


  196. /*
  197. adcinit过程如下
  198. 1. input_allocate_device
  199. 2. ioremap
  200. 3. irq:request_irq
  201. 4. clk:clk_get,clk_enable
  202. 5. input_register_device
  203. 6. run_adc
  204. 其中 必须先进行ioremap,再进行request_irq,否则会出现oops错误
  205. */
  206. static int __init adc_init(void)
  207. {
  208.     int ret=0;
  209. //申请 内存
  210.     adc_dev = input_allocate_device();
  211.     debug("%s: input_allocate_device() adc_dev=%p\n" ,__FILE__,adc_dev);
  212.     if (adc_dev==NULL) {
  213.         printk(KERN_ERR "%s: Not enough memory\n" ,__FILE__);
  214.         return -ENOMEM;
  215.         
  216.     }
  217.     debug("%s: input_allocate_device() adc_dev=%p\n" ,__FILE__,adc_dev);
  218. //ioremap操作
  219.     adc_base_addr=ioremap(S3C2410_PA_ADC,0x20);
  220.     debug("in %s,ioremap VIR_adc_base_addr=%p--------->[%s]\n",__func__,adc_base_addr,adc_base_addr==NULL?ino:iok);
  221.     if(adc_base_addr==NULL)
  222.         {
  223.             printk(KERN_ERR "%s:%s Can't ioremap %d\n", __FILE__,__func__,S3C2410_PA_ADC);
  224.             ret =-EIO;
  225.             goto err_free_dev;
  226.         }
  227.     
  228. //free_irq最后一个参数必须和request_irq最后一个参数一致,都为adc_dev
  229.     if (request_irq(IRQ_ADC,irq_proc, IRQF_SHARED|IRQF_SAMPLE_RANDOM, adc_dev_name, adc_dev)) {
  230.                 printk(KERN_ERR "%s:%s: Can't allocate irq %d\n", __FILE__,__func__,IRQ_ADC);
  231.                   ret = -EIO;
  232.             goto err_free_map;
  233.         }
  234.     
  235.     debug("%s: request_irq()\n" ,__FILE__);
  236.        
  237. //clk操作
  238.     adc_clk = clk_get(NULL,"adc");

  239.     if(adc_clk==NULL)
  240.         {
  241.         printk(KERN_ERR "%s:%s: Failed to clk_get\n",__FILE__,__func__);
  242.             ret =-ENOENT;
  243.             goto err_free_irq;
  244.         };
  245.     clk_enable(adc_clk);

  246. //adc_dev相关的设置
  247.     adc_dev->name=DEV_NAME;
  248.     adc_dev->phys=DEV_PHYS;
  249.     adc_dev->id.bustype=BUS_VIRTUAL;
  250.     
  251.     adc_dev->id.vendor=0x0001;
  252.     adc_dev->id.product=0x0001;
  253.     adc_dev->id.version=0x0001;
  254.     
  255.     adc_dev->event=adc_event;
  256. //设置事件支持
  257.         set_bit(EV_ABS ,adc_dev->evbit);
  258.     set_bit(EV_LED,adc_dev->evbit);
  259.     adc_dev->ledbit[0]=(LED_NUML| LED_CAPSL|LED_SCROLLL| LED_COMPOSE|LED_KANA);
  260.     
  261.     input_set_abs_params(adc_dev, ABS_X, 0, 0xFFFFFFFF, 1, 0);
  262.     ////AB_X代表的是 (value &0x0fff) | (ch << 16) | adc_rate <<20 |(pre <<24)
  263.     debug("%s:%s input_register_device()\n" ,__FILE__,__func__);
  264.        ret = input_register_device(adc_dev);
  265.     if (ret) {
  266.         printk(KERN_ERR "%s:%s: Failed to register device\n",__FILE__,__func__);
  267.         goto err_free_irq;
  268.     }
  269. //最后 run_adc
  270.     run_adc(adc_ch,adc_pre);
  271.     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);
  272.        return 0;
  273. err_free_irq:
  274.         //Trying to free already-free IRQ 4
  275.         // free_irq最后一个参数必须和request_irq最后一个参数一致
  276.         //否则出现 Trying to free already-free IRQ 80错误
  277.     
  278.     free_irq(IRQ_ADC, adc_dev);
  279.         
  280. err_free_map:
  281.      if(adc_base_addr!=NULL) iounmap(adc_base_addr);
  282. err_free_dev:
  283.     
  284.     input_free_device(adc_dev);
  285.     
  286.     return ret;
  287. }


  288. /*
  289. adc_exit过程
  290. 1. input_unregister_device
  291. 2. free_irq(IRQ_ADC, adc_dev);
  292. 3. iounmap
  293. 4. clk_disable;clk_put
  294. 其中必须先free_irq再进行iounmap
  295. */
  296. static void __exit adc_exit(void)
  297. {
  298.         
  299.     if(adc_dev!=NULL)
  300.     {
  301.         input_unregister_device(adc_dev);
  302.         //Trying to free already-free IRQ 4
  303.         // free_irq最后一个参数必须和request_irq最后一个参数一致
  304.         //否则出现 Trying to free already-free IRQ 80错误
  305.         free_irq(IRQ_ADC, adc_dev);
  306.         
  307.     }
  308.     if(adc_base_addr!=NULL) iounmap(adc_base_addr);
  309.     if(adc_clk!=NULL)
  310.     {
  311.             clk_disable(adc_clk);
  312.             clk_put(adc_clk);
  313.             adc_clk=NULL;
  314.     }
  315.     printk("%s->%s(),%s has exit!\n",__FILE__,__func__,DEV_NAME);
  316. }
  317. /*
  318. 参考
  319. http://blog.csdn.net/ralph_sqd/article/details/5933388
  320. http://blog.csdn.net/wangyunqian6/article/details/6563921
  321. */
  322. module_param(adc_ch,int,0644);
  323. MODULE_PARM_DESC(adc_ch,"set ad channel");

  324. module_param(adc_pre,int,0644);
  325. MODULE_PARM_DESC(adc_pre,"set adc prescale");

  326. module_param(adc_rate,int,0644);
  327. MODULE_PARM_DESC(adc_rate,"set adc's distinguishability");

  328. MODULE_DESCRIPTION("driver adc for TQ2440");

  329. MODULE_AUTHOR("keytounix");
  330. MODULE_LICENSE("GPL");
  331. module_init(adc_init);
  332. module_exit(adc_exit);
二.  测试文件
  1. //test.c
  2. #include <string.h>
  3. #include <stdio.h>
  4. #include <sys/types.h>
  5. #include <sys/stat.h>
  6. #include <fcntl.h>
  7. #include <linux/input.h>
  8. #include <linux/uinput.h>
  9. #include <stdio.h>
  10. #include <sys/time.h>
  11. #include <sys/types.h>
  12. #include <unistd.h>
  13. /*
  14. #define EV_SYN 0x00
  15.   
  16. #define EV_KEY 0x01 //按键
  17.   
  18. #define EV_REL 0x02 //相对坐标(轨迹球)
  19.   
  20. #define EV_ABS 0x03 //绝对坐标
  21.   
  22. #define EV_MSC 0x04 //其他
  23.   
  24. #define EV_SW 0x05
  25.   
  26. #define EV_LED 0x11 //LED
  27.   
  28. #define EV_SND 0x12//声音
  29.   
  30. #define EV_REP 0x14//repeat
  31.   
  32. #define EV_FF 0x15
  33.   
  34. #define EV_PWR 0x16
  35.   
  36. #define EV_FF_STATUS 0x17
  37.   
  38. #define EV_MAX 0x1f
  39.   
  40. #define EV_CNT (EV_MAX+1)
  41. struct input_event {
  42.   
  43. struct timeval time; //按键时间
  44.   
  45. __u16 type; //类型,在下面有定义
  46.   
  47. __u16 code; //要模拟成什么按键
  48.   
  49. __s32 value;//是按下还是释放
  50.   
  51. };
  52. */
  53. #ifdef DEBUG
  54. #define debug(format,msg...) printf(format,##msg)
  55. #else
  56. #define debug(format,msg...) do{}while(0)
  57. #endif
  58. #define EVEN_NAME "/dev/event0"

  59. int main(int argn,char * argv[])
  60. {
  61.     int fd;
  62.     fd_set rfds;
  63.     struct timeval tv;
  64.     struct input_event event;
  65.     unsigned int var=0;
  66.     fd = open(EVEN_NAME,O_RDWR|O_NONBLOCK);
  67.     if(fd<=0){
  68.          printf("error open %s\n",EVEN_NAME);
  69.          return -1;
  70.          }
  71.     int i = 1;
  72.     while(1)
  73.          {
  74.     //轮寻 直到 fd 可读 或者 5s超时时间

  75.     FD_ZERO(&rfds);
  76.     FD_SET(fd, &rfds);
  77.     tv.tv_sec = 5;
  78.     tv.tv_usec = 0;
  79.     select(fd+1, &rfds, NULL, NULL, &tv);
  80.          
  81.     /*AB_X事件格式
  82.     (value &0x0fff) | (ch << 16) | adc_rate <<20 |(pre <<24)
  83.     pre,rate,ch,var
  84.     16,3,3,10
  85.     */
  86.     read(fd,&event,sizeof(struct input_event)) ;
  87.     if(event.type==EV_ABS)
  88.     {
  89.     var=event.value;
  90.      debug("in %s->%s():%02x,%02x,%02x,%02x\n",__FILE__,__func__,(var>>16)&0xffff,(var>>13)&0x07,(var>>10)&0x07,var & 0x03ff);
  91.     }
  92.     sleep(1);
  93.     
  94.     /*
  95.         如下 是 操作 LED事件的
  96.     */
  97. if(event.type!=EV_SYN)
  98.     {
  99.     event.type = EV_LED;
  100.     i++;
  101.     i%=0xff;
  102.     event.value = event.value &(~0x3ff);
  103.     event.code = 0;
  104.     gettimeofday(&event.time,0);
  105.     write(fd,&event,sizeof(event));
  106.     }
  107. }
  108.     close(fd);
  109. }
三.  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
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP