SPI特色
SPI為一種同步的全雙工串列通信介面,常用於連接周邊晶片,例如:ADC、DAC、通訊IC等
- 一部主機可以與多台從機通訊,但需要分時進行
- 如果從機太多可使用GPIO來取代SSL線
- 主機設定從機的SSL腳位為低準位作為選擇
- 一般傳輸速率為5M/10M/20M bps
- SPI是利用Rising edge, Falling edge決定資料的採樣

- 4條主要訊號線
- MOSI
- Master Output, Slave Input
- MISO
- Master Input, Slave Output
- SCK
- 串列時脈,由master發出
- 時鐘訊號線
- 有時鐘訊號線,因此為同步通訊
- SCK最高頻率被限制在$f_{pclk}$
- SSI/NSS/CS
- Slave Selected,master選擇要跟哪個slave進行通訊
- 主機將從機SSI拉低成低電位,並將其他從機拉成高電位,藉此與特定從機溝通
- 同理將SSL電位拉高,結束與從機之間的通訊
- MOSI
- SPI為全雙工
- 有兩條數據線,可以同時收發數據
- NSS啟動通訊
- SCK
- SCK邊緣觸發
- 上升/下降緣
- 告知訊號可以發生變化了
- SCK邊緣採樣
- 上升/下降緣
- 採樣對應的輸入輸出
- SCK邊緣觸發
- CPOL
- Clock Polarity
- 選擇待機模式下的SCK要保持高準位還是低準位
- CPOL=1 -> 待機高準位
- CPOL=0 -> 待機低準位
- CPHA
- Clock Phase
- 選擇資料再SCK的基數次變化或是偶數次變化
- CPHA=0 -> 奇次讀取
- CPHA=1 -> 偶次讀取
- 例如選擇奇數次讀取,對於CPOL=0來說是上升緣採樣;對於CPOL=1來說就是下降緣採樣

因此將CPOL與CPHA結合可以決定資料在哪種狀態下讀取。如果在Rising edge讀取數據,則發送端在Falling edge進行資料的轉態
其中CPOL=0,CPHA=0(奇數邊緣)以及CPOL=1,CPHA=1(偶數邊緣)使用最多。實踐使用時Master, Slaves之間要協調好要使用哪種模式進行溝通

