Linux SPI驱动学习——调用SPI读写函数

  • 博客说明
  • 开发环境
  • 1. 基于W25Q16型FLASH来看SPI的读写函数
  • 附录

博客说明

撰写日期 2019.10.25
完稿日期 未完稿
最近维护 暂无
本文作者 multimicro
联系方式 multimicro@qq.com
资料链接 本文无附件资料
GitHub https://github.com/wifialan/drivers/blob/master/w25q16_spi.c
原文链接 https://blog.csdn.net/multimicro/article/details/102738026

开发环境

环境说明 详细信息 备注信息
操作系统 Ubunut 18.04
开发板 JZ2440-V3
Linux内核 linux-3.4.2

1. 基于W25Q16型FLASH来看SPI的读写函数

这里将w25q16注册为了一个mtd设备,和字符设备最大不同点就是,不同的设备类型中的方法不同,对于flash硬件,注册为mtd设备会更加便于操作。这里给出源码供分析参考:

/**  Driver for S3C2440 base board.**  Copyright (C) 2019  Alan NWPU <alantian.at@gmail.com>**  This program is free software; you can redistribute it and/or modify*  it under the terms of the GNU General Public License as published by*  the Free Software Foundation; either version 2 of the License, or*  (at your option) any later version.**  This program is distributed in the hope that it will be useful,*  but WITHOUT ANY WARRANTY; without even the implied warranty of*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the*  GNU General Public License for more details.**  You should have received a copy of the GNU General Public License*  along with this program; if not, write to the Free Software*  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA*  MULTIBEANS, NPU Youyi West Ave, Beilin District, Xi'an, China.*/#include <linux/module.h>   /* Every Linux kernel module must include this head */
#include <linux/init.h>     /* Every Linux kernel module must include this head */
#include <linux/kernel.h>   /* printk() */
#include <linux/fs.h>       /* struct fops */
#include <linux/errno.h>    /* error codes */
#include <linux/delay.h>
#include <linux/moduleparam.h>
#include <linux/spi/spi.h>#include <linux/types.h>
#include <linux/gpio.h>
#include <linux/types.h>
#include <linux/irq.h>
#include <linux/slab.h>
#include <linux/uaccess.h>#include <linux/interrupt.h>
#include <linux/pm.h>
#include <linux/device.h>
#include <asm/irq.h>
#include <mach/gpio-nrs.h>
#include <mach/regs-gpio.h>
#include <mach/gpio.h>
#include <mach/hardware.h>
#include <plat/gpio-cfg.h>
#include <linux/spi/spi.h>#include <linux/mtd/cfi.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/partitions.h>#define DRV_NAME "s3c2440_w25q16"
#define DRV_AUTHOR "Alan Tian <alantian.at@gmail.com>"
#define DRV_DESC "S3C2440 W25Q16 Driver"static int w25q16_bus_spi_probe(struct spi_device *spi);
static int w25q16_spi_remove(struct spi_device *spi);struct spi_master *spi_master_p;
struct spi_device *spi_flash;
struct spi_message *spi_mesg_p;void SPIFlashReadID(int *pMID, int *pDID)
{unsigned char tx_buf[4];unsigned char rx_buf[2];tx_buf[0] = 0x90;tx_buf[1] = 0;tx_buf[2] = 0;tx_buf[3] = 0;spi_write_then_read(spi_flash, tx_buf, 4, rx_buf, 2);       //先发送tx_buf数组里面的4个字节,然后在读取2个字节并存入rx_buf中*pMID = rx_buf[0];*pDID = rx_buf[1];
}static void SPIFlashWriteEnable(int enable)
{unsigned char val = enable ? 0x06 : 0x04;spi_write(spi_flash, &val, 1);
}static unsigned char SPIFlashReadStatusReg1(void)
{unsigned char val;unsigned char cmd = 0x05;spi_write_then_read(spi_flash, &cmd, 1, &val, 1);return val;
}static unsigned char SPIFlashReadStatusReg2(void)
{unsigned char val;unsigned char cmd = 0x35;spi_write_then_read(spi_flash, &cmd, 1, &val, 1);return val;
}static void SPIFlashWaitWhenBusy(void)
{while (SPIFlashReadStatusReg1() & 1){/* 休眠一段时间 *//* Sector erase time : 60ms* Page program time : 0.7ms* Write status reg time : 10ms*/set_current_state(TASK_INTERRUPTIBLE);schedule_timeout(HZ/100);  /* 休眠10MS后再次判断 */}
}static void SPIFlashWriteStatusReg(unsigned char reg1, unsigned char reg2)
{    unsigned char tx_buf[4];SPIFlashWriteEnable(1);  tx_buf[0] = 0x01;tx_buf[1] = reg1;tx_buf[2] = reg2;spi_write(spi_flash, tx_buf, 3);   SPIFlashWaitWhenBusy();
}static void SPIFlashClearProtectForStatusReg(void)
{unsigned char reg1, reg2;reg1 = SPIFlashReadStatusReg1();reg2 = SPIFlashReadStatusReg2();reg1 &= ~(1<<7);reg2 &= ~(1<<0);SPIFlashWriteStatusReg(reg1, reg2);
}static void SPIFlashClearProtectForData(void)
{/* cmp=0,bp2,1,0=0b000 */unsigned char reg1, reg2;reg1 = SPIFlashReadStatusReg1();reg2 = SPIFlashReadStatusReg2();reg1 &= ~(7<<2);reg2 &= ~(1<<6);SPIFlashWriteStatusReg(reg1, reg2);
}/* erase 4K */
void SPIFlashEraseSector(unsigned int addr)
{unsigned char tx_buf[4];tx_buf[0] = 0x20;tx_buf[1] = addr >> 16;tx_buf[2] = addr >> 8;tx_buf[3] = addr & 0xff;SPIFlashWriteEnable(1);  spi_write(spi_flash, tx_buf, 4);SPIFlashWaitWhenBusy();
}/* program */
void SPIFlashProgram(unsigned int addr, unsigned char *buf, int len)
{unsigned char tx_buf[4];   struct spi_transfer t[] = {{.tx_buf        = tx_buf,.len      = 4,},{.tx_buf     = buf,.len     = len,},};struct spi_message   m;tx_buf[0] = 0x02;tx_buf[1] = addr >> 16;tx_buf[2] = addr >> 8;tx_buf[3] = addr & 0xff;SPIFlashWriteEnable(1);  spi_message_init(&m);spi_message_add_tail(&t[0], &m);spi_message_add_tail(&t[1], &m);spi_sync(spi_flash, &m);SPIFlashWaitWhenBusy();
}void SPIFlashRead(unsigned int addr, unsigned char *buf, int len)
{/* spi_write_then_read规定了tx_cnt+rx_cnt < 32* 所以对于大量数据的读取,不能使用该函数*/unsigned char tx_buf[4];   struct spi_transfer    t[] = {{.tx_buf        = tx_buf,.len      = 4,},{.rx_buf     = buf,.len     = len,},};struct spi_message   m;tx_buf[0] = 0x03;tx_buf[1] = addr >> 16;tx_buf[2] = addr >> 8;tx_buf[3] = addr & 0xff;spi_message_init(&m);spi_message_add_tail(&t[0], &m);spi_message_add_tail(&t[1], &m);spi_sync(spi_flash, &m);
}static void SPIFlashInit(void)
{SPIFlashClearProtectForStatusReg();SPIFlashClearProtectForData();
}/* 构造注册一个mtd_info* mtd_device_register(master, parts, nr_parts)**//* 首先: 构造注册spi_driver* 然后: 在spi_driver的probe函数里构造注册mtd_info*/static struct mtd_info spi_flash_dev;static int spi_flash_erase(struct mtd_info *mtd, struct erase_info *instr)
{unsigned int addr = instr->addr;unsigned int len  = 0;/* 判断参数 */if ((addr & (spi_flash_dev.erasesize - 1)) || (instr->len & (spi_flash_dev.erasesize - 1))){printk("addr/len is not aligned\n");return -EINVAL;}for (len = 0; len < instr->len; len += 4096){SPIFlashEraseSector(addr);addr += 4096;}instr->state = MTD_ERASE_DONE;mtd_erase_callback(instr);return 0;
}static int spi_flash_read(struct mtd_info *mtd, loff_t from, size_t len,size_t *retlen, u_char *buf)
{SPIFlashRead(from, buf, len);*retlen = len;return 0;
}static int spi_flash_write(struct mtd_info *mtd, loff_t to, size_t len,size_t *retlen, const u_char *buf)
{unsigned int addr = to;unsigned int wlen  = 0;/* 判断参数 */if ((to & (spi_flash_dev.erasesize - 1)) || (len & (spi_flash_dev.erasesize - 1))){printk("addr/len is not aligned\n");return -EINVAL;}for (wlen = 0; wlen < len; wlen += 256){SPIFlashProgram(addr, (unsigned char *)buf, 256);addr += 256;buf += 256;}*retlen = len;return 0;
}static struct spi_driver w25q16_spi_driver =
{.driver     ={.name   = "w25q16",.owner  = THIS_MODULE,},.probe      = w25q16_bus_spi_probe,.remove     = __devexit_p(w25q16_spi_remove),
};static int __devinit w25q16_bus_spi_probe(struct spi_device *spi)
{ int mid, did;printk("w25q16 probe function\n");spi_flash = spi;s3c2410_gpio_cfgpin(spi->chip_select, S3C2410_GPIO_OUTPUT);SPIFlashInit();SPIFlashReadID(&mid, &did);printk("SPI Flash ID: %02x %02x\n", mid, did);memset(&spi_flash_dev, 0, sizeof(spi_flash_dev));/* 构造注册一个mtd_info* mtd_device_register(master, parts, nr_parts)**//* Setup the MTD structure */spi_flash_dev.name = "w25q16_flash";spi_flash_dev.type = MTD_NORFLASH;spi_flash_dev.flags = MTD_CAP_NORFLASH;spi_flash_dev.size = 0x200000;  /* 2M */spi_flash_dev.writesize = 1;spi_flash_dev.writebufsize = 4096; /* 没有用到 */spi_flash_dev.erasesize = 4096;  /* 擦除的最小单位 */spi_flash_dev.owner = THIS_MODULE;spi_flash_dev._erase = spi_flash_erase;spi_flash_dev._read  = spi_flash_read;spi_flash_dev._write = spi_flash_write;mtd_device_register(&spi_flash_dev, NULL, 0);return 0;}static int w25q16_spi_remove(struct spi_device *spi)
{mtd_device_unregister(&spi_flash_dev);return 0;}static int __init w25q16_driver_init(void)
{int ret;printk("\n************ driver init begin ************\n\n");ret = spi_register_driver(&w25q16_spi_driver);if(ret){spi_unregister_driver(&w25q16_spi_driver);printk(DRV_NAME "\tFailed register spi driver. Error: %d\n",ret);}printk("\n************* driver init end *************\n\n");return ret;
}static void __exit w25q16_driver_exit(void)
{printk("Unregister w25q16 flash!\n");
//    misc_deregister(&w25q16_miscdev);spi_unregister_driver(&w25q16_spi_driver);
}module_init(w25q16_driver_init);
module_exit(w25q16_driver_exit);MODULE_AUTHOR( DRV_AUTHOR );
MODULE_DESCRIPTION(DRV_DESC);
MODULE_LICENSE("GPL");

