以下是一个示例代码,演示了如何在Linux系统中使用SPI接口读写和擦除Winbond SPI Flash。这个示例代码使用设备树来描述SPI设备和SPI Flash,以便在Linux内核启动时自动加载SPI驱动程序。
设备树描述:
```
&spi0 {
#address-cells = <1>;
#size-cells = <0>;
pinctrl-names = "default";
pinctrl-0 = <&spi0_pins>;
status = "okay";
flash: w25q64@0 {
compatible = "winbond,w25q64";
spi-max-frequency = <10000000>;
reg = <0>;
};
};
&iomuxc {
spi0_pins: spi0 {
pinctrl-single,pins = <
MX6QDL_PAD_EIM_D16__GPIO3_IO16 0x82
MX6QDL_PAD_EIM_D17__ECSPI1_SS0 0x82
MX6QDL_PAD_EIM_D18__ECSPI1_SCLK 0x82
MX6QDL_PAD_EIM_D19__ECSPI1_MISO 0x82
MX6QDL_PAD_EIM_D20__ECSPI1_MOSI 0x82
>;
};
};
```
其中,`&spi0`描述了SPI控制器和SPI Flash,`flash`节点的`compatible`属性指定了该节点对应的Flash型号。`spi-max-frequency`属性指定了SPI Flash的最大时钟频率,`reg`属性指定了Flash在SPI总线上的地址。`&iomuxc`节点描述了SPI控制器的引脚连接。
示例代码:
```c
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/spi/spi.h>
#define WINBOND_W25Q64_ID 0xEF4017
static struct spi_device *flash_device;
static int flash_read_id(struct spi_device *spi)
{
u8 tx_buf[4] = {0x9F, 0x00, 0x00, 0x00};
u8 rx_buf[4] = {0};
struct spi_message msg;
struct spi_transfer xfers[2];
xfers[0].tx_buf = tx_buf;
xfers[0].len = 4;
xfers[1].rx_buf = rx_buf;
xfers[1].len = 4;
spi_message_init(&msg);
spi_message_add_tail(&xfers[0], &msg);
spi_message_add_tail(&xfers[1], &msg);
spi_sync(spi, &msg);
return (rx_buf[1] << 16) | (rx_buf[2] << 8) | rx_buf[3];
}
static int flash_erase(struct spi_device *spi, unsigned int offset, unsigned int len)
{
u8 tx_buf[4] = {0x20, (offset >> 16) & 0xFF, (offset >> 8) & 0xFF, offset & 0xFF};
struct spi_message msg;
struct spi_transfer xfer;
xfer.tx_buf = tx_buf;
xfer.len = 4;
spi_message_init(&msg);
spi_message_add_tail(&xfer, &msg);
spi_sync(spi, &msg);
msleep(10);
tx_buf[0] = 0xD8;
xfer.tx_buf = tx_buf;
xfer.len = 1;
spi_message_init(&msg);
spi_message_add_tail(&xfer, &msg);
spi_sync(spi, &msg);
return 0;
}
static int flash_write(struct spi_device *spi, unsigned int offset, unsigned int len, const u8 *buf)
{
u8 tx_buf[4] = {0x02, (offset >> 16) & 0xFF, (offset >> 8) & 0xFF, offset & 0xFF};
struct spi_message msg;
struct spi_transfer xfers[2];
xfers[0].tx_buf = tx_buf;
xfers[0].len = 4;
xfers[1].tx_buf = buf;
xfers[1].len = len;
spi_message_init(&msg);
spi_message_add_tail(&xfers[0], &msg);
spi_message_add_tail(&xfers[1], &msg);
spi_sync(spi, &msg);
return 0;
}
static int flash_probe(struct spi_device *spi)
{
int ret;
if (spi->dev.of_node) {
struct device_node *np = spi->dev.of_node;
const char *compat = of_device_get_match_data(&spi->dev);
if (!of_device_is_compatible(np, compat)) {
dev_err(&spi->dev, "device tree mismatch\n");
return -EINVAL;
}
}
if (spi->max_speed_hz > 10000000)
spi->max_speed_hz = 10000000;
if (spi->bits_per_word != 8)
spi->bits_per_word = 8;
ret = spi_setup(spi);
if (ret < 0)
return ret;
ret = flash_read_id(spi);
if (ret != WINBOND_W25Q64_ID) {
dev_err(&spi->dev, "unknown flash id: %x\n", ret);
return -ENODEV;
}
flash_device = spi;
return 0;
}
static int flash_remove(struct spi_device *spi)
{
if (flash_device == spi)
flash_device = NULL;
return 0;
}
static const struct spi_device_id flash_id_table[] = {
{"w25q64", 0},
{},
};
MODULE_DEVICE_TABLE(spi, flash_id_table);
static const struct of_device_id flash_of_match[] = {
{ .compatible = "winbond,w25q64", .data = "w25q64" },
{},
};
MODULE_DEVICE_TABLE(of, flash_of_match);
static struct spi_driver flash_driver = {
.driver = {
.name = "winbond-flash",
.owner = THIS_MODULE,
.of_match_table = flash_of_match,
},
.probe = flash_probe,
.remove = flash_remove,
.id_table = flash_id_table,
};
static int __init flash_init(void)
{
return spi_register_driver(&flash_driver);
}
static void __exit flash_exit(void)
{
spi_unregister_driver(&flash_driver);
}
module_init(flash_init);
module_exit(flash_exit);
MODULE_AUTHOR("Your Name");
MODULE_DESCRIPTION("Winbond W25Q64 SPI flash driver");
MODULE_LICENSE("GPL");
```
在这个示例代码中,`flash_read_id`函数用于读取Flash的ID,`flash_erase`函数用于擦除Flash的指定区域,`flash_write`函数用于向Flash写入数据。`flash_probe`函数用于在SPI设备匹配时初始化Flash设备,`flash_remove`函数用于在SPI设备卸载时清理Flash设备。`flash_driver`结构体定义了SPI驱动程序的属性,以及驱动程序的入口和出口函数。`flash_init`和`flash_exit`函数分别在驱动程序加载和卸载时调用。