linux下SPI驱动总结

前言

一、概念

SPI(Serial Peripheral Interface一一串行外设接口)总线是Motorola公司 [2]推出的一种同步串行接口技术。SPI总线系统是一种同步串行外接口,允许MCU/MPU与各种外围设备以串行方式进行通信,数据交换。外围设备包括FLASHRAM、A/D转换器、网络控制器、MCU等。SPI是一种高速的,全双工,同步的通信总线。(来自百度百科)
关键词: 串行 高速 全双工(发送的同时可以接收)

二、硬件特性

1、线制说明
三线制:CLK时钟、 CS片选、 MOSI(此线作为输入输出线),三线是SPI的半双工通信,单根数据线可以分时发送和接收;
四线制: CLK时钟、 CS片选、 MOSI(主机输出)、MISO(主机输入),标准四线是全双工
2、电气连接
标准四线
三线
3、极性/模式
spi根据极性和相位有四种模式

三、驱动软件

此说明以linux内核5.10.165, Designware SPI core controller 外设IP核为例总结

1、主要文件和作用

spi-dw-mmio.c
spi外设控制器设备树节点解析,将spi外设封装成platform设备,对platform设备设备的操作封装;
以platform的device成员,创建struct dw_spi_mmio 后,对struct dw_spi_mmio成员初始化;
解析设备树中外设寄存器 时钟等资源赋值给其中struct dw_spi dws 成员的子成员,struct dw_spi结构定义于spi-dw.h

此文件主要结构:

struct dw_spi_mmio {
	struct dw_spi  dws;
	struct clk     *clk;
	struct clk     *pclk;
	void           *priv;
	struct reset_control *rstc;
};

spi-dw-core.c
对struct dw_spi 结构的操作封装,spi外设中断处理函数的处理,对struct dw_spi的成员struct spi_controller *master的初始化
此文件主要结构:

struct dw_spi {
	struct spi_controller	*master;  //对spi外设封装表示

	void __iomem		*regs;  //spi外设的寄存器地址
	unsigned long		paddr;
	int			irq;
	u32			fifo_len;	/* depth of the FIFO buffer */
	u32			max_mem_freq;	/* max mem-ops bus freq */
	u32			max_freq;	/* max bus freq supported */

	u32			caps;		/* DW SPI capabilities */

	u32			reg_io_width;	/* DR I/O width in bytes */
	u16			bus_num;
	u16			num_cs;		/* supported slave numbers */
	void (*set_cs)(struct spi_device *spi, bool enable);

	/* Current message transfer state info */
	void			*tx;
	unsigned int		tx_len;
	void			*rx;
	unsigned int		rx_len;
	u8			buf[SPI_BUF_SIZE];
	int			dma_mapped;
	u8			n_bytes;	/* current is a 1/2 bytes op */
	irqreturn_t		(*transfer_handler)(struct dw_spi *dws);
	u32			current_freq;	/* frequency in hz */
	u32			cur_rx_sample_dly;
	u32			def_rx_sample_dly_ns;

	/* Custom memory operations */
	struct spi_controller_mem_ops mem_ops;

	/* DMA info */
	struct dma_chan		*txchan;
	u32			txburst;
	struct dma_chan		*rxchan;
	u32			rxburst;
	u32			dma_sg_burst;
	unsigned long		dma_chan_busy;
	dma_addr_t		dma_addr; /* phy address of the Data register */
	const struct dw_spi_dma_ops *dma_ops;
	struct completion	dma_completion;

#ifdef CONFIG_DEBUG_FS
	struct dentry *debugfs;
	struct debugfs_regset32 regset;
#endif
};

spidev.c
对挂载spi外设上的spi设备进行读写访问的通用驱动接口封装, 在此对于spi设备的操作也是通用的字符设备操作结构体,如下

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.
	 */
	.write =	spidev_write,
	.read =		spidev_read,
	.unlocked_ioctl = spidev_ioctl,
	.compat_ioctl = spidev_compat_ioctl,
	.open =		spidev_open,
	.release =	spidev_release,
	.llseek =	no_llseek,
};

spi.c
spi驱动的核心层;
对于spi外设到spi设备信息读写接口;
spi外设控制器的最终注册;
spi消息队列的管理;
spi_controller和spi_device的分配接口;

2、主要数据结构

spi外设的表示:

struct spi_controller {
	struct device	dev;

	struct list_head list; //一个芯片不只有一个spi外设,对于一个spi外设会将此外设挂到spi外设的列表里

	/* other than negative (== assign one dynamically), bus_num is fully
	 * board-specific.  usually that simplifies to being SOC-specific.
	 * example:  one SOC has three SPI controllers, numbered 0..2,
	 * and one board's schematics might show it using SPI-2.  software
	 * would normally use bus_num=2 for that controller.
	 */
	s16			bus_num;

	/* chipselects will be integral to many controllers; some others
	 * might use board-specific GPIOs.
	 */
	u16			num_chipselect;
	//一些钩子函数的定义//
	
};

spi设备的表示:

struct spi_device {
	struct device		dev;
	struct spi_controller	*controller;
	struct spi_controller	*master;	/* compatibility layer */
	u32			max_speed_hz;
	u8			chip_select;
	u8			bits_per_word;
	bool			rt;
	u32			mode;
#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 */
#define	SPI_CS_WORD	0x1000			/* toggle cs after each word */
#define	SPI_TX_OCTAL	0x2000			/* transmit with 8 wires */
#define	SPI_RX_OCTAL	0x4000			/* receive with 8 wires */
#define	SPI_3WIRE_HIZ	0x8000			/* high impedance turnaround */
	int			irq;
	void			*controller_state;
	void			*controller_data;
	char			modalias[SPI_NAME_SIZE];
	const char		*driver_override;
	int			cs_gpio;	/* LEGACY: chip select gpio */
	struct gpio_desc	*cs_gpiod;	/* chip select gpio desc */
	struct spi_delay	word_delay; /* inter-word delay */

	/* 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
	 *  - chipselect delays
	 *  - ...
	 */
};

spi消息的表示:

struct spi_message {
	struct list_head	transfers;

	struct spi_device	*spi;

	unsigned		is_dma_mapped:1;

	/* REVISIT:  we might want a flag affecting the behavior of the
	 * last transfer ... allowing things like "read 16 bit length L"
	 * immediately followed by "read L bytes".  Basically imposing
	 * a specific message scheduling algorithm.
	 *
	 * Some controller drivers (message-at-a-time queue processing)
	 * could provide that as their default scheduling algorithm.  But
	 * others (with multi-message pipelines) could need a flag to
	 * tell them about such special cases.
	 */

	/* completion is reported through a callback */
	void			(*complete)(void *context);
	void			*context;
	unsigned		frame_length;
	unsigned		actual_length;
	int			status;

	/* for optional use by whatever driver currently owns the
	 * spi_message ...  between calls to spi_async and then later
	 * complete(), that's the spi_controller controller driver.
	 */
	struct list_head	queue;
	void			*state;

	/* list of spi_res reources when the spi message is processed */
	struct list_head        resources;
};

3、驱动初始化流程

3.1、spi外设初始化流程
在这里插入图片描述

3.2、下挂设备初始化流程
在这里插入图片描述

四、应用层

1、消息结构 struct spi_ioc_transfer

struct spi_ioc_transfer {
	__u64		tx_buf;
	__u64		rx_buf;

	__u32		len;
	__u32		speed_hz;

	__u16		delay_usecs;
	__u8		bits_per_word;
	__u8		cs_change;
	__u8		tx_nbits;
	__u8		rx_nbits;
	__u8		word_delay_usecs;
	__u8		pad;

	/* If the contents of 'struct spi_ioc_transfer' ever change
	 * incompatibly, then the ioctl number (currently 0) must change;
	 * ioctls with constant size fields get a bit more in the way of
	 * error checking than ones (like this) where that field varies.
	 *
	 * NOTE: struct layout is the same in 64bit and 32bit userspace.
	 */
};

2、应用编程示例
打开设备描述符:
spi_fd = open(devname, O_RDWR); devname为/dev/spidev*.* ,表示第几个spi外设下的第几个设备,如/dev/spidev0.0;

初始化配置读写模式、最大速率、传输字位数:

 ioctl(spi_fd, SPI_IOC_WR_MODE, mode); //mode  为 0  1  2  3 具体看控制的外部器件支持哪种模式
 ioctl(spi_fd, SPI_IOC_RD_MODE, mode);    
 ioctl(spi_fd, SPI_IOC_WR_BITS_PER_WORD, bits);  //bits  可以为 8 
 ioctl(spi_fd, SPI_IOC_RD_BITS_PER_WORD, bits);           
 ioctl(spi_fd, SPI_IOC_WR_MAX_SPEED_HZ, speed);  //被控制器件支持的最大速率    
 ioctl(spi_fd, SPI_IOC_RD_MAX_SPEED_HZ, speed);

读外部器件数据

struct spi_ioc_transfer transfer;
uint8_t spidev_read(uint8_t addr)
{
        unit8_t  out[3]={0},in[3]={0};
        out[0] = rd_cmd; //不同的芯片定义不同,看芯片手册具体描述
        out[1] = addr;   //不同的芯片定义不同
        out[2] = dummy_data; //这里是0x00

        transfer.tx_buf = (unsigned long)out;
        transfer.rx_buf = (unsigned long)in;
        transfer.len = 3;
        int ret = ioctl(spi_file, SPI_IOC_MESSAGE(1), &transfer);
        if (ret >= 3)
        {

            return in[2]; 
        }
        else
        {
            return  0xff;  //error
        }
}

向外部器件写数据

void spidev_write(uint8_t addr, uint8_t data)
{
        unit8_t  out[3]={0};
        out[0] = wr_cmd; //不同的芯片定义不同
        out[1] = addr;   //不同的芯片定义不同
        out[2] = data; 
        transfer.tx_buf = (unsigned long)out;
        transfer.len = 3;
        int ret = ioctl(spi_file, SPI_IOC_MESSAGE(1), &transfer);
        if (ret >= 3)
        {
           // success
        }
        else
        {
           //error
        }
}
  • 21
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值