include\linux\spi\spi.h
Documentation\devicetree\bindings\spi\spi-bus.txt
drivers\spi\spi.c
、drivers\spi\spi-sh.c
在设备树中,对于SPI Master,必须的属性如下:
可选的属性如下:
其他属性都是驱动程序相关的,不同的SPI Master驱动程序要求的属性可能不一样。
在SPI Master对应的设备树节点下,每一个子节点都对应一个SPI设备,这个SPI设备连接在该SPI Master下面。
这些子节点中,必选的属性如下:
可选的属性如下:
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
static struct spi_master *g_virtual_master;
static struct work_struct g_virtual_ws;
static const struct of_device_id spi_virtual_dt_ids[] = {
{ .compatible = "100ask,virtual_spi_master", },
{ /* sentinel */ }
};
static void spi_virtual_work(struct work_struct *work)
{
struct spi_message *mesg;
while (!list_empty(&g_virtual_master->queue)) {
mesg = list_entry(g_virtual_master->queue.next, struct spi_message, queue);
list_del_init(&mesg->queue);
/* 假装硬件传输已经完成 */
mesg->status = 0;
if (mesg->complete)
mesg->complete(mesg->context);
}
}
static int spi_virtual_transfer(struct spi_device *spi, struct spi_message *mesg)
{
#if 0
/* 方法1: 直接实现spi传输 */
/* 假装传输完成, 直接唤醒 */
mesg->status = 0;
mesg->complete(mesg->context);
return 0;
#else
/* 方法2: 使用工作队列启动SPI传输、等待完成 */
/* 把消息放入队列 */
mesg->actual_length = 0;
mesg->status = -EINPROGRESS;
list_add_tail(&mesg->queue, &spi->master->queue);
/* 启动工作队列 */
schedule_work(&g_virtual_ws);
/* 直接返回 */
return 0;
#endif
}
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, 0);
if (master == NULL) {
dev_err(&pdev->dev, "spi_alloc_master error.\n");
return -ENOMEM;
}
master->transfer = spi_virtual_transfer;
INIT_WORK(&g_virtual_ws, spi_virtual_work);
master->dev.of_node = pdev->dev.of_node;
ret = spi_register_master(master);
if (ret < 0) {
printk(KERN_ERR "spi_register_master error.\n");
spi_master_put(master);
return ret;
}
return 0;
}
static int spi_virtual_remove(struct platform_device *pdev)
{
/* 反注册spi_master */
spi_unregister_master(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");
测试程序:
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
/* dac_test /dev/spidevB.D */
int main(int argc, char **argv)
{
int fd;
unsigned int val;
struct spi_ioc_transfer xfer[1];
int status;
unsigned char tx_buf[2];
unsigned char rx_buf[2];
if (argc != 3)
{
printf("Usage: %s /dev/spidevB.D \n" , argv[0]);
return 0;
}
fd = open(argv[1], O_RDWR);
if (fd < 0) {
printf("can not open %s\n", argv[1]);
return 1;
}
val = strtoul(argv[2], NULL, 0);
val <<= 2; /* bit0,bit1 = 0b00 */
val &= 0xFFC; /* 只保留10bit */
tx_buf[1] = val & 0xff;
tx_buf[0] = (val>>8) & 0xff;
memset(xfer, 0, sizeof xfer);
xfer[0].tx_buf = tx_buf;
xfer[0].rx_buf = rx_buf;
xfer[0].len = 2;
status = ioctl(fd, SPI_IOC_MESSAGE(1), xfer);
if (status < 0) {
printf("SPI_IOC_MESSAGE %d\n", errno);
return -1;
}
/* 打印 */
val = (rx_buf[0] << 8) | (rx_buf[1]);
val >>= 2;
printf("Pre val = %d\n", val);
return 0;
}
设备树:
vitural_spi_master {
compatible = "hilbert,virtual_spi_master";
status = "okay";
cs-gpios = <&gpio4 27 GPIO_ACTIVE_LOW>;
num-chipselects = <1>;
#address-cells = <1>;
#size-cells = <0>;
virtual_spi_dev: virtual_spi_dev@0 {
compatible = "spidev";
reg = <0>;
spi-max-frequency = <100000>;
};
};
# 1. 使用不同的开发板内核时, 一定要修改KERN_DIR
# 2. KERN_DIR中的内核要事先配置、编译, 为了能编译内核, 要先设置下列环境变量:
# 2.1 ARCH, 比如: export ARCH=arm64
# 2.2 CROSS_COMPILE, 比如: export CROSS_COMPILE=aarch64-linux-gnu-
# 2.3 PATH, 比如: export PATH=$PATH:/work/imx-6ull/ToolChain-6.3.1/gcc-linaro-6.3.1-2017.05-x86_64_aarch64-linux-gnu/bin
# 注意: 不同的开发板不同的编译器上述3个环境变量不一定相同,
# 请参考各开发板的高级用户使用手册
KERN_DIR = /work/imx6ull-sdk/Linux-4.9.88
all:
make -C $(KERN_DIR) M=`pwd` modules
$(CROSS_COMPILE)gcc -o spi_test spi_test.c
clean:
make -C $(KERN_DIR) M=`pwd` modules clean
rm -rf modules.order spi_test
obj-m += virtual_spi_master.o
以上笔记源自
韦东山
老师的视频课程,感谢韦老师,韦老师是嵌入式培训界一股清流,为嵌入式linux开发点起的星星之火,也愿韦老师桃李满园。聚是一团火,散是满天星!
在这样一个速食的时代,坚持做自己,慢下来,潜心琢磨,心怀敬畏,领悟知识,才能向下扎到根,向上捅破天,背着世界往前行!
仅此向嵌入行业里的每一个认真做技术的从业者致敬!