0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

【GD32H757Z海棠派开发板使用手册】第十一讲 SPI-SPI NOR FLASH读写实验

聚沃科技 2024-06-04 11:42 次阅读
加入交流群
微信小助手二维码

扫码添加小助手

加入工程师交流群

wKgZomYgeJOAUiXJAB6mQrDJGEg027.png

11.1实验内容

通过本实验主要学习以下内容:

  • SPI简介
  • GD32H7 SPI简介
  • SPI NOR FLASH——GD25Q128ESIGR简介
  • 使用GD32H7 SPI接口实现对GD25Q128ESIGR的读写操作

11.2实验原理

11.2.1SPI简介

SPI(Serial Peripheral interface),顾名思义是串行外设接口,和UART不同的是,SPI是同步通讯接口,所以带有时钟线,而UART是异步通讯接口,不需要时钟线。

SPI通常使用4根线,分别为SCK、MOSI、MISO、NSS(CS):

  • SCK:串列时脉,由主机发出
  • MOSI:主机输出从机输入信号(数据由主机发出)
  • MISO:主机输入从机输出信号(数据由从机发出)
  • NSS:片选信号,由主机发出,一般是低电位有效

SPI默认为全双工工作,在这种工作模式下,主机通过MOSI线发送数据的同时,也在MISO线上接受数据,简单来说就是主机和从机之间进行数据交换。

SPI是一个可以实现一主多从的通讯接口,从机的片选由主机NSS脚来控制:

wKgZomZGtu6APTaFAAG8RWzLK4U261.png

每个通讯时刻,只有一个从机NSS被主机选中,选中方式为主机拉低响应的NSS(CS)脚。

SPI的数据线只有一条(虽然有MOSI和MISO,但实际上每个CLK主机都只能发送和接受一个bit),所以称之为单线SPI。从SPI衍生出来的还有4线制SPI(QSPI)和8线制SPI(OSPI)以及其他多线制SPI,这个我们后面具体再聊。

11.2.2GD32H7 SPI简介

GD32H7 的SPI主要特性如下:

◼具有全双工、 半双工和单工模式的主从操作;

◼ 32位宽度,独立的发送和接收FIFO;

◼ 4位到32位数据帧格式;

◼低位在前或高位在前的数据位顺序;

◼软件和硬件NSS管理,MOSI与MISO引脚复用功能的交换;

◼硬件CRC计算、发送和校验;

◼发送和接收支持DMA模式;

◼支持SPI TI模式;

◼多主机多从机功能;

◼配置和设置保护;

◼可调的数据帧之间的最小延时和NSS与数据流之间的最小延时;

◼主机模式错误可触发中断,上溢、 下溢和CRC错误检测;

◼可调的主设备接收器采样时间;

◼可配置的FIFO阈值(数据打包) ;

◼在从机模式,下溢条件可配置;

◼支持SPI四线功能的主机模式(只有SPI3 / 4)。

以下为GD32H7 SPI的框图:

wKgaomZeitGAb4p8AADlvien-Qc228.png

如果小伙伴用过GD的其他系列MCU的SPI的话,就会发现H7和其他系列再SPI上的一个很大的不同,比如聚沃发布的紫藤派开发板用到的GD32F470的SPI是通过一个发送缓冲区和一个接受缓冲区这两个缓冲区来进行数据收发的,而H7产品则采用FIFO的模式进行数据收发管理,且发送FIFO(TXFIFO)对应TX位移寄存器,接受FIFO(RXFIFO)对应RX位移寄存器。当CPU或DMA将数据写到TXFIFO中(需要先判断TXFIFO是否有足够的空间能够写入数据),TXFIFO中的数据将会被转移到TX位移寄存器中,实现发送;反之,当RX位移寄存器收到数据,会将数据转移到RXFIFO中(需要保证RXFIFO有足够的空间存入数据),RXFIFO会通知CPU或者DMA取走数据。

GD32H7的SPI TxFIFO和RxFIFO的大小都为16*32位,FIFO的存在使得当CPU或者DMA来不及处理SPI数据时,能够防止发生数据过载或丢失。需要提醒的是,SPI正在发送的数据不一定是最新写到TxFIFO中的数据,因为最新数据在TxFIFO的末尾;CPU或者DMA接收到的数据不一定就是SPI最新的数据,因为SPI最新的数据在RxFIFO的末尾。

