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

Posted 无痕幽雨

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Lora1278驱动V4.4.2讲解二:驱动多个SX1278芯片相关的知识,希望对你有一定的参考价值。

最近有个项目,要做微型网关,对于尺寸、体积、功耗、成本以及开发周期有要求,方案基于以前的Lora网关为基础进行快速研发,里面唯一的难点就是用一个MCU驱动多个SX1278,通过对比SX1278的sx12xxDrivers-V2.1.0和sx12xxDrivers-V4.4.2,发现V4.4.2驱动更好修改,于是决定在V4.4.2驱动基础之上增加支持多片SX1278功能。

sx12xxDrivers-V4.4.2驱动移植请看:Lora1278驱动V4.4.2讲解一:驱动移植

一、思路

增加多片SX1278的总体思路就是运用面向对象思想实现,举例:

SX1278Drivers RF1,RF2RF3;

RF1.init();

RF2.init();

RF3.init();

等等,实现起来很轻松,在嵌入式C开发中怎么实现呢,思路如下:

抽象出功能相同的一套函数,抽象出功能不通的函数并定义函数指针,把全局变量和函数指针定义成结构体,定义RF1/RF2/RF3实体,并把实体指针当做那套抽象出的函数的入参。类似如下:

    net_work_rf_process(&s_tNetWorkRF1);                       //无线线程
    net_work_protocol_analysis_process(&s_tNetWorkRF1);        //协议解析
    father_station_routing_table_maintenance(&s_tNetWorkRF1);  //父节路由点维护
    net_work_logic_process(&s_tNetWorkRF1);                    //组网逻辑
    net_work_check_sx1278_process(&s_tNetWorkRF1);             //SX1278维护线程
    s_tNetWorkRF1.ptRadio->IrqProcess(&s_tNetWorkRF1.tSX1276); //DIO0处理函数

二、驱动修改

sx12xxDrivers-V4.4.2详细说明请看上篇博客(驱动讲解一),总体思路如下:

1、radio.h抽象出驱动接口;

/*!
 * \\brief Radio driver definition
 */
