SPI

開發平臺

  • 野火開發板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();

}
發佈了17 篇原創文章 · 獲贊 20 · 訪問量 5218
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章