STM32編寫SPI
- 數據手冊查看相應PIN腳
- 控制暫存器CR1的BR位控制$f_{pclk}$分頻因子
- $f_{pclk}$指的是SPI所在總線上的總線頻率
- APB1為$f_{pclk1}$
- APB2為$f_{pclk2}$
- 通過設置控制暫存器CR1的CPOL以及CPHA位,可以將SPI設定成前面提到的四種模式之一
- 透過控制暫存器CR1的DEF位,可以配置成8, 16bits模式
- 透過控制暫存器CR1的LSBFIRST位可以選擇MSB先行還是LSB先行
- 控制暫存器CR2
- TXEIE=1時,開啟TX中斷
- RXNEIE=1時,開啟RX中斷
- 狀態暫存器SR
- RXNE=1時,接收暫存器非空
- TXE=1時,發送暫存器為空
- BSY=1時,表示SPI正在工作
- FRE=1時,幀格式發生錯誤
- 操作DR暫存器能夠控制SPI的讀寫
- 對DR進行寫入,會將資料寫入TX buffer
- 對DR進行讀取,會將RX buffer的資料讀取出來
閱讀datasheet重點
- 看特色
- Flash地址長度
- 分塊、扇區擦除大小
- 寫入數據大小範圍
- 最高運行頻率
- 電壓工作範圍
- etc.
- 大致了解各個PIN腳定義
- flash中的SPI的功能框圖提及到工作模式選項(Mode 0, 3)
- 狀態暫存器的介紹
- 重要暫存器
- BUSY位
- WEL位
- 重要暫存器
- 關注指令集
- 讀寫操作
- 擦除操作
- 讀狀態操作
- ID與製造商消息等
- 在寫code前要了解MCU與flash之間交互的機制
- CS PIN拉低拉高代表的意義
- 檢查flash狀態暫存器的bit位
- 關注SPI的SR暫存器每個比特位的意義
- dummy指令可以用來使flash回傳資料
- 擦除、寫入等操作前需要先開啟Write Enable
- 指令中的地址是MSB先行
- 每個指令之間都存在些許差異,編寫前需要詳細閱讀指令介紹
程式碼範例
- 通用標頭檔
#ifndef __BSP_SPI_H_
#define __BSP_SPI_H_
#include "stm32f4xx.h"
#include "stm32f4xx_gpio.h"
#include "stdint.h"
// RCC CLOCK
/*****************************************/
// SPI
#define SPI_RCC_CLK_CMD RCC_APB2PeriphClockCmd
#define SPI_RCC_CLK_PERIPH RCC_APB2Periph_SPI5
#define SPI_RCC_CLK_OP ENABLE
// GPIO
#define GPIO_RCC_CLK_CMD RCC_AHB1PeriphClockCmd
#define GPIO_RCC_CLK_PERIPH RCC_AHB1Periph_GPIOF
#define GPIO_RCC_CLK_OP ENABLE
/*****************************************/
// pin define
/*****************************************/
// CSS
#define SPI_CSS_GPIO_PORT GPIOF
#define SPI_CSS_GPIO_PIN GPIO_Pin_6
#define SPI_CSS_GPIO_MODE GPIO_Mode_OUT
#define SPI_CSS_GPIO_SPEED GPIO_Fast_Speed
#define SPI_CSS_GPIO_PUPD GPIO_PuPd_NOPULL
#define SPI_CSS_GPIO_OTYPE GPIO_OType_PP
// SCLK
#define SPI_SCLK_GPIO_PORT GPIOF
#define SPI_SCLK_GPIO_PIN GPIO_Pin_7
#define SPI_SCLK_GPIO_MODE GPIO_Mode_AF
#define SPI_SCLK_GPIO_PIN_SOURCE GPIO_PinSource7
#define SPI_SCLK_GPIO_AF GPIO_AF_SPI5
// MISO
#define SPI_MISO_GPIO_PORT GPIOF
#define SPI_MISO_GPIO_PIN GPIO_Pin_8
#define SPI_MISO_GPIO_MODE GPIO_Mode_AF
#define SPI_MISO_GPIO_PIN_SOURCE GPIO_PinSource8
#define SPI_MSIO_GPIO_AF GPIO_AF_SPI5
// MOSI
#define SPI_MOSI_GPIO_PORT GPIOF
#define SPI_MOSI_GPIO_PIN GPIO_Pin_9
#define SPI_MOSI_GPIO_MODE GPIO_Mode_AF
#define SPI_MOSI_GPIO_PIN_SOURCE GPIO_PinSource9
#define SPI_MOSI_GPIO_AF GPIO_AF_SPI5
// CS pin control
#define FLASH_SPI_CSS_LOW() GPIO_ResetBits(SPI_CSS_GPIO_PORT, SPI_CSS_GPIO_PIN)
#define FLASH_SPI_CSS_HIGH() GPIO_SetBits(SPI_CSS_GPIO_PORT, SPI_CSS_GPIO_PIN)
/*****************************************/
// SPI
/*****************************************/
#define FLASH_SPI SPI5
#define FLASH_SPI_OP ENABLE
#define FLASH_SPI_BRP SPI_BaudRatePrescaler_2
#define FLASH_SPI_CPHA SPI_CPHA_2Edge
#define FLASH_SPI_CPOL SPI_CPOL_High
#define FLASH_SPI_CRC 7
#define FLASH_SPI_DATA_SIZE SPI_DataSize_8b
#define FLASH_SPI_DIRECTION SPI_Direction_2Lines_FullDuplex
#define FLASH_SPI_FIRSTBIT SPI_FirstBit_MSB
#define FLASH_SPI_MODE SPI_Mode_Master
#define FLASH_SPI_NSS SPI_NSS_Soft
/*****************************************/
// Flash Command
/*****************************************/
#define FLASH_DUMMY 0xFF
#define FLASH_SPI_JEDEC 0x9F
#define FLASH_WRITE_EN 0x06
#define FLASH_READ_STATUS_REG 0x05
#define FLASH_POWER_DOWN 0xB9
#define FLASH_RELEASE_POWER_DOWN 0xAB
#define FLASH_ERASE_SECTOR 0x20
#define FLASH_READ_DATA 0x03
#define FLASH_PAGE_PROGRAME 0x02
/*****************************************/
// Functions
/*****************************************/
void Flash_Init(void);
uint32_t Flash_Read_WRITE_JEDEC_ID(void);
uint32_t SPI_FLASH_ReadID(void);
void Flash_Write_Enable(void);
void Flash_Wait_For_Ready(void);
void Flash_Power_Down(void);
uint32_t Flash_Release_Power_Down(void);
void Flash_Erase_Sector(uint32_t addr);
void Flash_Read_Data(uint32_t addr, uint8_t* buffer, uint32_t count);
void Flash_Write_Data(uint32_t addr, uint8_t* data, int count);
/*****************************************/
#endif /* __BSP_SPI_H_ */
- 幾個常見操作
- 讀寫
- 製造商信息
- 暫存器讀寫
- power down/release操作等
#include "bsp_spi.h"
/*
* GPIO Init function
*/
void Flash_GPIO_Init(){
GPIO_InitTypeDef gpio_init;
/* RCC Enable */
GPIO_RCC_CLK_CMD(GPIO_RCC_CLK_PERIPH, GPIO_RCC_CLK_OP);
/* Config AF Pin */
GPIO_PinAFConfig(SPI_SCLK_GPIO_PORT, SPI_SCLK_GPIO_PIN_SOURCE, SPI_SCLK_GPIO_AF);
GPIO_PinAFConfig(SPI_MISO_GPIO_PORT, SPI_MISO_GPIO_PIN_SOURCE, SPI_MSIO_GPIO_AF);
GPIO_PinAFConfig(SPI_MOSI_GPIO_PORT, SPI_MOSI_GPIO_PIN_SOURCE, SPI_MOSI_GPIO_AF);
/* configure css pin */
gpio_init.GPIO_Pin = SPI_CSS_GPIO_PIN;
gpio_init.GPIO_Mode = SPI_CSS_GPIO_MODE;
gpio_init.GPIO_Speed = SPI_CSS_GPIO_SPEED;
gpio_init.GPIO_PuPd = SPI_CSS_GPIO_PUPD;
gpio_init.GPIO_OType = SPI_CSS_GPIO_OTYPE;
GPIO_Init(SPI_CSS_GPIO_PORT, &gpio_init);
/* configure sclk pin */
gpio_init.GPIO_Pin = SPI_SCLK_GPIO_PIN;
gpio_init.GPIO_Mode = SPI_SCLK_GPIO_MODE;
GPIO_Init(SPI_SCLK_GPIO_PORT, &gpio_init);
/* configure miso pin */
gpio_init.GPIO_Pin = SPI_MISO_GPIO_PIN;
GPIO_Init(SPI_MISO_GPIO_PORT, &gpio_init);
/* configure mosi pin */
gpio_init.GPIO_Pin = SPI_MOSI_GPIO_PIN;
GPIO_Init(SPI_MOSI_GPIO_PORT, &gpio_init);
}
/*
* SPI Init function
*/
void Flash_SPI_Init(){
SPI_InitTypeDef spi_init;
/* RCC Enable */
SPI_RCC_CLK_CMD(SPI_RCC_CLK_PERIPH, SPI_RCC_CLK_OP);
/* Stop Flash Signal */
FLASH_SPI_CSS_HIGH();
/* configure SPI */
spi_init.SPI_BaudRatePrescaler = FLASH_SPI_BRP;
spi_init.SPI_CPHA = FLASH_SPI_CPHA;
spi_init.SPI_CPOL = FLASH_SPI_CPOL;
spi_init.SPI_CRCPolynomial = FLASH_SPI_CRC;
spi_init.SPI_DataSize = FLASH_SPI_DATA_SIZE;
spi_init.SPI_Direction = FLASH_SPI_DIRECTION;
spi_init.SPI_FirstBit = FLASH_SPI_FIRSTBIT;
spi_init.SPI_Mode = FLASH_SPI_MODE;
spi_init.SPI_NSS = FLASH_SPI_NSS;
SPI_Init(FLASH_SPI, &spi_init);
/* Enable SPI Flash */
SPI_Cmd(FLASH_SPI, FLASH_SPI_OP);
}
/*
* API interface
*/
void Flash_Init(){
Flash_GPIO_Init();
Flash_SPI_Init();
}
/*
* Flash Send & Read operation
*/
static uint8_t SPI_FLASH_SendByte(uint8_t byte){
while (SPI_I2S_GetFlagStatus(FLASH_SPI, SPI_I2S_FLAG_TXE) == RESET){}
SPI_I2S_SendData(FLASH_SPI, byte);
while (SPI_I2S_GetFlagStatus(FLASH_SPI, SPI_I2S_FLAG_RXNE) == RESET){}
return SPI_I2S_ReceiveData(FLASH_SPI);
}
/**
* Read Flash data
*/
static uint8_t SPI_FLASH_ReadByte(){
return SPI_FLASH_SendByte(FLASH_DUMMY);
}
/*
* FLASH W25Q256JV read/write JEDEC ID command
*/
uint32_t Flash_Read_WRITE_JEDEC_ID(){
uint32_t ManufacturerID, MemoryID, CapacityID;
/* CS LOW */
FLASH_SPI_CSS_LOW();
/* send command to flash */
SPI_FLASH_SendByte(FLASH_SPI_JEDEC);
/* read data */
ManufacturerID = SPI_FLASH_ReadByte();
MemoryID = SPI_FLASH_ReadByte();
CapacityID = SPI_FLASH_ReadByte();
/* CS HIGH */
FLASH_SPI_CSS_HIGH();
return (ManufacturerID << 16) | (MemoryID << 8) | CapacityID;
}
/*
* Flash Write Enable
*/
void Flash_Write_Enable(){
FLASH_SPI_CSS_LOW();
SPI_FLASH_SendByte(FLASH_WRITE_EN);
FLASH_SPI_CSS_HIGH();
}
/*
* Wait until BUSY & WEL become 0
*/
void Flash_Wait_For_Ready(){
uint8_t status=0;
FLASH_SPI_CSS_LOW();
SPI_FLASH_SendByte(FLASH_READ_STATUS_REG);
do{
status = SPI_FLASH_SendByte(FLASH_DUMMY);
}while((status & 0x01) == SET);
FLASH_SPI_CSS_HIGH();
}
/*
* Flash Power Down
*/
void Flash_Power_Down(){
FLASH_SPI_CSS_LOW();
SPI_FLASH_SendByte(FLASH_POWER_DOWN);
FLASH_SPI_CSS_HIGH();
}
/*
* Flash Release Power Down
*/
uint32_t Flash_Release_Power_Down(){
uint32_t rev;
FLASH_SPI_CSS_LOW();
SPI_FLASH_SendByte(FLASH_RELEASE_POWER_DOWN);
SPI_FLASH_SendByte(FLASH_DUMMY);
SPI_FLASH_SendByte(FLASH_DUMMY);
SPI_FLASH_SendByte(FLASH_DUMMY);
rev = SPI_FLASH_SendByte(FLASH_DUMMY);
FLASH_SPI_CSS_HIGH();
return rev;
}
/*
* Flash Erase Sector
*/
void Flash_Erase_Sector(uint32_t addr){
Flash_Write_Enable();
FLASH_SPI_CSS_LOW();
SPI_FLASH_SendByte(FLASH_ERASE_SECTOR);
SPI_FLASH_SendByte((addr >> 24) & 0xff);
SPI_FLASH_SendByte((addr >> 16) & 0xff);
SPI_FLASH_SendByte((addr >> 8) & 0xff);
SPI_FLASH_SendByte(addr & 0xff);
FLASH_SPI_CSS_HIGH();
Flash_Wait_For_Ready();
}
/*
* Flash Read Data
*/
void Flash_Read_Data(uint32_t addr, uint8_t* buffer, uint32_t count){
Flash_Wait_For_Ready();
FLASH_SPI_CSS_LOW();
SPI_FLASH_SendByte(FLASH_READ_DATA);
SPI_FLASH_SendByte((addr >> 24) & 0xff);
SPI_FLASH_SendByte((addr >> 16) & 0xff);
SPI_FLASH_SendByte((addr >> 8) & 0xff);
SPI_FLASH_SendByte(addr & 0xff);
while (count--){
*(buffer++) = SPI_FLASH_SendByte(FLASH_DUMMY);
}
FLASH_SPI_CSS_HIGH();
}
/*
* Flash Page Write
*/
void Flash_Write_Data(uint32_t addr, uint8_t* data, int count){
Flash_Write_Enable();
FLASH_SPI_CSS_LOW();
SPI_FLASH_SendByte(FLASH_PAGE_PROGRAME);
SPI_FLASH_SendByte((addr >> 24) & 0xff);
SPI_FLASH_SendByte((addr >> 16) & 0xff);
SPI_FLASH_SendByte((addr >> 8) & 0xff);
SPI_FLASH_SendByte(addr & 0xff);
while(count--){
SPI_FLASH_SendByte(*data);
data++;
}
FLASH_SPI_CSS_HIGH();
Flash_Wait_For_Ready();
}