SPI外设驱动相关
SPI原理
SPI 简介
SPI(Serial Peripheral Interface)是由Motorola提出的一种同步串行总线通信协议.
SPI接口不需要进行寻址操作,且为全双工通信,显得简单高效。同时在多个从设备的系统中,每个从设备需要独立的使能信号,硬件上比I2C系统要稍微复杂一些。
SPI具有以下特性:
- 全双工同步通信
- 速率可自定义,支持高达数十mbps
- 有主机/从机的区分
- 支持单主机多从机的连接
- 无应答机制
SPI每个设备都需要连接以下4个信号才能正常工作:
- SCLK:串行时钟信号
- SS/CS:片选信号(当cs信号有效时,才能进行数据传输)
- MISO:主机输入从机输出信号
- MOSI:主机输出从机输入信号
一个SPI Master可连接一个或多个Slave
在连接多个Slave时每个Slave在Master上都需要一个片选信号线
只有片选信号有效的Slave会响应Master----拉低,低有效
SPI 硬件原理
SPI接口在内部硬件实际上是两个简单的移位寄存器,传输的数据为8位,在主器件产生的从器件使能信号和移位脉冲下,按位传输,高位在前,低位在后。在SCLK的下降沿上数据改变,主器件和从器件同时进行传输,主器件的移位寄存器的最高位移入从器件的移位寄存器的最低位,同时从器件的移位寄存器的最高位移入主器件移位寄存器的最低位。因此SPI的每一次传输都是双向传输,通过在主设备和从设备之间移位完成传输操作。
通讯是通过数据交换完成的,这里先要知道SPI是串行通讯协议,也就是说数据是一位一位的传输的。这就是SCLK时钟线存在的原因,由SCK提供时钟脉冲,SDI,SDO则基于此脉冲完成数据传输。数据输出通过 SDO线,数据在时钟上升沿或下降沿时改变,在紧接着的下降沿或上升沿被读取。完成一位数据传输,输入也使用同样原理。这样,在至少8次时钟信号的改变(上沿和下沿为一次),就可以完成8位数据的传输。
SPI 传输模式
传输模式:有4种传输mode,通过cpol和cpha来决定采用哪种mode
- Cpol(clock polarity):决定空闲时sclk的电平极性
- Cpol = 0:空闲时,sclk的电平是:0
- Cpol = 1:空闲时,sclk的电平是:1
- Cpha(clock phase):决定在sclk的前沿还是后沿采集数据
- Cpha = 0:在sclk的前沿采集数据
- Cpha = 1:在sclk的后沿采集数据
在sclk才采集数据期间(前沿或后沿),MISO MOSI不会改变电平。
SPI 时序
CS setup time:传输开始时,CS信号建立时间
CS hold time:传输结束时,CS信号保持时间
CS idle time:两次传输之间,CS的间隔时间
SCK low time:SCK信号低电平持续时间
SCK high time:SCK信号高电平持续时间
SCK period = SCK high time + SCK low time
SPI传输结束模式
-
Normal mode
一次传输完成后,CS重新被拉高
-
De-assert mode
每个byte传输完成后,CS都会重新被拉高
-
Pause mode
传输完成后,CS仍然处于被拉低的状态
SPI状态机
SPI有如下三种状态:
- IDLE
- BUSY
- PAUSE IDLE
SPI开始处于IDLE状态,在传输数据时会进入BUSY状态;
数据传输开始时,SPI会进入BUSY状态;
在数据传输结束后,根据配置的SPI传输模式,SPI会进入IDLE状态或PAUSE IDLE状态,等待下一次传输再次进入BUSY状态。
SPI数据传输
SPI的发送和接收各有32字节的FIFO用于缓存数据,CPU可直接读写FIFO,也可以通过DMA读写。
SPI注册流程
SPI工作流程
SPI配置
1.DTS里将User的节点挂载到使用的SPI channel node里,配置status ,spi-max-frequency等属性,以touch示例如下:
&spi6 { status = "okay"; …… gt9896s@0 { compatible = "goodix,gt9896s"; spi-max-frequency = <1000000>; ... } }
2.User 配置SPI对应的GPIO,dts根据需要配置, 以touch示例如下:
&spi6 { …… pinctrl-names = "gt9896s_reset_active", "gt9896s_reset_suspend", "gt9896s_spi_mode"; pinctrl-0 = <&ctp_gt9896s_reset_active>; pinctrl-1 = <&ctp_gt9896s_reset_suspend>; pinctrl-2 = <&ctp_gt9896s_spi_mode>; …… gt9896s@0 { …… } } &pio { …… ctp_ts_reset_active: reset_active { pins_cmd_dat { pinmux = <PINMUX_GPIO3__FUNC_GPIO3>; output-high; }; }; …… ctp_ts_reset_suspend: reset_suspend { pins_cmd_dat { pinmux = <PINMUX_GPIO3__FUNC_GPIO3>; output-low; }; }; …… ctp_gt9896s_spi_mode: spimode_default { pins_cmd_dat { pinmux = <PINMUX_GPIO164__FUNC_SPI6_B_MI>, //类似macro定义在如下中找到: /kernel-*/include/dt-bindings/pinctrl/mt****-pinfunc.h) <PINMUX_GPIO165__FUNC_SPI6_B_CSB>, <PINMUX_GPIO166__FUNC_SPI6_B_MO>, <PINMUX_GPIO167__FUNC_SPI6_B_CLK>; drive-strength = <2>; }; };
上述drive-strength设成0代表2mA,1代表4mA,…..,7代表16mA(最大), MISO driving需配置slave;
需要注意的是 mediatek,pad-select = <1>; (这个属性是指MTK SPI pad sel功能:SPI内部针对不同GPIO有1个数据通路的切换开关(不影响pin状态,只影响MISO对应的GPIO的数据是否能成功传到SPI里,如果设错,则会读出都是一连串0),这个开关叫pad sel,默认值是0)这里配成1还是0,要根据GPIO mode:A对应0,B(比如PINMUX_GPIO166__FUNC_SPI6_B_MO)对应1;
3.User Driver中增加解析code,以touch示例如下:
/* get pinctrl handler from of node */ core_data->pinctrl = devm_pinctrl_get( core_data->ts_dev->spi_dev->controller->dev.parent); if (IS_ERR_OR_NULL(core_data->pinctrl)) { ts_info("Failed to get pinctrl handler[need confirm]"); core_data->pinctrl = NULL; return -EINVAL; } /* default spi mode */ core_data->pin_spi_mode_default = pinctrl_lookup_state( core_data->pinctrl, "gt9896s_spi_mode"); if (IS_ERR_OR_NULL(core_data->pin_spi_mode_default)) { r = PTR_ERR(core_data->pin_spi_mode_default); ts_err("Failed to get pinctrl state:%s, r:%d", PINCTRL_STATE_SPI_DEFAULT, r); core_data->pin_spi_mode_default = NULL; goto exit_pinctrl_put; } r = pinctrl_select_state(core_data->pinctrl, core_data->pin_spi_mode_default ); if (r < 0) { ts_err("Failed to select int suspend state, r:%d", r); goto err_suspend_pinctrl; }
本文作者:caseyzz
本文链接:https://www.cnblogs.com/caseyzq/p/16926084.html
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步