您的位置:首页 > 运维架构 > Linux

基于fl2440内核linux-3.0移植----添加adc驱动

2014-08-27 08:27 288 查看
一、开发环境

内核版本:linux-3.0

开发板:FL2440(nandflash:K9F1G08 128M)

编译器:arm-linux-gcc 4.3.2

二、原理分析

1. 硬件原理图分析。由原理图得知:处理器S3C2440 提供了8 通道10 位模数转换接口(其中有四个通道用于触摸屏),其微分

线性误差(Differential Linearity Error)可达±1.0 LSB,积分线性误差(Integral LinearityError)可达±2.0 LSB,FL2440 开发板引出其中一路接1 个可调电阻,可做AD 模数转换测试,其硬件电路如下:

三、配置内核

make menuconfig来配置内核,因为我用的内核是linux-3.0版本,其对ADC是默认选项的(不可选择),

System Type --->

-*- ADC common driver support

如果用的内核版本是不可选择的,那个可以直接创建设备节点,然后运行测试程序。如果不是默认选上的话,那么在内核就不要选上,然后编译内核下载到开发板上。

下面是自己写驱动加载的过程:

四、写驱动和测试程序:

编写适合fl2440开发板的adc驱动,文件名称:dev_adc.c(代码在后面),编译生成dev_adc.ko文件,并下载到开发板/目录下面。

编写适合自己驱动的adc测试程序,文件名称:s3c_adc.c(代码在后面),并有gcc工具编译生成可执行文件,并下载到开发板/usr/sbin目录下。

五、加载和测试adc驱动
[root@root /]# ls

apps dev_adc init mnt sys var

bin dev_adc.ko jbs.mp3 proc tmp vim_conf.bin

data etc lib root tslib yw.mp3

dev info linuxrc sbin usr

[root@root /]# insmod dev_adc.ko

[root@root /]# cat proc/devices

Character devices:

1 mem

2 pty

3 ttyp

4 /dev/vc/0

4 tty

5 /dev/tty

5 /dev/console

5 /dev/ptmx

7 vcs

10 misc

13 input

[root@root /]# mknod dev/adc c 10 59

[root@root /]# s3c_adc

adc default channel value : 0

adc channel 0 value : 0

adc channel 1 value : 0

adc channel 2 value : 0

adc channel 3 value : 0

至此adc驱动添加就成功了,注意的是如果内核已经选上了System Type ---> -*- ADC common driver support ,直接创建设备节点就好了,运行测试程序就好。添加驱动会不成功,显示insmod: can't
insert 'dev_adc.ko': invalid parameter,我估计是中断号被内核支持的ADC占用。

以下是驱动和测试程序:

/***************************dev_adc.c***********************************************/

#include <plat/regs-adc.h>

#include <linux/errno.h>

#include <linux/kernel.h>

#include <linux/module.h>

#include <linux/init.h>

#include <linux/input.h>

#include <linux/serio.h>

#include <linux/clk.h>

#include <linux/sched.h>

#include <linux/wait.h>

#include <linux/miscdevice.h>

#include <asm/io.h>

#include <asm/irq.h>

#include <asm/uaccess.h>

static void __iomem *adc_base;

static struct clk *adc_clk;

int dev_id = 1;

DEFINE_MUTEX(ADC_LOCK);

#define DEVICE_NAME "adc"

static DECLARE_WAIT_QUEUE_HEAD(adc_waitq);

static volatile int ev_adc = 0;

static int adc_data;

static void start_adc(void)

{

unsigned int tmp;

tmp = (1 << 14) | (255 << 6) | (0 << 3); /* 0 1 00000011 000 0 0 0 */

writel(tmp, adc_base + S3C2410_ADCCON);

tmp = readl(adc_base + S3C2410_ADCCON);

tmp = tmp | (1 << 0); /* 0 1 00000011 000 0 0 1 */

writel(tmp, adc_base + S3C2410_ADCCON);

}

static irqreturn_t adc_irq(int irq, void *dev_id)

{

if (!ev_adc)

{

adc_data = readl(adc_base + S3C2410_ADCDAT0) & 0x3ff;

ev_adc = 1;

wake_up_interruptible(&adc_waitq);

}

return IRQ_HANDLED;

}

static int adc_open(struct inode *inode, struct file *file)

{

return 0;

}

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

{

mutex_lock(&ADC_LOCK);

if (!ev_adc)

{

if (filp->f_flags & O_NONBLOCK)

{

return -EAGAIN;

}

else

{

start_adc();

wait_event_interruptible(adc_waitq, ev_adc);

}

}

ev_adc = 0;

copy_to_user(buffer, (char *)&adc_data, sizeof(adc_data));

mutex_unlock(&ADC_LOCK);

return sizeof(adc_data);

}

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

{

return 0;

}

static struct file_operations adc_fops = {

.owner = THIS_MODULE,

.open = adc_open,

.read = adc_read,

.release = adc_release,

};

static struct miscdevice adc_miscdev = {

.minor = MISC_DYNAMIC_MINOR,

.name = DEVICE_NAME,

.fops = &adc_fops,

};

static int __init adc_init(void)

{

int ret;

ret = request_irq(IRQ_ADC, adc_irq, IRQF_SHARED, DEVICE_NAME, &dev_id);

{

printk(KERN_ERR "IRQ%d error %d\n", IRQ_ADC, ret);

return -EINVAL;

}

adc_base = ioremap(S3C2410_PA_ADC, 0x20);

if (adc_base == NULL)

{

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

ret = -EINVAL;

goto err_nomap;

}

adc_clk = clk_get(NULL, "adc");

if (!adc_clk)

{

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

goto err_irq;

return -ENOENT;

}

clk_enable(adc_clk);

if (ret)

{

printk(KERN_ERR "cannot register miscdev on minor=%d (%d)\n", MISC_DYNAMIC_MINOR, ret);

goto err_noclk;

}

printk(DEVICE_NAME " initialized!\n");

return 0;

err_nomap:

iounmap(adc_base);

err_noclk:

clk_disable(adc_clk);

clk_put(adc_clk);

err_irq:

free_irq(IRQ_ADC, &dev_id);

return ret;

}

static void __exit adc_exit(void)

{

free_irq(IRQ_ADC, &dev_id);

iounmap(adc_base);

if (adc_clk)

{

clk_disable(adc_clk);

clk_put(adc_clk);

adc_clk = NULL;

}

misc_deregister(&adc_miscdev);

}

module_init(adc_init);

module_exit(adc_exit);

MODULE_LICENSE("GPL");

/**********************s3c_adc.c**********************************/

#include <stdio.h>

#include <stdlib.h>

#include <unistd.h>

#include <sys/ioctl.h>

#include <sys/types.h>

#include <sys/stat.h>

#include <fcntl.h>

#include <sys/select.h>

#include <sys/time.h>

#include <errno.h>

int main(void)

{

int adc_fd;

int adc_value, adc_size;

int i;

adc_fd = open("/dev/adc", O_RDONLY);

if (adc_fd < 0)

{

perror("open device adc");

exit(1);

}

adc_size = read(adc_fd, &adc_value, sizeof(adc_value));

printf("adc default channel value : %d\n", adc_value);

for (i = 0; i < 4; i++)

{

adc_size = read(adc_fd, &adc_value, sizeof(adc_value));

printf("adc channel %d value : %d\n", i, adc_value);

}

close(adc_fd);

return 0;

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: