转载地址:http://blog.chinaunix.net/uid-25445243-id-4026974.html

一、W25Q32BV芯片简介

W25X是一系列SPI接口Flash芯片的简称,它采用SPI接口和CPU通信,本文使用的W25Q32BV容量为32M,具体特性如下:

1.1、基本特性

该芯片最大支持104MHz的时钟,供电电压范围在2.7~3.6V,SPI的片选信号CS低有效,在操作芯片的时候,

需要将/WP和/HOLD管脚接电源。

发送地址或数据到设备时,MOSI管脚数据采样在CLK的上升沿,从芯片读数据或者状态时,MISO管脚数据采样在CLK

的下降沿,所以在设置SPI的工作模式时,必须设置为MODE0或者MODE3,本文设置为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、状态寄存器

W25Q32BV共有两个字节的状态寄存器,我们需要关心的就是BIT0和BIT1。

BIT0:busy flag,1:busy,0:free。

BIT1:write enable latch,1:write enable,0:write disable。

1.4、操作要求

在操作W25Q32BV时,如果是写数据到芯片,则每写一个字节,都需要读取一个数据。

在从芯片接收数据时,首先往芯片写一个字节的0xff,然后就是需要读取的数据。

二、设备驱动

2.1、设备注册

在系统启动的时候,首先会对设备信息进行注册,见《Linux spi驱动分析(一)----总线驱动》中的3.1,所以编写w25q的设备
驱动程序时,首先需要对设备信息进行注册,具体内容如下:

点击(此处)折叠或打开

  1. #if defined(CONFIG_SPI_FLASH_W25Q)
  2. static struct gsc3280_spi_info w25q_spi1_dev_platdata = {
  3. .cs_type            = 1,
  4. .pin_cs            = 87,
  5. .num_cs            = 1,
  6. .cs_value            = 0,
  7. .lsb_flg            = 0,
  8. .bits_per_word    = 8,
  9. };
  10. #endif
  11. static struct spi_board_info gsc3280_spi_devices[] = {
  12. #if defined(CONFIG_SPI_FLASH_W25Q)
  13. {
  14. .modalias        = "spi-w25q",
  15. .bus_num        = 1,
  16. .chip_select        = 2,
  17. .mode            = SPI_MODE_3,
  18. .max_speed_hz    = 5 * 1000 * 1000,
  19. .controller_data    = &w25q_spi1_dev_platdata,
  20. },
  21. #endif
  22. };
  23. static int __init gsc3280_spi_devices_init(void)
  24. {
  25. spi_register_board_info(gsc3280_spi_devices, ARRAY_SIZE(gsc3280_spi_devices));
  26. return 0;
  27. }
  28. device_initcall(gsc3280_spi_devices_init);

2.2、初始化函数

首先我们从设备注册开始,程序如下:

点击(此处)折叠或打开

  1. static struct spi_driver w25q_driver = {
  2. .driver    = {
  3. .name    = "spi-w25q",
  4. .owner    = THIS_MODULE,
  5. },
  6. //.id_table    = w25q_ids,
  7. .probe    = w25q_probe,
  8. .remove    = __devexit_p(w25q_remove),
  9. };
  10. static int __init w25q_init(void)
  11. {
  12. return spi_register_driver(&w25q_driver);
  13. }
  14. static void __exit w25q_exit(void)
  15. {
  16. spi_unregister_driver(&w25q_driver);
  17. }
  18. module_init(w25q_init);
  19. module_exit(w25q_exit);

由于W25Q32BV使用SPI接口,所以将其注册为SPI驱动,接下来看下探测函数w25q_probe,程序如下:

