一,、W25Q32BV芯片簡介
W25X是一系列SPI接口Flash芯片的簡稱,,它采用SPI接口和CPU通信,本文使用的W25Q32BV容量為32M,,具體特性如下:
1.1,、基本特性
該芯片最大支持104MHz的時鐘,,供電電壓范圍在2.7~3.6V,SPI的片選信號CS低有效,,在操作芯片的時候,,
需要將/WP和/HOLD管腳接電源。
發(fā)送地址或數(shù)據(jù)到設(shè)備時,,MOSI管腳數(shù)據(jù)采樣在CLK的上升沿,,從芯片讀數(shù)據(jù)或者狀態(tài)時,MISO管腳數(shù)據(jù)采樣在CLK
的下降沿,,所以在設(shè)置SPI的工作模式時,,必須設(shè)置為MODE0或者M(jìn)ODE3,本文設(shè)置為MODE3,。
1.2,、存儲空間簡介
W25Q32BV總共有16384頁(page),每頁有256bytes,,每次最大可以編程一頁,。在擦除上,可以一次擦除
4KB,、32KB,、64KB,或者擦除整個芯片,。整個芯片的存儲空間如下圖:
W25Q32BV存儲空間分為sector和block。一個sector共有4KB,,一個block共有32KB,。一個sector存儲空間如下圖:
本文共支持四種擦除方式,分別如下:
1) cmd = 0x20,,sector擦除,,一次可以擦除4KB,。芯片共有1024個sector,。
2) cmd = 0x52,,半個block擦除,,一次可以擦除32KB,。芯片共有128個半block,。
3) cmd = 0xd8,block擦除,,一次可以擦除64KB,。芯片共有64個block。
4) cmd = 0xC7,,芯片擦除,,擦除整個芯片。
1.3,、狀態(tài)寄存器
W25Q32BV共有兩個字節(jié)的狀態(tài)寄存器,,我們需要關(guān)心的就是BIT0和BIT1。
BIT0:busy flag,,1:busy,,0:free。
BIT1:write enable latch,,1:write enable,,0:write disable。
1.4,、操作要求
在操作W25Q32BV時,,如果是寫數(shù)據(jù)到芯片,則每寫一個字節(jié),,都需要讀取一個數(shù)據(jù),。
在從芯片接收數(shù)據(jù)時,首先往芯片寫一個字節(jié)的0xff,,然后就是需要讀取的數(shù)據(jù),。
二、設(shè)備驅(qū)動
2.1,、設(shè)備注冊
在系統(tǒng)啟動的時候,,首先會對設(shè)備信息進(jìn)行注冊,見《Linux spi驅(qū)動分析(一)----總線驅(qū)動》中的3.1,,所以編寫w25q的設(shè)備
驅(qū)動程序時,,首先需要對設(shè)備信息進(jìn)行注冊,具體內(nèi)容如下:
-
#if defined(CONFIG_SPI_FLASH_W25Q)
-
static struct gsc3280_spi_info w25q_spi1_dev_platdata = {
-
.cs_type = 1,
-
.pin_cs = 87,
-
.num_cs = 1,
-
.cs_value = 0,
-
.lsb_flg = 0,
-
.bits_per_word = 8,
-
};
-
#endif
-
static struct spi_board_info gsc3280_spi_devices[] = {
-
#if defined(CONFIG_SPI_FLASH_W25Q)
-
{
-
.modalias = "spi-w25q",
-
.bus_num = 1,
-
.chip_select = 2,
-
.mode = SPI_MODE_3,
-
.max_speed_hz = 5 * 1000 * 1000,
-
.controller_data = &w25q_spi1_dev_platdata,
-
},
-
#endif
-
-
};
-
static int __init gsc3280_spi_devices_init(void)
-
{
-
spi_register_board_info(gsc3280_spi_devices, ARRAY_SIZE(gsc3280_spi_devices));
-
return 0;
-
}
-
device_initcall(gsc3280_spi_devices_init);
2.2,、初始化函數(shù)
首先我們從設(shè)備注冊開始,,程序如下:
-
static struct spi_driver w25q_driver = {
-
.driver = {
-
.name = "spi-w25q",
-
.owner = THIS_MODULE,
-
},
-
//.id_table = w25q_ids,
-
.probe = w25q_probe,
-
.remove = __devexit_p(w25q_remove),
-
};
-
-
-
static int __init w25q_init(void)
-
{
-
return spi_register_driver(&w25q_driver);
-
}
-
static void __exit w25q_exit(void)
-
{
-
spi_unregister_driver(&w25q_driver);
-
}
-
module_init(w25q_init);
-
module_exit(w25q_exit);
由于W25Q32BV使用SPI接口,所以將其注冊為SPI驅(qū)動,,接下來看下探測函數(shù)w25q_probe,,程序如下:
-
static int __devinit w25q_probe(struct spi_device *spi)
-
{
-
int ret = 0;
-
struct w25q_dev *w25q;
-
-
DBG("############\n");
-
DBG("w25q spi flash probe start.\n");
-
w25q = kzalloc(sizeof(struct w25q_dev), GFP_KERNEL);
-
if (!w25q) {
-
DBG("!!!!kzalloc error!\n");
-
return -ENOMEM;
-
}
-
ret = spi_setup(spi);
-
if (ret != 0) {
-
DBG("!!!!setup error!\n");
-
return ret;
-
}
-
w25q->spi = spi;
-
mutex_init(&w25q->mlock);
-
strlcpy(w25q->name, W25Q_SPI_FLASH_NAME, sizeof(w25q->name));
-
ret = alloc_chrdev_region(&w25q->devt, 0, W25Q_MAX_MINOR, "w25q");
-
if (ret < 0) {
-
DBG("!!!!%s: failed to allocate char dev region!\n", __FILE__);
-
goto err_kzall;
-
}
-
w25q->dev.devt = MKDEV(MAJOR(w25q->devt), 1);
-
cdev_init(&w25q->cdev, &w25q_fops);
-
w25q->cdev.owner = THIS_MODULE;
-
ret = cdev_add(&w25q->cdev, w25q->devt, 1);
-
if (ret) {
-
DBG("!!!!cdev add error!\n");
-
goto err_alloc;
-
}
-
w25q->class = class_create(THIS_MODULE, "w25q-spi");
-
if (IS_ERR(w25q->class)) {
-
DBG("!!!!failed in create w25q spi flash class!\n");
-
goto err_alloc;;
-
}
-
device_create(w25q->class, NULL, w25q->devt, NULL, "w25q");
-
dev_set_drvdata(&spi->dev, w25q);
-
DBG("w25q spi flash probe success.\n");
-
DBG("############\n");
-
return 0;
-
-
err_alloc:
-
unregister_chrdev_region(w25q->devt, W25Q_MAX_MINOR);
-
err_kzall:
-
kfree(w25q);
-
printk(KERN_ERR "!!!!!!w25q spi flash probe error.!!!!!!\n");
-
return ret;
-
}
說明:
1) 首先申請?jiān)O(shè)備驅(qū)動結(jié)構(gòu)體。
2) 調(diào)用spi_setup(spi)函數(shù)對設(shè)備信息初始化,。
3) 初始化設(shè)備驅(qū)動結(jié)構(gòu)體成員變量,。
4) 創(chuàng)建/dev目錄下操作文件,,操作函數(shù)集為w25q_fops。
5) 將設(shè)備驅(qū)動結(jié)構(gòu)體中的鏈表插入本文件全局鏈表w25q_device_list中,,以便在函數(shù)操作集的open函數(shù)中找到設(shè)備驅(qū)動結(jié)構(gòu)體,。
remove函數(shù)是探測函數(shù)的相反過程,具體程序如下:
-
static int __devexit w25q_remove(struct spi_device *spi)
-
{
-
struct w25q_dev *w25q = dev_get_drvdata(&spi->dev);
-
-
cdev_del(&w25q->cdev);
-
unregister_chrdev_region(w25q->devt, W25Q_MAX_MINOR);
-
device_destroy(w25q->class, w25q->devt);
-
class_destroy(w25q->class);
-
kfree(w25q);
-
return 0;
-
}
2.3,、操作函數(shù)集w25q_fops
操作函數(shù)集結(jié)構(gòu)體具體內(nèi)容如下:
-
static const struct file_operations w25q_fops = {
-
.owner = THIS_MODULE,
-
.open = w25q_open,
-
.write = w25q_write,
-
.unlocked_ioctl = w25q_ioctl,
-
.read = w25q_read,
-
.llseek = w25q_llseek,
-
.release = w25q_release,
-
};
接下來我們一一講述,。
首先看下open函數(shù)w25q_open,具體程序如下:
-
static int w25q_open(struct inode *inode, struct file *file)
-
{
-
struct w25q_dev *w25q = container_of(inode->i_cdev, struct w25q_dev, char_cdev);
-
-
if (test_and_set_bit(W25Q_BIT_LOCK_OPEN, &w25q->bit_lock)) {
-
DBG("!!!!w25q open err, busy!\n");
-
return -EBUSY;
-
}
-
file->private_data = w25q;
-
return 0;
-
}
說明:
1) 通過container_of找到在探測函數(shù)w25q_probe中定義的設(shè)備驅(qū)動結(jié)構(gòu)體,。
2) 測試并且設(shè)置忙標(biāo)志,,如果測試忙,直接忙退出,。
3) 將找到的設(shè)備驅(qū)動結(jié)構(gòu)體指針指向file->private_data,,在函數(shù)操作集的其他函數(shù)中就可以使用設(shè)備驅(qū)動結(jié)構(gòu)體了。
接下來看下寫函數(shù)w25q_write(),,程序如下:
-
#define W25Q_BUF_LEN 4096
-
#define W25Q_PAGE_NUM 256
-
static ssize_t w25q_write(struct file *file, const char __user *user_buf, size_t count, loff_t *ppos)
-
{
-
int ret = 0;
-
u8 *buf_start, *buf_tmp, *w25q_buf;
-
struct w25q_dev *w25q= file->private_data;
-
u32 buf_size = 0, page_num = W25Q_PAGE_NUM, len = 0;
-
-
DBG("@@@@w25q write start\n");
-
buf_start = buf_tmp = kzalloc(W25Q_BUF_LEN, GFP_KERNEL);
-
w25q_buf = w25q->buf = kzalloc(page_num + 4, GFP_KERNEL);
-
if (!buf_start || !w25q_buf) {
-
DBG("!!!!kzalloc error!\n");
-
return -ENOMEM;
-
}
-
ret = mutex_lock_interruptible(&w25q->mlock);
-
if (ret) {
-
DBG("!!!!mutex lock error!");
-
goto exit_kfree;
-
}
-
len = W25Q_BUF_LEN;
-
buf_size = min(count, len);
-
if (copy_from_user(buf_tmp, user_buf, buf_size)) {
-
DBG("!!!!copy_from_user() error!\n");
-
ret = -EFAULT;
-
goto exit_lock;
-
}
-
DBG("w25q->const_addr = 0x%x\n", w25q->const_addr);
-
buf_tmp = buf_start;
-
w25q->cmd = W25X_PAGE_PROG;
-
w25q->addr = w25q->const_addr;
-
while(buf_size) {
-
w25q->buf = w25q_buf;
-
w25q->len = min(buf_size, page_num);
-
memcpy(w25q->buf + 4, buf_tmp, w25q->len);
-
ret = w25q_write_date(w25q);
-
if (ret != 0) {
-
break;
-
}
-
buf_tmp += w25q->len;
-
w25q->addr += w25q->len;
-
buf_size -= w25q->len;
-
}
-
-
exit_lock:
-
mutex_unlock(&w25q->mlock);
-
exit_kfree:
-
kfree(buf_start);
-
kfree(w25q_buf);
-
if (ret != 0)
-
DBG("!!!!w25q write error!\n");
-
else
-
DBG("w25q write success\n");
-
return ret;
-
}
說明:
1) 寫函數(shù)首先申請兩段內(nèi)存,,第一段內(nèi)存用于存儲從應(yīng)用層復(fù)制來的待寫數(shù)據(jù),最大為4KB,。第二段內(nèi)存用于存儲每次
往W25Q32BV寫的數(shù)據(jù),。由于W25Q32BV每次最大能寫256bytes,所以page_num = 256,,加上4是由于每次
傳輸時,,需要在最前面加上一個字節(jié)的命令和三個字節(jié)的地址。
2) 獲取本次可以傳輸?shù)淖畲髷?shù)據(jù)長度,。
3) 設(shè)置好傳輸?shù)腸md和起始地址,然后進(jìn)入while循環(huán),。
4) 在while循環(huán)中,獲取本次可以傳輸?shù)淖畲箝L度,,最長為256bytes,,然后將其拷貝到buf中,加上4的目的是因?yàn)閎uf
的前四個字節(jié)需要放置命令和地址,。
5) 調(diào)用w25q_write_date(w25q)函數(shù)實(shí)現(xiàn)數(shù)據(jù)傳輸。
6) 更新變量,,為下一次傳輸做好準(zhǔn)備,。
w25q_write_date(w25q)函數(shù)具體內(nèi)容如下:
-
static void w25q_write_enable(struct w25q_dev *w25q)
-
{
-
u8 cmd = W25X_WRITE_ENABLE;
-
-
spi_w8r8(w25q->spi, cmd);
-
}
-
static int w25q_wait_null(struct w25q_dev *w25q)
-
{
-
uint8_t limit = 5;
-
-
/* wait BUSY bit clear */
-
while(((w25q_read_stat_reg(w25q) & 0x01) == 0x01) && (limit != 0)) {
-
limit--;
-
mdelay(50);
-
}
-
if (limit == 0) {
-
DBG("!!!!w25q_wait_null:time out!\n");
-
return -EBUSY;
-
}
-
else
-
return 0;
-
}
-
/*
-
* when you call this function,
-
* the w25q->cmd, w25q->len(tx date len),
-
* w25q->addr and w25q->buf(date) are OK
-
*
-
*/
-
static int w25q_write_date(struct w25q_dev *w25q)
-
{
-
int ret = 0;
-
u8 i = 0, rx = 0;
-
struct spi_message message;
-
struct spi_transfer x[(w25q->len + 4) * 2];
-
-
w25q_write_enable(w25q); //SET WEL
-
ret = w25q_wait_null(w25q);
-
if (ret != 0) {
-
DBG("!!!!w25q_write_date: wait null err!\n");
-
return ret;
-
}
-
if((w25q_read_stat_reg(w25q) & 0x02) != 0x02) {
-
DBG("!!!!state register write able is 0\n");
-
return -EBUSY; //disable write
-
}
-
-
DBG("cmd = 0x%x, addr = 0x%x\n", w25q->cmd, w25q->addr);
-
w25q->buf[0] = w25q->cmd;
-
w25q->buf[1] = ((u8)(w25q->addr >> 16));
-
w25q->buf[2] = ((u8)(w25q->addr >> 8));
-
w25q->buf[3] = ((u8)w25q->addr);
-
-
spi_message_init(&message);
-
memset(x, 0, sizeof x);
-
for (i = 0; i < (w25q->len + 4) * 2; i++) {
-
x[i].len = 1;
-
spi_message_add_tail(&x[i], &message);
-
if ((i % 2) == 0) {
-
x[i].tx_buf = w25q->buf++;
-
} else {
-
x[i].rx_buf = ℞
-
}
-
}
-
/* do the i/o */
-
ret = spi_sync(w25q->spi, &message);
-
if (ret != 0) {
-
DBG("!!!!w25q_write_date: spi_sync() error!");
-
return ret;
-
}
-
ret = w25q_wait_null(w25q);
-
if (ret != 0)
-
DBG("!!!!w25q_write_date: w25q_wait_null() error!");
-
return ret;
-
}
說明:
1) 在調(diào)用w25q_write_date(w25q)函數(shù)之前,,需要首先設(shè)置好w25q->cmd, w25q->len(tx date len),,
w25q->addr和w25q->buf(date)變量,。
2) 設(shè)置芯片狀態(tài)寄存器,使其可寫,。
3) 等待芯片不忙,。
4) 讀取芯片狀態(tài)寄存器,查看其是否可寫,。
5) 配置發(fā)送buf,,調(diào)用spi_sync(w25q->spi, &message);函數(shù)實(shí)現(xiàn)寫數(shù)據(jù),。
6) 等待芯片不忙,,退出。
接下來看下函數(shù)操作集中的ioctl函數(shù),程序如下:
-
static long w25q_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
-
{
-
int ret = 0;
-
u32 get_value = 0;
-
struct w25q_dev *w25q= file->private_data;
-
void __user *argp = (void __user *)arg;
-
int __user *p = argp;
-
-
DBG("@@@@w25q ioctl start.\n");
-
ret = mutex_lock_interruptible(&w25q->mlock);
-
if (ret) {
-
DBG("!!!!mutex lock error!\n");
-
return ret;
-
}
-
if ((_IOC_TYPE(cmd) != W25Q_IOC_MAGIC) || (_IOC_NR(cmd) > W25Q_IOC_MAXNR)) {
-
DBG("!!!!ioc type or ioc nr error!\n");
-
ret = -ENOTTY;
-
goto exit;
-
}
-
switch(cmd) {
-
case W25Q_SECTOR_ERASE:
-
case W25Q_HALF_BLOCK_ERASE:
-
case W25Q_BLOCK_ERASE:
-
if (get_user(get_value, p)) {
-
DBG("!!!!get value error!\n");
-
ret = -EFAULT;
-
goto exit;
-
}
-
ret = w25q_erase(w25q, get_value, cmd);
-
break;
-
case W25Q_CHIP_ERASE:
-
ret = w25q_chip_erase(w25q);
-
break;
-
case W25Q_READ_DEVICE_ID:
-
ret = w25q_read_id(w25q);
-
if (ret == 0)
-
put_user(w25q->result, p);
-
break;
-
default:
-
DBG("!!!!cmd error!\n");
-
ret = -ENOTTY;
-
break;
-
}
-
-
exit:
-
mutex_unlock(&w25q->mlock);
-
if (ret != 0)
-
DBG("!!!!w25q ioctl error!\n");
-
else
-
DBG("w25q ioctl success.\n");
-
return ret;
-
}
說明:
1) 目前共支持5個命令,包括sector擦除,,half block擦除,,block擦除,,芯片擦除和讀取芯片ID。
2) 前三種擦除方式共用一個函數(shù)w25q_erase(w25q, get_value, cmd);,程序如下:
-
static int w25q_erase(struct w25q_dev *w25q, u32 num, unsigned int cmd)
-
{
-
int ret = 0;
-
u8 *buf_start;
-
-
switch(cmd) {
-
case W25Q_SECTOR_ERASE:
-
DBG("sector erase cmd\n");
-
if (num > W25Q_SECTOR_MAX) {
-
DBG("!!!!sector max is over\n");
-
return -EFAULT;
-
}
-
w25q->const_addr = num * W25Q_ONE_SECTOR_ADDR;
-
w25q->cmd = W25X_SECTOR_ERASE_CMD;
-
break;
-
case W25Q_HALF_BLOCK_ERASE:
-
DBG("half block erase cmd\n");
-
if (num > W25Q_HALF_BLOCK_MAX) {
-
DBG("!!!!half block max is over\n");
-
return -EFAULT;
-
}
-
w25q->const_addr = num * W25Q_HALF_BLOCK_ADDR;
-
w25q->cmd = W25X_HALF_BLOCK_ERASE_CMD;
-
break;
-
case W25Q_BLOCK_ERASE:
-
DBG("block erase cmd\n");
-
if (num > W25Q_BLOCK_MAX) {
-
DBG("!!!!block max is over\n");
-
return -EFAULT;
-
}
-
w25q->const_addr = num * W25Q_ONE_BLOCK_ADDR;
-
w25q->cmd = W25X_BLOCK_ERASE_CMD;
-
break;
-
}
-
DBG("w25q->const_addr = 0x%x\n", w25q->const_addr);
-
w25q->len = 0;
-
buf_start = w25q->buf = kzalloc(w25q->len + 4, GFP_KERNEL);
-
if (!buf_start) {
-
DBG("!!!!kzalloc is error\n");
-
return -ENOMEM;
-
}
-
w25q->addr = w25q->const_addr;
-
ret = w25q_write_date(w25q);
-
kfree(buf_start);
-
if (ret != 0) {
-
DBG("!!!!w25q_erase: spi write err!\n");
-
return ret;
-
}
-
DBG("w25q_erase: erase OK\n");
-
return ret;
-
}
說明:
1) 首先根據(jù)不同的擦除方式,設(shè)置命令和地址兩個變量,。
2) 調(diào)用w25q_write_date(w25q);函數(shù)實(shí)現(xiàn)數(shù)據(jù)傳輸。
芯片擦除函數(shù)w25q_chip_erase()如下:
-
static int w25q_chip_erase(struct w25q_dev *w25q)
-
{
-
int ret = 0;
-
u8 cmd = W25X_CHIP_ERASE;
-
-
DBG("w25q_chip_erase\n");
-
w25q_write_enable(w25q); //SET WEL
-
ret = w25q_wait_null(w25q);
-
if (ret != 0) {
-
DBG("!!!!chip_erase: wait null err!\n");
-
return ret;
-
}
-
if((w25q_read_stat_reg(w25q) & 0x02) != 0x02) {
-
DBG("!!!!state register write able is 0\n");
-
return -EBUSY; //disable write
-
}
-
spi_w8r8(w25q->spi, cmd);
-
return w25q_wait_null(w25q);
-
}
讀取設(shè)備ID函數(shù)w25q_read_id()如下:
-
static int w25q_read_id(struct w25q_dev *w25q)
-
{
-
int ret = 0;
-
u8 *buf_start;
-
-
DBG("w25q_read_id\n");
-
w25q->len = 2;
-
w25q->addr = 0;
-
w25q->cmd = W25X_READ_ID_CMD;
-
buf_start = w25q->buf = kzalloc(w25q->len, GFP_KERNEL);
-
if (!buf_start) {
-
DBG("!!!!kzalloc is error\n");
-
return -ENOMEM;
-
}
-
ret = w25q_read_data(w25q);
-
w25q->buf = buf_start;
-
w25q->result = *w25q->buf << 8;
-
w25q->buf++;
-
w25q->result |= *w25q->buf;
-
kfree(buf_start);
-
if (ret != 0) {
-
DBG("!!!!w25q_read_id: w25q_read_data error!\n");
-
return ret;
-
}
-
DBG("w25q_read_id: read id OK\n");
-
return ret;
-
}
說明:
1) 首先設(shè)置好變量,申請內(nèi)存
2) 調(diào)用w25q_read_data()函數(shù)實(shí)現(xiàn)讀取數(shù)據(jù),。
w25q_read_data函數(shù)如下:
-
/*
-
* when you call this function,
-
* the w25q->cmd, w25q->len(receive len)
-
* w25q->buf(kzalloc receive) and w25q->addr are OK
-
*
-
*/
-
static int w25q_read_data(struct w25q_dev *w25q)
-
{
-
int ret = 0;
-
struct spi_message message;
-
struct spi_transfer x[(w25q->len + 4) * 2];
-
u8 i = 0, rx = 0, dumy_value = 0xff, tx_buff[4] = {0};
-
-
w25q_write_enable(w25q); //SET WEL
-
ret = w25q_wait_null(w25q);
-
if (ret != 0) {
-
DBG("!!!!chip_erase: wait null err!\n");
-
return ret;
-
}
-
if((w25q_read_stat_reg(w25q) & 0x02) != 0x02) {
-
DBG("!!!!state register write able is 0\n");
-
return -EBUSY; //disable write
-
}
-
-
DBG("cmd = 0x%x, addr = 0x%x\n", w25q->cmd, w25q->addr);
-
tx_buff[0] = w25q->cmd;
-
tx_buff[1] = ((uint8_t)(w25q->addr >> 16));
-
tx_buff[2] = ((uint8_t)(w25q->addr >> 8));
-
tx_buff[3] = ((uint8_t)(w25q->addr));
-
-
spi_message_init(&message);
-
memset(x, 0, sizeof x);
-
for (i = 0; i < 8; i++) { //cmd
-
x[i].len = 1;
-
spi_message_add_tail(&x[i], &message);
-
if ((i % 2) == 0) {
-
x[i].tx_buf = &tx_buff[i / 2];
-
} else {
-
x[i].rx_buf = ℞
-
}
-
}
-
for (i = 8; i < (w25q->len + 4) * 2; i++) {
-
x[i].len = 1;
-
spi_message_add_tail(&x[i], &message);
-
if ((i % 2) == 0) {
-
x[i].tx_buf = &dumy_value;
-
} else {
-
x[i].rx_buf = w25q->buf++;
-
}
-
}
-
/* do the i/o */
-
return spi_sync(w25q->spi, &message);
-
}
說明:
1) 在從芯片讀取數(shù)據(jù)時的格式為:首先發(fā)送一個字節(jié)命令+三個字節(jié)讀取地址,,然后就可以接收數(shù)據(jù)了。
2) 第一個for循環(huán)發(fā)送的是命令和地址,,第二個for循環(huán)是接收數(shù)據(jù),。
3) 調(diào)用此函數(shù)之前,需要設(shè)置好w25q->cmd, w25q->len(receive len),,w25q->buf(kzalloc receive)和w25q->addr,。
接下來看下函數(shù)操作集中的讀數(shù)據(jù)函數(shù)w25q_read(),程序如下:
-
static ssize_t w25q_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos)
-
{
-
int ret = 0;
-
u8 *buf_start, *buf_tmp, *w25q_buf;
-
struct w25q_dev *w25q = file->private_data;
-
u32 buf_size = 0, read_len = 0, page_num = W25Q_PAGE_NUM;
-
-
DBG("@@@@w25q read start\n");
-
buf_start = buf_tmp = kzalloc(W25Q_BUF_LEN, GFP_KERNEL);
-
w25q_buf = w25q->buf = kzalloc(page_num, GFP_KERNEL);
-
if (!buf_start || !w25q_buf ) {
-
DBG("!!!!kzalloc error!\n");
-
return -ENOMEM;
-
}
-
ret = mutex_lock_interruptible(&w25q->mlock);
-
if (ret) {
-
DBG("!!!!mutex lock error!\n");
-
goto exit_kfree;
-
}
-
read_len = W25Q_BUF_LEN;
-
buf_size = min(count, read_len);
-
read_len = buf_size;
-
-
w25q->cmd = W25X_READ_DATA;
-
w25q->addr = w25q->const_addr;
-
DBG("w25q->addr = 0x%x\n", w25q->addr);
-
while (buf_size) {
-
w25q->buf = w25q_buf;
-
w25q->len = min(buf_size, page_num);
-
ret = w25q_read_data(w25q);
-
if (ret != 0) {
-
goto exit_lock;
-
}
-
memcpy(buf_tmp, w25q_buf, w25q->len);
-
buf_tmp += w25q->len;
-
buf_size -= w25q->len;
-
w25q->addr += w25q->len;
-
}
-
ret = copy_to_user(user_buf, buf_start, read_len);
-
ret = read_len -ret;
-
-
exit_lock:
-
mutex_unlock(&w25q->mlock);
-
exit_kfree:
-
kfree(buf_start);
-
kfree(w25q_buf);
-
DBG("w25q read stop, ret = %d\n", ret);
-
return ret;
-
}
說明:
1) 此函數(shù)需要申請兩段內(nèi)存空間,,第一段用于存放從W25Q32BV接收到的數(shù)據(jù),,第二段用于存放每次接收的數(shù)據(jù)。
2) 設(shè)置好變量后,,調(diào)用w25q_read_data(w25q)讀取數(shù)據(jù),。
3) 讀取完成后,將讀取到的數(shù)據(jù)拷貝到應(yīng)用層,。
三,、應(yīng)用層測試
應(yīng)用層測試程序如下:
-
/*
-
* first you must erase,
-
* then write, then read
-
* or you can read only
-
*
-
*/
-
#include "w25q.h"
-
-
-
int main(int argc, char **argv)
-
{
-
char str[10] = {0};
-
int fd = 0, ret = 0;
-
unsigned char buffer[BUFSIZE] = {0};
-
unsigned int i = 0, idCmd = 0, num = 0;
-
-
fd = open("/dev/w25q", O_RDWR);
-
if (fd < 0) {
-
printf("Open ADC Device Faild!\n");
-
exit(1);
-
}
-
while(1) {
-
idCmd = 0;
-
printf("please enter the cmd and num :\n");
-
scanf("%s%x", str, &num);
-
//printf("cmd = %s, idFreq = %d\n", str, idFreq);
-
if (num >= 0) {
-
if (strcmp(str, "SECTOR") == 0) {
-
idCmd = W25Q_SECTOR_ERASE;
-
ret = ioctl(fd, idCmd, &num);
-
if (ret != 0) {
-
printf("sector erase Faild!\n");
-
}
-
} else if(strcmp(str, "HALF") == 0) {
-
idCmd = W25Q_HALF_BLOCK_ERASE;
-
ret = ioctl(fd, idCmd, &num);
-
if (ret != 0) {
-
printf("half block erase Faild!\n");
-
}
-
} else if(strcmp(str, "BLOCK") == 0) {
-
idCmd = W25Q_BLOCK_ERASE;
-
ret = ioctl(fd, idCmd, &num);
-
if (ret != 0) {
-
printf("block erase Faild!\n");
-
}
-
} else if(strcmp(str, "CHIP") == 0) {
-
idCmd = W25Q_CHIP_ERASE;
-
ret = ioctl(fd, idCmd, &num);
-
if (ret != 0) {
-
printf("chip erase Faild!\n");
-
}
-
} else if(strcmp(str, "ID") == 0) {
-
idCmd = W25Q_READ_DEVICE_ID;
-
ret = ioctl(fd, idCmd, &num);
-
if (ret != 0) {
-
printf("read ID Faild!\n");
-
} else {
-
printf("ID = 0x%x\n", num);
-
}
-
} else if(strcmp(str, "READ") == 0) {
-
memset(buffer, 0, BUFSIZE);
-
printf("------------\n");
-
for (i = 0; i < WRITE_NUM; i++) {
-
if((i != 0) && ((i % 8) == 0)) {
-
printf("\n");
-
}
-
printf("0x%x ", buffer[i]);
-
}
-
printf("\n------------\n");
-
ret = read(fd, buffer, WRITE_NUM);
-
printf("\n------------\n");
-
for (i = 0; i < WRITE_NUM; i++) {
-
if((i != 0) && ((i % 8) == 0)) {
-
printf("\n");
-
}
-
printf("0x%x ", buffer[i]);
-
}
-
printf("\n------------\n");
-
} else if(strcmp(str, "WRITE") == 0) {
-
for (i = 0; i < WRITE_NUM; i++) {
-
buffer[i] = i;
-
}
-
ret = write(fd, buffer, WRITE_NUM);
-
if (ret != 0) {
-
printf("w25q write oper Faild!\n");
-
}
-
} else if(strcmp(str, "QUIT") == 0) {
-
break;
-
} else {
-
printf("wrong string\n");
-
}
-
} else {
-
printf("wrong input num(< 0)\n");
-
}
-
}/* end while(1) */
-
close(fd);
-
return 0;
-
}
說明:
1) 首先從終端接收命令內(nèi)容。
2) 比較命令,,然后進(jìn)入不同的處理流程,。
四、測試演示
4.1,、讀取芯片ID
4.2,、讀寫芯片
|