附录

  1. Github: w25q16_spi.c

Linux SPI驱动学习——调用SPI读写函数相关推荐

  1. STM32CubeMX | 基于STM32使用HAL库硬件SPI驱动WK2124一拖四SPI转四路串口芯片

    STM32CubeMX | 基于STM32使用HAL库硬件SPI驱动WK2124一拖四SPI转四路串口芯片 STM32基础工程生成 首先使用STM32CUBEMX生成STM32的基础工程,配置时钟到7 ...

  2. linux v4l2系统详解,Linux摄像头驱动学习之:(一)V4L2_框架分析

    这段时间开始搞安卓camera底层驱动了,把以前的的Linux视频驱动回顾一下,本篇主要概述一下vfl2(video for linux 2). 一. V4L2框架: video for linux ...

  3. ​Linux下C如何调用PCI Lib函数

    Linux下C如何调用PCI Lib函数 在Linux下,可以通过"setpci"和"setpci"命令来访问PCI设备的配置空间,那么能否用程序来访问PCI ...

  4. 和菜鸟一起学linux总线驱动之初识spi驱动数据传输流程【转】

    转自:http://blog.csdn.net/eastmoon502136/article/details/7921846 对于SPI的一些结构体都有所了解之后呢,那么再去瞧瞧SPI的那些长见的操作 ...

  5. Linux spi驱动分析(四)----SPI设备驱动(W25Q32BV)

    一.W25Q32BV芯片简介 W25X是一系列SPI接口Flash芯片的简称,它采用SPI接口和CPU通信,本文使用的W25Q32BV容量为32M,具体特性如下: 1.1.基本特性 该芯片最大支持10 ...

  6. nuc972 spi驱动修改提升SPI nor flash读写性能

    在nuc972板子上测试SPI noflash的时候发现启动比nand慢很多,从上电到进入console用了60秒. 通过抓SPI波形发现两个字节之间间隔有5us, 而发送一个字节所花费的时间只有1u ...

  7. USB U盘Linux mass_storage驱动学习笔记(1)

    学习博客地址:https://blog.csdn.net/fudan_abc 学习书名:<Linux那些事之我是USB> 看代码之前,我曾经认真的思考过这么一个问题,我需要关注的仅仅是dr ...

  8. Linux DMA 驱动学习总结

    Linux DMA驱动构架分析 以linux2.6.32中的S3C2440驱动为例进行分析,DMA驱动所对应的源码为linux-2.6.32.2\arch \arm\mach-s3c2440\dma. ...

  9. linux设备驱动学习,linux设备驱动学习4

    Linux设备驱动程序学习(4) -高级字符驱动程序操作[(1)ioctl and llseek] 今天进入<Linux设备驱动程序(第3版)>第六章高级字符驱动程序操作的学习. 一.io ...

