W25Q64调试

article/2025/8/22 1:48:18

简介

W25Q系列的器件在灵活性和性能方面远远超过普通的串行闪存器件。W25Q64将8M字节的容量分为128个块,每个块大小为64K字节,每个块又分为16个扇区,每个扇区4K个字节。

引脚介绍

在这里插入图片描述

串行数据输入、输出和 IOs(DI、DO 和 IO0、IO1、IO2、IO3)

W25Q64、W25Q16 和 W25Q32 支持标准 SPI、双倍 SPI 和四倍 SPI。标准的 SPI 传输用单向的 DI(输入)引脚连续的写命令、地址或者数据在串行时钟(CLK)的上升沿时写入到芯片内。

写保护(/WP)

写保护引脚(/WP)用来保护状态寄存器。和状态寄存器的块保护位(SEC、TB、BP2、BP1 和BP0)和状态寄存器保护位(SRP)对存储器进行一部分或者全部的硬件保护。/WP 引脚低电平有效。当状态寄存器 2 的 QE 位被置位了,/WP 引脚(硬件写保护)的功能不可用。

保持端(/HOLD)

当/HOLD 引脚是有效时,允许芯片暂停工作。在/CS 为低电平时,当/HOLD 变为低电平,DO 引脚将变为高阻态,在 DI 和 CLK 引脚上的信号将无效。当/HOLD 变为高电平,芯片恢复工作。/HOLD 功能用在当有多个设备共享同一 SPI 总线时。/HOLD 引脚低电平有效。当状态寄存器 2 的 QE 位被置位了,/ HOLD 引脚的功能不可用。

串行时钟(CLK)

串行时钟输入引脚为串行输入和输出操作提供时序。(见 SPI 操作)。
设备数据传输是从高位开始,数据传输的格式为 8bit,数据采样从第二个时间边沿开始,空闲状态时,时钟线 clk 为高电平。

W25Q64操作原理

通过SPI接口,用标准的SPI协议发送相应指令给flash,然后flash根据命令进行各种相关操作。
① 写使能:06H
② 读状态寄存器指令:05H
③ 写状态寄存器指令:01H
④ 读数据:03H
⑤ 页写:02H
⑥ 扇区擦除指令:20H
⑦ 块擦除指令:D8H
⑧ 芯片擦除指令:07H
⑨ 掉电指令:B9H
⑩ 读ID指令:90H

在这里插入图片描述
在这里插入图片描述

极性CPOL和相位CPHA

(1) CKPOL (Clock Polarity) = CPOL = POL = Polarity = (时钟)极性
(2) CKPHA (Clock Phase) = CPHA = PHA = Phase = (时钟)相位

CPOL和CPHA,分别都可以是0或时1,对应的四种组合就是:

在这里插入图片描述
SPI的CPOL,表示当SCLK空闲idle的时候,其电平的值是低电平0还是高电平1:CPOL=0,时钟空闲idle时候的电平是低电平,所以当SCLK有效的时候,就是高电平,就是所谓的active-high。
CPOL=1,时钟空闲idle时候的电平是高电平,所以当SCLK有效的时候,就是低电平,就是所谓的active-low。
在这里插入图片描述

CPHA=0,表示第一个边沿:
对于CPOL=0,idle时候的是低电平,第一个边沿就是从低变到高,所以是上升沿;
对于CPOL=1,idle时候的是高电平,第一个边沿就是从高变到低,所以是下降沿;

CPHA=1,表示第二个边沿:
对于CPOL=0,idle时候的是低电平,第二个边沿就是从高变到低,所以是下降沿;
对于CPOL=1,idle时候的是高电平,第一个边沿就是从低变到高,所以是上升沿;
在这里插入图片描述
在这里插入图片描述

示例代码

DTSI

w25q64: w25q64@00 {status = "okay";compatible = "rockchip,w25q64";reg = <0x00>;spi-max-frequency = <24000000>;wp-gpio = <&gpio0 12 GPIO_ACTIVE_HIGH>; /*GPIO0_B4*/
//     spi-cs-high;spi-cpha;       /*SPI mode: CPHA = 1*/spi-cpol;       /*SPI mode: CPOL = 1*/
};

源码

w25q64.c

