SX1281驱动学习笔记一:Lora驱动移植

Posted 无痕幽雨

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了SX1281驱动学习笔记一:Lora驱动移植相关的知识,希望对你有一定的参考价值。

目录

一、资料下载

1、中文手册下载地址:

2、英文手册下载地址:

 3、固件下载地址:

4、SX1281的速率计算器下载地址:

5、SX128X区别:

二、驱动讲解

1、radio.h文件 

2、sx1281.c文件

3、sx1281-hal.c文件

4、main.c文件

三、问题汇总

1、接收数据长度问题


一、资料下载

最近要用SX1281做一款产品,把学习过程记录下,以便后期查阅。

1、中文手册下载地址:

DS_SX1280-1-2_V3.0_SC.zip_sx1280中文手册-其它文档类资源-CSDN下载

2、英文手册下载地址:

SX1281 | 长距离低功耗 2.4 GHz 射频收发器 | Semtech

 3、固件下载地址:

SX1281 | 长距离低功耗 2.4 GHz 射频收发器 | Semtech

4、SX1281的速率计算器下载地址:

SX1280 | 长距离低功耗 2.4 GHz 射频收发器 | Semtech

5、SX128X区别:

 SX1281不支持测距功能;SX1280支持普通测试功能,不支持高级测距功能;SX1282支持普通测距和高级测距功能。如果仅仅用作数据传输,三款芯片没有区别。

二、驱动讲解

下载SX1281DemoCodeDriver-C.zip固件包,解压缩后,打开工程,环境如下:

1)软件:MDK V5;

2)MCU型号:STM32L476RGTx;

3)固件版本为:Firmware Version: 170919A;

工程如下图所示:

 和SX1281相关文件如下图所示,一共5个文件:

1、radio.h文件 

其中radio.h为接口抽象文件,把和SX128X相关操作,全部抽象为API接口,官方提供,无需更改。

