嵌入式Linux下ADC的驱动程序实现与应用

时间:2022-08-27 05:06:11

嵌入式Linux下ADC的驱动程序实现与应用

摘 要:详细介绍S3C2410芯片ADC模块以及Linux的驱动模型,并且通过S3C2410内置的ADC驱动程序设计说明字符型设备驱动开发方法;将驱动编译为模块的方式,单独加载入内核,便于调试。以MINICOM为操作台,控制驱动模块的加载和应用程序的运行。并通过实例介绍ADC驱动程序在电阻、电压等测试中的实际应用;从实验结果可以看出ADC驱动可以被成功加载和调用;该驱动可以测试电压、电流等标准工程量信号,或作为工业传感器接口的一部分对现场标准工程量信号进行采集处理。

关键词:S3C2410;ADC;Linux;字符设备驱动程序

中图分类号:TP311文献标识码:B

文章编号:1004373X(2008)2203303

Implement and Application of ADC Driver about Embedded-Linux

SUN Dehui,LIANG Xin,YANG Yang

(Key Laboratory of Beijing Municipality,The FAT Laboratory,North China University of Technology,Beijing,100041,China)

Abstract:The module of ADC in S3C2410 CMOS chip and the model about Linux drivers are expounded,the method of developing character device drivers are illuminated by realizing an ADC driver.As convenient to debug,compiling the drivers into module and "insmod" it into kernel.Updating the drivers module and application by MINICOM,one kind of consoles.Application on testing resistance and voltage using ADC driver are introduced through an example.In the end,it is obviously that ADC drivers module could be "insmoded"and called successful from the result of experiment.using the drivers testing resistance,voltage and many other standard signal.ADC drivers can collect the standard signal of plants as one part of interface of industrial sensor.

Keywords:S3C2410;ADC;Linux;character device driver

1 引 言

S3C2410开发板制造商提供了绝大部分的驱动程序,但有时出于实际开发的需要、应用程序的稳定性考虑,用户往往需要开发一个自己需要的接口驱动程序。下面分析Linux字符设备驱动程序的结构,以及ADC驱动程序的开发。本驱动可以测试电压、电流等标准工程量信号,或作为工业传感器接口的一部分对现场标准工程量信号进行采集处理(可以在平台上为传感器预留接口,本文在作电路板时已直接将测试用的滑动变阻器与A/D接口相连)。

2 S3C2410X及ADC模块

S3C2410X是韩国三星电子公司推出的一款基于ARM920T内核的16/32位RISC嵌入式微处理器,该处理器主要面向手持式设备以及高性价比、低功耗方面的应用[1]。S3C2410X芯片集成1个LCD控制器(支持STN和TFT带有触摸屏的液晶显示屏)、SDRAM控制器、3个通道的UART、4个通道的DMA、4个具有PWM功能的计时器和1个内部时钟、8通道的10位ADC,同时S3C2410X还有丰富的外部接口,例如触摸屏接口、I2C总线接口、主从USB设备接口、SPI接口、SD/MMC卡接口等[2]。

S3C2410X芯片内部集成了一个8路10位A/D转换器(其中第5、第6通道可用于支持触摸屏接口)。ADC模块是带采样保持器的,在25 MHz A/D转换时钟下,最高转换速率是500 kS/s,以片上采样、保持的方式工作,支持掉电模式,其测量模拟输入电压范围为0~3.3 V。由于ADC转换模块和触摸屏控制是共用的8通道模拟信号输入,所以要单独实现A/D转换功能需要把ADC触摸屏控制器(ADCTSC)设置成通常工作模式(ADCTSC的AUTO_PST=0,XY_PST=0)[3,4]

A/D转换时间在PCLK频率为50 MHz并且预分频值为49的情况下,转换10位数据的时间为:

A/D转换频率=50 MHz/(49+1)=1 MHz

转换时间=1/(1 MHz/5 cycles)=1/200 kHz

=5 μs

对ADC操作,主要是对下面的ADC几组寄存器进行读写操作:

ADC控制寄存器:ADCCON(2410ARM平台下寄存器物理地址是 0X5800000);

ADC触摸屏控制寄存器:ADCTSC(2410ARM平台下寄存器物理地址 0X58000004);

ADC数据寄存器:ADCDAT0/1(2410ARM平台下寄存器物理地址 0X5800000C/0X58000010) [5]。

3 Linux的设备驱动程序模型

