SPI驱动理论与实例分析2


前言

DEBUG 用的一些宏:

//将一字节十进制数字转换成二进制 debug用,一般用于查看寄存器每个位的数值
#define PRINTF_BINARY_PATTERN_INT8 "%c %c %c %c %c %c %c %c"
#define PRINTF_BYTE_TO_BINARY_INT8(i)    \
    (((i) & 0x80ll) ? '1' : '0'), \
    (((i) & 0x40ll) ? '1' : '0'), \
    (((i) & 0x20ll) ? '1' : '0'), \
    (((i) & 0x10ll) ? '1' : '0'), \
    (((i) & 0x08ll) ? '1' : '0'), \
    (((i) & 0x04ll) ? '1' : '0'), \
    (((i) & 0x02ll) ? '1' : '0'), \
    (((i) & 0x01ll) ? '1' : '0')

#define PRINTF_BINARY_PATTERN_INT16 \
    PRINTF_BINARY_PATTERN_INT8" "PRINTF_BINARY_PATTERN_INT8
#define PRINTF_BYTE_TO_BINARY_INT16(i) \
    PRINTF_BYTE_TO_BINARY_INT8((i) >> 8),   PRINTF_BYTE_TO_BINARY_INT8(i)
#define PRINTF_BINARY_PATTERN_INT32 \
    PRINTF_BINARY_PATTERN_INT16" "PRINTF_BINARY_PATTERN_INT16
#define PRINTF_BYTE_TO_BINARY_INT32(i) \
    PRINTF_BYTE_TO_BINARY_INT16((i) >> 16), PRINTF_BYTE_TO_BINARY_INT16(i)
#define PRINTF_BINARY_PATTERN_INT64    \
    PRINTF_BINARY_PATTERN_INT32" "PRINTF_BINARY_PATTERN_INT32
#define PRINTF_BYTE_TO_BINARY_INT64(i) \
    PRINTF_BYTE_TO_BINARY_INT32((i) >> 32), PRINTF_BYTE_TO_BINARY_INT32(i)

其实驱动难的真不是软件,而是硬件,特别是当数据手册又臭又长的时候,高通的数据手册会加倍臭长,特别是把寄存器设置给分散得很凌乱的时候……就怕设置少了器件埋下了惊天大雷好吧。
毕竟数据手册又不是只给程序员看的,设备特性才是人家的卖点。
我们要提炼出使用方法,也就是接线和协议,包括协议中的交互规则。

先声明一下,本人真的是很讨厌高通的驱动框架,非常的复杂。
高通的官方 spi 驱动:kernel/drivers/spispi-qup.c
但是一些操作还是可以拿出来说说的。
例如:驱动入口函数

int32_t QUP_Init(T_LcmConfig *pCfg)
{
    T_QUP *pDev = &qupDev;

    if(QUP_InitConfig() < 0)
    {
        LOG_ERROR("qup config init fail.\n");
        return -1;
    }
    pDev->pBuf = (uint16_t *)kmalloc(sizeof(T_QUP), GFP_KERNEL);
    if(pDev->pBuf == NULL)
    {
        LOG_ERROR("kmalloc fail.\n");
        return -1;
    }
    if((spi_register_driver(&qup_SpiDrv) < 0) || (pDev->ucSpiFlag==0))
    {
        LOG_ERROR("register spi driver fail, please check qup kernel support.\n");
        return -1;
    }
    if(Mutex_CreateHdl(&(pDev->tMutex)) < 0)
    {
        LOG_ERROR("Mutex_CreateHdl fail.\n");
        goto Create_Mutex_Fail;
    }
    QUP_Reset();
    QUP_Setup();
    LOG_INFO("qup init success.\n"); 
    return 0;

Create_Mutex_Fail:
    spi_unregister_driver(&qup_SpiDrv);
    return -1;
}

static int32_t qup_InitConfig(void)
{
    T_QUP *pDev   = &qupDev;
    //配置设备管脚
    pDev->tCfg.uiCs     = 2;
    ......

    //配置的管脚初始化
    Gpio_SetDir(pDev->tCfg.uiCs, 1);
    gpio_set_value(pDev->tCfg.uiCs, 1);
    ......

    return 0;
}

之前我们都是把管脚初始化放在 probe 函数的,这里放在驱动入口函数也没问题,只要使用到硬件之前有初始化的就可以了。
而且之前获取 CS 管脚是通过 sysfs 文件系统获取的,而 sysfs 文件系统中的 spi 节点的 CS 属性也是通过设备树写的,这里是竟然是直接写编号!!!
再来看 probe 函数(节省):

static int32_t QUP_SpiProbe(struct spi_device *pSpiDev)
{
    T_QUPDev *pDev = &qupDev;
    int32_t iRet=-1;
    
    LOG_DEBUG("probe spi device '%s' success and infomation as follows: \n", 
                pSpiDev->modalias);
    LOG_DEBUG("max_speed_hz = %d, chip_select=%d, mode=0x%x, bits_per_word=%d", 
                pSpiDev->max_speed_hz, pSpiDev->chip_select, 
                pSpiDev->mode, pSpiDev->bits_per_word);

    pSpiDev->max_speed_hz = pDev->tCfg.uiRate;
    pSpiDev->cs_gpio      = pDev->tCfg.uiCs;        //把 CS 管脚分配给设备
    pSpiDev->mode        |= SPI_MODE_3;
    iRet = spi_setup(pSpiDev);
    if(iRet < 0) 
    {
        LOG_ERROR("qup spi setup fail.\n");
        return -1;
    }
    
    ......
    
    pDev->ucSpiFlag = 1;
    return 0;
}

可以看出 probe 函数中最重要的操作就是把 CS 管脚赋给设备结构体,同时把设备结构体交给 spi_setup 去完成主控器时序参数的初始化。

