SPI Flash讀取操作

今天看到有人在問SPI flash讀取數據的方法,爲什麼這樣讀取?

先給出一個函數,SPI的讀取函數:

/*!
    \brief      read a block of data from the flash
    \param[in]  pbuffer: pointer to the buffer that receives the data read from the flash
    \param[in]  read_addr: flash's internal address to read from
    \param[in]  num_byte_to_read: number of bytes to read from the flash
    \param[out] none
    \retval     none
*/
spiflash_ret spiflash_buffer_read(uint8_t* pbuffer, uint32_t read_addr, uint16_t num_byte_to_read)
{
    spiflash_ret ret = spiflash_ret_success;
    /* select the flash: chip slect low */
    SPI_FLASH_CS_LOW();

    /* send "read from memory " instruction */
    spi_flash_send_byte(READ);

    /* send read_addr high nibble address byte to read from */
    spi_flash_send_byte((read_addr & 0xFF0000) >> 16);
    /* send read_addr medium nibble address byte to read from */
    spi_flash_send_byte((read_addr& 0xFF00) >> 8);
    /* send read_addr low nibble address byte to read from */
    spi_flash_send_byte(read_addr & 0xFF);

    /* while there is data to be read */
    while(num_byte_to_read--){
        /* read a byte from the flash */
        *pbuffer = spi_flash_send_byte(DUMMY_BYTE);
        /* point to the next location where the byte read will be saved */
        pbuffer++;
    }

    /* deselect the flash: chip select high */
    SPI_FLASH_CS_HIGH();
    
    return ret;
}

函數的參數就不再說了,有註釋看下就可以。一個是返回的數據地址,一個是即將讀取的地址,這裏應該通俗一點說,要讀取flash上哪個位置,最後一個是讀取的字節數!

OK!,這裏還是有學問的,至少要有這樣的疑問:

對於第二個參數!第二個參數是flash的位置,就是說我們要從flash上哪個地方讀取數據!

那必須要想到這個範圍是多大?範圍是多大要看flash的容量

這裏比如說我用的是4M的flash ,4M 哦, 4 * 1024 * 1024個字節嘛

這裏我先把4M的二進制和十六進制表示出來,後面用:

4M的十六進制表方法:0x40 00 00 (每兩個字節之間空了一個空格)

4M的二進制表示方法:100 0000 0000 0000 0000 0000(不好意思,有點長,總共是23位,你數一數)

好了進入函數裏面

首先,SPI的片選你要知道,不知道的網上搜一大把,我就不說了(因爲我也不知道.....)

要說的重點先通過

    spi_flash_send_byte(READ);

這個函數發送了READ數據,不好意思READ這裏定義的是0x03,前面沒有定義,抱歉。

就是說在讀取的時候要先發送一個0x03,爲什麼是0x03呢?

來,我們看下SPEC,Flash的數據手冊:

貼下圖吧:

 這裏找到了讀取flash數據的方法:

The Read Data Bytes(Read) command is followed by a 3-byte address(A23-A00,and each bit is latched-in on the rising edge of SCLK.

好了,就看着一句話,看看我們發現了什麼 (Read)command is followed by 3-byte address

Read command 就是0x03,看下面的圖 Command 03H ,對吧,followed by 3-byte address 就是圖上的24-bit address

爲什麼是24-bit address,想一下,發送了0x03給flash之後,我們還沒有告訴flash我們要讀這個flash的哪個位置呢

所以緊跟着再把要讀的位置告訴flash,這個位置是多少位呢?23位!也就是起前面我們說的4M的二進制表示方法,但是這裏爲什麼要是24bit呢,對8整除嘛,最高一個bit不用就是了。

這就是我們代碼中進跟着的發送了三個byte的地址的原因。爲什麼要分三次發呢?發送先發高位還是先發低位呢?

看圖,圖上是從23到0,所以你發送的時候也要先發高位的,再發低位,分三次發的原因是我們這個函數只支持發送8位的數據

貼下這個函數:

/*!
    \brief      send a byte through the SPI interface and return the byte received from the SPI bus
    \param[in]  byte: byte to send
    \param[out] none
    \retval     the value of the received byte
*/
uint8_t spi_flash_send_byte(uint8_t byte)
{
    /* loop while data register in not emplty */
    while (RESET == spi_i2s_flag_get(SPI1,SPI_FLAG_TBE));

    /* send byte through the SPI1 peripheral */
    spi_i2s_data_transmit(SPI1,byte);

    /* wait to receive a byte */
    while(RESET == spi_i2s_flag_get(SPI1,SPI_FLAG_RBNE));

    /* return the byte read from the SPI bus */
    return(spi_i2s_data_receive(SPI1));
}

看到了吧,參數是uint8_t。

發送了讀取命令和讀取位置之後,flash就知道我們要取哪裏的數據了,那麼想讀多少我們就讀多少了!

後面就是循環讀取的操作了。

    /* while there is data to be read */
    while(num_byte_to_read--){
        /* read a byte from the flash */
        *pbuffer = spi_flash_send_byte(DUMMY_BYTE);
        /* point to the next location where the byte read will be saved */
        pbuffer++;
    }

怎麼循環讀取?

直接對flash發送一個DUMMY_BYTE(0xFF)的指令就可以了,然後呢,這個函數返回的就是所要讀取的數據,你按照順序讀取的話,就讀到對應地址開始的順序數據了,就跟讀文件一樣,一直往下讀了。

這裏有兩個以爲,有人說,爲什麼要發送0xFF呢?其實發什麼都可以,這個不用管,-_-||

最後來看下spi_flash_send_byte這個函數

就前面貼的這個函數,裏面有註釋可以大概看下,

主要的就是這一句代碼:

    /* send byte through the SPI1 peripheral */
    spi_i2s_data_transmit(SPI1,byte);

和下面這句代碼,-_-||,淨說些廢話,總共4句,兩句重要

    /* return the byte read from the SPI bus */
    return(spi_i2s_data_receive(SPI1));

先看transmit,發送數據的這句:

這函數的實現:

/*!
    \brief      SPI transmit data
    \param[in]  spi_periph: SPIx(x=0,1,2)
    \param[in]  data: 16-bit data
    \param[out] none
    \retval     none
*/
void spi_i2s_data_transmit(uint32_t spi_periph, uint16_t data)
{
    SPI_DATA(spi_periph) = (uint32_t)data;
}

很簡單,就是在一個寄存器上寫一個數據,沒猜錯的話

再看另外一個接收函數實現:

/*!
    \brief      SPI receive data
    \param[in]  spi_periph: SPIx(x=0,1,2)
    \param[out] none
    \retval     16-bit data
*/
uint16_t spi_i2s_data_receive(uint32_t spi_periph)
{
    return ((uint16_t)SPI_DATA(spi_periph));
}

在同樣的寄存器上讀取數據,這個是什麼依據呢?

看下我們所用MCU的數據手冊,看到SPI這一部分,其中有關於SPI_DATA的介紹:

 看下下面文字說明:“硬件有兩個緩衝區:發送緩衝區和接收緩衝區,向SPI_DATA(就是這個寄存器)寫入數據會把數據存入到發送緩衝區,從SPI_DATA讀數據,將從接收緩衝區獲得數據。”

這也就是SDK中這麼實現這兩個函數的理論依據,當然這個是個16bit的寄存器,可以選擇8bit的或者16bit的都可以。

沒有明白的可以留言討論哇,^_^

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章