SPI簡(jiǎn)介
SPI為串行外設(shè)接口,全稱Serial Peripheral interface,是一種全雙工、同步的通信總線,廣泛用于不同設(shè)備之間的板級(jí)通訊。
MM32F0140的SPI支持接收和發(fā)送1 ~ 32位數(shù)據(jù)同時(shí)進(jìn)行,主模式最大速率24Mbps,從模式最大速率12Mbps,支持一個(gè)主機(jī)與多個(gè)從機(jī)操作,支持DMA操作。
數(shù)據(jù)通信
在進(jìn)行SPI數(shù)據(jù)通信時(shí),通常由MOSI、MISO、SCK、NSS四個(gè)管腳與外部器件相連。如圖1所示,MOSI管腳將來(lái)自主設(shè)備的數(shù)據(jù)輸入到從設(shè)備,MISO管腳將從設(shè)備響應(yīng)的數(shù)據(jù)傳入主設(shè)備,從設(shè)備通過(guò)SCK管腳獲得主設(shè)備提供的時(shí)鐘信號(hào),使發(fā)送和接收部分使用相同的時(shí)鐘,保證數(shù)據(jù)傳輸?shù)目煽啃浴SS管腳進(jìn)行從設(shè)備選擇,使主設(shè)備可以和某個(gè)從設(shè)備一對(duì)一單獨(dú)通信。
主設(shè)備數(shù)據(jù)輸入(MISO)
MISO為主設(shè)備輸入從設(shè)備輸出管腳,傳輸方向?yàn)閺脑O(shè)備發(fā)送到主設(shè)備。
主設(shè)備數(shù)據(jù)輸出(MOSI)
MOSI為主設(shè)備輸出從設(shè)備輸入管腳,傳輸方向?yàn)橹髟O(shè)備發(fā)送到從設(shè)備。
時(shí)鐘(SCK)
SCK為串口時(shí)鐘,控制數(shù)據(jù)交換的速率,由主設(shè)備產(chǎn)生,通過(guò)SCK引腳傳輸供從設(shè)備使用。
片選(NSS)
NSS為從設(shè)備選擇管腳,SPI通過(guò)控制片選管腳NSS來(lái)控制多個(gè)從設(shè)備。當(dāng)NSS引腳功能被激活后,配置作為主設(shè)備的SPI進(jìn)入主模式,將會(huì)拉低NSS引腳,其余連接到主設(shè)備NSS的SPI設(shè)備由于檢測(cè)到了NSS拉低的信號(hào),會(huì)自動(dòng)進(jìn)入從設(shè)備模式。
圖1.SPI數(shù)據(jù)通信
數(shù)據(jù)傳輸時(shí)序
SPI可以通過(guò)配置時(shí)鐘極性(CPOL)與相位(CPHA)選擇四種不同的數(shù)據(jù)傳輸時(shí)序,即四種工作方式。
時(shí)鐘極性(CPOL)
時(shí)鐘極性是SCK在空閑時(shí)保持的電平狀態(tài),當(dāng)CPOL配置為0時(shí),SCK在空閑狀態(tài)為低電平,即兩次傳輸之間為低電平;當(dāng)CPOL配置為1時(shí),SCK在空閑狀態(tài)為高電平,即兩次傳輸之間為高電平。
時(shí)鐘相位(CPHA)
時(shí)鐘相位用于決定采樣時(shí)刻,當(dāng)CPHA配置為0時(shí),第一個(gè)數(shù)據(jù)位采樣從第一個(gè)時(shí)鐘邊沿開(kāi)始;當(dāng)CPHA配置為1時(shí),第一個(gè)數(shù)據(jù)位采樣從第二個(gè)時(shí)鐘邊沿開(kāi)始;對(duì)數(shù)據(jù)進(jìn)行邊沿采樣需要CPOL與CPHA的共同配置決定。
SCK低電平空閑,第一個(gè)沿采樣
當(dāng)CPOL=0且CPHA=0,第一位數(shù)據(jù)位在SCK時(shí)鐘的第一個(gè)時(shí)鐘上升沿被采樣,如圖2所示。
圖2.SCK低電平空閑且第一個(gè)沿采樣
SCK高電平空閑,第一個(gè)沿采樣
當(dāng)CPOL=1且CPHA=0,第一位數(shù)據(jù)位的SCK時(shí)鐘的第一個(gè)時(shí)鐘下降沿被采樣,如圖3所示。
圖3.SCK高電平空閑且第一個(gè)沿采樣
SCK低電平空閑,第二個(gè)沿采樣
當(dāng)CPOL=0且CPHA=1,第一個(gè)數(shù)據(jù)位在SCK時(shí)鐘的第二個(gè)時(shí)鐘下降沿被采樣,如圖4所示。
圖4.SCK低電平空閑且第二個(gè)沿采樣
SCK高電平空閑,第二個(gè)沿采樣
當(dāng)CPOL=1且CPHA=1,第一個(gè)數(shù)據(jù)位在SCK時(shí)鐘的第二個(gè)時(shí)鐘上升沿被采樣,如圖5所示。
圖5.SCK高電平空閑且第二個(gè)沿采樣
SPI配置
主模式
通過(guò)配置波特率發(fā)生器(SPI_I2S_SPBREG)設(shè)定串行時(shí)鐘波特率,公式為:波特率=fpclk/SPBRG (fpclk是APB時(shí)鐘頻率)。配置通用控制寄存器(SPI_I2S_CCTL)的SPI數(shù)據(jù)寬度位(SPILEN),決定數(shù)據(jù)幀的長(zhǎng)度是7位還是8位;配置時(shí)鐘相位選擇位(CPHA)與時(shí)鐘極性標(biāo)志位(CPOL),確定時(shí)序模式,為保證數(shù)據(jù)正常傳輸,主從設(shè)備的時(shí)序模式應(yīng)保持配置一致;配置LSB在前使能位(LSBFE),決定數(shù)據(jù)位的輸出順序。操作全局控制寄存器(SPI_I2S_GCTL)的DW8_32位進(jìn)行發(fā)送和接收數(shù)據(jù)寄存器有效數(shù)據(jù)選擇,可配置為只有低8位有效或32位數(shù)據(jù)都有效;操作主機(jī)模式位(MODE)為1,選擇主機(jī)模式;配置SPI/I2S選擇位(SPIEN)為1,使能SPI。
若只接收而不發(fā)送數(shù)據(jù),則配置接收數(shù)據(jù)個(gè)數(shù)寄存器(SPI_I2S_RXDNR),定義下次接收過(guò)程中需要接收字節(jié)的個(gè)數(shù)。
從模式
配置通用控制寄存器(SPI_I2S_CCTL)的LSB在前使能位(LSBFE),決定數(shù)據(jù)位的輸出順序是從最低有效位到最高有效位或從最高有效位到最低有效位;配置SPI數(shù)據(jù)寬度位(SPILEN),決定數(shù)據(jù)幀的長(zhǎng)度是7位還是8位;配置時(shí)鐘相位選擇位(CPHA)與時(shí)鐘極性標(biāo)志位(CPOL),決定時(shí)序模式。配置全局控制寄存器(SPI_I2S_GCTL)的主機(jī)模式位(MODE)為0,選擇從機(jī)模式;配置SPI/I2S選擇位(SPIEN)為1,使能SPI。
數(shù)據(jù)發(fā)送
主模式
將需要發(fā)送的數(shù)據(jù)寫入發(fā)送數(shù)據(jù)寄存器(SPI_I2S_TXREG),該寄存器的有效位由全局控制器(SPI_I2S_GCTL)的DW8_32位控制。在發(fā)送第一個(gè)數(shù)據(jù)位時(shí),整個(gè)數(shù)據(jù)被傳輸?shù)揭莆患拇嫫?,后續(xù)數(shù)據(jù)通過(guò)移位寄存器串行輸出到MOSI引腳。當(dāng)中斷狀態(tài)寄存器(SPI_I2S_INTSTAT)的發(fā)送緩沖器有效中斷標(biāo)志位(TX_INTF)被置1,數(shù)據(jù)已從發(fā)送緩沖器被傳輸?shù)揭莆患拇嫫鳌?/p>
從模式
當(dāng)從設(shè)備收到SCK傳來(lái)的時(shí)鐘信號(hào),同時(shí)接收到MOSI引腳傳輸?shù)牡谝粋€(gè)數(shù)據(jù)位,從設(shè)備開(kāi)始發(fā)送,第一個(gè)位被發(fā)送到MISO引腳,其余bit位被傳輸?shù)揭莆患拇嫫?,通過(guò)移位寄存器將數(shù)據(jù)串行發(fā)送。當(dāng)中斷狀態(tài)寄存器(SPI_I2S_INTSTAT)的發(fā)送緩沖器有效中斷標(biāo)志位(TX_INTF)被置1,表示第一位已發(fā)送,其余位被傳輸?shù)揭莆患拇嫫鳌?/p>
數(shù)據(jù)接收
主模式
從MISO引腳接收數(shù)據(jù),數(shù)據(jù)通過(guò)移位寄存器,在最后一個(gè)采樣時(shí)鐘邊沿后,數(shù)據(jù)字節(jié)被傳輸?shù)浇邮站彌_器中。當(dāng)中斷狀態(tài)寄存器(SPI_I2S_INTSTAT)的接收端數(shù)據(jù)有效中斷標(biāo)志位(RX_INTF)置1,數(shù)據(jù)接收完成,主模式下不再發(fā)送時(shí)鐘信號(hào)。
從模式
從MOSI引腳接收數(shù)據(jù),數(shù)據(jù)通過(guò)移位寄存器,在最后一個(gè)采樣時(shí)鐘邊沿后,數(shù)據(jù)字節(jié)被傳輸?shù)浇邮站彌_器中。當(dāng)中斷狀態(tài)寄存器(SPI_I2S_INTSTAT)的接收端數(shù)據(jù)有效中斷標(biāo)志位(RX_INTF)置1,數(shù)據(jù)接收完成。
實(shí)驗(yàn)
本實(shí)驗(yàn)為回環(huán)測(cè)試,通過(guò)使用杜邦線連接SPI的MISO與MOSI引腳,實(shí)現(xiàn)數(shù)據(jù)的發(fā)送與接收。配置SPI主機(jī),SPI進(jìn)行一次數(shù)據(jù)發(fā)送與接收并對(duì)發(fā)送與接收信息進(jìn)行驗(yàn)證,并通過(guò)串口打印傳輸情況,若有發(fā)送與接收數(shù)據(jù)不同的情況,串口打印出錯(cuò)信息與出錯(cuò)個(gè)數(shù),若驗(yàn)證成功則打印"spi loopback xfer done."。
啟用外設(shè)時(shí)鐘 enable_clock()
實(shí)驗(yàn)使用SPI1,且需要通過(guò)串口打印實(shí)驗(yàn)現(xiàn)象,因此需啟用SPI1與UART的外設(shè)時(shí)鐘。
{ /* Enable UART1 clock. */ RCC->APB2ENR |= RCC_APB2_PERIPH_UART1; /* Enable GPIOA clock. */ RCC->AHB1ENR |= RCC_AHB1_PERIPH_GPIOA; /* Enable SPI1 clock. */ RCC->APB2ENR |= RCC_APB2_PERIPH_SPI1; }
配置引腳 pin_init()
配置SPI的NSS(PA4)、MOSI(PA7)、MISO(PA6)、SCK(PA5)引腳,因?yàn)閷?shí)驗(yàn)現(xiàn)象通過(guò)串口顯示,所以配置UART的TX(PA9)與RX(PA10)引腳。
void pin_init() { /* Setup NSS(PA4). */ GPIOA->CHL = ~GPIO_CRL_MODE4_MASK; GPIOA->CHL |= (GPIO_PinMode_AF_PushPull << GPIO_CRL_MODE4_SHIFT); /* PA4 multiplexed push-pull output. */ GPIOA->AFRL = ~GPIO_AFRL_AFR_MASK; GPIOA->AFRL |= (GPIO_AF_0 << GPIO_CRL_MODE4_SHIFT); /* Use AF0. */ /* Setup MOSI(PA7). */ GPIOA->CHL = ~GPIO_CRL_MODE7_MASK; GPIOA->CHL |= (GPIO_PinMode_AF_PushPull << GPIO_CRL_MODE7_SHIFT); /* PA7 multiplexed push-pull output. */ GPIOA->AFRL |= (GPIO_AF_0 << GPIO_CRL_MODE7_SHIFT); /* Use AF0. */ /* Setup MISO(PA6). */ GPIOA->CHL = ~GPIO_CRL_MODE6_MASK; GPIOA->CHL |= (GPIO_PinMode_In_Floating << GPIO_CRL_MODE6_SHIFT); /* PA6 floating input. */ GPIOA->AFRL |= (GPIO_AF_0 << GPIO_CRL_MODE6_SHIFT); /* Use AF0. */ /* Setup SCK(PA5). */ GPIOA->CHL = ~GPIO_CRL_MODE5_MASK; GPIOA->CHL |= (GPIO_PinMode_AF_PushPull << GPIO_CRL_MODE5_SHIFT); /* PA5 floating input. */ GPIOA->AFRL |= (GPIO_AF_0 << GPIO_CRL_MODE5_SHIFT); /* Use AF0. */ /* Setup PA9, PA10. */ GPIOA->CRH = ~GPIO_CRH_MODE9_MASK; GPIOA->CRH |= (GPIO_PinMode_AF_PushPull << GPIO_CRH_MODE9_SHIFT); /* PA9 multiplexed push-pull output. */ GPIOA->AFRH = ~GPIO_AFRH_AFR_MASK; GPIOA->AFRH |= (GPIO_AF_1 << GPIO_CRH_MODE9_SHIFT); /* Use AF1. */ GPIOA->CRH = ~GPIO_CRH_MODE10_MASK; GPIOA->CRH |= (GPIO_PinMode_In_Floating << GPIO_CRH_MODE10_SHIFT); /* PA10 floating input. */ GPIOA->AFRH |= (GPIO_AF_1 << GPIO_CRH_MODE10_SHIFT); /* Use AF1. */ }
UART初始化 uart_init()
初始化UART,配置時(shí)鐘頻率、波特率、數(shù)據(jù)長(zhǎng)度、停止位、傳輸模式及是否使用校驗(yàn)。
void uart_init() { /* Clear the corresponding bit to be used. */ UART1->CCR = ~( UART_CCR_PEN_MASK | UART_CCR_PSEL_MASK | UART_CCR_SPB0_MASK | UART_CCR_SPB1_MASK | UART_CCR_CHAR_MASK ); UART1->GCR = ~( UART_GCR_AUTOFLOWEN_MASK | UART_GCR_RXEN_MASK | UART_GCR_TXEN_MASK ); /* WordLength. */ UART1->CCR |= UART_CCR_CHAR_MASK; /* XferMode. */ UART1->GCR |= (UART_XferMode_RxTx << UART_GCR_RXEN_SHIFT); /* Setup baudrate, BOARD_DEBUG_UART_FREQ = 48000000u, BOARD_DEBUG_UART_BAUDRATE = 9600u. */ UART1->BRR = (BOARD_DEBUG_UART_FREQ / BOARD_DEBUG_UART_BAUDRATE) / 16u; UART1->FRA = (BOARD_DEBUG_UART_FREQ / BOARD_DEBUG_UART_BAUDRATE) % 16u; /* Enable UART1. */ UART1->GCR |= UART_GCR_UARTEN_MASK; }
SPI初始化 spi_init()
操作全局控制寄存器(SPI_I2S_GCTL)的MODE位,配置SPI為主模式,操作波特率發(fā)生器(SPI_I2S_SPBREG)配置波特率為400KHz,總線時(shí)鐘頻率為48MHz,操作通用控制寄存器(SPI_I2S_CCTL)的CPOL位與CPHA位配置通信模式,操作LSB在前使能位(LSBFE)令數(shù)據(jù)發(fā)送或接收最高位在前,操作全局控制寄存器對(duì)發(fā)送和接收數(shù)據(jù)寄存器有效數(shù)據(jù)進(jìn)行選擇(DW8_32),配置為只有低8位有效,設(shè)置NSS位置1,使硬件控制主模式下的NSS輸出,配TXEN位與RXEN位置1,使能發(fā)送與接收;置SPI/I2S選擇位(SPIEN)為1,使能SPI。
void spi_init() { /* Master. */ SPI1->GCTL = SPI_I2S_GCTL_MODE_MASK; /* Master mode. */ /* XferMode. */ SPI1->GCTL |= (SPI_I2S_GCTL_RXEN_MASK | SPI_I2S_GCTL_TXEN_MASK); /* Enable TX and RX. */ /* AutoCS. */ SPI1->GCTL |= SPI_I2S_GCTL_NSS_MASK; /* NSS select signal that from hardware. */ /* BaudRate. */ SPI1->SPBRG = 120u; /* SPBRG = fpclk / baudrate = 48000000 / 400000 = 120. */ SPI1->CCTL = ~(SPI_I2S_CCTL_TXEDGE_MASK | SPI_I2S_CCTL_RXEDGE_MASK); /* Sampling data in the middle of transmission data bits. */ /* DataWidth. */ SPI1->GCTL = ~SPI_I2S_GCTL_DW832_MASK; /* Only the lower 8 bits are valid. */ /* CPOL CPHA. */ SPI1->CCTL = ~(SPI_I2S_CCTL_CPHA_MASK | SPI_I2S_CCTL_CPOL_MASK); /* CPOL = 0, CPHA = 0. */ /* LSB first enable bit. */ SPI1->CCTL = ~SPI_I2S_CCTL_LSBFE_MASK; /* The highest bit of data transmission or reception comes first. */ /* Enbale SPI. */ SPI1->GCTL |= SPI_I2S_GCTL_SPIEN_MASK; }
SPI發(fā)送數(shù)據(jù) spi_putbyte()
當(dāng)發(fā)送緩沖器未滿時(shí),將數(shù)據(jù)傳入發(fā)送數(shù)據(jù)寄存器(SPI_I2S_TXRFG),根據(jù)初始化配置,數(shù)據(jù)低8位有效,通過(guò)MOSI引腳串行輸出。
void spi_putbyte(uint8_t c) { while (SPI_I2S_CSTAT_TXFULL_MASK SPI1->CSTAT) {} SPI1->TXREG = c; }
SPI接收數(shù)據(jù) spi_getbyte()
當(dāng)接收端緩沖器接收了一個(gè)完整字節(jié)時(shí),讀接收數(shù)據(jù)寄存器(SPI_I2S_RXREG),返回接收數(shù)據(jù)。
uint8_t spi_getbyte() { while (0u == (SPI_I2S_CSTAT_RXAVL_MASK SPI1->CSTAT) ) {} return SPI1->RXREG; }
main()函數(shù)
main()函數(shù)結(jié)合上述操作,初始化SPI,定義發(fā)送數(shù)組spi_tx_buf[16]并賦值,將spi_tx_buf[16]數(shù)組中的數(shù)值進(jìn)行發(fā)送,定義接收數(shù)組spi_rx_buf[16]對(duì)數(shù)據(jù)進(jìn)行接收。本實(shí)驗(yàn)使用杜邦線將MOSI引腳與MISO相連,因此,發(fā)送數(shù)據(jù)與接收數(shù)據(jù)應(yīng)相同,將發(fā)送數(shù)組與接收數(shù)組的數(shù)值進(jìn)行比較,定義變量spi_xfer_err_count用于計(jì)數(shù)發(fā)送與接收數(shù)值不同的數(shù)據(jù)個(gè)數(shù)。若發(fā)送與接收數(shù)值相同則串口輸出"spi loopback xfer done.",若不同則串口輸出傳輸錯(cuò)誤與出錯(cuò)個(gè)數(shù)。實(shí)驗(yàn)現(xiàn)象如圖6所示。
int main() { enable_clock(); pin_init(); uart_init(); printf("spi_basic example.rn"); spi_init(); for (uint32_t i = 0u; i < 16u; i++) { spi_tx_buf[i] = i; } /* SPI xfer once. */ for (uint32_t i = 0u; i < 16u; i++) { spi_putbyte(spi_tx_buf[i]); spi_rx_buf[i] = spi_getbyte(); } /* validation. */ spi_xfer_err_count = 0u; for (uint32_t i = 0u; i < 16u; i++) { if (spi_rx_buf[i] != spi_tx_buf[i]) { spi_xfer_err_count++; } } if (spi_xfer_err_count == 0u) { printf("spi loopback xfer done.rn"); } else { printf("spi loopback xfer error. spi_xfer_err_count = %urn", (unsigned)spi_xfer_err_count); } while (1) {} }
圖6.實(shí)驗(yàn)現(xiàn)象
來(lái)源: 靈動(dòng)MM32MCU
-
mcu
+關(guān)注
關(guān)注
146文章
17349瀏覽量
352756 -
接口
+關(guān)注
關(guān)注
33文章
8706瀏覽量
151987 -
SPI
+關(guān)注
關(guān)注
17文章
1724瀏覽量
92173 -
通信總線
+關(guān)注
關(guān)注
0文章
45瀏覽量
9890
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論