struct Radio_s

    /*!
     * \\brief Initializes the radio
     *
     * \\param [IN] events Structure containing the driver callback functions
     */
    void    ( *Init )( RadioEvents_t *events );
    /*!
     * Return current radio status
     *
     * \\param status Radio status.[RF_IDLE, RF_RX_RUNNING, RF_TX_RUNNING]
     */
    Radiostate_t ( *GetStatus )( void );
    /*!
     * \\brief Configures the radio with the given modem
     *
     * \\param [IN] modem Modem to be used [0: FSK, 1: LoRa]
     */
    void    ( *SetModem )( RadioModems_t modem );
    /*!
     * \\brief Sets the channel frequency
     *
     * \\param [IN] freq         Channel RF frequency
     */
    void    ( *SetChannel )( uint32_t freq );
    /*!
     * \\brief Checks if the channel is free for the given time
     *
     * \\param [IN] modem      Radio modem to be used [0: FSK, 1: LoRa]
     * \\param [IN] freq       Channel RF frequency
     * \\param [IN] rssiThresh RSSI threshold
     * \\param [IN] maxCarrierSenseTime Max time while the RSSI is measured
     *
     * \\retval isFree         [true: Channel is free, false: Channel is not free]
     */
    bool    ( *IsChannelFree )( RadioModems_t modem, uint32_t freq, int16_t rssiThresh, uint32_t maxCarrierSenseTime );
    /*!
     * \\brief Generates a 32 bits random value based on the RSSI readings
     *
     * \\remark This function sets the radio in LoRa modem mode and disables
     *         all interrupts.
     *         After calling this function either Radio.SetRxConfig or
     *         Radio.SetTxConfig functions must be called.
     *
     * \\retval randomValue    32 bits random value
     */
    uint32_t ( *Random )( void );
    /*!
     * \\brief Sets the reception parameters
     *
     * \\param [IN] modem        Radio modem to be used [0: FSK, 1: LoRa]
     * \\param [IN] bandwidth    Sets the bandwidth
     *                          FSK : >= 2600 and <= 250000 Hz
     *                          LoRa: [0: 125 kHz, 1: 250 kHz,
     *                                 2: 500 kHz, 3: Reserved]
     * \\param [IN] datarate     Sets the Datarate
     *                          FSK : 600..300000 bits/s
     *                          LoRa: [6: 64, 7: 128, 8: 256, 9: 512,
     *                                10: 1024, 11: 2048, 12: 4096  chips]
     * \\param [IN] coderate     Sets the coding rate (LoRa only)
     *                          FSK : N/A ( set to 0 )
     *                          LoRa: [1: 4/5, 2: 4/6, 3: 4/7, 4: 4/8]
     * \\param [IN] bandwidthAfc Sets the AFC Bandwidth (FSK only)
     *                          FSK : >= 2600 and <= 250000 Hz
     *                          LoRa: N/A ( set to 0 )
     * \\param [IN] preambleLen  Sets the Preamble length
     *                          FSK : Number of bytes
     *                          LoRa: Length in symbols (the hardware adds 4 more symbols)
     * \\param [IN] symbTimeout  Sets the RxSingle timeout value
     *                          FSK : timeout in number of bytes
     *                          LoRa: timeout in symbols
     * \\param [IN] fixLen       Fixed length packets [0: variable, 1: fixed]
     * \\param [IN] payloadLen   Sets payload length when fixed length is used
     * \\param [IN] crcOn        Enables/Disables the CRC [0: OFF, 1: ON]
     * \\param [IN] freqHopOn    Enables disables the intra-packet frequency hopping
     *                          FSK : N/A ( set to 0 )
     *                          LoRa: [0: OFF, 1: ON]
     * \\param [IN] hopPeriod    Number of symbols between each hop
     *                          FSK : N/A ( set to 0 )
     *                          LoRa: Number of symbols
     * \\param [IN] iqInverted   Inverts IQ signals (LoRa only)
     *                          FSK : N/A ( set to 0 )
     *                          LoRa: [0: not inverted, 1: inverted]
     * \\param [IN] rxContinuous Sets the reception in continuous mode
     *                          [false: single mode, true: continuous mode]
     */
    void    ( *SetRxConfig )( RadioModems_t modem, uint32_t bandwidth,
                              uint32_t datarate, uint8_t coderate,
                              uint32_t bandwidthAfc, uint16_t preambleLen,
                              uint16_t symbTimeout, bool fixLen,
                              uint8_t payloadLen,
                              bool crcOn, bool freqHopOn, uint8_t hopPeriod,
                              bool iqInverted, bool rxContinuous );
    /*!
     * \\brief Sets the transmission parameters
     *
     * \\param [IN] modem        Radio modem to be used [0: FSK, 1: LoRa]
     * \\param [IN] power        Sets the output power [dBm]
     * \\param [IN] fdev         Sets the frequency deviation (FSK only)
     *                          FSK : [Hz]
     *                          LoRa: 0
     * \\param [IN] bandwidth    Sets the bandwidth (LoRa only)
     *                          FSK : 0
     *                          LoRa: [0: 125 kHz, 1: 250 kHz,
     *                                 2: 500 kHz, 3: Reserved]
     * \\param [IN] datarate     Sets the Datarate
     *                          FSK : 600..300000 bits/s
     *                          LoRa: [6: 64, 7: 128, 8: 256, 9: 512,
     *                                10: 1024, 11: 2048, 12: 4096  chips]
     * \\param [IN] coderate     Sets the coding rate (LoRa only)
     *                          FSK : N/A ( set to 0 )
     *                          LoRa: [1: 4/5, 2: 4/6, 3: 4/7, 4: 4/8]
     * \\param [IN] preambleLen  Sets the preamble length
     *                          FSK : Number of bytes
     *                          LoRa: Length in symbols (the hardware adds 4 more symbols)
     * \\param [IN] fixLen       Fixed length packets [0: variable, 1: fixed]
     * \\param [IN] crcOn        Enables disables the CRC [0: OFF, 1: ON]
     * \\param [IN] freqHopOn    Enables disables the intra-packet frequency hopping
     *                          FSK : N/A ( set to 0 )
     *                          LoRa: [0: OFF, 1: ON]
     * \\param [IN] hopPeriod    Number of symbols between each hop
     *                          FSK : N/A ( set to 0 )
     *                          LoRa: Number of symbols
     * \\param [IN] iqInverted   Inverts IQ signals (LoRa only)
     *                          FSK : N/A ( set to 0 )
     *                          LoRa: [0: not inverted, 1: inverted]
     * \\param [IN] timeout      Transmission timeout [ms]
     */
    void    ( *SetTxConfig )( RadioModems_t modem, int8_t power, uint32_t fdev,
                              uint32_t bandwidth, uint32_t datarate,
                              uint8_t coderate, uint16_t preambleLen,
                              bool fixLen, bool crcOn, bool freqHopOn,
                              uint8_t hopPeriod, bool iqInverted, uint32_t timeout );
    /*!
     * \\brief Checks if the given RF frequency is supported by the hardware
     *
     * \\param [IN] frequency RF frequency to be checked
     * \\retval isSupported [true: supported, false: unsupported]
     */
    bool    ( *CheckRfFrequency )( uint32_t frequency );
    /*!
     * \\brief Computes the packet time on air in ms for the given payload
     *
     * \\Remark Can only be called once SetRxConfig or SetTxConfig have been called
     *
     * \\param [IN] modem      Radio modem to be used [0: FSK, 1: LoRa]
     * \\param [IN] pktLen     Packet payload length
     *
     * \\retval airTime        Computed airTime (ms) for the given packet payload length
     */
    uint32_t  ( *TimeOnAir )( RadioModems_t modem, uint8_t pktLen );
    /*!
     * \\brief Sends the buffer of size. Prepares the packet to be sent and sets
     *        the radio in transmission
     *
     * \\param [IN]: buffer     Buffer pointer
     * \\param [IN]: size       Buffer size
     */
    void    ( *Send )( uint8_t *buffer, uint8_t size );
    /*!
     * \\brief Sets the radio in sleep mode
     */
    void    ( *Sleep )( void );
    /*!
     * \\brief Sets the radio in standby mode
     */
    void    ( *Standby )( void );
    /*!
     * \\brief Sets the radio in reception mode for the given time
     * \\param [IN] timeout Reception timeout [ms]
     *                     [0: continuous, others timeout]
     */
    void    ( *Rx )( uint32_t timeout );
    /*!
     * \\brief Start a Channel Activity Detection
     */
    void    ( *StartCad )( void );
    /*!
     * \\brief Sets the radio in continuous wave transmission mode
     *
     * \\param [IN]: freq       Channel RF frequency
     * \\param [IN]: power      Sets the output power [dBm]
     * \\param [IN]: time       Transmission mode timeout [s]
     */
    void    ( *SetTxContinuousWave )( uint32_t freq, int8_t power, uint16_t time );
    /*!
     * \\brief Reads the current RSSI value
     *
     * \\retval rssiValue Current RSSI value in [dBm]
     */
    int16_t ( *Rssi )( RadioModems_t modem );
    /*!
     * \\brief Writes the radio register at the specified address
     *
     * \\param [IN]: addr Register address
     * \\param [IN]: data New register value
     */
    void    ( *Write )( uint16_t addr, uint8_t data );
    /*!
     * \\brief Reads the radio register at the specified address
     *
     * \\param [IN]: addr Register address
     * \\retval data Register value
     */
    uint8_t ( *Read )( uint16_t addr );
    /*!
     * \\brief Writes multiple radio registers starting at address
     *
     * \\param [IN] addr   First Radio register address
     * \\param [IN] buffer Buffer containing the new register's values
     * \\param [IN] size   Number of registers to be written
     */
    void    ( *WriteBuffer )( uint16_t addr, uint8_t *buffer, uint8_t size );
    /*!
     * \\brief Reads multiple radio registers starting at address
     *
     * \\param [IN] addr First Radio register address
     * \\param [OUT] buffer Buffer where to copy the registers data
     * \\param [IN] size Number of registers to be read
     */
    void    ( *ReadBuffer )( uint16_t addr, uint8_t *buffer, uint8_t size );
    /*!
     * \\brief Sets the maximum payload length.
     *
     * \\param [IN] modem      Radio modem to be used [0: FSK, 1: LoRa]
     * \\param [IN] max        Maximum payload length in bytes
     */
    void    ( *SetMaxPayloadLength )( RadioModems_t modem, uint8_t max );
    /*!
     * \\brief Sets the network to public or private. Updates the sync byte.
     *
     * \\remark Applies to LoRa modem only
     *
     * \\param [IN] enable if true, it enables a public network
     */
    void    ( *SetPublicNetwork )( bool enable );
    /*!
     * \\brief Gets the time required for the board plus radio to get out of sleep.[ms]
     *
     * \\retval time Radio plus board wakeup time in ms.
     */
    uint32_t  ( *GetWakeupTime )( void );
    /*!
     * \\brief Process radio irq
     */
    void ( *IrqProcess )( void );
    /*
     * The next functions are available only on SX126x radios.
     */
    /*!
     * \\brief Sets the radio in reception mode with Max LNA gain for the given time
     *
     * \\remark Available on SX126x radios only.
     *
     * \\param [IN] timeout Reception timeout [ms]
     *                     [0: continuous, others timeout]
     */
    void    ( *RxBoosted )( uint32_t timeout );
    /*!
     * \\brief Sets the Rx duty cycle management parameters
     *
     * \\remark Available on SX126x radios only.
     *
     * \\param [in]  rxTime        Structure describing reception timeout value
     * \\param [in]  sleepTime     Structure describing sleep timeout value
     */
    void ( *SetRxDutyCycle ) ( uint32_t rxTime, uint32_t sleepTime );
