在ARM Linux下使用GPIO模拟SPI时序详解
一、???????概述
SPI是英文SerialPeripheral Interface的縮寫,顧名思義就是串行外圍設(shè)備接口。SPI是一種高速、全雙工、同步通信總線,標準的SPI有4個引腳,常用于單片機和EEPROM、FLASH、實時時鐘、數(shù)字信號處理等器件的通信。SPI通信原理要比I2C簡單,它主要是主從方式通信,這種通信方式通常只有一個主機和一個或多個從機,標準的SPI是4根線,分別是SSEL(片選)、SCLK(時鐘,也寫作SCK)、MOSI(主機輸入,從機輸出)和MISO(主機輸入,從機輸出)。
SSEL:從設(shè)備片選使能信號。譬如從設(shè)備是低電平使能的話,當拉低這個引腳,從設(shè)備就會被選中,主機和這個被選中的從設(shè)備通信。
SCLK:時鐘信號,即主機產(chǎn)生。
MOSI:主機給從機發(fā)送指令或者數(shù)據(jù)的通道。
MISO:主機讀取從機的狀態(tài)或者數(shù)據(jù)的通道。
?
在某些情況下,也可以用3根或者2根線的SPI進行通信。譬如,主機只給從機發(fā)送指令,從機不需要回復數(shù)據(jù)的時候,MISO就可以不要;而主機只讀從機的數(shù)據(jù),不需要給從機發(fā)送數(shù)據(jù)的時候,MOSI就可以不要;當一個主機與一個從機通信時,從機的片選有時可以固定為有效電平而一直處于使能狀態(tài),那么SSEL可以不要,此時如果主機只給從機發(fā)數(shù)據(jù),那么SSEL和MISO都可以不要;如果主機只讀取從機發(fā)送來的數(shù)據(jù),那么SSEL和MOSI都可以不要。當然一般情況下說的SPI都指的是標準的SPI,有4根線進行通信。
?
?
二、? 時序分析
眾所周知,SPI時序有4種模式,在講解4種模式之前先學習兩個單詞,即Polarity和Phase,這兩個單詞分別是SPI的時鐘極性(Polarity)和相位(Phase),最常見的寫法CPOL和CPHA,也有其它的一些寫法,如:
(1) CKPOL (Clock Polarity) = CPOL = POL = Polarity =(時鐘)極性
(2) CKPHA (Clock Phase) ??= CPHA = PHA = Phase = (時鐘)相位
?
那么時鐘極性是什么概念呢?SPI通信過程分為空閑狀態(tài)和通信狀態(tài),空閑狀態(tài)是指SCLK在數(shù)據(jù)發(fā)送之前和發(fā)送完之后的狀態(tài),通信狀態(tài)當然就是指發(fā)送數(shù)據(jù)的狀態(tài)。CPOL=1,那么空閑狀態(tài)SCLK為高電平,反之,CPOL=0,則空閑狀態(tài)SCLK為低電平。
?
SPI是一個環(huán)形的總線結(jié)構(gòu),主要是在SCLK的控制下,兩個雙向移位寄存器進行數(shù)據(jù)交換。那么主機和從機在進行交換數(shù)據(jù)的時候就設(shè)計到一個問題,即主機在什么時刻輸出到MOSI上而從機在什么時刻采樣這個數(shù)據(jù),或者從機什么時刻輸出到MISO上而主機什么時刻采樣這個數(shù)據(jù)。同步通信的一個特點就是所有數(shù)據(jù)的變化和采樣都是伴隨著時鐘沿進行的,也就是說數(shù)據(jù)總是在時鐘的邊沿附近變化或被采樣,而一個完整的時鐘周期必定包含了一個上升沿和一個下降沿,這是周期的定義所決定的,只是這兩個沿的先后并無規(guī)定。又因為數(shù)據(jù)從產(chǎn)生到它穩(wěn)定是需要一定的時間,那么如果主機在上升沿輸出數(shù)據(jù)到MOSI,從機就只能在下降沿去采樣這個數(shù)據(jù)了。反之,如果一方在下降沿輸出數(shù)據(jù),那么另一方就必須在上升沿采樣這個數(shù)據(jù)。那么由誰來決定上升沿采樣還是下降沿采樣,上升沿輸出還是下降沿輸出呢?
?
當CPHA=1時,表示數(shù)據(jù)的輸出在一個時鐘周期的第一個沿上,至于這個沿是上升沿還是下降沿,就得由CPOL來決定。CPOL=1那就是下降沿,反之就是上升沿。那么數(shù)據(jù)采樣自然就是第二個沿上了。
當CPHA=0時,表示數(shù)據(jù)采樣在一個時鐘周期的第一個沿上,至于這個沿是上升沿還是下降沿,就得由CPOL來決定。CPOL=1那就是下降沿,反之就是上升沿。那么數(shù)據(jù)輸出自然就是第二個沿上了。
?
通過以上的學習,SPI的4中模式其實已經(jīng)浮現(xiàn)出來了,下面對此做一總結(jié):
| Mode0 | CPOL=0,CPHA=0 |
| Mode1 | CPOL=0,CPHA=1 |
| Mode2 | CPOL=1,CPHA=0 |
| Mode3 | CPOL=1,CPHA=1 |
?
圖形比文字更容易看懂,所以SPI4種模式的時序圖如下:
再進一步說明模式1。當SPI處于使能狀態(tài),而且發(fā)送FIFO內(nèi)有有效數(shù)據(jù)時,設(shè)置SSEL信號為低,表示開始傳輸數(shù)據(jù)。來自Slave的數(shù)據(jù)立刻發(fā)送到Master的接收數(shù)據(jù)線MISO。半個SCLK時鐘周期之后,有效的Master數(shù)據(jù)傳輸?shù)?/span>MOSI。此時Master和Slave數(shù)據(jù)都已經(jīng)有效,SCLK管腳在接下來的半個SCLK時鐘周期之后變?yōu)楦唠娖健?shù)據(jù)在SCLK時鐘的上升沿被捕獲(采樣),在時鐘的下降沿被傳送(輸出),傳輸完成后SSEL恢復為高電平。
?
再進一步說明模式2。當SPI為使能狀態(tài),而且發(fā)送FIFO內(nèi)有有效數(shù)據(jù)時,設(shè)置SSEL信號為低表示開始傳輸數(shù)據(jù)。半個SCLK時鐘周期之后,Master和Slave的有效數(shù)據(jù)分別在各自的傳輸線上有效。同時,SCLK從第一個上升沿開始有效。數(shù)據(jù)在SCLK時鐘的下降沿被捕獲,在時鐘的上升沿被傳送,傳輸完成后SSEL恢復為高電平。
?
再進一步的說明模式3。當SPI為使能狀態(tài),而且發(fā)送FIFO內(nèi)有有效數(shù)據(jù)時,設(shè)置SSEL信號為低表示開始傳輸數(shù)據(jù)。此時Slave的數(shù)據(jù)立刻發(fā)送到Master的接收數(shù)據(jù)線MISO。半個SCLK周期之后,Master的有效數(shù)據(jù)傳送到MOSI。再過半個SCLK時鐘周期之后,SCLK管腳設(shè)置為低。這表示數(shù)據(jù)在SCLK時鐘的下降沿被捕獲,在SCLK時鐘的上升沿被傳送,傳輸完成后SSEL恢復為高電平。
?
再進一步的說明模式4。當SPI為使能狀態(tài),而且發(fā)送FIFO內(nèi)有有效數(shù)據(jù)時,設(shè)置SSEL信號為低表示開始傳輸數(shù)據(jù)。半個SCLK時鐘周期后,Master和Slave數(shù)據(jù)在各自的傳輸線上有效。同時,時鐘SCLK從1個下降沿開始有效。數(shù)據(jù)在SCLK時鐘的上升沿被捕獲,在時鐘的下降沿被傳送,傳輸完成后SSEL恢復為高電平。
?
?
?
三、? SPI訪問寄存器模式
?
常見的SPI訪問寄存器模式有3種,分別是SingleAccess(單次訪問)、Burst Access(突發(fā)訪問)、FIFO Access(FIFO訪問)。
?
SingleAccess:通過寫訪問發(fā)送一個地址字節(jié)后跟一個數(shù)據(jù)字節(jié),或者發(fā)送一個地址字節(jié),再通過讀訪問接收一個數(shù)據(jù)字節(jié)。發(fā)送幀時SSEL為低,發(fā)送完最后一個字節(jié)后SSEL變?yōu)楦摺H缦聢D,將0x0A寫到寄存器0x02上,再從該寄存器上讀其值。
W + addr. 0x02 Data: 0x0A ?????????????????????R + addr. 0x02 Data:
注意:SSEL第一次為低電平使能時,MISO變?yōu)楦唠娖?#xff0c;然后立即變?yōu)榈碗娖?#xff0c;這表示從設(shè)備芯片已經(jīng)裝備好(Thefirst time CSn goes low, MISO goes high and then low again immediately,indicating that the chip is ready.)。
Single Byte Access (Write and Read)
?
?
BurstAccess:一個地址字節(jié)后跟多個數(shù)據(jù)字節(jié)。在每個數(shù)據(jù)字節(jié)之間地址字節(jié)會在內(nèi)部自動增加,并且片選信號SSEL在發(fā)送幀時為低電平,在每個數(shù)據(jù)字節(jié)之間也保持低電平,當發(fā)送完最后一個字節(jié)后變?yōu)楦唠娖健H?#xff1a;
BYTE xdata regValues[] = {1,2,3};
halSpiWriteBurstReg(0x00, regValues,sizeof(regValues));
halSpiReadReg(0x00);
halSpiReadReg(0x01);
halSpiReadReg(0x02);
BurstWrite Followed by Single Read
?
?
FIFO Access:如果地址字節(jié)對應(yīng)的是FIFO地址,那么隨后的數(shù)據(jù)字節(jié)將針對FIFO。該地址字節(jié)沒有自動遞增而被存儲,并且不需要在每個數(shù)據(jù)字節(jié)之間發(fā)送該地址。片選信號SSEL在發(fā)送幀時為低電平,在每個數(shù)據(jù)字節(jié)之間也保持低電平,當發(fā)送完最后一個字節(jié)后變?yōu)楦唠娖健?/p>
?
特別說明:不同的SPI從設(shè)備地址字節(jié)的內(nèi)容不一定相同,需要查看相關(guān)手冊,才能知道地址字節(jié)的構(gòu)成是什么樣的。例如:RFM69H這個模塊SPI的地址字節(jié)的組成如下:
The first byteis the address byte. It is made of:
u? wnr bit, whichis 1 for write access and 0 for read access
u?7 bits of address, MSB first
?
又如CCXX00的地址字節(jié)組成如下:
The R/W bit in the address header controlsif the register should be written or read, and the burst bit controls if it
is a single access or a burst access.
| R/W | Burst | A5 | A4 | A3 | A2 | A1 | A0 |
注:其實地址位只有5位,最高位為讀寫位,1為讀,0為寫;后面一位是burstAccess的使能位,當為1時使能。
?
?
四、? 例程解析
特別注意:在主設(shè)備這邊配置SPI接口時鐘的時候一定要弄清楚從設(shè)備的時鐘要求,因為主設(shè)備這邊的時鐘極性和相位都是以從設(shè)備為基準的。因此在時鐘極性的配置上一定要搞清楚從設(shè)備是在時鐘的上升沿還是下降沿接收數(shù)據(jù),是在時鐘的下降沿還是上升沿輸出數(shù)據(jù)。但要注意的是,由于主設(shè)備的SDO連接從設(shè)備的SDI,從設(shè)備的SDO連接主設(shè)備的SDI,從設(shè)備SDI接收的數(shù)據(jù)是主設(shè)備的SDO發(fā)送過來的,主設(shè)備SDI接收的數(shù)據(jù)是從設(shè)備SDO發(fā)送過來的,所以主設(shè)備這邊SPI時鐘極性的配置(即SDO的配置)跟從設(shè)備的SDI接收數(shù)據(jù)的極性是相反的,跟從設(shè)備SDO發(fā)送數(shù)據(jù)的極性是相同的。
?
通過手冊得知(MOSI is generated by the master on the falling edge ofSCK and is sampled by the slave (i.e. this SPI interface) on the rising edge ofSCK. MISO is generated by the slave on the falling edge of SCK.A transferalways starts by the NSS pin going low. MISO is high impedance when NSS ishigh.),本例程使用的從設(shè)備的SPI工作在Mode1,即CPOL= 0 and CPHA = 0,且SSEL低電平使能,當SSEL為高電平時,MISO為高。故主機的MOSI在空閑狀態(tài)時為高。
/********************************************************************************** Copyright: (C) 2015 YangZheng<yz2012ww@gmail.com> * All rights reserved.** Filename: dev_rfm69h.c* Description: This file* * Version: 1.0.0(12/28/2015~)* Author: Yang Zheng<yz2012ww@gmail.com>* ChangeLog: 1, Release initialversion on "12/28/2015 07:46:07 PM"* ********************************************************************************/ #include<linux/kernel.h> #include<linux/version.h> #include<linux/module.h> #include<linux/types.h> #include<linux/errno.h> #include<linux/fcntl.h> #include<linux/mm.h> #include<linux/proc_fs.h> #include<linux/fs.h> #include<linux/slab.h> #include<linux/init.h> #include<asm/uaccess.h> #include<asm/io.h> #include <asm/system.h> #include<linux/miscdevice.h> #include<linux/delay.h> #include<linux/sched.h>#include<linux/proc_fs.h> #include<linux/poll.h>#include<asm/bitops.h> #include<asm/uaccess.h> #include<asm/irq.h>#include<linux/moduleparam.h> #include <linux/ioport.h> #include<linux/interrupt.h> #include<linux/cdev.h> #include<linux/semaphore.h> #include<linux/wait.h>#define IOCONFIG4_4 IO_ADDRESS(0x200f0000+ 0x054) //復用管腳gpio4_4 #define IOCONFIG4_5 IO_ADDRESS(0x200f0000+ 0x050) //復用管腳gpio4_5 #define IOCONFIG4_6 IO_ADDRESS(0x200f0000+ 0x04c) //復用管腳gpio4_6 #define IOCONFIG4_7 IO_ADDRESS(0x200f0000+ 0x048) //復用管腳gpio4_7#define GPIO4_BASE 0x20180000 //gpio基地址 #define GPIO4_SIZE 0x10000 // 64KB #define GPIO4_DIR IO_ADDRESS(GPIO4_BASE + 0x400) //gpio4方向寄存器 #defineGPIO_SPI_CS_REG IO_ADDRESS(GPIO4_BASE+ 0x40) //gpio4_4數(shù)據(jù)寄存器 #defineGPIO4_SPI_SCK_REG IO_ADDRESS(GPIO4_BASE + 0x80) //gpio4_5數(shù)據(jù)寄存器 #defineGPIO4_SPI_MOSI_REG IO_ADDRESS(GPIO4_BASE+ 0x100) //gpio4_6數(shù)據(jù)寄存器 #defineGPIO4_SPI_MISO_REG IO_ADDRESS(GPIO4_BASE+ 0x200) //gpio4_7數(shù)據(jù)寄存器 #define GPIO4_4 (1<< 4) //gpio4_4 #define GPIO4_5 (1<< 5) //gpio4_5 #define GPIO4_6 (1<< 6) //gpio4_6 #define GPIO4_7 (1<< 7) //gpio4_7#define DATA_LENGTH 21#define DRV_AUTHOR "Yang Zheng<yz2012ww@gmail.com>" #define DRV_DESC "spi driver" #define DRV_VERSION "v0.0.1"#if 0 #defineDBG(x...) printk(x) #define DBG_PRINT #else #defineDBG(x...) do {} while (0) #endifstaticDECLARE_WAIT_QUEUE_HEAD(spi_waitq); unsigned char read_tmp_data[DATA_LENGTH] = {0}; void __iomem *reg_ssp_base_va;typedef unsignedchar byte; typedef unsignedshort word;static int read_flag = 0;/****************************************************************************** **函數(shù)名稱:Set_nCS **函數(shù)功能:禁用片選 **輸入?yún)?shù):無 **輸出參數(shù):無 **注意: 高電平為禁用片選,低電平為使能片選 ******************************************************************************/ void Set_nCS(void) {unsigned char regvalue;writel(0, IOCONFIG4_4); //gpio模式regvalue = readl(GPIO4_DIR); //設(shè)置gpio輸出regvalue |= GPIO4_4;writel(regvalue, GPIO4_DIR);regvalue = readl(GPIO_SPI_CS_REG); regvalue |= GPIO4_4;writel(regvalue, GPIO_SPI_CS_REG); //禁用片選 }/****************************************************************************** **函數(shù)名稱:Clr_nCS **函數(shù)功能:使能片選 **輸入?yún)?shù):無 **輸出參數(shù):無 **注意: 高電平為禁用片選,低電平為使能片選 ******************************************************************************/ void Clr_nCS(void) {unsigned char regvalue;writel(0, IOCONFIG4_4); //gpio模式regvalue = readl(GPIO4_DIR); //設(shè)置gpio輸出regvalue |= GPIO4_4;writel(regvalue, GPIO4_DIR);regvalue = readl(GPIO_SPI_CS_REG); regvalue &= ~GPIO4_4;writel(0, GPIO_SPI_CS_REG); //使能片選}/****************************************************************************** **函數(shù)名稱:Set_SCK **函數(shù)功能:SCK為高電平 **輸入?yún)?shù):無 **輸出參數(shù):無 **注意: ******************************************************************************/ void Set_SCK(void) {unsigned char regvalue;writel(0, IOCONFIG4_5); //gpio模式regvalue = readl(GPIO4_DIR); //設(shè)置gpio輸出regvalue |= GPIO4_5;writel(regvalue, GPIO4_DIR);regvalue =readl(GPIO4_SPI_SCK_REG); //設(shè)置gpio輸出高電平regvalue |= GPIO4_5;writel(regvalue, GPIO4_SPI_SCK_REG); }/****************************************************************************** **函數(shù)名稱:Set_SCK **函數(shù)功能:SCK為高電平 **輸入?yún)?shù):無 **輸出參數(shù):無 **注意: ******************************************************************************/ void Clr_SCK(void) {unsigned char regvalue;writel(0, IOCONFIG4_5); //gpio模式regvalue = readl(GPIO4_DIR); //設(shè)置gpio輸出regvalue |= GPIO4_5;writel(regvalue, GPIO4_DIR);regvalue =readl(GPIO4_SPI_SCK_REG); //設(shè)置gpio輸出低電平regvalue &= ~GPIO4_5;writel(regvalue,GPIO4_SPI_SCK_REG); }/****************************************************************************** **函數(shù)名稱:Set_MOSI **函數(shù)功能:MOSI為高電平 **輸入?yún)?shù):無 **輸出參數(shù):無 **注意: ******************************************************************************/ void Set_MOSI(void) {unsigned char regvalue;writel(0, IOCONFIG4_6); //gpio模式regvalue = readl(GPIO4_DIR); //設(shè)置gpio輸出regvalue |= GPIO4_6;writel(regvalue, GPIO4_DIR);regvalue = readl(GPIO4_DIR); //設(shè)置gpio輸出高電平regvalue |= GPIO4_6;writel(regvalue, GPIO4_SPI_MOSI_REG);}/****************************************************************************** **函數(shù)名稱:Set_MOSI **函數(shù)功能:MOSI為低電平 **輸入?yún)?shù):無 **輸出參數(shù):無 **注意: ******************************************************************************/ void Clr_MOSI(void) {unsigned char regvalue;writel(0, IOCONFIG4_6); //gpio模式regvalue = readl(GPIO4_DIR); //設(shè)置gpio輸出regvalue |= GPIO4_6;writel(regvalue, GPIO4_DIR);regvalue = readl(GPIO4_DIR); //設(shè)置gpio輸出低電平regvalue &= ~GPIO4_6;writel(regvalue, GPIO4_SPI_MOSI_REG);}/****************************************************************************** **函數(shù)名稱:MISO_H **函數(shù)功能:讀取MISO_H的值 **輸入?yún)?shù):無 **輸出參數(shù):無 **注意: ******************************************************************************/ unsigned charMISO_H(void) {unsigned char regvalue;writel(0, IOCONFIG4_7); //gpio模式regvalue = readl(GPIO4_DIR); //設(shè)置gpio輸入regvalue &= ~GPIO4_7;writel(regvalue, GPIO4_DIR);regvalue =readl(GPIO4_SPI_MISO_REG); //讀取輸入值//DBG("regvalue = %x\n",regvalue);return regvalue; }/****************************************************************************** **函數(shù)名稱:SPICmd8bit **函數(shù)功能:SPI寫入?yún)?shù)8bit **輸入?yún)?shù):WrPara **輸出參數(shù):無 **注意: 保留nCS輸出Low ******************************************************************************/ voidSPICmd8bit(byte WrPara) {byte bitcnt; Clr_nCS();Clr_SCK();for(bitcnt = 8; bitcnt != 0; bitcnt--){Clr_SCK();if(WrPara&0x80){Set_MOSI();}else{Clr_MOSI();}Set_SCK();WrPara <<= 1;}Clr_SCK();Set_MOSI();//Set_nCS(); //*此處不關(guān)閉nCS,使用連續(xù)模式* }/****************************************************************************** **函數(shù)名稱:SPIRead8bitt **函數(shù)功能:SPI讀取參數(shù)8bit **輸入?yún)?shù):讀取8bit數(shù)據(jù)——RdPara **輸出參數(shù):無 **注意: 保留nCS輸出Low ******************************************************************************/ byte SPIRead8bit(void) {byte RdPara = 0;byte bitcnt;Clr_nCS();Set_MOSI(); //讀FIFO,維持SDI為H for(bitcnt=8; bitcnt!=0; bitcnt--){Clr_SCK();RdPara <<= 1;Set_SCK();if(MISO_H()){RdPara |= 0x01;}else{;}}Clr_SCK();//Set_nCS(); //*此處不關(guān)閉nCS,使用連續(xù)模式*//DBG("RdPara = %x\n",RdPara);return(RdPara); }/****************************************************************************** **函數(shù)名稱:SPIRead **函數(shù)功能:SPI讀取一個地址數(shù)據(jù) **輸入?yún)?shù):adr **輸出參數(shù):無 ******************************************************************************/ byte SPIRead(byteadr) {byte tmp; SPICmd8bit(adr); //發(fā)送要讀取的地址tmp = SPIRead8bit(); //讀取數(shù)據(jù) Set_nCS();return(tmp); }/****************************************************************************** **函數(shù)名稱:SPIWrite **函數(shù)功能:SPI寫入一個16數(shù)據(jù)(高8位地址,低8位數(shù)據(jù)) **輸入?yún)?shù):WrPara **輸出參數(shù):無 ******************************************************************************/ void SPIWrite(wordWrPara) { byte bitcnt; Clr_SCK(); //注意SCK先清0,保持低Clr_nCS();WrPara |= 0x8000; //寫數(shù)據(jù)高位置1for(bitcnt=16; bitcnt!=0; bitcnt--){Clr_SCK();if(WrPara&0x8000){Set_MOSI();}else{Clr_MOSI();}Set_SCK();WrPara <<= 1;}Clr_SCK();Set_MOSI();Set_nCS(); } /****************************************************************************** **函數(shù)名稱:SPIBurstRead **函數(shù)功能:SPI連續(xù)讀取模式 **輸入?yún)?shù):adr——讀取地址 ** ptr——存儲數(shù)據(jù)指針 ** length 讀取長度 **輸出參數(shù):無,數(shù)據(jù)存在ptr中 ******************************************************************************/ voidSPIBurstRead(byte adr, byte *ptr, byte length) {byte i;if(length<=1) //讀取長度必須大于1{return;}else{Clr_SCK(); //注意SCK先清0,保持低Clr_nCS();SPICmd8bit(adr); //讀取地址for(i=0;i<length;i++){ptr[i]= SPIRead8bit();}Set_nCS(); } }/****************************************************************************** **函數(shù)名稱:SPIBurstWrite **函數(shù)功能:SPI連續(xù)寫入模式 **輸入?yún)?shù):adr——寫入地址 ** ptr——存儲數(shù)據(jù)指針 ** length 寫入長度 **輸出參數(shù):無 ******************************************************************************/ voidBurstWrite(byte adr, byte *ptr, byte length) {byte i;if(length<=1) //讀取長度不為0或1{return;}else { Clr_SCK(); //注意SCK先清0,保持低Clr_nCS(); SPICmd8bit(adr|0x80); //連續(xù)寫for(i=0;i<length;i++){SPICmd8bit(ptr[i]);}Set_nCS(); } }longspi_ioctl(struct file *file, unsigned int cmd, unsigned long arg) {……switch(cmd){case SSP_READ_ALT:…...val = *(unsigned int*)arg;addr = (unsignedchar)(val&0xff);data =SPIRead(addr);……*(unsigned int *)arg= (unsigned int)(data&0x0000ff);break;case SSP_READ_BURST:{unsignedchar addr = 0x00;……/*讀取數(shù)據(jù) */SPIBurstRead(addr,read_tmp_data, DATA_LENGTH);…...break;}case SSP_WRITE_ALT:data = 0;val = *(unsigned int *)arg;tmp = (unsignedshort)((val&0xff0000)>>8); tmp |= (unsignedshort)((val&0x0000ff)>>0);……SPIWrite(tmp);break; case SSP_WRITE_BURST:res =copy_from_user(tmp_str, (unsigned char *)arg, sizeof(tmp_str));if (res != 0){printk("copydata from the user space error\n");}……BurstWrite(addr,data, DATA_LENGTH);break;default:{printk("Nosuch spi command %#x!\n", cmd);return -1;}}return 0; }int spi_open(structinode * inode, struct file * file) {……return 0; }intspi_close(struct inode * inode, struct file * file) {return 0; }static structfile_operations spi_fops = {.owner =THIS_MODULE,.unlocked_ioctl = spi_ioctl,.open =spi_open,.release =spi_close };static structmiscdevice spi_dev = {.minor =MISC_DYNAMIC_MINOR, .name =DEV_NAME,.fops =&spi_fops, };static int __initspi_gpio_init(void) {int ret;ret = misc_register(&spi_dev);if (ret < 0){printk("registerspi_gpio device failed!\n");return -1;}printk("SPI driver initializesuccessful! .\n");return 0; }static void __exitspi_gpio_exit(void) {misc_deregister(&spi_dev);printk("SPI driver exit!\n"); } module_init(spi_gpio_init); module_exit(spi_gpio_exit);MODULE_LICENSE("GPL"); MODULE_AUTHOR(DRV_AUTHOR); MODULE_DESCRIPTION(DRV_DESC);
創(chuàng)作挑戰(zhàn)賽新人創(chuàng)作獎勵來咯,堅持創(chuàng)作打卡瓜分現(xiàn)金大獎
總結(jié)
以上是生活随笔為你收集整理的在ARM Linux下使用GPIO模拟SPI时序详解的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 微信小程序制作模板套用时需要注意什么呢?
- 下一篇: Linux学习笔记——例说makefil