struct Radio_s

    /*!
     * \\brief Initializes the radio
     *
     * \\param [IN] callbacks Structure containing the driver callback functions
     */
    void ( *Init )( RadioCallbacks_t *callbacks );

    /*!
     * \\brief Resets the radio
     */
    void ( *Reset )( void );

    /*!
     * \\brief Gets the current radio status
     *
     * \\retval      status        Radio status
     */
    Radiostatus_t ( *GetStatus )( void );

    /*!
     * \\brief Writes the given command to the radio
     *
     * \\param [in]  opcode        Command opcode
     * \\param [in]  buffer        Command parameters byte array
     * \\param [in]  size          Command parameters byte array size
     */
    void ( *WriteCommand )( RadioCommands_t opcode, uint8_t *buffer, uint16_t size );

    /*!
     * \\brief Reads the given command from the radio
     *
     * \\param [in]  opcode        Command opcode
     * \\param [in]  buffer        Command parameters byte array
     * \\param [in]  size          Command parameters byte array size
     */
    void ( *ReadCommand )( RadioCommands_t opcode, uint8_t *buffer, uint16_t size );

    /*!
     * \\brief Writes multiple radio registers starting at address
     *
     * \\param [in]  address       First Radio register address
     * \\param [in]  buffer        Buffer containing the new register's values
     * \\param [in]  size          Number of registers to be written
     */
    void ( *WriteRegisters )( uint16_t address, uint8_t *buffer, uint16_t size );

    /*!
     * \\brief Writes the radio register at the specified address
     *
     * \\param [in]  address       Register address
     * \\param [in]  value         New register value
     */
    void ( *WriteRegister )( uint16_t address, uint8_t value );

    /*!
     * \\brief Reads multiple radio registers starting at address
     *
     * \\param [in]  address       First Radio register address
     * \\param [out] buffer        Buffer where to copy the registers data
     * \\param [in]  size          Number of registers to be read
     */
    void ( *ReadRegisters )( uint16_t address, uint8_t *buffer, uint16_t size );

    /*!
     * \\brief Reads the radio register at the specified address
     *
     * \\param [in]  address       Register address
     *
     * \\retval      value         The register value
     */
    uint8_t ( *ReadRegister )( uint16_t address );

    /*!
     * \\brief Writes Radio Data Buffer with buffer of size starting at offset.
     *
     * \\param [in]  offset        Offset where to start writing
     * \\param [in]  buffer        Buffer pointer
     * \\param [in]  size          Buffer size
     */
    void ( *WriteBuffer )( uint8_t offset, uint8_t *buffer, uint8_t size );

    /*!
     * \\brief Reads Radio Data Buffer at offset to buffer of size
     *
     * \\param [in]  offset        Offset where to start reading
     * \\param [out] buffer        Buffer pointer
     * \\param [in]  size          Buffer size
     */
    void ( *ReadBuffer )( uint8_t offset, uint8_t *buffer, uint8_t size );

    /*!
     * \\brief Gets the current status of the radio DIOs
     *
     * \\retval      status        [Bit #3: DIO3, Bit #2: DIO2,
     *                             Bit #1: DIO1, Bit #0: BUSY]
     */
    uint8_t ( *GetDioStatus )( void );

    /*!
     * \\brief Return firmware version
     *
     * \\retval      firmware      The firmware version
     */
    uint16_t ( *GetFirmwareVersion )( void );

    /*!
     * \\brief Sets the power regulators operating mode
     *
     * \\param [in]  mode          [0: LDO, 1:DC_DC]
     */
    void ( *SetRegulatorMode )( RadioRegulatorModes_t mode );

    /*!
     * \\brief Sets the radio in configuration mode
     *
     * \\param [in]  mode          The standby mode to put the radio into
     */
    void ( *SetStandby )( RadioStandbyModes_t mode );

    /*!
     * \\brief Sets the radio for the given protocol
     *
     * \\param [in]  packetType    [PACKET_TYPE_GFSK, PACKET_TYPE_LORA,
     *                             PACKET_TYPE_FLRC, PACKET_TYPE_BLE]
     *
     * \\remark This method has to be called before SetRfFrequency,
     *         SetModulationParams and SetPacketParams
     */
    void ( *SetPacketType )( RadioPacketTypes_t packetType );

    /*!
     * \\brief Set the modulation parameters
     *
     * \\param [in]  modParams     A structure describing the modulation parameters
     */
    void ( *SetModulationParams )( ModulationParams_t *modParams );

    /*!
     * \\brief Sets the packet parameters
     *
     * \\param [in]  packetParams  A structure describing the packet parameters
     */
    void ( *SetPacketParams )( PacketParams_t *packetParams );

    /*!
     * \\brief Sets the RF frequency
     *
     * \\param [in]  frequency     RF frequency [Hz]
     */
    void ( *SetRfFrequency )( uint32_t frequency );

    /*!
     * \\brief Sets the data buffer base address for transmission and reception
     *
     * \\param [in]  txBaseAddress Transmission base address
     * \\param [in]  rxBaseAddress Reception base address
     */
    void ( *SetBufferBaseAddresses )( uint8_t txBaseAddress, uint8_t rxBaseAddress );

    /*!
     * \\brief Sets the transmission parameters
     *
     * \\param [in]  power         RF output power [-18..13] dBm
     * \\param [in]  rampTime      Transmission ramp up time
     */
    void ( *SetTxParams )( int8_t power, RadioRampTimes_t rampTime );

    /*!
     * \\brief   Sets the IRQ mask and DIO masks
     *
     * \\param [in]  irqMask       General IRQ mask
     * \\param [in]  dio1Mask      DIO1 mask
     * \\param [in]  dio2Mask      DIO2 mask
     * \\param [in]  dio3Mask      DIO3 mask
     */
    void ( *SetDioIrqParams )( uint16_t irqMask, uint16_t dio1Mask, uint16_t dio2Mask, uint16_t dio3Mask );

    /*!
     * \\brief Sets the Sync Word given by index used in GFSK, FLRC and BLE protocols
     *
     * \\remark 5th byte isn't used in FLRC and BLE protocols
     *
     * \\param [in]  syncWordIdx   Index of SyncWord to be set [1..3]
     * \\param [in]  syncWord      SyncWord bytes ( 5 bytes )
     *
     * \\retval      status        [0: OK, 1: NOK]
     */
    uint8_t ( *SetSyncWord )( uint8_t syncWordIdx, uint8_t *syncWord );

    /*!
     * \\brief Sets the radio in reception mode
     *
     * \\param [in]  timeout       Structure describing the reception timeout value
     */
    void ( *SetRx )( TickTime_t timeout );

    /*!
     * \\brief Reads the payload received. If the received payload is longer
     * than maxSize, then the method returns 1 and do not set size and payload.
     *
     * \\param [out] payload       A pointer to a buffer into which the payload will be copied
     * \\param [out] size          A pointer to the size of the payload received
     * \\param [in]  maxSize       The maximal size allowed to copy into the buffer
     */
    uint8_t ( *GetPayload )( uint8_t *payload, uint8_t *size, uint8_t maxSize );

    /*!
     * \\brief Sends a payload
     *
     * \\param [in]  payload       A pointer to the payload to send
     * \\param [in]  size          The size of the payload to send
     * \\param [in]  timeout       The timeout for Tx operation
     */
    void ( *SendPayload )( uint8_t *payload, uint8_t size, TickTime_t timeout );

    /*!
     * \\brief Set the driver in polling mode.
     *
     * In polling mode the application is responsible to call ProcessIrqs( ) to
     * execute callbacks functions.
     * The default mode is Interrupt Mode.
     * @code
     * // Initializations and callbacks declaration/definition
     * radio = SX1281( mosi, miso, sclk, nss, busy, int1, int2, int3, rst, &callbacks );
     * radio.Init( );
     * radio.SetPollingMode( );
     *
     * while( true )
     * 
     *                            //     IRQ processing is automatically done
     *     radio.ProcessIrqs( );  // <-- here, as well as callback functions
     *                            //     calls
     *     // Do some applicative work
     * 
     * @endcode
     *
     * \\see SX1281SetInterruptMode
     */
    void ( *SetPollingMode )( void );

    /*!
     * \\brief Set the driver in interrupt mode.
     *
     * In interrupt mode, the driver communicate with the radio during the
     * interruption by direct calls to ProcessIrqs( ). The main advantage is
     * the possibility to have low power application architecture.
     * This is the default mode.
     * @code
     * // Initializations and callbacks declaration/definition
     * radio = SX1281( mosi, miso, sclk, nss, busy, int1, int2, int3, rst, &callbacks );
     * radio.Init( );
     * radio.SetInterruptMode( );   // Optionnal. Driver default behavior
     *
     * while( true )
     * 
     *     // Do some applicative work
     * 
     * @endcode
     *
     * \\see SX1281SetPollingMode
     */
    void ( *SetInterruptMode )( void );

    /*!
     * \\brief Initializes the radio registers to the recommended default values
     */
    void ( *SetRegistersDefault )( void );

    /*!
     * \\brief Gets the current Operation Mode of the Radio
     *
     * \\retval      RadioOperatingModes_t last operating mode
     */
    RadioOperatingModes_t ( *GetOpMode )( void );

    /*!
     * \\brief Sets the radio in sleep mode
     *
     * \\param [in]  sleepConfig   The sleep configuration describing data
     *                            retention and RTC wake-up
     */
    void ( *SetSleep )( SleepParams_t sleepConfig );

    /*!
     * \\brief Sets the radio in FS mode
     */
    void ( *SetFs )( void );

    /*!
     * \\brief Sets the radio in transmission mode
     *
     * \\param [in]  timeout       Structure describing the transmission timeout value
     */
    void ( *SetTx )( TickTime_t timeout );

    /*!
     * \\brief Sets the Rx duty cycle management parameters
     *
     * \\param [in]  rxTime        Structure describing reception timeout value
     * \\param [in]  sleepTime     Structure describing sleep timeout value
     */
    void ( *SetRxDutyCycle )( RadioTickSizes_t Step, uint16_t NbStepRx, uint16_t RxNbStepSleep );

    /*!
     * \\brief Sets the radio in CAD mode
     *
     * \\see SX1281::SetCadParams
     */
    void ( *SetCad )( void );

    /*!
     * \\brief Sets the radio in continuous wave transmission mode
     */
    void ( *SetTxContinuousWave )( void );

    /*!
     * \\brief Sets the radio in continuous preamble transmission mode
     */
    void ( *SetTxContinuousPreamble )( void );

    /*!
     * \\brief Gets the current radio protocol
     *
     * \\retval      packetType    [PACKET_TYPE_GFSK, PACKET_TYPE_LORA,
     *                             PACKET_TYPE_FLRC, PACKET_TYPE_BLE, PACKET_TYPE_NONE]
     */
    RadioPacketTypes_t ( *GetPacketType )( void );

    /*!
     * \\brief Sets the number of symbols to be used for Channel Activity
     *        Detection operation
     *
     * \\param [in]  cadSymbolNum  The number of symbol to use for Channel Activity
     *                            Detection operations [LORA_CAD_01_SYMBOL, LORA_CAD_02_SYMBOL,
     *                            LORA_CAD_04_SYMBOL, LORA_CAD_08_SYMBOL, LORA_CAD_16_SYMBOL]
     */
    void ( *SetCadParams )( RadioLoRaCadSymbols_t cadSymbolNum );

    /*!
     * \\brief Gets the last received packet buffer status
     *
     * \\param [out] payloadLength Last received packet payload length
     * \\param [out] rxStartBuffer Last received packet buffer address pointer
     */
    void ( *GetRxBufferStatus )( uint8_t *payloadLength, uint8_t *rxStartBuffer );

    /*!
     * \\brief Gets the last received packet payload length
     *
     * \\param [out] pktStatus     A structure of packet status
     */
    void ( *GetPacketStatus )( PacketStatus_t *pktStatus );

    /*!
     * \\brief Returns the instantaneous RSSI value for the last packet received
     *
     * \\retval      rssiInst      Instantaneous RSSI
     */
    int8_t ( *GetRssiInst )( void );

    /*!
     * \\brief Returns the current IRQ status
     *
     * \\retval      irqStatus     IRQ status
     */
    uint16_t ( *GetIrqStatus )( void );

    /*!
     * \\brief Clears the IRQs
     *
     * \\param [in]  irq           IRQ(s) to be cleared
     */
    void ( *ClearIrqStatus )( uint16_t irq );

    /*!
     * \\brief Calibrates the given radio block
     *
     * \\param [in]  calibParam    The description of blocks to be calibrated
     */
    void ( *Calibrate )( CalibrationParams_t calibParam );

    /*!
     * \\brief Saves the current selected modem configuration into data RAM
     */
    void ( *SetSaveContext )( void );

    /*!
     * \\brief Sets the chip to automatically send a packet after the end of a packet reception
     *
     * \\remark The offset is automatically compensated inside the function
     *
     * \\param [in]  time          The delay in us after which a Tx is done
     */
    void ( *SetAutoTx )( uint16_t time );

    /*!
     * \\brief Sets the chip to automatically receive a packet after the end of a packet transmission
     *
     * \\remark The offset is automatically compensated inside the function
     *
     * \\param [in]  time          The delay in us after which a Rx is done
     */
    void ( *SetAutoFS )( uint8_t enable );

    /*!
     * \\brief Enables or disables long preamble detection mode
     *
     * \\param [in]  enable        [0: Disable, 1: Enable]
     */
    void ( *SetLongPreamble )( uint8_t enable );

    /*!
     * \\brief Saves the payload to be send in the radio buffer
     *
     * \\param [in]  payload       A pointer to the payload
     * \\param [in]  size          The size of the payload
     */
    void ( *SetPayload )( uint8_t *payload, uint8_t size );

    /*!
     * \\brief Sets the Sync Word given by index used in GFSK, FLRC and BLE protocols
     *
     * \\remark 5th byte isn't used in FLRC and BLE protocols
     *
     * \\param [in]  syncWordIdx   Index of SyncWord to be set [1..3]
     * \\param [in]  syncWord      SyncWord bytes ( 5 bytes )
     *
     * \\retval      status        [0: OK, 1: NOK]
     */
    void ( *SetSyncWordErrorTolerance )( uint8_t errorBits );

    /*!
     * \\brief Sets the Initial value for the LFSR used for the CRC calculation
     *
     * \\param [in]  seed          Initial LFSR value ( 4 bytes )
     *
     */
    void ( *SetCrcSeed )( uint16_t seed );

    /*!
     * \\brief Set the Access Address field of BLE packet
     *
     * \\param [in]  accessAddress The access address to be used for next BLE packet sent
     *
     * \\see SX1281::SetBleAdvertizerAccessAddress
     */
    void ( *SetBleAccessAddress )( uint32_t accessAddress );

    /*!
     * \\brief Set the Access Address for Advertizer BLE packets
     *
     * All advertizer BLE packets must use a particular value for Access
     * Address field. This method sets it.
     *
     * \\see SX1281::SetBleAccessAddress
     */
    void ( *SetBleAdvertizerAccessAddress )( void );


    /*!
     * \\brief Sets the seed used for the CRC calculation
     *
     * \\param [in]  seed          The seed value
     *
     */
    void ( *SetCrcPolynomial )( uint16_t seed );

    /*!
     * \\brief Sets the Initial value of the LFSR used for the whitening in GFSK, FLRC and BLE protocols
     *
     * \\param [in]  seed          Initial LFSR value
     */
    void ( *SetWhiteningSeed )( uint8_t seed );

    /*!
     * \\brief Return the Estimated Frequency Error in LORA operations
     *
     * \\retval efe                The estimated frequency error [Hz]
     */
    double ( *GetFrequencyError )( void );