;

2、sx1276.c实现接口;

3、用户和底层打交道是通过5个事件,用户实现,底层驱动调用;

/*!
 * \\brief Radio driver callback functions
 */
typedef struct

    /*!
     * \\brief  Tx Done callback prototype.
     */
    void    ( *TxDone )( void );
    /*!
     * \\brief  Tx Timeout callback prototype.
     */
    void    ( *TxTimeout )( void );
    /*!
     * \\brief Rx Done callback prototype.
     *
     * \\param [IN] payload Received buffer pointer
     * \\param [IN] size    Received buffer size
     * \\param [IN] rssi    RSSI value computed while receiving the frame [dBm]
     * \\param [IN] snr     SNR value computed while receiving the frame [dB]
     *                     FSK : N/A ( set to 0 )
     *                     LoRa: SNR value in dB
     */
    void    ( *RxDone )(uint8_t *payload, uint16_t size, int16_t rssi, int8_t snr );
    /*!
     * \\brief  Rx Timeout callback prototype.
     */
    void    ( *RxTimeout )( void );
    /*!
     * \\brief Rx Error callback prototype.
     */
    void    ( *RxError )( void );
    /*!
     * \\brief  FHSS Change Channel callback prototype.
     *
     * \\param [IN] currentChannel   Index number of the current channel
     */
    void ( *FhssChangeChannel )( uint8_t currentChannel );

    /*!
     * \\brief CAD Done callback prototype.
     *
     * \\param [IN] channelDetected    Channel Activity detected during the CAD
     */
    void ( *CadDone ) ( bool channelActivityDetected );
RadioEvents_t;

4、DIO中断处理

Lora有DIO0-DIO5,通过它们来指示目前芯片状态(比用SPI查询,快),底层驱动有对应的处理函数:

SX1276OnDio0Irq,

SX1276OnDio1Irq,

SX1276OnDio2Irq,

SX1276OnDio3Irq,

SX1276OnDio4Irq

DIO5没有,

这5个DIO处理函数在驱动里面是放到一个数组中,

/*!
 * Hardware DIO IRQ callback initialization
 */
static DioIrqHandler *DioIrq[] =  SX1276OnDio0Irq, SX1276OnDio1Irq,
                                   SX1276OnDio2Irq, SX1276OnDio3Irq,
                                   SX1276OnDio4Irq, NULL ;

然后需要外部实现:void SX1276IoIrqInit(DioIrqHandler **irqHandlers )

查看例子:

void SX1276IoIrqInit( DioIrqHandler **irqHandlers )

    GpioSetInterrupt( &SX1276.DIO0, LL_EXTI_MODE_IT, LL_EXTI_TRIGGER_RISING, IRQ_HIGH_PRIORITY, irqHandlers[0] );
    GpioSetInterrupt( &SX1276.DIO1, LL_EXTI_MODE_IT, LL_EXTI_TRIGGER_RISING, IRQ_HIGH_PRIORITY, irqHandlers[1] );
    GpioSetInterrupt( &SX1276.DIO2, LL_EXTI_MODE_IT, LL_EXTI_TRIGGER_RISING, IRQ_HIGH_PRIORITY, irqHandlers[2] );
    GpioSetInterrupt( &SX1276.DIO3, LL_EXTI_MODE_IT, LL_EXTI_TRIGGER_RISING, IRQ_HIGH_PRIORITY, irqHandlers[3] );
    GpioSetInterrupt( &SX1276.DIO4, LL_EXTI_MODE_IT, LL_EXTI_TRIGGER_RISING, IRQ_HIGH_PRIORITY, irqHandlers[4] );
    GpioSetInterrupt( &SX1276.DIO5, LL_EXTI_MODE_IT, LL_EXTI_TRIGGER_RISING, IRQ_HIGH_PRIORITY, irqHandlers[5] );

功能就是把中断处理函数放到对应的IO口中断处理函数里面,官方例程实现有点复杂,因为他写的是通用架构(我个人感觉没必要,总之记住一句话:把IO口中断放到对应的IO口中断处理函数里面)。以上是历程思路,也可以用驱动V2.1.0的查询思路,很简单:就是查询IO口状态,如果有效,则调用对应的函数,实现如下:

实现函数IrqProcess(入参void* ptRFStruct是为了支持多芯片,你可以直接在函数里面用实体实现):

#if (0 == LORA_IRQ_IS_ENABLE)
void IrqProcess( void* ptRFStruct )

	SX1276_t* ptSX1276 = (SX1276_t*)ptRFStruct;
    DioIrqHandler*  ptIrqHandler = NULL;
    if(NULL == ptSX1276)
        return;
    

    if(GpioRead(&ptSX1276->DIO0))
        ptIrqHandler=ptSX1276->DioIrq[0];
        if(NULL != ptIrqHandler)
            ptIrqHandler(ptSX1276,NULL);
        
    
