SPI Flash读取操作

4 篇文章 0 订阅

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

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

  • 6
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 12
    评论
### 回答1: SPI(Serial Peripheral Interface)是一种串行外设接口协议,常用于芯片之间进行通信。Verilog是一种硬件描述语言,用于设计和仿真数字电路。 要使用Verilog进行SPI读取Flash ID,首先需要编写一个SPI主设备的模块。该模块包含SPI总线控制器和Flash设备接口。在SPI总线控制器中,需要实现SPI协议的时序和通信规则,包括片选信号的生成、时钟信号的同步和数据的传输控制。Flash设备接口负责与Flash芯片进行通信,包括命令的发送和接收、数据的读取和写入。 通过SPI协议读取Flash ID的过程如下: 1. 选择Flash设备:在片选信号的激活期间,将SPI总线的片选信号置为低电平,使得Flash设备准备接受命令。 2. 发送Flash ID读取命令:向Flash设备发送读取Flash ID的命令,该命令指示Flash设备将Flash ID的数据传送到SPI总线上。 3. 接收Flash ID:等待Flash设备将Flash ID的数据传送到SPI总线,然后通过数据线接收该数据,并存储到适当的寄存器中。 4. 取消Flash设备选择:在片选信号的非激活期间,将SPI总线的片选信号恢复为高电平,结束Flash设备的选择。 通过以上步骤,SPI主设备可以成功读取Flash ID。 需要注意的是,上述过程只涵盖了读取Flash ID的基本步骤,实际的Verilog代码编写还需要考虑时序要求、数据校验等细节。根据具体的芯片型号和SPI协议,可能还需要进行一些参数配置和状态转换。因此,在实际应用中,还需要根据具体情况进行设计和实现。 ### 回答2: SPI(Serial Peripheral Interface)是一种通信协议,它可以用于与外部设备进行数据交换。在Verilog中,我们可以使用SPI读取Flash的ID。 首先,我们需要定义SPI总线的时钟信号、数据输入信号、数据输出信号和片选信号。接下来,我们需要编写SPI控制器的模块。 SPI控制器的模块设计如下: 1. 定义所有输入和输出端口。 2. 使用一个寄存器来保存要发送的数据。 3. 使用一个计数器来追踪要发送的位数。 4. 根据时钟信号的上升沿将数据位发送到MISO(Master In Slave Out)线上。 5. 在时钟信号的下降沿读取MOSI(Master Out Slave In)线上的数据位。 6. 当所有位都发送和接收完毕后,将数据保存到OUTPUT端口。 代码实现如下: ```verilog module spi_flashid ( input wire clk, // 时钟信号 input wire cs, // 片选信号 input wire cpol, // 时钟极性 input wire cpha, // 时钟相位 output wire [7:0] id // Flash ID ); reg [23:0] tx_data; reg [7:0] rx_data; reg [3:0] count; reg enable; always @(posedge clk) begin if (enable) begin if (cs == 1'b0) begin if (count < 24) begin if (count < 8) tx_data[count] <= spi_data[count]; else tx_data[count] <= 8'b0; rx_data[count] <= spi_data[count - 8]; count <= count + 1; end else enable <= 1'b0; end else enable <= 1'b0; end else begin if (cs == 1'b1 && cpol == 1'b1) enable <= 1'b1; else if (cs == 1'b0 && cpol == 1'b0) enable <= 1'b1; end end assign id = rx_data; endmodule ``` 在这个SPI控制器模块中,我们将Flash的ID保存在8位的输出端口id中。时钟信号被用来同步数据的传输。片选信号cs与时钟极性cpol和时钟相位cpha一起用来启用和禁用SPI通信。 ### 回答3: SPI (Serial Peripheral Interface)是一种常用于芯片间通信的协议。在Verilog中,我们可以使用SPI协议来读取Flash的ID。下面是一个简单的Verilog代码示例,实现了SPI读取Flash ID的功能。 ```verilog module spi_flash ( input wire clk, // 时钟信号 input wire reset, // 复位信号 output wire [23:0] flash_id // Flash ID信号,共24位 ); reg [7:0] cmd_channel; // 命令通道,8位 reg [23:0] data_channel; // 数据通道,24位 reg [3:0] state; // 状态机变量 // 初始化 initial begin cmd_channel = 8'h03; // 读取FlashID的命令 state = 4'h0; // 状态机初始状态 end always @(posedge clk or posedge reset) begin if (reset) begin // 复位时将状态机恢复初始状态 state <= 4'h0; data_channel <= 24'h0; end else begin case(state) 4'h0: begin // 发送命令 data_channel <= {7'h0, cmd_channel}; state <= 4'h1; end 4'h1: begin // 接收FlashID data_channel <= {7'h0, flash_id[23:8]}; state <= 4'h2; end 4'h2: begin // 完成读取 data_channel <= 24'h0; state <= 4'h0; end endcase end end assign flash_id = data_channel; // 输出Flash ID endmodule ``` 在上述代码中,我们定义了一个`spi_flash`模块,其中包含一个时钟信号`clk`、一个复位信号`reset`和一个输出的Flash ID信号`flash_id`。我们使用一个简单的4位状态机来管理SPI协议的读取过程。初始化时,状态机处于初始状态。在每个时钟周期的上升沿,状态机按照以下步骤进行工作: - 状态0:发送命令。将命令放入数据通道,并准备切换到下一个状态。 - 状态1:接收Flash ID。根据Flash ID的位宽,将数据通道中的数据放入对应的位置,并准备切换到下一个状态。 - 状态2:完成读取。将数据通道清零,并准备切换到初始状态。 最后,我们将数据通道的值赋给Flash ID信号输出。 这样,通过使用这段Verilog代码,我们可以实现在SPI协议下读取Flash的ID。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 12
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值