这里的两个项目是根据公司的模块写的,和别的驱动不太一样,虽然还是驱动的框架,但是用途不太一样。

  1. 对于数量多而简单的驱动,没必要挨个注册,这里的驱动就是集成到主板驱动上,一个主板一个驱动;
  2. 对于复杂的驱动,类似网卡、无线网卡、USB,这些有厂商和内核提供驱动,一般作为模块编进系统;
  3. 将驱动写成 API 的方式集成到主板驱动的好处:
    a. 可以让其他需要用到该驱动的模块直接调用;
    b. 这些设备不需要应用工程师控制,减少文件系统的复杂度。

一、idt8v97003(无线收发器)

原理图

在这里插入图片描述

设备树

spi_0: spi@78b5000 {
    status = "ok";
    pinctrl-0 = <&spi_0_pins>;     //选择spi控制器0的管脚配置,spi控制器0的管脚配置是处理器写死的,不要去改
    pinctrl-names = "default";
    cs-select = <0>;               //选择编号为0的设备
}

其实配置 SPI 最重要的也就是配置管脚复用为 SPI 功能。
SPI 的的从设备有很多,但是我们只会控制 SPI 主控器,如何驱动信息是发给哪一个从设备的呢?
只能通过 CS 编号。SPI 控制器或许有多个 CS 管脚,但是这个不重要,因为不够的可以用 GPIO 代替。
只有被使能的管脚才能和主机通信。所以每次发消息前都是先使能,发完消息就失能。

数据手册

在这里插入图片描述
在这里插入图片描述
设备的地址空间是 0X00 到 0X49,寻址模式是 15 位。也就是说,每个寄存器有15个位。
在传输操作中,如果寄存器0x00 的第5位和第2位设置为1,地址自动递增,否则递减。
在递增模式下,如果地址达到0x49,则将其置为0x00。在递减模式下,如果地址达到0x00,则将其置为0x49。
默认是递增的。
在这里插入图片描述
SPI操作在CSB上有高到低的转换时开始,在CSB上有低到高的转换时停止。如果传输方向位R/nW为1,则为读操作,否则为写操作。
该设备支持多字节的读取或写操作。位A14到A0表示寄存器地址。设备读取或写入数据到这个地址,只要CSB保持在低水平就会继续。设备会根据地址编码位自动增加或减少地址。
在这里插入图片描述
MSB 模式:
如果要写数据到寄存器,那么先发写位+15位地址+8位寄存器数据。
如果要读寄存器的数据,那么先发读位+15位地址。然后就会收到8位数据。
在这里插入图片描述
LSB 模式:
首先操作是一样的,但是地址和读写位、数据位的顺序都是相反的。
在这里插入图片描述
MSB 模式写入多个字节:(寄存器地址会递增,前面说过了,默认是递增的;第一个例子是递减的)
还是之前的操作,但是第二个数据会自动写到下一个地址。
在这里插入图片描述
LSB 模式读多个字节:
略…
在这里插入图片描述
SPI 的时序,对于不连续的地址也可以,写入多个地址,然后接受多个地址的数据。
在这里插入图片描述
宽度大于8位的配置寄存器是为同步访问提供的双缓冲寄存器。
对于这些寄存器,需要先将多字节设置写入缓冲寄存器。新配置直到将1写入寄存器0x0F位0,以将它们从缓冲寄存器传输到活动寄存器。
传输位是自动清除的。多字节配置数据可以从缓冲寄存器或0x01位5寄存器中指定的缓冲区读取模式位进行回读取。
寄存器0x10到0x1D、0x22到0x25和0x29到0x2C都是双缓冲的。

提示:
其实正常j讲到这里就可以了,因为每个 SPI 模块的操作各异,相同点已经讲完了。
接下去的内容会让崩溃,抛开无效寄存器,假如每个寄存器有8个,每个位又有0/1两种情况,两种情况对应不用的功能。
那么就需要了解 0X49 X 8 X 2 个功能,也就是 1168 个功能…文章篇幅有限,会给举个例子,但是具体讲不了。
在这里插入图片描述
所有寄存器功能的地址归类,可以看到的是用不到的和没用的还是挺多的。
在这里插入图片描述
预设寄存器中每个位的意义:
在这里插入图片描述
每个位的功能选择:
在这里插入图片描述
在这里插入图片描述
再讲下去我怕浪费各位同学的青春……总之以后开发时,每个模块的寄存设置不一样,自己要自己学会看数据手册。

整理数据手册

数据手册又不是只给程序员看的,里面芯片介绍、热特性、,所以一定要把所有程序相关的转移出来,做到一目了然的效果。

/* IDT8V97003 register map:
 * 0000~000F Preface registers
 * 0010~0043 Control registers
 * 0044~0049 Status registers
 * 所有数据寄存器皆为8位
 */

/* Preface registers
 * 0000-0001 Startup Control Resigters
 * 0002      Reserved
 * 0003-0006 Device Type, ID and Version Registers
 * 0007      Reserved
 * 0008-000B Unused
 * 000C 000D Vendor ID Control Registers
 * 000E-000F Unused
*/
#define IDT8V97003_SCR_0         0      //高位与对应低位互为镜像,且互为镜像的位需要同时设置 R/W
#define IDT8V97003_SCR_1         0x01   //D5:BufferReadMode  R/W
#define IDT8V97003_DeviceType    0x03   //Device Type  R    default:0000_0110
#define IDT8V97003_DeviceID_0    0x04   //DeviceID     R
#define IDT8V97003_DeviceID_1    0x05   //DeviceID     R
#define IDT8V97003_DeviceVersion 0x06   //DeviceVersion R
#define IDT8V97003_VendorID_0    0x0C   //Vendor ID    R    default:0010_0110
#define IDT8V97003_VendorID_1    0x0D   //Vendor ID    R    default:0000_0100

/* Control registers
 * 0010-0019 Feedback Divider Control Registers
 * 001A-001D Phase Adjustments Control Registers
 * 001E      DSM Control Registers
 * 001F-0020 Manual VCO and Digital Band Selection
 * 0021      Calibration Control Registers
 * 0022-0023 Band Select Clock Divider Control Registers
 * 0024-0025 Reserved
 * 0026-0027 Lock Detect Control Registers
 * 0028      Power Down Control Registers
 * 0029-002C Input Control Registers
 * 002D-002F Charge Pump Current Control Registers
 * 0030      Charge Pump Current Control Registers
 * 0031-0032 Re-Sync Control Registers
 * 0033-003B Output Control Registers
 * 003C      Reserved
 * 003D      Reserved
 * 003E-0040 Unused or Reserved
 * 0041-0043 Reserved
 */
