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的都可以。

没有明白的可以留言讨论哇,^_^

猜你喜欢

转载自blog.csdn.net/yangkunhenry/article/details/104703339
SPI