Linux下SPI驱动开发

基于子系统去开发驱动程序已经是linux内核中普遍的做法了。前面写过基于I2C子系统的驱动开发。本文介绍另外一种常用总线SPI的开发方法。SPI子系统的开发和I2C有很多的相似性,大家可以对比学习。本主题分为两个部分叙述,第一部分介绍基于SPI子系统开发的理论框架;第二部分以华清远见教学平台FS_S5PC100上的M25P10芯片为例(内核版本2.6.29),编写一个SPI驱动程序实例。

 二、SPI总线协议简介

介绍驱动开发前,需要先熟悉下SPI通讯协议中的几个关键的地方,后面在编写驱动时,需要考虑相关因素。

SPI总线由MISO(串行数据输入)、MOSI(串行数据输出)、SCK(串行移位时钟)、CS(使能信号)4个信号线组成。如FS_S5PC100上的M25P10芯片接线为:


上图中M25P10的D脚为它的数据输入脚,Q为数据输出脚,C为时钟脚。

         SPI常用四种数据传输模式,主要差别在于:输出串行同步时钟极性(CPOL)和相位(CPHA)可以进行配置。如果CPOL= 0,串行同步时钟的空闲状态为低电平;如果CPOL= 1,串行同步时钟的空闲状态为高电平。如果CPHA= 0,在串行同步时钟的前沿(上升或下降)数据被采样;如果CPHA = 1,在串行同步时钟的后沿(上升或下降)数据被采样。


这四种模式中究竟选择哪种模式取决于设备。如M25P10的手册中明确它可以支持的两种模式为:CPOL=0 CPHA=0  和 CPOL=1 CPHA=1

三、linux下SPI驱动开发

首先明确SPI驱动层次,如下图:

我们以上面的这个图为思路

1、  Platform bus

Platform bus对应的结构是platform_bus_type,这个内核开始就定义好的。我们不需要定义。

2、Platform_device

SPI控制器对应platform_device的定义方式,同样以S5PC100中的SPI控制器为例,参看arch/arm/plat-s5pc1xx/dev-spi.c文件

structplatform_device s3c_device_spi0 = {

.name         ="s3c64xx-spi", //名称,要和Platform_driver匹配

.id       =0, //第0个控制器,S5PC100中有3个控制器

.num_resources    =ARRAY_SIZE(s5pc1xx_spi0_resource),//占用资源的种类

.resource     =s5pc1xx_spi0_resource,//指向资源结构数组的指针

.dev= {

.dma_mask       = &spi_dmamask,  //dma寻址范围     

.coherent_dma_mask  = DMA_BIT_MASK(32),  //可以通过关闭cache等措施保证一致性的dma寻址范围

.platform_data= &s5pc1xx_spi0_pdata,//特殊的平台数据,参看后文

},

};

static structs3c64xx_spi_cntrlr_info s5pc1xx_spi0_pdata= {

.cfg_gpio = s5pc1xx_spi_cfg_gpio,  //用于控制器管脚的IO配置

.fifo_lvl_mask = 0x7f,

.rx_lvl_offset = 13,

};

static int s5pc1xx_spi_cfg_gpio(structplatform_device *pdev)

{

switch (pdev->id) {

case 0:

s3c_gpio_cfgpin(S5PC1XX_GPB(0),S5PC1XX_GPB0_SPI_MISO0);

s3c_gpio_cfgpin(S5PC1XX_GPB(1),S5PC1XX_GPB1_SPI_CLK0);

s3c_gpio_cfgpin(S5PC1XX_GPB(2),S5PC1XX_GPB2_SPI_MOSI0);

s3c_gpio_setpull(S5PC1XX_GPB(0),S3C_GPIO_PULL_UP);

s3c_gpio_setpull(S5PC1XX_GPB(1),S3C_GPIO_PULL_UP);

s3c_gpio_setpull(S5PC1XX_GPB(2),S3C_GPIO_PULL_UP);

break;

case 1:

s3c_gpio_cfgpin(S5PC1XX_GPB(4),S5PC1XX_GPB4_SPI_MISO1);

s3c_gpio_cfgpin(S5PC1XX_GPB(5),S5PC1XX_GPB5_SPI_CLK1);

s3c_gpio_cfgpin(S5PC1XX_GPB(6),S5PC1XX_GPB6_SPI_MOSI1);

s3c_gpio_setpull(S5PC1XX_GPB(4),S3C_GPIO_PULL_UP);

s3c_gpio_setpull(S5PC1XX_GPB(5),S3C_GPIO_PULL_UP);

s3c_gpio_setpull(S5PC1XX_GPB(6),S3C_GPIO_PULL_UP);

break;

case 2:

s3c_gpio_cfgpin(S5PC1XX_GPG3(0),S5PC1XX_GPG3_0_SPI_CLK2);

s3c_gpio_cfgpin(S5PC1XX_GPG3(2),S5PC1XX_GPG3_2_SPI_MISO2);

s3c_gpio_cfgpin(S5PC1XX_GPG3(3), S5PC1XX_GPG3_3_SPI_MOSI2);

s3c_gpio_setpull(S5PC1XX_GPG3(0),S3C_GPIO_PULL_UP);

s3c_gpio_setpull(S5PC1XX_GPG3(2),S3C_GPIO_PULL_UP);

s3c_gpio_setpull(S5PC1XX_GPG3(3),S3C_GPIO_PULL_UP);

break;

default:

dev_err(&pdev->dev, "InvalidSPI Controller number!");

return -EINVAL;

}

3、Platform_driver

再看platform_driver,参看drivers/spi/spi_s3c64xx.c文件

static structplatform_driver s3c64xx_spi_driver = {

.driver= {

.name   = "s3c64xx-spi",  //名称,和platform_device对应

.owner= THIS_MODULE,

},

.remove= s3c64xx_spi_remove,

.suspend= s3c64xx_spi_suspend,

.resume= s3c64xx_spi_resume,

};

platform_driver_probe(&s3c64xx_spi_driver,s3c64xx_spi_probe);//注册s3c64xx_spi_driver

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wwgfwj.html