摘 要:詳細(xì)介紹S3C2410芯片ADC模塊以及Linux的驅(qū)動(dòng)模型,并且通過S3C2410內(nèi)置的ADC驅(qū)動(dòng)程序設(shè)計(jì)說明字符型設(shè)備驅(qū)動(dòng)開發(fā)方法;將驅(qū)動(dòng)編譯為模塊的方式,單獨(dú)加載入內(nèi)核,便于調(diào)試。以MINICOM為操作臺(tái),控制驅(qū)動(dòng)模塊的加載和應(yīng)用程序的運(yùn)行。并通過實(shí)例介紹ADC驅(qū)動(dòng)程序在電阻、電壓等測(cè)試中的實(shí)際應(yīng)用;從實(shí)驗(yàn)結(jié)果可以看出ADC驅(qū)動(dòng)可以被成功加載和調(diào)用;該驅(qū)動(dòng)可以測(cè)試電壓、電流等標(biāo)準(zhǔn)工程量信號(hào),或作為工業(yè)傳感器接口的一部分對(duì)現(xiàn)場(chǎng)標(biāo)準(zhǔn)工程量信號(hào)進(jìn)行采集處理。
關(guān)鍵詞:S3C2410;ADC;Linux;字符設(shè)備驅(qū)動(dòng)程序
中圖分類號(hào):TP311文獻(xiàn)標(biāo)識(shí)碼:B
文章編號(hào):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開發(fā)板制造商提供了絕大部分的驅(qū)動(dòng)程序,但有時(shí)出于實(shí)際開發(fā)的需要、應(yīng)用程序的穩(wěn)定性考慮,用戶往往需要開發(fā)一個(gè)自己需要的接口驅(qū)動(dòng)程序。下面分析Linux字符設(shè)備驅(qū)動(dòng)程序的結(jié)構(gòu),以及ADC驅(qū)動(dòng)程序的開發(fā)。本驅(qū)動(dòng)可以測(cè)試電壓、電流等標(biāo)準(zhǔn)工程量信號(hào),或作為工業(yè)傳感器接口的一部分對(duì)現(xiàn)場(chǎng)標(biāo)準(zhǔn)工程量信號(hào)進(jìn)行采集處理(可以在平臺(tái)上為傳感器預(yù)留接口,本文在作電路板時(shí)已直接將測(cè)試用的滑動(dòng)變阻器與A/D接口相連)。
2 S3C2410X及ADC模塊
S3C2410X是韓國三星電子公司推出的一款基于ARM920T內(nèi)核的16/32位RISC嵌入式微處理器,該處理器主要面向手持式設(shè)備以及高性價(jià)比、低功耗方面的應(yīng)用[1]。S3C2410X芯片集成1個(gè)LCD控制器(支持STN和TFT帶有觸摸屏的液晶顯示屏)、SDRAM控制器、3個(gè)通道的UART、4個(gè)通道的DMA、4個(gè)具有PWM功能的計(jì)時(shí)器和1個(gè)內(nèi)部時(shí)鐘、8通道的10位ADC,同時(shí)S3C2410X還有豐富的外部接口,例如觸摸屏接口、I2C總線接口、主從USB設(shè)備接口、SPI接口、SD/MMC卡接口等[2]。
S3C2410X芯片內(nèi)部集成了一個(gè)8路10位A/D轉(zhuǎn)換器(其中第5、第6通道可用于支持觸摸屏接口)。ADC模塊是帶采樣保持器的,在25 MHz A/D轉(zhuǎn)換時(shí)鐘下,最高轉(zhuǎn)換速率是500 kS/s,以片上采樣、保持的方式工作,支持掉電模式,其測(cè)量模擬輸入電壓范圍為0~3.3 V。由于ADC轉(zhuǎn)換模塊和觸摸屏控制是共用的8通道模擬信號(hào)輸入,所以要單獨(dú)實(shí)現(xiàn)A/D轉(zhuǎn)換功能需要把ADC觸摸屏控制器(ADCTSC)設(shè)置成通常工作模式(ADCTSC的AUTO_PST=0,XY_PST=0)[3,4]
A/D轉(zhuǎn)換時(shí)間在PCLK頻率為50 MHz并且預(yù)分頻值為49的情況下,轉(zhuǎn)換10位數(shù)據(jù)的時(shí)間為:
A/D轉(zhuǎn)換頻率=50 MHz/(49+1)=1 MHz
轉(zhuǎn)換時(shí)間=1/(1 MHz/5 cycles)=1/200 kHz
=5 μs
對(duì)ADC操作,主要是對(duì)下面的ADC幾組寄存器進(jìn)行讀寫操作:
ADC控制寄存器:ADCCON(2410ARM平臺(tái)下寄存器物理地址是 0X5800000);
ADC觸摸屏控制寄存器:ADCTSC(2410ARM平臺(tái)下寄存器物理地址 0X58000004);
ADC數(shù)據(jù)寄存器:ADCDAT0/1(2410ARM平臺(tái)下寄存器物理地址 0X5800000C/0X58000010) [5]。
3 Linux的設(shè)備驅(qū)動(dòng)程序模型
目前Linux支持的設(shè)備驅(qū)動(dòng)可分為3種:字符設(shè)備(character device)、塊設(shè)備(block device)、網(wǎng)絡(luò)接口設(shè)備(network interface)[2]。本文所涉及的ADC摸塊驅(qū)動(dòng)程序就是屬于字符設(shè)備驅(qū)動(dòng)程序。字符設(shè)備指那些無需緩沖直接存取的設(shè)備。在對(duì)字符設(shè)備發(fā)出讀、寫請(qǐng)求時(shí),實(shí)際的硬件I/O一般就緊接著發(fā)生了,是Linux設(shè)備中最簡單的一種。應(yīng)用程序可以用與存取文件相同的系統(tǒng)調(diào)用來打開、讀寫及關(guān)閉它。即使此設(shè)備是將系統(tǒng)連接到網(wǎng)絡(luò)中的PPP后臺(tái)進(jìn)程的modem也是如此。字符設(shè)備驅(qū)動(dòng)程序一般要包含open,close,read,write等幾個(gè)系統(tǒng)調(diào)用[6]。
I/O子系統(tǒng)向內(nèi)核其他部分提供了一個(gè)統(tǒng)一的標(biāo)準(zhǔn)設(shè)備接口,這是通過數(shù)據(jù)結(jié)構(gòu)file_operations[7]來完成的。這個(gè)結(jié)構(gòu)中的每一個(gè)成員的名字都對(duì)應(yīng)著一個(gè)系統(tǒng)調(diào)用,用戶進(jìn)程利用系統(tǒng)調(diào)用在對(duì)設(shè)備文件進(jìn)行諸如read/write操作時(shí),系統(tǒng)調(diào)用通過設(shè)備文件的主設(shè)備號(hào)找到相應(yīng)的設(shè)備驅(qū)動(dòng)程序,然后讀取這個(gè)數(shù)據(jù)結(jié)構(gòu)相應(yīng)的函數(shù)指針,接著把控制權(quán)交給該函數(shù),這是Linux的設(shè)備驅(qū)動(dòng)程序工作的基本原理。編寫設(shè)備驅(qū)動(dòng)程序的主要工作就是編寫子函數(shù),并填file_operations的各個(gè)域。
4 ADC底層驅(qū)動(dòng)程序的實(shí)現(xiàn)
ADC驅(qū)動(dòng)程序的主要任務(wù),就是把2410的ADC內(nèi)置模塊的使用傳遞給應(yīng)用程序,為了便于理解,下面就按照驅(qū)動(dòng)程序加載、使用的順序,來講述ADC底層驅(qū)動(dòng)的實(shí)現(xiàn)。
4.1 驅(qū)動(dòng)程序的加載及初始化
驅(qū)動(dòng)程序的加載方式有2種:一種是將其作為內(nèi)核的一部分,直接編譯到內(nèi)核中,即靜態(tài)編譯,也可以單獨(dú)作為一個(gè)模塊編譯,在需要時(shí)再動(dòng)態(tài)地把它加載入內(nèi)核,不需要時(shí)也可從內(nèi)核中刪除,即動(dòng)態(tài)連接。由于S3C2410X芯片帶MMU,在此,使用動(dòng)態(tài)連接方式進(jìn)行加載,也便于調(diào)試[8]。
在Linux的相關(guān)路徑下,使用指令insmod s3c2410-adc.o 即可將編譯好的ADC驅(qū)動(dòng)程序以模塊的方式加載入內(nèi)核。當(dāng)驅(qū)動(dòng)加載入內(nèi)核之后,首先要調(diào)用s3c2410_adc_init()函數(shù)。絕大多數(shù)驅(qū)動(dòng)程序,都要在XXX_Init()函數(shù)中完成驅(qū)動(dòng)程序的初始化,這其中包括物理地址的映射、中斷注冊(cè)、管腳和相應(yīng)寄存器的初始化等。當(dāng)然也可在open(),read()函數(shù)中對(duì)寄存器進(jìn)行初始化,視具體程序要求而定(本文對(duì)ADC部分寄存器的初始化是在read部分完成的)。
向系統(tǒng)增加一個(gè)驅(qū)動(dòng)程序則意味著要賦予它一個(gè)主設(shè)備號(hào),這一賦值過程是通過register_chrdev()函數(shù)來實(shí)現(xiàn)的,這個(gè)函數(shù)定義在
int register_chrdev(unsigned int major,const char *name,struct file_operations *fops);
register_chrdev()需要3個(gè)參數(shù):參數(shù)一是希望獲得的設(shè)備號(hào),如果是零,系統(tǒng)將選擇一個(gè)沒有被占用的設(shè)備號(hào)返回;參數(shù)二是設(shè)備文件名,參數(shù)三用來登記驅(qū)動(dòng)程序?qū)嶋H執(zhí)行操作的函數(shù)的指針[7](在上文中提到的file_operations結(jié)構(gòu)體中定義)。如果登記成功,返回設(shè)備的主設(shè)備號(hào),不成功,返回一個(gè)負(fù)值。
ADC初始化主要代碼如下:
int __init s3c2410_adc_init(void)
{
int ret;
ADCTSC = 0;//將ADCTSC寄存器設(shè)成通常工作模式
ret = register_chrdev(0,DEVICE_NAME,s3c2410_fops);
if (ret < 0) {
printk(DEVICE_NAME \" can't get major number\\\\");
return ret;
}//初始化主設(shè)備號(hào)為0,使系統(tǒng)為此驅(qū)動(dòng)程序動(dòng)態(tài)地分配一個(gè)主設(shè)備號(hào)
adcMajor=ret;
printk (DEVICE_NAME\"\\\initialized\\\\");
return 0;
4.2 打開、讀、寫ADC驅(qū)動(dòng)程序
當(dāng)應(yīng)用程序打開ADC驅(qū)動(dòng)程序時(shí),通過指針s3c2410_fops調(diào)用對(duì)應(yīng)的s3c2410_adc_open()函數(shù),這個(gè)函數(shù)比較簡單,不予介紹。S3C2410X有6個(gè)通道可做一般功能使用的ADC,在本驅(qū)動(dòng)程序中只使用0,1兩個(gè)通道,且在對(duì)應(yīng)的應(yīng)用程序中進(jìn)行設(shè)置,會(huì)在下文中予以介紹。應(yīng)用程序要讀取某一路的ADC值時(shí),先調(diào)用s3c2410_adc_write()函數(shù),把用戶程序要求的通道號(hào)channel和預(yù)分頻比值prescale傳遞到ADC設(shè)備的結(jié)構(gòu)體變量中。這2個(gè)結(jié)構(gòu)體成員定義如下:
typedef struct {
struct semaphore lock;
wait_queue_head_t wait;
int channel;// ADC_DEV設(shè)備通道號(hào)
int prescale; // ADC_DEV設(shè)備預(yù)分頻比
}ADC_DEV;
s3c2410_adc_write()函數(shù)的部分代碼如下:
int data;
copy_from_user(data,buffer,count);
adcdev.channel=ADC_WRITE_GETCH(data);//設(shè)置通道號(hào)
adcdev.prescale=ADC_WRITE_GETPRE(data); //設(shè)置預(yù)分頻比
return count;
}
ADC_WRITE_GETCH(data)和ADC_WRITE_GETPRE(data)是2個(gè)帶參數(shù)的宏,通過簡單的算法處理,將用戶程序要求的通道號(hào)和預(yù)分頻比分別剃出。
接下來應(yīng)用程序調(diào)用s3c2410_adc_read()函數(shù)來啟動(dòng)某一通道的ADC轉(zhuǎn)換并讀取轉(zhuǎn)換后的數(shù)據(jù),這一過程是主要對(duì)ADC物理寄存器進(jìn)行操作,主要代碼如下:
ADCCON = PRESCALE_EN | PRSCVL(prescale) | ADC_INPUT((ch));
ADCCON |= ADC_START//開啟ADC轉(zhuǎn)換
interruptible_sleep_on(adcdev.wait);//休眠用戶進(jìn)程,等待轉(zhuǎn)換結(jié)束后,再將其喚醒
ret = ADCDAT0; //轉(zhuǎn)換結(jié)束,數(shù)據(jù)存儲(chǔ)在ADCDAT0寄存器中
ret = 0x3ff;
copy_to_user(buffer,(char *)ret,sizeof(ret));//將轉(zhuǎn)換結(jié)果傳遞給用戶程序
其中PRESCALE_EN,PRSCVL(prescale),DC_INPUT((ch)),ADC_START這幾個(gè)宏分別對(duì)ADCCON寄存器的相應(yīng)位進(jìn)行了設(shè)置,copy_to_user()將ret這個(gè)在內(nèi)核空間局部變量中的10 b數(shù)據(jù)傳遞給用戶空間。
本ADC驅(qū)動(dòng)程序主要的函數(shù)介紹完畢,接下來只要完成s3c2410_adc_release(),s3c2410_adc_exit()等其他函數(shù)就可以。前文已經(jīng)提過,字符設(shè)備驅(qū)動(dòng)程序所有的系統(tǒng)調(diào)用都是通過file_operations結(jié)構(gòu)體[7]集合的,對(duì)于本程序,定義如下:
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,
};
用戶程序在調(diào)用時(shí)只需像使用read,write操作普通文件一樣對(duì)底層進(jìn)行字符設(shè)備進(jìn)行操作。
5 ADC驅(qū)動(dòng)程序的應(yīng)用
在基于2410的Linux環(huán)境下,用這個(gè)驅(qū)動(dòng)程序可以實(shí)現(xiàn)外部模擬信號(hào)到2410數(shù)字信號(hào)的轉(zhuǎn)換,下面是一個(gè)最基本的電壓測(cè)量的運(yùn)用,原理圖如圖1所示。
通過改變2個(gè)滑動(dòng)變阻器兩端的電壓來分別得到2個(gè)模擬輸入信號(hào),通過導(dǎo)線直接連接到S3C2410芯片的AIN0和AIN1引腳(暫不考慮放大和濾波處理)。
在應(yīng)用程序中,通過一個(gè)for循環(huán)語句來實(shí)現(xiàn)對(duì)AIN0和AIN1兩路通道的循環(huán)采集數(shù)據(jù)的。
while( stop==0 )
{
for(i=0;i<=1;i++){ //采樣0~1路A/D值,通道號(hào)就是在這里設(shè)置的
d=((float)GetADresult(i)*3.3)/1024.0;
printf(\"AIN%d=%8.4f\\\\",i,d);}
}
其中GetADresult()函數(shù)即實(shí)現(xiàn)read和write系統(tǒng)調(diào)用:
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轉(zhuǎn)換結(jié)果。根據(jù)返回值data來計(jì)算模擬電壓為:
date/210×3.3。
在上位機(jī)上使用相應(yīng)的gcc編譯器將驅(qū)動(dòng)程序和應(yīng)用程序編譯后,下載到開發(fā)板上運(yùn)行,可在minicom或超級(jí)終端上看到如圖2所示運(yùn)行結(jié)果。
6 結(jié) 語
基于Linux和S3C2410的嵌入式產(chǎn)品運(yùn)用已經(jīng)越來越廣泛,分析Linux下ADC驅(qū)動(dòng)程序的開發(fā),通過本文的介紹,讀者可以對(duì)Linux的驅(qū)動(dòng)程序的結(jié)構(gòu)、編寫以及實(shí)際應(yīng)用能有一定的了解。
參考文獻(xiàn)
[1]潘巨龍,黃寧.ARM9嵌入式Linux系統(tǒng)構(gòu)建與應(yīng)用[M].北京:北京航空航天大學(xué)出版社,2007.
[2]孫天澤,袁文菊.嵌入式設(shè)計(jì)及Linux驅(qū)動(dòng)開發(fā)指南[M].2版.北京:電子工業(yè)出版社,2007.
[3]舒云.邱紹峰.基于Windows CE.NET的ADC驅(qū)動(dòng)程序?qū)崿F(xiàn)與應(yīng)用研究[J].工業(yè)控制計(jì)算機(jī),2007,20(4):57-58,61.
[4]暢衛(wèi)功,丁忠林.嵌入式Linux系統(tǒng)中觸摸屏驅(qū)動(dòng)的研究[J].微計(jì)算機(jī)信息,2007,23(20):103-105.
[5]S3c4210 user′s manual.Samsung Electrionic.2004.
[6]何世烈,陳建.基于嵌入式Linux的設(shè)備驅(qū)動(dòng)程序設(shè)計(jì)[J].單片機(jī)與嵌入式系統(tǒng)應(yīng)用,2007(7):65-67.[7]JonatbanCorbet,Alessandro Rubini,Greg Kroah-Hartman.LINUX設(shè)備驅(qū)動(dòng)程序[M].3版.魏永明,譯.北京:中國電力出版社,2005.
[8]宋寶華,華清遠(yuǎn)見嵌入式培訓(xùn)中心.LINUX設(shè)備驅(qū)動(dòng)開發(fā)詳解[M].北京:人民郵電出版社,2008.
[9]潘輝,賈世祥.基于s3c2410和嵌入式Linux的D/A轉(zhuǎn)換的實(shí)現(xiàn)[J].微計(jì)算機(jī)信息,2007(20):128-129,130.
[10]劉淼.嵌入式系統(tǒng)接口設(shè)計(jì)與Linux驅(qū)動(dòng)程序開發(fā)[M].北京:北京航空航天大學(xué)出版社,2006.
[11]孫婷,田澤,閆效鶯.基于S3C2440的Windows CE設(shè)備驅(qū)動(dòng)的研究與實(shí)踐\\.現(xiàn)代電子技術(shù),2008,31(6):153-155,158.
作者簡介 孫德輝 男,1962年出生,吉林人,教授,博士。研究方向?yàn)榫W(wǎng)絡(luò)控制理論與網(wǎng)絡(luò)自動(dòng)化技術(shù)、嵌入式技術(shù)與信息家電。
注:本文中所涉及到的圖表、注解、公式等內(nèi)容請(qǐng)以PDF格式閱讀原文