Linux中SPI
参考资料
https://www.cnblogs.com/aaronLinux/p/6219146.html
1.SPI
2.SPI传输
2.1传输示例
首先,CS0拉低选中的SPI Flash ,
然后在每个时钟周期,
DO输出对应的电平。
SPI FLASH会在每个时钟的上升沿读取D0的电平。
2.2SPI模式
根据SCK的电平以及数据在第一个跳变沿还是第二个跳变沿传输,SPI传输总共有四种模式。
3.SPI总线设备驱动模型
SPI系统中设涉及两类硬件:
SPI控制器
SPI设备
spi控制器有驱动程序,提供spi的传输能力。
spi设备也有自己的驱动程序,提供spi设备的访问能力。
4.spi设备驱动框架图
SPI驱动程序由spi_master及spi_device组成
spi_master是在设备树中定义的spi master及master
下边的device信息;当左边drive中的of_device_id和设备树中的compatible参数匹配的时候,会调用driver中的prboe函数,probe函数会生成master,还会生成spi_device。
spi设备左边包括一个spi驱动,当其中的of_device_id和spi_device匹配时,会调用左边driver中的probe函数。
4.1SPI控制器驱动程序
基于平台总线设备驱动模型实现。在probe函数中除了生成spi_master,还会创建spi_device结构体。
4.2SPI设备驱动程序
左边是spi_drive,里边有id_table表示能支持哪些SPI设备,有probe函数。
右边是spi_device,用来描述spi设备,可以来自设备树或者c文件
5.spi设备树处理过程
5.1spi_device
/**
* struct spi_device - Master side proxy for an SPI slave device
* @dev: Driver model representation of the device.
* @master: SPI controller used with the device.
* @max_speed_hz: Maximum clock rate to be used with this chip
* (on this board); may be changed by the device's driver.
* The spi_transfer.speed_hz can override this for each transfer.
* @chip_select: Chipselect, distinguishing chips handled by @master.
* @mode: The spi mode defines how data is clocked out and in.
* This may be changed by the device's driver.
* The "active low" default for chipselect mode can be overridden
* (by specifying SPI_CS_HIGH) as can the "MSB first" default for
* each word in a transfer (by specifying SPI_LSB_FIRST).
* @bits_per_word: Data transfers involve one or more words; word sizes
* like eight or 12 bits are common. In-memory wordsizes are
* powers of two bytes (e.g. 20 bit samples use 32 bits).
* This may be changed by the device's driver, or left at the
* default (0) indicating protocol words are eight bit bytes.
* The spi_transfer.bits_per_word can override this for each transfer.
* @irq: Negative, or the number passed to request_irq() to receive
* interrupts from this device.
* @controller_state: Controller's runtime state
* @controller_data: Board-specific definitions for controller, such as
* FIFO initialization parameters; from board_info.controller_data
* @modalias: Name of the driver to use with this device, or an alias
* for that name. This appears in the sysfs "modalias" attribute
* for driver coldplugging, and in uevents used for hotplugging
* @cs_gpio: gpio number of the chipselect line (optional, -ENOENT when
* when not using a GPIO line)
*
* @statistics: statistics for the spi_device
*
* A @spi_device is used to interchange data between an SPI slave
* (usually a discrete chip) and CPU memory.
*
* In @dev, the platform_data is used to hold information about this
* device that's meaningful to the device's protocol driver, but not
* to its controller. One example might be an identifier for a chip
* variant with slightly different functionality; another might be
* information about how this particular board wires the chip's pins.
*/
struct spi_device {
struct device dev;
struct spi_master *master;
u32 max_speed_hz;/* 该设备能支持的spi时钟最大值 */
u8 chip_select;/* 是对应的spi_master下边的第几个设备 */
u8 bits_per_word;/* 每个基本的spi传输涉及多少位 */
u16 mode; /*spi_chpa spi_cpol组合起来得到spi传输的四种模式 */
#define SPI_CPHA 0x01 /* clock phase */
#define SPI_CPOL 0x02 /* clock polarity */
#define SPI_MODE_0 (0|0) /* (original MicroWire) */
#define SPI_MODE_1 (0|SPI_CPHA)
#define SPI_MODE_2 (SPI_CPOL|0)
#define SPI_MODE_3 (SPI_CPOL|SPI_CPHA)
#define SPI_CS_HIGH 0x04 /* chipselect active high? */
#define SPI_LSB_FIRST 0x08 /* per-word bits-on-wire */
#define SPI_3WIRE 0x10 /* SI/SO signals shared */
#define SPI_LOOP 0x20 /* loopback mode */
#define SPI_NO_CS 0x40 /* 1 dev/bus, no chipselect */
#define SPI_READY 0x80 /* slave pulls low to pause */
#define SPI_TX_DUAL 0x100 /* transmit with 2 wires */
#define SPI_TX_QUAD 0x200 /* transmit with 4 wires */
#define SPI_RX_DUAL 0x400 /* receive with 2 wires */
#define SPI_RX_QUAD 0x800 /* receive with 4 wires */
int irq;
void *controller_state;
void *controller_data;
char modalias[SPI_NAME_SIZE];
int cs_gpio; /* chip select gpio */
/* the statistics */
struct spi_statistics statistics;
/*
* likely need more hooks for more protocol options affecting how
* the controller talks to each chip, like:
* - memory packing (12 bit samples into low bits, others zeroed)
* - priority
* - drop chipselect after each word
* - chipselect delays
* - ...
*/
};
5.2设备树中的spi节点
spi4 {
compatible = "spi-gpio";/* 这个属性很关键,因为根据这个属性找到spi_master */
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_spi4>;
pinctrl-assert-gpios = <&gpio5 8 GPIO_ACTIVE_LOW>;
status = "okay";
gpio-sck = <&gpio5 11 0>;
gpio-mosi = <&gpio5 10 0>;
cs-gpios = <&gpio5 7 0>;
num-chipselects = <1>;
#address-cells = <1>;/* 这个SPI Master下的SPI设备,需要多少个cell来表述它的片选引脚 */
#size-cells = <0>; /* 这个必须设置为0 */
/* gpio_spi是spi_master 下边的device节点 */
gpio_spi: gpio_spi@0 {
compatible = "fairchild,74hc595";/* 根据它找到spi device驱动 */
gpio-controller;
#gpio-cells = <2>;
reg = <0>;/* 使用哪个片选引脚 */
registers-number = <1>;
registers-default = /bits/ 8 <0x57>;
spi-max-frequency = <10000>;/* 该设备支持的最大spi时钟 */
};
};
6.spi device设备驱动程序编写
6.1怎么编写SPI设备驱动程序
1.查看原理图,确定设备在哪个spi控制器下边。
2.在设备树中找到对应的spi控制器,在该节点下创建子节点,表示spi device。
6.2注册spi driver
spi设备树中的节点,会被转换为一个spi device结构体。
需要编写一个spi driver来支持它。
参考spidev.c来编写设备驱动程序。
基于spi bus模型来编写驱动程序。
6.3怎么发起传输
6.3.1在函数spi.h中
Linux-4.9.88\include\linux\spi\spi.h
/**
* spi_write - SPI synchronous write
* @spi: device to which data will be written
* @buf: data buffer
* @len: data buffer size
* Context: can sleep
*
* This function writes the buffer @buf.
* Callable only from contexts that can sleep.
*
* Return: zero on success, else a negative error code.
*/
static inline int
spi_write(struct spi_device *spi, const void *buf, size_t len)
{
struct spi_transfer t = {
.tx_buf = buf,
.len = len,
};
return spi_sync_transfer(spi, &t, 1);
}
/**
* spi_read - SPI synchronous read
* @spi: device from which data will be read
* @buf: data buffer
* @len: data buffer size
* Context: can sleep
*
* This function reads the buffer @buf.
* Callable only from contexts that can sleep.
*
* Return: zero on success, else a negative error code.
*/
static inline int
spi_read(struct spi_device *spi, void *buf, size_t len)
{
struct spi_transfer t = {
.rx_buf = buf,
.len = len,
};
return spi_sync_transfer(spi, &t, 1);
}
/* this copies txbuf and rxbuf data; for small transfers only! */
extern int spi_write_then_read(struct spi_device *spi,
const void *txbuf, unsigned n_tx,
void *rxbuf, unsigned n_rx);
/**
* spi_w8r8 - SPI synchronous 8 bit write followed by 8 bit read
* @spi: device with which data will be exchanged
* @cmd: command to be written before data is read back
* Context: can sleep
*
* Callable only from contexts that can sleep.
*
* Return: the (unsigned) eight bit number returned by the
* device, or else a negative error code.
*/
static inline ssize_t spi_w8r8(struct spi_device *spi, u8 cmd)
{
ssize_t status;
u8 result;
status = spi_write_then_read(spi, &cmd, 1, &result, 1);
/* return negative errno or unsigned value */
return (status < 0) ? status : result;
}
/**
* spi_w8r16 - SPI synchronous 8 bit write followed by 16 bit read
* @spi: device with which data will be exchanged
* @cmd: command to be written before data is read back
* Context: can sleep
*
* The number is returned in wire-order, which is at least sometimes
* big-endian.
*
* Callable only from contexts that can sleep.
*
* Return: the (unsigned) sixteen bit number returned by the
* device, or else a negative error code.
*/
static inline ssize_t spi_w8r16(struct spi_device *spi, u8 cmd)
{
ssize_t status;
u16 result;
status = spi_write_then_read(spi, &cmd, 1, &result, 2);
/* return negative errno or unsigned value */
return (status < 0) ? status : result;
}
/**
* spi_w8r16be - SPI synchronous 8 bit write followed by 16 bit big-endian read
* @spi: device with which data will be exchanged
* @cmd: command to be written before data is read back
* Context: can sleep
*
* This function is similar to spi_w8r16, with the exception that it will
* convert the read 16 bit data word from big-endian to native endianness.
*
* Callable only from contexts that can sleep.
*
* Return: the (unsigned) sixteen bit number returned by the device in cpu
* endianness, or else a negative error code.
*/
static inline ssize_t spi_w8r16be(struct spi_device *spi, u8 cmd)
{
ssize_t status;
__be16 result;
status = spi_write_then_read(spi, &cmd, 1, &result, 2);
if (status < 0)
return status;
return be16_to_cpu(result);
}
6.3.2函数解析
在spi子系统中,用spi_transfer结构体描述一个传输,用spi_message管理整个传输。
spi_transfer结构体:
spi_message结构体
spi传输示例
7.编写spi DAC模块驱动程序
7.1基于spi master spi device设备驱动模型来编写设备的驱动程序。
1.查看原理图,看spi device在哪个spi master下边,
然后再设备树中对应的master下边添加设备节点。
2.编写驱动程序,注册一个spi device。
3.编写测试程序。
1.将DAC模块连接到SPI_A上边
2.在设备树中创建子节点
3.编写spi_device设备驱动程序
#include <linux/init.h>
#include <linux/module.h>
#include <linux/ioctl.h>
#include <linux/fs.h>
#include <linux/device.h>
#include <linux/err.h>
#include <linux/list.h>
#include <linux/errno.h>
#include <linux/mutex.h>
#include <linux/slab.h>
#include <linux/compat.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/acpi.h>
#include <linux/spi/spi.h>
#include <linux/spi/spidev.h>
#include <linux/uaccess.h>
#define SPI_IOC_WR 123
/*-------------------------------------------------------------------------*/
static struct spi_device *dac;
static int major;
static long
spidev_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
int val;
int err;
unsigned char tx_buf[2];
unsigned char rx_buf[2];
struct spi_message msg;
struct spi_transfer xfer[1];
int status;
memset(&xfer[0], 0, sizeof(xfer));
/* copy_from_user */
err = copy_from_user(&val, (const void __user *)arg, sizeof(int));
printk("spidev_ioctl get val from user: %d\n", val);
/* 发起SPI传输: */
/* 1. 把val修改为正确的格式 */
val <<= 2; /* bit0,bit1 = 0b00 */
val &= 0xFFC; /* 只保留10bit */
tx_buf[1] = val & 0xff;
tx_buf[0] = (val>>8) & 0xff;
/* 2. 发起SPI传输同时写\读 */
/* 2.1 构造transfer
* 2.2 加入message
* 2.3 调用spi_sync
*/
xfer[0].tx_buf = tx_buf;
xfer[0].rx_buf = rx_buf;
xfer[0].len = 2;
spi_message_init(&msg);
spi_message_add_tail(&xfer[0], &msg);
status = spi_sync(dac, &msg);
/* 3. 修改读到的数据的格式 */
val = (rx_buf[0] << 8) | (rx_buf[1]);
val >>= 2;
printk("spidev_ioctl get val to user: %d\n", val);
/* copy_to_user */
err = copy_to_user((void __user *)arg, &val, sizeof(int));
return 0;
}
static const struct file_operations spidev_fops = {
.owner = THIS_MODULE,
/* REVISIT switch to aio primitives, so that userspace
* gets more complete API coverage. It'll simplify things
* too, except for the locking.
*/
.unlocked_ioctl = spidev_ioctl,
};
static struct class *spidev_class;
static const struct of_device_id spidev_dt_ids[] = {
{ .compatible = "100ask,dac" },
{},
};
/*-------------------------------------------------------------------------*/
static int spidev_probe(struct spi_device *spi)
{
printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);
/* 1. 记录spi_device */
dac = spi;
/* 2. 注册字符设备 */
major = register_chrdev(0, "100ask_dac", &spidev_fops);
spidev_class = class_create(THIS_MODULE, "100ask_dac");
device_create(spidev_class, NULL, MKDEV(major, 0), NULL, "100ask_dac");
printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);
return 0;
}
static int spidev_remove(struct spi_device *spi)
{
/* 反注册字符设备 */
device_destroy(spidev_class, MKDEV(major, 0));
class_destroy(spidev_class);
unregister_chrdev(major, "100ask_dac");
printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);
return 0;
}
static struct spi_driver spidev_spi_driver = {
.driver = {
.name = "100ask,dac",
.of_match_table = of_match_ptr(spidev_dt_ids),
},
.probe = spidev_probe,
.remove = spidev_remove,
};
/*-------------------------------------------------------------------------*/
static int __init spidev_init(void)
{
int status;
printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);
status = spi_register_driver(&spidev_spi_driver);
if (status < 0) {
}
return status;
}
module_init(spidev_init);
static void __exit spidev_exit(void)
{
printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);
spi_unregister_driver(&spidev_spi_driver);
}
module_exit(spidev_exit);
MODULE_LICENSE("GPL");
4.测试程序
/* 参考: tools\spi\spidev_fdx.c */
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <linux/types.h>
#define SPI_IOC_WR 123
/* dac_test /dev/100ask_dac <val> */
int main(int argc, char **argv)
{
int fd;
unsigned int val;
int status;
unsigned char tx_buf[2];
unsigned char rx_buf[2];
if (argc != 3)
{
printf("Usage: %s /dev/100ask_dac <val>\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);
status = ioctl(fd, SPI_IOC_WR, &val);
if (status < 0) {
printf("SPI_IOC_WR\n");
return -1;
}
/* 打印 */
printf("Pre val = %d\n", val);
return 0;
}
5.Makefile
KERN_DIR = /home/book/100ask_imx6ull-sdk/Linux-4.9.88
all:
make -C $(KERN_DIR) M=`pwd` modules
$(CROSS_COMPILE)gcc -o dac_test dac_test.c
clean:
make -C $(KERN_DIR) M=`pwd` modules clean
rm -rf modules.order dac_test
obj-m += dac_drv.o
8.spi master的实现
1.修改设备树
2.编写驱动
分配、设置、注册spi_master
8.1spi传输概述
使用spi传输时,最小的传输单位是spi_transfer
一个设备,可以传输多个spi_transfer, 这些spi_tranfer会放在一个spi_message中。
spi_master中传输函数
代码
#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 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("www.100ask.net");
之后,再在master函数中实现这个传输函数。