/*FB分压器整数部分 R/W*/
#define IDT8V97003_NIntR_0           0x10  //default: 0001 1111 = d'31 最小值 d'12 R/W
#define IDT8V97003_NIntR_1           0x11
/*FB分压器小数部分,Nfrac是分数除比的分子值。可编程范围为0 ~ (MOD−1)。R/W*/
#define IDT8V97003_NFracR_0          0x12  //default: 1011 1100 1111 1111 1111 1110 1000 0110
#define IDT8V97003_NFracR_1          0x13
#define IDT8V97003_NFracR_2          0x14
#define IDT8V97003_NFracR_3          0x15
/*FB分压器模量部分 ;R/W    */
#define IDT8V97003_NModR_0           0x16  //default: 1111 1111 1111 1111 1111 1110 0000 0000
#define IDT8V97003_NModR_1           0x17
#define IDT8V97003_NModR_2           0x18
#define IDT8V97003_NModR_3           0x19
/*相位调整控制寄存器。相位调整(相位值)必须小于模量(Nmod值)。 R/W*/
#define IDT8V97003_PhaseR_0          0x1A  //default: d'1
#define IDT8V97003_PhaseR_1          0x1B
#define IDT8V97003_PhaseR_2          0x1C
#define IDT8V97003_PhasRe_3          0x1D
/*DSM控制寄存器 R/W*/
#define IDT8V97003_DSMOrderR         0x1E  /* D7~D5 DSM Order   000=OFF 设备工作在整数模式,忽略小数部分。default:111
                                            * D4~D3 Dither Gain 00=LSB Dither (Recommended) (default)
                                            * D2    Shape Dither Enable 0/1=Shaped dither disabled/enabled
                                            * D1    Dither Enable  0/1 = Dither off/on
                                            */
/*校准控制寄存器             R/W*/
#define IDT8V97003_CalibrationR_0    0x1F  /* D7:ManuBandEn 启用校准模式 0/1=自动/手动压控振荡器和数字波段选择。
                                            * D6/D5/D4:0
                                            * D3~D0:VCOManu 手动选择VCO(ManuBandEn=1时) 0000/.../0111=VCO0/.../VCO7  default:1000
                                            */
#define IDT8V97003_CalibrationR_1    0x20  /* D7:0
                                            * D6~D0:手动选择数字波段(ManuBandEn=1时)  0000000/.../1111111=Band/.../Band127 default:10000000
                                            */
#define IDT8V97003_CalibrationR_2    0x21  /* D7:ForceRelockVCO  0/1=正常操作(default)/VCO强制重新校准,自动清除。(当锁相环在整数模式下使用时,在编程反馈分压器值后,必须将该位设置为1。)
                                            * D6:PhAdj           相位调整触发器 0/1=正常操作(default)/触发相位调整一次。自动清除。
                                            * D5:BandSelDisable  禁用波段选择,当写入寄存器0x0010-0x0019时,该位将防止VCO重新校准。 0/1=是/否重新校准 default:0
                                            * D4:ManualReSync    手动重新同步锁相环输出。0/1=正常操作(default)/同步锁相环输出到PFD参考的特定阶段,由相位调整控制寄存器<31:0>指定。自动清除。
                                            * D3~D2:0
                                            * D1~D0:             波段选择/校准分辨率。00/01/10/11=Reserved/Reserved/4x resolution/8x resolution(default)
                                            */
/*频带选择时钟分频控制寄存器               R/W*/
// This value should be set so that FPFD / BndSelDiv is < 100kHz and > 50kHz
#define IDT8V97003_BSCDCR_0          0x22  //BndSelDiv[12:0] default:1010 0000 0000
#define IDT8V97003_BSCDCR_1          0x23  //D7~D5:Unused  D4:BndSelDiv<D12>
/*锁定检测控制寄存器           R/W*/
#define IDT8V97003_LDCR_0            0x26  /* D7~D5:Unused
                                            * D4:LD_Disable  禁用锁定检测           0/1=禁用(default)/启用锁定检测电路
                                            * D3:AutoRecalEn 启用自动重新校准         0/1=禁用(default)/启用(如果检测到LD上的解锁,则会自动重新校准)
                                            */

#define IDT8V97003_LDCR_1            0x27  /* D7~D6:Unused
                                            * D5~4:LDPinMode  00/01=数字锁检测(默认);正常锁探测器功能/校准完成                      10/11=Low/High
                                            * D3:Unused
                                            * D2~D0:锁探测器精度设置(ns)   000=0.375 (default)  001/010/100/101/110/111=0.75/1.5/2.4/5.2/5.2/8.5/8.5
                                            */
/*断电控制寄存器         R/W*/
#define IDT8V97003_PDCR              0x28  /* D7:Reserved
                                            * D6:ref_vreg_pwrdwn  参考输入路径调节器断电控制                0/1=开启调节器(默认)/调节器断电
                                            * D5:pdcp_vreg_pwrdwn 鉴相器和电荷泵调节器断电控制 0/1=开启(默认)/关闭
                                            * D4:fb_vreg_pwrdwn   反馈分频调节器断电控制 0/1=开启(默认)/关闭
                                            * D3:outA_vreg_pwrdwn 输出稳压器断电控制 0/1=开启(默认)/关闭
                                            * D2:outBbuf_vreg_pwrdwn 输出路径调节器和输出b调节器的断电控制  0/1=开启(默认)/关闭
                                            * D1:PDNAnaRegu       模拟调节器断电控制 该位设置为1将关闭以上的所有调节器 default:0
                                            * D0:VCO_En    0/1=禁用所有VCO(将VCO电源电流IVCO降低到46mA(典型值))/正常模式
                                            */
