linux内核SPI总线驱动分析(一)

2023-11-05

原文地址:http://www.cnblogs.com/liugf05/archive/2012/12/03/2800457.html

SPI总线驱动分析

 

1 SPI概述
      SPI是英语Serial Peripheral interface的缩写,顾名思义就是串行外围设备接口,是Motorola首先在其MC68HCXX系列处理器上定义的。SPI接口主要应用在 EEPROM,FLASH,实时时钟,AD转换器,还有数字信号处理器和数字信号解码器之间。SPI是一种高速的,全双工,同步的通信总线,并且在芯片的管脚上只占用四根线,节约了芯片的管脚,同时为PCB的布局上节省空间,提供方便。
      SPI的通信原理很简单,它以主从方式工作,这种模式通常有一个主设备和一个或多个从设备,需要4根线,事实上3根也可以。也是所有基于SPI的设备共有的,它们是SDI(数据输入),SDO(数据输出),SCLK(时钟),CS(片选)。
      MOSI(SDO):主器件数据输出,从器件数据输入。
      MISO(SDI):主器件数据输入,从器件数据输出。
      SCLK :时钟信号,由主器件产生。
      CS:从器件使能信号,由主器件控制。
      其中CS是控制芯片是否被选中的,也就是说只有片选信号为预先规定的使能信号时(高电位或低电位),对此芯片的操作才有效,这就允许在同一总线上连接多个SPI设备成为可能。需要注意的是,在具体的应用中,当一条SPI总线上连接有多个设备时,SPI本身的CS有可能被其他的GPIO脚代替,即每个设备的CS脚被连接到处理器端不同的GPIO,通过操作不同的GPIO口来控制具体的需要操作的SPI设备,减少各个SPI设备间的干扰。
      SPI是串行通讯协议,也就是说数据是一位一位从MSB或者LSB开始传输的,这就是SCK时钟线存在的原因,由SCK提供时钟脉冲,MISO、MOSI则基于此脉冲完成数据传输。 SPI支持4-32bits的串行数据传输,支持MSB和LSB,每次数据传输时当从设备的大小端发生变化时需要重新设置SPI Master的大小端。

 

2 Linux SPI驱动总体架构
      在2.6的linux内核中,SPI的驱动架构可以分为如下三个层次:SPI 核心层、SPI控制器驱动层和SPI设备驱动层。
      Linux 中SPI驱动代码位于drivers/spi目录。
2.1 SPI核心层
      SPI核心层是Linux的SPI核心部分,提供了核心数据结构的定义、SPI控制器驱动和设备驱动的注册、注销管理等API。其为硬件平台无关层,向下屏蔽了物理总线控制器的差异,定义了统一的访问策略和接口;其向上提供了统一的接口,以便SPI设备驱动通过总线控制器进行数据收发。
      Linux中,SPI核心层的代码位于driver/spi/ spi.c。由于该层是平台无关层,本文将不再叙述,有兴趣可以查阅相关资料。
2.2 SPI控制器驱动层
      SPI控制器驱动层,每种处理器平台都有自己的控制器驱动,属于平台移植相关层。它的职责是为系统中每条SPI总线实现相应的读写方法。在物理上,每个SPI控制器可以连接若干个SPI从设备。
      在系统开机时,SPI控制器驱动被首先装载。一个控制器驱动用于支持一条特定的SPI总线的读写。一个控制器驱动可以用数据结构struct spi_master来描述。

   在include/liunx/spi/spi.h文件中,在数据结构struct spi_master定义如下:

 

  1. struct spi_master {  
  2.     struct device   dev;  
  3.     s16         bus_num;  
  1.     u16         num_chipselect;  
  2.     int         (*setup)(struct spi_device *spi);  
  3.     int         (*transfer)(struct spi_device *spi, struct spi_message *mesg);  
  4.     void        (*cleanup)(struct spi_device *spi);  
  5. };  

 

     bus_num为该控制器对应的SPI总线号。
      num_chipselect 控制器支持的片选数量,即能支持多少个spi设备 
      setup函数是设置SPI总线的模式,时钟等的初始化函数, 针对设备设置SPI的工作时钟及数据传输模式等。
在spi_add_device函数中调用。 
      transfer函数是实现SPI总线读写方法的函数。实现数据的双向传输,可能会睡眠

    cleanup注销时候调用

