1.spi总线
1.1spi简介
1> SPI接口是Motorola 首先提出的全双工同步串行外围接口,
2> 采用主从模式(Master Slave)架构, 支持多slave模式应用,一般仅支持单Master。
3> 时钟由Master控制,在时钟移位脉冲下,数据按位传输,高位在前,低位在后(MSB first);
4> SPI接口有2根单向数据线,为全双工通信,目前应用中的数据速率可达几Mbps的水平。
5> SPI总线被广泛地使用在FLASH、ADC、LCD等设备与MCU间,要求通讯速率较高的场合。
2. SPI总线的硬件连接
SPI接口共有4根信号线,分别是:
1> 设备选择线(从设备的选择线:通过片选线决定主机和哪个从机进行通信的):
NCS NSS /CS /SS(引脚的名字上边有一个横杠表示低电平有效)
2> 时钟线 : CLK SCLK SCK SCL
3> 串行输出数据线 : MOSI
4> 串行输入数据线 : MISO
M : Master S : Slave I : Input O : Output
• (1)MOSI:主器件数据输出,从器件数据输入
• (2)MISO:主器件数据输入,从器件数据输出
• (3)SCLK :时钟信号,由主器件产生
• (4)/SS:从器件使能信号,由主器件控制(片选)
3. spi时序解析
1> 起始信号 : NSS信号线由高变低,是SPI通讯的起始信号
2> 结束信号 : NSS信号线由低变高,是SPI通讯的停止信号
3> 数据传输:SPI使用MOSI及MISO信号线来传输数据,使用SCK信号线进行数据同步。MOSI及MISO数据线在SCK的每个时钟周期传输一位数据,且数据输入输出是同时进行的。SPI每次数据传输可以 8 位或 16 位为单位,每次传输的单位数不受限制。
4> 数据传输:在时钟的上升沿期间,主机向MOSI数据线上写入数据,同时从机向MISO上写入数据;
在时钟的下降沿期间,主机从MISO数据线上读取数据,同时从机从MOSI线上读取数据。
1> 在SPI操作中,最重要的两项设置就是时钟极性(CPOL)和时钟相位(CPHA)这两项即是主从设备间数据采样的约定方式。
2> 时钟极性CPOL : 设置时钟空闲时的电平
当CPOL = 0 ,SCK引脚在空闲状态保持低电平;
当CPOL = 1 ,SCK引脚在空闲状态保持高电平。
3> **时钟相位CPHA :**设置数据采样时的时钟沿
当 CPHA=0 时,MOSI或 MISO 数据线上的信号将会在 SCK时钟线的奇数边沿被采样
当 CPHA=1时, MOSI或 MISO 数据线上的信号将会在 SCK时钟线的偶数边沿被采样
SPI总线有四种通信的模式,具体使用那种通信模式进行通信,需要通信的双方约定一个通信模式。
4. spi驱动框架
配置spi核心层和spi控制器驱动到内核中
spi控制器驱动配置:
Device Drivers —>
[*] SPI support ---> <*> STMicroelectronics STM32 SPI controller
spi核心层配置:
CONFIG_SPI_MASTER=y
重新编译内核
make uImage LOADADDR=0xc2000000
将编译好的内核拷贝到tftpboot目录下
5.spi设备驱动的API
1.分配并初始化对象
struct spi_driver {
int (*probe)(struct spi_device *spi);
int (*remove)(struct spi_device *spi);
struct device_driver driver;
};
struct device_driver {
const char *name;
const struct of_device_id *of_match_table;
}
2.注册
#define spi_register_driver(driver) \
__spi_register_driver(THIS_MODULE, driver)
3.注销
void spi_unregister_driver(struct spi_driver *sdrv)
4.一键注册的宏
module_spi_driver(结构体变量名);
6.spi驱动框架
#include <linux/init.h>
#include <linux/module.h>
#include <linux/spi/spi.h>
int myspi_probe(struct spi_device* spi)
{
printk("%s:%d\n", __func__, __LINE__);
return 0;
}
int myspi_remove(struct spi_device* spi)
{
printk("%s:%d\n", __func__, __LINE__);
return 0;
}
struct of_device_id oftable[] = {
{.compatible = "st,myspi",},
{}
};
MODULE_DEVICE_TABLE(of,oftable);
struct spi_driver myspi = {
.probe = myspi_probe,
.remove = myspi_remove,
.driver = {
.name = "hello",
.of_match_table = oftable,
},
};
module_spi_driver(myspi);
MODULE_LICENSE("GPL");
7. spi控制器设备树
stm32mp151.dtsi
spi4: spi@44005000 {
#address-cells = <1>;
#size-cells = <0>;
compatible = "st,stm32h7-spi";
reg = <0x44005000 0x400>;
interrupts = <GIC_SPI 84 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&rcc SPI4_K>;
resets = <&rcc SPI4_R>;
dmas = <&dmamux1 83 0x400 0x01>,
<&dmamux1 84 0x400 0x01>;
dma-names = "rx", "tx";
power-domains = <&pd_core>;
status = "disabled";
};
7.1参考内核帮助文档编写自己的设备树
&spi4{
pinctrl-names = "default", "sleep";
pinctrl-0 = <&spi4_pins_b>;
pinctrl-1 = <&spi4_sleep_pins_b>;
cs-gpios = <&gpioe 11 0>;
status = "okay";
m74hc595@0{
compatible = "st,myspi";
reg = <0>;
spi-max-frequency = <10000000>; //10Mhz
};
};
8.spi相关的结构体及函数
int spi_write(struct spi_device *spi, const void *buf, size_t len) //发数据
int spi_read(struct spi_device *spi, void *buf, size_t len) //接收数据
int spi_write_then_read(struct spi_device *spi, //同时收发
const void *txbuf, unsigned n_tx,
void *rxbuf, unsigned n_rx);