zynq裸机和linux spidev操作W25Q16

写使能

读取地址0x000051的值

发 03 00 00 51

收 0x74

BD

linux_spi_oled_system.tcl

注意事项:

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 复制代码
相关推荐
金士顿17 分钟前
ASP.NET Core BackgroundService 深度解析:设备后台任务到底应该怎么设计
linux·嵌入式·asp.net core·arm64
j7~28 分钟前
【Linux网络加餐课】(篇八)网络版计算器(终篇):守护进程、标准 IO 重定向与部署打包
linux·运维·网络编程·tcp·守护进程·io重定向·部署打包
Escalating_xu37 分钟前
【Linux 多线程】自旋锁:从 atomic_flag 原子操作到 pthread_spin_* 与高竞争优化
linux·系统架构
kkkkkkkkkk_Z1 小时前
学嵌入式和Linux应用编程|学习日记:深入理解TCP协议与网络编程实战
linux·笔记·学习
kaoa0001 小时前
Linux入门攻坚——90、Hadoop-2-MapReduce计算框架及Hadoop生态系统概览
linux·hadoop·mapreduce
The Chosen One9852 小时前
OS第二章随手记(2.1)
linux·运维·服务器·笔记
xx~t3 小时前
嵌入式——ARM——汇编1
linux·汇编·arm开发·嵌入式硬件·arm
ly76893 小时前
磁盘 I/O 延迟突增:用 iostat、blktrace 与火焰图定位到具体调用栈
java·linux·前端·数据库·iostat·磁盘 i/o·blktrace
程序员-Benothing3 小时前
Linux查找文件命令:find、locate、which、whereis实战
linux·运维·服务器
j7~3 小时前
【Linux网络加餐】(篇七)网络版计算器(中):协议落地、报文分隔与完整链路
linux·c++·网络编程·tcp·报文分割·网络计算器·协议落地