Linux系统驱动(十八)SPI总线(未整理)

一、SPI总线协议简介

高速、同步、全双工、非差分、总线式
传输速度在几十M

差分总线和非差分总线
非差分总线:受压降影响,通信距离短,IIC、SPI、UART
差分总线:抗干扰能力较强,传输距离相对较远,

485总线理论上可以传输1200m

DB9就是串口,有9个引脚,

四根线

四种工作模式,
MODE0和MODE3常用,

spi总线特点

SPI 是串行外设接口(Serial Peripheral Interface)的缩写。它

是 Motorola 公司推出的一种同步串行接口技术,是一种高

速的,全双工,同步的通信总线。

SPI优点:

支持全双工通信,通信简单,数据传输速率快

1):高速、同步、全双工、非差分、总线式

2):主从机通信模式

缺点:

没有指定的流控制,没有应答机制确认是否接收到数据,

所以跟IIC总线协议比较在数据的可靠性上有一定的缺陷。

spi管脚及模式

可以一主机多从机,具体和那个从机通讯通过cs片选决定。

MISO :主机输入,从机输出

MOSI :主机输出,从机输入

SCK :时钟线(只能主机控制)

CS :片选线

数据传输的四种方式:

CPOL(时钟极性) : 0:时钟起始位低电平,1:时钟起始为高电平

CPHA(时钟相位) :0:第一个时钟周期采样,1:第二个时钟周期采样

spi协议解析
在这里插入图片描述
CPOL=0,CPHA=0:此时空闲态时,SCLK处于低电平,数据

采样是在第1个边沿,也就是 SCLK由低电平到高电平的跳变,

所以数据采样是在上升沿,数据发送是在下降沿。

CPOL=0,CPHA=1:此时空闲态时,SCLK处于低电平,数据

发送是在第1个边沿,也就是 SCLK由低电平到高电平的跳变,

所以数据采样是在下降沿,数据发送是在上升沿。

CPOL=1,CPHA=0:此时空闲态时,SCLK处于高电平,数据

采集是在第1个边沿,也就是 SCLK由高电平到低电平的跳变,

所以数据采集是在下降沿,数据发送是在上升沿。

CPOL=1,CPHA=1:此时空闲态时,SCLK处于高电平,数据

发送是在第1个边沿,也就是 SCLK由高电平到低电平的跳变,

所以数据采集是在上升沿,数据发送是在下降沿。

二、SPI子系统驱动

在这里插入图片描述

锁存器芯片:IO口扩展,可以锁存,稳定输出

(二)SPI子系统API


(三)SPI设备树节点

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"; //1.是否使能
		};

问:如何将控制器驱动和核心层选配到内核中?

将stm32mp157a中spi控制器设备树和核心层选配到内核中

spi控制器驱动配置:(make menuconfig)
Device Drivers —>
[] SPI support —>
<
> STMicroelectronics STM32 SPI controller

spi核心层配置:
Device Drivers —>
[*] SPI support —>

重新编译内核
make uImage LOADADDR=0xc2000000

将编译好的内核拷贝到tftpboot目录下
cp arch/arm/boot/uImage ~/tftpboot/
2.2spi子系统API
1.分配并初始化对象
struct spi_driver {
int (*probe)(struct spi_device *spi);
//匹配成功执行的函数
int (*remove)(struct spi_device *spi);
//分离的时候执行的函数
struct device_driver driver;
//父类
const struct spi_device_id *id_table;
//idtable匹配方式
};
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(变量名)
2.3spi子系统驱动实例
#include <linux/init.h>
#include <linux/module.h>
#include <linux/spi/spi.h>
int m74hc595_probe(struct spi_device
spi)
{
printk(“%s:%s:%d\n”, FILE, func, LINE);
return 0;
}
int m74hc595_remove(struct spi_device
spi)
{
printk(“%s:%s:%d\n”, FILE, func, LINE);
return 0;
}
struct of_device_id oftable[] = {
{.compatible = “hqyj,m74hc595”,},
{}
};
struct spi_driver m74hc595 = {
.probe = m74hc595_probe,
.remove = m74hc595_remove,
.driver = {
.name = “m74hc595”,
.of_match_table = oftable,
}
};
module_spi_driver(m74hc595);
MODULE_LICENSE(“GPL”);