点击(此处)折叠或打开

  1. static int __devinit w25q_probe(struct spi_device *spi)
  2. {
  3. int ret = 0;
  4. struct w25q_dev *w25q;
  5. DBG("############\n");
  6. DBG("w25q spi flash probe start.\n");
  7. w25q = kzalloc(sizeof(struct w25q_dev), GFP_KERNEL);
  8. if (!w25q) {
  9. DBG("!!!!kzalloc error!\n");
  10. return -ENOMEM;
  11. }
  12. ret = spi_setup(spi);
  13. if (ret != 0) {
  14. DBG("!!!!setup error!\n");
  15. return ret;
  16. }
  17. w25q->spi = spi;
  18. mutex_init(&w25q->mlock);
  19. strlcpy(w25q->name, W25Q_SPI_FLASH_NAME, sizeof(w25q->name));
  20. ret = alloc_chrdev_region(&w25q->devt, 0, W25Q_MAX_MINOR, "w25q");
  21. if (ret < 0) {
  22. DBG("!!!!%s: failed to allocate char dev region!\n", __FILE__);
  23. goto err_kzall;
  24. }
  25. w25q->dev.devt = MKDEV(MAJOR(w25q->devt), 1);
  26. cdev_init(&w25q->cdev, &w25q_fops);
  27. w25q->cdev.owner = THIS_MODULE;
  28. ret = cdev_add(&w25q->cdev, w25q->devt, 1);
  29. if (ret) {
  30. DBG("!!!!cdev add error!\n");
  31. goto err_alloc;
  32. }
  33. w25q->class = class_create(THIS_MODULE, "w25q-spi");
  34. if (IS_ERR(w25q->class)) {
  35. DBG("!!!!failed in create w25q spi flash class!\n");
  36. goto err_alloc;;
  37. }
  38. device_create(w25q->class, NULL, w25q->devt, NULL, "w25q");
  39. dev_set_drvdata(&spi->dev, w25q);
  40. DBG("w25q spi flash probe success.\n");
  41. DBG("############\n");
  42. return 0;
  43. err_alloc:
  44. unregister_chrdev_region(w25q->devt, W25Q_MAX_MINOR);
  45. err_kzall:
  46. kfree(w25q);
  47. printk(KERN_ERR "!!!!!!w25q spi flash probe error.!!!!!!\n");
  48. return ret;
  49. }

说明:
        1) 首先申请设备驱动结构体。
        2) 调用spi_setup(spi)函数对设备信息初始化。
        3) 初始化设备驱动结构体成员变量。
        4) 创建/dev目录下操作文件,操作函数集为w25q_fops。
        5) 将设备驱动结构体中的链表插入本文件全局链表w25q_device_list中,以便在函数操作集的open函数中找到设备驱动结构体。
        remove函数是探测函数的相反过程,具体程序如下:

点击(此处)折叠或打开

  1. static int __devexit w25q_remove(struct spi_device *spi)
  2. {
  3. struct w25q_dev *w25q = dev_get_drvdata(&spi->dev);
  4. cdev_del(&w25q->cdev);
  5. unregister_chrdev_region(w25q->devt, W25Q_MAX_MINOR);
  6. device_destroy(w25q->class, w25q->devt);
  7. class_destroy(w25q->class);
  8. kfree(w25q);
  9. return 0;
  10. }

2.3、操作函数集w25q_fops

操作函数集结构体具体内容如下:

点击(此处)折叠或打开

  1. static const struct file_operations w25q_fops = {
  2. .owner = THIS_MODULE,
  3. .open = w25q_open,
  4. .write = w25q_write,
  5. .unlocked_ioctl = w25q_ioctl,
  6. .read = w25q_read,
  7. .llseek = w25q_llseek,
  8. .release = w25q_release,
  9. };

接下来我们一一讲述。
        首先看下open函数w25q_open,具体程序如下:

点击(此处)折叠或打开

  1. static int w25q_open(struct inode *inode, struct file *file)
  2. {
  3. struct w25q_dev *w25q = container_of(inode->i_cdev, struct w25q_dev, char_cdev);
  4. if (test_and_set_bit(W25Q_BIT_LOCK_OPEN, &w25q->bit_lock)) {
  5. DBG("!!!!w25q open err, busy!\n");
  6. return -EBUSY;
  7. }
  8. file->private_data = w25q;
  9. return 0;
  10. }

说明:
        1) 通过container_of找到在探测函数w25q_probe中定义的设备驱动结构体。
        2) 测试并且设置忙标志,如果测试忙,直接忙退出。
        3) 将找到的设备驱动结构体指针指向file->private_data,在函数操作集的其他函数中就可以使用设备驱动结构体了。
        接下来看下写函数w25q_write(),程序如下:

点击(此处)折叠或打开

  1. #define W25Q_BUF_LEN                4096
  2. #define W25Q_PAGE_NUM                256
  3. static ssize_t w25q_write(struct file *file, const char __user *user_buf, size_t count, loff_t *ppos)
  4. {
  5. int ret = 0;
  6. u8 *buf_start, *buf_tmp, *w25q_buf;
  7. struct w25q_dev *w25q= file->private_data;
  8. u32 buf_size = 0, page_num = W25Q_PAGE_NUM, len = 0;
  9. DBG("@@@@w25q write start\n");
  10. buf_start = buf_tmp = kzalloc(W25Q_BUF_LEN, GFP_KERNEL);
  11. w25q_buf = w25q->buf = kzalloc(page_num + 4, GFP_KERNEL);
  12. if (!buf_start || !w25q_buf) {
  13. DBG("!!!!kzalloc error!\n");
  14. return -ENOMEM;
  15. }
  16. ret = mutex_lock_interruptible(&w25q->mlock);
  17. if (ret) {
  18. DBG("!!!!mutex lock error!");
  19. goto exit_kfree;
  20. }
  21. len = W25Q_BUF_LEN;
  22. buf_size = min(count, len);
  23. if (copy_from_user(buf_tmp, user_buf, buf_size)) {
  24. DBG("!!!!copy_from_user() error!\n");
  25. ret = -EFAULT;
  26. goto exit_lock;
  27. }
  28. DBG("w25q->const_addr = 0x%x\n", w25q->const_addr);
  29. buf_tmp = buf_start;
  30. w25q->cmd = W25X_PAGE_PROG;
  31. w25q->addr = w25q->const_addr;
  32. while(buf_size) {
  33. w25q->buf = w25q_buf;
  34. w25q->len = min(buf_size, page_num);
  35. memcpy(w25q->buf + 4, buf_tmp, w25q->len);
  36. ret = w25q_write_date(w25q);
  37. if (ret != 0) {
  38. break;
  39. }
  40. buf_tmp += w25q->len;
  41. w25q->addr += w25q->len;
  42. buf_size -= w25q->len;
  43. }
  44. exit_lock:
  45. mutex_unlock(&w25q->mlock);
  46. exit_kfree:
  47. kfree(buf_start);
  48. kfree(w25q_buf);
  49. if (ret != 0)
  50. DBG("!!!!w25q write error!\n");
  51. else
  52. DBG("w25q write success\n");
  53. return ret;
  54. }

说明:
        1) 写函数首先申请两段内存,第一段内存用于存储从应用层复制来的待写数据,最大为4KB。第二段内存用于存储每次
            往W25Q32BV写的数据。由于W25Q32BV每次最大能写256bytes,所以page_num = 256,加上4是由于每次
            传输时,需要在最前面加上一个字节的命令和三个字节的地址。
        2) 获取本次可以传输的最大数据长度。
        3) 设置好传输的cmd和起始地址,然后进入while循环。
        4) 在while循环中,获取本次可以传输的最大长度,最长为256bytes,然后将其拷贝到buf中,加上4的目的是因为buf
            的前四个字节需要放置命令和地址。
        5) 调用w25q_write_date(w25q)函数实现数据传输。
        6) 更新变量,为下一次传输做好准备。
        w25q_write_date(w25q)函数具体内容如下:

点击(此处)折叠或打开

  1. static void w25q_write_enable(struct w25q_dev *w25q)
  2. {
  3. u8 cmd = W25X_WRITE_ENABLE;
  4. spi_w8r8(w25q->spi, cmd);
  5. }
  6. static int w25q_wait_null(struct w25q_dev *w25q)
  7. {
  8. uint8_t limit = 5;
  9. /* wait BUSY bit clear */
  10. while(((w25q_read_stat_reg(w25q) & 0x01) == 0x01) && (limit != 0)) {
  11. limit--;
  12. mdelay(50);
  13. }
  14. if (limit == 0) {
  15. DBG("!!!!w25q_wait_null:time out!\n");
  16. return -EBUSY;
  17. }
  18. else
  19. return 0;
  20. }
  21. /*
  22. * when you call this function,
  23. * the w25q->cmd, w25q->len(tx date len),
  24. * w25q->addr and w25q->buf(date) are OK
  25. *
  26. */
  27. static int w25q_write_date(struct w25q_dev *w25q)
  28. {
  29. int ret = 0;
  30. u8 i = 0, rx = 0;
  31. struct spi_message message;
  32. struct spi_transfer    x[(w25q->len + 4) * 2];
  33. w25q_write_enable(w25q);    //SET WEL
  34. ret = w25q_wait_null(w25q);
  35. if (ret != 0) {
  36. DBG("!!!!w25q_write_date: wait null err!\n");
  37. return ret;
  38. }
  39. if((w25q_read_stat_reg(w25q) & 0x02) != 0x02) {
  40. DBG("!!!!state register write able is 0\n");
  41. return -EBUSY;    //disable write
  42. }
  43. DBG("cmd = 0x%x, addr = 0x%x\n", w25q->cmd, w25q->addr);
  44. w25q->buf[0] = w25q->cmd;
  45. w25q->buf[1] = ((u8)(w25q->addr >> 16));
  46. w25q->buf[2] = ((u8)(w25q->addr >> 8));
  47. w25q->buf[3] = ((u8)w25q->addr);
  48. spi_message_init(&message);
  49. memset(x, 0, sizeof x);
  50. for (i = 0; i < (w25q->len + 4) * 2; i++) {
  51. x[i].len = 1;
  52. spi_message_add_tail(&x[i], &message);
  53. if ((i % 2) == 0) {
  54. x[i].tx_buf = w25q->buf++;
  55. } else {
  56. x[i].rx_buf = &rx;
  57. }
  58. }
  59. /* do the i/o */
  60. ret = spi_sync(w25q->spi, &message);
  61. if (ret != 0) {
  62. DBG("!!!!w25q_write_date: spi_sync() error!");
  63. return ret;
  64. }
  65. ret = w25q_wait_null(w25q);
  66. if (ret != 0)
  67. DBG("!!!!w25q_write_date: w25q_wait_null() error!");
  68. return ret;
  69. }