//    if(GpioRead(&ptSX1276->DIO1))
//        ptIrqHandler=ptSX1276->DioIrq[1];
//        if(NULL != ptIrqHandler)
//            ptIrqHandler(ptSX1276,NULL);
//        
//    
//    if(GpioRead(&ptSX1276->DIO2))
//        ptIrqHandler=ptSX1276->DioIrq[2];
//        if(NULL != ptIrqHandler)
//            ptIrqHandler(ptSX1276,NULL);
//        
//    
//    if(GpioRead(&ptSX1276->DIO3))
//        ptIrqHandler=ptSX1276->DioIrq[3];
//        if(NULL != ptIrqHandler)
//            ptIrqHandler(ptSX1276,NULL);
//        
//    
//    if(GpioRead(&ptSX1276->DIO4))
//        ptIrqHandler=ptSX1276->DioIrq[4];
//        if(NULL != ptIrqHandler)
//            ptIrqHandler(ptSX1276,NULL);
//        
//    
//    if(GpioRead(&ptSX1276->DIO5))
//        ptIrqHandler=ptSX1276->DioIrq[5];
//        if(NULL != ptIrqHandler)
//            ptIrqHandler(ptSX1276,NULL);
//        
//    

#endif

由于我测试驱动,只用到了DIO0,因此其他处理函数屏蔽掉。

5、超时处理函数

驱动提供了超时处理函数SX1276OnTimeoutIrq,驱动里面定义的三个超时事件:

/*!
 * Tx and Rx timers
 */
TimerEvent_t TxTimeoutTimer;
TimerEvent_t RxTimeoutTimer;
TimerEvent_t RxTimeoutSyncWord;

驱动初始化:

    // Initialize driver timeout timers
    TimerInit( &TxTimeoutTimer, SX1276OnTimeoutIrq );
    TimerInit( &RxTimeoutTimer, SX1276OnTimeoutIrq );
    TimerInit( &RxTimeoutSyncWord, SX1276OnTimeoutIrq );

主要在收、发、接收同步字超时时候处理,这里如果不用低功耗模式(MCU休眠),则可以直接忽略,同时在应用层自己做对应逻辑,或者在应用层超时时候直接调用这个超时处理函数即可。

三、实现驱动支持多芯片

1、查看sx1276.c的全局变量:

/*
 * Private global variables
 */

/*!
 * Radio callbacks variable
 */
static RadioEvents_t *RadioEvents;

/*!
 * Reception buffer
 */
static uint8_t RxTxBuffer[RX_BUFFER_SIZE];

/*
 * Public global variables
 */

/*!
 * Radio hardware and global parameters
 */
SX1276_t SX1276;

/*!
 * Hardware DIO IRQ callback initialization
 */
DioIrqHandler *DioIrq[] =  SX1276OnDio0Irq, SX1276OnDio1Irq,
                            SX1276OnDio2Irq, SX1276OnDio3Irq,
                            SX1276OnDio4Irq, NULL ;

/*!
 * Tx and Rx timers
 */
TimerEvent_t TxTimeoutTimer;
TimerEvent_t RxTimeoutTimer;
TimerEvent_t RxTimeoutSyncWord;

分析如下:

1、RadioEvents变量指针为事件处理,需要用户实现,然后传入进来,由底层驱动调用,每个芯片的事件处理肯定不同,因此RadioEvents放到struct SX1276_s结构体中;

2、static uint8_t RxTxBuffer[RX_BUFFER_SIZE];接收发送缓存,放到struct SX1276_s结构体中;

3、SX1276_t SX1276;去掉,由用户层定义,当做驱动层API的入参;

4、DioIrq不变,修改DIO中断处理的,增加SX1276_t*指针入参;

5、三个超时处理事件变量,放到struct SX1276_s结构体中;

6、struct SX1276_s结构体中增加电源控制脚、射频开关控制脚,修改后如下:

/*!
 * Radio hardware and global parameters
 */
typedef struct SX1276_s

    //GPIO
    Gpio_t        Reset;
    Gpio_t        Power;
    Gpio_t        RFswitch;
    Gpio_t        DIO0;
    Gpio_t        DIO1;
    Gpio_t        DIO2;
    Gpio_t        DIO3;
    Gpio_t        DIO4;
    Gpio_t        DIO5;
    Spi_t         Spi;
    RadioSettings_t Settings;

    //事件
    RadioEvents_t *RadioEvents;
    //缓存
    uint8_t RxTxBuffer[RX_BUFFER_SIZE];
    //超时事件
    TimerEvent_t TxTimeoutTimer;
    TimerEvent_t RxTimeoutTimer;
    TimerEvent_t RxTimeoutSyncWord;
    //IO中断
#if (0 == LORA_IRQ_IS_ENABLE)
    DioIrqHandler **DioIrq;
#endif
SX1276_t;

7、Spi_t里面增加SpiId变量,修改如下;

/*!
 * SPI object type definition
 */
typedef struct Spi_s

    SPI_TypeDef* SpiId;
    Gpio_t Mosi;
    Gpio_t Miso;
    Gpio_t Sclk;
    Gpio_t Nss;
Spi_t;

8、接口修改

对应的接口增加void*入参,因为radio.h为对外接口文件,sx1276.h包含radio.h,因此struct SX1276_s类型对radio.h是不可见的,

修改后的radio.h抽象接口:

/*!
 * \\brief Radio driver definition
 */
