SPI Flash读取操作

Agnes ·
更新时间:2024-11-13
· 654 次阅读

今天看到有人在问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的都可以。

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


作者:yangkunhenry



spi flash

需要 登录 后方可回复, 如果你还没有账号请 注册新账号
相关文章