/*输入控制寄存器 R/W*/
#define IDT8V97003_InputControl_0    0x29  //InputControl_0[D7~D0] + InputControl_1[D1~D0]=R[9:0],输入除法值(参考)  default:d'1
#define IDT8V97003_InputControl_1    0x2A  /* D7~D5:Unused
                                            * D4:RefDoubler_Delay  为输入倍频器选择标准或扩展脉冲宽度延迟 0/1=标准脉冲宽度(默认值)。输入参考频率>50MHz时使用/延长脉冲宽度用于低频(<50MHz)
                                            * D3:Input_Type     选择差分或单端输入 0/1=单端输入/差分输入(default)
                                            * D2:RefDoubler_En  启用输入引用加倍器 0/1=disabled/enabled(default)
                                            * D1~D0=R<9>~R<8>
                                            */
#define IDT8V97003_InputControl_2    0x2B  /* D7:Mult_En     MULT Enable  0/1=not enabled(default)/enabled
                                            * D6:Mult_reset  重置参考倍频器模块(MULT) 0/1= is active (default)/is reset 当输入倍增器(MULT)正在使用时,
                                            *                  建议对设备进行适当的MULT设置,保持Mult_reset = 1并将其切换到低(活动),
                                            *                  然后使用TransferOn位(寄存器0F,位0)传输数据,用于双缓冲寄存器,并重新锁定锁相锁(ForceRelock)
                                            * D5~D0:Mult<5:0>  输入时钟的倍频因子。当启用时,乘法器块(Mult)将相位检波器的输入频率乘到更高的频率
                                            */
#define IDT8V97003_InputControl_3    0x2C  /* D7:Mult_mux_ena  为倍频器使能多路复用器 0/1=disabled(default)/enabled
                                            * D6:Mult_d2s_ena  差分到单端块启用倍频器 0/1=disabled(default)/enabled
                                            * D5:Mult_cp_ena   电荷泵启用倍频器 0/1=disabled(default)/enabled
                                            * D4:Mult_force_vchi  倍增器电平强制控制在高电平 0/1=正常操作(default)/倍增器控制电压充电到VDD
                                            * D3:Mult_force_vclow 倍增器电平强制控制在低电平 0/1=正常操作(default)/倍增器控制电压放电到GND
                                            *                      如果没有使用输入倍频器,建议将Mult_force_vclow位设置为1 (High)
                                            * D2~D0:1 0 0
                                            */
/*充电泵控制寄存器 R/W*/
#define IDT8V97003_CPCCR_0           0x2D
#define IDT8V97003_CPCCR_1           0x2E
#define IDT8V97003_CPCCR_2           0x2F
#define IDT8V97003_CPCCR_3           0x30
/*重新同步控制寄存器 R/W*/
#define IDT8V97003_ReSyncControl_0   0x31  //D7~D0:1 0 0 0 1 0 0 0
#define IDT8V97003_ReSyncControl_1   0x32  //D7:AutoReSync  选择自动同步模式 0/1=设备不会将“锁相环输出与引用”同步到特定相位/在重新锁定后,设备将“锁相环输出与引用”同步到特定相位
/*输出控制寄存器 R/W*/
#define IDT8V97003_OutputControl_0   0x33  /* D7~D4:Unused
                                            * D3~D0:RF_OUTA_pwr[3:0]  RF_OUTA电源设置 设置RF_OUTA的输出功率。设置数越高输出功率越大,最高可达最大值,这取决于所使用的输出负载
                                                0000/0001=OFF(default)/最小输出功率设置 1100-1111=最高输出功率
                                            */
#define IDT8V97003_OutputControl_1   0x34  /* D7:1
                                            * D6:Unused
                                            * D5:Mute_until_LD  0/1=输出与锁检测无关(默认)/“锁定检测”值为“高”时,才使能输出
                                            * D4:RF_OUTA_ena    0/1= RF_OUTA is disabled (MUTED & default)/enabled
                                            * D3~D0:1 1 1 0
                                            */

#define IDT8V97003_OutputControl_2   0x35  /* D7~D4:Unused
                                            * D3~D0:RF_OUTB_pwr[3:0]  RF_OUTB电源设置 设置数越高输出功率越大,最高可达最大值,这取决于所使用的输出负载
                                            *                          0000/0001=OFF(default)/最小输出功率设置 1100-1111=最高输出功率
                                            */
#define IDT8V97003_OutputControl_3   0x36  /* D7:1
                                            * D6~D5:Unused
                                            * D4:RF_OUTB_ena  0/1=RF_OUTB is disabled (MUTED & default)/enabled
                                            * D3~D0:1 1 1 0
                                            */
/*0037~003A Reserved*/
#define IDT8V97003_OutputControl_4   0x3B  /* D7:OutDoubler_Ena  射频输出倍频器使能 0/1=1x path (Divide by 1) path Enabled (default)/2x path Enabled (RF Output Doubler Enabled)
                                            *                      当VCO频率不大于9GHz时,OutDoubler_Ena只能设置为1。
                                            *                      对于除1(绕过Output Divider和Doubler), OutDoubler_Ena Bit和Out_Divider_Ena Bit都必须设置为0 (Low)。
                                            * D6:OutDivider_Ena  射频输出分频器使能 0/1=1x or 2x path Enabled (default)/RF Output Divider Enabled
                                            *                      注意:倍频器和差分器两者不能同时使能
                                            * D5:OutDoubler_Freq 射频输出加倍器频率设置 0/1=VCO频率7.0-9.0GHz(默认)/VCO频率5.5-7.0GHz
                                            * D4~D3:Unused
                                            * D2~D0:OutDiv[2:0]  射频输出分压器(M0)设置
                                                                 000/001/011/100/101/110/111
                                                                 =
                                                                 Unused/Div By 2/Div By 4/Div By 8/Div By 16/Div By 32/Unused/Unused
                                            */

