编写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.c
、drivers\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 数据传输流程
2.2 写代码
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/errno.h>
#include <linux/timer.h>
#include <linux/delay.h>
#include <linux/list.h>
#include <linux/workqueue.h>
#include <linux/interrupt.h>
#include <linux/platform_device.h>
#include <linux/io.h>
#include <linux/spi/spi.h>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;}
#endifreturn 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 0ret = spi_register_master(master);if (ret < 0) {printk(KERN_ERR "spi_register_master error.\n");spi_master_put(master);return ret;}
#elseret = spi_bitbang_start(g_virtual_bitbang);if (ret) {printk("bitbang start failed with %d\n", ret);return ret;}#endifreturn 0;}static int spi_virtual_remove(struct platform_device *pdev)
{
#if 0 /* 反注册spi_master */spi_unregister_master(g_virtual_master);
#endifspi_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开发点起的星星之火,也愿韦老师桃李满园。聚是一团火,散是满天星!
在这样一个速食的时代,坚持做自己,慢下来,潜心琢磨,心怀敬畏,领悟知识,才能向下扎到根,向上捅破天,背着世界往前行!
仅此向嵌入行业里的每一个认真做技术的从业者致敬!