目前Linux支持的设备驱动可分为3种:字符设备(character device)、块设备(block device)、网络接口设备(network interface)[2]。本文所涉及的ADC摸块驱动程序就是属于字符设备驱动程序。字符设备指那些无需缓冲直接存取的设备。在对字符设备发出读、写请求时,实际的硬件I/O一般就紧接着发生了,是Linux设备中最简单的一种。应用程序可以用与存取文件相同的系统调用来打开、读写及关闭它。即使此设备是将系统连接到网络中的PPP后台进程的modem也是如此。字符设备驱动程序一般要包含open,close,read,write等几个系统调用[6]。

I/O子系统向内核其他部分提供了一个统一的标准设备接口,这是通过数据结构file_operations[7]来完成的。这个结构中的每一个成员的名字都对应着一个系统调用,用户进程利用系统调用在对设备文件进行诸如read/write操作时,系统调用通过设备文件的主设备号找到相应的设备驱动程序,然后读取这个数据结构相应的函数指针,接着把控制权交给该函数,这是Linux的设备驱动程序工作的基本原理。编写设备驱动程序的主要工作就是编写子函数,并填file_operations的各个域。

4 ADC底层驱动程序的实现

ADC驱动程序的主要任务,就是把2410的ADC内置模块的使用传递给应用程序,为了便于理解,下面就按照驱动程序加载、使用的顺序,来讲述ADC底层驱动的实现。

4.1 驱动程序的加载及初始化

驱动程序的加载方式有2种:一种是将其作为内核的一部分,直接编译到内核中,即静态编译,也可以单独作为一个模块编译,在需要时再动态地把它加载入内核,不需要时也可从内核中删除,即动态连接。由于S3C2410X芯片带MMU,在此,使用动态连接方式进行加载,也便于调试[8]。

在Linux的相关路径下,使用指令insmod s3c2410-adc.o 即可将编译好的ADC驱动程序以模块的方式加载入内核。当驱动加载入内核之后,首先要调用s3c2410_adc_init()函数。绝大多数驱动程序,都要在XXX_Init()函数中完成驱动程序的初始化,这其中包括物理地址的映射、中断注册、管脚和相应寄存器的初始化等。当然也可在open(),read()函数中对寄存器进行初始化,视具体程序要求而定(本文对ADC部分寄存器的初始化是在read部分完成的)。

向系统增加一个驱动程序则意味着要赋予它一个主设备号,这一赋值过程是通过register_chrdev()函数来实现的,这个函数定义在

int register_chrdev(unsigned int major,const char *name,struct file_operations *fops);

register_chrdev()需要3个参数:参数一是希望获得的设备号,如果是零,系统将选择一个没有被占用的设备号返回;参数二是设备文件名,参数三用来登记驱动程序实际执行操作的函数的指针[7](在上文中提到的file_operations结构体中定义)。如果登记成功,返回设备的主设备号,不成功,返回一个负值。

ADC初始化主要代码如下:

int __init s3c2410_adc_init(void)

{

int ret;

ADCTSC = 0;//将ADCTSC寄存器设成通常工作模式

ret = register_chrdev(0,DEVICE_NAME,&s3c2410_fops);

if (ret < 0) {

printk(DEVICE_NAME " can't get major number\\n");

return ret;

}//初始化主设备号为0,使系统为此驱动程序动态地分配一个主设备号

adcMajor=ret;

printk (DEVICE_NAME"\\tinitialized\\n");

return 0;

4.2 打开、读、写ADC驱动程序

当应用程序打开ADC驱动程序时,通过指针&s3c2410_fops调用对应的s3c2410_adc_open()函数,这个函数比较简单,不予介绍。S3C2410X有6个通道可做一般功能使用的ADC,在本驱动程序中只使用0,1两个通道,且在对应的应用程序中进行设置,会在下文中予以介绍。应用程序要读取某一路的ADC值时,先调用s3c2410_adc_write()函数,把用户程序要求的通道号channel和预分频比值prescale传递到ADC设备的结构体变量中。这2个结构体成员定义如下:

typedef struct {

struct semaphore lock;

wait_queue_head_t wait;

int channel;// ADC_DEV设备通道号

int prescale; // ADC_DEV设备预分频比

}ADC_DEV;

s3c2410_adc_write()函数的部分代码如下:

int data;

copy_from_user(&data,buffer,count);

adcdev.channel=ADC_WRITE_GETCH(data);//设置通道号

adcdev.prescale=ADC_WRITE_GETPRE(data); //设置预分频比

return count;

}

ADC_WRITE_GETCH(data)和ADC_WRITE_GETPRE(data)是2个带参数的宏,通过简单的算法处理,将用户程序要求的通道号和预分频比分别剃出。

接下来应用程序调用s3c2410_adc_read()函数来启动某一通道的ADC转换并读取转换后的数据,这一过程是主要对ADC物理寄存器进行操作,主要代码如下:

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

ADCCON |= ADC_START//开启ADC转换