;

2、sx1281.c文件

 sx1281.c为实现文件,实现上述所有接口,官方提供,无需更改。

3、sx1281-hal.c文件

sx1281-hal.c为SX1281驱动和用户之间提供实现层:

1)Radio变量提供radio.h抽象的实例,便于用于通过它调用sx1281.c的函数。

2)需要用户提供:HAL_Delay()、GpioWrite()、GpioRead、SpiInOut()、SpiIn()等函数。

3)HAL_Delay()函数实现:这个函数就是一个延时函数,根据手册自己设定,为了调试,这里实现了一个ms级的软件延时函数。

4)GpioWrite()函数实现:

HAL库:

void GpioWrite( GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin, uint32_t value )

    HAL_GPIO_WritePin( GPIOx, GPIO_Pin , ( GPIO_PinState ) value );

LL库:

#define GpioWrite(__PORT,__PIN,VALUE)   do                                          \\
                                            if(VALUE)                               \\
                                                LL_GPIO_SetOutputPin(__PORT,__PIN);  \\
                                            else                                   \\
                                                LL_GPIO_ResetOutputPin(__PORT,__PIN);\\
                                                                                    \\
                                        while(0);

4)GpioRead()函数实现:

HAL库:

uint32_t GpioRead( GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin )

    return HAL_GPIO_ReadPin( GPIOx, GPIO_Pin );