全双工模式下,当GD32H7 SPI主机TX位移寄存器被写入数据时,TX位移寄存器通过MOSI信号线将字节传送给从机,从机也将自己的位移寄存器内容通过MISO信号线返回给主机的RX位移寄存器。外设的写操作和读操作是同步完成的。如果只进行写操作,主机只需忽略接收到的字节;反之,若主机要读取从机的一个字节,就必须发送一个空字节来引发从机的传输。

SPI数据bit在CLK的有效边沿被锁存,而有效边沿是可以选择的,分别为:

  • 第一个上升沿
  • 第一个下降沿
  • 第二个下降沿
  • 第二个上升沿

通过SPI_CFG1寄存器中的CKPL位和CKPH位来设置有效锁存沿。其中CKPL位决定了空闲状态时SCK的电平,CKPH位决定了第一个或第二个时钟跳变沿为有效采样边沿。SPI_CFG1中的LF位可以配置数据顺序, 当LF=1时,SPI先发送LSB位,当LF=0时,则先发送MSB位。SPI_CFG0中的DZ[4:0]位域配置数据长度, 可以设置数据长度为4位至32位。下图为SPI的时序图:

wKgZomZGtwyAMa87AAEFndP9lRg166.png

4线SPI(QSPI)的时序图如下(CKPL=1, CKPH=1, LF=0) ,我们可以看到QSPI是通过MOSI、MISO、IO2、IO3来进行数据收或发,所以QSPI是工作在半双工模式:

wKgaomZGtxiAQZDAAACi0HWwbFI179.png

这里再介绍下SPI的NSS(片选)功能。NSS电平由主机来控制,主机将需要操作的从机NSS拉低,从而使该从机在总线上生效。

主机控制NSS的方式有两种——硬件方式和软件方式。主机硬件NSS模式下,NSS脚只能选择特定IO口(具体见datasheet中IO口功能表),当开始进行数据读写时,NSS自动拉低,这种方式的优点是主机NSS由硬件自动控制,缺点是只能控制一个从机;主机NSS软件模式下,NSS可以使用任意IO口,需要控制哪个从机,软件将对于IO拉低即可,这种方式的优点是可以实现一个主机多个从机的通讯,缺点是软件需要介入控制NSS脚。

从机获取NSS状态的方式也有两种——硬件方式和软件方式。从机硬件NSS模式下,SPI从NSS引脚获取NSS电平, 在软件NSS模式(NSSIM = 1) 下,SPI根据SNSSI位得到NSS电平。

SPI除了单线全双工模式外,还有很多其他方式,比如可以实现只用MOSI进行数据收和发的半双工通讯,这样就可以省下MISO用作他处了,具体可以参考GD32FH7系列官方用户手册。

这里着重介绍下H7 SPI的数据长度和SPI_CFG0中的BYTEN(bit23)和WORDEN(bit24)。BYTEN和WORDEN用来指示对FIFO的访问宽度:

wKgaomZeiw6ATZdnAAA_mySfJPU353.png

建议SPI的数据长度设置(SPI_CFG0中的DZ[4:0]位域)和FIFO访问宽度匹配,比如设置SPI数据长度为8,则需要配置FIFO访问宽度为字节访问,若配置FIFO访问宽度为半字访问,当发送一个8位数据时,总线上会产生16个clock,从而导致数据错位。

下面介绍下SPI的发送和接受流程:

发送流程

在完成初始化过程之后, SPI模块使能并保持在空闲状态。在主机模式下, 当软件写一个数据到TxFIFO时,发送过程开始。在从机模式下,当SCK引脚上的SCK信号开始翻转, 且NSS引脚电平有效, 发送过程开始。 所以, 在从机模式下,应用程序必须确保在数据发送开始前, 数据已经写入TxFIFO中。

当SPI开始发送一个数据帧时, 首先将这个数据帧从TxFIFO加载到移位寄存器中,然后开始发送加载的数据。

对SPI_TDATA的写访问由TP——TxFIFO数据包空间有效标志事件管理。

wKgZomZeix6AI-Y1AABvvFGtuoY065.png

当TP标志设置为1时,应用程序对SPI数据寄存器写入适当数量的数据,以传输数据包的内容。在上传新的完整包后,应用程序检查TP值,检查TxFIFO是否可以接收额外的数据包,如果TP = 1,则逐包上传,直到TP读取0。
在主机模式下, 若想要实现连续发送功能, 那么在当前数据帧发送完成前, 软件应该将下一个数据写入SPI_TDATA寄存器中。 只要TxFIFO中存在数据, 数据发送便一直继续, 直至TxFIFO变为空。