#include <linux/init.h>
#include <linux/module.h>
#include <linux/ioctl.h>
#include <linux/fs.h>
#include <linux/device.h>
#include <linux/err.h>
#include <linux/list.h>
#include <linux/errno.h>
#include <linux/mutex.h>
#include <linux/slab.h>
#include <linux/compat.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/kernel.h>#include <linux/spi/spi.h>
#include <linux/spi/spidev.h>#include <linux/delay.h>
#include <linux/uaccess.h>
#include <linux/of_gpio.h>
#include "w25q64.h"/** This supports access to SPI devices using normal userspace I/O calls.* Note that while traditional UNIX/POSIX I/O semantics are half duplex,* and often mask message boundaries, full SPI support requires full duplex* transfers.  There are several kinds of internal message boundaries to* handle chipselect management and other protocol options.** SPI has a character major number assigned.  We allocate minor numbers* dynamically using a bitmask.  You must use hotplug tools, such as udev* (or mdev with busybox) to create and destroy the /dev/spidevB.C device* nodes, since there is no fixed association of minor numbers with any* particular SPI bus or device.*/
#define SPIDEV_MAJOR			155	/* assigned */
#define N_SPI_MINORS			32	/* ... up to 256 *//*W25Q64 CMD*/
#define WRITE_ENABLE 0x06
#define PAGE_PROGRAM 0x02
#define READ_DATA 0x03
#define WRITE_STATUS_REG 0x01
#define READ_STATUS_REG 0x05
#define CHIP_ERASE 0xc7
#define SECTOR_ERASE 0x20
#define BLOCK_32KB_ERASE 0x52
#define BLOCK_64KB_ERASE 0xD8
#define READ_DEVICE_ID 0x90
#define READ_UID 0x9Fstatic DECLARE_BITMAP(minors, N_SPI_MINORS);/* Bit masks for spi_device.mode management.  Note that incorrect* settings for some settings can cause *lots* of trouble for other* devices on a shared bus:**  - CS_HIGH ... this device will be active when it shouldn't be*  - 3WIRE ... when active, it won't behave as it should*  - NO_CS ... there will be no explicit message boundaries; this*	is completely incompatible with the shared bus model*  - READY ... transfers may proceed when they shouldn't.** REVISIT should changing those flags be privileged?*/
#define SPI_MODE_MASK		(SPI_CPHA | SPI_CPOL | SPI_CS_HIGH \| SPI_LSB_FIRST | SPI_3WIRE | SPI_LOOP \| SPI_NO_CS | SPI_READY | SPI_TX_DUAL \| SPI_TX_QUAD | SPI_RX_DUAL | SPI_RX_QUAD)struct spidev_data {dev_t			devt;spinlock_t		spi_lock;struct spi_device	*spi;struct list_head	device_entry;/* TX/RX buffers are NULL unless this device is open (users > 0) */struct mutex		buf_lock;unsigned		users;u8			*tx_buffer;u8			*rx_buffer;u32			speed_hz;unsigned int cur_addr;unsigned wp_gpio;
};static LIST_HEAD(device_list);
static DEFINE_MUTEX(device_list_lock);static unsigned bufsiz = 4096;
module_param(bufsiz, uint, S_IRUGO);
MODULE_PARM_DESC(bufsiz, "data bytes in biggest supported SPI message");/*-------------------------------------------------------------------------*/static char spi_w25x_status(struct spi_device *spi)
{       int     status;char tbuf[]={READ_STATUS_REG};char rbuf[1] = {1};struct spi_transfer     t = {.tx_buf         = tbuf,.len            = ARRAY_SIZE(tbuf),};struct spi_transfer     r = {.rx_buf         = rbuf,.len            = ARRAY_SIZE(rbuf),};struct spi_message      m;spi_message_init(&m);spi_message_add_tail(&t, &m);spi_message_add_tail(&r, &m);status = spi_sync(spi, &m);return rbuf[0];
}
//modified by xzq degain for retry 5 times @20211105
static int spi_w25x_wait_ready(struct spi_device *spi )
{char retval = 1;int retry = 5;dev_dbg(&spi->dev, "wait ready...");do {retval = spi_w25x_status(spi);retval &= 0xff;retval &= 1;retry --;mdelay(5);}while((retval != 0) && (retry != 0));if(retval)dev_err(&spi->dev,"no ready\n");elsedev_dbg(&spi->dev, "OK\n");return 0;
}
//modified by xzq end
static int spi_w25x_write_enable(struct spi_device *spi)
{       int     status;char cmd_buf[1] = {WRITE_ENABLE};struct spi_transfer cmd = {.tx_buf = cmd_buf,.len = ARRAY_SIZE(cmd_buf),};struct spi_message      m;spi_message_init(&m);spi_message_add_tail(&cmd, &m);status = spi_sync(spi, &m);dev_dbg(&spi->dev, "write enable\n");return status;
}static int spi_read_w25x_id_0(struct spi_device *spi)
{       int     status;char tbuf[]={READ_UID};char rbuf[5];struct spi_transfer     t = {.tx_buf         = tbuf,.len            = ARRAY_SIZE(tbuf),};struct spi_transfer     r = {.rx_buf         = rbuf,.len            = ARRAY_SIZE(rbuf),};struct spi_message      m;spi_message_init(&m);spi_message_add_tail(&t, &m);spi_message_add_tail(&r, &m);status = spi_sync(spi, &m);dev_err(&spi->dev, "ID = %02x %02x %02x %02x %02x\n",rbuf[0], rbuf[1], rbuf[2], rbuf[3], rbuf[4]);return status;
}static int
spi_w25x_sector_erase(struct spidev_data *spidev, unsigned long size)
{int status;char cmd[4] = {SECTOR_ERASE};struct spi_device *spi = spidev->spi;struct spi_transfer t = {.tx_buf = cmd,.len = ARRAY_SIZE(cmd),};struct spi_message m;unsigned int flash_addr = spidev->cur_addr;int count = (int)size;for ( ; count > 0; count -= W25Q64_SECTOR) {cmd[1] = (unsigned char)((flash_addr & 0xff0000) >> 16);cmd[2] = (unsigned char)((flash_addr & 0xff00) >> 8);cmd[3] = (unsigned char)(flash_addr & 0xff);spi_w25x_write_enable(spi);spi_message_init(&m);spi_message_add_tail(&t, &m);status = spi_sync(spi, &m);spi_w25x_wait_ready(spi);dev_dbg(&spi->dev,"start addr: %x, sector erase OK\n", flash_addr);flash_addr += W25Q64_SECTOR;}return status;
}static int
spi_w25x_32kb_block_erase(struct spidev_data *spidev)
{int status;char cmd[4] = {BLOCK_32KB_ERASE};struct spi_device *spi = spidev->spi;struct spi_transfer t = {.tx_buf = cmd,.len = ARRAY_SIZE(cmd),};struct spi_message m;cmd[1] = (unsigned char)((spidev->cur_addr & 0xff0000) >> 16);cmd[2] = (unsigned char)((spidev->cur_addr & 0xff00) >> 8);cmd[3] = (unsigned char)(spidev->cur_addr & 0xff);spi_w25x_write_enable(spi);spi_message_init(&m);spi_message_add_tail(&t, &m);status = spi_sync(spi, &m);spi_w25x_wait_ready(spi);dev_dbg(&spi->dev,"32kb block erase OK\n");return status;
}static int
spi_w25x_64kb_block_erase(struct spidev_data *spidev)
{int status;char cmd[4] = {BLOCK_64KB_ERASE};struct spi_device *spi = spidev->spi;struct spi_transfer t = {.tx_buf = cmd,.len = ARRAY_SIZE(cmd),};struct spi_message m;cmd[1] = (unsigned char)((spidev->cur_addr & 0xff0000) >> 16);cmd[2] = (unsigned char)((spidev->cur_addr & 0xff00) >> 8);cmd[3] = (unsigned char)(spidev->cur_addr & 0xff);spi_w25x_write_enable(spi);spi_message_init(&m);spi_message_add_tail(&t, &m);status = spi_sync(spi, &m);spi_w25x_wait_ready(spi);dev_dbg(&spi->dev,"64kb block erase OK\n");return status;
}static int spi_w25x_chip_erase(struct spi_device *spi)
{int status;char chip_erase[1] = {CHIP_ERASE};struct spi_transfer erase = {.tx_buf = chip_erase,.len = ARRAY_SIZE(chip_erase),};struct spi_message m;spi_w25x_write_enable(spi);spi_message_init(&m);spi_message_add_tail(&erase, &m);status = spi_sync(spi, &m);spi_w25x_wait_ready(spi);dev_dbg(&spi->dev,"chip erase OK\n");return status;
}static loff_t
spi_w25x_llseek(struct file *filp, loff_t offset, int orig)
{loff_t ret = 0;struct spidev_data	*spidev;spidev = filp->private_data;switch (orig) {case SEEK_SET:if (offset < 0) {ret = -EINVAL;break;}if ((unsigned int)offset > W25Q64_SIZE) {ret = -EINVAL;break;}spidev->cur_addr = (unsigned int)offset;ret = spidev->cur_addr;break;case SEEK_CUR:if ((spidev->cur_addr + offset) > W25Q64_SIZE) {ret = -EINVAL;break;}if ((spidev->cur_addr + offset) < 0) {ret = -EINVAL;break;}spidev->cur_addr += offset;ret = spidev->cur_addr;break;default:ret =  - EINVAL;break;}dev_dbg(&spidev->spi->dev, "set curr addr:%02X\n", (unsigned int)ret);return ret;}static ssize_t
spidev_sync(struct spidev_data *spidev, struct spi_message *message)
{DECLARE_COMPLETION_ONSTACK(done);int status;struct spi_device *spi;spin_lock_irq(&spidev->spi_lock);spi = spidev->spi;spin_unlock_irq(&spidev->spi_lock);if (spi == NULL)status = -ESHUTDOWN;elsestatus = spi_sync(spi, message);if (status == 0)status = message->actual_length;return status;
}static inline ssize_t
spidev_sync_write(struct spidev_data *spidev, size_t len)
{int status;char cmd[1] = {PAGE_PROGRAM};unsigned char addr[3];struct spi_transfer c[] = {{.tx_buf = cmd,.len = ARRAY_SIZE(cmd),},{.tx_buf = addr,.len = ARRAY_SIZE(addr),},};struct spi_transfer t = {.tx_buf		= spidev->tx_buffer,.len		= len,.speed_hz	= spidev->speed_hz,};struct spi_message	m;addr[0] = (unsigned char)((spidev->cur_addr & 0xff0000) >> 16);addr[1] = (unsigned char)((spidev->cur_addr & 0xff00) >> 8);addr[2] = (unsigned char)(spidev->cur_addr & 0xff);spi_w25x_write_enable(spidev->spi);spi_message_init(&m);spi_message_add_tail(&c[0], &m);spi_message_add_tail(&c[1], &m);spi_message_add_tail(&t, &m);status = spidev_sync(spidev, &m);spi_w25x_wait_ready(spidev->spi);return status;
}static inline ssize_t
spidev_sync_read(struct spidev_data *spidev, size_t len)
{int status;char cmd[] = {READ_DATA};unsigned char addr[3];struct spi_transfer	t[] = {{.tx_buf = cmd,.len = ARRAY_SIZE(cmd),.speed_hz = spidev->speed_hz,},{.tx_buf = addr,.len = ARRAY_SIZE(addr),},{.rx_buf		= spidev->rx_buffer,.len		= len,.speed_hz	= spidev->speed_hz,}};struct spi_message	m;addr[0] = (unsigned char)((spidev->cur_addr & 0xff0000) >> 16);addr[1] = (unsigned char)((spidev->cur_addr & 0xff00) >> 8);addr[2] = (unsigned char)(spidev->cur_addr & 0xff);spi_message_init(&m);spi_message_add_tail(&t[0], &m);spi_message_add_tail(&t[1], &m);spi_message_add_tail(&t[2], &m);status = spidev_sync(spidev, &m);spi_w25x_wait_ready(spidev->spi);return status;
}/* Read-only message with current device setup */
static ssize_t
spidev_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos)
{struct spidev_data	*spidev;ssize_t			status = 0;/* chipselect only toggles at start or end of operation */if (count > bufsiz)return -EMSGSIZE;spidev = filp->private_data;mutex_lock(&spidev->buf_lock);status = spidev_sync_read(spidev, count);if (status > 0) {unsigned long	missing;missing = copy_to_user(buf, spidev->rx_buffer, status);if (missing == status)status = -EFAULT;elsestatus = status - missing;}mutex_unlock(&spidev->buf_lock);return status;
}/* Write-only message with current device setup */
static ssize_t
spidev_write(struct file *filp, const char __user *buf,size_t count, loff_t *f_pos)
{struct spidev_data	*spidev;ssize_t			status = 0;unsigned long		missing;/* chipselect only toggles at start or end of operation */if (count > bufsiz)return -EMSGSIZE;spidev = filp->private_data;gpio_set_value(spidev->wp_gpio, 1);mutex_lock(&spidev->buf_lock);missing = copy_from_user(spidev->tx_buffer, buf, count);if (missing == 0)status = spidev_sync_write(spidev, count);elsestatus = -EFAULT;mutex_unlock(&spidev->buf_lock);gpio_set_value(spidev->wp_gpio, 0);return status;
}static long
spidev_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{int			err = 0;int			retval = 0;struct spidev_data	*spidev;struct spi_device	*spi;u32			tmp;/* Check type and command number */if (_IOC_TYPE(cmd) != W25Q64_MAGIC)return -ENOTTY;/* Check access direction once here; don't repeat below.* IOC_DIR is from the user perspective, while access_ok is* from the kernel perspective; so they look reversed.*/if (_IOC_DIR(cmd) & _IOC_READ)err = !access_ok(VERIFY_WRITE,(void __user *)arg, _IOC_SIZE(cmd));if (err == 0 && _IOC_DIR(cmd) & _IOC_WRITE)err = !access_ok(VERIFY_READ,(void __user *)arg, _IOC_SIZE(cmd));if (err)return -EFAULT;/* guard against device removal before, or while,* we issue this ioctl.*/spidev = filp->private_data;spin_lock_irq(&spidev->spi_lock);spi = spi_dev_get(spidev->spi);spin_unlock_irq(&spidev->spi_lock);if (spi == NULL)return -ESHUTDOWN;/* use the buffer lock here for triple duty:*  - prevent I/O (from us) so calling spi_setup() is safe;*  - prevent concurrent SPI_IOC_WR_* from morphing*    data fields while SPI_IOC_RD_* reads them;*  - SPI_IOC_MESSAGE needs the buffer locked "normally".*/mutex_lock(&spidev->buf_lock);switch (cmd) {/* read requests */case W25Q64_IOC_SECTOR_ERASE:retval = spi_w25x_sector_erase(spidev, arg);break;case W25Q64_IOC_32KB_BLOCK_ERASE:retval = spi_w25x_32kb_block_erase(spidev);break;case W25Q64_IOC_64KB_BLOCK_ERASE:retval = spi_w25x_64kb_block_erase(spidev);break;case W25Q64_IOC_CHIP_ERASE:retval = spi_w25x_chip_erase(spi);break;case SPI_IOC_RD_MODE:retval = __put_user(spi->mode & SPI_MODE_MASK,(__u8 __user *)arg);break;case SPI_IOC_RD_MODE32:retval = __put_user(spi->mode & SPI_MODE_MASK,(__u32 __user *)arg);break;case SPI_IOC_RD_LSB_FIRST:retval = __put_user((spi->mode & SPI_LSB_FIRST) ?  1 : 0,(__u8 __user *)arg);break;case SPI_IOC_RD_BITS_PER_WORD:retval = __put_user(spi->bits_per_word, (__u8 __user *)arg);break;case SPI_IOC_RD_MAX_SPEED_HZ:retval = __put_user(spidev->speed_hz, (__u32 __user *)arg);break;/* write requests */case SPI_IOC_WR_MODE:case SPI_IOC_WR_MODE32:if (cmd == SPI_IOC_WR_MODE)retval = __get_user(tmp, (u8 __user *)arg);elseretval = __get_user(tmp, (u32 __user *)arg);if (retval == 0) {u32	save = spi->mode;if (tmp & ~SPI_MODE_MASK) {retval = -EINVAL;break;}tmp |= spi->mode & ~SPI_MODE_MASK;spi->mode = (u16)tmp;retval = spi_setup(spi);if (retval < 0)spi->mode = save;elsedev_dbg(&spi->dev, "spi mode %x\n", tmp);}break;case SPI_IOC_WR_LSB_FIRST:retval = __get_user(tmp, (__u8 __user *)arg);if (retval == 0) {u32	save = spi->mode;if (tmp)spi->mode |= SPI_LSB_FIRST;elsespi->mode &= ~SPI_LSB_FIRST;retval = spi_setup(spi);if (retval < 0)spi->mode = save;elsedev_dbg(&spi->dev, "%csb first\n",tmp ? 'l' : 'm');}break;case SPI_IOC_WR_BITS_PER_WORD:retval = __get_user(tmp, (__u8 __user *)arg);if (retval == 0) {u8	save = spi->bits_per_word;spi->bits_per_word = tmp;retval = spi_setup(spi);if (retval < 0)spi->bits_per_word = save;elsedev_dbg(&spi->dev, "%d bits per word\n", tmp);}break;case SPI_IOC_WR_MAX_SPEED_HZ:retval = __get_user(tmp, (__u32 __user *)arg);if (retval == 0) {u32	save = spi->max_speed_hz;spi->max_speed_hz = tmp;retval = spi_setup(spi);if (retval >= 0)spidev->speed_hz = tmp;elsedev_dbg(&spi->dev, "%d Hz (max)\n", tmp);spi->max_speed_hz = save;}break;default:return -EINVAL;}mutex_unlock(&spidev->buf_lock);spi_dev_put(spi);return retval;
}#ifdef CONFIG_COMPAT
static long
spidev_compat_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{return spidev_ioctl(filp, cmd, (unsigned long)compat_ptr(arg));
}
#else
#define spidev_compat_ioctl NULL
#endif /* CONFIG_COMPAT */static int spidev_open(struct inode *inode, struct file *filp)
{struct spidev_data	*spidev;int			status = -ENXIO;mutex_lock(&device_list_lock);list_for_each_entry(spidev, &device_list, device_entry) {if (spidev->devt == inode->i_rdev) {status = 0;break;}}if (status) {pr_debug("spidev: nothing for minor %d\n", iminor(inode));goto err_find_dev;}if (!spidev->tx_buffer) {spidev->tx_buffer = kmalloc(bufsiz, GFP_KERNEL);if (!spidev->tx_buffer) {dev_err(&spidev->spi->dev, "open/ENOMEM\n");status = -ENOMEM;goto err_find_dev;}}if (!spidev->rx_buffer) {spidev->rx_buffer = kmalloc(bufsiz, GFP_KERNEL);if (!spidev->rx_buffer) {dev_err(&spidev->spi->dev, "open/ENOMEM\n");status = -ENOMEM;goto err_alloc_rx_buf;}}spidev->users++;filp->private_data = spidev;mutex_unlock(&device_list_lock);return 0;err_alloc_rx_buf:kfree(spidev->tx_buffer);spidev->tx_buffer = NULL;
err_find_dev:mutex_unlock(&device_list_lock);return status;
}static int spidev_release(struct inode *inode, struct file *filp)
{struct spidev_data	*spidev;mutex_lock(&device_list_lock);spidev = filp->private_data;filp->private_data = NULL;/* last close? */spidev->users--;if (!spidev->users) {int		dofree;kfree(spidev->tx_buffer);spidev->tx_buffer = NULL;kfree(spidev->rx_buffer);spidev->rx_buffer = NULL;spin_lock_irq(&spidev->spi_lock);if (spidev->spi)spidev->speed_hz = spidev->spi->max_speed_hz;/* ... after we unbound from the underlying device? */dofree = (spidev->spi == NULL);spin_unlock_irq(&spidev->spi_lock);if (dofree)kfree(spidev);}mutex_unlock(&device_list_lock);return 0;
}static const struct file_operations spidev_fops = {.owner =	THIS_MODULE,/* REVISIT switch to aio primitives, so that userspace* gets more complete API coverage.  It'll simplify things* too, except for the locking.*/.write =	spidev_write,.read =		spidev_read,.unlocked_ioctl = spidev_ioctl,.compat_ioctl = spidev_compat_ioctl,.open =		spidev_open,.release =	spidev_release,.llseek =	spi_w25x_llseek,
};/*-------------------------------------------------------------------------*//* The main reason to have this class is to make mdev/udev create the* /dev/spidevB.C character device nodes exposing our userspace API.* It also simplifies memory management.*/static struct class *spidev_class;#ifdef CONFIG_OF
static const struct of_device_id spidev_dt_ids[] = {{ .compatible = "rockchip,w25q64" },{},
};
MODULE_DEVICE_TABLE(of, spidev_dt_ids);
#endif/*-------------------------------------------------------------------------*/static int spidev_probe(struct spi_device *spi)
{struct spidev_data	*spidev;struct device_node *np = spi->dev.of_node;int			status;unsigned long		minor;dev_err(&spi->dev, "probe,rk3399.0\n");/** spidev should never be referenced in DT without a specific* compatible string, it is a Linux implementation thing* rather than a description of the hardware.*/if (spi->dev.of_node && !of_match_device(spidev_dt_ids, &spi->dev)) {dev_err(&spi->dev, "buggy DT: spidev listed directly in DT\n");WARN_ON(spi->dev.of_node &&!of_match_device(spidev_dt_ids, &spi->dev));}/* Allocate driver data */spidev = kzalloc(sizeof(*spidev), GFP_KERNEL);if (!spidev)return -ENOMEM;/* Initialize the driver data */spidev->spi = spi;spin_lock_init(&spidev->spi_lock);mutex_init(&spidev->buf_lock);INIT_LIST_HEAD(&spidev->device_entry);/* If we can allocate a minor number, hook up this device.* Reusing minors is fine so long as udev or mdev is working.*/mutex_lock(&device_list_lock);minor = find_first_zero_bit(minors, N_SPI_MINORS);if (minor < N_SPI_MINORS) {struct device *dev;spidev->devt = MKDEV(SPIDEV_MAJOR, minor);dev = device_create(spidev_class, &spi->dev, spidev->devt,spidev, "w25q64");status = PTR_ERR_OR_ZERO(dev);} else {dev_err(&spi->dev, "no minor number available!\n");status = -ENODEV;}if (status == 0) {set_bit(minor, minors);list_add(&spidev->device_entry, &device_list);}mutex_unlock(&device_list_lock);spidev->speed_hz = spi->max_speed_hz;if (status == 0)spi_set_drvdata(spi, spidev);elsekfree(spidev);spidev->wp_gpio = of_get_named_gpio(np, "wp-gpio", 0);if (!gpio_is_valid(spidev->wp_gpio)) {dev_err(&spi->dev, "wp-gpio: %d is invalid\n", spidev->wp_gpio);return -ENODEV;}status = gpio_request(spidev->wp_gpio, "wp-gpio");if (status) {dev_err(&spi->dev, "wp-gpio: %d request failed!\n", spidev->wp_gpio);gpio_free(spidev->wp_gpio);return -ENODEV;}gpio_direction_output(spidev->wp_gpio, 0);gpio_export(spidev->wp_gpio, 0);spi_read_w25x_id_0(spi);return status;
}static int spidev_remove(struct spi_device *spi)
{struct spidev_data	*spidev = spi_get_drvdata(spi);gpio_free(spidev->wp_gpio);/* make sure ops on existing fds can abort cleanly */spin_lock_irq(&spidev->spi_lock);spidev->spi = NULL;spin_unlock_irq(&spidev->spi_lock);/* prevent new opens */mutex_lock(&device_list_lock);list_del(&spidev->device_entry);device_destroy(spidev_class, spidev->devt);clear_bit(MINOR(spidev->devt), minors);if (spidev->users == 0)kfree(spidev);mutex_unlock(&device_list_lock);return 0;
}static struct spi_driver spidev_spi_driver = {.driver = {.name =		"w25q64",.of_match_table = of_match_ptr(spidev_dt_ids),},.probe =	spidev_probe,.remove =	spidev_remove,/* NOTE:  suspend/resume methods are not necessary here.* We don't do anything except pass the requests to/from* the underlying controller.  The refrigerator handles* most issues; the controller driver handles the rest.*/
};/*-------------------------------------------------------------------------*/static int __init spidev_init(void)
{int status;/* Claim our 256 reserved device numbers.  Then register a class* that will key udev/mdev to add/remove /dev nodes.  Last, register* the driver which manages those device numbers.*/BUILD_BUG_ON(N_SPI_MINORS > 256);status = register_chrdev(SPIDEV_MAJOR, "w25q64", &spidev_fops);if (status < 0)return status;spidev_class = class_create(THIS_MODULE, "w25q64");if (IS_ERR(spidev_class)) {unregister_chrdev(SPIDEV_MAJOR, spidev_spi_driver.driver.name);return PTR_ERR(spidev_class);}status = spi_register_driver(&spidev_spi_driver);if (status < 0) {class_destroy(spidev_class);unregister_chrdev(SPIDEV_MAJOR, spidev_spi_driver.driver.name);}return status;
}
module_init(spidev_init);static void __exit spidev_exit(void)
{spi_unregister_driver(&spidev_spi_driver);class_destroy(spidev_class);unregister_chrdev(SPIDEV_MAJOR, spidev_spi_driver.driver.name);
}
module_exit(spidev_exit);MODULE_AUTHOR("Yubel");
MODULE_DESCRIPTION("User mode SPI device interface");
MODULE_LICENSE("GPL");