LL库:

#define GPIO_READ_BIT(__GPIO,__PIN)     LL_GPIO_IsInputPinSet(__GPIO,__PIN)

#define GpioRead(__GPIO,__PIN)          GPIO_READ_BIT(__GPIO,__PIN)

5)SpiInOut()函数实现:

HAL库:

/*!
 * @brief Sends txBuffer and receives rxBuffer
 *
 * @param [IN] txBuffer Byte to be sent
 * @param [OUT] rxBuffer Byte to be sent
 * @param [IN] size Byte to be sent
 */
void SpiInOut( uint8_t *txBuffer, uint8_t *rxBuffer, uint16_t size )

    HAL_SPIEx_FlushRxFifo( &SpiHandle );
    #ifdef USE_DMA
        blockingDmaFlag = true;
        HAL_SPI_TransmitReceive_DMA( &SpiHandle, txBuffer, rxBuffer, size );
        WAIT_FOR_BLOCKING_FLAG
    #else
        HAL_SPI_TransmitReceive( &SpiHandle, txBuffer, rxBuffer, size, HAL_MAX_DELAY );
    #endif

这里有传入了一个发送数组首地址、一个接收数组首地址,还有一个长度;我的疑问是先把数据发送出去,再接收,还是发送一个字节,接收一个字节。往下看底层函数HAL_SPI_TransmitReceive(因为HAL_SPI_TransmitReceive_DMA和HAL_SPI_TransmitReceive实现功能是一样的):

    while ((hspi->TxXferCount > 0U) || (hspi->RxXferCount > 0U))
    
      /* check TXE flag */
      if (txallowed && (hspi->TxXferCount > 0U) && (__HAL_SPI_GET_FLAG(hspi, SPI_FLAG_TXE)))
      
        if (hspi->TxXferCount > 1U)
        
          hspi->Instance->DR = *((uint16_t *)pTxData);
          pTxData += sizeof(uint16_t);
          hspi->TxXferCount -= 2U;
        
        else
        
          *(__IO uint8_t *)&hspi->Instance->DR = (*pTxData++);
          hspi->TxXferCount--;
        
        /* Next Data is a reception (Rx). Tx not allowed */
        txallowed = 0U;