2.3 SPI设备驱动层
      SPI设备驱动层为用户接口层,其为用户提供了通过SPI总线访问具体设备的接口。
      SPI设备驱动层可以用两个模块来描述,struct spi_driver和struct spi_device。
      相关的数据结构如下:

 

  1. struct spi_driver {  
  2.     int         (*probe)(struct spi_device *spi);  
  3.     int         (*remove)(struct spi_device *spi);  
  4.     void            (*shutdown)(struct spi_device *spi);  
  5.     int         (*suspend)(struct spi_device *spi, pm_message_t mesg);  
  6.     int         (*resume)(struct spi_device *spi);  
  7.     struct device_driver    driver;  
  8. }; 

 

  Driver是为device服务的,spi_driver注册时会扫描SPI bus上的设备,进行驱动和设备的绑定,probe函数用于驱动和设备匹配时被调用。从上面的结构体注释中我们可以知道,SPI的通信是通过消息队列机制,而不是像I2C那样通过与从设备进行对话的方式。

 

  1. struct spi_device {  
  2.     struct device       dev;  
  3.     struct spi_master   *master;  
  4.     u32         max_speed_hz;  
  5.     u8          chip_select;  
  6.     u8          mode;    
  7.     u8          bits_per_word;  
  8.     int         irq;  
  9.     void            *controller_state;  
  10.     void            *controller_data;  
  11.     char            modalias[32];   
  12. }; 

 

        .modalias   = "m25p10",

        .mode   =SPI_MODE_0,   //CPOL=0, CPHA=0 此处选择具体数据传输模式

        .max_speed_hz    = 10000000, //最大的spi时钟频率

        /* Connected to SPI-0 as 1st Slave */

        .bus_num    = 0,   //设备连接在spi控制器0上

        .chip_select    = 0, //片选线号,在S5PC100的控制器驱动中没有使用它作为片选的依据,而是选择了下文controller_data里的方法。

        .controller_data = &smdk_spi0_csi[0],  

通常来说spi_device对应着SPI总线上某个特定的slave。并且spi_device封装了一个spi_master结构体。spi_device结构体包含了私有的特定的slave设备特性,包括它最大的频率,片选那个,输入输出模式等等

3 OMAP3630 SPI控制器
      OMAP3630上SPI是一个主/从的同步串行总线,这边有4个独立的SPI模块(SPI1,SPI2,SPI3,SPI4),各个模块之间的区别在于SPI1支持多达4个SPI设备,SPI2和SPI3支持2个SPI设备,而SPI4只支持1个SPI设备。

SPI控制器具有以下特征:
     1.可编程的串行时钟,包括频率,相位,极性。
     2.支持4到32位数据传输
     3.支持4通道或者单通道的从模式
     4.支持主的多通道模式
         4.1全双工/半双工
         4.2只发送/只接收/收发都支持模式
         4.3灵活的I/O端口控制
         4.4每个通道都支持DMA读写
     5.支持多个中断源的中断时间
     6.支持wake-up的电源管理
     7.内置64字节的FIFO

 

4 spi_device以下一系列的操作是在platform板文件中完成!

spi_device的板信息用spi_board_info结构体来描述:
struct spi_board_info {
char modalias[SPI_NAME_SIZE];
const void*platform_data;
void*controller_data;
intirq;
u32max_speed_hz;
u16bus_num;
u16chip_select;
u8 mode;
};

 

这个结构体记录了SPI外设使用的主机控制器序号、片选信号、数据比特率、SPI传输方式等

构建的操作是以下的两个步骤:

1.

 

static struct spi_board_info s3c_spi_devs[] __initdata = {

{

.modalias = "m25p10a",

.mode = SPI_MODE_0,

.max_speed_hz = 1000000,

.bus_num = 0,

.chip_select = 0,

.controller_data = &smdk_spi0_csi[SMDK_MMCSPI_CS],

},

};

2.

 

而这个info在init函数调用的时候会初始化:

spi_register_board_info(s3c_spi_devs,ARRAY_SIZE(s3c_spi_devs));

 