w25q64.h

#ifndef W25Q64_H
#define W25Q64_H#include <linux/types.h>
#include <linux/ioctl.h>#define W25Q64_SIZE 0x800000
#define W25Q64_PAGE_LENGTH	256
#define W25Q64_SECTOR	4096
#define W25Q64_32KB_BLOCK 32768
#define W25Q64_64KB_BLOCK 65536/* IOCTL commands */
#define W25Q64_MAGIC			'k'/*Erase SPI Flash*/
#define W25Q64_IOC_SECTOR_ERASE			_IOW(W25Q64_MAGIC, 6, __u32)
#define W25Q64_IOC_32KB_BLOCK_ERASE		_IOW(W25Q64_MAGIC, 7, __u32)
#define W25Q64_IOC_64KB_BLOCK_ERASE		_IOW(W25Q64_MAGIC, 8, __u32)
#define W25Q64_IOC_CHIP_ERASE			_IOW(W25Q64_MAGIC, 9, __u32)#endif /* W25Q64_H */

http://chatgpt.dhexx.cn/article/Ocl1rIiA.shtml

相关文章

软件SPI读写W25Q64硬件SPI读写W25Q64

目录 软件SPI读写W25Q64 MySPI W25Q64 主函数 硬件SPI读写W25Q64 软件SPI读写W25Q64 程序整体框架&#xff1a; SPI模块包含通信引脚封装&#xff0c;初始化&#xff0c;SPI三个基本时序单元&#xff08;起始&#xff0c;终止&#xff0c;交换一个字节&#xff09; W2…