#if (USE_SPI_CRC != 0U)
        /* Enable CRC Transmission */
        if ((hspi->TxXferCount == 0U) && (hspi->Init.CRCCalculation == SPI_CRCCALCULATION_ENABLE))
        
          /* Set NSS Soft to received correctly the CRC on slave mode with NSS pulse activated */
          if (((hspi->Instance->CR1 & SPI_CR1_MSTR) == 0U) && ((hspi->Instance->CR2 & SPI_CR2_NSSP) == SPI_CR2_NSSP))
          
            SET_BIT(hspi->Instance->CR1, SPI_CR1_SSM);
          
          SET_BIT(hspi->Instance->CR1, SPI_CR1_CRCNEXT);
        
#endif /* USE_SPI_CRC */
      

      /* Wait until RXNE flag is reset */
      if ((hspi->RxXferCount > 0U) && (__HAL_SPI_GET_FLAG(hspi, SPI_FLAG_RXNE)))
      
        if (hspi->RxXferCount > 1U)
        
          *((uint16_t *)pRxData) = hspi->Instance->DR;
          pRxData += sizeof(uint16_t);
          hspi->RxXferCount -= 2U;
          if (hspi->RxXferCount <= 1U)
          
            /* set fiforxthresold before to switch on 8 bit data size */
            SET_BIT(hspi->Instance->CR2, SPI_RXFIFO_THRESHOLD);
          
        
        else
        
          (*(uint8_t *)pRxData++) = *(__IO uint8_t *)&hspi->Instance->DR;
          hspi->RxXferCount--;
        
        /* Next Data is a Transmission (Tx). Tx is allowed */
        txallowed = 1U;
      
      if ((Timeout != HAL_MAX_DELAY) && ((HAL_GetTick() - tickstart) >=  Timeout))
      
        errorcode = HAL_TIMEOUT;
        goto error;
      
    

从代码分析是发送一个字节,接收一个字节,我的实现:

uint8_t Spi1InOut( uint8_t outData )

	uint8_t RxData=0;

    while(!LL_SPI_IsActiveFlag_TXE(SPI1));
    LL_SPI_TransmitData8(SPI1,outData);
    //Soft_delay_us(100);
    while(!LL_SPI_IsActiveFlag_TXE(SPI1));
    while(!LL_SPI_IsActiveFlag_RXNE(SPI1));
    RxData = LL_SPI_ReceiveData8(SPI1);
    return RxData;  


void SpiInOut( uint8_t *txBuffer, uint8_t *rxBuffer, uint16_t size )

    uint16_t i=0;
    
    if(NULL == txBuffer || NULL == rxBuffer || !size)
        return;
    

    for(i=0;i<size;i++)
        rxBuffer[i] = Spi1InOut(txBuffer[i]);
    

6)SpiIn()函数实现:

HAL库:

void SpiIn( uint8_t *txBuffer, uint16_t size )

    #ifdef USE_DMA
        blockingDmaFlag = true;
        HAL_SPI_Transmit_DMA( &SpiHandle, txBuffer, size );
        WAIT_FOR_BLOCKING_FLAG
    #else
        HAL_SPI_Transmit( &SpiHandle, txBuffer, size, HAL_MAX_DELAY );
    #endif

我的实现:

void SpiIn( uint8_t *txBuffer, uint16_t size )

    uint16_t i=0;

    if(NULL == txBuffer || !size)
        return;
    

    for(i=0;i<size;i++)
        Spi1InOut(txBuffer[i]);
    

7)GpioSetIrq()函数屏蔽掉

8)SPI初始化:

HAL库:

void SpiInit( void )

    SpiHandle.Instance                = SPI1;
    SpiHandle.Init.Mode               = SPI_MODE_MASTER;
    SpiHandle.Init.Direction          = SPI_DIRECTION_2LINES;
    SpiHandle.Init.DataSize           = SPI_DATASIZE_8BIT;
    SpiHandle.Init.CLKPolarity        = SPI_POLARITY_LOW;
    SpiHandle.Init.CLKPhase           = SPI_PHASE_1EDGE;
    SpiHandle.Init.NSS                = SPI_NSS_SOFT;
    SpiHandle.Init.BaudRatePrescaler  = SPI_BAUDRATEPRESCALER_16;
    SpiHandle.Init.FirstBit           = SPI_FIRSTBIT_MSB;
    SpiHandle.Init.TIMode             = SPI_TIMODE_DISABLE;
    SpiHandle.Init.CRCCalculation     = SPI_CRCCALCULATION_DISABLE;
    SpiHandle.Init.CRCPolynomial      = 7;
    SpiHandle.Init.CRCLength          = SPI_CRC_LENGTH_DATASIZE;
    SpiHandle.Init.NSSPMode           = SPI_NSS_PULSE_DISABLE;

    if ( HAL_SPI_Init( &SpiHandle ) != HAL_OK )
    
        Error_Handler( );
    

