[①ADRV902x]: SPI控制接口,API函数

前言

本篇文章是针对ADI公司ADRV902x系列射频器件中SPI(Serial Peripherial Interface)控制接口,参考设计中相关API函数的学习总结。注意相关知识是基于通用SPI之上,并且针对ADRV902x器件的。每个SPI寄存器都是8位的位宽。

SPI bus signals

SPI总线包括四个信号:

C S ‾ \overline{CS} CS:

C S ‾ \overline{CS} CS是低电平有效片选信号,作为baseband processor对transceiver的使能信号,当 C S ‾ \overline{CS} CS为高电平时,transceiver忽略clock和data信号。在传输中强制拉高 C S ‾ \overline{CS} CS电平,会使整个或者部分传输失败。transceiver SPI_EN pin的输入。

S C L K SCLK SCLK:

S C L K SCLK SCLK是串行接口参考时钟信号,由baseband processor提供,频率在10 MHz ~ 25 MHz之间,当 C S ‾ \overline{CS} CS为低电平时有效。transceiver SPI_CLK pin的输入。

S D I O SDIO SDIO S D O SDO SDO:

当SPI配置为4-wire bus(4线)模式时,会使用两个data信号:SDIO和SDO。SDIO是baseband processor data的输出和transceiver的输入,使用的是 SPI_DIO pin。与之相反的,SDO是transceiver data的输入和baseband processor的输入,使用的是SPI_DO pin。当SPI配置为3-wire bus(3线)模式时,SDIO信号用作双工模式,不使用SDO。

SPI data transfer protocal

ADRV902x器件SPI的传输协议包括了Phase1和Phase2,Phase1是control cycle,主要是向transceiver写入一些控制字,比如说接下来的数据是读操作还是写操作,也传输相应的寄存器地址。
Phase2是data field transfer cycle,顾名思义就是传输8-bit SPI寄存器的值。
Phase1 instruction format(16-bit):

R / W ‾ R/\overline{W} R/W:bit 15,logic high表示为读操作,logic low表示写操作。
D 14 : D 0 D14:D0 D14:D0:SPI寄存器的起始地址,如果地址无效,在写操作中,写入的比特会被忽略,在读操作中,会返回全0。

SPI configuration using API function