/* Status Registers
 * 0044      Digital Lock and Calibration and VCO Status Registers
 * 0045      Digital Band Status Registers
 * 0046-0048 Reserved or Unused
 * 0049      Loss of Lock Status Registers
*/
/*数字校准锁定和VCO状态寄存器 Read only*/
#define IDT8V97003_StatusRegisters_0 0x44  /* D7:DigLock      0/1=锁相环未锁定(default)/锁相环已锁定(根据寄存器0x27中的LDP设置)
                                            * D6:BandSelDone  波段选择完成(校正完成) 0/1=未完成(default)/已完成
                                            * D5~D4:Unused
                                            * D3~D0:VcoSts[3:0]  VCO当前状态 0000/.../0111=VCO0/.../VCO7  1000-1111:Unused
                                            */
/*数字波段状态寄存器 R*/
#define IDT8V97003_StatusRegisters_1 0x45  /* D7:Unused
                                            * D6~D0:BandSts[6:0]  数字频带的当前状态
                                            *                      000 0000/.../111 1111=Band 0/.../Band 127
                                            */
#define IDT8V97003_StatusRegisters_2 0x46  //Reserved
#define IDT8V97003_StatusRegisters_3 0x47  //Reserved
#define IDT8V97003_StatusRegisters_4 0x48  //Unused
/*锁状态缺失寄存器*/
#define IDT8V97003_StatusRegisters_5 0x49

编写驱动

数据结构

#define 97003_REG_NUM 73    //寄存器数量

//idt8v97003寄存器参数
typedef struct
{
    uint8_t     uAddr;      //寄存器地址
    uint32_t    uiData;     //需要写入或者读出的数据
}T_97003RegParam;

//idt8v97003配置
typedef struct
{
    uint32_t            uiSdio;       //三线input/output,四线input
    uint32_t            uiSdo;        //三线input/output,四线output
    uint32_t            uiSclk;       //时钟
    uint32_t            uiCsPin;      //片选
    uint32_t            uiNreset;     //复位引脚
}T_97003Config;

//an41908设备对象结构体
typedef struct
{
    uint32_t            uaRegCache[97003_REG_NUM];    //idt8v97003寄存器cache
    uint8_t             ucSpiFlag;                    //SPI设备探测成功标志, 1.成功  0.失败
    T_97003Config       tCfg;                         //idt8v97003配置
    struct spi_device   *pSpiDev;                     //spi设备指针
}T_97003Dev;

读写函数

//从机使能
static void Idt8v97003_EnableCs(void)
{
    T_97003Dev *pDev = &gIDT8v97003Dev;
    gpio_set_value(pDev->tCfg.uiCsPin, 1);
}

//从机失能
static void Idt8v97003_DisableCs(void)
{
    T_97003Dev *pDev = &gIDT8v97003Dev;
    gpio_set_value(pDev->tCfg.uiCsPin, 0);
}

/* 写寄存器数据
 * uiAddr:从设备寄存器地址
 * ucData:要写入的数据
 */
static uint32_t Idt8v97003_WriteReg(uint8_t uiAddr, uint8_t ucData)
{
    int32_t iRet = -1;
    T_97003Dev *pDev = &gIDT8v97003Dev;
    struct spi_device *pSpidev = pDev->pSpiDev;
    uint8_t ucTxBuf[4] = {0};

    ucTxBuf[0] = 1<<7;
    ucTxBuf[1] = uiAddr;
    ucTxBuf[2] = ucData;

    Idt8v97003_DisableCs();
    iRet = spi_write(pSpidev, ucTxBuf, sizeof(ucTxBuf));
    Idt8v97003_EnableCs();
    if(iRet < 0)
    {
        LOG_ERROR("write REG fail.\n");
        return -1;
    }
    LOG_DEBUG("IDT8V97003 SPI write reg:Addr = \"PRINTF_BINARY_PATTERN_INT8\" \"PRINTF_BINARY_PATTERN_INT8\"\nData = \"PRINTF_BINARY_PATTERN_INT8\"\n",
                PRINTF_BYTE_TO_BINARY_INT8(ucTxBuf[0]),
                PRINTF_BYTE_TO_BINARY_INT8(ucTxBuf[1]),
                PRINTF_BYTE_TO_BINARY_INT8(ucTxBuf[2]));

    return 0;
}

因为芯片业务是控制无线收发器,并没有要读取的数据,所以只需要实现 write 操作。

完成驱动结构体

static int32_t Idt8v97003_SpiProbe(struct spi_device *pSpiDev)
{
    T_97003Dev *pDev = &gIDT8v97003Dev;
    int32_t iRet=-1;

    LOG_DEBUG(DBG_LENS_IRIS, "probe spi device '%s' success and infomation as follows: \n",
              pSpiDev->modalias);
    LOG_DEBUG(DBG_LENS_IRIS, "max_speed_hz = %d, chip_select=%d, mode=0x%x, bits_per_word=%d",
              pSpiDev->max_speed_hz, pSpiDev->chip_select,
              pSpiDev->mode, pSpiDev->bits_per_word);

    pSpiDev->max_speed_hz = 2000000;
    iRet = spi_setup(pSpiDev);
    if(iRet < 0)
    {
        LOG_ERROR("Idt8v97003 spi setup fail.\n");
        return -1;
    }

    pDev->pSpiDev = pSpiDev;    //直接把初始化完成的设备结构体给了设备指针
    pDev->ucSpiFlag = 1;
    return 0;
}

static int32_t Idt8v97003_SpiRemove(struct spi_device *pSpiDev)
{
    LOG_INFO("remove spi device '%s' success.\n", pSpiDev->modalias);
    return 0;
}

static const struct of_device_id Idt8v97003_of_match[] = {
    { .compatible = "qcom,idt8v97003" },
    { /* Sentinel */ }
};

static struct spi_driver gIdt8v97003_SpiDrv =
{
    .driver  =
    {
        .name   = "idt8v97003",
        .owner  = THIS_MODULE,
        .of_match_table = Idt8v97003_of_match, 
    },
    .probe  = Idt8v97003_SpiProbe,
    .remove = Idt8v97003_SpiRemove
};

驱动出入口