我的实现:

/**
  * @brief SPI1 Initialization Function
  * @param None
  * @retval None
  */
static void spi1_init(void)


    /* USER CODE BEGIN SPI1_Init 0 */

    /* USER CODE END SPI1_Init 0 */

    //LL_SPI_InitTypeDef SPI_InitStruct = 0;

    //LL_GPIO_InitTypeDef GPIO_InitStruct;

    /* Peripheral clock enable */
    LL_IOP_GRP1_EnableClock(LL_IOP_GRP1_PERIPH_GPIOA); 
    LL_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_SPI1);

    //
    LL_SPI_Disable(SPI1);

    /**SPI1 GPIO Configuration  
    PA5   ------> SPI1_SCK
    PA6   ------> SPI1_MISO
    PA7   ------> SPI1_MOSI 
    */
    do
        const static LL_GPIO_InitTypeDef c_tGPIO_InitStruct = 
            .Pin        = LL_GPIO_PIN_5,
            .Mode       = LL_GPIO_MODE_ALTERNATE,
            .Speed      = LL_GPIO_SPEED_FREQ_VERY_HIGH,
            .OutputType = LL_GPIO_OUTPUT_PUSHPULL,
            .Pull       = LL_GPIO_PULL_UP,
            .Alternate  = LL_GPIO_AF_0,
        ;
        LL_GPIO_Init(GPIOA, (LL_GPIO_InitTypeDef*)&c_tGPIO_InitStruct);
    while(0);
    do
        const static LL_GPIO_InitTypeDef c_tGPIO_InitStruct = 
            .Pin        = LL_GPIO_PIN_6,
            .Mode       = LL_GPIO_MODE_ALTERNATE,
            .Speed      = LL_GPIO_SPEED_FREQ_HIGH,
            .OutputType = LL_GPIO_OUTPUT_PUSHPULL,
            .Pull       = LL_GPIO_PULL_UP,
            .Alternate  = LL_GPIO_AF_0,
        ;
        LL_GPIO_Init(GPIOA, (LL_GPIO_InitTypeDef*)&c_tGPIO_InitStruct);
    while(0);
    do
        const static LL_GPIO_InitTypeDef c_tGPIO_InitStruct = 
            .Pin        = LL_GPIO_PIN_7,
            .Mode       = LL_GPIO_MODE_ALTERNATE,
            .Speed      = LL_GPIO_SPEED_FREQ_HIGH,
            .OutputType = LL_GPIO_OUTPUT_PUSHPULL,
            .Pull       = LL_GPIO_PULL_UP,
            .Alternate  = LL_GPIO_AF_0,
        ;
        LL_GPIO_Init(GPIOA, (LL_GPIO_InitTypeDef*)&c_tGPIO_InitStruct);
    while(0);

    /* SPI1 interrupt Init */
    NVIC_SetPriority(SPI1_IRQn, 0);
    NVIC_DisableIRQ(SPI1_IRQn);
    //NVIC_EnableIRQ(SPI1_IRQn);

    /* SPI1 parameter configuration*/
    /* USER CODE BEGIN SPI1_Init 1 */

    /* USER CODE END SPI1_Init 1 */
    do
        const static LL_SPI_InitTypeDef c_tSPI_InitStruct = 
            .TransferDirection  = LL_SPI_FULL_DUPLEX,
            .Mode               = LL_SPI_MODE_MASTER,               //
            .DataWidth          = LL_SPI_DATAWIDTH_8BIT,            //
            .ClockPolarity      = LL_SPI_POLARITY_LOW,              //
            .ClockPhase         = LL_SPI_PHASE_1EDGE,               //
            .NSS                = LL_SPI_NSS_SOFT,                  //
            .BaudRate           = LL_SPI_BAUDRATEPRESCALER_DIV16,   //
            .BitOrder           = LL_SPI_MSB_FIRST,                 //
            .CRCCalculation     = LL_SPI_CRCCALCULATION_DISABLE,
            .CRCPoly            = 7,
        ;
        LL_SPI_Init(SPI1, (LL_SPI_InitTypeDef*)&c_tSPI_InitStruct);
        //LL_SPI_SetDataWidth(SPI1,LL_SPI_DATAWIDTH_8BIT);
    while(0);
    LL_SPI_SetStandard(SPI1, LL_SPI_PROTOCOL_MOTOROLA);
    LL_SPI_DisableNSSPulseMgt(SPI1);
    /* USER CODE BEGIN SPI1_Init 2 */

    /* USER CODE END SPI1_Init 2 */


    /* Configure the SPI1 FIFO Threshold */
    LL_SPI_SetRxFIFOThreshold(SPI1, LL_SPI_RX_FIFO_TH_QUARTER);

    /* Configure SPI1 transfer interrupts */
    /* Enable TXE   Interrupt */
    //LL_SPI_EnableIT_TXE(SPI1);
    /* Enable RXNE  Interrupt */
    //LL_SPI_EnableIT_RXNE(SPI1);
    /* Enable SPI1 Error Interrupt */
    //LL_SPI_EnableIT_ERR(SPI1);



    LL_SPI_Enable(SPI1);

 如果能够打印出来如下信息,则表示SPI驱动移植成功。

