《嵌入式应用设计课程设计报告linux数据采集课设报告》.doc
北 华 航 天 工 业 学 院课程设计报告(论文)设计课题:Linux系统下系统的数据采集专业班级: 学生姓名: 指导教师: 王达伟 设计时间: 2011年12月 北华航天工业学院电子工程系 嵌入式应用设计 课程设计任务书姓 名:专 业:电子信息工程班级:指导教师:王达伟职 称:讲师课程设计题目:Linux系统下系统的数据采集设计要求:(1)根据技术指标确定硬件实现方案,画出系统的电路原理图(2)确定软件流程图,并编写程序(3)在超级终端显示测量电压值(4)提交设计报告技术指标:(1)输入电压范围所需仪器设备:计算机 嵌入式系统实验箱 虚拟机vmware和Red Hat9成果验收形式:上机验收参考文献:1 孟庆春,牛欣源著. linux教程.电子工业出版社.2 张玲,周旭著. linux操作系统原理与应用.西安电子科技大学出版社.3 魏永明,耿岳等译. linux设备驱动程序.中国电力出版社.时间安排第17周到18周完成设计题目指导教师:王达伟 教研室主任:王俊红2011年 12月28日 内 容 摘 要嵌入式系统是嵌入式计算机系统的总称。Linux是嵌入式操作系统中的一种应用比较广泛的操作系统。嵌入式Linux 是一种适用于嵌入式系统的源码开放的占先式实时多任务操作系统,是目前操作系统领域中的一个热点,其重点与难点是驱动程序的开发。开发嵌人式Linux 下的设备驱动程序,可以更好地利用新硬件特性,提高系统访问硬件的效率,改善整个应用系统的性能。驱动程序修改非常方便,使应用系统非常灵活。Linux的驱动开发中模块方式调试效率很高,它使用insmod工具将编译的模块直接插入内核,如果出现故障,可以使用rmmod从内核中卸载模块,不需要重新启动内核,这使驱动调试效率大大提高。A/D转换器是模拟信号源和CPU之间联系的接口,它的任务是将连续变化的模拟信号转换为数字信号,以便计算机和数字系统进行处理、存储、控制和显示。本设计是在Linux环境下对S3C2410芯片的8通道10位A/D的操作与控制实现数据的转换采集。通过调整三个按钮。采集出的数据会随之变化。索引关键词:嵌入式 Linux 驱动程序 A/D转换 数据采集 目 录一 概 述 1二 实验原理 1二 方案设计 3三 实验步骤 3 四 程序源代码 6五 结论及体会9 六 参考文献 10一、 概述 本课程设计通过编写驱动程序以及测试程序并进行编译,在Linux系统下实现模拟数据转换成数字数据并采集。Linux 中的驱动设计是嵌入式Linux 开发中十分重要的部分,驱动程序的作用是应用程序与硬件之间的一个中间软件层,为应用程序展现硬件的所有功能。Linux 的驱动开发调试有两种方法,一种是直接编译到内核,再运行新的内核来测试;二是编译为模块的形式,单独加载运行调试。模块方式调试效率很高,它使用insmod 工具将编译的模块直接插入内核,如果出现故障,使用rmmod 从内核中卸载模块。不需要重新启动内核,这使驱动调试效率太大提高。A/D转换器是模拟信号源和CPU之间联系的接口,它的任务是将连续变化的模拟信号转换为数字信号,以便计算机和数字系统进行处理、存储、控制和显示。在工业控制盒数据采集及许多其他领域中,A/D转换是不可缺少的。二、 实验原理3.1 A/D 转换器 A/D转换器是模拟信号源和 CPU之间联系的接口,它的任务是将连续变化的模拟信号转换为数字信号,以便计算机和数字系统进行处理、存储、控制和显示。在工业控制和数据采集及许多其他领域中,A/D 转换是不可缺少的。 A/D 转换器有以下类型:逐位比较型、积分型、计数型、并行比较型、电压频率型,主要应根据使用场合的具体要求,按照转换速度、精度、价格、功能以及接口条件等因素来决定选择何种类型。常用的有以下两种: 1、双积分型的 A/D 转换器 双积分式也称二重积分式,其实质是测量和比较两个积分的时间,一个是对模拟输入电压积分的时间 T0,此时间往往是固定的;另一个是以充电后的电压为初值,对参考电源 Vref反向积分,积分电容被放电至零所需的时间 T1。模拟输入电压 Vi与参考电压 VRef之比,等于上述两个时间之比。由于 VRef、T0 固定,而放电时间 T1 可以测出,因而可计算出模拟输入电压的大小(VRef 与 Vi符号相反)。由于 T0、VRef 为已知的固定常数,因此反积分时间 T1与输入模拟电压 Vi 在T0时间内的平均值成正比。输入电压 Vi 愈高,VA 愈大,T1就愈长。在 T1开始时刻,控制逻辑同时打开计数器的控制门开始计数,直到积分器恢复到零电平时, 计数停止。 则计数器所计出的数字即正比于输入电压 Vi在 T0时间内的平均值,于是完成了一次 A/D 转换。由于双积分型 A/D 转换是测量输入电压 Vi 在 T0 时间内的平均值,所以对常态干扰(串摸干扰)有很强的抑制作用,尤其对正负波形对称的干扰信号,抑制效果更好。双积分型的A/D 转换器电路简单,抗干扰能力强,精度高,这是突出的优点。但转换速度比较慢,常用的 A/D 转换芯片的转换时间为毫秒级。例如 12 位的积分型 A/D 芯片 ADCETl2BC,其转换时间为 lms。因此适用于模拟信号变化缓慢,采样速率要求较低,而对精度要求较高,或现场干扰较严重的场合。例如在数字电压表中常被采用。2、逐次逼近型的 A/D 转换器 逐次逼近型(也称逐位比较式)的A/D 转换器,应用比积分型更为广泛,其原理框图如图1 所示,主要由逐次逼近寄存器SAR、D/A 转换器、比较器以及时序和控制逻辑等部分组成。它的实质是逐次把设定的SAR 寄存器中的数字量经D/A转换后得到电压Vc与待转换模拟电压V。进行比较。比较时,先从SAR的最高位开始,逐次确定各位的数码应是“1”还是“0”,其工作过程如下: 转换前,先将 SAR 寄存器各位清零。转换开始时,控制逻辑电路先设定SAR 寄存器的最高位为“1” ,其余位为“0” ,此试探值经 D/A 转换成电压 Vc,然后将 Vc 与模拟输入电压Vx比较。如果 VxVc,说明 SAR最高位的“1”应予保留;如果 Vx<Vc,说明 SAR 该位应予清零。然后再对SAR寄存器的次高位置“1” ,依上述方法进行 D/A 转换和比较。如此重复上述过程,直至确定 SAR 寄存器的最低位为止。过程结束后,状态线改变状态,表明已完成一次转换。最后,逐次逼近寄存器 SAR 中的内容就是与输入模拟量 V 相对应的二进制数字量。显然 A/D转换器的位数 N 决定于 SAR 的位数和 D/A 的位数。图 1(b)表示四位 A/D 转换器的逐次逼近过程。转换结果能否准确逼近模拟信号,主要取决于 SAR和 D/A的位数。位数越多,越能准确逼近模拟量,但转换所需的时间也越长。 逐次逼近式的 A/D 转换器的主要特点是:转换速度较快,在 1100/s 以内,分辨率可以达 18 位,特别适用于工业控制系统。转换时间固定,不随输入信号的变化而变化。抗干扰能力相对积分型的差。例如,对模拟输入信号采样过程中,若在采样时刻有一个干扰脉冲迭加在模拟信号上,则采样时,包括干扰信号在内,都被采样和转换为数字量,这就会造成较大的误差,所以有必要采取适当的滤波措施。(a)逐次渐进式A/D转换原理图 (b)逐次逼近过程原理图图1 逐次逼近式A/D转换器三、 方案设计本课题是基于linux操作系统的数据采集,应用试验箱上面的ADC模块,通过编写linux对应的ADC模块驱动程序实现数据采集功能,并通过minicom显示在超级终端上面。课题完成过程中应完成以下任务:1、了解嵌入式系统实验箱核心板及ADC模块的电路原理图及工作原理。2、 理解无操作系统情况下(把S3C2410作为一个32位的单片机),用片上集成的ADC模块实现数据采集的工作过程。3、 熟悉linux文件系统,掌握linux字符设备驱动程序开发原理及的流程,理解用户应用程序调用内核设备驱动程序的过程。 4、使用通用的Linux操作,编写简单的字符设备驱动程序,完成应用程序调用字符设备驱动程序,深入理解程序执行过程。5、编写ADC模块的设备驱动程序,在实验箱上验证编写的设备驱动程序。利用minicom,显示结果显示在PC机的终端上。 四、 实验步骤1、准备工作、虚拟机设置如下:a、将vmware联网改为桥接方式,顺序选中菜单VMsettingsHardwareNetwaor Adapter,在弹出对话框的右边将Network Connection,改为Bridged;b、顺序选中菜单EditVirtual Network EditorHost Virtual Network Mapping,在弹出的对话框中选“Host Virtual Network Mapping”属性页,为VMnet0选为计算机网卡,如图2:图2主机虚拟网络映射方式设置c、将F盘共享;d、关闭防火墙,运行命令 #chkconfig iptables offe、重启nfs和portmap服务,运行命令#service nfs restart#service portmap restart1.2、NFS挂载配置如下:主机:L1.3、将F盘的ADC目录拷贝到linux下地/root/myjob目录下: 执行命令:cp /mnt/hgfs/F/ADC rf /root/myjob2、编译ADC驱动文件并挂载到试验箱的操作系统下、进入/root/myjob/ADC目录,修改Makefile文件(在实验箱上运行的版本)KERNELDIR = /arm2410cl/kernel/linux-2410cl/#KERNELDIR=/usr/src/linux-8INCLUDEDIR = $(KERNELDIR)/include#CROSS_COMPILE=CROSS_COMPILE=/opt/host/armv4l/bin/armv4l-unknown-linux-其它地方不用修改,存盘退出2.2、回到/root/myjob/ADC目录,编译程序,运行命令:#make2.3、挂载/root/myjob到试验箱步骤如下:a、进入试验箱操作系统,运行命令minicom同时重启试验箱:b、设置试验箱的IP、网关,运行命令:c、运行完以上步骤即可挂载,运行命令yaffsmount t nfs o nolock 192.168.1.169:/root/myjob /mnt/nfs3、运行调试程序2、运行调试程序,运行命令yaffs./test_adc.o 运行结果如下:/mnt/nfslsbusybox-1.00-pre10 demo target/mnt/nfscd adc/mnt/nfs/adclsADC initializeddevice open sucess!五、 程序源代码1.驱动程序源代码使用vi编辑器或其他编辑器阅读理解源代码。其中adc_read,adc_write函数完成驱动的读写接口功能,do_write函数实现将用户写入的数据逆序排列,通过读取函数读取转换的数据。这里只是演示接口的实现过程和内核驱动对用户的数据的处理。adc_ioctl函数演示ioctl调用接口的实现过程。驱动代码如下:#include <linux/config.h>#include <linux/module.h>#include <linux/init.h>#include <linux/kernel.h> /* printk() */#include <linux/irq.h>#include <linux/poll.h>#include <asm/hardware.h>#include <asm/semaphore.h>#include <asm/arch/S3C2410.h>#include <asm/arch/cpu_s3c2410.h>#undef DEBUG#ifdef DEBUG#define DPRINTK(x.) printk(_FUNCTION_"(%d): ",_LINE_);printk(#x);#else#define DPRINTK(x.) (void)(0)#endif#define START_ADC_AIN(x) ADCCON = PRESCALE_EN | PRSCVL(255) | ADC_INPUT(x) ; ADCCON |= ADC_START; #define DEVICE_NAME "sinosys" /*设备的目录名为sinosys*/#define adc_MAJOR 254 /*主设备号为254*/#define adc_MINOR 0 /*从设备号为0*/#define CHANNEL_IOW('p', 0xa2,int) /*定义ioctl号*/#define MAX_CHANNEL 2 /*最大的通道数*/ static struct semaphore adc_lock; /*锁*/static wait_queue_head_t adc_wait; /*等待队列*/static unsigned int adc_ain; /*当前通道*/static void adcdone_int_handler(int irq, void *dev_id, struct pt_regs *reg)DPRINTK("adcdone_initn");wake_up(&adc_wait); /*唤醒等待队列*/*adc的原始读函数*/int s3c2410_adc_read(int ain)int ret = 0;if (down_interruptible(&adc_lock) /*加锁*/return -ERESTARTSYS;START_ADC_AIN(ain); /*启动ADC转换过程*/sleep_on_timeout(&adc_wait, HZ/100); /*把程序加到等待队列中*/ /*超时值为HZ/100(10ms)*/ret = ADCDAT0 ; /*苏醒到代表ADC转换结束,以读取ADC的值*/up(&adc_lock); /*解锁*/adc_wait = NULL;DPRINTK("AIN%d = 0x%04x, %dn", ain, ret, ADCCON & 0x80 ? 1:0);return (ret & 0x3ff); /*返回ADC中的转换值*/static ssize_t adc_read(struct file *filp, char *buffer, size_t count, loff_t *ppos)int retval ;retval = s3c2410_adc_read( adc_ain ); /*调用ADC原始读函数*/DPRINTK("The value of channel %d : %xn", adc_ain, retval);retval = put_user(retval, (int *)buffer); /*把数据传回用户空间*/if (!retval)retval = sizeof(unsigned long);return retval;/*IO口控制函数*/static ssize_t adc_ioctl(struct inode *inode, struct file *file,unsigned int cmd, unsigned long arg)switch(cmd)case CHANNEL:DPRINTK("Change to ADC channel %dn",(int)arg);if (int)arg < MAX_CHANNEL) adc_ain = (int) arg;break;default:DPRINTK("error cmd numbern");break;return 0;static ssize_t adc_open(struct inode *inode, struct file *file)MOD_INC_USE_COUNT;printk("device open sucess!n");adc_ain = 0;return 0;static ssize_t adc_release(struct inode *inode, struct file *filp)MOD_DEC_USE_COUNT;printk("device releasen");return 0;static struct file_operations adc_fops = owner: THIS_MODULE,read: adc_read,ioctl: adc_ioctl,open: adc_open,release: adc_release,;#ifdef CONFIG_DEVFS_FSstatic devfs_handle_t devfs_adc_dir, devfs_adcraw;#endifint _init s3c2410_adc_init(void)init_MUTEX(&adc_lock); /*初始化锁*/init_waitqueue_head(&adc_wait); /*初始化等待队列*/* normal ADC */ADCTSC = 0; /XP_PST(NOP_MODE); /*设定ADC寄存器初值*/if (request_irq(IRQ_ADC_DONE, adcdone_int_handler, SA_INTERRUPT,"ADC", NULL) < 0) /*申请中断*/goto irq_err;#ifdef CONFIG_DEVFS_FS /*建立sinosys设备目录*/devfs_adc_dir = devfs_mk_dir(NULL, DEVICE_NAME, NULL);devfs_adcraw = devfs_register(devfs_adc_dir, "adc", DEVFS_FL_DEFAULT,adc_MAJOR, adc_MINOR, S_IFCHR | S_IRUSR | S_IWUSR,&adc_fops, NULL);#elseint result;SET_MODULE_OWNER(&adc_fops);result = register_chrdev(adc_MAJOR, "scullc", &adc_fops);if (result < 0) return result;/ if (adc_MAJOR = 0) adc_MAJOR = result; /* dynamic */#endifprintk(DEVICE_NAME " initializedn");return 0;irq_err:return 1;module_init(s3c2410_adc_init); /*定义模块的初始化函数为s3c2410_adc_init*/#ifdef MODULEvoid _exit s3c2410_adc_exit(void)free_irq(IRQ_ADC_DONE, NULL);module_exit(s3c2410_adc_exit);MODULE_LICENSE("GPL");#endif测试程序代码测试程序test_adc.c代码如下:#include <stdio.h>#include <stdlib.h>#include <fcntl.h>#include <sys/ioctl.h>#include <unistd.h>#define CHANNEL_IOW('p', 0xa2,int)int main()int fd;int result;int i,j;fd=open("/dev/sinosys/adc",O_RDWR);if(fd < 0)printf("#DEMO device open fail#n");return (-1);for(i=0; i<2; i+)ioctl(fd, CHANNEL, (long)i); /*通过ioctl函数转换通道*/for(j=0; j<4; j+) read(fd,&result,1);printf("Channel %d value: %xn",i,result);close(fd);return(0); 六、 结论及体会通过程序与实验箱的调试,最终结果显示准确。改变实验箱上的两个通道按钮,系统程序结果中采集的数据能够发生相应变化并显示准确结果。通过本次课设,使我对驱动程序有了进一步的了解,熟练了linux的一些基本操作命令并且初步理解了linux下的驱动程序的开发框架;同时也发现自己知识上一不足,在今后的学习中会更加重识学也的积累,不断提升自己。七、 参考文献1 孟庆春,牛欣源著. linux教程.电子工业出版社.2 张玲,周旭著. linux操作系统原理与应用.西安电子科技大学出版社. 3 魏永明,耿岳等译. linux设备驱动程序.中国电力出版社. 电子工程系 嵌入式应用设计 课程设计成绩评定表专业:电子信息工程 班级: 学号: 姓名: 课题名称设计任务与要求指导教师评语 建议成绩: 指导教师:课程小组评定评定成绩: 课程负责人: 年 月 日