姜育生 梁妮 賀國(guó)旗
摘? 要:文章從嵌入式Linux系統(tǒng)下MDM9607平臺(tái)的SPI驅(qū)動(dòng)程序設(shè)計(jì)出發(fā),介紹了SPI通信流程和體系結(jié)構(gòu),分析了標(biāo)準(zhǔn)4線、擴(kuò)展6線SPI驅(qū)動(dòng)原理并給出了部分關(guān)鍵程序的代碼,通過(guò)軟件配置給出了可供上層調(diào)用的API函數(shù),并分別對(duì)標(biāo)準(zhǔn)4線、擴(kuò)展6線SPI驅(qū)動(dòng)進(jìn)行了測(cè)試驗(yàn)證,實(shí)驗(yàn)結(jié)果表明該類驅(qū)動(dòng)能夠精確地完成實(shí)時(shí)數(shù)據(jù)傳輸,對(duì)SPI外設(shè)的驅(qū)動(dòng)程序設(shè)計(jì)具有較強(qiáng)的參考意義,可廣泛應(yīng)用于CPE、4G模組、5G模組、物聯(lián)網(wǎng)等設(shè)備。
關(guān)鍵詞:MDM9607;Linux系統(tǒng);SPI總線;驅(qū)動(dòng)解析
中圖分類號(hào):TN92? 文獻(xiàn)標(biāo)識(shí)碼:A? 文章編號(hào):2096-4706(2023)02-0149-04
Analysis and Application of SPI Interface Driver Based on MDM9607 Platform
JIANG Yusheng, LIANG Ni, HE Guoqi
(Shaanxi Open University, Xi'an? 710119, China)
Abstract: Starting from the design of SPI driver for MDM9607 platform under embedded Linux system, this paper introduces the SPI communication process and architecture, analyzes the principle of standard 4-wire and extended 6-wire SPI driver, and gives the code of some key programs. Through software configuration, it gives the API functions that can be called by the upper layer, and tests and verifies the standard 4-wire and extended 6-wire SPI drivers respectively. The experimental results show that this kind of driver can accurately complete the real-time data transmission, which has strong reference significance for the driver design of SPI peripherals, and can be widely used in CPE, 4G module, 5G module, Internet of Things and other devices.
Keywords: MDM9607; Linux system; SPI bus; driver analysis
0? 引? 言
SPI(Serial Peripheral interface)是一種高速的、全雙工、同步的通信總線。SPI采用主—從模式(Master-Slave)的控制方式,采用CS片來(lái)控制主機(jī)與從機(jī)通信,主機(jī)或從機(jī)的數(shù)據(jù)在時(shí)鐘上升沿或降低沿同步,并能夠同時(shí)傳輸數(shù)據(jù)。本文基于Linux操作系統(tǒng)與MDM9607平臺(tái),分別介紹標(biāo)準(zhǔn)4線、擴(kuò)展6線SPI驅(qū)動(dòng)設(shè)計(jì)過(guò)程,并進(jìn)行功能測(cè)試驗(yàn)證[1]。
1? 硬件電路
SPI外接1.8 V的MCU電路參考設(shè)計(jì)(實(shí)際應(yīng)用中為匹配阻抗可能需要層串聯(lián)電阻),標(biāo)準(zhǔn)4線SPI各數(shù)據(jù)線引腳默認(rèn)1.8 V,不用則懸空。若另一端為3.3 V,則需要增加電平轉(zhuǎn)換芯片。如圖1所示。
SPI設(shè)備驅(qū)動(dòng),分為標(biāo)準(zhǔn)4線和擴(kuò)展6線兩種。4線:SPI_CS_N(SPI片選信號(hào))、SPI_MOSI(SPI數(shù)據(jù)輸出)、SPI_MISO(SPI數(shù)據(jù)輸入)、SPI_CLK(SPI時(shí)鐘),通常用來(lái)連接EEPROM、FLASH、LCD等,由模塊發(fā)起請(qǐng)求。
6線SPI主要比4線的SPI多了兩根控制線,主要用于通信前的握手功能,通信雙方都準(zhǔn)備好了才發(fā)送數(shù)據(jù),通常用來(lái)與MCU通信,模塊、MCU均可發(fā)起請(qǐng)求,相比串口通信也更高速。
相比4線SPI多出的兩個(gè)數(shù)據(jù)線功能為:
(1)SPI_MRDY:引腳用戶選擇,模塊輸出信號(hào),空閑為低;當(dāng)模塊要輸出數(shù)據(jù)時(shí),驅(qū)動(dòng)自動(dòng)拉高該P(yáng)IN。
(2)SPI_SRDY:引腳用戶選擇,SPI Slave ready信號(hào),空閑為低;當(dāng)SPISlave準(zhǔn)備好接收/發(fā)送數(shù)據(jù)時(shí),拉高該P(yáng)IN。
2? 通信流程
2.1? SPI Master發(fā)起請(qǐng)求
SPI Master發(fā)起請(qǐng)求流程如圖2所示。
SPI Master流程為:
(1)驅(qū)動(dòng)自動(dòng)拉高SPI_MRDY通知SPI Slave。
(2)判斷SPI_SRDY是否為高,否則等待SPI_SRDY的上升沿中斷。
(3)收到Slave上升沿,開(kāi)始SPI傳輸。
(4)傳輸完畢,如果要繼續(xù)發(fā)送數(shù)據(jù),則保持SPI_MRDY為高,并繼續(xù)第二步,否則拉低SPI_MRDY。
SPI Slave流程為:
(1)收到SPI_MRDY上升沿中斷,表示SPI Master需要發(fā)送數(shù)據(jù)。
(2)準(zhǔn)備好SPI傳輸,并拉高SPI_SRDY通知SPI Master開(kāi)始SPI傳輸。
(3)等待SPI傳輸結(jié)束,并拉低SPI_SRDY。
(4)如果SPI_MRDY為高,再繼續(xù)第二步。
2.2? SPI Slave發(fā)起請(qǐng)求
SPI Slave發(fā)起請(qǐng)求流程如圖3所示。
SPI Slave流程:
(1)準(zhǔn)備好SPI傳輸,并拉高SPI_SRDY。
(2)等待SPI傳輸結(jié)束,并拉低SPI_SRDY。
(3)如果要繼續(xù)發(fā)送數(shù)據(jù),則繼續(xù)第1步。
SPI Master流程:
(1)收到SPI_SRDY上升沿中斷,表示從機(jī)要發(fā)送數(shù)據(jù)。
(2)拉高SPI_MRDY,并開(kāi)始SPI傳輸。
(3)等待傳輸結(jié)束,并拉低SPI_MRDY[2,3]。
3? 軟件配置
3.1? SPI控制器配置說(shuō)明
Linux的SPI體系結(jié)構(gòu)分為以下3個(gè)組成部分。
3.1.1? SPI核心
SPI核心提供了SPI總線驅(qū)動(dòng)和設(shè)備驅(qū)動(dòng)的注冊(cè)、注銷方法、SPI通信方法,以及與具體控制器無(wú)關(guān)的代碼以及探測(cè)設(shè)備、檢測(cè)設(shè)備地址的上層代碼等。
3.1.2? SPI總線(控制器)驅(qū)動(dòng)
SPI總線驅(qū)動(dòng)是對(duì)SPI硬件體系控制器端的實(shí)現(xiàn),控制器由CPU控制,也可以直接集成在CPU內(nèi)部。
3.1.3? SPI 設(shè)備驅(qū)動(dòng)
SPI設(shè)備驅(qū)動(dòng)是對(duì)SPI硬件體系結(jié)構(gòu)中設(shè)備端的實(shí)現(xiàn),即客戶的SPI從設(shè)備驅(qū)動(dòng),設(shè)備一般掛接在受CPU控制的SPI控制器上,通過(guò)SPI控制器與CPU交換數(shù)據(jù)[4,5]。
以上3個(gè)部分,一般只需要關(guān)心和修改SPI設(shè)備驅(qū)動(dòng)。
SPI總線驅(qū)動(dòng):即SPI控制器,MDM9607平臺(tái)使用的是設(shè)備樹節(jié)點(diǎn)spi-qup-v2;其硬件參數(shù)配置,如所兼容的driver、引腳的選擇、寄存器地址、CLK、中斷號(hào),以及系統(tǒng)休眠和工作時(shí)的管腳配置等都已經(jīng)做好,不需要關(guān)心和修改。
3.2? SPI設(shè)備驅(qū)動(dòng)
3.2.1? 4線SPI設(shè)備驅(qū)動(dòng)
4線SPI設(shè)備驅(qū)動(dòng)位于ql-ol-kernel/drivers/spi/spidev.c,驅(qū)動(dòng)不使用設(shè)備樹傳參方式,直接insmod帶入?yún)?shù)更靈活,內(nèi)核模塊加載時(shí)支持的參數(shù)為:
(1)Busnum:SPI控制器編號(hào)為6,由下面代碼配置決定,此參數(shù)必須傳入,否則SPI從設(shè)備會(huì)找不到控制器而導(dǎo)致加載失敗。
aliases
{
/* smdtty devices*/
smd7 = &smdtty_data1;
smd8 = &smdtty_data4;
smd9 = &smdtty_data2;
smd21 = &smdtty_data21;
smd36 = &smdtty_loopback;
/*spi devices,Busnum = 6*/
spi6 = &spi_6;
i2c2 = &i2c_2;
i2c4 = &i2c_4;
sdhc2 = &sdhc_2;
}
(2)chipselect:片選支持0、1、2、3,此參數(shù)必須傳入,否則SPI設(shè)備會(huì)注冊(cè)失敗。
(3)spimode :SPI支持4種工作模式,其值為相位(CPHA 0x01)和極性(CPOL 0x02)的按位或,驅(qū)動(dòng)代碼默認(rèn)使用SPI_MODE_3模式,用戶可以在insmod時(shí)修改,時(shí)鐘極性CPOL:即SPI空閑時(shí),時(shí)鐘信號(hào)SCLK的電平(1:空閑時(shí)高電平,0:空閑時(shí)低電平),時(shí)鐘相位CPHA:即SPI在SCLK第幾個(gè)邊沿開(kāi)始采樣(0:第一個(gè)邊沿開(kāi)始,1:第二個(gè)邊沿開(kāi)始)[6,7]。
(4)maxspeed:可選參數(shù),驅(qū)動(dòng)默認(rèn)為9.6 MHz,實(shí)際支持的最大值由SPI控制器的配置決定,與理論最大值并不沖突;支持的可選值960 000、4 800 000、9 600 000、16 000 000、19 200 000、25 000 000、50 000 000。
(5)bufsize:可選參數(shù),設(shè)定SPI傳輸隊(duì)列中每個(gè)transfer的大小,默認(rèn)值4 096 Bytes,用戶可根據(jù)自己傳輸每次數(shù)據(jù)量的大小來(lái)設(shè)定。
(6)加載命令:insmod /lib/modules/3.18.20/kernel/drivers/
spi/spidev.ko busnum=6 chipselect=0 spimode=0 maxspeed=
19200000。
執(zhí)行l(wèi)smod查看載入系統(tǒng)的模塊,顯示:spidev 6473 0 - Live 0xbf03a000,再執(zhí)行l(wèi)s /dev/spidev6.0命令,顯示/dev/spidev6.0,設(shè)備加載成功[8,9]。
3.2.2? 6線SPI設(shè)備驅(qū)動(dòng)
6線SPI設(shè)備驅(qū)動(dòng)位于ql-ol-kernel/drivers/spi/quec_chn_spi.c,驅(qū)動(dòng)不使用設(shè)備樹傳參方式,直接從insmod 命令行帶入客戶參數(shù),內(nèi)核模塊加載時(shí)支持的參數(shù)為:
(1)Busnum數(shù)值和4線相同;chipselect片選和4線一致,可選參數(shù),驅(qū)動(dòng)默認(rèn)值為0;spimode和4線一致,驅(qū)動(dòng)代碼默認(rèn)使用SPI_MODE_0模式,用戶可以在insmod時(shí)修改;speed_hz和4線相同,frame_size默認(rèn)值512 Bytes。
(2)gpiomodemready設(shè)置SPI_MRDY,gpiomcuready設(shè)置SPI_SRDY,一般使用默認(rèn)管腳,可傳參修改。
加載命令:insmod /lib/modules/3.18.20/kernel/drivers/spi/quec_spi_chn.ko speed_hz=19200000
gpiomodemready=38 gpiomcuready=34。
執(zhí)行l(wèi)smod查看載入系統(tǒng)的模塊,顯示:quec_spi_chn 9069 0 - Live 0xbf03a000,再執(zhí)行l(wèi)s /dev/spidev6_0_*命令,顯示:/dev/spidev6_0_0到 /dev/spidev6_0_7,6線SPI驅(qū)動(dòng)虛擬出了8個(gè)數(shù)據(jù)通道,設(shè)備加載成功[10]。
4? SPI API
SDK中提供了一套完整的用戶編程接口,lib目錄下提供API接口庫(kù),include目錄是所有API的頭文件,通過(guò)上面的配置,SPI設(shè)備節(jié)點(diǎn)已經(jīng)注冊(cè)成功,可以直接使用下面API進(jìn)行應(yīng)用層操作,4線SPI操作API需要include頭文件ql_spi.h[11,12]。
4.1? Spi mode枚舉
SPI支持的工作模式:
typedef enum
{
SPIMODE0 = SPI_MODE_0,
SPIMODE1 = SPI_MODE_1,
SPIMODE2 = SPI_MODE_2,
SPIMODE3=SPI_MODE_3,
}SPI_MODE;
4.2? Spi clock枚舉
SPI默認(rèn)支持的時(shí)鐘大小:
typedef enum
{
S_960K = 960000,
S_4_8M = 4800000,
S_9_6M = 9600000,
S_16M = 16000000,
S_19_2M = 19200000,
S_25M = 25000000,
S_50M = 50000000,
}SPI_SPEED;
4.3? SPI_Init函數(shù)
打開(kāi)SPI設(shè)備并配置對(duì)應(yīng)的參數(shù)。int Ql_SPI_Init(char *dev_name,SPI_MODE mode,uint8_t bits, SPI_SPEED speed);參數(shù):dev_name(SPI設(shè)備,需要手動(dòng)加載spidev.ko);SPI_MODE(SPI4種工作模式,SPI_MODE枚舉值);bits(發(fā)送數(shù)據(jù)字的位數(shù),支持4,8,16,32);Speed(SPI控制器輸出時(shí)鐘,SPI_SPEED枚舉值)。
4.4? Ql_SPI_Write_Read函數(shù)
讀寫SPI數(shù)據(jù)。int Ql_SPI_Write_Read(int fd,uint8_t* write_buf,uint8_t* read_buf,uint32_t len);參數(shù):fd(spi設(shè)備文件描述符);write_buf(SPI寫數(shù)據(jù)指針);read _buf(SPI讀數(shù)據(jù)指針);len(讀寫數(shù)據(jù)長(zhǎng)度)。
SPI通信是全雙工的,只讀可以配置write_buf內(nèi)容為0,只寫可以丟棄read_buf內(nèi)容,由于標(biāo)準(zhǔn)SPI是讀寫在一個(gè)transfer里面,所有操作是全雙工的。向read_buf傳遞一個(gè)NULL,就是一次只寫操作,會(huì)丟棄MISO線上的數(shù)據(jù);同樣向write_buf傳遞一個(gè)NULL,就是一次只讀操作。
4.5? Ql_SPI_Deinit函數(shù)
關(guān)閉SPI設(shè)備。int Ql_SPI_Deinit(int fd);參數(shù):fd(SPI設(shè)備文件描述符)。
6線SPI驅(qū)動(dòng)同時(shí)虛擬出了8個(gè)數(shù)據(jù)通道留做備用,直接使用open、read、write來(lái)讀寫SPI設(shè)備,并使用select監(jiān)聽(tīng)設(shè)備實(shí)現(xiàn)異步通知[13,14]。
5? SPI功能測(cè)試驗(yàn)證
因?yàn)槲催B接spi slave設(shè)備,這里我們可以直接短接SPI_MOSI和SPI_MISO數(shù)據(jù)線進(jìn)行自發(fā)自收測(cè)試。
5.1? 4線SPI功能驗(yàn)證
以SPI_MODE_0,8bits/word,19.2M speed 初始化設(shè)備,向設(shè)備寫1 024個(gè)字節(jié),同時(shí)讀取1 024字節(jié)回,測(cè)試程序?yàn)椋?/p>
#define device "/dev/spidev6.0"
int main(int argc,char *argv[])
{
int fd;
int i;
uint8_t writebuf[1024];
uint8_t readbuf[1024];
fd = QL_SPI_Init(define,SPIMODE0,8,
S_19_2M);
for(i = 0;i < 1024;I++)
writebuf[i] = i % 256;
QL_SPI_Write_Read(fd,writebuf,readbuf,
1024);
for(i = 0;i < 1024;i++)
{
if(!(i % 32))
puts("");
printf("%.2X",readbuf[i]);
}
puts("");
}
在example/spi/std_spi目錄,make生成example_spi可執(zhí)行程序,加載完驅(qū)動(dòng)后執(zhí)行./ example_spi,結(jié)果為:
spi mode:0x0
bits per word:8
max speed:19200000Hz(19200kHz)
00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D
0E 0F……………………………………F0
F1 F2 F3 F4 F5 F6 F7 F8 F9 FA FB FC FD FE FF
5.2? 6線SPI功能驗(yàn)證
進(jìn)入到example/spi/six_line目錄,make生成example_six_line_spi可執(zhí)行程序,6線SPI除了短接SPI_MOSI和SPI_MISO以外,MRDY和SRDY也要進(jìn)行硬件連接,才能進(jìn)行自發(fā)自收測(cè)試[15,16]。加載完驅(qū)動(dòng)后執(zhí)行./ example_six_line_spi,結(jié)果為:
read 25 bytes hello,I am a six line spi
read 15 bytes test process!
通過(guò)實(shí)際的驗(yàn)證測(cè)試,4線和6線的SPI收到的數(shù)據(jù)與發(fā)出去的一致,可以正常使用。
6? 結(jié)? 論
本文針對(duì)MDM9607平臺(tái)的SPI驅(qū)動(dòng)展開(kāi)研究,重點(diǎn)闡述了軟件的配置流程,給出了相關(guān)API調(diào)用接口,并進(jìn)行了驗(yàn)證測(cè)試,測(cè)試結(jié)果證明性能穩(wěn)定。目前幾乎所有的處理器都支持SPI通信,尤其是對(duì)基于嵌入式Linux操作系統(tǒng)的驅(qū)動(dòng)程序開(kāi)發(fā),具有一定的借鑒意義。
參考文獻(xiàn):
[1] 徐陽(yáng).高速SPI接口電路的設(shè)計(jì)與驗(yàn)證 [D].西安:西安電子科技大學(xué),2020.
[2] 王鳴山.車聯(lián)網(wǎng)4GLTE模塊低紋波供電電源設(shè)計(jì) [J].單片機(jī)與嵌入式系統(tǒng)應(yīng)用,2019,19(11):47-52.
[3] 錢小東,宣浩,葛鵬.一種基于MLVDS的SPI通信鏈路設(shè)計(jì) [J].數(shù)字技術(shù)與應(yīng)用,2021,39(6):13-15.
[4] 閻景波,張磊,孫東亞.基于F28335的McBSP實(shí)現(xiàn)SPI接口的方法研究 [J].信息技術(shù)與信息化,2020(7):146-148.
[5] 陳亮,蔡聲鎮(zhèn),盧麗婉.一種面向存儲(chǔ)的高性能雙向SPI傳輸機(jī)制 [J].微電子學(xué)與計(jì)算機(jī),2021,38(3):72-76+83.
[6] 許云龍.基于APB總線的SPI接口的設(shè)計(jì)與實(shí)現(xiàn) [J].電子質(zhì)量,2020(7):128-132.
[7] 劉滿.SPI協(xié)議接口的設(shè)計(jì)與實(shí)現(xiàn) [D].西安:西安電子科技大學(xué),2020.
[8] 陸鵬.一種從機(jī)SPI通信接口的FPGA設(shè)計(jì)與實(shí)現(xiàn) [J].信息通信,2020(3):142-143.
[9] 芮正新,辛克廷.一種基于DMA方式SPI接口的通信方法 [J].儀器儀表與分析監(jiān)測(cè),2020(3):9-12.
[10] 宋風(fēng)雷.基于DSP的eCAN總線與SPI總線之間數(shù)據(jù)轉(zhuǎn)換器的設(shè)計(jì)與實(shí)現(xiàn) [D].長(zhǎng)沙:湖南大學(xué),2018.
[11] 高培,何棟煒,鄭瀟.ESP32SPISlave總線接口驅(qū)動(dòng)解析及應(yīng)用 [J].單片機(jī)與嵌入式系統(tǒng)應(yīng)用,2019,19(2):9-14.
[12] 趙冬青,梁璠,上官鵬,等.一種基于SPIFLASH的FPGA固件更新方法 [J].電子設(shè)計(jì)工程,2020,28(16):11-16.
[13] 李增科,李云鵬,席東學(xué).基于SPI協(xié)議的雙DSP通訊設(shè)計(jì)與實(shí)現(xiàn) [J].電子測(cè)量技術(shù),2020,43(19):159-164.
[14] 王俊,劉國(guó)美,李興智,等.一種使用SPI接口的DSP間數(shù)據(jù)交叉?zhèn)鬏敿夹g(shù) [J].信息技術(shù)與信息化,2021(5):206-207+210.
[15] 韓子川.28Gb/s光收發(fā)機(jī)SPI和UART的設(shè)計(jì) [D].南京:東南大學(xué),2019.
[16] 張拓智,孔德岐,朱恩亮,等.嵌入式Linux系統(tǒng)下的HI3210驅(qū)動(dòng)軟件設(shè)計(jì)與實(shí)現(xiàn)[J].航空計(jì)算技術(shù),2019,49(3):99-102.
作者簡(jiǎn)介:姜育生(1979—),男,漢族,陜西西安人,高級(jí)工程師,碩士,研究方向:通信技術(shù)。
收稿日期:2022-08-26
基金項(xiàng)目:陜西哲學(xué)社會(huì)科學(xué)重點(diǎn)研究基地項(xiàng)目(22JZ019)