W25Q64Flash芯片STM32操作

1、W25Q64Flash芯片介绍 通讯方式&#xff1a;SPI通讯 大小&#xff1a;8M&#xff08;Byte&#xff09; &#xff08;128块&#xff08;Block&#xff09;&#xff0c;每块64K字节&#xff0c;每块16个扇区&#xff08;Sector&#xff09;&#xff0c;每个扇区4K字节&…

STM32学习笔记(十一)丨SPI通信(W25Q64芯片简介,使用SPI读写W25Q64存储器芯片)

本篇文章包含的内容 一、SPI的通信协议及其原理1.1 SPI简介1.2 SPI通信的硬件连接1.3 SPI的时序基本单元1.3.1 起始条件和终止条件1.3.2 交换字节&#xff08;模式0&#xff0c;先移入&#xff0c;再移出&#xff09;1.3.3 交换字节&#xff08;模式1&#xff0c;先移出&#x…

看单片机原理图-外部FLASHW25Q64

系列文章目录 看单片机原理图-最小系统 看单片机原理图-最小系统电源电路 看单片机原理图-输入输出电路LED指示、按键输入 看单片机原理图-红外遥控、EEPROM 看单片机原理图-FLASH 文章目录 系列文章目录前言一、FLASH 前言 硬件&#xff1a;百问网100ASK_STM32F103_MINI开发…

