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注销结构体
实现
- 修改设备树, 首先配置引脚复用, 使用软件片选, 之后在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>; //硬件片选对应的引脚
};
};
参考示例
- 搭建对应的结构, 初始化字符设备
- 使用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);
- 引脚的获取, 从spi->dev.of_node获取节点, 但是引脚在更上一层节点, 使用函数of_get_parent函数, 初始化引脚
- 设置spi_device的参数
spi->mode = SPI_MODE_0; //设置SPI为模式0,也就是CPOL=0.CPHA=0
spi_setup(spi); //设置好以后需要使用spi_setup配置一下
icm20608.private_data = spi;
- 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);