struct Radio_s

    /*!
     * \\brief Initializes the radio
     *
     * \\param [IN] events Structure containing the driver callback functions
     */
    void    ( *Init )(void* ptSX1276, RadioEvents_t *events );
    /*!
     * Return current radio status
     *
     * \\param status Radio status.[RF_IDLE, RF_RX_RUNNING, RF_TX_RUNNING]
     */
    RadioState_t ( *GetStatus )( void* ptSX1276 );
    /*!
     * \\brief Configures the radio with the given modem
     *
     * \\param [IN] modem Modem to be used [0: FSK, 1: LoRa]
     */
    void    ( *SetModem )(void* ptSX1276, RadioModems_t modem );
    /*!
     * \\brief Sets the channel frequency
     *
     * \\param [IN] freq         Channel RF frequency
     */
    void    ( *SetChannel )(void* ptSX1276, uint32_t freq );
    /*!
     * \\brief Checks if the channel is free for the given time
     *
     * \\param [IN] modem      Radio modem to be used [0: FSK, 1: LoRa]
     * \\param [IN] freq       Channel RF frequency
     * \\param [IN] rssiThresh RSSI threshold
     * \\param [IN] maxCarrierSenseTime Max time while the RSSI is measured
     *
     * \\retval isFree         [true: Channel is free, false: Channel is not free]
     */
    bool    ( *IsChannelFree )(void* ptSX1276, RadioModems_t modem, uint32_t freq, int16_t rssiThresh, uint32_t maxCarrierSenseTime );
    /*!
     * \\brief Generates a 32 bits random value based on the RSSI readings
     *
     * \\remark This function sets the radio in LoRa modem mode and disables
     *         all interrupts.
     *         After calling this function either Radio.SetRxConfig or
     *         Radio.SetTxConfig functions must be called.
     *
     * \\retval randomValue    32 bits random value
     */
    uint32_t ( *Random )( void* ptSX1276 );
    /*!
     * \\brief Sets the reception parameters
     *
     * \\param [IN] modem        Radio modem to be used [0: FSK, 1: LoRa]
     * \\param [IN] bandwidth    Sets the bandwidth
     *                          FSK : >= 2600 and <= 250000 Hz
     *                          LoRa: [0: 125 kHz, 1: 250 kHz,
     *                                 2: 500 kHz, 3: Reserved]
     * \\param [IN] datarate     Sets the Datarate
     *                          FSK : 600..300000 bits/s
     *                          LoRa: [6: 64, 7: 128, 8: 256, 9: 512,
     *                                10: 1024, 11: 2048, 12: 4096  chips]
     * \\param [IN] coderate     Sets the coding rate (LoRa only)
     *                          FSK : N/A ( set to 0 )
     *                          LoRa: [1: 4/5, 2: 4/6, 3: 4/7, 4: 4/8]
     * \\param [IN] bandwidthAfc Sets the AFC Bandwidth (FSK only)
     *                          FSK : >= 2600 and <= 250000 Hz
     *                          LoRa: N/A ( set to 0 )
     * \\param [IN] preambleLen  Sets the Preamble length
     *                          FSK : Number of bytes
     *                          LoRa: Length in symbols (the hardware adds 4 more symbols)
     * \\param [IN] symbTimeout  Sets the RxSingle timeout value
     *                          FSK : timeout in number of bytes
     *                          LoRa: timeout in symbols
     * \\param [IN] fixLen       Fixed length packets [0: variable, 1: fixed]
     * \\param [IN] payloadLen   Sets payload length when fixed length is used
     * \\param [IN] crcOn        Enables/Disables the CRC [0: OFF, 1: ON]
     * \\param [IN] freqHopOn    Enables disables the intra-packet frequency hopping
     *                          FSK : N/A ( set to 0 )
     *                          LoRa: [0: OFF, 1: ON]
     * \\param [IN] hopPeriod    Number of symbols between each hop
     *                          FSK : N/A ( set to 0 )
     *                          LoRa: Number of symbols
     * \\param [IN] iqInverted   Inverts IQ signals (LoRa only)
     *                          FSK : N/A ( set to 0 )
     *                          LoRa: [0: not inverted, 1: inverted]
     * \\param [IN] rxContinuous Sets the reception in continuous mode
     *                          [false: single mode, true: continuous mode]
     */
    void    ( *SetRxConfig )(void* ptSX1276, RadioModems_t modem, uint32_t bandwidth,
                              uint32_t datarate, uint8_t coderate,
                              uint32_t bandwidthAfc, uint16_t preambleLen,
                              uint16_t symbTimeout, bool fixLen,
                              uint8_t payloadLen,
                              bool crcOn, bool freqHopOn, uint8_t hopPeriod,
                              bool iqInverted, bool rxContinuous );
    /*!
     * \\brief Sets the transmission parameters
     *
     * \\param [IN] modem        Radio modem to be used [0: FSK, 1: LoRa]
     * \\param [IN] power        Sets the output power [dBm]
     * \\param [IN] fdev         Sets the frequency deviation (FSK only)
     *                          FSK : [Hz]
     *                          LoRa: 0
     * \\param [IN] bandwidth    Sets the bandwidth (LoRa only)
     *                          FSK : 0
     *                          LoRa: [0: 125 kHz, 1: 250 kHz,
     *                                 2: 500 kHz, 3: Reserved]
     * \\param [IN] datarate     Sets the Datarate
     *                          FSK : 600..300000 bits/s
     *                          LoRa: [6: 64, 7: 128, 8: 256, 9: 512,
     *                                10: 1024, 11: 2048, 12: 4096  chips]
     * \\param [IN] coderate     Sets the coding rate (LoRa only)
     *                          FSK : N/A ( set to 0 )
     *                          LoRa: [1: 4/5, 2: 4/6, 3: 4/7, 4: 4/8]
     * \\param [IN] preambleLen  Sets the preamble length
     *                          FSK : Number of bytes
     *                          LoRa: Length in symbols (the hardware adds 4 more symbols)
     * \\param [IN] fixLen       Fixed length packets [0: variable, 1: fixed]
     * \\param [IN] crcOn        Enables disables the CRC [0: OFF, 1: ON]
     * \\param [IN] freqHopOn    Enables disables the intra-packet frequency hopping
     *                          FSK : N/A ( set to 0 )
     *                          LoRa: [0: OFF, 1: ON]
     * \\param [IN] hopPeriod    Number of symbols between each hop
     *                          FSK : N/A ( set to 0 )
     *                          LoRa: Number of symbols
     * \\param [IN] iqInverted   Inverts IQ signals (LoRa only)
     *                          FSK : N/A ( set to 0 )
     *                          LoRa: [0: not inverted, 1: inverted]
     * \\param [IN] timeout      Transmission timeout [ms]
     */
    void    ( *SetTxConfig )(void* ptSX1276, RadioModems_t modem, int8_t power, uint32_t fdev,
                              uint32_t bandwidth, uint32_t datarate,
                              uint8_t coderate, uint16_t preambleLen,
                              bool fixLen, bool crcOn, bool freqHopOn,
                              uint8_t hopPeriod, bool iqInverted, uint32_t timeout );
    /*!
     * \\brief Checks if the given RF frequency is supported by the hardware
     *
     * \\param [IN] frequency RF frequency to be checked
     * \\retval isSupported [true: supported, false: unsupported]
     */
    bool    ( *CheckRfFrequency )(void* ptSX1276, uint32_t frequency );
    /*!
     * \\brief Computes the packet time on air in ms for the given payload
     *
     * \\Remark Can only be called once SetRxConfig or SetTxConfig have been called
     *
     * \\param [IN] modem      Radio modem to be used [0: FSK, 1: LoRa]
     * \\param [IN] pktLen     Packet payload length
     *
     * \\retval airTime        Computed airTime (ms) for the given packet payload length
     */
    uint32_t  ( *TimeOnAir )(void* ptSX1276, RadioModems_t modem, uint8_t pktLen );
    /*!
     * \\brief Sends the buffer of size. Prepares the packet to be sent and sets
     *        the radio in transmission
     *
     * \\param [IN]: buffer     Buffer pointer
     * \\param [IN]: size       Buffer size
     */
    void    ( *Send )(void* ptSX1276, uint8_t *buffer, uint8_t size );
    /*!
     * \\brief Sets the radio in sleep mode
     */
    void    ( *Sleep )( void* ptSX1276 );
    /*!
     * \\brief Sets the radio in standby mode
     */
    void    ( *Standby )( void* ptSX1276 );
    /*!
     * \\brief Sets the radio in reception mode for the given time
     * \\param [IN] timeout Reception timeout [ms]
     *                     [0: continuous, others timeout]
     */
    void    ( *Rx )(void* ptSX1276, uint32_t timeout );
    /*!
     * \\brief Start a Channel Activity Detection
     */
    void    ( *StartCad )( void* ptSX1276 );
    /*!
     * \\brief Sets the radio in continuous wave transmission mode
     *
     * \\param [IN]: freq       Channel RF frequency
     * \\param [IN]: power      Sets the output power [dBm]
     * \\param [IN]: time       Transmission mode timeout [s]
     */
    void    ( *SetTxContinuousWave )(void* ptSX1276, uint32_t freq, int8_t power, uint16_t time );
    /*!
     * \\brief Reads the current RSSI value
     *
     * \\retval rssiValue Current RSSI value in [dBm]
     */
    int16_t ( *Rssi )(void* ptSX1276, RadioModems_t modem );
    /*!
     * \\brief Writes the radio register at the specified address
     *
     * \\param [IN]: addr Register address
     * \\param [IN]: data New register value
     */
    void    ( *Write )(void* ptSX1276, uint16_t addr, uint8_t data );
    /*!
     * \\brief Reads the radio register at the specified address
     *
     * \\param [IN]: addr Register address
     * \\retval data Register value
     */
    uint8_t ( *Read )(void* ptSX1276, uint16_t addr );
    /*!
     * \\brief Writes multiple radio registers starting at address
     *
     * \\param [IN] addr   First Radio register address
     * \\param [IN] buffer Buffer containing the new register's values
     * \\param [IN] size   Number of registers to be written
     */
    void    ( *WriteBuffer )(void* ptSX1276, uint16_t addr, uint8_t *buffer, uint8_t size );
    /*!
     * \\brief Reads multiple radio registers starting at address
     *
     * \\param [IN] addr First Radio register address
     * \\param [OUT] buffer Buffer where to copy the registers data
     * \\param [IN] size Number of registers to be read
     */
    void    ( *ReadBuffer )(void* ptSX1276, uint16_t addr, uint8_t *buffer, uint8_t size );
    /*!
     * \\brief Sets the maximum payload length.
     *
     * \\param [IN] modem      Radio modem to be used [0: FSK, 1: LoRa]
     * \\param [IN] max        Maximum payload length in bytes
     */
    void    ( *SetMaxPayloadLength )(void* ptSX1276, RadioModems_t modem, uint8_t max );
    /*!
     * \\brief Sets the network to public or private. Updates the sync byte.
     *
     * \\remark Applies to LoRa modem only
     *
     * \\param [IN] enable if true, it enables a public network
     */
    void    ( *SetPublicNetwork )(void* ptSX1276, bool enable );
    /*!
     * \\brief Gets the time required for the board plus radio to get out of sleep.[ms]
     *
     * \\retval time Radio plus board wakeup time in ms.
     */
    uint32_t  ( *GetWakeupTime )(void* ptSX1276 );
    /*!
     * \\brief Process radio irq
     */
    void ( *IrqProcess )( void* ptSX1276 );
    /*
     * The next functions are available only on SX126x radios.
     */
    /*!
     * \\brief Sets the radio in reception mode with Max LNA gain for the given time
     *
     * \\remark Available on SX126x radios only.
     *
     * \\param [IN] timeout Reception timeout [ms]
     *                     [0: continuous, others timeout]
     */
    void    ( *RxBoosted )(void* ptSX1276, uint32_t timeout );
    /*!
     * \\brief Sets the Rx duty cycle management parameters
     *
     * \\remark Available on SX126x radios only.
     *
     * \\param [in]  rxTime        Structure describing reception timeout value
     * \\param [in]  sleepTime     Structure describing sleep timeout value
     */
    void ( *SetRxDutyCycle ) (void* ptSX1276, uint32_t rxTime, uint32_t sleepTime );