W25Q64 Flash芯片原理与应用方案(含W25Q64中文数据手册)

W25Q64是华邦公司推出的大容量SPI FLASH产品&#xff0c;其容量为64Mb&#xff08;8MB&#xff09;&#xff0c;应用较为广泛。 W25Q系列的器件在灵活性和性能方面远远超过普通的串行闪存器件。W25Q64将8M字节的容量分为128个块&#xff0c;每个块大小为64K字节&#xff0c;每个…

cortex_m3_stm32嵌入式学习笔记(二十一):SPI实验(通信总线)

SPI 是英语 Serial Peripheral interface 的缩写&#xff0c;顾名思义就是串行外围设备接口。是 Motorola首先在其 MC68HCXX 系列处理器上定义的。 SPI 接口主要应用在 EEPROM&#xff0c; FLASH&#xff0c;实时时钟&#xff0c; AD 转换器&#xff0c;还有数字信号处理器和数…

STM-32:SPI通信协议/W25Q64简介—软件SPI读写W25Q64

目录 一、SPI简介1.1电路模式1.2通信原理1.3SPI时序基本单元1.3.1起始和终止1.3.2交换字节 二、W25Q642.1W25Q64简介2.2W25Q64硬件电路2.3W25Q64框图2.4Flash操作注意事项 三、软件SPI读写W25Q643.1接线图3.2程序代码 一、SPI简介 SPI是串行外设接口&#xff08;Serial Periph…

