一、SPI子系统组成
I2C子系统与SPI子系统涉及的几乎一样,就是在调用底层API时不一样
1.SPI设备驱动
先上结构体:
SPI BUS:
struct bus_type spi_bus_type = {
.name = "spi",
.dev_groups = spi_dev_groups,
.match = spi_match_device,
.uevent = spi_uevent,
};
SPI BUS的"device结构体":
struct spi_device {
struct device dev;
struct spi_master *master;//重点,用于调用API
u32 max_speed_hz;
u8 chip_select;
u8 bits_per_word;
u16 mode;
....
};
I2C BUS 的"driver结构体":
struct spi_driver {
const struct spi_device_id *id_table;//不支持设备树的id_table
int (*probe)(struct spi_device *spi);
int (*remove)(struct spi_device *spi);
void (*shutdown)(struct spi_device *spi);
struct device_driver driver;//支持设备树的id_table
};
2.spi总线驱动
先上结构体:
struct spi_master {
struct device dev;
struct list_head list;
int (*transfer)(struct spi_device *spi,struct spi_message *mesg);//实现与spi设备通信的函数
int (*transfer_one_message)(struct spi_master *master,struct spi_message *mesg);
int (*unprepare_transfer_hardware)(struct spi_master *master);
int (*prepare_message)(struct spi_master *master,struct spi_message *message);
int (*unprepare_message)(struct spi_master *master,struct spi_message *message);
}
与I2C子系统几乎相同,I2C的适配器就相当于SPI的控制器,I2C的i2c_adapter到了SPI这里就变成了spi_master
步骤如下:
1.在设备树中加入spi控制器
2.写platform driver去的probe中定义了spi_master并且实现了底层的与SPI通信的API然后将spi_master注册进了内核。
这样在spi总线中只要有spi设备加入了dtb文件那么内核就会自动实现将spi_master加入到I2C设备结构体下,以便我们设备驱动的driver能够通过spi设备获取到API,从而实现对spi设备的读写控制。
3.原理性问题不深究
由于我们谈怎么使用所以关于内核如何实现这个spi子系统我们不做很深入研究(比如在注册完spi_master内核是怎么把spi_master传递给注册入spi bus的device的呢?)
二、spi 设备驱动编写
1.步骤
//1.更改设备树
1.如果要使用pinctl子系统,我们就要去配置它复用为spi与其电气属性
如:
pinctrl_ecspi3: icm20608 {
fsl,pins = <
MX6UL_PAD_UART2_TX_DATA__GPIO1_IO20 0x10b0 /* CS */
MX6UL_PAD_UART2_RX_DATA__ECSPI3_SCLK 0x10b1 /* SCLK */
MX6UL_PAD_UART2_RTS_B__ECSPI3_MISO 0x10b1 /* MISO */
MX6UL_PAD_UART2_CTS_B__ECSPI3_MOSI 0x10b1 /* MOSI */
>;
};
2.增加i2c设备节点到对应的i2c大节点下
如:
&ecspi3 {
fsl,spi-num-chipselects = <1>;
cs-gpios = <&gpio1 20 GPIO_ACTIVE_LOW>;//片选引脚要配置GPIO子系统
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_ecspi3>;//必须对应上面的pinctl
status = "okay";
spidev: icm20608@0 {
compatible = "alientek,icm20608";
spi-max-frequency = <8000000>;
reg = <0>;
};
};
//2.编写driver.c
1.定义与初始化struct i2c_driver
/* 传统匹配方式 ID 列表 */
static const struct spi_device_id icm20608_id[] = {
{"alientek,icm20608", 0},
{}
};
/* 设备树匹配列表 */
static const struct of_device_id icm20608_of_match[] = {
{ .compatible = "alientek,icm20608" },
{ /* Sentinel */ }
};
/* SPI 驱动结构体 */
static struct spi_driver icm20608_driver = {
.probe = icm20608_probe,
.remove = icm20608_remove,
.driver = {
.owner = THIS_MODULE,
.name = "icm20608",
.of_match_table = icm20608_of_match,
},
.id_table = icm20608_id,
};
2.实现probe与remove
1.static int xx_probe(struct spi_device *spi);
注册字符设备,实现fops,记录下spi中的spi_master,在open的时候传入filp->private_data,这样就可以在read/write是调用到API
2.static int xx_remove(struct spi_device *spi);
注销与释放字符设备
3.注册i2c_driver
spi_register_driver(&icm20608_driver);
4.注销i2c_driver
spi_unregister_driver(&icm20608_driver);
2.如何使用API
先上结构体:
struct spi_transfer {
const void *tx_buf;
void *rx_buf;
unsigned len;
....
struct list_head transfer_list;
};
struct spi_message {
struct list_head transfers;
struct spi_device *spi;
....
};
写spi设备
unsigned char *txdata;
struct spi_message m;
struct spi_transfer *t
struct spi_device *spi = (struct spi_device *)dev->private_data;//我们将probe获取的spi赋值给了private_data
/* 一共发送 len+1 个字节的数据,第一个字节为
寄存器首地址,len 为要写入的寄存器的集合,*/
*txdata = reg & ~0x80; /* 写数据的时候首寄存器地址 bit8 要清零 */
memcpy(txdata+1, buf, len); /* 把 len 个寄存器拷贝到 txdata 里 */
t->tx_buf = txdata; /* 要发送的数据 */
t->len = len+1; /* t->len=发送的长度+读取的长度 */
spi_message_init(&m); /* 初始化 spi_message */
spi_message_add_tail(t, &m);
ret = spi_sync(spi, &m); /* 同步发送
读spi设备
unsigned char txdata[1];
unsigned char * rxdata;
struct spi_message m;
struct spi_transfer *t
struct spi_device *spi = (struct spi_device *)dev->private_data;//我们将probe获取的spi赋值给了private_data
/* 一共发送 len+1 个字节的数据,第一个字节为
寄存器首地址,一共要读取 len 个字节长度的数据,*/
txdata[0] = reg | 0x80; //读发送要读的寄存器地址(最高位要为1)
t->tx_buf = txdata; /* 要发送的数据 */
t->rx_buf = rxdata; /* 要读取的数据 */
t->len = len+1; /* t->len=发送的长度+读取的长度 */
spi_message_init(&m); /* 初始化 spi_message */
spi_message_add_tail(t, &m);
ret = spi_sync(spi, &m); /* 同步发送