SPI子系统

SPI子系统简述

在这里插入图片描述

  • SPI为同步全双工串口协议
    为了能够为上层提供统一的接口,Linux内核实现SPI子系统,该子系统将底层的实现过程留给驱动工程师来实现,而仅仅提供一个标准的接口函数
    在这里插入图片描述在这里插入图片描述

  • *1实际上控制器驱动相当于st7789v控制器驱动(提供了寄存器),厂商提供。

  • *2 SPI总线是soc厂商提供的总线驱动。

  • *3我们要做的驱动程序就是使其二者联系起来,并且按照一定的逻辑来编写底层接口,

  • 当然在开发过程中linux提供了SPI子系统接口,帮助我们更方便、更规范的去实现。

  • 实际上大多数复杂通用的设备都是用对应的框架

  • *SPI子系统驱动中最底层为SPI控制器(实现最基本的读写操作)

  • *实际上驱动开发分为两个部分,一部分由BSP开发工程师来完成SPI控制器驱动的实现(与SOC硬件非常精密)。另一部分那些SPI提供的接口则是给驱动开发工程师来完成具体驱动,例如开发一个网卡驱动。

SPI设备结构体:
struct spi_device{
	struct device dev;//该成员为设备文件,一般将其设置为 platform 的dev即可。
	u32 max_speed_hz;//最大传输速率,驱动中可用spi_transfer.speed_hz修改
	u8 chip_select;//片选线,默认低电平。可以用 SPI_CS_HIGH 来设置。
	u32 mode;//数据传输模式。可以通过指定 SPI_LSB_FIRST 来设置。
	int irq;//传输时的中断号,该值内核中通过解析设备树来获取
};
  • 实际上设备结构体的初始化并不需要驱动开发工程师来完成,因为已经引入设备树。
    在这里插入图片描述
    spi0: spi@1c68000 { 
    	compatible = "allwinner,sun8i-h3-spi"; //用来匹配SPI控制器驱动程序
    	reg = <0x01c68000 0x1000>;//指定spi控制器的寄存器物理地址
    	interrupts = <GIC_SPI 65 IRQ_TYPE_LEVEL_HIGH>;//指定SPI控制器的中断线为spi第65个中断号,
    	clocks = <&ccu CLK_BUS_SPI0>, <&ccu CLK_SPI0>;
    	clock-names = "ahb", "mod"; //CLK_BUS_SPI0 是 ahb 时钟,CLK_SPI0 是 mod 时钟
    	dmas = <&dma 23>, <&dma 23>; 
    	dma-names = "rx", "tx"; 
    	pinctrl-names = "default"; 
    	pinctrl-0 = <&spi0_pins>; 
    	resets = <&ccu RST_BUS_SPI0>; //resets 属性指定了其复位信号线
    	status = "disabled"; 
    	#address-cells = <1>; 
    	#size-cells = <0>; //#address-cells 和#size-cells 指定了其子节点的 reg 列表格式
    };
    
  • 此时我们将两个SPI设备挂载在SPI0总线上,ili9341控制器的TFT液晶显示屏和MAX6675热电偶探测器
    &spi0 {
    cs-gpios=<&pio 6 0 GPIO_ACTIVE_LOW>,<&pio 6 1 GPIO_ACTIVE_LOW>;//SPI子系统内部属性,指定了SPI主设备的片选线
    status = "okay";
    	ili9341@0 {//ili9341控制器的TFT液晶显示屏
    		compatible = "ilitek,ili9341";
    		reg = <0>;//指定该节点的地址,即第一个设备
    		spi-max-frequency = <15000000>;
    		spi-cpol;
    		spi-cpha;//是 SPI 子系统内部的特有属性,用来指定 SPI 的工作模式:
    		rotate = <270>;
    		bgr;
    		fps = <10>;
    		buswidth = <8>;
    		reset-gpios = <&pio 1 7 GPIO_ACTIVE_LOW>;
    		dc-gpios = <&pio 1 5 GPIO_ACTIVE_LOW>;
    		debug = <0>;
    	};
    	max6675@1 {//MAX6675热电偶探测器
    		compatible = "max,max6675";
    		reg = <1>;//reg = <1>表示挂载的第二个设备;
    		spi-max-frequency = <1000000>;
    	}
    };
    
实例化一个 spi 驱动

spi结构体与platform_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;//该成员为设备驱动结构体,保存设备的一些信息
	};
static struct spi_driver w25qxx_cdev = { 
	.driver = { 
		.name = "w25qxx", 
		.owner = THIS_MODULE, 
	}, 
	.probe = w25qxx_probe, 
	.remove = w25qxx_remove, 
	.id_table = w25qxx_ids,
};
SPI驱动注册与注销:
ret = spi_register_driver(&w25qxx_cdev);//上述定义的w25qxx_cdev的驱动,对其进行注册
spi_unregister_driver(&w25qxx_cdev);//对注册的驱动进行注销
上面等价于module_spi_driver(w25qxx_cdev);
SPI 数据传输
  • Linux将SPI子系统的数据传输抽象为SPI IO模型,该模型类似于客户端通信,我们发送数据被抽象为向客户端发送一条或多条消息的过程。而一条消息又由多个传输包组成。
    在这里插入图片描述
  • 为了将数据在总线传输提供了两种数据结构。
    分别是 spi_message 和 spi_transfer(该结构体是 SPI 数据传输的最小单元)
    struct spi_transfer {
    	const void *tx_buf;//发送数据缓存区
    	void *rx_buf;//接收数据缓存区
    	unsigned len;//发送和接收缓存区的大小,发送和缓存区的大小必须相同
    	u8 bits_per_word;//单次传输数据时的长度,这里的单位为字
    	struct spi_delay cs_change_delay;//指定传输数据之后延时多久再改变 cs 片选线的状态
    };
    
  • 消息事务包含了一个或者多个传输事务,最终数据传输给 SPI 控制器驱动程序的是以消息的方式,因此我们需要知道如何去操作。
    struct spi_message {
    	struct list_head transfers;//传输事物
    	unsigned is_dma_mapped:1;//是否使用dma
    	void (*complete)(void *context);//传输完毕的回调函数
    	void *context;//回调函数的形参
    	unsigned frame_length;//传输长度
    	unsigned actual_length;//实际传输长度
    	int status;//传输状态报告标志,0为成功
    };
    
  • 实现一个完整的数据传输需要将 spi_transfer 添加到 spi_message 中,首先我们需要先初始化消息事务,
    //消息初始化:
    static inline void spi_message_init(struct spi_message *m)
    //现将 spi_transfer 添加到 spi_message 中
    static inline void spi_message_add_tail(struct spi_transfer *t, struct spi_message *m)
    //通知 SPI 控制器驱动程序来进行数据传输
    extern int spi_sync(struct spi_device *spi, struct spi_message *message);//同步传输
    extern int spi_async(struct spi_device *spi, struct spi_message *message);
    同步传输函数,即此时 CPU 将会在此等待传输结束,因此该函数不能用于中断中
    异步传输函数,此时 CPU 发送数据之后会执行其他的,当数据传输完毕后,此时会去调用回调函数
    
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

栋哥爱做饭

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值