4、main.c文件

1)官网驱动main.c中其它和Lora相关的拷贝过来:

2)switch( AppState )屏蔽掉

3)增加delay()

    while(1)        
        SX1281ProcessIrqs( );        
        my_delay_ms(1000);
    

4)修改5个函数

void OnTxDone( void )

    AppState = APP_TX;
    printf( "<>>>>>>>>OnTxDone\\n\\r" ); 
    Radio.SetDioIrqParams( TxIrqMask, TxIrqMask, IRQ_RADIO_NONE, IRQ_RADIO_NONE );
    Radio.SendPayload((uint8_t*)"12345",5, ( TickTime_t ) RX_TIMEOUT_TICK_SIZE, TX_TIMEOUT_VALUE );


void OnRxDone( void )

    AppState = APP_RX;
    //printf( "<>>>>>>>>OnRxDone\\n\\r" ); 
    BufferSize = 0;
    Radio.GetPayload( Buffer, &BufferSize, BUFFER_SIZE );
    Buffer[BufferSize+1] = 0;
    //printf("size = %d ,%s",BufferSize,Buffer);
    MY_PRINTF("OnRxDone\\r\\n",Buffer,BufferSize);
    //Radio.SetRx( ( TickTime_t )  RX_TIMEOUT_TICK_SIZE, RX_TIMEOUT_VALUE  );


void OnTxTimeout( void )

    AppState = APP_TX_TIMEOUT;
    printf( "<>>>>>>>>TXE\\n\\r" ); 
    Radio.SetDioIrqParams( TxIrqMask, TxIrqMask, IRQ_RADIO_NONE, IRQ_RADIO_NONE );
    Radio.SendPayload((uint8_t*)"12345",5, ( TickTime_t ) RX_TIMEOUT_TICK_SIZE, TX_TIMEOUT_VALUE );


void OnRxTimeout( void )

    AppState = APP_RX_TIMEOUT;
    printf( "<>>>>>>>>OnRxTimeout\\n\\r" ); 
    //Radio.SetRx( ( TickTime_t )  RX_TIMEOUT_TICK_SIZE, RX_TIMEOUT_VALUE  );
    Radio.SetRx( ( TickTime_t )  RX_TIMEOUT_TICK_SIZE, 0xFFFF  );


void OnRxError( IrqErrorCode_t errorCode )

    AppState = APP_RX_ERROR;
    printf( "RXE<>>>>>>>>\\n\\r" ); 
    Radio.SetRx( ( TickTime_t )  RX_TIMEOUT_TICK_SIZE, RX_TIMEOUT_VALUE  );


void OnCadDone( bool channelActivityDetected )

    printf( "<>>>>>>>>OnCadDone\\n\\r" );

5)SpreadingFactor配置:

 增加:

    Radio.SetStandby( STDBY_RC );
    switch(modulationParams.Params.LoRa.SpreadingFactor)
        case LORA_SF5:
        case LORA_SF6:
            Radio.WriteRegister(0x0925,0x1E);
            break;
        case LORA_SF7:
        case LORA_SF8:
            Radio.WriteRegister(0x0925,0x37);
            break;
        case LORA_SF9:
        case LORA_SF10:
        case LORA_SF11:
        case LORA_SF12:
            Radio.WriteRegister(0x0925,0x32);
            break;
    

6)接收相关:

 通过如下配置为接收:

    Radio.SetDioIrqParams( RxIrqMask, RxIrqMask, IRQ_RADIO_NONE, IRQ_RADIO_NONE );

    //Radio.SetRx( ( TickTime_t )  RX_TIMEOUT_TICK_SIZE, RX_TIMEOUT_VALUE  );
    Radio.SetRx( ( TickTime_t )  RX_TIMEOUT_TICK_SIZE, 0xFFFF  );

7)通过如下配置为发送:

    Radio.SetDioIrqParams( TxIrqMask, TxIrqMask, IRQ_RADIO_NONE, IRQ_RADIO_NONE );
    Radio.SendPayload((uint8_t*)"12345",5, ( TickTime_t ) RX_TIMEOUT_TICK_SIZE, TX_TIMEOUT_VALUE );

8)屏蔽掉SX1281SetPollingMode( );增加SX1281SetInterruptMode();因为SX1281ProcessIrqs( )函数中,如下逻辑不对:

    if( PollingMode == true )
    
        if( IrqState == true )
        
            __disable_irq( );
            IrqState = false;
            __enable_irq( );
        
        else
        
            return;
        
    

下面是接收其他数据效果图:

<----------------------------------------------------------------------------------------------------------------------------->

三、问题汇总

2022.10.03

1、接收数据长度问题

显现:就是发送5个字节,但是接收是128字节

参数配置:

/*!
 * \\brief Defines the buffer size, i.e. the payload size
 */
#define BUFFER_SIZE                                 128    

    packetParams.PacketType = PACKET_TYPE_LORA;
    packetParams.Params.LoRa.PreambleLength = 6;
    packetParams.Params.LoRa.HeaderType = LORA_PACKET_VARIABLE_LENGTH;
    packetParams.Params.LoRa.PayloadLength = BUFFER_SIZE;
    packetParams.Params.LoRa.CrcMode = LORA_CRC_ON;
    packetParams.Params.LoRa.InvertIQ = LORA_IQ_NORMAL;

