编写SPI_Master驱动程序_新方法

编写SPI_Master驱动程序_新方法


文章目录

  • 编写SPI_Master驱动程序_新方法
    • 一. SPI驱动框架
      • 1.1 总体框架
      • 1.2 怎么编写SPI_Master驱动
        • 1.2.1 编写设备树
        • 1.2.2 编写驱动程序
    • 二、 编写程序
      • 2.1 数据传输流程
      • 2.2 写代码
    • 致谢


参考资料:

  • 内核头文件:include\linux\spi\spi.h
  • 内核文档:Documentation\devicetree\bindings\spi\spi-bus.txt
    • 内核源码:drivers\spi\spi.cdrivers\spi\spi-sh.c

一. SPI驱动框架

1.1 总体框架

1.2 怎么编写SPI_Master驱动

1.2.1 编写设备树

在设备树中,对于SPI Master,必须的属性如下:

  • #address-cells:这个SPI Master下的SPI设备,需要多少个cell来表述它的片选引脚
  • #size-cells:必须设置为0
  • compatible:根据它找到SPI Master驱动

可选的属性如下:

  • cs-gpios:SPI Master可以使用多个GPIO当做片选,可以在这个属性列出那些GPIO
  • num-cs:片选引脚总数

其他属性都是驱动程序相关的,不同的SPI Master驱动程序要求的属性可能不一样。

在SPI Master对应的设备树节点下,每一个子节点都对应一个SPI设备,这个SPI设备连接在该SPI Master下面。

这些子节点中,必选的属性如下:

  • compatible:根据它找到SPI Device驱动
  • reg:用来表示它使用哪个片选引脚
  • spi-max-frequency:必选,该SPI设备支持的最大SPI时钟

可选的属性如下:

  • spi-cpol:这是一个空属性(没有值),表示CPOL为1,即平时SPI时钟为低电平
  • spi-cpha:这是一个空属性(没有值),表示CPHA为1),即在时钟的第2个边沿采样数据
  • spi-cs-high:这是一个空属性(没有值),表示片选引脚高电平有效
  • spi-3wire:这是一个空属性(没有值),表示使用SPI 三线模式
  • spi-lsb-first:这是一个空属性(没有值),表示使用SPI传输数据时先传输最低位(LSB)
  • spi-tx-bus-width:表示有几条MOSI引脚;没有这个属性时默认只有1条MOSI引脚
  • spi-rx-bus-width:表示有几条MISO引脚;没有这个属性时默认只有1条MISO引脚
  • spi-rx-delay-us:单位是毫秒,表示每次读传输后要延时多久
  • spi-tx-delay-us:单位是毫秒,表示每次写传输后要延时多久

1.2.2 编写驱动程序

  • 核心为:分配/设置/注册spi_master结构体
  • 对于老方法,spi_master结构体的核心是transfer函数

二、 编写程序

2.1 数据传输流程

编写SPI_Master驱动程序_新方法_第1张图片

2.2 写代码

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

static struct spi_master *g_virtual_master;
static struct spi_bitbang *g_virtual_bitbang;
static struct completion g_xfer_done;


static const struct of_device_id spi_virtual_dt_ids[] = {
	{ .compatible = "hilbert,virtual_spi_master", },
	{ /* sentinel */ }
};

/* xxx_isr() { complete(&g_xfer_done)  } */

static int spi_virtual_transfer(struct spi_device *spi,
				struct spi_transfer *transfer)
{
	int timeout;

#if 1	
	/* 1. init complete */
	reinit_completion(&g_xfer_done);

	/* 2. 启动硬件传输 */
	complete(&g_xfer_done);

	/* 3. wait for complete */
	timeout = wait_for_completion_timeout(&g_xfer_done,
					      100);
	if (!timeout) {
		dev_err(&spi->dev, "I/O Error in PIO\n");
		return -ETIMEDOUT;
	}
#endif
	return transfer->len;
}


static int spi_virtual_probe(struct platform_device *pdev)
{
	struct spi_master *master;
	int ret;
	
	/* 分配/设置/注册spi_master */
	g_virtual_master = master = spi_alloc_master(&pdev->dev, sizeof(struct spi_bitbang));
	if (master == NULL) {
		dev_err(&pdev->dev, "spi_alloc_master error.\n");
		return -ENOMEM;
	}

	g_virtual_bitbang = spi_master_get_devdata(master);

	/* 怎么设置spi_master?
	 * 1. spi_master使用默认的函数
	 * 2. 分配/设置 spi_bitbang结构体: 主要是实现里面的txrx_bufs函数
	 * 3. spi_master要能找到spi_bitbang
	 */
	g_virtual_bitbang->master = master;
	g_virtual_bitbang->txrx_bufs = spi_virtual_transfer;

#if 0
	ret = spi_register_master(master);
	if (ret < 0) {
		printk(KERN_ERR "spi_register_master error.\n");
		spi_master_put(master);
		return ret;
	}
#else
	ret = spi_bitbang_start(g_virtual_bitbang);
	if (ret) {
		printk("bitbang start failed with %d\n", ret);
		return ret;
	}

#endif

	return 0;

	
}

static int spi_virtual_remove(struct platform_device *pdev)
{
#if 0	
	/* 反注册spi_master */
	spi_unregister_master(g_virtual_master);
#endif
	spi_bitbang_stop(g_virtual_bitbang);
	spi_master_put(g_virtual_master);
	return 0;
}


static struct platform_driver spi_virtual_driver = {
	.probe = spi_virtual_probe,
	.remove = spi_virtual_remove,
	.driver = {
		.name = "virtual_spi",
		.of_match_table = spi_virtual_dt_ids,
	},
};

static int virtual_master_init(void)
{
	return platform_driver_register(&spi_virtual_driver);
}

static void virtual_master_exit(void)
{
	platform_driver_unregister(&spi_virtual_driver);
}

module_init(virtual_master_init);
module_exit(virtual_master_exit);

MODULE_DESCRIPTION("Virtual SPI bus driver");
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Hilbert");
        spi3 {
                compatible = "hilbert,virtual_spi_master";
                status = "okay";
                cs-gpios = <&gpio0 27 GPIO_ACTIVE_LOW>;
                num-chipselects = <1>;
                #address-cells = <1>;
                #size-cells = <0>;

                virtual_spi_dev: virtual_spi_dev@0 {
                        compatible = "hilbert,virtual_spi_device";
                        reg = <0>;
                        spi-max-frequency = <100000>;
                };
        };

致谢


以上笔记源自韦东山老师的视频课程,感谢韦老师,韦老师是嵌入式培训界一股清流,为嵌入式linux开发点起的星星之火,也愿韦老师桃李满园。聚是一团火,散是满天星!

在这样一个速食的时代,坚持做自己,慢下来,潜心琢磨,心怀敬畏,领悟知识,才能向下扎到根,向上捅破天,背着世界往前行!
仅此向嵌入行业里的每一个认真做技术的从业者致敬!


你可能感兴趣的:(SPI总线,驱动开发)