一、开发环境
内核版本: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.bindata etc lib root tslib yw.mp3dev 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
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
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
", IRQ_ADC, ret);
return -EINVAL;
}
adc_base = ioremap(S3C2410_PA_ADC, 0x20);
if (adc_base == NULL)
{
printk(KERN_ERR "Failed to remap register block
");
ret = -EINVAL;
goto err_nomap;
}
adc_clk = clk_get(NULL, "adc");
if (!adc_clk)
{
printk(KERN_ERR "failed to find adc clock source
");
goto err_irq;
return -ENOENT;
}
clk_enable(adc_clk);
if (ret)
{
printk(KERN_ERR "cannot register miscdev on minor=%d (%d)
", MISC_DYNAMIC_MINOR, ret);
goto err_noclk;
}
printk(DEVICE_NAME " initialized!
");
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
#include
#include
#include
#include
#include
#include
#include
#include
#include
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
", adc_value);
for (i = 0; i < 4; i++)
{
adc_size = read(adc_fd, &adc_value, sizeof(adc_value));
printf("adc channel %d value : %d
", i, adc_value);
}
close(adc_fd);
return 0;
}