spi_register_board_info(s3c_spi_devs,ARRAY_SIZE(s3c_spi_devs));//注册spi_board_info。这个代码会把spi_board_info注册到链表board_list上。spi_device封装了一个spi_master结构体,事实上spi_master的注册会在spi_register_board_info之后,spi_master注册的过程中会调用scan_boardinfo扫描board_list,找到挂接在它上面的spi设备,然后创建并注册spi_device。

 

至此spi_device就构建并注册完成了!!!!!!!!!!!!!

 

5 spi_driver的构建与注册

 

driver有几个重要的结构体:spi_driver、spi_transfer、spi_message

driver有几个重要的函数    :spi_message_init、spi_message_add_tail、spi_sync

 

   //spi_driver的构建

static struct spi_driver   m25p80_driver = { 

.driver = {

        .name   ="m25p80",

        .bus    =&spi_bus_type,

        .owner  = THIS_MODULE,

    },

    .probe  = m25p_probe,

    .remove =__devexit_p(m25p_remove),

};

//spidriver的注册

 

spi_register_driver(&m25p80_driver);

在有匹配的spi_device时,会调用m25p_probe

 

probe里完成了spi_transfer、spi_message的构建;

spi_message_init、spi_message_add_tail、spi_sync、spi_write_then_read函数的调用

 