在ADI的参考设计中(https://www.analog.com/en/design-center/landing-pages/001/transceiver-evaluation-software.html)提供了SPI相关的API函数。
关于SPI的配置可以调用adi_adrv9025_SpiCfgSet()。这个函数同样会在adi_adrv9025_Initialize()和 adi_adrv9025_PreMcsInit_v2()这些顶层的初始化函数中被调用。
下面是adi_adrv9025_SpiCfgSet()在参考设计中的函数原型:

/**
* \brief Sets the ADRV9025 device SPI settings (3wire/4wire, msbFirst, etc).
*
* This function will use the settings in the passed spi structure parameter
* to set SPI stream mode, address auto increment direction, msbFirst/lsbfirst,
* and 3wire/4wire mode for the ADRV9025 SPI controller.  The ADRV9025 device
* always uses SPI MODE 0 (CPHA=0, CPOL=0) and a 16-bit instruction word.
*
* \pre This function is a helper function and does not need to be called
*      directly by the user.
*
* \dep_begin
* \dep{device->common.devHalInfo}
* \dep{init->spiSettings->MSBFirst}
* \dep{init->spiSettings->enSpiStreaming}
* \dep{init->spiSettings->autoIncAddrUp}
* \dep{init->spiSettings->fourWireMode}
* \dep{init->spiSettings->cmosPadDrvStrength}
* \dep_end
*
* \param device Structure pointer to ADRV9025 device data structure
* \param spi Pointer to ADRV9025 SPI controller settings - not platform hardware SPI settings
*
* \retval ADI_COMMON_ACT_WARN_RESET_LOG Recovery action for log reset
* \retval ADI_COMMON_ACT_ERR_RESET_INTERFACE Recovery action for SPI reset required
* \retval ADI_COMMON_ACT_NO_ACTION Function completed successfully, no action required
*/
int32_t adi_adrv9025_SpiCfgSet(adi_adrv9025_Device_t*      device,
                               adi_adrv9025_SpiSettings_t* spi);

从中可以看出关于SPI的相关参数的配置是通过adi_adrv9025_SpiSettings_t这个结构体实现。

/**
 * \brief Data structure to hold SPI settings for all system device types
 */
typedef struct adi_adrv9025_SpiSettings
{
    uint8_t msbFirst;                                /*!< 1 = MSB First, 0 = LSB First Bit order for SPI transaction */
    uint8_t enSpiStreaming;                          /*!< Not Recommended - most registers in ADRV9025 API are not consecutive */
    uint8_t autoIncAddrUp;                           /*!< For SPI Streaming, set address increment direction. 1= next addr = addr+1, 0:addr = addr-1 */
    uint8_t fourWireMode;                            /*!< 1: Use 4-wire SPI, 0: 3-wire SPI (SDIO pin is bidirectional). NOTE: ADI's FPGA platform always uses 4-wire mode */
    adi_adrv9025_CmosPadDrvStr_e cmosPadDrvStrength; /*!< Drive strength of CMOS pads when used as outputs (SDIO, SDO, GP_INTERRUPT, GPIO 1, GPIO 0) */
} adi_adrv9025_SpiSettings_t;

下面的表格是对结构体中参数的介绍:

[①ADRV902x]: SPI控制接口,API函数_第1张图片

SPI write/read API

SPI的读写在上层可以调用参考设计中的adi_adrv9025_SpiByteWrite()和adi_adrv9025_SpiByteRead()这两个API。

/**
* \brief writes a byte of data to the part.
*
* \dep_begin
* \dep{device->halInfo}
* \dep_end
*
* \param device Pointer to the ADRV9025 device data structure.
* \param addr the address of the register to write to.
* \param data the value to write to the register.
*
* \retval ADRV9025_ACT_WARN_RESET_LOG Recovery action for log reset
* \retval ADRV9025_ACT_ERR_CHECK_PARAM Recovery action for bad parameter check
* \retval ADRV9025_ACT_ERR_RESET_SPI Recovery action for SPI reset required
* \retval ADRV9025_ACT_NO_ACTION Function completed successfully, no action required
*
*/
int32_t adi_adrv9025_SpiByteWrite(adi_adrv9025_Device_t* device,
                                  uint16_t               addr,
                                  uint8_t                data);
                                  
/**
* \brief reads a byte of data from the part.
*
* \dep_begin
* \dep{device->halInfo}
* \dep_end
*
* \param device Pointer to the ADRV9025 device data structure.
* \param addr the address of the register to read from.
* \param readData a pointer to a location to write the register data to.
*
* \retval ADRV9025_ACT_WARN_RESET_LOG Recovery action for log reset
* \retval ADRV9025_ACT_ERR_CHECK_PARAM Recovery action for bad parameter check
* \retval ADRV9025_ACT_ERR_RESET_SPI Recovery action for SPI reset required
* \retval ADRV9025_ACT_NO_ACTION Function completed successfully, no action required
*
*/
int32_t adi_adrv9025_SpiByteRead(adi_adrv9025_Device_t* device,
                                 uint16_t               addr,
                                 uint8_t*               readData);                                  

在这两个函数中底层的实现都会调用HAL(Hardware Abstraction Layer)接口中的API函数,其实它是两个函数指针:

/* SPI Interface */
int32_t (*adi_hal_SpiWrite)(void*         devHalCfg,
                            const uint8_t txData[],
                            uint32_t      numTxBytes) = NULL;

int32_t (*adi_hal_SpiRead)(void*         devHalCfg,
                           const uint8_t txData[],
                           uint8_t       rxData[],
                           uint32_t      numRxBytes) = NULL;

在定义平台platform时会定义这两个函数指针的具体实现,如平台为ADS9,就用ADS9平台的SPI读写方法,具体参考adi_platform.c文件:

/**
 * \brief Platform setup
 * 
 * \param devHalInfo void pointer to be casted to the HAL config structure
 * \param platform Platform to be assigning the function pointers
 * 
 * \return 
 */
int32_t adi_hal_PlatformSetup(void*               devHalInfo,
                              adi_hal_Platforms_e platform)
{
    UNUSED_PARA(devHalInfo);
    adi_hal_Err_e error = ADI_HAL_OK;
    switch (platform)
    {
    case ADI_ADS9_PLATFORM:
        adi_hal_HwOpen          = ads9_HwOpen;
        adi_hal_HwClose         = ads9_HwClose;
        adi_hal_HwReset         = ads9_HwReset;
        adi_hal_DevHalCfgCreate = ads9_DevHalCfgCreate;
        adi_hal_DevHalCfgFree   = ads9_DevHalCfgFree;
        adi_hal_HwVerify        = ads9_HwVerify;

        adi_hal_SpiInit              = ads9_SpiInit;     /* TODO: remove?  called by HwOpen() */
        adi_hal_SpiWrite             = ads9_SpiWrite_v2; //ads9_SpiWrite;
        adi_hal_SpiRead              = ads9_SpiRead_v2;
        adi_hal_CustomSpiStreamWrite = NULL;
        adi_hal_CustomSpiStreamRead  = NULL;

        adi_hal_LogFileOpen  = ads9_LogFileOpen;
        adi_hal_LogLevelSet  = ads9_LogLevelSet;
        adi_hal_LogLevelGet  = ads9_LogLevelGet;
        adi_hal_LogWrite     = ads9_LogWrite;
        adi_hal_LogFileClose = ads9_LogFileClose;

        adi_hal_Wait_us = ads9_TimerWait_us;
        adi_hal_Wait_ms = ads9_TimerWait_ms;

        /* only required to support the ADI FPGA*/
        adi_hal_BbicRegisterRead   = ads9_BbicRegisterRead;
        adi_hal_BbicRegisterWrite  = ads9_BbicRegisterWrite;
        adi_hal_BbicRegistersRead  = ads9_BbicRegistersRead;
        adi_hal_BbicRegistersWrite = ads9_BbicRegistersWrite;

        break;

    case ADI_ADS8_PLATFORM:
        adi_hal_HwOpen          = ads8_HwOpen;
        adi_hal_HwClose         = ads8_HwClose;
        adi_hal_HwReset         = ads8_HwReset;
        adi_hal_DevHalCfgCreate = ads8_DevHalCfgCreate;
        adi_hal_DevHalCfgFree   = ads8_DevHalCfgFree;
        adi_hal_HwVerify        = ads8_HwVerify;

        adi_hal_SpiInit              = ads8_SpiInit; /* TODO: remove?  called by HwOpen() */
        adi_hal_SpiWrite             = ads8_SpiWrite_v2;
        adi_hal_SpiRead              = ads8_SpiRead_v2;
        adi_hal_CustomSpiStreamWrite = NULL;
        adi_hal_CustomSpiStreamRead  = NULL;

        adi_hal_LogFileOpen  = ads8_LogFileOpen;
        adi_hal_LogLevelSet  = ads8_LogLevelSet;
        adi_hal_LogLevelGet  = ads8_LogLevelGet;
        adi_hal_LogWrite     = ads8_LogWrite;
        adi_hal_LogFileClose = ads8_LogFileClose;

        adi_hal_Wait_us = ads8_TimerWait_us;
        adi_hal_Wait_ms = ads8_TimerWait_ms;

        /* only required to support the ADI FPGA*/
        adi_hal_BbicRegisterRead   = ads8_BbicRegisterRead;
        adi_hal_BbicRegisterWrite  = ads8_BbicRegisterWrite;
        adi_hal_BbicRegistersRead  = ads8_BbicRegistersRead;
        adi_hal_BbicRegistersWrite = ads8_BbicRegistersWrite;

        break;

        
    case ADI_UNKNOWN_PLATFORM:
        adi_hal_HwOpen = NULL;
        adi_hal_HwClose         = NULL;
        adi_hal_HwReset         = NULL;
        adi_hal_DevHalCfgCreate = NULL;
        adi_hal_DevHalCfgFree   = NULL;
        adi_hal_HwVerify = NULL;

        adi_hal_SpiInit              = NULL; /* TODO: remove?  called by HwOpen() */
        adi_hal_SpiWrite             = NULL;
        adi_hal_SpiRead              = NULL;
        adi_hal_CustomSpiStreamWrite = NULL;
        adi_hal_CustomSpiStreamRead  = NULL;

        adi_hal_LogFileOpen  = NULL;
        adi_hal_LogLevelSet  = NULL;
        adi_hal_LogLevelGet  = NULL;
        adi_hal_LogWrite     = ads8_LogWrite;
        adi_hal_LogFileClose = NULL;

        adi_hal_Wait_us = NULL;
        adi_hal_Wait_ms = NULL;

        /* only required to support the ADI FPGA*/
        adi_hal_BbicRegisterRead   = ads8_BbicRegisterRead;
        adi_hal_BbicRegisterWrite  = ads8_BbicRegisterWrite;
        adi_hal_BbicRegistersRead  = ads8_BbicRegistersRead;
        adi_hal_BbicRegistersWrite = ads8_BbicRegistersWrite;
        break;

    default:
        error = ADI_HAL_GEN_SW;
        break;
    }

    return error;
}

当然这里就可以设计用户自定义的底层SPI读写实现方法。

SPI2

transcevier也提供了primary SPI的接口,API函数配置基本一样,名称不同,例如adi_adrv9025_Spi2CfgSet(adi_adrv9025_Device_t *device, uint8_t spi2Enable):

/**
 * \brief Enables/Disables SPI2 on ADRV9025
 * \brief Not currently implemented
 *
 * The ADRV9025 device can enable a second SPI port on the low voltage GPIO[3:0]
 * pins.  This SPI port allows read/write access to a limited set of
 * TxAttenuation and Rx gain index registers.
 *
 * The SPI2 uses the same configuration that is programmed for SPI. This
 * includes LSB/MSB first, 4 wire mode, streaming, and address increment.
 *
 * One unique feature about the TxAtten control is that the SPI register value
 * can be set and does not update to the transmitter until a GPIO pin is
 * toggled (See spi2TxAttenGpioSel function parameter). The GPIO pin is level
 * sensitive, selecting the TxAtten programmed in either the
 * tx(1/2)_attenuation_s1 or tx(1/2)_attenuation_s2 bitfields of the second SPI
 * registers.  The GPIO pin used to switch between the two TxAtten settings is
 * user selectable using the ENUM in the function parameter.
 *
 * For readback of TxAttenuation SPI registers, first write the desired register
 * to force the value to be updated, before reading it back across SPI.
 *
 * \dep_begin
 * \dep{device->common.devHalInfo}
 * \dep_end
 *
 * \param device Pointer to the ADRV9025 data structure
 * \param spi2Enable Enable(=1)/Disable(=0) SPI2 protocol on ADRV9025
 *
 * \retval ADI_ADRV9025_WARN_RESET_LOG Recovery action for log reset
 * \retval ADI_ADRV9025_ERR_CHECK_PARAM Recovery action for bad parameter check
 * \retval ADI_ADRV9025_ERR_RESET_SPI Recovery action for SPI reset required
 * \retval ADI_ADRV9025_NO_ACTION Function completed successfully, no action required
 */
int32_t adi_adrv9025_Spi2CfgSet(adi_adrv9025_Device_t* device,
                                uint8_t                spi2Enable);

你可能感兴趣的:(fpga开发,射频工程)