注册 登录  
 加关注
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

还东国的博客

行之苟有恒,久久自芬芳

 
 
 

日志

 
 

嵌入式开发实战4——内核的编译之十一ADC驱动的修改增加  

2012-06-27 23:16:31|  分类: ARM开发 |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |
 

嵌入式开发实战4——内核的编译之十一ADC驱动的修改增加

 

ADC这个东西比较复杂,原来做电力方面的东西上,采样主要是用他来完成,当然,在这里的东西就不难了,毕竟与实际的产品还是有很大差距的。

主要是增加了mini2440_adc.c 和 s3c24xx-adc.h两个文件到drivers/char/这个目录下,并且修改了目录下的两个工程文件:  Makefile 、 Kconfig。

两个源文件的内容如下:

#include <linux/errno.h>

#include <linux/kernel.h>

#include <linux/module.h>

#include <linux/slab.h>

#include <linux/input.h>

#include <linux/init.h>

 

 

#include <linux/serio.h>

#include <linux/delay.h>

#include <linux/clk.h>

#include <linux/wait.h>

#include <linux/sched.h>

#include <asm/io.h>

#include <asm/irq.h>

#include <asm/uaccess.h>

#include <mach/regs-clock.h>

#include <plat/regs-timer.h>

  

#include <plat/regs-adc.h>

#include <mach/regs-gpio.h>

#include <linux/cdev.h>

#include <linux/miscdevice.h>

 

//自己定义的头文件,因原生内核并没有包含

#include "s3c24xx-adc.h"

 

#undef DEBUG

//#define DEBUG

#ifdef DEBUG

#define DPRINTK(x...) {printk(__FUNCTI ON__"(%d): ",__LINE__);printk(##x);}

#else

#define DPRINTK(x...) (void)(0)

#endif

 

// 定义ADC转换设备名称,将出现在/dev/adc

#define DEVICE_NAME  "adc"

 

static void __iomem *base_addr;

 

// 定义ADC设备结构

typedef struct {

 wait_queue_head_t wait;

 int channel;

 int prescale;

}ADC_DEV;

 

 

 

// 声明全局信号量,以便和触摸屏驱动程序共享A/D 转换器

DECLARE_MUTEX(ADC_LOCK);

//ADC 驱动是否拥有A/D 转换器资源的状态变量

static int OwnADC = 0;

 

static ADC_DEV adcdev;

static volatile int ev_adc = 0;

static int adc_data;

 

static struct clk  *adc_clock;

 

// 定义ADC相关的寄存器

#define ADCCON            (*(volatile unsigned long *)(base_addr + S3C2410_ADCCON))  //ADC control

#define ADCTSC      (*(volatile unsigned long *)(base_addr + S3C2410_ADCTSC)) //ADC  touch  screen

control

#define ADCDLY      (*(volatile unsigned long *)(base_addr + S3C2410_ADCDLY)) //ADC  start  or  Interval

Delay

#define ADCDAT0          (*(volatile unsigned long *)(base_addr + S3C2410_ADCDAT0))  //ADC  conversion

data 0

#define ADCDAT1          (*(volatile unsigned long *)(base_addr + S3C2410_ADCDAT1))  //ADC  conversion

data 1

#define ADCUPDN     (*(volatile unsigned long *)(base_addr + 0x14)) //Stylus Up/Down interrupt status

 

#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)

 

// 定义“开启AD输入”宏,因为比较简单,故没有做成函数

#define START_ADC_AIN(ch, prescale) \

 do{ \

    ADCCON = PRESCALE_EN | PRSCVL(prescale) | ADC_INPUT((ch)) ; \

  ADCCON |= ADC_START; \

 }while(0)

 

//ADC 中断处理函数

static irqreturn_t adcdone_int_handler(int irq, void *dev_id)

 

 

{

     //如果 ADC驱动拥有“A/D 转换器”资源,则从ADC寄存器读取转换结果

 if (OwnADC) {

  adc_data = ADCDAT0 & 0x3ff;

 

  ev_adc = 1;

  wake_up_interruptible(&adcdev.wait);

 }

 

 return IRQ_HANDLED;

}

 

//ADC 读函数,一般对应于用户层/ 应用层的设备读函数(read)

