今天看到有人在问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