最新文章

  1. Spring环境搭建,IoC容器初体验~
  2. 这个登上Nature封面的「群体学习」,无需中央协调员,比联邦学习更优秀
  3. Calendar类的常用成员方法
  4. 2017计算机应用技术考研,2017年中国科学技术大学081203计算机应用技术考研专业目录与考试科目.docx...
  5. ajax 偶尔302,关于Ajax 中response出现302的一点见解
  6. [转]DotNet C#开源资源汇总
  7. day11 内置函数
  8. rabbitmqBat常用指令
  9. 2015-2016前端知识体系(转)
  10. matlab单枝节匹配器,第八讲微带匹配电路单枝节匹配电路.ppt
  11. MD5以及SHA加密不在安全的分析
  12. 球面坐标系与三角函数 Spherical Coordinates and Trigonometric Functions
  13. 高中信息技术简答题汇总
  14. 长文:读《经济学32定律》
  15. 王学岗移动架构34——IOC注入框架设计
  16. 给图片加水印的简单方法,手机图片加水印也可以用
  17. 网络存储技术Windows server 2012 (项目二 动态磁盘的配置与管理)
  18. 二叉搜索树的插入,删除,和中序遍历
  19. 一个简单的视频播放器
  20. mysql 分表和分区_Mysql分表和分区的区别

热门文章

  1. 算法-回溯backtrack
  2. dwz ajax提交,DWZ-JUI Ajax.post 封装
  3. 岭回归实现鲍鱼年龄预测 MATLAB实现
  4. 制作mac os x lion启动盘
  5. 1032 挖掘机技术哪家强 (20分) 测试点4
  6. Python 解密 pdf 文件
  7. SAP所有模块用户出口(User Exits) 一
  8. html5 video speed control插件,谷歌浏览器插件Video Speed Manager 视频速度控制插件
  9. 游戏同步方案——帧同步
  10. 提车二月记--小鹏P7