interruptible_sleep_on(&adcdev.wait);//休眠用户进程,等待转换结束后,再将其唤醒

ret = ADCDAT0; //转换结束,数据存储在ADCDAT0寄存器中

ret &= 0x3ff;

copy_to_user(buffer,(char *)&ret,sizeof(ret));//将转换结果传递给用户程序

其中PRESCALE_EN,PRSCVL(prescale),DC_INPUT((ch)),ADC_START这几个宏分别对ADCCON寄存器的相应位进行了设置,copy_to_user()将ret这个在内核空间局部变量中的10 b数据传递给用户空间。

本ADC驱动程序主要的函数介绍完毕,接下来只要完成s3c2410_adc_release(),s3c2410_adc_exit()等其他函数就可以。前文已经提过,字符设备驱动程序所有的系统调用都是通过file_operations结构体[7]集合的,对于本程序,定义如下:

static struct file_operations s3c2410_fops = {

owner:THIS_MODULE,

open:s3c2410_adc_open,

read:s3c2410_adc_read,

write:s3c2410_adc_write,

release:s3c2410_adc_release,

};

用户程序在调用时只需像使用read,write操作普通文件一样对底层进行字符设备进行操作。

5 ADC驱动程序的应用

在基于2410的Linux环境下,用这个驱动程序可以实现外部模拟信号到2410数字信号的转换,下面是一个最基本的电压测量的运用,原理图如图1所示。

通过改变2个滑动变阻器两端的电压来分别得到2个模拟输入信号,通过导线直接连接到S3C2410芯片的AIN0和AIN1引脚(暂不考虑放大和滤波处理)。

在应用程序中,通过一个for循环语句来实现对AIN0和AIN1两路通道的循环采集数据的。

while( stop==0 )

{

for(i=0;i

d=((float)GetADresult(i)*3.3)/1024.0;

printf("AIN%d=%8.4f\\t",i,d);}

}

其中GetADresult()函数即实现read和write系统调用:

static int GetADresult(int channel)

{

int PRESCALE=0X**;

int data=ADC_WRITE(channel,PRESCALE);

write(adc_fd,&data,sizeof(data));

read(adc_fd,&data,sizeof(data));

return data;

}

GetADresult()的返回值就是通过copy_to_user()传递过来的10位A/D转换结果。根据返回值data来计算模拟电压为:

date/210×3.3。

在上位机上使用相应的gcc编译器将驱动程序和应用程序编译后,下载到开发板上运行,可在minicom或超级终端上看到如图2所示运行结果。

6 结 语

基于Linux和S3C2410的嵌入式产品运用已经越来越广泛,分析Linux下ADC驱动程序的开发,通过本文的介绍,读者可以对Linux的驱动程序的结构、编写以及实际应用能有一定的了解。

参考文献

[1]潘巨龙,黄宁.ARM9嵌入式Linux系统构建与应用[M].北京:北京航空航天大学出版社,2007.

[2]孙天泽,袁文菊.嵌入式设计及Linux驱动开发指南[M].2版.北京:电子工业出版社,2007.

[3]舒云.邱绍峰.基于Windows 的ADC驱动程序实现与应用研究[J].工业控制计算机,2007,20(4):57-58,61.

[4]畅卫功,丁忠林.嵌入式Linux系统中触摸屏驱动的研究[J].微计算机信息,2007,23(20):103-105.

[5]S3c4210 user′s manual.Samsung Electrionic.2004.

[6]何世烈,陈建.基于嵌入式Linux的设备驱动程序设计[J].单片机与嵌入式系统应用,2007(7):65-67.[7]JonatbanCorbet,Alessandro Rubini,Greg Kroah-Hartman.LINUX设备驱动程序[M].3版.魏永明,译.北京:中国电力出版社,2005.

[8]宋宝华,华清远见嵌入式培训中心.LINUX设备驱动开发详解[M].北京:人民邮电出版社,2008.

[9]潘辉,贾世祥.基于s3c2410和嵌入式Linux的D/A转换的实现[J].微计算机信息,2007(20):128-129,130.

[10]刘淼.嵌入式系统接口设计与Linux驱动程序开发[M].北京:北京航空航天大学出版社,2006.

[11]孙婷,田泽,闫效莺.基于S3C2440的Windows CE设备驱动的研究与实践\.现代电子技术,2008,31(6):153-155,158.

作者简介 孙德辉 男,1962年出生,吉林人,教授,博士。研究方向为网络控制理论与网络自动化技术、嵌入式技术与信息家电。

注:本文中所涉及到的图表、注解、公式等内容请以PDF格式阅读原文

上一篇:基于FPGA的多通道串行A/D转换器的控制器设计 下一篇:Gbps试验系统中高速串行接口的设计与实现