Linux中SPI驱动调试总结

1.先了解一下spi协议的基本信息,包括spi的工作原理,4中不同模式的区别。

Mode 0 CPOL=0, CPHA=0 
Mode 1 CPOL=0, CPHA=1
Mode 2 CPOL=1, CPHA=0 
Mode 3 CPOL=1, CPHA=1

时钟极性CPOL: 不工作时,时钟信号SCLK的电平
时钟相位CPHA: 即SPI在SCLK第几个边沿开始采样 (0:第一个边沿开始; 1:第二个边沿开始)

Mode0(CLK开始为0,第一个边沿开始采样,那就是上升沿采样)
Mode1(CLK开始为0,第二边沿开始采样,那就是下降沿采样)
Mode2(CLK开始为1,第一个边沿开始采样,那就是下降沿采样)
Mode3 (CLK开始为1,第二个边沿开始采样,那就是上升沿采样)

如下图所示

 

 

2.添加spi设备

             参考dts下的配置

3.内核中spi编程模板,内核中有许多的spi驱动可供参考(driver/spi/*)

                 kernel/driver/spi/spidev.c

4.在调试的时候,先用示波器量一下每跟信号线的信号是否正常。

 spi的波形如下图,不同的工作模式略有不同,但万变不离其宗。

  

   CS(片选)(平常是高电平,通信时被拉低)

   CLK(时钟) 测量clk的频率是否为设定值

   MOSI(主机输出从机输入)

   MISO(主机输入从机输出)

              在某些芯片中MOSI和MISO会合在一根线中

 

    4.1有些芯片要通过拉高或拉低某一引脚来作为主机或从机,这个要看芯片手册。

           4.2之前遇到一种情况,CS没有信号,居然发现芯片的CS引脚没有连接到CPU上,芯片怎么可能正常工作呢

           4.3MISO读回来的数据全部是0

                  4.3.1通过示波器量出来的信号也的确是0,查看芯片手册才知道,原来这个引脚被设置成高阻态了,要拉高另一个引脚才能作为MISO功能。

                  4.3.2根据芯片手册,要读一个寄存器的值,只有发几个特定的字节过去,然后接着读就可以了,可是读回来的值全部是零。从芯片手册中,读寄存器的值的时序为下图

 

代码是这样实现的

int xxx_spi_read(unsigned int addr, unsigned int *val, size_t len)
{
    struct spi_device *spi =xxx_spi;
    struct spi_message message;
    struct spi_transfer x[3];
    int status;
    u8 write_buf[5];
    u8 read_buf[4];

    write_buf[0] =(len == 4) ? xxxx_SPI_CMD_32_READ : xxxx_SPI_CMD_16_READ;
    write_buf[1] = (addr & 0xff000000) >> 24;
    write_buf[2] = (addr & 0x00ff0000) >> 16;
    write_buf[3] = (addr & 0x0000ff00) >> 8;
    write_buf[4] = (addr & 0x000000ff) >> 0;

    spi_message_init(&message);
    memset(x, 0, sizeof(x));

    x[0].len = 5;//(1+4)
    x[0].tx_buf = write_buf;     //命令+地址
    spi_message_add_tail(&x[0], &message);

    x[1].len = 4;//4
    x[1].tx_buf = write_buf;    //dummy(任意字节)
    spi_message_add_tail(&x[1], &message);

    x[2].len = len;            
    x[2].rx_buf = read_buf;  //接收数据
    spi_message_add_tail(&x[2], &message);

    status = spi_sync(spi, &message);

    if (len == 4)
        *val = read_buf[3] | read_buf[2] << 8 | read_buf[1] << 16 |
            read_buf[0] << 24;
    else
        *val = read_buf[1] | read_buf[0] << 8;

    return status;
}

重复跑这段代码,发现第二个spi_transfer发过去的全部是0,x[1].tx_buf = write_buf,明明不是0啊。也就是说第二个和第三个spi_transfer的数据全部丢失。于是就用一个spi_transfer来实现传输,改动如下

int xxx_spi_read(unsigned int addr, unsigned int *val, size_t len)
{
    struct spi_device *spi = xxxx_spi;
    struct spi_message message;
    struct spi_transfer x[1];
    int status;
    u8 write_buf[13];
    u8 read_buf[13];

    write_buf[0] =(len == 4) ? xxxx_SPI_CMD_32_READ : xxxx_SPI_CMD_16_READ;
    write_buf[1] = (addr & 0xff000000) >> 24;
    write_buf[2] = (addr & 0x00ff0000) >> 16;
    write_buf[3] = (addr & 0x0000ff00) >> 8;
    write_buf[4] = (addr & 0x000000ff) >> 0;

    spi_message_init(&message);
    memset(x, 0, sizeof(x));

    x[0].len = 9+len;
    x[0].tx_buf = write_buf;
    x[0].rx_buf = read_buf;
    spi_message_add_tail(&x[0], &message);
    status = spi_sync(spi, &message);

    if (len == 4)
        *val = read_buf[12] | read_buf[11] << 8 | read_buf[10] << 16 |
            read_buf[9] << 24;  //接收到的第10个字节有效
    else
        *val = read_buf[10] | read_buf[9] << 8;

    return status;
}

一个message只有一个transfer,读回来的数据就正常了。这个给spi控制器驱动实现有关,也就是芯片厂商没做好适配造成对的。

 

     4.4写寄存器的信号正常,也有应答信号,但读不了数据。感觉信号都对了,但通信还是有问题,觉得很不合理。于是,将芯片上的电源引脚测量了一遍,发现问题了,某一路电源没有供电(硬件的同事设计的电路),将这一路电源供电上,通信就没有问题了。

 

     4.5在imx平台上,spi的管脚可以功能复用。比如,第一路spi可以多路gpio供选择(如下图),硬件上连接到了那几个gpio上,就需要在设备树上添加相应的信息,具体的可参考对应的pdf。

 

     4.6在调试一个降噪模块时,发现每次发过去的信号,应答波形总是不对。这里,刚开始发送的第一个字节为0x05,通过示波器测量出的波形为

 

       由于这里的spi设置为工作模式为0,那么在数据在CLK的上升沿有效,根据协议读取的数据为1010 000B,就是0xA0。可是0x05,对应的二进制为0000 0101B,原来这里反过来了。说好的高位先传输呢,直接跟cpu厂商那边的工作人员联系一下,原来高位或低位先传输是可以配置的。配置对传输方式后,发出的信号如下,接受到的应答信号也正常了。

 

 

     4.7与上图相比,在每传输完一个字节后,spi的时钟会有一定的延时,这中波形与上图也是一样的。

 

 4.8当波形正确,芯片仍不正常工作时,这时要检测一下硬件电路了。

 

 

以上图为例,要注意的就有以上几点

 6脚需要用100k到220k的电阻来上拉或下拉

 9脚需要供电

 12脚通过高低电平来选择主从模式

 17脚芯片处在主机模式下,该引脚可复位MPU.从机模式先,高低电平来决定工作模式

 18脚要连接到地,如果拉高就可以让spi引脚变为高阻状态

这些在芯片工作不正常时,都是需要认真检查的。

4.9某些芯片需要dummy_clk(无效clk,一个或多个,可能需要修改spi控制器驱动,供spi驱动配置)

4.10 spi一般有fifo和dma两种传输方式,对于时序严格(红外)或者数据量大的传输方式,可配置成dma模式。

  • 6
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值