本实验编写ADC驱动程序,并加到内核中,对内核进行配置和编译,生成内核镜像文件后烧写到开发板,在开发板运行测试驱动程序。前提需要掌握ADC驱动的程序框架,熟悉掌握设备驱动中并发控制机制并知道如何将驱动静态添加到内核;
一、实验设备与软件环境
1、操作系统:Linux虚拟机、Red Hat Enterprise Linux 6.3。
2、硬件平台:FriendlyARM开发板mini2440。
3、软件:SecureCRT 8.1。
4、内核版本:linux 2.6.32.2。
二、操作的具体流程
- 编写ADC驱动程序的源文件adc.c
(1)在/opt /shiyan目录下创建lesson_adc文件夹
①在/opt/shiyan目录下右击鼠标打开终端;
②输入“mkdir lesson_adc”,创建一个名为lesson_adc的文件夹;
(2)输入“cd lesson_adc”命令,进入lesson_adc目录下
(3)输入gedit adc.c
(4)编写adc.c代码(源代码见附录1)
2. 拷贝驱动代码与修改文件
(1)拷贝:把/opt /shiyan/lesson_adc/目录下的驱动代码adc.c拷到/opt/FriendlyARM/mini2440/linux-2.6.32.2/drivers/char路径下
(2)修改/opt/FriendlyARM/mini2440/linux-2.6.32.2/drivers/char目录下的Kconfig文件和Makefile文件
①修改Kconfig文件(左图为修改前,右图为修改后)
②修改Makefile文件(左图为修改前,右图为修改后)
3.回到内核目录,在命令行执行make menuconfig,找到刚刚添加的ADC驱动:Devices Driver → Character Devices→ ADC,空格选上并保存退出
4.执行make zImage,生成内核镜像文件zImage:
5.将镜像文件zImage烧写到开发板上;
6.编辑编译应用程序(源代码见附录2)
(1)在/opt /shiyan/lesson_adc目录下编辑应用程序adc_test.c
(2)在/opt /shiyan/lesson_adc目录下执行arm-linux-gcc adc_test.c -o adc_test命令生成adc_test文件
7.打开secureCRT,接上mini2440开发板,输入“rz”命令添加zImage和adc_test文件
8.将测试程序和镜像文件传输到开发板后,修改文件权限后输入“adc-test”运行,旋转开发板上的W1可调电阻(蓝色按钮),运行结果如下:
三、操作异常问题与解决方案
异常问题:在secureCRT上运行时输入./adc_test,显示“Open ADC Device Faild”。
解决方案:将“./adc_test”改为“adc-test”即可
四、实验总结
在实验中,编写ADC驱动程序,并加到内核中,对内核进行配置和编译,生成内核镜像文件后烧写到开发板,在开发板运行测试驱动程序,最终结果是在secureCRT上输入“adc-test”,旋转开发板上的W1可调电阻(蓝色按钮)会出现不同的数值,顺时针旋转数值变大,反之数值变小。
五、附录
附录1
#include <plat/nand.h>
#include <linux/miscdevice.h>
#include <asm/irq.h>
#include <mach/hardware.h>
#include <linux/kernel.h>
#include <mach/regs-gpio.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/types.h>
#include <linux/delay.h>
#include <linux/moduleparam.h>
#include <linux/slab.h>
#include <linux/errno.h>
#include <linux/ioctl.h>
#include <linux/cdev.h>
#include <linux/string.h>
#include <linux/list.h>
#include <linux/pci.h>
#include <asm/uaccess.h>
#include <asm/atomic.h>
#include <asm/unistd.h>
#include <linux/interrupt.h>
#include <linux/ioport.h>
#include <linux/io.h>
#include <mach/gpio-fns.h>
#include<linux/sched.h>
#include<linux/init.h>
#include<asm/io.h>
#include<linux/uaccess.h>
#define DEVICE_NAME “my2440_adc”
static DECLARE_WAIT_QUEUE_HEAD(adc_waitq);
static volatile int ev_adc = 0;
static int adc_data;
static void __iomem *adc_base;
static struct clk *adc_clk;
DECLARE_MUTEX(ADC_LOCK);
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)
{
int ret;
ret = request_irq(IRQ_ADC, adc_irq, IRQF_SHARED, DEVICE_NAME, 1);
if (ret)
{
printk(KERN_ERR “IRQ%d error %d\n”, IRQ_ADC, ret);
return -EINVAL;
}
return 0;
}
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 ssize_t adc_read(struct file *filp, char *buffer, size_t count, loff_t *ppos)
{
if (down_trylock(&ADC_LOCK))
{
return -EBUSY;
}
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));
up(&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;
adc_clk = clk_get(NULL, “adc”);
if (!adc_clk)
{
printk(KERN_ERR “failed to find adc clock source\n”);
return -ENOENT;
}
rch/arm/plat-s3c/clock.c涓?/
clk_enable(adc_clk);
adc_base = ioremap(S3C2410_PA_ADC, 0x20);
if (adc_base == NULL)
{
printk(KERN_ERR “Failed to remap register block\n”);
ret = -EINVAL
goto err_noclk;
}
ret = misc_register(&adc_miscdev);
if (ret)
printk(KERN_ERR “cannot register miscdev on minor=%d (%d)\n”, MISC_DYNAMIC_MINOR, ret);
goto err_nomap;
}
printk(DEVICE_NAME " initialized!\n");
return 0;
clk_disable(adc_clk);
clk_put(adc_clk);
err_nomap:
iounmap(adc_base);
return ret;
}
static void __exit adc_exit(void)
{
free_irq(IRQ_ADC, 1);
iounmap(adc_base);
if (adc_clk)
{
clk_disable(adc_clk);
clk_put(adc_clk);
adc_clk = NULL;
}
misc_deregister(&adc_miscdev);
}
附录2
(adc_test.c)
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
int main(int argc, char **argv)
{
int fd;
fd = open("/dev/my2440_adc", 0);
if(fd < 0)
{
printf(“Open ADC Device Faild!\n”);
exit(1);
}
while(1)
{
int ret;
int data;
ret = read(fd, &data, sizeof(data));
if(ret != sizeof(data))
{
if(errno != EAGAIN)
{
printf(“Read ADC Device Faild!\n”);
}
continue;
}
else
{
printf(“Read ADC value is: %d\n”, data);
}
}
close(fd);
return 0;
}