接收流程
在最后一个采样时钟边沿之后, 接收到的数据将从移位寄存器存入到RxFIFO, 且RP——RxFIFO数据包空间有效标志 位置1。

wKgaomZeiyuAHBE9AAB2Wee2k-c095.png

软件通过读SPI_RDATA寄存器获得接收的数据, 此操作会自动清除RP标志位(当RxFIFO数据量少于FIFOLVL标准)。 在全双工主机模式(MFD)中, 仅当TxFIFO非空时,硬件才接收下一个数据帧。
对SPI_RDATA的读访问由RP事件管理。 当RP标志设置为1时,应用程序读取SPI数据寄存器相当数量的数据,以下载单个数据包内容。下载完整数据包后,应用程序会检查RP值,查看RxFIFO中是否有其他数据包,如果有,则逐包下载,直到RP读到0。
接收数据时, 主机提供时钟信号, 当主机停止或挂起SPI时才会停止接收流程。主机通过将MSTART位置1来启动流程, 可通过向SPI_CTL0寄存器的MSPDR为写1来请求挂起,或者向MASP位写1来设置上溢挂起。

11.2.3SPI FLASH——GD25Q128ESIGR简介

GD25Q128ESIGR是一款容量为128Mbit(即16Mbyte)的SPI接口的NOR FLASH,其支持SPI和QSPI模式,芯片示意图如下:

wKgaomZGtzGAeKaSAABOyFlqAis956.png

GD25Q128ESIGR管脚定义如下:

wKgZomZGtz2AHBTpAADoXtfKGP0321.png

GD25Q128ESIGR内部flash结构如下:

wKgaomZei1CANfeEAACqzNN5-BY495.png

下面介绍GD25Q128ESIGR的一些功能码。

Write Enable (WREN) (06H) :接受到该命令后,GD25Q128ESIGR做好接受数据并进行存储的准备,时序如下:

wKgaomZGt1uADgO6AABE5nXZFUw843.png

Read Status Register (RDSR) (05H or 35H or 15H) :读GD25Q128ESIGR的状态,时序如下:

wKgaomZGt2iAZ27JAADcULbDKgM319.png

Read Data Bytes (READ) (03H) :接受到该命令后,GD25Q128ESIGR将数据准备好供主机读走,时序如下:

wKgZomZGt3OAfcktAAC-cw2PFnk420.png

Dual Output Fast Read (3BH) :使GD25Q128ESIGR切换到QSPI模式,时序如下:

wKgZomZGt4WAXYejAAD4-W0AVwI742.png

Quad Output Fast Read (6BH) :QSPI读命令,时序如下:

wKgaomZGt5CARXRwAAFdcIES_y0316.png

Quad Page Program (32H) :QSPI写命令,时序如下:

wKgZomZGt52AfzjcAAD4QGFpgL4956.png

Sector Erase (SE) (20H) :Sector擦除命令,时序如下:

wKgaomZGt8SAJ6rzAABsjZB4j98071.png

GD25Q128ESIGR就介绍到这里,读者可以在兆易创新官网下载该NOR FLASH的datasheet以获取更多信息。

11.3硬件设计

海棠派开发板SPI——NOR FLASH的硬件设计如下:

wKgaomZGt9CAQQMZAAC_TVQigbY825.png

从图中可以看出,本实验使用的是普通单线SPI,GD25Q128ESIGR的片选由GD32H757的PF6控制,并采用主机NSS软件模式,GD25Q128ESIGR的SO、SI和SCLK分别和GD32H757的PF8(SPI4_MISO)、PB9(SPI4_MOSI)以及PF7(SPI4_CLK)相连。

11.4代码解析

11.4.1SPI初始化函数

在driver_spi.c文件中定义了SPI初始化函数driver_spi_init:

C void driver_spi_init(typdef_spi_struct *spix) { spi_parameter_struct spi_init_struct; rcu_periph_clock_enable(spix->rcu_spi_x); /* spi configure */ spi_i2s_deinit(spix->spi_x); spix->spi_sck_gpio->speed=GPIO_OSPEED_60MHZ; spix->spi_mosi_gpio->speed=GPIO_OSPEED_60MHZ; spix->spi_miso_gpio->speed=GPIO_OSPEED_60MHZ; driver_gpio_general_init(spix->spi_cs_gpio); driver_gpio_general_init(spix->spi_sck_gpio); driver_gpio_general_init(spix->spi_mosi_gpio); driver_gpio_general_init(spix->spi_miso_gpio); if(spix->spi_mode==MODE_DMA) { if(spix->spi_rx_dma!=NULL) { if(spix->frame_size==SPI_DATASIZE_8BIT){ driver_dma_com_init(spix->spi_rx_dma,(uint32_t)&SPI_RDATA(spix->spi_x),NULL,DMA_Width_8BIT,DMA_PERIPH_TO_MEMORY); } else if(spix->frame_size==SPI_DATASIZE_16BIT){ driver_dma_com_init(spix->spi_rx_dma,(uint32_t)&SPI_RDATA(spix->spi_x),NULL,DMA_Width_16BIT,DMA_PERIPH_TO_MEMORY); } else if(spix->frame_size==SPI_DATASIZE_32BIT){ driver_dma_com_init(spix->spi_rx_dma,(uint32_t)&SPI_RDATA(spix->spi_x),NULL,DMA_Width_32BIT,DMA_PERIPH_TO_MEMORY); } } if(spix->spi_tx_dma!=NULL) { if(spix->frame_size==SPI_DATASIZE_8BIT){ driver_dma_com_init(spix->spi_tx_dma,(uint32_t)&SPI_TDATA(spix->spi_x),NULL,DMA_Width_8BIT,DMA_MEMORY_TO_PERIPH); } else if(spix->frame_size==SPI_DATASIZE_16BIT){ driver_dma_com_init(spix->spi_tx_dma,(uint32_t)&SPI_TDATA(spix->spi_x),NULL,DMA_Width_16BIT,DMA_MEMORY_TO_PERIPH); } else if(spix->frame_size==SPI_DATASIZE_32BIT){ driver_dma_com_init(spix->spi_tx_dma,(uint32_t)&SPI_TDATA(spix->spi_x),NULL,DMA_Width_32BIT,DMA_MEMORY_TO_PERIPH); } } } if(spix->spi_cs_gpio!=NULL) { driver_gpio_pin_set(spix->spi_cs_gpio); } spi_struct_para_init(&spi_init_struct); /* SPI3 parameter config */ spi_init_struct.trans_mode = SPI_TRANSMODE_FULLDUPLEX; spi_init_struct.device_mode = spix->device_mode; spi_init_struct.data_size = spix->frame_size; spi_init_struct.clock_polarity_phase = spix->clock_polarity_phase; if(spix->device_mode==SPI_MASTER){ spi_init_struct.nss = SPI_NSS_SOFT; }else{ spi_init_struct.nss = SPI_NSS_HARD; } spi_init_struct.prescale = spix->prescale; spi_init_struct.endian = spix->endian; spi_init(spix->spi_x, &spi_init_struct); /* enable SPI byte access */ spi_byte_access_enable(spix->spi_x); /* configure SPI current data number */ spi_current_data_num_config(spix->spi_x, 0); spi_nss_output_enable(spix->spi_x); /* enable SPI3 */ spi_enable(spix->spi_x); /* start SPI master transfer */ spi_master_transfer_start(spix->spi_x, SPI_TRANS_START); }

11.4.2SPI轮训接受一个数函数

在driver_spi.c文件中定义了使用轮训方式发送接受一个字节数据函数driver_spi_master_transmit_receive_byte:

C uint8_t driver_spi_master_transmit_receive_byte(typdef_spi_struct *spix,uint8_t byte) { spi_i2s_flag_clear(spix->spi_x, SPI_STATC_TXURERRC|SPI_STATC_RXORERRC|SPI_STATC_CRCERRC|SPI_STATC_FERRC|SPI_STATC_CONFERRC); driver_spi_flag_wait_timeout(spix,SPI_FLAG_TP,SET); spi_i2s_data_transmit(spix->spi_x,byte); driver_spi_flag_wait_timeout(spix,SPI_FLAG_RP,SET); return spi_i2s_data_receive(spix->spi_x); }

上面函数中有带超时功能的等待SPI状态的函数driver_spi_flag_wait_timeout,该函数定义在driver_spi.c:

C Drv_Err driver_spi_flag_wait_timeout(typdef_spi_struct *spix, uint32_t flag ,FlagStatus wait_state) { __IO uint64_t timeout = driver_tick; while(wait_state!=spi_i2s_flag_get(spix->spi_x, flag)){ if((timeout+SPI_TIMEOUT_MS) <= driver_tick) { return DRV_ERROR; } } return DRV_SUCCESS; }

11.4.3SPI NOR FLASH 接口bsp层函数

操作NOR FLASH的函数都定义在bsp层文件bsp_spi_nor.c中,这个文件中定义的函数都是针对NOR FLASH特性来实现的,我们选取几个函数进行介绍。

1、NOR FLASH按sector擦除函数bsp_spi_nor_sector_erase,该函数流程是:使能NOR FLASH的写功能->拉低片选->向NOR FLASH发送sector擦除指令SE(0x20)->从低地址到高地址发送需要擦除的地址->拉高片选->等待NOR FALSH内部操作完成(循环去读NOR FLASH状态,直到读出编程状态为0)

C void bsp_spi_nor_sector_erase(uint32_t sector_addr) { /* send write enable instruction */ bsp_spi_nor_write_enable(); /* sector erase */ /* select the flash: chip select low */ bsp_spi_nor_cs_low(); /* send sector erase instruction */ driver_spi_master_transmit_receive_byte(&BOARD_SPI,SE); /* send sector_addr high nibble address byte */ driver_spi_master_transmit_receive_byte(&BOARD_SPI,(sector_addr & 0xFF0000) >> 16); /* send sector_addr medium nibble address byte */ driver_spi_master_transmit_receive_byte(&BOARD_SPI,(sector_addr & 0xFF00) >> 8); /* send sector_addr low nibble address byte */ driver_spi_master_transmit_receive_byte(&BOARD_SPI,sector_addr & 0xFF); /* deselect the flash: chip select high */ bsp_spi_nor_cs_high(); /* wait the end of flash writing */ bsp_spi_nor_wait_for_write_end(); }

2、按page写数据函数bsp_spi_nor_page_write,该函数实现在page范围内写数据,该函数流程是:使能NOR FLASH的写功能->拉低片选->向NOR FLASH发送写指令WRITE(0x02)->从低地址到高地址发送要写的地址(每次进行写数据时,只需要给初始地址即可,写完一个数据后NOR FLASH内部会自动把地址+1)->写数据->拉高片选->等待NOR FALSH内部操作完成(循环去读NOR FLASH状态,直到读出编程状态为0)

C void bsp_spi_nor_page_write(uint8_t* pbuffer, uint32_t write_addr, uint16_t num_byte_to_write) { /* enable the write access to the flash */ bsp_spi_nor_write_enable(); /* select the flash: chip select low */ bsp_spi_nor_cs_low(); /* send "write to memory" instruction */ driver_spi_master_transmit_receive_byte(&BOARD_SPI,WRITE); /* send write_addr high nibble address byte to write to */ driver_spi_master_transmit_receive_byte(&BOARD_SPI,(write_addr & 0xFF0000) >> 16); /* send write_addr medium nibble address byte to write to */ driver_spi_master_transmit_receive_byte(&BOARD_SPI,(write_addr & 0xFF00) >> 8); /* send write_addr low nibble address byte to write to */ driver_spi_master_transmit_receive_byte(&BOARD_SPI,write_addr & 0xFF); /* while there is data to be written on the flash */ while(num_byte_to_write--){ /* send the current byte */ driver_spi_master_transmit_receive_byte(&BOARD_SPI,*pbuffer); /* point on the next byte to be written */ pbuffer++; } /* deselect the flash: chip select high */ bsp_spi_nor_cs_high(); /* wait the end of flash writing */ bsp_spi_nor_wait_for_write_end(); }

3、按buffer写数据函数bsp_spi_nor_buffer_write,该函数实现任意长度数据写入,使用page写函数搭配算法,可以跨page进行写数据:

C void bsp_spi_nor_buffer_write(uint8_t* pbuffer, uint32_t write_addr, uint16_t num_byte_to_write) { uint8_t num_of_page = 0, num_of_single = 0, addr = 0, count = 0, temp = 0; addr = write_addr % SPI_FLASH_PAGE_SIZE; count = SPI_FLASH_PAGE_SIZE - addr; num_of_page = num_byte_to_write / SPI_FLASH_PAGE_SIZE; num_of_single = num_byte_to_write % SPI_FLASH_PAGE_SIZE; /* write_addr is SPI_FLASH_PAGE_SIZE aligned */ if(0 == addr){ /* num_byte_to_write < SPI_FLASH_PAGE_SIZE */ if(0 == num_of_page) bsp_spi_nor_page_write(pbuffer,write_addr,num_byte_to_write); /* num_byte_to_write > SPI_FLASH_PAGE_SIZE */ else{ while(num_of_page--){ bsp_spi_nor_page_write(pbuffer,write_addr,SPI_FLASH_PAGE_SIZE); write_addr += SPI_FLASH_PAGE_SIZE; pbuffer += SPI_FLASH_PAGE_SIZE; } bsp_spi_nor_page_write(pbuffer,write_addr,num_of_single); } }else{ /* write_addr is not SPI_FLASH_PAGE_SIZE aligned */ if(0 == num_of_page){ /* (num_byte_to_write + write_addr) > SPI_FLASH_PAGE_SIZE */ if(num_of_single > count){ temp = num_of_single - count; bsp_spi_nor_page_write(pbuffer,write_addr,count); write_addr += count; pbuffer += count; bsp_spi_nor_page_write(pbuffer,write_addr,temp); }else bsp_spi_nor_page_write(pbuffer,write_addr,num_byte_to_write); }else{ /* num_byte_to_write > SPI_FLASH_PAGE_SIZE */ num_byte_to_write -= count; num_of_page = num_byte_to_write / SPI_FLASH_PAGE_SIZE; num_of_single = num_byte_to_write % SPI_FLASH_PAGE_SIZE; bsp_spi_nor_page_write(pbuffer,write_addr, count); write_addr += count; pbuffer += count; while(num_of_page--){ bsp_spi_nor_page_write(pbuffer,write_addr,SPI_FLASH_PAGE_SIZE); write_addr += SPI_FLASH_PAGE_SIZE; pbuffer += SPI_FLASH_PAGE_SIZE; } if(0 != num_of_single) bsp_spi_nor_page_write(pbuffer,write_addr,num_of_single); } } }

4、按buffer读数据函数bsp_spi_nor_buffer_read,该函数实现任意地址读数据,该函数流程是:拉低片选->向NOR FLASH发送读指令READ(0x03)->从低地址到高地址发送要读的地址(每次进行读数据时,只需要给初始地址即可,读完一个数据后NOR FLASH内部会自动把地址+1)->读数据->拉高片选:

C void bsp_spi_nor_buffer_read(uint8_t* pbuffer, uint32_t read_addr, uint16_t num_byte_to_read) { /* select the flash: chip slect low */ bsp_spi_nor_cs_low(); /* send "read from memory " instruction */ driver_spi_master_transmit_receive_byte(&BOARD_SPI,READ); /* send read_addr high nibble address byte to read from */ driver_spi_master_transmit_receive_byte(&BOARD_SPI,(read_addr & 0xFF0000) >> 16); /* send read_addr medium nibble address byte to read from */ driver_spi_master_transmit_receive_byte(&BOARD_SPI,(read_addr& 0xFF00) >> 8); /* send read_addr low nibble address byte to read from */ driver_spi_master_transmit_receive_byte(&BOARD_SPI,read_addr & 0xFF); /* while there is data to be read */ while(num_byte_to_read--){ /* read a byte from the flash */ *pbuffer = driver_spi_master_transmit_receive_byte(&BOARD_SPI,NOR_DUMMY_BYTE); /* point to the next location where the byte read will be saved */ pbuffer++; } /* deselect the flash: chip select high */ bsp_spi_nor_cs_high(); }

11.4.4main函数实现

以下为main函数代码:

C int main(void) { //延时、共用驱动部分初始化 driver_init(); //初始化LED组和默认状态 bsp_led_group_init(); bsp_led_on(&LED1); bsp_led_off(&LED2); //初始化UART打印 bsp_uart_init(&BOARD_UART); //初始化SPI bsp_spi_init(&BOARD_SPI); //初始化SPI NOR bsp_spi_nor_init(); printf_log("\n\rSPI Flash:GD25Q configured...\n\r"); //读取flash id flash_id = bsp_spi_nor_read_id(); printf_log("\n\rThe Flash_ID:0x%X\n\r",flash_id); //比对flash id是否一致 if(SFLASH_4B_ID == flash_id || SFLASH_16B_ID == flash_id) { printf_log("\n\rWrite to tx_buffer:\n\r"); //准备数据 for(uint16_t i = 0; i < BUFFER_SIZE; i++){ tx_buffer[i] = i; printf_log("0x%02X ",tx_buffer[i]); if(15 == i%16){ printf_log("\n\r"); } } printf_log("\n\r"); printf_log("\n\rRead from rx_buffer:\n\r"); //擦除要写入的sector bsp_spi_nor_sector_erase(FLASH_WRITE_ADDRESS); //写入数据 bsp_spi_nor_buffer_write(tx_buffer,FLASH_WRITE_ADDRESS,TX_BUFFER_SIZE); //延时等待写完成 delay_ms(10); //回读写入数据 bsp_spi_nor_buffer_read(rx_buffer,FLASH_READ_ADDRESS,RX_BUFFER_SIZE); /* printf_log rx_buffer value */ for(uint16_t i = 0; i < BUFFER_SIZE; i++){ printf_log("0x%02X ", rx_buffer[i]); if(15 == i%16){ printf_log("\n\r"); } } printf_log("\n\r"); //比较回读和写入数据 if(ERROR == memory_compare(tx_buffer,rx_buffer,BUFFER_SIZE)){ printf_log("Err:Data Read and Write aren't Matching.\n\r"); //写入错误 /* turn off all leds */ bsp_led_on(&LED2); /* turn off all leds */ bsp_led_on(&LED1); while(1); }else{ printf_log("\n\rSPI-GD25Q16 Test Passed!\n\r"); } }else{ //ID读取错误 /* spi flash read id fail */ printf_log("\n\rSPI Flash: Read ID Fail!\n\r"); /* turn off all leds */ bsp_led_on(&LED2); /* turn off all leds */ bsp_led_on(&LED1); while(1); } while(1){ /* turn off all leds */ bsp_led_toggle(&LED2); /* turn off all leds */ bsp_led_toggle(&LED1); delay_ms(200); } }

main函数中实现了向特定NOR FLASH地址写数据,并回读出来,并将写入的数据和回读出来的数据进行对比,看是否写入成功。

11.5实验结果

将本实验例程烧录到GD32H757紫海棠派开发板中,将会显示对外部SPI flash写入以及读取的数据以及最终的校验结果,如果写入读取校验正确,将会显示SPI-GD25QXX Test Passed,LED1和LED2将会交替闪烁。

教程GD32 MCU方案商聚沃科技原创发布,了解更多GD32 MCU教程,关注聚沃科技官网

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 单片机
    +关注

    关注

    6078

    文章

    45612

    浏览量

    674717
  • mcu
    mcu
    +关注

    关注

    147

    文章

    19211

    浏览量

    405047
  • 开发板
    +关注

    关注

    26

    文章

    6476

    浏览量

    121275
  • NOR flash
    +关注

    关注

    2

    文章

    109

    浏览量

    23957
  • GD32
    +关注

    关注

    7

    文章

    435

    浏览量

    27676
收藏 人收藏
加入交流群
微信小助手二维码

扫码添加小助手

加入工程师交流群

    评论

    相关推荐
    热点推荐

    如何使用 SPI NOR 闪存启动电路

    尝试从 SPI NOR 闪存启动自定义 T2080 我能够使用 CW IDE 中的闪存编程器将自定义 RCW bin 文件加载到串行 NOR 闪存中,但在使用
    发表于 04-21 09:07

    使用JL杰理AC696N开发板实现PC模式下读取外挂SPI Flash虚拟为U盘的具体案例操作说明_V1.0

    开发板上的三个SPI引脚( SPIDI 、 SPICLK 、 SPICS )通过跳线帽或杜邦线连接到外挂Flash芯片的对应引脚。 Flash型号 :支持市面上主流的
    发表于 04-18 00:04

    通用SPI接口的NOR Flash存储器特性

    在嵌入式系统设计中,NOR Flash存储器凭借其快速读取和可靠的数据存储能力,成为代码存储与直接执行(XIP)的核心器件。英尚代理推出的一款适合工业级应用的SPI接口NOR
    的头像 发表于 04-07 15:15 240次阅读

    JL杰理AC696N系列芯片SPI接口实战:驱动Flash、LCD与RGB灯

    文章阐述了,杰理AC696N系列芯片功能:驱动Flash、LCD、RGB等。AC696N开发板支持SPI主机模式,可轻松驱动这种类型外设。
    的头像 发表于 04-01 17:49 1299次阅读
    JL杰理AC696N系列芯片<b class='flag-5'>SPI</b>接口实战:驱动<b class='flag-5'>Flash</b>、LCD与RGB灯

    NOR Flash 到 NAND Flash 和SD NAND,从底层结构到应用差异

    nor flash,nor nand,sd nand,spi nor,nand flash
    的头像 发表于 03-05 18:24 518次阅读
    从<b class='flag-5'>NOR</b> <b class='flag-5'>Flash</b> 到 NAND <b class='flag-5'>Flash</b> 和SD NAND,从底层结构到应用差异

    深入解析Rockchip SFC驱动:SPI Flash传输流程与问题排查指南

    Controller)驱动 (spi-rockchip-sfc.c),用于高效管理SPI Flash读写传输。本文基于Linux内核驱动代码与Rockchip官方
    的头像 发表于 02-04 07:13 918次阅读
    深入解析Rockchip SFC驱动:<b class='flag-5'>SPI</b> <b class='flag-5'>Flash</b>传输流程与问题排查指南

    SPI NOR FlashSPI NAND Flash存储芯片的区别

    SPI NOR FlashSPI NAND Flash并非相互替代,而是互补关系。SPI
    的头像 发表于 01-29 16:58 1003次阅读
    <b class='flag-5'>SPI</b> <b class='flag-5'>NOR</b> <b class='flag-5'>Flash</b>和<b class='flag-5'>SPI</b> NAND <b class='flag-5'>Flash</b>存储芯片的区别

    ESP32-C5迷你开发板上手指南!轻松驱动SPI屏幕!

    本文将带你一步步完成WT9932C5-TINY开发板的烧录与SPI屏幕驱动,从硬件连接到软件烧录,直至最终的效果演示。无需复杂的前期准备,跟着教程操作即可快速上手。硬件连接1准备材料1
    的头像 发表于 01-19 18:04 1276次阅读
    ESP32-C5迷你<b class='flag-5'>开发板</b>上手指南!轻松驱动<b class='flag-5'>SPI</b>屏幕!

    国产SPI NOR Flash接口闪存介绍

    在当今各类电子设备对存储性能要求日益提升的背景下,SPI NOR Flash凭借其高速读取、低功耗及灵活接口等优势,成为嵌入式系统代码存储的关键元件。GT25Q系列SPI
    的头像 发表于 12-26 11:51 745次阅读

    高性能SPI NOR FLASH芯片ZB25VQ系列推荐

    在嵌入式系统、物联网设备及各类存储应用中,SPI NOR FLASH芯片因其接口简单、功耗低、读写速度快等特点,成为代码存储与数据缓存的常见选择。S
    的头像 发表于 12-01 14:52 974次阅读

    SPI NOR FLASH是什么,与SPI NAND Flash的区别

    SPI NOR FLASH是什么?   SPI NOR FLASH是一种非易失性存储器,它通过串
    的头像 发表于 08-21 09:26 1975次阅读

    【BPI-CanMV-K230D-Zero开发板体验】+闪存读写程序的分析

    0xC8,其设备ID则是0x18。 图3 读取ID 至于数据的读写,则需要依据该程序框架自行补充完成。 仍以读取芯片ID为例,通常的C语言程序为: u32 SPI_FLASH_ReadID(void
    发表于 06-30 16:28

    【Banana Pi BPI-RV2开发板试用体验】开箱上电

    BPI-RV2 RISC-V开源路由器之开箱上电 背景 现在刚好正在研究短距及网络相关的东东。 最近刚好有幸得到了一块香蕉 BPI-RV2 RISC-V 开源路由器开发板。感谢电子发烧友
    发表于 06-26 19:51

    【Banana Pi BPI-RV2开发板试用体验】开发板介绍视频

    一. 开发板介绍香蕉 BPI-RV2 RISC-V 路由器开发板采用矽昌通信 SF21H8898 芯片方案矽昌 SF21H8898 四核6
    发表于 06-24 23:51

    第十七章 SPI——读写串行FLASH

    本章介绍SPI协议,其为高速全双工通信总线,含物理层、协议层内容,还讲解W55MH32的SPI特性、初始化及DMA相关配置。
    的头像 发表于 06-19 17:06 1507次阅读
    <b class='flag-5'>第十</b>七章 <b class='flag-5'>SPI</b>——<b class='flag-5'>读写</b>串行<b class='flag-5'>FLASH</b>