例如:

 

  1.  */  
  2. static int m25p10a_read( struct m25p10a *flash, loff_t from,   
  3.         size_t len, char *buf )  
  4. {  
  5.     int r_count = 0, i;  
  6.     struct spi_transfer st[2];  
  7.     struct spi_message  msg;  
  8.       
  9.     spi_message_init( &msg );  
  10.     memset( st, 0, sizeof(st) );  
  11.   
  12.     flash->cmd[0] = CMD_READ_BYTES;  
  13.     flash->cmd[1] = from >> 16;  
  14.     flash->cmd[2] = from >> 8;  
  15.     flash->cmd[3] = from;  
  16.   
  17.     st[ 0 ].tx_buf = flash->cmd;  
  18.     st[ 0 ].len = CMD_SZ;  
  19.     spi_message_add_tail( &st[0], &msg );  
  20.   
  21.     st[ 1 ].rx_buf = buf;  
  22.     st[ 1 ].len = len;  
  23.     spi_message_add_tail( &st[1], &msg );  
  24.   
  25.     mutex_lock( &flash->lock );  
  26.       
  27.     /* Wait until finished previous write command. */  
  28.     if (wait_till_ready(flash)) {  
  29.         mutex_unlock( &flash->lock );  
  30.         return -1;  
  31.     }  
  32.   
  33.     spi_sync( flash->spi, &msg );  
  34.     r_count = msg.actual_length - CMD_SZ;  
  35.     printk( "in (%s): read %d bytes\n", __func__, r_count );  
  36.     for( i = 0; i < r_count; i++ ) {  
  37.         printk( "0x%02x\n", buf[ i ] );  
  38.     }  
  39.   
  40.     mutex_unlock( &flash->lock );  
  41.     return 0;  
  42. }  
  43. static int m25p10a_write( struct m25p10a *flash, loff_t to,   
  44.         size_t len, const char *buf )  
  45. {  
  46.     int w_count = 0, i, page_offset;
  47.   
  48.     struct spi_transfer st[2]; 
  49.  
  50.     struct spi_message  msg;  
  51.     write_enable( flash );  //写使能  
  52.       

        spi_message_init( &msg );  

  1.     memset( st, 0, sizeof(st) );  
  2.   
  3.     flash->cmd[0] = CMD_PAGE_PROGRAM;  
  4.     flash->cmd[1] = to >> 16;  
  5.     flash->cmd[2] = to >> 8;  
  6.     flash->cmd[3] = to;  
  7.   
  8.     st[ 0 ].tx_buf = flash->cmd;  
  9.     st[ 0 ].len = CMD_SZ;  
  10.   //填充spi_transfer,将transfer放在队列后面
  11.     spi_message_add_tail( &st[0], &msg );  
  12.   
  13.     st[ 1 ].tx_buf = buf;  
  14.     st[ 1 ].len = len;  
  15.     spi_message_add_tail( &st[1], &msg );  
  16.   
  1.  
  2.         spi_sync( flash->spi, &msg );   调用spi_master发送spi_message
  3.     
  4.     return 0;  

 

 

  1.   
  2. static int m25p10a_probe(struct spi_device *spi)   
  3. {   
  4.     int ret = 0;  
  5.     struct m25p10a  *flash;  
  6.     char buf[ 256 ];  
  7.     flash = kzalloc( sizeof(struct m25p10a), GFP_KERNEL );  
  8.     flash->spi = spi;  
  9.     /* save flash as driver's private data */  
  10.     spi_set_drvdata( spi, flash );    
  1.     memset( buf, 0x7, 256 );  
  2.     m25p10a_write( flash, 0, 20, buf); //0地址写入20个7  
  3.     memset( buf, 0, 256 );  
  4.     m25p10a_read( flash, 0, 25, buf ); //0地址读出25个数  
  5.  
  6.     return 0;   
  7. }   

 

 

 

 

到目前为止,完成了SPI的驱动和应用


本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

linux内核SPI总线驱动分析(一) 的相关文章

  • 用Java实现简单计算器

    实训题 8 简单计算器 一 项目说明 实训目的 掌握 Java GUI 开发中的布局管理和事件处理机制 实训要求 1 要使用 java 的 GUI 设计出计算器界面 2 通过界面按钮 可以实现整数或浮点数的四则运算 并能将结果显示在界面中
  • 2020-01-24

    新年快乐 祝各位粉丝以及csdn的朋友 新年快乐 2020没有解决不了的问题 加油
  • Linux/Centos源码安装python3任意版本

    首先安装编译工具 sudo yum y install cmake3 glib2 devel libpcap libpcap devel libgcrypt devel glib2 devel qt devel qt5 qtbase dev
  • js小学生图区_多种方式实现js图片预览

    js多种方式图片预览 持续更新 设置自己的变量存储区 var Util file file image show img show Util file nchange function f if this files 0 type inde
  • IDEA 使用技巧

    1 idea展开折叠类中所有方法 展开 ctrl shift 折叠 ctrl shift 2 Idea中设置注释位置 自动缩进到合适位置
  • 【PID控制算法】

    PID控制小解 比例环节 P 成比例的反映偏差信号 偏差一旦产生 控制器立即产生控制作用 较小误差 积分环节 I 主要用于消除静态误差 提高系统得无差度 积分作用的强弱取决于积分时间常数 Ti Ti越 大 积分作用越弱 反之越强 微分环节
  • 如何改进研发项目文档内容质量?

    书接上回 研发项目文档重要吗 个人以为 改进研发项目文档内容质量 需要深入研发流程 梳理研发流程中的信息需求和依赖关系 完善相关的内容规范 模板 和评审规范 从系统上保障研发文档的质量 所以这段时间 协调痛点场景作为改进试点 参与到研发设计
  • 从控制理论的根轨迹法和稳定性分析谈到舵机PD控制代码实现

    在上一次谈到基于MPU6050的基于一阶互补滤波算法实现后 本来想接着就自适应一阶互补滤波和卡尔曼滤波再写一篇的 但是卡尔曼滤波算法我自己写出来并进行姿态解算后发现效果不很好 才疏学浅 等我调好了再写吧 昨天花了半下午做了一个基于MPU60

随机推荐

  • Visual Studio 2017 设置透明背景图

    一 前言 给大家分享一下 如何为VS2017设置透明背景图 下面是一张设置前和设置后的图片 设置前 设置后 二 设置背景图片的扩展程序 我们打开VS的扩展安装界面 工具 gt 扩展和更新 gt 联机 输入 Background 可以看到有两
  • Mysql偶尔连接失败的问题

    在项目中 偶尔会出现获取jdbc连接失败的报错 其中有一种因为连接失效的报错 The last packet successfully received from the server was 1 312 227 milliseconds
  • 时序预测:从两篇高影响力的论文谈起

    未来可以预测吗 本文从两篇高影响力的时序预测文章谈起 其中一篇是18年放在arXiv上的文章 文中总结性地提出了时序卷积网络 TCN Temporal Convolutional Network 短短两三年引用数已经破千 TCN作为一种基准
  • java常用集合之Map

    目录 Map集合基本内容 特点 扩容 遍历 Map集合实现 1 HashMap 基本原理 Table数组中的的Node 2 HashTable 3 ConcurrentHashMap 4 TreeMap 5 LinkedHashMap Ma
  • 关于启动fabric网络错误:cryptogen tool not found. exiting

    在启动fabric中first network网络时 需要执行一个自动化脚本 byfn sh m generate 该脚本需要自动生成相关证书信息 fabric官方提供了两款工具 configtxgen cryptogen 在脚本执行中会从
  • JUnit5学习之六:参数化测试(Parameterized Tests)基础

    项目主页 https github com zq2599 blog demos 该项目在GitHub上的主页 git仓库地址 https https github com zq2599 blog demos git 该项目源码的仓库地址 h
  • 断言(assert)的用法

    我一直以为 assert 仅仅是个报错函数 事实上 它居然是个宏 并且作用并非 报错 在经过对其进行一定了解之后 对其作用及用法有了一定的了解 assert 的用法像是一种 契约式编程 在我的理解中 其表达的意思就是 程序在我的假设条件下
  • MySQL8.0.26安装配置教程(windows 64位)

    目录 一 进入MySQL官网下载安装 二 配置并初始化MySQL 三 配置环境变量 四 检验安装 一 进入MySQL官网下载安装 进入Mysql官网MySQL 点击DOWNLOADS下拉页面点击红框内容 跳转页面后点击红框 下载后并解压到目
  • 8个Python免费网站,一周熟练Python,知道就是赚到!

    前言 Python 已经成为一种再主流不过的编程语言了 许多同学开始学习它 又不知道该如何入手 希望在一周内学习最强大和最流行的编程语言之一 是的 你读得对 如果你有奉献精神 你可以在一周内学习Python 今天呢 我给大家推荐八个免费学习
  • 教你如何有效防止DDos攻击?

    DDos又称分布式拒绝服务 DDos是利用大量合理的请求造成资源过载 导致服务不可用 就比如一个餐馆总共有100个座位 突然有一天某个商家恶意竞争 雇佣了200个人来到这个餐馆坐着不吃不喝 门口还排着长长的队 导致餐馆无法正常营业 这就是D
  • 【Flutter 2-7】Flutter手把手教程UI布局和Widget——垂直布局控件Column

    Column Column是在Flutter中常见的布局控件 它负责垂直方向布局 Row负责水平方向布局 二者都是继承于Flex 类似于iOS里面的UIScrollView 但是又有很多不同 先来看一下Column的构造函数 Column
  • 初级unity开发的重难点知识总结

    生命周期函数 10个 Awake gt OnEnable gt Start gt FixedUpdate gt Update gt LateUpdate gt OnGUI gt Reset gt OnDisable gt OnDestroy
  • 原来的控件delphi7里何处寻?

    经常有朋友提这样的问题 我原来在delphi5或者delphi6中用的很熟的控件到哪里去了 是不是在delphi7中没有了呢 这是不是意味着我以前写的代码全都不能够移植到delphi7中来了呢 是不是我必须学习适应这些新的控件呢 现在我就对
  • 最全的中文编码

    GB18030
  • PTA L2-010 排座位 (25 分)

    include
  • 百亿级数据分表后怎么分页查呢?

    当业务规模达到一定规模之后 像淘宝日订单量在5000万单以上 美团3000万单以上 数据库面对海量的数据压力 分库分表就是必须进行的操作了 而分库分表之后一些常规的查询可能都会产生问题 最常见的就是比如分页查询的问题 一般我们把分表的字段称
  • HCIP VRRP技术

    一 VRRP概述 VRRP Virtual Router Pedundancy Protocol 虚拟路由器冗余协议 既能够实现网关的备份 又能够解决多个网关之间互相冲突的问题 从而提高网络可靠性 局域网中的用户的终端通常采用配置一个默认网
  • osg学习(六十)setTextureMode setTextureAttributeAndModes

    ss setTextureMode unit mode value ss setTextureAttributeAndModes unit tex get osg StateSet cpp void StateSet setTextureM
  • linux系统gpio中断的实现(提供例程)

    该接口基于linux提供的通用中断API接口实现 1 流程 gpio外部引脚发生电平变化 上升沿 下降沿 gt 触发驱动层 driver c 的中断服务函数 gt 在中断服务函数中通过异步消息机制通知到应用层 app c 2 中断响应时间测
  • linux内核SPI总线驱动分析(一)

    原文地址 http www cnblogs com liugf05 archive 2012 12 03 2800457 html SPI总线驱动分析 1 SPI概述 SPI是英语Serial Peripheral interface的缩写