static ssize_t s3c2410_adc_read(struct file *filp, char *buffer, size_t count, loff_t *ppos)

{

 char str[20];

 int value;

 size_t len;

 

     //判断“A/D 转换器”资源是否可用

  if (down_trylock(&ADC_LOCK) == 0) {

  OwnADC = 1; // 标记“A/D 转换器”资源状态为可用

  START_ADC_AIN(adcdev.channel, adcdev.prescale); // 开始转换

  wait_event_interruptible(adcdev.wait, ev_adc); // 通过终端的方式等待转换结果

 

  ev_adc = 0;

 

    DPRINTK("AIN[%d] = 0x%04x, %d\n", adcdev.channel, adc_data, ADCCON & 0x80 ? 1:0);

 

         // 把转换结果赋予 value,以便传递到用户层/ 应用层

  value = adc_data;

 

        // 释放“A/D 转换器”资源

  OwnADC = 0;

  up(&ADC_LOCK);

  } else {

        // 没有“A/D 转换器”资源,赋值为“-1 ”

  value = -1;

 }

 

 

 

  len = sprintf(str, "%d\n", value);

  if (count >= len) {

         // 把转换结果传递到用户层/ 应用层

    int r = copy_to_user(buffer, str, len);

    return r ? r : len;

  } else {

  return -EINVAL;

 }

}

 

//打开ADC设备的函数,一般对应于用户态程序的open

static int s3c2410_adc_open(struct inode *inode, struct file *filp)

{

     //初始化中断队列

 init_waitqueue_head(&(adcdev.wait));

 

     // 缺省通道为“0 ”

 adcdev.channel=0;

 adcdev.prescale=0xff;

 

 DPRINTK( "adc opened\n");

 return 0;

}

 

static int s3c2410_adc_release(struct inode *inode, struct file *filp)

{

  DPRINTK( "adc closed\n");

 return 0;

}

 

 

static struct file_operations dev_fops = {

 owner: THIS_MODULE,

 open: s3c2410_adc_open,

 read:s3c2410_adc_read, 

 release: s3c2410_adc_release,

};

 

 

 

static struct miscdevice misc = {

  .minor = MISC_DYNAMIC_MINOR,

  .name = DEVICE_NAME,

  .fops = &dev_fops,

};

 

static int __init dev_init(void)

{

 int ret;

 

 base_addr=ioremap(S3C2410_PA_ADC,0x20);

  if (base_addr == NULL) {

    printk(KERN_ERR "Failed to remap register block\n");

  return -ENOMEM;

 }

 

  adc_clock = clk_get(NULL, "adc");

  if (!adc_clock) {

    printk(KERN_ERR "failed to get adc clock source\n");

  return -ENOENT;

 }

 clk_enable(adc_clock);

 

 /* normal ADC */

  ADCTSC = 0;

 

    //注册中断

  ret = request_irq(IRQ_ADC, ad cdone_int_handler, IRQF_SHARED, DEVICE_NAME, &adcdev);

  if (ret) {

  iounmap(base_addr);

  return ret;

 }

 

     // 注册设备

  ret = misc_register(&misc);

 

 printk (DEVICE_NAME"\tinitialized\n");

 return ret;

}

 

 

 

static void __exit dev_exit(void)

{

    //释放中断

 free_irq(IRQ_ADC, &adcdev);

 iounmap(base_addr);

 

 if (adc_clock) {

  clk_disable(adc_clock);

  clk_put(adc_clock);

  adc_clock = NULL;

 }

 

 misc_deregister(&misc);

}

 

// 导出信号量“ADC_LOCK”,以便触摸屏驱动使用

EXPORT_SYMBOL(ADC_LOCK);

module_init(dev_init);

module_exit(dev_exit);

MODULE_LICENSE("GPL");

MODULE_AUTHOR("FriendlyARM Inc.");

 

“s3c24xx-adc.h ”,它也在drivers/char

目录下,内容为:

#ifndef _S3C2410_ADC_H_

#define _S3C2410_ADC_H_

 

#define ADC_WRITE(ch, prescal e) ((ch)<<16|(prescale))

 

#define ADC_WRITE_GETCH(data)   (((data)>>16)&0x7)