;

定义在sx1276-board.c:

/*!
 * Radio driver structure initialization
 */
const struct Radio_s Radio =

    SX1276Init,
    SX1276GetStatus,
    SX1276SetModem,
    SX1276SetChannel,
    SX1276IsChannelFree,
    SX1276Random,
    SX1276SetRxConfig,
    SX1276SetTxConfig,
    SX1276CheckRfFrequency,
    SX1276GetTimeOnAir,
    SX1276Send,
    SX1276SetSleep,
    SX1276SetStby,
    SX1276SetRx,
    SX1276StartCad,
    SX1276SetTxContinuousWave,
    SX1276ReadRssi,
    SX1276Write,
    SX1276Read,
    SX1276WriteBuffer,
    SX1276ReadBuffer,
    SX1276SetMaxPayloadLength,
    SX1276SetPublicNetwork,
    SX1276GetWakeupTime,
#if (0 == LORA_IRQ_IS_ENABLE)
    IrqProcess,
#else
    NULL, // void ( *IrqProcess )( void )
#endif
    NULL, // void ( *RxBoosted )( uint32_t timeout ) - SX126x Only
    NULL, // void ( *SetRxDutyCycle )( uint32_t rxTime, uint32_t sleepTime ) - SX126x Only
;

sx1276.c接口实现修改,以SX1276Init为例,其它函数修改类似:

void SX1276Init(void* ptRFStruct, RadioEvents_t *events )

    uint8_t i;

    SX1276_t* ptSX1276 = (SX1276_t*)ptRFStruct;
    if(NULL == ptSX1276)
        return;
    

    ptSX1276->RadioEvents = events;

    // Initialize driver timeout timers
    TimerInit( &ptSX1276->TxTimeoutTimer, SX1276OnTimeoutIrq );
    TimerInit( &ptSX1276->RxTimeoutTimer, SX1276OnTimeoutIrq );
    TimerInit( &ptSX1276->RxTimeoutSyncWord, SX1276OnTimeoutIrq );

    //TRACE_DEBUG("SX1276Init=%d\\r\\n",1);
    SX1276Reset( ptSX1276 );
    //TRACE_DEBUG("SX1276Init=%d\\r\\n",2);
    while(0x6C != SX1276Read(ptSX1276,0x06))
        TRACE_ERROR("Hard SPI Err!\\r\\n");
        my_delay_ms(500);
    
    //TRACE_DEBUG("SX1276Init=%d\\r\\n",3);

    RxChainCalibration(ptSX1276 );

    SX1276SetOpMode(ptSX1276, RF_OPMODE_SLEEP );

#if (LORA_IRQ_IS_ENABLE)
    SX1276IoIrqInit(ptSX1276, DioIrq );
#else
    ptSX1276->DioIrq = DioIrq;
#endif

    for( i = 0; i < sizeof( RadioRegsInit ) / sizeof( RadioRegisters_t ); i++ )
    
        SX1276SetModem(ptSX1276, RadioRegsInit[i].Modem );
        SX1276Write(ptSX1276, RadioRegsInit[i].Addr, RadioRegsInit[i].Value );
    

    SX1276SetModem(ptSX1276, MODEM_FSK );

    ptSX1276->Settings.State = RF_IDLE;

    //TRACE_DEBUG("SX1276Init irq= %x\\r\\n",SX1276Read(REG_LR_IRQFLAGS));

四、应用层修改

1、首先定义不同实体:

static net_work_t   s_tNetWorkRF1;
static net_work_t   s_tNetWorkRF2;
static net_work_t   s_tNetWorkRF3;

2、实现不同的事件处理函数:

static void OnTxDoneRF1( void )

    SET_EVENT(&s_tNetWorkRF1.tRfTxDone);


static void OnRxDoneRF1(uint8_t *payload, uint16_t size, int16_t rssi, int8_t snr )

     if(WAIT_EVENT(&s_tNetWorkRF1.tRfRxDone) || size > NET_WOEK_RF_BUFFER_SIZE || NULL == payload)
        return;
    
    memcpy(s_tNetWorkRF1.chRadioRxBuffer,payload,size);
    s_tNetWorkRF1.hwRadioRxSize = size;
    s_tNetWorkRF1.chPacketRssiValue = rssi;
    s_tNetWorkRF1.chSnrValue = snr;
    SET_EVENT(&s_tNetWorkRF1.tRfRxDone);


static void OnTxTimeoutRF1( void )

    //Radio.Sleep( );


static void OnRxTimeoutRF1( void )

    //Radio.Sleep( );


static void OnRxErrorRF1( void )

    //Radio.Sleep( );


static void OnTxDoneRF2( void )

    SET_EVENT(&s_tNetWorkRF2.tRfTxDone);


static void OnRxDoneRF2(uint8_t *payload, uint16_t size, int16_t rssi, int8_t snr )

     if(WAIT_EVENT(&s_tNetWorkRF2.tRfRxDone) || size > NET_WOEK_RF_BUFFER_SIZE || NULL == payload)
        return;
    
    memcpy(s_tNetWorkRF2.chRadioRxBuffer,payload,size);
    s_tNetWorkRF2.hwRadioRxSize = size;
    s_tNetWorkRF2.chPacketRssiValue = rssi;
    s_tNetWorkRF2.chSnrValue = snr;
    SET_EVENT(&s_tNetWorkRF2.tRfRxDone);


static void OnTxTimeoutRF2( void )

    //Radio.Sleep( );


static void OnRxTimeoutRF2( void )

    //Radio.Sleep( );


static void OnRxErrorRF2( void )

    //Radio.Sleep( );


static void OnTxDoneRF3( void )

    SET_EVENT(&s_tNetWorkRF3.tRfTxDone);


static void OnRxDoneRF3(uint8_t *payload, uint16_t size, int16_t rssi, int8_t snr )

     if(WAIT_EVENT(&s_tNetWorkRF3.tRfRxDone) || size > NET_WOEK_RF_BUFFER_SIZE || NULL == payload)
        return;
    
    memcpy(s_tNetWorkRF3.chRadioRxBuffer,payload,size);
    s_tNetWorkRF3.hwRadioRxSize = size;
    s_tNetWorkRF3.chPacketRssiValue = rssi;
    s_tNetWorkRF3.chSnrValue = snr;
    SET_EVENT(&s_tNetWorkRF3.tRfRxDone);


static void OnTxTimeoutRF3( void )

    //Radio.Sleep( );


static void OnRxTimeoutRF3( void )

    //Radio.Sleep( );


static void OnRxErrorRF3( void )

    //Radio.Sleep( );

/*!
 * Radio events function pointer
 */
static const RadioEvents_t s_RadioEventsRF1 = 
    .TxDone             = OnTxDoneRF1,
    .TxTimeout          = OnTxTimeoutRF1,
    .RxDone             = OnRxDoneRF1,
    .RxTimeout          = OnRxTimeoutRF1,
    .RxError            = OnRxErrorRF1,
    .FhssChangeChannel  = NULL,
    .CadDone            = NULL,
;
static const RadioEvents_t s_RadioEventsRF2 = 
    .TxDone             = OnTxDoneRF2,
    .TxTimeout          = OnTxTimeoutRF2,
    .RxDone             = OnRxDoneRF2,
    .RxTimeout          = OnRxTimeoutRF2,
    .RxError            = OnRxErrorRF2,
    .FhssChangeChannel  = NULL,
    .CadDone            = NULL,
;
static const RadioEvents_t s_RadioEventsRF3 = 
    .TxDone             = OnTxDoneRF3,
    .TxTimeout          = OnTxTimeoutRF3,
    .RxDone             = OnRxDoneRF3,
    .RxTimeout          = OnRxTimeoutRF3,
    .RxError            = OnRxErrorRF3,
    .FhssChangeChannel  = NULL,
    .CadDone            = NULL,
;

3、初始化如下:

void user_net_work_init(net_work_app_api_t *ptAppApiRF1,net_work_app_api_t *ptAppApiRF2,net_work_app_api_t *ptAppApiRF3)

    srand(s_chRandomNumber[0]%s_chRandomNumber[1]+s_chRandomNumber[2]%s_chRandomNumber[3]);     //随机数种子
    TRACE_DEBUG("srand=%d\\r\\n",(s_chRandomNumber[0]%s_chRandomNumber[1]+s_chRandomNumber[2]%s_chRandomNumber[3]));

    memset((uint8_t*)&s_tNetWorkRF1,0x00,sizeof(net_work_t));
    memset((uint8_t*)&s_tNetWorkRF2,0x00,sizeof(net_work_t));
    memset((uint8_t*)&s_tNetWorkRF3,0x00,sizeof(net_work_t));

    s_tNetWorkRF1.tChannel = RF_DATA_CHANNEL_BASE_STATION;
    s_tNetWorkRF2.tChannel = RF_DATA_CHANNEL_HANDHELD_DEVICE;
    s_tNetWorkRF3.tChannel = RF_DATA_CHANNEL_SENSOR_1;

    SX1276IoInit(&s_tNetWorkRF1.tSX1276,s_tNetWorkRF1.tChannel);
    SX1276IoInit(&s_tNetWorkRF2.tSX1276,s_tNetWorkRF2.tChannel);
    SX1276IoInit(&s_tNetWorkRF3.tSX1276,s_tNetWorkRF3.tChannel);

    SpiInit(&s_tNetWorkRF1.tSX1276.Spi,RF1_SPI);
    SpiInit(&s_tNetWorkRF2.tSX1276.Spi,RF2_SPI);
    SpiInit(&s_tNetWorkRF3.tSX1276.Spi,RF3_SPI);

    net_work_init(&s_tNetWorkRF1,s_chRfBufferRF1,sizeof(s_chRfBufferRF1),(RadioEvents_t*)&s_RadioEventsRF1,ptAppApiRF1);
    TRACE_DEBUG("RF1 init success\\r\\n");
    net_work_init(&s_tNetWorkRF2,s_chRfBufferRF2,sizeof(s_chRfBufferRF2),(RadioEvents_t*)&s_RadioEventsRF2,ptAppApiRF2);
    TRACE_DEBUG("RF2 init success\\r\\n");
    net_work_init(&s_tNetWorkRF3,s_chRfBufferRF3,sizeof(s_chRfBufferRF3),(RadioEvents_t*)&s_RadioEventsRF3,ptAppApiRF3);
    TRACE_DEBUG("RF3 init success\\r\\n");


4、应用接口思路同驱动一样

示例:

/*************************************  APP  **********************************/
bool net_work_service_process(void)

    net_work_rf_process(&s_tNetWorkRF1);                       //无线线程
    net_work_protocol_analysis_process(&s_tNetWorkRF1);        //协议解析
    father_station_routing_table_maintenance(&s_tNetWorkRF1);  //父节路由点维护
    net_work_logic_process(&s_tNetWorkRF1);                    //组网逻辑
    net_work_check_sx1278_process(&s_tNetWorkRF1);             //SX1278维护线程
    s_tNetWorkRF1.ptRadio->IrqProcess(&s_tNetWorkRF1.tSX1276); //DIO0处理函数

    net_work_rf_process(&s_tNetWorkRF2);                       //无线线程
    net_work_protocol_analysis_process(&s_tNetWorkRF2);        //协议解析
    father_station_routing_table_maintenance(&s_tNetWorkRF2);  //父节路由点维护
    net_work_logic_process(&s_tNetWorkRF2);                    //组网逻辑
    net_work_check_sx1278_process(&s_tNetWorkRF2);             //SX1278维护线程
    s_tNetWorkRF2.ptRadio->IrqProcess(&s_tNetWorkRF2.tSX1276); //DIO0处理函数

    net_work_rf_process(&s_tNetWorkRF3);                       //无线线程
    net_work_protocol_analysis_process(&s_tNetWorkRF3);        //协议解析
    father_station_routing_table_maintenance(&s_tNetWorkRF3);  //父节路由点维护
    net_work_logic_process(&s_tNetWorkRF3);                    //组网逻辑
    net_work_check_sx1278_process(&s_tNetWorkRF3);             //SX1278维护线程
    s_tNetWorkRF3.ptRadio->IrqProcess(&s_tNetWorkRF3.tSX1276); //DIO0处理函数

    return true;

读取RSSI应用层API:

/********************************  SX1278维护线程  ****************************/
static bool  net_work_check_rf_chip_state(net_work_t *ptNetWork)

    int16_t             iTemp   = 0;
    static  uint8_t     chNum   = 0;

    if(RF_IDLE == ptNetWork->ptRadio->GetStatus(&ptNetWork->tSX1276))
        chNum = 0;
        return true;
    

    iTemp = ptNetWork->ptRadio->Rssi(&ptNetWork->tSX1276,MODEM_LORA);
    TRACE_DEBUG("RF rx rssi = %d\\r\\n",iTemp);

    if(iTemp <= (-160))
        chNum++;
        TRACE_ERROR("RF rx rssi error = %d\\r\\n",iTemp);
        if(chNum >= 5)
            chNum = 0;
            return false;
        
    else
        chNum=0;
    
    return true;

读取RSSI驱动层API:

int16_t SX1276ReadRssi(void* ptRFStruct, RadioModems_t modem )

    int16_t rssi = 0;
	SX1276_t* ptSX1276 = (SX1276_t*)ptRFStruct;
    if(NULL == ptSX1276)
        return 0;
    
    switch( modem )
    
    case MODEM_FSK:
        rssi = -( SX1276Read(ptSX1276, REG_RSSIVALUE ) >> 1 );
        break;
    case MODEM_LORA:
        if( ptSX1276->Settings.Channel > RF_MID_BAND_THRESH )
        
            rssi = RSSI_OFFSET_HF + SX1276Read(ptSX1276, REG_LR_RSSIVALUE );
        
        else
        
            rssi = RSSI_OFFSET_LF + SX1276Read(ptSX1276, REG_LR_RSSIVALUE );
        
        break;
    default:
        rssi = -1;
        break;
    
    return rssi;

运行效果如下:

以上是关于Lora1278驱动V4.4.2讲解二:驱动多个SX1278芯片的主要内容,如果未能解决你的问题,请参考以下文章

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

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

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

Arduino 和 SX1278 的 LoRa B 类实现 TDMA

Arduino Uno 实现的 Lora sx1278

Xilinx Spartan 6 驱动ADS1278/4