XuSenfeng

个人站

复读了,更新随缘,有的文件不全或者图片缺失具体看我的笔记库(https://github.com/XuSenfeng/note)


SPI

目录

SPI

和I2C类似, 主要的结构体是spi_master及认购提, 主要的实现就是个结构体的实现, 有通过控制器和外设交流的通信函数

主要实现的函数有transfer或者transfer_one_message函数, 6ull主机使用的是后者

6u自己使用的结构体叫spi_imx_data结构体, 实现函数tx和rx

spi_imx_data结构体中有spi_master, 还有函数setup_transfer以及txrx_bufs函数

setup_transfer: 设置tx, rx的绑定函数

  • spi_master
struct spi_master *spi_alloc_master(struct device *dev, 											unsigned size)

申请结构体, 同时申请片选以及spi_imx_data, 之后再进行处理

void spi_master_put(struct spi_master *master)

释放

intspi_register_master(struct spi_master *master)

注册

void spi_unregister_master(struct spi_master *master)

注销

实现

在设备树中使用cs-gpios定义片选信号, 实际使用的是软件片选, 所以复用为gpio

struct spi_device {
	struct device		dev;
	struct spi_master	*master;
	u32			max_speed_hz;
	u8			chip_select;
	u8			bits_per_word;
	u16			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 */
	int			irq;
	void			*controller_state;
	void			*controller_data;
	char			modalias[SPI_NAME_SIZE];
	int			cs_gpio;	/* chip select gpio */

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

每一个spi_device都有一个spi_master, 挂载在某一个spi控制器, 由设备树创建, 这个的mode参数需要自己设置

spi_driver

struct spi_driver {
	const struct spi_device_id *id_table;
	int			(*probe)(struct spi_device *spi);
	int			(*remove)(struct spi_device *spi);
	void			(*shutdown)(struct spi_device *spi);
	struct device_driver	driver;
};

使用函数spi_register_driver注册结构体, spi_unregister_driver注销结构体

实现

  1. 修改设备树, 首先配置引脚复用, 使用软件片选, 之后在ecspi3节点下面创建设备
&ecspi1 {
	fsl,spi-num-chipselects = <1>;	//片选信号的数量
	cs-gpios = <&gpio4 9 0>;		//片选引脚
	pinctrl-names = "default";
	pinctrl-0 = <&pinctrl_ecspi1>;
	status = "okay";

	flash: m25p80@0 {	//对应的芯片,后面的0表示接到的硬件片选上
		#address-cells = <1>;
		#size-cells = <1>;
		compatible = "st,m25p32";
		spi-max-frequency = <20000000>;	//最大的频率
		reg = <0>;	//硬件片选对应的引脚
	};
};

参考示例

  1. 搭建对应的结构, 初始化字符设备
  2. 使用SPI进行读写 用到两个重要的结构体 spi_transfer, spi_message
struct spi_transfer {
	const void	*tx_buf;
	void		*rx_buf;
	unsigned	len;

	dma_addr_t	tx_dma;
	dma_addr_t	rx_dma;
	struct sg_table tx_sg;
	struct sg_table rx_sg;

	unsigned	cs_change:1;
	unsigned	tx_nbits:3;
	unsigned	rx_nbits:3;
#define	SPI_NBITS_SINGLE	0x01 /* 1bit transfer */
#define	SPI_NBITS_DUAL		0x02 /* 2bits transfer */
#define	SPI_NBITS_QUAD		0x04 /* 4bits transfer */
	u8		bits_per_word;
	u16		delay_usecs;
	u32		speed_hz;

	struct list_head transfer_list;
};

tx_buf发送的数据, rx_buf读取的数据, len数据的长度, 不能直接使用, 要封装为spi_message

struct spi_message {
	struct list_head	transfers;

	struct spi_device	*spi;

	unsigned		is_dma_mapped:1;

	void			(*complete)(void *context);
	void			*context;
	unsigned		frame_length;
	unsigned		actual_length;
	int			status;

	struct list_head	queue;
	void			*state;
};

使用之前进行初始化

  static inline void spi_message_init(struct spi_message *m)

之后把spi_transfer添加到队列中, list_head使用这个链表进行连接

  spi_message_add_tail(struct spi_transfer *t, struct 							spi_message *m)

最后使用函数进行传输

  spi_sync(struct spi_device *spi, struct spi_message 					*message);
  1. 引脚的获取, 从spi->dev.of_node获取节点, 但是引脚在更上一层节点, 使用函数of_get_parent函数, 初始化引脚
  2. 设置spi_device的参数
    spi->mode = SPI_MODE_0;  //设置SPI为模式0,也就是CPOL=0.CPHA=0
    spi_setup(spi);	//设置好以后需要使用spi_setup配置一下
    icm20608.private_data = spi;
  1. spi读写寄存器, 设置
static s32 icm20608_write_regs(struct icm20608_dev *dev, u8 reg, u8 *buf, int len)
{
    unsigned char txdata[1]; 
    struct spi_message m;
    struct spi_transfer *t;

    struct spi_device *spi = dev->private_data;
	//发送写的第一位寄存器
    gpio_set_value(dev->cs_gpio, 0);

    t = kzalloc(sizeof(struct spi_transfer), GFP_KERNEL);

    txdata[0] = reg & ~0x80;
    t->tx_buf =txdata;
    t->len = 1;

    spi_message_init(&m);
    spi_message_add_tail(t, &m);
    spi_sync(spi, &m);
    
    //读取数据
    t->tx_buf = buf;
    t->len = len;

    spi_message_init(&m);
    spi_message_add_tail(t, &m);
    spi_sync(spi, &m);

    gpio_set_value(dev->cs_gpio, 1);
    kfree(t);
    return 0;
}

也可以直接使用spi_read, spi_write函数

static int icm20608_read_regs(struct icm20608_dev *dev, u8 reg, 				void *buf, int len)
{
    struct spi_device *spi = dev->private_data;
    u8 data = reg | 0x80;
    gpio_set_value(dev->cs_gpio, 0);

    spi_write(spi, &data, 1);
    spi_read(spi, buf, len);

    gpio_set_value(dev->cs_gpio, 1);

}

使用官方的片选

每次读写的时候把寄存器和数据合二为一

使用函数

int spi_write_then_read(struct spi_device *spi,const void 	*txbuf, unsigned n_tx,void *rxbuf, unsigned n_rx);