查看: 2882|回复: 0

【从0教学嵌入式Linux】第二十五集

[复制链接]
  • TA的每日心情
    奋斗
    2016-6-24 09:52
  • 签到天数: 112 天

    连续签到: 1 天

    [LV.6]常住居民II

    发表于 2016-6-21 14:34:17 | 显示全部楼层 |阅读模式
    分享到:
    第25课:SPI驱动程序开发
    图片100.jpg
    (一)SPI子系统驱动
    一、概述
    基于子系统去开发驱动程序已经是Linux内核中普遍的做法了。前面写过基于I2C        子系统的驱动开发。本文介绍另一种常用总线。SPI子系统的开发和I2C有很多相似性,大家可以对比学习。
    二、SPI总线协议简介
    介绍驱动开发前,需要先熟悉下SPI通讯协议中的几个关键的地方,后面再编写驱动时,需要考虑相关因素。
    SPI总线由MISO(串行数据输入)、MOSI(串行数据输出)、SCK(串行移位时钟)、CS(使能信号)4个信号线组成。
    SPI常用四种数据传输模式的主要差别在于:输出串行同步时钟极性(CPOL)和相位(CPHA)可以进行配置。如果CPOL=0,串行同步时钟的空闲状态为低电平;如果CPOL=1,串行同步时钟的空闲状态为高电平。如果CPHA=0,在串行同步时钟的前沿(上升或下降)数据被采样;如果CPHA=1,在串行时钟的后沿(上升或下降)数据被采样。
    图片1.jpg

    图片2.jpg
    这四种模式中究竟选择哪种模式取决于设备。
    三、LinuxSPI驱动开发
    首先明确SPI驱动层次,如下图:
    图片3.jpg
    我们以上面这个图为思路
    1.Platform bus
    Platform bus对应的结构是platform_bus_type,这个内核是开始就定义好的。
    2.Platform_device
    SPI控制器对应platform_device的定义方式,参看arch/arm/plat-samsung/dev-spi.c文件
    图片4.jpg
    3.Platform_driver
    再来看platform_driver,参看drivers/spi/spi_s3c.c文件
    图片5.jpg
    Platform_driver_probe(&s3c_spi_driver,s3c_spi_probe);注册driver
    然后根据传入的platform_device参数,构建一个用于描述SPI控制器的结构体spi_master并注册。Spi_register_master(master)。后续注册的spi_device需要选定自己的spi_master,并利用spi_master提供的传输功能传输spi数据。
    I2C类似,SPI也有一个描述控制器的对象叫spi_master,其主要成员是主机控制器的序号(系统中可能存在多个SPI主机控制器)、片选数量、SPI模式和时钟设置用到的函数、数据传输用到的函数等。
    图片6.jpg
    4.Spi bus
    SPI总线对应的总线类型为spi_bus_type,在内核的drivers/spi/spi.c中定义
    图片7.jpg
    对应的匹配规则是(高版本的匹配规则会稍有变化,引入了id_table,可以匹配多个spi设备名称
    图片8.jpg
    5.spi_devic
    下面讲spi_device的构建与注册。Spi_device对应的含义是挂载在spi总线上的一个设备,所以描述它的时候应该明确它自身的设备特性、传输要求、及挂接在哪个总线上。
    图片9.jpg
    spi_register_board_infos3c_spi_devs,ARRAY_SIZE(s3c_spi_devs);//注册spi_board_info。这个代码会把spi_board_info注册到链表board_list上。
    事实上上文提到的spi_master的注册会在spi_register_board_info之后,spi_master注册的过程中会调用scan_board_info扫描board_list,找到挂接在它上面的spi设备,然后创建并注册spi_device
    图片10.jpg
    6.spi_driver(参考)
    本文先以Linux内核中的/driver/mtd/devices/m25p80.c驱动为参考。
    图片11.jpg
    Spi_register_driver(&m25p80_driver);   //spi driver 的注册
    在有匹配的spi driver时,会调用m25p_probe
    Static int_devinit m25p_probestruct spi_device *spi
    {
    ........
    }
    根据传入的spi_device参数,可以找到对应的spi_master,接下来就可以利用spi子系统为我们完成数据交互了,可以参看m25p80_read函数,要完成传输先理解下面几个给够的含义(这两个结构的定义及详细注释参见include/linux/spi/spi.h
    Spi_message:描述一次完整的传输,即cs信号从高->->高的传输
    Spi_transfer:多个api_transfer构成一个spi_message
    举例说明:m25p80的读过程如下图
    图片12.jpg
    可以分解为两个spi_transfer,一个是写命令,另一个是读数据具体实现参见m25p80.c中的m25p80_read函数,下面内容摘取之此函数
    图片13.jpg
    Spi_sync为同步方式发送,还可以用spi_async异步方式,需要设置回调完成函数。
    另外也可以选择一些封装好的更容易使用的函数,这些函数可以在include/linux/spi/spi.h文件中找到,如:
    extern int spi_write_then_read(struct spi_device *spi,
    const u8 *txbuf,unsigned n_tx,
    u8 *rxbuf,unsigned n_rx);
    (二)编写SPI驱动概述(参考)
    本文不具体分析Linux内核中SPI总线的架构,只针对这种架构阐述如何进行SPI设备驱动的编写。简而言之,SPI驱动的编写分为两个部分:
    第一:spi_device的构建和注册
    第二:spi_driver的构建和注册
    1.spi_device的构建并注册
    首先在板文件中添加spi_board_info,例如:
    图片14.jpg
    并在板文件的init函数中调用spi_register_board_infos3c_spi_devsARRAY_SIZEs3c_spi_devs));这个函数会把spi_board_info注册到链表board_list上,spi_device封装了一个spi_master结构体,事实上spi_master的注册会在spi_register_board_info之后,spi_master注册的过程中会调用scan_boardinfo扫描board_list,找到挂接在它上面的spi设备然后创建并注册spi_device
    2.Spi_driver的构建与注册分为三步:
    (1)构建spi_driver
    图片15.jpg
    (2)Spi_driver的注册
    Spi_register_driver&m25p80_driver); 当匹配了spi_device以后调用probe
    (3)实现probe操作
    Spi_transfer(里面集成了数据buf空间地址等信息)
    Spi_message(是spi_transfer的集合)的构建:spi_message_init(初始化spi_message)、spi_message_add_tail(将新的spi_transfer添加到spi_message队列尾部)
    Spi_sync函数的调用(调用spi_master发送spi_message
    例如:
    图片16.jpg
    这样就基本完成了SPI设备的驱动编写,读写具体操作要根据芯片的时序来确定,具体如何利用SPI传数据那么就要看自己程序的逻辑。
    回复

    使用道具 举报

    您需要登录后才可以回帖 注册/登录

    本版积分规则

    关闭

    站长推荐上一条 /2 下一条

    手机版|小黑屋|与非网

    GMT+8, 2024-4-20 03:47 , Processed in 0.122793 second(s), 16 queries , MemCache On.

    ICP经营许可证 苏B2-20140176  苏ICP备14012660号-2   苏州灵动帧格网络科技有限公司 版权所有.

    苏公网安备 32059002001037号

    Powered by Discuz! X3.4

    Copyright © 2001-2020, Tencent Cloud.