STM32CubeMx之硬件SPI驱动W25Q64

STM32CubeMx之硬件SPI驱动W25Q64 1.SPI简介 SPI是串行外设接口&#xff08;Serial Peripheral Interface&#xff09;的缩写&#xff0c;是一种高速的&#xff0c;全双工&#xff0c;同步的通信总线&#xff0c;并且在芯片的管脚上只占用四根线&#xff0c;节约了芯片的管脚&a…

SPI读写串行FLASH(W25Q64)

文章目录 1、SPI协议1、硬件连接2、通讯时序3、不同的通信模式 2、W25Q64介绍3、SPI读写驱动编写4、源码 1、SPI协议 SPI 协议是由摩托罗拉公司提出的通讯协议(Serial Peripheral Interface)&#xff0c;即串行外围设备接口&#xff0c;是一种高速全双工的通信总线。它被广泛地…

STM32入门开发: 介绍SPI总线、读写W25Q64(FLASH)(硬件+模拟时序)

一、环境介绍 编程软件: keil5 操作系统: win10 MCU型号: STM32F103ZET6 STM32编程方式: 寄存器开发 (方便程序移植到其他单片机) SPI总线: STM32本身支持SPI硬件时序&#xff0c;本文示例代码里同时采用模拟时序和硬件时序两种方式读写W25Q64。 模拟时序更加方便移植到…

