写使能
读取地址0x000051的值发 03 00 00 51
收 0x74

BD

注意事项:
Vivado 自动生成了两个未使用的片选信号。请在 system_wrapper.v 文件中删除与这两个片选相关的代码,以避免综合报错。
未使用的片选信号包括:
bash
output SPI_0_0_ss1_o;
output SPI_0_0_ss2_o;
PIN.xdc
bash
## SPI SCLK
set_property PACKAGE_PIN M15 [get_ports SPI_0_0_sck_io]
set_property IOSTANDARD LVCMOS33 [get_ports SPI_0_0_sck_io]
## SPI MOSI
set_property PACKAGE_PIN L16 [get_ports SPI_0_0_io0_io]
set_property IOSTANDARD LVCMOS33 [get_ports SPI_0_0_io0_io]
## SPI MISO
set_property PACKAGE_PIN K14 [get_ports SPI_0_0_io1_io]
set_property IOSTANDARD LVCMOS33 [get_ports SPI_0_0_io1_io]
## SPI CS
set_property PACKAGE_PIN J14 [get_ports SPI_0_0_ss_io]
set_property IOSTANDARD LVCMOS33 [get_ports SPI_0_0_ss_io]
## DC
set_property PACKAGE_PIN N20 [get_ports {GPIO_EMIO_tri_io[0]}]
set_property IOSTANDARD LVCMOS33 [get_ports {GPIO_EMIO_tri_io[0]}]
## RES
set_property PACKAGE_PIN U19 [get_ports {GPIO_EMIO_tri_io[1]}]
set_property IOSTANDARD LVCMOS33 [get_ports {GPIO_EMIO_tri_io[1]}]
代码
裸机,驱动和应用是三个独立运行的例子
裸机
main.c
c
#include "xparameters.h"
#include "xspips.h"
#include "xil_printf.h"
#include "sleep.h"
#define SPI_DEVICE_ID XPAR_XSPIPS_0_DEVICE_ID
#define CS_ASSERT 0x00 // 片选引脚激活,低电平使能SPI设备通信
#define CS_RELEASE 0x0F // 片选引脚释放,高电平禁用SPI设备通信
#define W25Q_WREN 0x06 // 写使能命令(Write Enable),允许写操作,必须在进行写操作之前先发送此命令
#define W25Q_RDSR 0x05 // 读取状态寄存器命令(Read Status Register),用来读取闪存的状态寄存器
#define W25Q_PP 0x02 // 页编程命令(Page Program),用于将数据写入闪存的指定页
#define W25Q_READ 0x03 // 读取数据命令(Read Data),用于从闪存中读取数据
#define W25Q_SE 0x20 // 扇区擦除命令(Sector Erase),用于擦除闪存中的指定扇区
#define SR_WIP 0x01
#define TEST_ADDR 0x000051
#define TEST_VALUE 0x74
static XSpiPs SpiInstance;
/*================ CS =================*/
static inline void cs_low() {
XSpiPs_SetSlaveSelect(&SpiInstance, CS_ASSERT);
usleep(2);
}
static inline void cs_high() {
XSpiPs_SetSlaveSelect(&SpiInstance, CS_RELEASE);
usleep(5);
}
/*================ SR =================*/
static u8 read_sr(void)
{
u8 tx[2] = {W25Q_RDSR, 0};
u8 rx[2] = {0};
cs_low();
XSpiPs_PolledTransfer(&SpiInstance, tx, rx, 2);
cs_high();
return rx[1];
}
static void wait_busy(void)
{
while (read_sr() & SR_WIP) {
usleep(100);
}
}
/*================ WREN =================*/
static void write_enable(void)
{
u8 cmd = W25Q_WREN;
cs_low();
XSpiPs_PolledTransfer(&SpiInstance, &cmd, NULL, 1);
cs_high();
}
/*================ 擦除 =================*/
static void erase_sector(u32 addr)
{
u8 tx[4];
write_enable();
tx[0] = W25Q_SE;
tx[1] = (addr >> 16);
tx[2] = (addr >> 8);
tx[3] = addr;
cs_low();
XSpiPs_PolledTransfer(&SpiInstance, tx, NULL, 4);
cs_high();
wait_busy();
}
/*================ 写 =================*/
static void write_byte(u32 addr, u8 data)
{
u8 tx[5];
write_enable();
tx[0] = W25Q_PP;
tx[1] = (addr >> 16);
tx[2] = (addr >> 8);
tx[3] = addr;
tx[4] = data;
cs_low();
XSpiPs_PolledTransfer(&SpiInstance, tx, NULL, 5);
cs_high();
wait_busy();
}
/*================ 读 =================*/
static u8 read_byte(u32 addr)
{
u8 tx[5];
u8 rx[5] = {0};
tx[0] = W25Q_READ;
tx[1] = (addr >> 16);
tx[2] = (addr >> 8);
tx[3] = addr;
tx[4] = 0x00;
cs_low();
XSpiPs_PolledTransfer(&SpiInstance, tx, rx, 5);
cs_high();
return rx[4];
}
/*================ main =================*/
int main(void)
{
XSpiPs_Config *cfg;
u8 wr = TEST_VALUE;
u8 rd;
xil_printf("\r\n=== SPI WRITE+READ LOOP ===\r\n");
cfg = XSpiPs_LookupConfig(SPI_DEVICE_ID);
if (!cfg) return -1;
if (XSpiPs_CfgInitialize(&SpiInstance, cfg, cfg->BaseAddress) != XST_SUCCESS)
return -1;
XSpiPs_SetOptions(&SpiInstance,
XSPIPS_MASTER_OPTION |
XSPIPS_FORCE_SSELECT_OPTION);
XSpiPs_SetClkPrescaler(&SpiInstance, XSPIPS_CLK_PRESCALE_64);
cs_high();
usleep(1000);
while (1)
{
xil_printf("\r\n--- cycle ---\r\n");
/* 1. 擦 */
xil_printf("erase...\r\n");
erase_sector(TEST_ADDR);
/* 2. 写 */
xil_printf("write...\r\n");
write_byte(TEST_ADDR, wr);
while(1){
/* 3. 读 */
rd = read_byte(TEST_ADDR);
xil_printf("WRITE=0x%02X READ=0x%02X %s\r\n",
wr, rd, (wr==rd)?"OK":"FAIL");
sleep(1);
}
}
}
驱动
myw25q16.c
c
应用
main.c
c