说明:
        1) 在调用w25q_write_date(w25q)函数之前,需要首先设置好w25q->cmd, w25q->len(tx date len),
            w25q->addr和w25q->buf(date)变量。
        2) 设置芯片状态寄存器,使其可写。
        3) 等待芯片不忙。
        4) 读取芯片状态寄存器,查看其是否可写。
        5) 配置发送buf,调用spi_sync(w25q->spi, &message);函数实现写数据。
        6) 等待芯片不忙,退出。
        接下来看下函数操作集中的ioctl函数,程序如下:

点击(此处)折叠或打开

  1. static long w25q_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
  2. {
  3. int ret = 0;
  4. u32 get_value = 0;
  5. struct w25q_dev *w25q= file->private_data;
  6. void __user *argp = (void __user *)arg;
  7. int __user *p = argp;
  8. DBG("@@@@w25q ioctl start.\n");
  9. ret = mutex_lock_interruptible(&w25q->mlock);
  10. if (ret) {
  11. DBG("!!!!mutex lock error!\n");
  12. return ret;
  13. }
  14. if ((_IOC_TYPE(cmd) != W25Q_IOC_MAGIC) || (_IOC_NR(cmd) > W25Q_IOC_MAXNR)) {
  15. DBG("!!!!ioc type or ioc nr error!\n");
  16. ret = -ENOTTY;
  17. goto exit;
  18. }
  19. switch(cmd) {
  20. case W25Q_SECTOR_ERASE:
  21. case W25Q_HALF_BLOCK_ERASE:
  22. case W25Q_BLOCK_ERASE:
  23. if (get_user(get_value, p)) {
  24. DBG("!!!!get value error!\n");
  25. ret = -EFAULT;
  26. goto exit;
  27. }
  28. ret = w25q_erase(w25q, get_value, cmd);
  29. break;
  30. case W25Q_CHIP_ERASE:
  31. ret = w25q_chip_erase(w25q);
  32. break;
  33. case W25Q_READ_DEVICE_ID:
  34. ret = w25q_read_id(w25q);
  35. if (ret == 0)
  36. put_user(w25q->result, p);
  37. break;
  38. default:
  39. DBG("!!!!cmd error!\n");
  40. ret = -ENOTTY;
  41. break;
  42. }
  43. exit:
  44. mutex_unlock(&w25q->mlock);
  45. if (ret != 0)
  46. DBG("!!!!w25q ioctl error!\n");
  47. else
  48. DBG("w25q ioctl success.\n");
  49. return ret;
  50. }

说明:
        1) 目前共支持5个命令,包括sector擦除,half block擦除,block擦除,芯片擦除和读取芯片ID。
        2) 前三种擦除方式共用一个函数w25q_erase(w25q, get_value, cmd);,程序如下:

点击(此处)折叠或打开

  1. static int w25q_erase(struct w25q_dev *w25q, u32 num, unsigned int cmd)
  2. {
  3. int ret = 0;
  4. u8 *buf_start;
  5. switch(cmd) {
  6. case W25Q_SECTOR_ERASE:
  7. DBG("sector erase cmd\n");
  8. if (num > W25Q_SECTOR_MAX) {
  9. DBG("!!!!sector max is over\n");
  10. return -EFAULT;
  11. }
  12. w25q->const_addr = num * W25Q_ONE_SECTOR_ADDR;
  13. w25q->cmd = W25X_SECTOR_ERASE_CMD;
  14. break;
  15. case W25Q_HALF_BLOCK_ERASE:
  16. DBG("half block erase cmd\n");
  17. if (num > W25Q_HALF_BLOCK_MAX) {
  18. DBG("!!!!half block max is over\n");
  19. return -EFAULT;
  20. }
  21. w25q->const_addr = num * W25Q_HALF_BLOCK_ADDR;
  22. w25q->cmd = W25X_HALF_BLOCK_ERASE_CMD;
  23. break;
  24. case W25Q_BLOCK_ERASE:
  25. DBG("block erase cmd\n");
  26. if (num > W25Q_BLOCK_MAX) {
  27. DBG("!!!!block max is over\n");
  28. return -EFAULT;
  29. }
  30. w25q->const_addr = num * W25Q_ONE_BLOCK_ADDR;
  31. w25q->cmd = W25X_BLOCK_ERASE_CMD;
  32. break;
  33. }
  34. DBG("w25q->const_addr = 0x%x\n", w25q->const_addr);
  35. w25q->len = 0;
  36. buf_start = w25q->buf = kzalloc(w25q->len + 4, GFP_KERNEL);
  37. if (!buf_start) {
  38. DBG("!!!!kzalloc is error\n");
  39. return -ENOMEM;
  40. }
  41. w25q->addr = w25q->const_addr;
  42. ret = w25q_write_date(w25q);
  43. kfree(buf_start);
  44. if (ret != 0) {
  45. DBG("!!!!w25q_erase: spi write err!\n");
  46. return ret;
  47. }
  48. DBG("w25q_erase: erase OK\n");
  49. return ret;
  50. }

说明:
        1) 首先根据不同的擦除方式,设置命令和地址两个变量。
        2) 调用w25q_write_date(w25q);函数实现数据传输。
        芯片擦除函数w25q_chip_erase()如下:

点击(此处)折叠或打开

  1. static int w25q_chip_erase(struct w25q_dev *w25q)
  2. {
  3. int ret = 0;
  4. u8 cmd = W25X_CHIP_ERASE;
  5. DBG("w25q_chip_erase\n");
  6. w25q_write_enable(w25q);    //SET WEL
  7. ret = w25q_wait_null(w25q);
  8. if (ret != 0) {
  9. DBG("!!!!chip_erase: wait null err!\n");
  10. return ret;
  11. }
  12. if((w25q_read_stat_reg(w25q) & 0x02) != 0x02) {
  13. DBG("!!!!state register write able is 0\n");
  14. return -EBUSY;    //disable write
  15. }
  16. spi_w8r8(w25q->spi, cmd);
  17. return w25q_wait_null(w25q);
  18. }

读取设备ID函数w25q_read_id()如下:

点击(此处)折叠或打开

  1. static int w25q_read_id(struct w25q_dev *w25q)
  2. {
  3. int ret = 0;
  4. u8 *buf_start;
  5. DBG("w25q_read_id\n");
  6. w25q->len = 2;
  7. w25q->addr = 0;
  8. w25q->cmd = W25X_READ_ID_CMD;
  9. buf_start = w25q->buf = kzalloc(w25q->len, GFP_KERNEL);
  10. if (!buf_start) {
  11. DBG("!!!!kzalloc is error\n");
  12. return -ENOMEM;
  13. }
  14. ret = w25q_read_data(w25q);
  15. w25q->buf = buf_start;
  16. w25q->result = *w25q->buf << 8;
  17. w25q->buf++;
  18. w25q->result |= *w25q->buf;
  19. kfree(buf_start);
  20. if (ret != 0) {
  21. DBG("!!!!w25q_read_id: w25q_read_data error!\n");
  22. return ret;
  23. }
  24. DBG("w25q_read_id: read id OK\n");
  25. return ret;
  26. }

说明:
        1) 首先设置好变量,申请内存
        2) 调用w25q_read_data()函数实现读取数据。
        w25q_read_data函数如下:

点击(此处)折叠或打开

  1. /*
  2. * when you call this function,
  3. * the w25q->cmd, w25q->len(receive len)
  4. * w25q->buf(kzalloc receive) and w25q->addr are OK
  5. *
  6. */
  7. static int w25q_read_data(struct w25q_dev *w25q)
  8. {
  9. int ret = 0;
  10. struct spi_message message;
  11. struct spi_transfer    x[(w25q->len + 4) * 2];
  12. u8 i = 0, rx = 0, dumy_value = 0xff, tx_buff[4] = {0};
  13. w25q_write_enable(w25q);    //SET WEL
  14. ret = w25q_wait_null(w25q);
  15. if (ret != 0) {
  16. DBG("!!!!chip_erase: wait null err!\n");
  17. return ret;
  18. }
  19. if((w25q_read_stat_reg(w25q) & 0x02) != 0x02) {
  20. DBG("!!!!state register write able is 0\n");
  21. return -EBUSY;    //disable write
  22. }
  23. DBG("cmd = 0x%x, addr = 0x%x\n", w25q->cmd, w25q->addr);
  24. tx_buff[0] = w25q->cmd;
  25. tx_buff[1] = ((uint8_t)(w25q->addr >> 16));
  26. tx_buff[2] = ((uint8_t)(w25q->addr >> 8));
  27. tx_buff[3] = ((uint8_t)(w25q->addr));
  28. spi_message_init(&message);
  29. memset(x, 0, sizeof x);
  30. for (i = 0; i < 8; i++) {    //cmd
  31. x[i].len = 1;
  32. spi_message_add_tail(&x[i], &message);
  33. if ((i % 2) == 0) {
  34. x[i].tx_buf = &tx_buff[i / 2];
  35. } else {
  36. x[i].rx_buf = &rx;
  37. }
  38. }
  39. for (i = 8; i < (w25q->len + 4) * 2; i++) {
  40. x[i].len = 1;
  41. spi_message_add_tail(&x[i], &message);
  42. if ((i % 2) == 0) {
  43. x[i].tx_buf = &dumy_value;
  44. } else {
  45. x[i].rx_buf = w25q->buf++;
  46. }
  47. }
  48. /* do the i/o */
  49. return spi_sync(w25q->spi, &message);
  50. }

说明:
        1) 在从芯片读取数据时的格式为:首先发送一个字节命令+三个字节读取地址,然后就可以接收数据了。
        2) 第一个for循环发送的是命令和地址,第二个for循环是接收数据。
        3) 调用此函数之前,需要设置好w25q->cmd, w25q->len(receive len),w25q->buf(kzalloc receive)和w25q->addr。
        接下来看下函数操作集中的读数据函数w25q_read(),程序如下:

点击(此处)折叠或打开

  1. static ssize_t w25q_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos)
  2. {
  3. int ret = 0;
  4. u8 *buf_start, *buf_tmp, *w25q_buf;
  5. struct w25q_dev *w25q = file->private_data;
  6. u32 buf_size = 0, read_len = 0, page_num = W25Q_PAGE_NUM;
  7. DBG("@@@@w25q read start\n");
  8. buf_start = buf_tmp = kzalloc(W25Q_BUF_LEN, GFP_KERNEL);
  9. w25q_buf = w25q->buf = kzalloc(page_num, GFP_KERNEL);
  10. if (!buf_start || !w25q_buf ) {
  11. DBG("!!!!kzalloc error!\n");
  12. return -ENOMEM;
  13. }
  14. ret = mutex_lock_interruptible(&w25q->mlock);
  15. if (ret) {
  16. DBG("!!!!mutex lock error!\n");
  17. goto exit_kfree;
  18. }
  19. read_len = W25Q_BUF_LEN;
  20. buf_size = min(count, read_len);
  21. read_len = buf_size;
  22. w25q->cmd = W25X_READ_DATA;
  23. w25q->addr = w25q->const_addr;
  24. DBG("w25q->addr = 0x%x\n", w25q->addr);
  25. while (buf_size) {
  26. w25q->buf = w25q_buf;
  27. w25q->len = min(buf_size, page_num);
  28. ret = w25q_read_data(w25q);
  29. if (ret != 0) {
  30. goto exit_lock;
  31. }
  32. memcpy(buf_tmp, w25q_buf, w25q->len);
  33. buf_tmp += w25q->len;
  34. buf_size -= w25q->len;
  35. w25q->addr += w25q->len;
  36. }
  37. ret = copy_to_user(user_buf, buf_start, read_len);
  38. ret = read_len -ret;
  39. exit_lock:
  40. mutex_unlock(&w25q->mlock);
  41. exit_kfree:
  42. kfree(buf_start);
  43. kfree(w25q_buf);
  44. DBG("w25q read stop, ret = %d\n", ret);
  45. return ret;
  46. }

说明:
        1) 此函数需要申请两段内存空间,第一段用于存放从W25Q32BV接收到的数据,第二段用于存放每次接收的数据。
        2) 设置好变量后,调用w25q_read_data(w25q)读取数据。
        3) 读取完成后,将读取到的数据拷贝到应用层。