int32_t Idt8v97003_Init(T_97003Config *pCfg)
{
    T_97003Dev *pDev = &gIDT8v97003Dev;
    int32_t iRet=-1;

    if(Idt8v97003_InitConfig() < 0)
    {
        LOG_ERROR("bu240 config init fail.\n");
        goto Init_Config_Fail;
    }
    if((spi_register_driver(&gIdt8v97003_SpiDrv) < 0) || (pDev->ucSpiFlag==0))
    {
        LOG_ERROR("register spi driver fail, please check bu240 kernel support.\n");
        goto Register_Spi_Fail;
    }

    Idt8v97003_InitReg();

    LOG_INFO("Idt8v97003 init success.\n");
    return 0;

Register_Spi_Fail:
Init_Config_Fail:
    return -1;
}

void Idt8v97003_Exit(void)
{
    spi_unregister_driver(&gIdt8v97003_SpiDrv);
    LOG_INFO("Idt8v97003 exit success.\n");

}

驱动编写其实也挺自由的,但是也就在那几个函数之间找个顺序填充而已。这里也是驱动入口处完成设备结构体的配置和寄存器的初始化。
用到函数如下:

//设备结构体配置
static int32_t Idt8v97003_InitConfig(void)
{
    T_97003Dev *pDev  = &gIDT8v97003Dev;

    pDev->tCfg.uiSdio   = ;
    pDev->tCfg.uiSclk   = ;
    pDev->tCfg.uiSdo    = ;
    pDev->tCfg.uiCsPin  = ;
    pDev->tCfg.uiNreset = ;

    //配置的管脚初始化
    Gpio_SetDir(pDev->tCfg.uiSdio, 1);
    Gpio_SetDir(pDev->tCfg.uiCsPin, 1);
    Gpio_SetDir(pDev->tCfg.uiNreset, 1);
    gpio_set_value(pDev->tCfg.uiSdio, 1);
    gpio_set_value(pDev->tCfg.uiCsPin, 1);

    return 0;
}

// 从机寄存器初始化,按照数据手册写入数据
static void Idt8v97003_InitReg(void)
{
    Idt8v97003_WriteReg();
}

二、Admv1139(ADC)

原理图

在这里插入图片描述

设备树

spi_1_pins: spi-1-pins {
    pins = "gpio61", "gpio62", "gpio63", "gpio64";
    function = "blsp1_spi";
    drive-strength = <8>;
    bias-disable;
};

spi_1: spi@78b6000 {
    status = "ok";
    pinctrl-0 = <&spi_1_pins>;
    pinctrl-names = "default";
    cs-select = <0>;
};

这里使用的是 spi 控制器1。一般最求性能的话,就不要把多个从设备都挂在同一个主机上。

数据手册

相比前一个,这个模块的寄存器相对来说寄存器少一些,但是功能复杂很多。
在这里插入图片描述
在这里插入图片描述
而且这个数据手册是我喜欢的类型,如下:
在这里插入图片描述
如果要设置这个寄存器,那么可以先选择 MSB 或者 LSB,然后在 spi message 里面 填充地址和写位,再接上上面这8位的具体值。
同样太多寄存器就不一一列举了。
下面是一个例程:

驱动编写

寄存器设置

//写寄存器初始参数
static T_Admv1139RegParam gAdmv1139_InitRegParam[] =
{
    {0x00, 0x18},
    {0x0A, 0x00},
    {0x13, 0x01},
    {0x14, 0x00},
    {0x15, 0x00},
    {0x17, 0x00},
    {0x1A, 0x00},
    {0x1C, 0xFF},
    {0x1F, 0x0C},
    {0x22, 0x00},
    {0x81, 0x0E},
    {0x83, 0x89},
    {0x85, 0x0C},
    {0x87, 0xEC},
    {0xB7, 0x3F},
    {0xB8, 0x3F},
    {0xB9, 0x00},
    {0xBA, 0x00},
    {0xBB, 0x3F},
    {0xBC, 0x3F},
    {0xBD, 0x00},
    {0xBE, 0x00},
    {0xC5, 0x01},
    {0xC6, 0x10},
    {0xCB, 0x10},
    {0xCC, 0x10},
    {0xCD, 0x18},
    {0x101, 0x04},
    {0x103, 0x00},
    {0x104, 0x0E},
    {0x12B, 0x52},
    {0x12C, 0x00},
    {0x12D, 0x00},
    {0x12E, 0x00},
    {0x130, 0x14},
    {0x131, 0x00},
    {0x132, 0x00},
    {0x133, 0x02},
    {0x137, 0x7F},
    {0x138, 0x00},
    {0x139, 0x5C},
    {0x141, 0x10},
    {0x142, 0x10},
    {0x143, 0x18},
    {0x18C, 0x20},
    {0x18F, 0x00},
    {0x190, 0x00},
    {0x192, 0x80},
    {0x193, 0x00},
    {0x197, 0x01}
};

//读寄存器初始参数
static T_Admv1139RegParam gAdmv1139_ReadIDParam[] =
{
    {ADMV1139_REG0004, 0x50},
    {ADMV1139_REG0005, 0x00}
};

//初始化寄存器
static void Admv1139_InitReg(void)
{
    uint32_t i=0;

    for(i=0; i<ARRAY_SIZE(gAdmv1139_InitRegParam); i++)
    {
        Admv1139_WriteReg(gAdmv1139_InitRegParam[i].uiAddr, gAdmv1139_InitRegParam[i].uiData);
    }
}

/*
 * 功能: Tx增益寄存器(0~15dB,1dB step)R/W
 * 参数: ucTx_rf_dsa1,ucTx_rf_dsa2  0~15dB,1dB step
 */
