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

2023-10-26

目录

一、资料下载

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参数不影响接收数据长度,也就是说这个参数设置多少无所谓,接收数据根据实际发送字长接收;隐士包头格式未测试

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

SX1281驱动学习笔记一:Lora驱动移植 的相关文章

  • 前端 token 应该放在哪里呢?

    总结 反正是服务端加密的传过来让前端存着的 通常的存储都可以放 只不过需要防范攻击就是了 风险 放在 webStorage 里的话 因为同源策略 可以在当前页面中注入脚本进行 xss 攻击来获取信息 比如在一个帖子下面回复了一串 js 脚本
  • 点云数据下的KD-tree

    点云数据下的KD tree检索 数据结构 构建KD tree 最近邻检索 KD tree简称K维树 是一种空间划分的数据结构 常被用于高维空间中的搜索 比如范围搜索和最近邻搜索 kd tree是二进制空间划分树的一种特殊情况 在激光雷达SL
  • go mod vender package io/fs is not in GOROOT (/usr/local/go/src/io/fs)

    一 问题解决 最近写区块链相关项目时候 对于智能合约部署时对其进行vender时报错 io fs is not in GOROOT usr local go src io fs 搜索了一下是因为go版本问题 我原本是1 15 5的版本 后来
  • 实施MES系统有何难点,以及怎么解决?

    实施MES系统有何难点 以及怎么解决 很多制造企业当前对于MES系统还处于认知阶段 制造业管理者们在不断了解MES系统的过程中 发现实施MES并没有想象中的简单 一个成熟的MES系统要经历哪些难点 如何更好的解决 一 MES系统有哪些实施难
  • Docker之DockerFile相关基础知识

    DockerFile相关基础知识 一 Docker镜像原理 1 操作系统组成部分 1 1 七大子系统 1 2 Linux文件系统 2 Docker镜像原理介绍 2 1 原理图 2 2 Docker镜像本质 2 3 统一文件系统 2 4 复用