三、应用层测试

应用层测试程序如下:

点击(此处)折叠或打开

  1. /*
  2. * first you must erase,
  3. * then write, then read
  4. * or you can read only
  5. *
  6. */
  7. #include "w25q.h"
  8. int main(int argc, char **argv)
  9. {
  10. char str[10] = {0};
  11. int fd = 0, ret = 0;
  12. unsigned char buffer[BUFSIZE] = {0};
  13. unsigned int i = 0, idCmd = 0, num = 0;
  14. fd = open("/dev/w25q", O_RDWR);
  15. if (fd < 0) {
  16. printf("Open ADC Device Faild!\n");
  17. exit(1);
  18. }
  19. while(1) {
  20. idCmd = 0;
  21. printf("please enter the cmd and num :\n");
  22. scanf("%s%x", str, &num);
  23. //printf("cmd = %s, idFreq = %d\n", str, idFreq);
  24. if (num >= 0) {
  25. if (strcmp(str, "SECTOR") == 0) {
  26. idCmd = W25Q_SECTOR_ERASE;
  27. ret = ioctl(fd, idCmd, &num);
  28. if (ret != 0) {
  29. printf("sector erase Faild!\n");
  30. }
  31. } else if(strcmp(str, "HALF") == 0) {
  32. idCmd = W25Q_HALF_BLOCK_ERASE;
  33. ret = ioctl(fd, idCmd, &num);
  34. if (ret != 0) {
  35. printf("half block erase Faild!\n");
  36. }
  37. } else if(strcmp(str, "BLOCK") == 0) {
  38. idCmd = W25Q_BLOCK_ERASE;
  39. ret = ioctl(fd, idCmd, &num);
  40. if (ret != 0) {
  41. printf("block erase Faild!\n");
  42. }
  43. } else if(strcmp(str, "CHIP") == 0) {
  44. idCmd = W25Q_CHIP_ERASE;
  45. ret = ioctl(fd, idCmd, &num);
  46. if (ret != 0) {
  47. printf("chip erase Faild!\n");
  48. }
  49. } else if(strcmp(str, "ID") == 0) {
  50. idCmd = W25Q_READ_DEVICE_ID;
  51. ret = ioctl(fd, idCmd, &num);
  52. if (ret != 0) {
  53. printf("read ID Faild!\n");
  54. } else {
  55. printf("ID = 0x%x\n", num);
  56. }
  57. } else if(strcmp(str, "READ") == 0) {
  58. memset(buffer, 0, BUFSIZE);
  59. printf("------------\n");
  60. for (i = 0; i < WRITE_NUM; i++) {
  61. if((i != 0) && ((i % 8) == 0)) {
  62. printf("\n");
  63. }
  64. printf("0x%x ", buffer[i]);
  65. }
  66. printf("\n------------\n");
  67. ret = read(fd, buffer, WRITE_NUM);
  68. printf("\n------------\n");
  69. for (i = 0; i < WRITE_NUM; i++) {
  70. if((i != 0) && ((i % 8) == 0)) {
  71. printf("\n");
  72. }
  73. printf("0x%x ", buffer[i]);
  74. }
  75. printf("\n------------\n");
  76. } else if(strcmp(str, "WRITE") == 0) {
  77. for (i = 0; i < WRITE_NUM; i++) {
  78. buffer[i] = i;
  79. }
  80. ret = write(fd, buffer, WRITE_NUM);
  81. if (ret != 0) {
  82. printf("w25q write oper Faild!\n");
  83. }
  84. } else if(strcmp(str, "QUIT") == 0) {
  85. break;
  86. } else {
  87. printf("wrong string\n");
  88. }
  89. } else {
  90. printf("wrong input num(< 0)\n");
  91. }
  92. }/* end while(1) */
  93. close(fd);
  94. return 0;
  95. }

说明:
        1) 首先从终端接收命令内容。
        2) 比较命令,然后进入不同的处理流程。

四、测试演示

4.1、读取芯片ID

4.2、读写芯片

