開發平臺
- 野火開發板F429
- 標準庫
SPI
SPI也是通訊協議,通常會拿去和I2C做比較。
SPI比I2C的數據線要多一點,可是通訊速率快了很多
SPI總線上也是可以掛載多個外設的,但不同的是SPI外設並沒有設備地址,所以每個SPI都有一條線選信號線(CS),當這條信號線被選中時,STM32就和信號線被選中的設備通信
SPI總線:MOSI(主設備輸出從設備輸出入)、MISO(主設備輸入從設備輸出)、SCL(時鐘線)+SPI片選線:CS(片選信號線)
SPI時序
CS(片選信號線)低電平時,表示通信開始,CS被拉高時表示通訊結束,在CS爲低電平期間,就可傳輸數據
對於SCL時鐘線,並非高電平有效或者低電平採集到的數據有效(這裏沒有I2C那麼簡單),SCL線上又分爲時鐘相位和時鐘極性
時鐘極性:通訊前SCL的電平高低(由自己決定)低電平爲1、高電平爲0
時鐘相位:什麼時候採樣,奇數邊沿採樣還是偶數邊沿採樣,奇數邊沿爲0,偶數邊沿爲1
所以SPI有四種採樣模式,但要根據不同外設的需求去選擇,不是所有的外設都支持這四種模式
如下圖:
數據傳輸時,可選擇高位先行還是低位先行,以及數據幀的格式:一次傳輸8位還是16位
SPI架構圖
同樣的如果要直接操作寄存器的話,一定要看架構圖
1.SCL、MISO、MOSI(並不是普通的GPIO口,需用複用成SPI)NSS配置成普通的GPIO口即可
2.波特率發生器就是配置寄存器CR1:分頻係數(即SPI的通訊速率)、通訊模式、數據幀格式、等等
(就是標準庫,SPI的那些參數)
3.發送接受數據(數據寄存器DR)
4.就是一些邏輯的控制(狀態寄存器SR)
其實,這些架構圖看多了,就會發現,都是大同小異
SPI讀寫串行FLASH
- W25Q128,採樣模式只支持模式0和模式3
- W25Q128有指令編碼,可發送指令編碼對其進行操作,詳情看手冊
編程步驟
你會發現和I2C沒什麼區別
1.初始化引腳
2.配置SPI參數
3.編寫發送接收函數
代碼如下:
#include "spi.h"
void SPI_GPIO_Config(void)
{
GPIO_InitTypeDef GPIO_InitStruct;
RCC_AHB1PeriphClockCmd(SPI_SCL_CLOCK|
SPI_MISO_CLOCK|
SPI_MOSI_CLOCK|
SPI_CS_CLOCK,ENABLE);
GPIO_PinAFConfig(SPI_SCL_PIN_PORT,SPI_SCL_PINSORUCE, GPIO_AF_SPI5);
GPIO_PinAFConfig(SPI_MISO_PIN_PORT,SPI_MISO_PINSORUCE, GPIO_AF_SPI5);
GPIO_PinAFConfig(SPI_MOSI_PIN_PORT,SPI_MOSI_PINSORUCE, GPIO_AF_SPI5);
/*
**PF7 PF8 PF9
**複用模式
**推輓輸出
**不上拉不下拉
**輸出速率50MHZ
*/
GPIO_InitStruct.GPIO_Mode=GPIO_Mode_AF;
GPIO_InitStruct.GPIO_OType=GPIO_OType_PP;
GPIO_InitStruct.GPIO_PuPd=GPIO_PuPd_NOPULL;
GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_InitStruct.GPIO_Pin=SPI_SCL_PIN;
GPIO_Init(SPI_SCL_PIN_PORT,&GPIO_InitStruct);
GPIO_InitStruct.GPIO_Pin=SPI_MOSI_PIN;
GPIO_Init(SPI_MOSI_PIN_PORT,&GPIO_InitStruct);
GPIO_InitStruct.GPIO_Pin=SPI_MISO_PIN;
GPIO_Init(SPI_MISO_PIN_PORT,&GPIO_InitStruct);
GPIO_InitStruct.GPIO_Mode=GPIO_Mode_OUT;
GPIO_InitStruct.GPIO_Pin=SPI_CS_PIN;
GPIO_Init(SPI_CS_PIN_PORT,&GPIO_InitStruct);
/*把CS拉高*/
SPI_CS_HIGH();
}
void SPI_Config(void)
{
SPI_InitTypeDef SPI_InitStruct;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI5,ENABLE);
SPI_GPIO_Config();
/*
**SPI主模式
**SPI沒有開始通訊時SCL爲低電平
**奇數採樣
**2分頻 45Mbits/s
**字長8位
**雙向全雙工通訊
**高位先行
**NSS 軟件模式
**沒有使能CRC校驗,CRC的值無效,隨意填
*/
SPI_InitStruct.SPI_Mode=SPI_Mode_Master;
SPI_InitStruct.SPI_CPOL=SPI_CPOL_Low;
SPI_InitStruct.SPI_CPHA=SPI_CPHA_1Edge;
SPI_InitStruct.SPI_BaudRatePrescaler=SPI_BaudRatePrescaler_2;
SPI_InitStruct.SPI_DataSize=SPI_DataSize_8b;
SPI_InitStruct.SPI_Direction=SPI_Direction_2Lines_FullDuplex;
SPI_InitStruct.SPI_FirstBit=SPI_FirstBit_MSB;
SPI_InitStruct.SPI_NSS=SPI_NSS_Soft;
SPI_InitStruct.SPI_CRCPolynomial=7;
SPI_Init(SPI_PORT,&SPI_InitStruct);
SPI_Cmd(SPI_PORT,ENABLE);
}
uint8_t SPI_Sendbyte(uint8_t data)
{
/*等待發送緩衝區爲空*/
while(SPI_I2S_GetFlagStatus(SPI_PORT,SPI_I2S_FLAG_TXE)!=SET);
/*發送數據*/
SPI_I2S_SendData(SPI_PORT,data);
/*等待接收緩衝區爲非空*/
while(SPI_I2S_GetFlagStatus(SPI_PORT,SPI_I2S_FLAG_RXNE)!=SET);
/*讀取數據並返回數據*/
return SPI_I2S_ReceiveData(SPI_PORT);
}
uint8_t SPI_Recvbyte(void)
{
return SPI_Sendbyte(DUMMY_BYTE);
}
uint32_t FLASH_ReadID(void)
{
uint32_t temp=0,temp0=0,temp1=0,temp2=0;
/*NSS拉低通訊開始*/
SPI_CS_LOW();
/*發送讀取ID命令*/
SPI_Sendbyte(JedecDeviceID);
/*獲取ID*/
temp0=SPI_Sendbyte(DUMMY_BYTE);
temp1=SPI_Sendbyte(DUMMY_BYTE);
temp2=SPI_Sendbyte(DUMMY_BYTE);
/*NSS拉高通訊結束*/
SPI_CS_HIGH();
/*ID*/
temp=(temp0 <<16)|(temp1 <<8)|(temp2);
return temp;
}
void FLASH_Readcmd(void)
{
/*NSS拉低通訊開始*/
SPI_CS_LOW();
SPI_Sendbyte(WriteEnable);
/*NSS拉高通訊結束*/
SPI_CS_HIGH();
}
void FLASH_Waitforwriteend(void)
{
uint8_t flash_flagstatus=0;
/*NSS拉低通訊開始*/
SPI_CS_LOW();
SPI_Sendbyte(ReadStatusReg);
flash_flagstatus=SPI_Sendbyte(DUMMY_BYTE);
while((flash_flagstatus & 0x01)==1)
{
flash_flagstatus=SPI_Sendbyte(DUMMY_BYTE);
}
/*NSS拉高通訊結束*/
SPI_CS_HIGH();
}
void FLASH_Sectorerase(uint32_t sectoraddr)
{
FLASH_Readcmd();
FLASH_Waitforwriteend();
SPI_CS_LOW();
SPI_Sendbyte(SectorErase);
SPI_Sendbyte((sectoraddr&0xFF0000) >>16);
SPI_Sendbyte((sectoraddr&0xFF00) >>8);
SPI_Sendbyte(sectoraddr&0xFF);
SPI_CS_HIGH();
FLASH_Waitforwriteend();
}
void FLASH_Writepage(uint32_t sectoraddr, uint8_t *pbuffer,uint32_t num)
{
FLASH_Readcmd();
SPI_CS_LOW();
SPI_Sendbyte(PageProgram);
SPI_Sendbyte((sectoraddr&0xFF0000) >>16);
SPI_Sendbyte((sectoraddr&0xFF00) >>8);
SPI_Sendbyte( sectoraddr&0xFF);
if(num > 256)
{
num=256;
printf("num已經超出範圍\n");
}
while(num--)
{
SPI_Sendbyte(*pbuffer);
pbuffer ++;
}
SPI_CS_HIGH();
FLASH_Waitforwriteend();
}
void FLASH_Readbuffer(uint32_t sectoraddr, uint8_t *pbuffer,uint32_t num)
{
FLASH_Readcmd();
SPI_CS_LOW();
SPI_Sendbyte(ReadData);
SPI_Sendbyte((sectoraddr&0xFF0000) >>16);
SPI_Sendbyte((sectoraddr&0x00FF00) >>8);
SPI_Sendbyte( sectoraddr&0x0000FF);
while(num--)
{
*pbuffer=SPI_Sendbyte(0xff);
pbuffer ++;
}
SPI_CS_HIGH();
}