#define ADC_WRITE_GETPRE(data)  ((data)&0xff)

 

#endif /* _S3C2410_ADC_H_ */

然后打开drivers/char/Makefile 文件,在大概 114 行加入ADC驱动程序目标模块:

obj-$(CONFIG_JS_RTC)            += js-rtc.o

js-rtc-y = rtc.o

 

obj-$(CONFIG_MINI2440_ADC)      += mini2440_adc.o

 

 

 

# Files generated that shall be removed upon make clean

clean-files := consolemap_deftbl.c defkeymap.c

再打开drivers/char/Kconfig 文件,加入ADC驱动配置选项:

config DEVKMEM

        bool "/dev/kmem virtual device support"

        default y

        help

                    Say Y here if you want to support the /dev/kmem device. The

                    /dev/kmem device is rarely used, but can be used for certain

          kind of kernel debugging operations.

          When in doubt, say "N".

 

config MINI2440_ADC

                bool "ADC driver for FriendlyARM Mini2440 development boards"

        depends on MACH_MINI2440

        default y if MACH_MINI2440

        help

          this is ADC driver for FriendlyARM Mini2440 development boards

          Notes: the touch-screen-driver required this option

 

config BFIN_JTAG_COMM

        tristate "Blackfin JTAG Communication"

        depends on BLACKFIN

        help

 

然后make menuconfig:

Device Drivers  --->

  Character devices  --->  在里用空格键选中上面的ADC选项。

下面:make   zImage

出现了几个错误:

In file included from drivers/char/mini2440_adc.c:51:

drivers/char/s3c24xx-adc.h:7:31: error: macro parameters must be comma-separated

drivers/char/mini2440_adc.c:153:37: warning: backslash and newline separated by space

drivers/char/mini2440_adc.c:155: error: expected identifier or '(' before 'do'

drivers/char/mini2440_adc.c:155:6: warning: backslash and newline separated by space

drivers/char/mini2440_adc.c:157:65: warning: backslash and newline separated by space

drivers/char/mini2440_adc.c:159:24: warning: backslash and newline separated by space

drivers/char/mini2440_adc.c:161: error: expected identifier or '(' before 'while'

drivers/char/mini2440_adc.c: In function 'dev_init':

drivers/char/mini2440_adc.c:380: error: 'ad' undeclared (first use in this function)

drivers/char/mini2440_adc.c:380: error: (Each undeclared identifier is reported only once

drivers/char/mini2440_adc.c:380: error: for each function it appears in.)

drivers/char/mini2440_adc.c:380: error: expected ')' before 'cdone_int_handler'

drivers/char/mini2440_adc.c:380: error: too few arguments to function 'request_irq'

错误很明显,一种是拷贝一些代码时产生的莫明的空格,另外一个是空行,引起宏的无法连接。修改就可以了。

再次make  zImage

这里需要说明的有个结构体的赋值问题:

 

    Some operations are not implemented by a driver. For example, a driver that handles a video card won't need to readfrom a directory structure. The corresponding entries in thefile_operations structure should be set toNULL.

    There is a gcc extension that makes assigning to this structure more convenient. You'll see it in modern drivers,and may catch you by surprise. This is what the new way of assigning to the structure looks like:

   
[cpp] view plaincopyprint?

1.  struct file_operations fops = {  
2.        read: device_read,  
3.        write: device_write,  
4.        open: device_open,  
5.        release: device_release  
6.     };  
7.        


       

    However, there's also a C99 way of assigning to elements of a structure, and this is definitely preferred over usingthe GNU extension. The version of gcc I'm currently using,2.95, supports the new C99 syntax. Youshould use this syntax in case someone wants to port your driver. It will help with compatibility:

    
[cpp] view plaincopyprint?

1.  struct file_operations fops = {  
2.         .read = device_read,  
3.         .write = device_write,  
4.         .open = device_open,  
5.         .release = device_release  
6.      };  
7.          

其实说白了上面的那种是GCC自己搞得规矩,下面的是标准的,推荐大家使用后者,以防止代码移植时出现问题

 

夜又深了,还说早回家呢。

  评论这张
 
阅读(1437)| 评论(0)
推荐 转载

历史上的今天

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2017