static int32_t Admv1139_Tx_RF_DSA_Reg(uint8_t ucTx_rf_dsa1, uint8_t ucTx_rf_dsa2)
{
    uint8_t ucTx_rf_dsa = (ucTx_rf_dsa2 << 4) | ucTx_rf_dsa1;
    int32_t iRet = 0;

    if((ucTx_rf_dsa1 < 0) || (ucTx_rf_dsa1 > 15) || (ucTx_rf_dsa2 < 0) || (ucTx_rf_dsa2 > 15))
    {
        LOG_ERROR("Write Tx_RF_DSA_Reg fail!Check the reg range in 0~15!\n");
    }

    iRet = Admv1139_WriteReg(0x15, ucTx_rf_dsa);
    if(iRet < 0)
    {
        LOG_ERROR("write Tx_RF_DSA_Reg fail.\n");
        return -1;
    }

    LOG_DEBUG("Set Tx_RF_DSA1:%ddB   Tx_RF_DSA2:%ddB\n", ucTx_rf_dsa1, ucTx_rf_dsa2);

    return 0;
}

/*
 * 功能描述: Rx增益寄存器
 * 输入参数: ucRx_rf_dsa, 0~15dB,1dB step  ucRx_if_dsaq,ucRx_if_dsai  0~4dB,1dB step
 */
static int32_t Admv1139_Rx_RFIF_Reg(uint8_t ucRx_rf_dsa, uint8_t ucRx_if_dsaq, uint8_t ucRx_if_dsai)
{
    uint8_t ucRx_rf_dsa = (0 << 4) | ucRx_rf_dsa;
    uint8_t ucRx_if     = (ucRx_if_dsaq << 4) | ucRx_if_dsai;

    if((ucRx_rf_dsa < 0)  ||
       (ucRx_rf_dsa > 15) ||
       (ucRx_if_dsaq < 0) ||
       (ucRx_if_dsaq > 4) ||
       (ucRx_if_dsai < 0) ||
       (ucRx_if_dsai > 4))
    {
        LOG_ERROR("Write Tx_RF_DSA_Reg fail!Check the range of reg!\n");
    }

    Admv1139_WriteReg(0x14, ucRx_rf_dsa);
    Admv1139_WriteReg(0x17, ucRx_if);

    LOG_DEBUG("Set Rx_RF_DSA:%ddB   Rx_IF_DSAQ:%ddB   Rx_IF_DSAI:%ddB\n",
                ucRx_rf_dsa, ucRx_if_dsaq, ucRx_if_dsai);

    return 0;
}

/*
 * 功能描述: LO nulling
 * 输入参数: void
 */
static void Admv1139_LOnulling(void)
{
    Admv1139_WriteReg(0x101, 0x0B);  //初始化并使能ADC以及ADC时钟
    Admv1139_WriteReg(0x12E, 0x02);  //重置LO nulling loop
    Admv1139_WriteReg(0x12D, 0x40);  //使能LO nulling loop
    Admv1139_WriteReg(0x137, 0x5F);  //power up the ADC reference
    Admv1139_WriteReg(0x103, 0x00);  //select LO nulling for the ADC
    Admv1139_WriteReg(0x12B, 0x53);  //tap the envelop detector
    Admv1139_WriteReg(0x130, 0x01);  //set the optimum LO nulling
    Admv1139_WriteReg(0x12D, 0x49);  //start the LO nulling
    msleep(1);
    Admv1139_WriteReg(0x12D, 0x48);  //finish the LO nulling
    Admv1139_WriteReg(0x12B, 0x52);  //disable envelop tapping
    Admv1139_WriteReg(0x101, 0x08);  //disable the ADC and the ADC clock
}

/*
* 功能: LO nulling disable
* 参数:
*/
static void Admv1139_LOnullingDisable(void)
{
    Admv1139_WriteReg(0x12D, 0x30);  //失能LO nulling loop
    Admv1139_WriteReg(0x131, 0x00);
    Admv1139_WriteReg(0x132, 0x00);
}

SPI 传输函数

//功能描述: Admv1139使能片选
static void Admv1139_EnableCs(void)
{
    T_Admv1139Dev *pDev = &gAdmv1139Dev;

    gpio_set_value(pDev->tCfg.uiCsPin, 1);
}

//Admv1139失能片选
static void Admv1139_DisableCs(void)
{
    T_Admv1139Dev *pDev = &gAdmv1139Dev;

    gpio_set_value(pDev->tCfg.uiCsPin, 0);
}

/*
 * 功能: 写寄存器数据
 * 参数: uiAddr 寄存器地址  ucData: 写入的寄存器数据
 */
static uint32_t Admv1139_WriteReg(uint16_t uiAddr, uint8_t ucData)
{
    int32_t iRet = -1;
    T_Admv1139Dev *pDev = &gAdmv1139Dev;
    struct spi_device *pSpidev = pDev->pSpiDev;
    uint16_t ucTxBuf[3] = {0};

    ucTxBuf[0] = 1<<15 | uiAddr;
    ucTxBuf[2] = ucData;

    Admv1139_DisableCs();
    iRet = spi_write(pSpidev, ucTxBuf, sizeof(ucTxBuf));
    Admv1139_EnableCs();
    if(iRet < 0)
    {
        LOG_ERROR("write REG fail.\n");
        return -1;
    }
    LOG_DEBUG("Admv1139 SPI write reg:Addr = \"PRINTF_BINARY_PATTERN_INT8\" \"PRINTF_BINARY_PATTERN_INT8\"\nData = \"PRINTF_BINARY_PATTERN_INT8\"\n",
                PRINTF_BYTE_TO_BINARY_INT8(ucTxBuf[0]),
                PRINTF_BYTE_TO_BINARY_INT8(ucTxBuf[1]),
                PRINTF_BYTE_TO_BINARY_INT8(ucTxBuf[2]));

    return 0;
}

/*
 * 功能: 读寄存器数据
 * 参数: uiAddr 寄存器地址  ucData: 写入的寄存器数据
 */
