平台 | 内核版本 | 安卓版本 |
---|---|---|
RK3399 | Linux4.4 | Android7.1 |
W25Q128介绍
W25Q128
阵列将16M
的容量分为 256
个块(Block)每个块大小为 64K
字节,每个块又分为16
个扇区(Sector
),每个扇区 4K
个字节。 W25Q128
的最少擦除单位为一个扇区,也就是每次必须擦除4K
个字节。操作需要给 W25Q128
开辟一个至少 4K
的缓存区,对 SRAM
要求比较高,要求芯片必须有 4K
以上 SRAM
才能很好的操作。
软件基础
在设备树中每一个spi
节点对应一个SPI
控制器(一般情况下软件将bus
和控制器配置成如下对应关系)
spi0 <==> bus 0
spi1 <==> bus 1
spi2 <==> bus 2
其中每个SPI
控制器上片选数有一个或多个,具体看芯片
SPI0_CSN0
SPI0_CSN1
SPI1_CSN0
SPI2_CSN0
SPI2_CSN1
SPI工作方式
SPI
以主从方式工作,这种模式通常有一个主设备和一个或多个从设备,需要至少4
根线
管脚 | 解释 |
---|---|
CS |
片选信号 |
SCLK |
时钟信号 |
MOSI |
主设备数据输出,从设备数据输入 |
MISO |
主设备数据输入,从设备数据输出 |
Linux
内核用CPOL
和CPHA
的组合来表示当前SPI
的四种工作模式
CPOL=0,CPHA=0 SPI_MODE_0
CPOL=0,CPHA=1 SPI_MODE_1
CPOL=1,CPHA=0 SPI_MODE_2
CPOL=1,CPHA=1 SPI_MODE_3
CPOL
:表示时钟信号的初始电平的状态,0
为低电平,1
为高电平
CPHA
:表示在哪个时钟沿采样,0
为第一个时钟沿采样,1
为第二个时钟沿采样
SPI
的四种工作模式波形图如下
硬件连接(以W25Q128FV为例子介绍)
w25q128fv | Firefly-RK3399 |
---|---|
CS | SPI1_CSN0 |
VCC | VCC3V3_SYS |
DO | SPI1_RXD |
DI | SPI1_TXD |
GND | GND |
HOLD | TP_RST(需要拉高到3V) |
CLK | SPI1_CLK |
DeviceTree
&spi1 {
status = "okay";
max-freq = <48000000>;
dev-port = <1>;
w25q128fv@10{
status = "okay";
compatible = "firefly,w25q128fv";
reg = <0x0>;
spi-max-frequency = <48000000>;
};
};
字段 | 解释 |
---|---|
dev-port |
表示bus_num ,因为这里用的是spi1 ,所以配置为1 |
@10 |
1 表示bus_num ,需要和dev-port 一致, 0 表示spi 设备使用CSN0 作为片选 |
reg = <0x0> |
表示spi 设备使用的片选,需要和上面一致,即CSN0 |
驱动
static struct of_device_id firefly_match_table[] = {
{ .compatible = "firefly,w25q128fv",},
{},
};
static struct spi_driver w25q128fv_spi_driver = {
.driver = {
.name = "firefly-spi",
.owner = THIS_MODULE,
.of_match_table = firefly_match_table,
},
.probe = w25q128fv_spi_probe,
};
static int w25q128fv_spi_init(void)
{
return spi_register_driver(&w25q128fv_spi_driver);
}
module_init(w25q128fv_spi_init);
当驱动匹配后
扫描二维码关注公众号,回复:
9339979 查看本文章
static int w25q128fv_spi_probe(struct spi_device *spi)
{
int ret = 0;
struct device_node __maybe_unused *np = spi->dev.of_node;
dev_dbg(&spi->dev, "Firefly SPI demo program\n");
if(!spi)
return -ENOMEM;
dev_err(&spi->dev, "w25q128fv_spi_probe: setup mode %d, %s%s%s%s%u bits/w, %u Hz max\n",
(int) (spi->mode & (SPI_CPOL | SPI_CPHA)),
(spi->mode & SPI_CS_HIGH) ? "cs_high, " : "",
(spi->mode & SPI_LSB_FIRST) ? "lsb, " : "",
(spi->mode & SPI_3WIRE) ? "3wire, " : "",
(spi->mode & SPI_LOOP) ? "loopback, " : "",
spi->bits_per_word, spi->max_speed_hz);
#ifdef USE_TIMER
/* init timer */
init_timer(&g_sds.timer);
/* setup the timer */
g_sds.timer.expires = jiffies + EXPIRES_PERIOD;
g_sds.timer.function = timeout_handler;
g_sds.timer.data = (unsigned long)spi;
/* add to system */
add_timer(&g_sds.timer);
#else
#endif
return ret;
}
定时时间到
#ifdef USE_TIMER
struct self_define_struct {
struct timer_list timer;
};
struct self_define_struct g_sds;
#define EXPIRES_PERIOD (5*HZ)
static void timeout_handler(unsigned long tdata)
{
struct spi_device *spi = (struct spi_device *)tdata;
g_sds.timer.expires = jiffies + EXPIRES_PERIOD;
add_timer(&g_sds.timer);
w25q128fv_spi_read_w25x_id_0(spi);
w25q128fv_spi_read_w25x_id_1(spi);
}
#endif
static int w25q128fv_spi_read_w25x_id_1(struct spi_device *spi)
{
int status;
char tbuf[] = {FIREFLY_SPI_READ_ID_CMD};
char rbuf[5];
/* 先发送写数据后读回数据 */
status = spi_write_then_read(spi, tbuf, sizeof(tbuf), rbuf, sizeof(rbuf));
dev_err(&spi->dev, "%s: ID = %02x %02x %02x %02x %02x\n", __FUNCTION__, rbuf[0], rbuf[1], rbuf[2], rbuf[3], rbuf[4]);
return status;
}
static int w25q128fv_spi_read_w25x_id_0(struct spi_device *spi)
{
int status;
char tbuf[]={FIREFLY_SPI_READ_ID_CMD};
char rbuf[5];
struct spi_transfer t = {
.tx_buf = tbuf,
.len = sizeof(tbuf),
};
struct spi_transfer r = {
.rx_buf = rbuf,
.len = sizeof(rbuf),
};
struct spi_message m;
/* 初始化message */
spi_message_init(&m);
/* 将tx rx buffer加入到message */
spi_message_add_tail(&t, &m);
spi_message_add_tail(&r, &m);
/* 发送message */
status = spi_sync(spi, &m);
dev_err(&spi->dev, "%s: ID = %02x %02x %02x %02x %02x\n", __FUNCTION__, rbuf[0], rbuf[1], rbuf[2], rbuf[3], rbuf[4]);
return status;
}