Linux spi驱动分析----SPI设备驱动(W25Q32BV)相关推荐

  1. linux内核组件分析之--设备驱动模型之bus

    前面我们分析了设备驱动模型中的device和driver,device和driver本来是不相关的东西,只因为bus的存在,才被联系到了一起.本节就来看看设备驱动模型中起枢纽作用的bus.本节的头文件 ...

  2. linux内核部件分析之——设备驱动模型之class

    前面看过了设备驱动模型中的bus.device.driver,这三种都是有迹可循的.其中bus代表实际的总线,device代表实际的设备和接口,而driver则对应存在的驱动.但本节要介绍的class ...

  3. linux内核部件分析(十)——设备驱动模型之class,linux内核部件分析(十)——设备驱动模型之class...

    前面看过了设备驱动模型中的bus.device.driver,这三种都是有迹可循的.其中bus代表实际的总线,device代表实际的设备和接口,而driver则对应存在的驱动.但本节要介绍的class ...

  4. Linux I2C子系统分析-I2C设备驱动

    接下来以一个实际的例子来看I2C设备驱动,就以drivers/i2c/i2c-dev.c为例. 先看它的初始化和注销函数 [cpp] view plaincopy static int __init  ...

  5. linux下camera驱动分析_LINUX设备驱动模型分析之三 驱动模块相关(DRIVER)接口分析...

    本系列前几篇文章链接如下: <LINUX设备驱动模型分析之一 总体概念说明> <LINUX设备驱动模型分析之二 总线(BUS)接口分析> 上一章我们分析了bus-driver- ...

  6. 由串口驱动分析rt-thread设备驱动框架

    内核对象管理框架简析 rt-thread中,线程.信号量.互斥锁.设备等等都是"内核对象",_object_container是一个数组,包含所有的内核对象信息. 对象信息的定义 ...

  7. Linux SPI驱动框架(3)——设备驱动层

    SPI设备驱动层   Linux SPI驱动框架(1)和(2)中分别介绍了SPI框架中核心层,和控制器驱动层.其实实际开发过程中,不是IC原厂工程师比较少会接触控制器驱动层,设备驱动层才是接触比较多的 ...

  8. linux i2c核心,总线与设备驱动,Linux2.6.37 I2C驱动框架分析(一)

    最近工作中又使用到了I2C,所以借S3C2440开发板GT2440为硬件平台温习一遍I2C驱动体系. linux内核中IIC驱动的体系框架 linux内核中IIC部分驱动代码位于:/drivers/i ...

  9. 一起分析Linux系统设计思想——05字符设备驱动框架剖析(四)

    在学习资料满天飞的大环境下,知识变得非常零散,体系化的知识并不多,这就导致很多人每天都努力学习到感动自己,最终却收效甚微,甚至放弃学习.我的使命就是过滤掉大量的垃圾信息,将知识体系化,以短平快的方式直 ...

最新文章

  1. 使用Python,OpenCV线程化方式提高视频FPS(每秒帧数)
  2. 【转】VS2008制作打包程序将安装路径写入注册表
  3. php flock 超时,php flock失效问题
  4. Log4net 中输出日志到文件,文件名根据日期生成
  5. UGUI_UGUI组件属性
  6. centos 6安装报错
  7. 【熊猫多模式站群开发日志】权限控制拦截器
  8. hdu1141 Factstone Benchmark(利用对数进行大数比较)
  9. 【论文分享】ACL 2020 社交网络谣言判别中可解释性相关研究
  10. linux下安装不同版本的jdk
  11. matlab 求矩阵各行的平均值
  12. 教程 - 【超详细】从零开始部署网站——阿里云主机CentOS系统
  13. Flask+ZUI 开发小型工具网站(四)——ZUI 前端主页面 tabs
  14. 你能为公司带来什么?
  15. 新手也可以制作出大片的效果——Movavi Video Editor 15 Plus Mac
  16. 乐优商城之后台管理系统的环境搭建(七)
  17. 是什么意思怎么读_知识丨南无怎么读?是什么意思?
  18. 两个必须更新浏览器的理由,危险就在你身边
  19. SQL如何进行帕累托分析?(窗口函数、累计百分比分类)
  20. Tensorflow (6) Attention 注意力机制

热门文章

  1. U盘重装Win10系统教程
  2. 微信群控系统的实现原理,微信群控系统源码的核心实现代码
  3. 微信群控的服务器怎么用,微信群控开发SDK使用教程--手机客户端返回聊天消息的原始内容给服务端...
  4. 如何在 JavaScript 中使用对象解构
  5. ios 高德获取定位_更新日志-iOS 定位SDK | 高德地图API
  6. 流量直升机-千牛插件功能列表
  7. 室内导航将成为杀手级应用
  8. 韩国程序员面试考什么?
  9. 新东方校长俞敏洪给我们的告诫。
  10. 临床执业助理医师(综合练习)题库【8】