static uint32_t Admv1139_ReadReg(uint8_t uiAddr, uint8_t ucData)
{
    int32_t iRet = -1;
    T_Admv1139Dev *pDev = &gAdmv1139Dev;
    struct spi_device *pSpidev = pDev->pSpiDev;
    uint8_t ucTxBuf[4] = {0};

    ucTxBuf[0] = 1<<7;
    ucTxBuf[1] = uiAddr;
    ucTxBuf[2] = ucData;

    Admv1139_DisableCs();
    iRet = spi_write(pSpidev, ucTxBuf, sizeof(ucTxBuf));
    Admv1139_EnableCs();
    if(iRet < 0)
    {
        LOG_ERROR("write REG fail.\n");
        return -1;
    }
    LOG_DEBUG("Admv1139 SPI write reg:Addr = \"PRINTF_BINARY_PATTERN_INT8\" \"PRINTF_BINARY_PATTERN_INT8\"\nData = \"PRINTF_BINARY_PATTERN_INT8\"\n",
                PRINTF_BYTE_TO_BINARY_INT8(ucTxBuf[0]),
                PRINTF_BYTE_TO_BINARY_INT8(ucTxBuf[1]),
                PRINTF_BYTE_TO_BINARY_INT8(ucTxBuf[2]));

    return 0;
}

注册驱动

static int32_t Admv1139_SpiProbe(struct spi_device *pSpiDev)
{
    T_Admv1139Dev *pDev = &gAdmv1139Dev;
    int32_t iRet=-1;

    LOG_DEBUG(DBG_LENS_IRIS, "probe spi device '%s' success and infomation as follows: \n",
              pSpiDev->modalias);
    LOG_DEBUG(DBG_LENS_IRIS, "max_speed_hz = %d, chip_select=%d, mode=0x%x, bits_per_word=%d",
              pSpiDev->max_speed_hz, pSpiDev->chip_select,
              pSpiDev->mode, pSpiDev->bits_per_word);

    pSpiDev->max_speed_hz = 2000000;
    iRet = spi_setup(pSpiDev);
    if(iRet < 0)
    {
        LOG_ERROR("bu240 spi setup fail.\n");
        return -1;
    }

    pDev->pSpiDev = pSpiDev;
    pDev->ucSpiFlag = 1;
    return 0;
}

static int32_t Admv1139_SpiRemove(struct spi_device *pSpiDev)
{
    LOG_INFO("remove spi device '%s' success.\n", pSpiDev->modalias);
    return 0;
}

驱动出入口

static int32_t Admv1139_InitConfig(void)
{
    T_Admv1139Dev *pDev  = &gAdmv1139Dev;
    T_DevType tDevType   = DEV_NULL;

    //根据平台产品型号配置并初始化管脚
    tDevType = Config_GetDevType();
    switch(tDevType)
    {
        case DEV_PTP4100:
        {
            pDev->tCfg.uiCenPin = ;
            pDev->tCfg.uiSdo    = ;
            pDev->tCfg.uiSclk   = ;
            pDev->tCfg.uiCsPin  = ;
            pDev->tCfg.uiLoad   = ;
            pDev->tCfg.uiTx_en  = ;
            pDev->tCfg.uiRx_en  = ;
            break;
        }
        default:
        {
            LOG_ERROR("unsuppot device type=%d.\n", tDevType);
            return -1;
        }
    }
    //配置的管脚初始化
    Gpio_SetDir(pDev->tCfg.uiCenPin, 1);
    Gpio_SetDir(pDev->tCfg.uiSdo, 1);
    Gpio_SetDir(pDev->tCfg.uiCsPin, 1);
    Gpio_SetDir(pDev->tCfg.uiLoad, 1);
    Gpio_SetDir(pDev->tCfg.uiTx_en, 1);
    gpio_set_value(pDev->tCfg.uiCenPin, 1);
    gpio_set_value(pDev->tCfg.uiSdo, 1);
    gpio_set_value(pDev->tCfg.uiCsPin, 1);
    gpio_set_value(pDev->tCfg.uiLoad, 0);
    gpio_set_value(pDev->tCfg.uiTx_en, 0);

    return 0;
}

//Admv1139驱动加载入口
int32_t Admv1139_Init(T_97003Config *pCfg)
{
    T_Admv1139Dev *pDev = &gAdmv1139Dev;
    int32_t iRet=-1;

    if(Admv1139_InitConfig() < 0)
    {
        LOG_ERROR("bu240 config init fail.\n");
        goto Init_Config_Fail;
    }
    if((spi_register_driver(&gAdmv1139_SpiDrv) < 0) || (pDev->ucSpiFlag==0))
    {
        LOG_ERROR("register spi driver fail, please check bu240 kernel support.\n");
        goto Register_Spi_Fail;
    }

    Admv1139_InitReg();
    LOG_INFO("Admv1139 init success.\n");
    return 0;

Register_Spi_Fail:
Init_Config_Fail:
	return -1;
}

//功能描述: Admv1139驱动卸载入口
void Admv1139_Exit(void)
{
    spi_unregister_driver(&gAdmv1139_SpiDrv);
    LOG_INFO("Admv1139 exit success.\n");
}

学习 SPI 驱动只要是学习一个框架,当然框架怎么好维护就怎么写。

代码中用到了几个封装好的函数:

/*
 * 功能: 设置管脚方向
 * 参数: uiPinId: 管脚编号
 *       uiDir: 管脚方向 0.input     1.output
 */
int32_t Gpio_SetDir(uint32_t uiPinId, uint32_t uiDir)
{
    if(gpio_is_valid(uiPinId) == 0)
    {
        LOG_ERROR("gpio%d is invalid.\n", uiPinId);
        return -1;
    }
    if(uiDir == 0)
        gpio_direction_input(uiPinId); 
    else
        gpio_direction_output(uiPinId, 0);

    return 0;
}

/*
 * 功能: 配置gpio
 * 参数: uiPinId: 管脚编号
 *       uiDir: 管脚方向 0.input     1.output
 *       uiLevel: 默认电平
 */
int32_t Gpio_Config(uint32_t uiPinId, uint32_t uiDir, uint32_t uiLevel)
{
    if(gpio_is_valid(uiPinId) == 0)
    {
        LOG_ERROR("gpio%d is invalid.\n", uiPinId);
        return -1;
    }
    if(uiDir == 0)
        gpio_direction_input(uiPinId); 
    else
        gpio_direction_output(uiPinId, !!uiLevel);

    return 0;
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值