段月驍 郭 斌 胡曉峰 羅 哉 陸 藝
(中國(guó)計(jì)量學(xué)院計(jì)量測(cè)試工程學(xué)院,杭州 310018)
在自動(dòng)化測(cè)試系統(tǒng)中,處理器往往需要通過(guò)模數(shù)轉(zhuǎn)換器獲取傳感器采集到的原始數(shù)據(jù)。S3C2440A微處理器自帶八通道10位的AD轉(zhuǎn)換器,在測(cè)量精度要求不高時(shí)可以直接使用。在實(shí)際應(yīng)用中,為滿足高精度測(cè)量要求,需要外擴(kuò)模數(shù)轉(zhuǎn)換器。筆者選用24位分辨率帶SPI接口的模數(shù)轉(zhuǎn)換器ADS1256作為S3C2440A的外部AD轉(zhuǎn)換器。SPI是一種高速、全雙工、同步通信總線。因其傳輸穩(wěn)定、高效、占用引腳數(shù)量少,在模數(shù)轉(zhuǎn)換器、Flash及MCU等串行設(shè)備中應(yīng)用廣泛。Linux是全球知名的開源自由多任務(wù)操作系統(tǒng),任何人都可以在遵循GPL(General Public License)協(xié)議的基礎(chǔ)上修改其源代碼以適應(yīng)實(shí)際需求。正是Linux操作系統(tǒng)的免費(fèi)、開放源代碼及內(nèi)核可裁剪等諸多優(yōu)點(diǎn),使其廣泛應(yīng)用于智能儀表及工業(yè)控制等嵌入式系統(tǒng)中。
筆者采用S3C2440A微處理器和嵌入式Linux操作系統(tǒng)作為開發(fā)平臺(tái),結(jié)合實(shí)際工程氣密性檢測(cè)系統(tǒng),設(shè)計(jì)S3C2440A與ADS1256的SPI接口,詳細(xì)分析了Linux2.6.32.2下ADS1256驅(qū)動(dòng)程序的設(shè)計(jì)、編譯、加載和測(cè)試過(guò)程。
筆者選用S3C2440A微處理器,該處理器采用ARM920T的核心,0.13μm的CMOS標(biāo)準(zhǔn)宏單元和存儲(chǔ)單元,主頻400MHz,最高工作頻率可達(dá)533MHz。S3C2440A提供了兩個(gè)SPI接口,每個(gè)接口分別包含兩個(gè)8位發(fā)送移位寄存器和接收移位寄存器,通過(guò)串行時(shí)鐘線(SCK)、主機(jī)輸出從機(jī)輸入(MOSI)、主機(jī)輸入從機(jī)輸出(MISO)和片選控制線(nSS)與外圍設(shè)備進(jìn)行通信。處理器帶有增強(qiáng)型ARM架構(gòu)內(nèi)存管理單元(MMU),支持Linux及Windows CE等多任務(wù)操作系統(tǒng),可擴(kuò)展能力強(qiáng)。
ADS1256是24位八通道Sigma-Delta(Σ-Δ)高分辨率低噪聲模數(shù)轉(zhuǎn)換芯片。ADS1256通過(guò)串行總線SPI與MCU進(jìn)行數(shù)據(jù)通信,簡(jiǎn)化了接口電路設(shè)計(jì)。芯片支持對(duì)PGA(可編程增益放大器)設(shè)置所造成的偏移與增益誤差進(jìn)行自校準(zhǔn)和系統(tǒng)校準(zhǔn)。芯片帶有通用數(shù)字I/O口和可編程時(shí)鐘輸出。
LM285D-2.5是低動(dòng)態(tài)阻抗、低溫度系數(shù)和低噪聲的電壓基準(zhǔn)芯片。設(shè)計(jì)中LM285D-2.5的電源電壓為5.0V,輸出電壓2.5V,為ADS1256模數(shù)轉(zhuǎn)換提供穩(wěn)定的參考電壓。
實(shí)際工程中需采集氣密性檢測(cè)系統(tǒng)中的壓差信號(hào)和壓力信號(hào)。差壓傳感器和壓力傳感器輸出的模擬信號(hào)由ADS1256轉(zhuǎn)換成數(shù)字信號(hào)后通過(guò)SPI接口傳入S3C2440A。ADS1256屬于高分辨率的模數(shù)轉(zhuǎn)換器,其外圍電路設(shè)計(jì)需要尤其注意以保證數(shù)據(jù)轉(zhuǎn)換精度。ADS1256外圍電路和S3C2440A的接口電路如圖1所示,ADS1256除了包含標(biāo)準(zhǔn)SPI總線的信號(hào)線SCLK、DIN、DOUT、CS外,還帶有指示數(shù)據(jù)是否已轉(zhuǎn)換好的標(biāo)志信號(hào)線DRDY(低有效)與串口配合使用。MCU可以把DRDY作為外部中斷源信號(hào),或者通過(guò)查詢方式訪問(wèn)ADS1256狀態(tài)寄存器中的DRDY位來(lái)判斷是否有數(shù)據(jù)可讀。此處ADS1256的信號(hào)線SCLK、DIN、DOUT、CS、DRDY分別接S3C2440A的信號(hào)線SPICLK0、SPIMOSI0、SPIMISO、nSS0、EINT0。LM285D-2.5與ADS1256的連接電路如圖2所示。LM285D-2.5的輸出VREFN和VREFP為ADS1256數(shù)模轉(zhuǎn)換提供穩(wěn)定的2.5V工作電壓。
圖1 ADS1256外圍電路和S3C2440A的SPI接口電路
圖2 LM285D-2.5與ADS1256的連接電路
設(shè)備驅(qū)動(dòng)程序是連接應(yīng)用程序和實(shí)際硬件的紐帶,它使得應(yīng)用軟件只需要調(diào)用操作系統(tǒng)的應(yīng)用編程接口(API)就可讓硬件完成要求的工作。Linux的外設(shè)分為3類:字符設(shè)備、塊設(shè)備和網(wǎng)絡(luò)接口[1]。模數(shù)轉(zhuǎn)換器ADS1256屬于字符型設(shè)備,字符型設(shè)備是使用最廣泛的一類外設(shè)。筆者選用Linux2.6.32.2為嵌入式操作系統(tǒng),arm-linux-gcc 4.4.3交叉編譯器用于編譯生成目標(biāo)驅(qū)動(dòng)程序。Linux給用戶提供了兩種開發(fā)設(shè)備驅(qū)動(dòng)的方式:設(shè)計(jì)成可加載模塊和直接編譯到Linux內(nèi)核中。前者適合驅(qū)動(dòng)處于開發(fā)調(diào)試的階段,驅(qū)動(dòng)開發(fā)采用模塊化思想,使得不需要修改內(nèi)核代碼,單獨(dú)編譯所開發(fā)的驅(qū)動(dòng)模塊即可,可以使用命令insmod和rmmod動(dòng)態(tài)加載和卸載驅(qū)動(dòng)。后者適合驅(qū)動(dòng)功能已經(jīng)調(diào)試成功的階段,直接編譯進(jìn)內(nèi)核后,該驅(qū)動(dòng)就能隨機(jī)啟動(dòng),應(yīng)用程序可以直接調(diào)用相應(yīng)的接口以操作硬件。為方便調(diào)試,筆者使用前者。
在加載驅(qū)動(dòng)程序時(shí),系統(tǒng)將首先調(diào)用驅(qū)動(dòng)的入口函數(shù)即初始化函數(shù),完成存儲(chǔ)空間分配、SPI0配置和字符設(shè)備注冊(cè)工作。初始化流程如圖3所示。
圖3 初始化流程
部分程序如下:
static int __init ads1256_spi0_init(void)
{
int ret;
spi_gpacon=ioremap(0x56000000, 4); //以下采用ioremap函數(shù)實(shí)現(xiàn)重定向功能,使定義的指針指向相應(yīng)的寄存器
…
s3c2410_gpio_cfgpin(S3C2410_GPE(11), S3C2410_GPE11_SPIMISO0); //設(shè)置SPI0端口
…
*spi_sppin0 = (0<<2) | (1<<1) | (0<<0) ; //配置SPI0的相關(guān)寄存器
…
*spi_clkcon |= (1<<18); //使能SPI時(shí)鐘
ret = register_chrdev(ADS1256_MAJOR, DEVICE_NAME, &ads1256_spi0_fops); //調(diào)用函數(shù)register_chrdev進(jìn)行設(shè)備注冊(cè)
…
}
在Linux下,無(wú)論是用戶程序還是內(nèi)核程序,都不能直接訪問(wèn)物理內(nèi)存,必須先通過(guò)函數(shù)ioremap()把物理地址映射成相應(yīng)的虛擬地址,再通過(guò)虛擬地址來(lái)對(duì)內(nèi)存進(jìn)行訪問(wèn)[2]。所以在ads1256_spi0_init中,首先完成SPI0配置寄存器的地址映射。其次完成SPI0端口和工作模式寄存器的設(shè)置。最后調(diào)用函數(shù)register_chrdev完成字符設(shè)備的注冊(cè)。退出函數(shù)static void _exit ads1256_spi0_exit(void)主要負(fù)責(zé)完成與初始化函數(shù)相反的操作,包括使用函數(shù)unregister_chrdev注銷設(shè)備和使用函數(shù)iounmap取消地址映射。
結(jié)構(gòu)體file_operations中的成員全部是函數(shù)指針。這些函數(shù)指針?biāo)赶虻暮瘮?shù)就是驅(qū)動(dòng)程序與Linux內(nèi)核的接口,是用戶空間對(duì)Linux進(jìn)行系統(tǒng)調(diào)用最終的落實(shí)者[3]。編寫字符設(shè)備驅(qū)動(dòng)程序的主要任務(wù)就是實(shí)現(xiàn)file_operations結(jié)構(gòu)體中函數(shù)指針?biāo)赶虻暮瘮?shù)[4]。Linux內(nèi)核提供了功能豐富的接口函數(shù),如open、write及ioctl等,設(shè)計(jì)驅(qū)動(dòng)程序時(shí)只要實(shí)現(xiàn)實(shí)際需要的接口函數(shù),其他未實(shí)現(xiàn)的部分不對(duì)驅(qū)動(dòng)程序造成影響。
本驅(qū)動(dòng)程序定義的file_operations為:
static struct file_operations ads1256_spi0_fops = {
.owner=THIS_MODULE,
.open=ads1256_spi0_open,
.write=ads1256_spi0_write,
.read=ads1256_spi0_read,
.release=ads1256_spi0_close,
}
函數(shù)ads1256_spi0_open和ads1256_spi0_close分別用于打開和關(guān)閉ADS1256設(shè)備,將模塊的使用計(jì)數(shù)加1和減1;ads1256_spi0_write用于向ADS1256寄存器組發(fā)送設(shè)置參數(shù)以控制設(shè)備的數(shù)據(jù)轉(zhuǎn)換模式,如PGA、數(shù)據(jù)輸出速率及是否自校準(zhǔn)等;ads1256_spi0_read用于讀取指定采集通道的轉(zhuǎn)換數(shù)據(jù)。
讀寫設(shè)備時(shí)需要在內(nèi)核地址空間和用戶地址空間之間傳輸數(shù)據(jù)。在Linux中,跨空間復(fù)制是通過(guò)定義在
在本驅(qū)動(dòng)程序中,用戶程序通過(guò)函數(shù)static ssize_t ads1256_spi0_write(struct file *filp, const char *buf, size_t count, loff_t *f_ops)實(shí)現(xiàn)對(duì)ADS1256的配置。在該函數(shù)中通過(guò)copy_from_user(dataTx, buf, count)把用戶空間的設(shè)置參數(shù)buf復(fù)制到內(nèi)核空間的dataTx中,然后在SPI0狀態(tài)寄存器的數(shù)據(jù)發(fā)送/接收準(zhǔn)備位為1時(shí),將dataTx寫入SPI0的發(fā)送數(shù)據(jù)寄存器中以實(shí)現(xiàn)對(duì)ADS1256工作模式的設(shè)置。
函數(shù)的關(guān)鍵代碼如下:
if(copy_from_user(dataTx, buf, count))
{
return-EFAULT;
}
*spi_gpgdat &= ~(1<<2); //使能ADS1256
udelay(500); //等待ADS1256穩(wěn)定
for(x = 0; x { while(!(*spi_spsta0&0x01)); //SPI0是否準(zhǔn)備好發(fā)送/接收數(shù)據(jù)? *spi_sptdat0 = dataTx[x]; udelay(8); } *spi_gpgdat |= (1<<2); //停用 ADS1256 本驅(qū)動(dòng)中的函數(shù)static ssize_t ads1256_spi0_read(struct file *filp,char *buf,size_t count,loff_t *f_ops)實(shí)現(xiàn)接收ADS1256的轉(zhuǎn)換結(jié)果。因?yàn)镾3C2440A的SPI接收寄存器為8位移位寄存器,要接收24位的ADS1256轉(zhuǎn)換數(shù)據(jù),需要分3次順序讀取SPI0的接收數(shù)據(jù)寄存器。函數(shù)copy_to_user(buf, dataRx, count)負(fù)責(zé)把內(nèi)核空間的數(shù)據(jù)dataRx復(fù)制到用戶空間的buf中。 函數(shù)的關(guān)鍵代碼如下: *spi_gpgdat &= ~(1<<2); //使能 ADS1256 udelay(500); //等待ADS1256穩(wěn)定 for(x = 0; x { while(!(*spi_spsta0&0x01)); // SPI0是否準(zhǔn)備好發(fā)送/接收數(shù)據(jù)? dataRx[x] = *spi_sprdat0; *spi_sptdat0 = 0xFF; } *spi_gpgdat |= (1<<2); //停用ADS1256 copy_to_user(buf, dataRx, count); 完成驅(qū)動(dòng)源程序后需要編寫Makefile文件用于編譯驅(qū)動(dòng)模塊,Makefile的作用是制定Linux內(nèi)核編譯設(shè)備驅(qū)動(dòng)程序的規(guī)則,如指定Linux的內(nèi)核版本、驅(qū)動(dòng)源文件所在的目錄及目標(biāo)驅(qū)動(dòng)的名稱等。本項(xiàng)目使用的Makefile文件格式如下: KERN_DIR = /opt/FriendlyARM/mini2440/linux-2.6.32.2 //編譯驅(qū)動(dòng)所需的內(nèi)核版本 all: make -C S|(KERN_DIR) M=′pwd′ modules //使用KERN_DIR中的Makefile編譯當(dāng)前目錄下的驅(qū)動(dòng)源文件 obj-m+= ads1256_drv.o //編譯生成目標(biāo)驅(qū)動(dòng)模塊ads1256_drv 編譯完成驅(qū)動(dòng)程序后,在控制臺(tái)輸入insmod ads1256_drv.ko加載ADS1256驅(qū)動(dòng)模塊。Linux將實(shí)際的外圍硬件在內(nèi)核中映射為相應(yīng)的設(shè)備節(jié)點(diǎn),使用戶程序控制實(shí)際硬件如同操作普通文件,輸入命令mknod /dev/ads1256_spi0 c 153 0創(chuàng)建ADS1256的字符設(shè)備節(jié)點(diǎn),這里c代表此驅(qū)動(dòng)程序是字符型設(shè)備驅(qū)動(dòng),153是主設(shè)備號(hào),0是次設(shè)備號(hào)。應(yīng)用程序通過(guò)open("/dev/ads1256_spi0",O_RDWR | O_NOCTTY)打開設(shè)備,調(diào)用驅(qū)動(dòng)程序提供的接口函數(shù)即可對(duì)ADS1256進(jìn)行相應(yīng)的讀寫操作。 圖4為ADS1256驅(qū)動(dòng)模塊加載后控制臺(tái)的輸出結(jié)果。 圖4 ADS1256驅(qū)動(dòng)模塊加載測(cè)試結(jié)果 由圖4可知,在加載模塊后,Linux依次正確地完成了寄存器映射、SPI0配置和驅(qū)動(dòng)模塊注冊(cè),輸入命令lsmod可以查詢到該驅(qū)動(dòng)。 將驅(qū)動(dòng)應(yīng)用在氣密性檢測(cè)系統(tǒng)中,ADS1256接壓力傳感器和差壓傳感器進(jìn)行工程測(cè)試。以壓力傳感器信號(hào)采集為例說(shuō)明驅(qū)動(dòng)測(cè)試情況,壓力傳感器采用Huba 511型陶瓷壓力傳感器,24V直流供電,采集的氣壓量程為0~1MPa。壓力傳感器的4~20mA電流信號(hào),通過(guò)250Ω精密電阻轉(zhuǎn)換成對(duì)應(yīng)的1~5V電壓信號(hào)。ADS1256輸入電壓量程為0~5V,24位分辨率對(duì)應(yīng)的輸出值為0~16 777 215。在控制臺(tái)輸入命令./ads1256_test,開始運(yùn)行工程測(cè)試程序。筆者設(shè)置采樣周期為1s,采樣10次,采樣結(jié)果如圖5所示,計(jì)算得到采樣平均值為11 817 308,對(duì)應(yīng)的測(cè)試氣壓為630.458kPa,與數(shù)字壓力表測(cè)量的630.500kPa相符,ADS1256驅(qū)動(dòng)成功。 圖5 采樣通道的AD轉(zhuǎn)換結(jié)果 設(shè)計(jì)了微處理器S3C2440A和模數(shù)轉(zhuǎn)換器ADS1256之間的硬件接口電路,詳細(xì)介紹了在嵌入式操作系統(tǒng)Linux 2.6.32.2下基于SPI總線的ADS1256驅(qū)動(dòng)程序的實(shí)現(xiàn)過(guò)程。將ADS1256應(yīng)用在氣密性檢測(cè)系統(tǒng)中,測(cè)試得到正確可靠的數(shù)據(jù)轉(zhuǎn)換結(jié)果,表明硬件設(shè)計(jì)正確,驅(qū)動(dòng)工作正常,為高精度多通道數(shù)據(jù)采集的嵌入式系統(tǒng)提供了一種解決方案。 [1] 宋寶華.Linux設(shè)備驅(qū)動(dòng)開發(fā)詳解[M].北京:人民郵電出版社,2011:118~138. [2] 尉軍軍.基于嵌入式系統(tǒng)的電流互感器準(zhǔn)確度測(cè)試儀的設(shè)計(jì)與實(shí)現(xiàn)[D].鎮(zhèn)江:江蘇大學(xué),2010:56~60. [3] 韋東山.嵌入式Linux應(yīng)用開發(fā)完全手冊(cè)[M].北京:人民郵電出版社,2008:384~389. [4] 朱園.嵌入式Linux設(shè)備驅(qū)動(dòng)的研究與開發(fā)[D].北京:北京郵電大學(xué),2008:29~32.2.4 讀函數(shù)
2.5 驅(qū)動(dòng)模塊編譯和加載
3 驅(qū)動(dòng)在氣密性檢測(cè)系統(tǒng)中的應(yīng)用
4 結(jié)束語(yǔ)