编写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;
}
#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开发点起的星星之火,也愿韦老师桃李满园。聚是一团火,散是满天星!
在这样一个速食的时代,坚持做自己,慢下来,潜心琢磨,心怀敬畏,领悟知识,才能向下扎到根,向上捅破天,背着世界往前行!
仅此向嵌入行业里的每一个认真做技术的从业者致敬!