精簡(jiǎn)ISA總線接口是一種8-bit寬度的雙向并行擴(kuò)展總線,其特點(diǎn)是地址數(shù)據(jù)分時(shí)復(fù)用8-bit總線,加上4條總線控制信號(hào),即可實(shí)現(xiàn)對(duì)外部數(shù)據(jù)的快速讀寫(xiě)。若再使能一條總線時(shí)鐘信號(hào)(共13條信號(hào)),就可實(shí)現(xiàn)高達(dá)10MB/s的數(shù)據(jù)傳輸。精簡(jiǎn)ISA總線作為英創(chuàng)主板的特色功能之一,在ESM6802、ESM7000、ESM7100、ESM335x等多款型號(hào)中均有配置。
對(duì)精簡(jiǎn)ISA總線接口的應(yīng)用編程,將通過(guò)3個(gè)部分分別加以介紹。本文是第一部分,主要介紹ISA總線的訪問(wèn)模式,以及最基本的總線數(shù)據(jù)讀寫(xiě)。第二部分介紹由應(yīng)用程序啟動(dòng)基于DMA的數(shù)據(jù)塊讀寫(xiě)。采用DMA方式實(shí)現(xiàn)數(shù)據(jù)庫(kù)讀寫(xiě),可有效降低CPU的負(fù)載率。第三部分介紹ISA擴(kuò)展硬件觸發(fā)DMA數(shù)據(jù)傳輸?shù)姆椒ǎ摲椒ㄖ饕嫦?a target="_blank">工業(yè)控制領(lǐng)域的高速數(shù)據(jù)采集。由于采用硬件觸發(fā)的DMA傳輸機(jī)制,使得前端的高速AD單元不再采用昂貴的高速SRAM做數(shù)據(jù)緩沖,從而使采集單元的成本大幅度下降。該方法可實(shí)現(xiàn)高達(dá)5MB/s(每秒5兆字節(jié))以上的數(shù)據(jù)采集率。
ISA總線信號(hào)定義如下:
信號(hào)及說(shuō)明 | PIN# | 信號(hào)及說(shuō)明 | |
RESET_B,硬件復(fù)位 | 1 | 2 | ISA_ADVn,地址鎖存控制信號(hào) |
ISA_AD0,地址數(shù)據(jù)總線,LSB | 3 | 4 | ISA_AD4,地址數(shù)據(jù)總線 |
ISA_AD1,地址數(shù)據(jù)總線 | 5 | 6 | ISA_AD5,地址數(shù)據(jù)總線 |
ISA_AD2,地址數(shù)據(jù)總線 | 7 | 8 | ISA_AD6,地址數(shù)據(jù)總線 |
ISA_AD3,地址數(shù)據(jù)總線 | 9 | 10 | ISA_AD7,地址數(shù)據(jù)總線,MSB |
MSLn,支持多模塊掛接總線 | 11 | 12 | ISA_WEn,數(shù)據(jù)寫(xiě)控制信號(hào) |
GPIO9,可選作為IRQ | 13 | 14 | ISA_RDn,數(shù)據(jù)讀控制信號(hào) |
GPIO8,可選作為IRQ | 15 | 16 | ISA_CSn,片選控制信號(hào) |
GPIO25,可選作為IRQ | 17 | 18 | VDD_5V0,+5V供電 |
GPIO24 / ISA_BCLK,同步時(shí)鐘ISA_BCLK | 19 | 20 | GND,電源信號(hào)地 |
本文以下部分,將以ESM7000 Linux平臺(tái)為例,介紹具體的編程方法。
ISA總線操作模式
精簡(jiǎn)ISA總線的寬度只有8-bit,因此它的地址尋址范圍也只有8-bit,即0x00 – 0xFF。ISA總線的驅(qū)動(dòng)程序支持應(yīng)用程序使用不同的地址范圍,來(lái)區(qū)別不同的訪問(wèn)類(lèi)型。根據(jù)ISA地址可有如下6種類(lèi)型:
模式 | offset | ISA總線操作 |
0 | 0x0000 … 0x00FF | 異步總線周期,CPU操作 |
1 | 0x1000 … 0x10FF | 異步總線周期,MemCpy方式DMA,固定端口地址 |
2 | 0x2000 … 0x20FF | 異步總線周期,外部信號(hào)觸發(fā)方式DMA,固定端口地址 |
3 | 0x3000 … 0x30FF | 同步總線周期,CPU操作,要求ISA端口地址16字節(jié)對(duì)齊 |
4 | 0x4000 … 0x40FF | 同步總線周期,MemCpy方式DMA,同步譯碼端口 |
5 | 0x5000 … 0x50FF | 同步總線周期,外部信號(hào)觸發(fā)方式DMA,同步譯碼端口 |
ISA總線的異步總線周期是指每個(gè)總線周期讀寫(xiě)一個(gè)字節(jié),只需用到ISA最基本的12條信號(hào)線;而同步總線周期則需要使用總線時(shí)鐘信號(hào)(共13條信號(hào)線),可實(shí)現(xiàn)一個(gè)總線周期讀寫(xiě)4個(gè)字節(jié)。在《在英創(chuàng)工控主板上實(shí)現(xiàn)高速工控?cái)?shù)據(jù)采集》一文中有對(duì)異步總線周期時(shí)序和同步總線周期時(shí)序較詳細(xì)的介紹。
由iMX7DSDMA(Smart DMA)控制器特性決定,ESM7000可支持的ISA總線操作類(lèi)型如下:
模式 | offset | ISA總線讀操作 |
0 | 0x0000 … 0x00FF | 異步總線周期,CPU讀,inc僅對(duì)本模式有效 |
3 | 0x3000 … 0x30FF | 同步總線周期,CPU讀,要求ISA端口地址16字節(jié)對(duì)齊 |
4 | 0x4000 … 0x40FF | 同步總線周期,MemCpy方式DMA讀,同步譯碼端口 |
5 | 0x5000 … 0x50FF | 同步總線周期,外部信號(hào)觸發(fā)方式DMA讀,同步譯碼端口 |
模式 | offset | ISA總線寫(xiě)操作 |
0 | 0x0000 … 0x00FF | 異步總線周期,CPU寫(xiě),inc僅對(duì)本模式有效 |
3 | 0x3000 … 0x30FF | 同步總線周期,CPU寫(xiě),要求ISA端口地址16字節(jié)對(duì)齊 |
4 | 0x4000 … 0x40FF | 同步總線周期,MemCpy方式DMA寫(xiě),同步譯碼端口 |
同步總線周期主要用于數(shù)據(jù)的高速傳送,因此采用固定數(shù)據(jù)端口。數(shù)據(jù)端口的譯碼則采用直接識(shí)別同步總線周期的方法,而不再采用對(duì)總線地址譯碼的方法。異步總線周期則主要用于擴(kuò)展電路單元內(nèi)各寄存器的控制。
采用DMA進(jìn)行ISA總線數(shù)據(jù)傳送的目的,是為了降低高速傳送大量數(shù)據(jù)時(shí)的CPU開(kāi)銷(xiāo),使系統(tǒng)在進(jìn)行高速數(shù)據(jù)采集的同時(shí),還能進(jìn)行其他的必要操作。MemCpy方式的DMA是指軟件線程啟動(dòng)DMA,然后該線程掛起等待DMA操作完成。在多線程環(huán)境中,其他線程即可在DMA執(zhí)行過(guò)程中得以并行運(yùn)行。MemCpy方式DMA的具體情況將在《精簡(jiǎn)ISA總線編程– Part 2》中介紹。硬件觸發(fā)DMA,為低成本實(shí)現(xiàn)高速數(shù)據(jù)采集提供了技術(shù)手段,將在《精簡(jiǎn)ISA總線編程– Part 3》中介紹相關(guān)的采集時(shí)序和程序?qū)崿F(xiàn)。
表格中的offset項(xiàng),是指在程序設(shè)計(jì)中使用的數(shù)據(jù)結(jié)構(gòu)struct isa_transfer的成員,其結(jié)構(gòu)如下:
structisa_transfer { void *rx_buf; /* != NULL: buffer for bus read */ void *tx_buf; /* != NULL: buffer for bus write */ unsigned len; /* buffer length in byte */ unsigned offset; /* offset,port address on isa bus */ unsigned inc; /* = 0: fixed offset, = 1: offset+1 after r/w */ }; |
每一個(gè)總線周期的操作只能是讀或?qū)懀虼嗽趇sa_transfer結(jié)構(gòu)中只能有一個(gè)buffer指針不為NULL。
ISA總線訪問(wèn)API
對(duì)ISA總線硬件端口的基本訪問(wèn)方法,包含在isa_api_v3.cpp文件中,如下所示:
#include #include #include #include #include #include #include"em335x_drivers.h" unsignedchar *isa_base = NULL; unsignedintmap_size = 4096; // return >= 0: return file handle // return < 0: return failed code intisa_open() { intfd; fd = open("/dev/em_isa", O_RDWR); if(fd< 0) { returnfd; } isa_base = (unsignedchar*)mmap(0,map_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); if (isa_base == MAP_FAILED) { printf("%s mmap failed\n", __func__); isa_base = NULL; close(fd); return -1; } returnfd; } intisa_close(intfd) { if(isa_base != NULL) { munmap(isa_base, map_size); isa_base = NULL; } returnclose(fd); } // offset: port address in isa bus unsignedcharisa_read(intfd, unsignedint offset) { unsignedcharval_b; int rc; offset&= 0xff; if(isa_base != NULL) { unsignedshortintval_w; val_w = *((unsignedshortint*)(isa_base + (offset << 1))); val_b = (unsignedchar)(val_w& 0xff); returnval_b; } val_b = offset; rc = read(fd, &val_b, sizeof(unsignedchar)); if(rc< 0) { // read error printf("%s failed %d\n", __func__, rc); } returnval_b; } // offset: port address in isa bus voidisa_write(intfd, unsignedint offset, unsignedcharval_b) { unsignedshortintval_w; int rc; offset&= 0xff; if(isa_base != NULL) { val_w = val_b; *((unsignedshortint*)(isa_base + (offset << 1))) = val_w; return; } val_w = ((offset & 0xff) << 8) | val_b; rc = write(fd, &val_w, sizeof(unsignedshortint)); if(rc< 0) { // read error printf("%s failed %d\n", __func__, rc); } } // offset: port address in isa bus unsignedshortintisa_read16(intfd, unsignedint offset) { unsignedshortintval_w; int rc; // 2-byte alignment is required for offset offset&= 0xfe; if(isa_base != NULL) { unsignedintval; val = *((unsignedint*)(isa_base + (offset << 1))); val_w = (unsignedshortint)((val>> 8) | (val& 0x00ff)); returnval_w; } val_w = (unsignedshortint)offset; rc = read(fd, &val_w, sizeof(unsignedshortint)); if(rc< 0) { // read error printf("%s failed %d\n", __func__, rc); } returnval_w; } // offset: port address in isa bus voidisa_write16(intfd, unsignedint offset, unsignedshortintval_w) { unsignedintval; intrc; // 2-byte alignment is required for offset offset&= 0xfe; if(isa_base != NULL) { val = val_w; val = ((val<< 8) & 0x00ff0000) | (val& 0x000000ff); *((unsignedint*)(isa_base + (offset << 1))) = val; return; } val = (offset << 16) | val_w; rc = write(fd, &val, sizeof(unsignedint)); if(rc< 0) { // read error printf("%s failed %d\n", __func__, rc); } } intisa_read_buf(intfd, structisa_transfer *tp) { int rc; rc = read(fd, tp, sizeof(structisa_transfer)); if(rc< 0) { // read error printf("%s failed %d\n", __func__, rc); } returnrc; } intisa_write_buf(intfd, structisa_transfer *tp) { int rc; rc = write(fd, tp, sizeof(structisa_transfer)); if(rc< 0) { // read error printf("%s failed %d\n", __func__, rc); } returnrc; } |
函數(shù)isa_open(..)和isa_close(..)是打開(kāi)關(guān)閉ISA驅(qū)動(dòng)的設(shè)備節(jié)點(diǎn)。為了提高應(yīng)用程序訪問(wèn)ISA硬件寄存器的速度,ISA驅(qū)動(dòng)實(shí)現(xiàn)了mmap功能,使isa_read(..)、isa_write(..)、isa_read16(..)和isa_write16(..)這4個(gè)函數(shù)可在用戶空間直接操作ISA總線上的硬件寄存器,大大提高了代碼的執(zhí)行速度。注意若在多個(gè)線程中均有調(diào)用isa_api_v3.cpp的函數(shù)時(shí),需要加互斥鎖,保證對(duì)硬件資源操作的完整性。
CPU字節(jié)/字讀寫(xiě)
函數(shù)isa_write(fd, 0x40, 0x55)是向地址為0x40的寄存器寫(xiě)入單個(gè)字節(jié)0x55,對(duì)應(yīng)的總線時(shí)序?yàn)椋?/p>
時(shí)序圖顯示了8位總線的地址/數(shù)據(jù)復(fù)用,地址需利用ADV#信號(hào)鎖存。
函數(shù)isa_read(fd, 0x40)是從地址為0x40的寄存器讀取單個(gè)字節(jié),對(duì)應(yīng)的總線時(shí)序?yàn)椋?/p>
在上述時(shí)序中,ISA總線懸空,沒(méi)有接具體的外圍設(shè)備,因此由于分布電容的作用,在數(shù)據(jù)時(shí)段總線繼續(xù)保持前面輸出的地址0x40,讀取的數(shù)據(jù)也會(huì)是0x40。若此時(shí)有外圍設(shè)備輸出數(shù)據(jù)至總線上,該數(shù)據(jù)則會(huì)被系統(tǒng)讀取。
函數(shù)isa_write16(fd, 0x40, 0xaa55)是向地址為0x40的字寄存器寫(xiě)入16-bit字0xaa55,對(duì)應(yīng)的總線時(shí)序?yàn)椋?/p>
從時(shí)序圖可見(jiàn),16-bit數(shù)據(jù)寫(xiě)被分成兩個(gè)連續(xù)的總線周期,其中低位字節(jié)0x55寫(xiě)入地址0x40寄存器,高位字節(jié)0xaa寫(xiě)入地址0x41寄存器。為了盡可能縮短2個(gè)周期的間隔,要求地址必須是偶對(duì)齊。
函數(shù)isa_read16(fd, 0x40)是從地址為0x40的字寄存器讀取16-bit字,對(duì)應(yīng)的總線時(shí)序?yàn)椋?/p>
同樣由于懸空總線的分布電容的作用,讀取的數(shù)據(jù)會(huì)是0x4140。
CPU數(shù)據(jù)塊讀寫(xiě)
為了提高數(shù)據(jù)傳輸速度,對(duì)數(shù)據(jù)塊操作,推薦采用同步總線周期。在ESM7000 Linux版本中,每個(gè)同步總線周期可傳送4個(gè)字節(jié)。為了進(jìn)一步加快數(shù)據(jù)塊的傳送,在驅(qū)動(dòng)中使用了ARMv7的匯編塊指令來(lái)支持16字節(jié)以上的數(shù)據(jù)傳輸。因此強(qiáng)烈建議數(shù)據(jù)塊傳輸長(zhǎng)度選擇為16字節(jié)的整倍數(shù),其數(shù)據(jù)端口占用16個(gè)ISA地址,即只對(duì)高4位地址譯碼。
進(jìn)行數(shù)據(jù)塊讀寫(xiě)需要用到structisa_transfer傳遞相關(guān)參數(shù)。以下是執(zhí)行32字節(jié)數(shù)據(jù)塊寫(xiě)的代碼,寫(xiě)入地址為0x3040。順序的數(shù)據(jù)可方便時(shí)序的觀察。
unsignedchargbuf[64 * 1024]; unsignedint i, value; structisa_transfer t; unsignedchar *pBuf8; // write data block memset(&t, 0, sizeof(structisa_transfer)); t.offset = 0x3040; t.len = 32; t.tx_buf = gbuf; // fill data value = 0x55; // initial value pBuf8 = (unsignedchar*)t.tx_buf; for(i = 0; i *pBuf8 = (unsignedchar)(value + i); pBuf8++; } isa_write_buf(fd, &t); |
對(duì)應(yīng)的總線時(shí)序說(shuō)明如下:
上圖可見(jiàn),32個(gè)字節(jié)分成了8個(gè)總線周期完成,大致的總線速率為13MB/s。展開(kāi)上述時(shí)序可看到:
總線上的數(shù)據(jù)0x55、0x59、…... 0x6D、0x71是每個(gè)總線周期CPU送出的第一個(gè)數(shù)據(jù),保持3個(gè)時(shí)鐘節(jié)拍,所以可在示波器中顯示出來(lái)。進(jìn)一步展開(kāi)可看到:
ISA擴(kuò)展單元可根據(jù)上述時(shí)序來(lái)支持高速的同步總線周期讀寫(xiě)邏輯電路,其要點(diǎn)包括:
●每個(gè)周期的第一個(gè)BCLK下降沿ADV#有效,標(biāo)志同步周期的開(kāi)始,之后連續(xù)7個(gè)BCLK下降沿后同步周期結(jié)束。
●第一個(gè)總線周期地址為輸入地址,之后每個(gè)總線周期的地址+4,按16模循環(huán)。因此要求數(shù)據(jù)端口占用16個(gè)ISA地址。
●BCLK頻率30MHz,從第5個(gè)上升沿開(kāi)始鎖存數(shù)據(jù),連續(xù)4個(gè)數(shù)據(jù)。沒(méi)有BCLK時(shí)輸出的數(shù)據(jù)沒(méi)有意義,不影響正常的數(shù)據(jù)傳輸。
以上是對(duì)精簡(jiǎn)ISA總線基本讀寫(xiě)的介紹,有興趣的客戶可與英創(chuàng)公司技術(shù)聯(lián)系,索取測(cè)試代碼源碼。技術(shù)支持郵箱:support@emtronix.com。
-
Linux
+關(guān)注
關(guān)注
87文章
11209瀏覽量
208721 -
嵌入式主板
+關(guān)注
關(guān)注
7文章
6084瀏覽量
35154
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論