树莓派系统介绍

树莓派是一个微型计算机&#xff0c;和普通的电脑没有什么区别&#xff0c;只是体积更小&#xff0c;只有卡片大小&#xff0c;存储能力和计算能力会差一点&#xff0c;主要用于学习&#xff0c;实验所用。 是电脑就要安装操作系统&#xff0c;树莓派官方推荐了两种系统&#…

树莓派 zero linux,树莓派 zero基本调试

回家之前就从网上购买了一堆设备&#xff0c;回去也不能闲着&#xff0c;可以利用家里相对齐全的准备安装调试。结果人还没回来&#xff0c;东西先到了。 购买的核心装备是树莓派zero w&#xff0c;虽然已经知道它比家族大哥树莓派小不少&#xff0c;但拿到手里还是惊奇它的小巧…

树莓派c语言访问mariadb,树莓派之MariaDB

8种机械键盘轴体对比 本人程序员&#xff0c;要买一个写代码的键盘&#xff0c;请问红轴和茶轴怎么选&#xff1f; 安装MariaDB MariaDB是MySQL的一个分支 直接命令行敲入&#xff1a;1sudo apt-get install mariadb-server 即可完成安装。 一开始安装完成后不知道是需要初始化…

python树莓派_树莓派python

广告关闭 腾讯云11.11云上盛惠 &#xff0c;精选热门产品助力上云&#xff0c;云服务器首年88元起&#xff0c;买的越多返的越多&#xff0c;最高返5000元&#xff01; 最初拿到树莓派的时候测试过,没成功,后来发现一张华丽丽的说明图,顿时醒悟了.. 记录下来,主要学习自 :htt…