随机推荐

  • react配置项目路径别名@

    为什么有的项目引入路径写 可以 自己的不行呢 因为别人的项目是配置过路径别名的 就表示src目录 因此我们也需要配置一遍 以下是react项目示范 vue项目同理 ts对 指向src目录的提示是不支持的 vite默认也不支持 所以需要手动配
  • 如何计算TCP吞吐量

    为什么80 的码农都做不了架构师 gt gt gt FROM how to calculate tcp throughput for long distance links 如何计算广域网链路的 TCP 吞吐量 刚刚点亮数据中心之间的高速网
  • Linux IP出现inet6 addr :fe80::20c:29ff:fe47:fd61/64 scope:l

    经常出现 inet6 addr fe80 20c 29ff fe47 fd61 64 scope link1 这种情况的不要担心了 试试这个方法 这也是我折磨了好多天整理的 不喜勿喷 谢谢 虚拟机设置权限 sudo chown book b
  • Git学习笔记

    配置user信息 配置user name git config global user name your name 配置user email git config global user email your email 查看所有配置信息
  • Jenkins中连接Git仓库时提示:无法连接仓库:Error performing git command: git ls-remote -h

    问题 Jenkins中连接Git仓库时提示 无法连接仓库 Error performing git command git ls remote h 原因 git的账号密码错误 解决方案 重新设置账号密码 操作 控制面板 凭证管理器 wind
  • 有趣的异常

    缘起 最近 在项目中遇到一个有趣的异常 在没附加调试器的情况下会直接崩溃 附加调试器后 会中断到调试器中 但是按 F5 继续运行后 程序还能继续执行 interesting 你能猜出这是个什么异常吗 初遇错误 在测试程序功能的时候 意外的崩
  • 【教程】一款Markdown 编辑器,免费版本 Typora 下载与使用.

    csdn资源老挂 再补一个网盘的 哪个能用用哪个吧 链接 https pan baidu com s 19c MJQRuas9v5lHxF1uB6A pwd f3n5 提取码 f3n5 gt gt 资源 lt
  • EXCEL-数据透视表、日数据整理成月数据

    1 当你面对一个很多年的日数据 想要把它整理成月数据 下图是2015年1月到2022年1月的日数据 2 首先我们把没用的信息挪开 在时间和日数据上加个表头 3 接着选中数据 包括表头 点击 插入 数据透视表 4 跳出来的框框 直接确认 5
  • AppsFlyer 研究(四)OneLink Deep Linking Guide

    一 简介 深度链接是指当用户打开移动应用时向其提供个性化的内容 或将用户带到应用内特定位置的操作 通过这种操作 您可以为用户提供优质的用户体验 从而极大加强用户与应用的互动 两种深度链接类型 由于用户不一定安装了移动应用 所以有两种深度链接
  • 曾经被视为「牛市制造机」们的机构巨鲸,如今都怎么了?

    这是白话区块链的第1790期原创 作者 Terry出品 白话区块链 ID hellobtc 11 月 17 日 萨尔瓦多总统 Nayib Bukele 表示 从明天开始 我们将每天购买一个比特币 直接开始了国家级别的比特币定投之旅 相信不少
  • 【图片二值化处理,以及byte[] 与bitmap互相转化问题】

    1 byte与bitmap相互转换 将byte流转换为bitmap byte signature item ToArray MemoryStream ms1 new MemoryStream signature Bitmap bm Bitm
  • 打开ABAQUS时,显示找不到 MFC140U.DLL 文件,打不开软件,亲测解决

    打开ABAQUS时 显示找不到 MFC140U DLL 文件 打不开软件 如何解决 下载了X64版本的 安装完毕后就可以打开了 Microsoft Visual C 2017 Redistributable 32位链接 link 64位链接
  • 时钟同步-注意客户端和服务端都需要开启123端口 udp协议

    确认时钟源 chronyc sources v chronyc tracking Linux Chronyd时间同步服务器详解 wangjie722703的博客 CSDN博客 local stratum 10 即使自己未能通过网络时间服务器
  • pytorch-lightning如何设置训练epoch

    Trainer初始化时添加max epochs参数 init model autoencoder LitAutoEncoder trainer pl Trainer gpus 8 max epochs 50 trainer fit auto
  • iOS uiscrollView 嵌套 问题 的解决

    苹果官方文档里面提过 最好不要嵌套scrollView 特别提过UITableView和UIWebView 因为在滑动时 无法知道到底是希望superScrollView滑动还是subScrollView滑动 一旦出现这种情况 情况就出乎我
  • 一文了解websocket全双工通信java实现&socket地址404问题解决

    websocket介绍 1 websocket介绍 1 1注解介绍 2 demo 2 1 后端代码 2 2 前端代码 2 3 效果 附录 socket地址404问题解决 1 websocket介绍 WebSocket是一种在单个TCP连接上
  • 背包问题

    一 01背包 题目 有一个容量为T的背包 现有n个物品 每个物品有都有一个体积w i 和自身价值v i 现在要求求出背包能够装的物品的价值最大 每个物品只可以装一次 基本思路 01背包是背包中的最基础的问题 后面很多背包问题都是01背包和完
  • [会议分享]2022年欧洲计算机科学与信息技术会议(ECCSIT 2022)

    2022年欧洲计算机科学与信息技术会议 ECCSIT 2022 重要信息 会议网址 www eccsit org 会议时间 2022年11月25 27日 召开地点 南京 截稿时间 2022年10月20日 录用通知 投稿后2周内 收录检索 E
  • 【DevOps核心理念基础】3. 敏捷开发最佳实践

    一 敏捷开发最佳实践 1 1 项目管理 1 2 需求管理 1 3 技术架构 1 4 技术开发 1 5 测试 二 敏捷开发最佳实践 2 1 敏捷开发的执行细节 三 全面的DevOps工具链 四 版本控制和协作开发工具 4 1 集中式版本控制工
  • SX1281驱动学习笔记一:Lora驱动移植

    目录 一 资料下载 1 中文手册下载地址 2 英文手册下载地址 3 固件下载地址 4 SX1281的速率计算器下载地址 5 SX128X区别 二 驱动讲解 1 radio h文件 2 sx1281 c文件 3 sx1281 hal c文件