发送配置:

    Buffer[0] = 1;
    Buffer[1] = 2;
    Buffer[2] = 3;
    Buffer[3] = 4;
    Buffer[4] = 5;
    Radio.SendPayload(Buffer,5, ( TickTime_t ) RX_TIMEOUT_TICK_SIZE, TX_TIMEOUT_VALUE );

接收显示:

    Radio.GetPayload( Buffer, &BufferSize, BUFFER_SIZE );
    MY_PRINTF("OnRxDone\\r\\n",Buffer,BufferSize);

结果:

 查遍了手册和官方提供的SX1281PingPong历程,没有解决;问官方支持,也没有解决。于是查看官方sx1280-devKit-v1p6历程,看到:

        case PER_TX_START:
            Eeprom.EepromData.DemoSettings.CntPacketTx++;
            DemoInternalState = APP_IDLE;

            Buffer[0] = ( Eeprom.EepromData.DemoSettings.CntPacketTx >> 24 ) & 0xFF;
            Buffer[1] = ( Eeprom.EepromData.DemoSettings.CntPacketTx >> 16 ) & 0xFF;
            Buffer[2] = ( Eeprom.EepromData.DemoSettings.CntPacketTx >> 8 )  & 0xFF;
            Buffer[3] = Eeprom.EepromData.DemoSettings.CntPacketTx & 0xFF;
            Buffer[4] = PerMsg[0];
            Buffer[5] = PerMsg[1];
            Buffer[6] = PerMsg[2];
            for( i = 7; i < Eeprom.EepromData.DemoSettings.PayloadLength; i++ )
            
                Buffer[i] = i;
            
            TX_LED = !TX_LED;
            IrqMask = IRQ_TX_DONE | IRQ_RX_TX_TIMEOUT;

            UpdateRadioFrequency( Channels[CurrentChannel] );
            Radio.SetRfFrequency( Channels[CurrentChannel] );

            Radio.SetDioIrqParams( IrqMask, IrqMask, IRQ_RADIO_NONE, IRQ_RADIO_NONE );
            Radio.SendPayload( Buffer, Eeprom.EepromData.DemoSettings.PayloadLength, 
                               ( TickTime_t ) RX_TIMEOUT_TICK_SIZE, Eeprom.EepromData.DemoSettings.TimeOnAir << 1  );

继续追代码:

        ModulationParams.Params.LoRa.SpreadingFactor = ( RadioLoRaSpreadingFactors_t )  Eeprom.EepromData.DemoSettings.ModulationParam1;
        ModulationParams.Params.LoRa.Bandwidth       = ( RadioLoRaBandwidths_t )        Eeprom.EepromData.DemoSettings.ModulationParam2;
        ModulationParams.Params.LoRa.CodingRate      = ( RadioLoRaCodingRates_t )       Eeprom.EepromData.DemoSettings.ModulationParam3;
        PacketParams.Params.LoRa.PreambleLength      =                                  Eeprom.EepromData.DemoSettings.PacketParam1;
        PacketParams.Params.LoRa.HeaderType          = ( RadioLoRaPacketLengthsModes_t )Eeprom.EepromData.DemoSettings.PacketParam2;
        PacketParams.Params.LoRa.PayloadLength       =                                  Eeprom.EepromData.DemoSettings.PacketParam3;
        PacketParams.Params.LoRa.Crc                 = ( RadioLoRaCrcModes_t )          Eeprom.EepromData.DemoSettings.PacketParam4;
        PacketParams.Params.LoRa.InvertIQ            = ( RadioLoRaIQModes_t )           Eeprom.EepromData.DemoSettings.PacketParam5;

        Eeprom.EepromData.DemoSettings.PayloadLength = PacketParams.Params.LoRa.PayloadLength;

于是明白了,发送的字长是由PacketParams.Params.LoRa.PayloadLength参数决定的,并不是由Radio.SendPayload()函数传入的参数决定。于是进行如下修改:

void rf_send(uint8_t* pchBuffer,uint16_t hwSize)

    if(NULL == pchBuffer || !hwSize)
        return;
    
    
    if(hwSize != packetParams.Params.LoRa.PayloadLength)
        packetParams.Params.LoRa.PayloadLength = hwSize;
        Radio.SetPacketParams( &packetParams );
    
    Radio.SendPayload(pchBuffer,hwSize, ( TickTime_t ) RX_TIMEOUT_TICK_SIZE, TX_TIMEOUT_VALUE );

结果:

注意:经过测试,在显示包头格式下,PacketParams.Params.LoRa.PayloadLength参数不影响接收数据长度,也就是说这个参数设置多少无所谓,接收数据根据实际发送字长接收;隐士包头格式未测试

以上是关于SX1281驱动学习笔记一:Lora驱动移植的主要内容,如果未能解决你的问题,请参考以下文章

SX1281驱动学习笔记二:驱动学习

SX1281驱动学习笔记二:驱动学习

Lora1278驱动V4.4.2讲解二:驱动多个SX1278芯片

LoRa笔记03 LoRa sx1276 sx1278空中唤醒研究

LoRa笔记02 LoRa sx1276 sx1278的发射功率研究

LoRa---官方例程移植