树莓派硬件介绍及配件选择

目录 树莓派Datasheet下载地址&#xff1a; Raspberry 4B 外观图&#xff1a; 技术规格书&#xff1a; 性能介绍&#xff1a; 树莓派配件选用 电源的选用&#xff1a; 树莓派外壳选用&#xff1a; 内存卡/U盘选用 树莓派Datasheet下载地址&#xff1a; Raspberry Pi …

树莓派Pico开发版

Pico开发版有16个PWM通道非常适合用于舵机及电机的控制[1]。 单板机(4B)、微控制器(Pico)&#xff0c;树莓派支持Micro Python和C编程[3]。Thony是Python的开发环境。 [1]【评测】树莓派Pico开发板详细评测&#xff0c;到底值不值&#xff1f; [2] Pico树莓派中文站。 [3]一…

1-树莓派及配件购买推荐

树莓派4b主板及配件购买推荐。 作者&#xff1a;白宸羽 套餐 购买链接&#xff1a;https://item.taobao.com/item.htm?spma1z10.5-c-s.w4002-22269478747.11.277c1a24rkExbq&id597680312428 TF卡建议选择16g&#xff0c;套餐建议选择“摄像头套餐” HDMI用于连接显示屏&a…

python树莓派编程_python树莓派编程

广告关闭 腾讯云11.11云上盛惠 ,精选热门产品助力上云,云服务器首年88元起,买的越多返的越多,最高返5000元! 例如,你可以用树莓派搭建你自己的家用云存储服务器。? 树莓派用python来进行编程。 树莓派项目的一个核心思想是python编程语言的使用。 python允许树莓派的拥…

树莓派如何第一次启动-树莓派从购买到启动一步一步完全版!

背景 闲来无事&#xff0c;在咸鱼上买了一个树莓派3B。买来配件都十分齐全&#xff0c;于是就想着启动来测试一下。下面是树莓派第一次启动的全过程&#xff0c;包含安装系统。 1 准备工作 1.1所需硬件 笔记本电脑、树莓派3B、16GTF卡、读卡器、电源和电源线共四种。 无需准…

树莓派价格暴涨买不起?他们自己做了一块价格还不到1/4的开发板平替树莓派,还火到海外去了

众所周知&#xff0c;树莓派诞生之初时的设计有四大要点 一个可编程的硬件&#xff08;功能很强&#xff09;√足够有趣&#xff0c;能吸引年轻人&#xff08;可玩性确实高&#xff09;√能反复扔进书包&#xff0c;不怕挤坏&#xff08;质量杠杠滴&#xff09;√ 还有最后一…