在这里插入图片描述

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);
//同时收发
&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 = "hqyj,m74hc595";
        reg = <0x0>;           //片选的下标
        spi-max-frequency = <10000000>; //10MHz
        //spi-cpol;  //mode0模式
        //spi-cpha;
    };
};

三、代码示例

数码管驱动

#include <linux/module.h>
#include <linux/init.h>
#include <linux/spi/spi.h>
#include <linux/fs.h>
#include "m74hc595.h"

#define CHRNAME "m74hc595"

int major;
struct class *cls;
struct device *dev;
struct spi_device * spidriver;

int code[]={
	0x3f,
    0x06,
    0x5b,
    0x4f,
    0x66,
    0x6d,
    0x7d,
    0x07,
    0x7f,
    0x6f,
    0x77,
    0x7c,
    0x39,
    0x5e,
    0x79,
    0x71
};

int m74hc595_open(struct inode *inode, struct file *file){
    printk("%s:%d\n",__func__,__LINE__);
    return 0;
}

int m74hc595_close(struct inode *inode, struct file *file){
    printk("%s:%d\n",__func__,__LINE__);
    return 0;
}

long m74hc595_ioctl(struct file *file, unsigned int cmd, unsigned long arg){
    u8 data[2]={0};
    int ret;
    switch (cmd){
    case SET_LIGHT_ONE:
        data[0]=0x01;
        data[1]=code[arg];
        ret = spi_write(spidriver,data,2);
        if(ret){
            pr_err("spi_write error:%d\n",__LINE__);
            return -ENAVAIL;
        }
        break;
    case SET_LIGHT_TWO:
        data[0]=0x2;
        data[1]=code[arg]|0x80;
        ret = spi_write(spidriver,data,2);
        if(ret){
            pr_err("spi_write error:%d\n",__LINE__);
            return -ENAVAIL;
        }
        break;

    case SET_LIGHT_THREE:
        data[0]=0x4;
        data[1]=code[arg];
        ret = spi_write(spidriver,data,2);
        if(ret){
            pr_err("spi_write error:%d\n",__LINE__);
            return -ENAVAIL;
        }
        break;
    
    case SET_LIGHT_FOUR:
        data[0]=0x8;
        data[1]=code[arg];
        ret = spi_write(spidriver,data,2);
        if(ret){
            pr_err("spi_write error:%d\n",__LINE__);
            return -ENAVAIL;
        }
        break;
    default:
        pr_err("cmd error\n");
        return -ENAVAIL;
    }
    return 0;
}
struct file_operations fops = {
    .open=m74hc595_open,
    .release=m74hc595_close,
    .unlocked_ioctl=m74hc595_ioctl,
};

int m74hc595_probe(struct spi_device *spi){
    printk("%s:%d\n",__func__,__LINE__);
    spidriver = spi;
    // 1.注册字符设备驱动
    major = register_chrdev(0, CHRNAME, &fops);
    if (major < 0)
    {
        pr_err("register_chrdev error\n");
        return major;
    }
    // 2.自动创建设备节点
    cls = class_create(THIS_MODULE, CHRNAME);
    if (IS_ERR(cls))
    {
        pr_err("class_create error\n");
        unregister_chrdev(major, CHRNAME);
        return PTR_ERR(cls);
    }
    dev = device_create(cls, NULL, MKDEV(major, 0), NULL, CHRNAME);
    if (IS_ERR(dev))
    {
        pr_err("device_create error\n");
        class_destroy(cls);
        unregister_chrdev(major, CHRNAME);
        return PTR_ERR(dev);
    }
    return 0;
}
int m74hc595_remove(struct spi_device *spi){
    printk("%s:%d\n",__func__,__LINE__);
    device_destroy(cls,MKDEV(major,0));
    class_destroy(cls);
    unregister_chrdev(major, CHRNAME);
    return 0;
}

struct of_device_id m74hc595_of_match_table[]={
    { .compatible="hqyj,m74hc595" },
    {},
};
struct spi_driver m74hc595_driver={
    .probe=m74hc595_probe,
    .remove=m74hc595_remove,
    .driver={
        .name="m74hc595",
        .of_match_table=m74hc595_of_match_table,
    }
};
module_spi_driver(m74hc595_driver);
MODULE_LICENSE("GPL");


  • 17
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值