注意,Lora1278驱动sx12xxDrivers-V2.1.0,原厂已经不更新和维护了,反馈的任何软件问题,

原厂就是一句话升级新的驱动,新驱动下载地址:https://github.com/Lora-net/LoRaMac-node

1276的驱动可以用与1278:

频段划分:

 其中频段1属于HF,频段2和3属于LF,在看驱动时候会用到。

射频输出:

TCXO设置:

一、驱动架构讲解

由于Lora芯片有好几款,官方本意是通过Radio.h文件抽象出接口层,对上有统一的接口,对

用户屏蔽掉硬件层差异,首先来看Radio的接口:

/*!* \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 );
};

以上是用户可见的的接口,由于芯片还有一些事件,比如:接收完成数据,发送完成数据,

接收超时等等,再看对事件的抽象:

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;

官方的库文件结合了DIO0和SPI(必须有NSS),来驱动Lora,其中SPI采用的查询模式,

至于是硬SPI还是软SPI无所谓。

DIO0则在接收时候映射为接收完成输出,在发送时候映射为发送完成输出,它们都是电平

变化输出,不是脉冲变化,然后有用户手动复位。

二、移植工作

1、打开官方文件,由于我用的是Lora1278,因此把画框的几个文件拷贝到工程里面:
/

2、平台相关的

我们需要把如下几个文件加到工程:

其中:

fifo.c 和fifo.h在LoRaMac-node-develop\src\system文件夹,不要改动;

spi.c是我手动自己新建的文件,spi.h在LoRaMac-node-develop\src\system文件夹,需要改动;

gpio.c和gpio.h在LoRaMac-node-develop\src\system文件夹,需要改动;

timer.c和timer.h在LoRaMac-node-develop\src\system文件夹,需要改动;

sx1276-board.c在LoRaMac-node-develop\src\boards\B-L072Z-LRWAN1拷贝,

sx1276-board.h在LoRaMac-node-develop\src\boards拷贝

main.c参考LoRaMac-node-develop\src\apps\ping-pong\NucleoL476

board-config.h从LoRaMac-node-develop\src\boards\NucleoL476拷贝

还有一个文件:rtc-board.c和rtc-board.h,这个暂时不讲。

3、移植工作

1)fifo文件

前面说了,无需改动,官方提供的;

2)spi

打开SPI.h文件,你会发现里面有好多函数,其实底层的驱动只用到了一个函数

uint16_t SpiInOut( Spi_t *obj, uint16_t outData ),调用在sx1276.c里面:

void SX1276WriteBuffer( uint16_t addr, uint8_t *buffer, uint8_t size )
{uint8_t i;//NSS = 0;GpioWrite( &SX1276.Spi.Nss, 0 );SpiInOut( &SX1276.Spi, addr | 0x80 );for( i = 0; i < size; i++ ){SpiInOut( &SX1276.Spi, buffer[i] );}//NSS = 1;GpioWrite( &SX1276.Spi.Nss, 1 );
}void SX1276ReadBuffer( uint16_t addr, uint8_t *buffer, uint8_t size )
{uint8_t i;//NSS = 0;GpioWrite( &SX1276.Spi.Nss, 0 );SpiInOut( &SX1276.Spi, addr & 0x7F );for( i = 0; i < size; i++ ){buffer[i] = SpiInOut( &SX1276.Spi, 0 );}//NSS = 1;GpioWrite( &SX1276.Spi.Nss, 1 );
}

我的实现:虽然返回的是uint16_t类型,但是底层还是用的uint8_t,因此SPI使用的是一个字节模式

uint16_t SpiInOut( Spi_t *obj, uint16_t outData )
{uint8_t RxData=0;if(NULL == obj){return 0;}if(NULL == obj->SpiId){return 0;}while(!LL_SPI_IsActiveFlag_TXE(obj->SpiId));LL_SPI_TransmitData8(obj->SpiId,outData);//Soft_delay_us(100);while(!LL_SPI_IsActiveFlag_TXE(obj->SpiId));while(!LL_SPI_IsActiveFlag_RXNE(obj->SpiId));RxData = LL_SPI_ReceiveData8(obj->SpiId);return RxData;
}

说说我的改动,原来的文件是传入了:

/*!* SPI peripheral ID*/
typedef enum
{SPI_1,SPI_2,
}SpiId_t;/*!* SPI object type definition*/
typedef struct Spi_s
{SpiId_t SpiId;Gpio_t Mosi;Gpio_t Miso;Gpio_t Sclk;Gpio_t Nss;
}Spi_t;

我改为了:(你们可以按着官网自己做)

/*!* 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;

然后我在SPI里面再添加一个函数:

void SpiInit(Spi_t *obj,SPI_TypeDef *tSpiX)
{if(NULL == obj || NULL == tSpiX){return;}obj->SpiId = tSpiX;
}

SPI的硬件初始化自己在main里面实现。

3)gpio这个就要实现多个函数了:
IO定义在board-config.h

由于1278只有一个输出控制开关,其它的全部删掉

#define RADIO_RESET                                 PC_6#define RADIO_NSS                                   PA_4#define RADIO_DIO_0                                 PA_8
#define RADIO_DIO_1                                 PB_11
#define RADIO_DIO_2                                 PB_10
#define RADIO_DIO_3                                 PB_2
#define RADIO_DIO_4                                 PB_1
#define RADIO_DIO_5                                 PB_0#define RADIO_ANT_SWITCH_POWER                      PA_2

有人会感到奇怪,PC_6按着以前的写法不应该是两个宏吗,一个定义端口号,一个定义IO口号,

其实你看下它的IO口定义宏就知道了:

/*!* STM32 Pin Names*/
#define MCU_PINS \PA_0 = 0, PA_1, PA_2, PA_3, PA_4, PA_5, PA_6, PA_7, PA_8, PA_9, PA_10, PA_11, PA_12, PA_13, PA_14, PA_15, \PB_0,     PB_1, PB_2, PB_3, PB_4, PB_5, PB_6, PB_7, PB_8, PB_9, PB_10, PB_11, PB_12, PB_13, PB_14, PB_15, \PC_0,     PC_1, PC_2, PC_3, PC_4, PC_5, PC_6, PC_7, PC_8, PC_9, PC_10, PC_11, PC_12, PC_13, PC_14, PC_15, \PD_0,     PD_1, PD_2, PD_3, PD_4, PD_5, PD_6, PD_7, PD_8, PD_9, PD_10, PD_11, PD_12, PD_13, PD_14, PD_15, \PE_0,     PE_1, PE_2, PE_3, PE_4, PE_5, PE_6, PE_7, PE_8, PE_9, PE_10, PE_11, PE_12, PE_13, PE_14, PE_15, \PF_0,     PF_1, PF_2, PF_3, PF_4, PF_5, PF_6, PF_7, PF_8, PF_9, PF_10, PF_11, PF_12, PF_13, PF_14, PF_15, \PH_0,     PH_1, PH_2, PH_3, PH_4, PH_5, PH_6, PH_7, PH_8, PH_9, PH_10, PH_11, PH_12, PH_13, PH_14, PH_15// SX1509 Pin Names
#define IOE_PINS \IOE_0, IOE_1, IOE_2, IOE_3, IOE_4, IOE_5, IOE_6, IOE_7, \IOE_8, IOE_9, IOE_10, IOE_11, IOE_12, IOE_13, IOE_14, IOE_15/*!* Board GPIO pin names*/
typedef enum
{MCU_PINS,IOE_PINS,// Not connectedNC = (int)0xFFFFFFFF
}PinNames;

IOE是一个驱动芯片,不管它,这里用不到。

再看实现:

void GpioInit( Gpio_t *obj, PinNames pin, uint32_t mode, uint32_t OutputType,  uint32_t pull, uint32_t Alternate, uint32_t value )
{if( pin < IOE_0 ){LL_GPIO_InitTypeDef     tGPIO_InitStruct = {0};if(NULL == obj){return;}obj->pin = pin;if(NC == obj->pin){return;}obj->pinIndex = ( 0x01 << ( obj->pin & 0x0F ) );if(obj->pinIndex > LL_GPIO_PIN_15){//errorwhile(1){TRACE_ERROR("gpio_get_struct is error \r\n");DelayMs(1000);}}if( ( obj->pin & 0xF0 ) == 0x00 ){obj->port       = GPIOA;obj->portIndex  = 0;LL_IOP_GRP1_EnableClock(LL_IOP_GRP1_PERIPH_GPIOA);}else if( ( obj->pin & 0xF0 ) == 0x10 ){obj->port       = GPIOB;obj->portIndex  = 1;LL_IOP_GRP1_EnableClock(LL_IOP_GRP1_PERIPH_GPIOB);}else if( ( obj->pin & 0xF0 ) == 0x20 ){obj->port       = GPIOC;obj->portIndex  = 2;LL_IOP_GRP1_EnableClock(LL_IOP_GRP1_PERIPH_GPIOC);}else if( ( obj->pin & 0xF0 ) == 0x30 ){obj->port       = GPIOD;obj->portIndex  = 3;LL_IOP_GRP1_EnableClock(LL_IOP_GRP1_PERIPH_GPIOD);}else{//errorwhile(1){TRACE_ERROR("gpio_get_struct is error \r\n");DelayMs(1000);}}tGPIO_InitStruct.Pin        = obj->pinIndex;tGPIO_InitStruct.Mode       = mode;tGPIO_InitStruct.Speed      = LL_GPIO_SPEED_FREQ_HIGH;tGPIO_InitStruct.OutputType = OutputType;tGPIO_InitStruct.Pull       = pull;tGPIO_InitStruct.Alternate  = Alternate;LL_GPIO_Init(obj->port,&tGPIO_InitStruct);if(LL_GPIO_MODE_OUTPUT == mode){GpioWrite( obj, value );}}else{
#if defined( BOARD_IOE_EXT )// IOExt PinGpioIoeInit( obj, pin, mode, config, type, value );
#endif}
}

是不是很简单,具体算法如下:

PinNames & 0x0F得到低四位,范围为0~15,然后1<<(0~15)就得到了PIN的序号,看MCU头文件定义:

#define LL_GPIO_PIN_8                      GPIO_BSRR_BS8 /*!< Select pin 8 */

#define GPIO_BSRR_BS8_Pos              (8U)
#define GPIO_BSRR_BS8_Msk              (0x1UL << GPIO_BSRR_BS8_Pos)             /*!< 0x00000100 */
#define GPIO_BSRR_BS8                  GPIO_BSRR_BS8_Msk

PinNames & 0xF0得到高四位,0x10对应A,0x20对应B等等。

//实现IO口中断设置(同时设置static Gpio_t *GpioIrq[16];)

void GpioSetInterrupt( Gpio_t *obj, uint8_t irqMode,uint8_t Trigger, IrqPriorities irqPriority, GpioIrqHandler *irqHandler )

//禁止IO口中断

void GpioRemoveInterrupt( Gpio_t *obj )

//IO口中断函数,根据输入的PIN,去执行相应函数,对应static Gpio_t *GpioIrq[16];

void gpio_exit_irq(uint32_t gpioPin);

//设置IO输出电平

void GpioWrite( Gpio_t *obj, uint32_t value )

//IO口取反(未使用)

void GpioToggle( Gpio_t *obj )

//读IO口值

uint32_t GpioRead( Gpio_t *obj )

以上函数全部需要实现,有点复杂,那是因为它做的是通用型驱动,兼容STM32的16个IO口。

有两种简单方案:

(1)你可以只设置连接到DIO0的PIN中断(驱动里面用上升沿中断,当然配置Lora中断取反,也可以设置为下降沿);

(2)可以不用IO口中断,用查询方式,这样IO口中断可以不用做,实现如下:

在sx1278.c添加函数,然后赋值给sx1276-board.c的倒数第三个参数:

IrqProcess函数需要实现如下功能:

void IrqProcess( void )
{if(DHIO是否是高电平){SX1276OnDio0Irq(NULL);}
}

(其它IO口中断函数,类似;但是经过测试,DIO1~DIO5未使用)

4)sx1276-board.c修改

这个文件和具体平台有关,其它函数根据自己的平台自己移植,1278的芯片下面三个函数这样改:

SX1276AntSwInit()和SX1276AntSwDeInit()保留空函数,SX1276SetAntSw()函数是控制天线的switch开关。

void SX1276AntSwInit( void )
{//GpioInit( &AntSwitchRx, RADIO_ANT_SWITCH_RX, PIN_OUTPUT, PIN_PUSH_PULL, PIN_NO_PULL, 0 );//GpioInit( &AntSwitchRx, RADIO_ANT_SWITCH_POWER, LL_GPIO_MODE_OUTPUT,LL_GPIO_OUTPUT_PUSHPULL, LL_GPIO_PULL_UP,LL_GPIO_AF_1,  0 );//GpioInit( &AntSwitchTxBoost, RADIO_ANT_SWITCH_TX_BOOST, PIN_OUTPUT, PIN_PUSH_PULL, PIN_NO_PULL, 0 );//GpioInit( &AntSwitchTxRfo, RADIO_ANT_SWITCH_TX_RFO, PIN_OUTPUT, PIN_PUSH_PULL, PIN_NO_PULL, 0 );
}void SX1276AntSwDeInit( void )
{//GpioInit( &AntSwitchRx, RADIO_ANT_SWITCH_RX, PIN_ANALOGIC, PIN_OPEN_DRAIN, PIN_NO_PULL, 0 );//GpioInit( &AntSwitchRx, RADIO_ANT_SWITCH_POWER, LL_GPIO_MODE_ANALOG,LL_GPIO_OUTPUT_OPENDRAIN, LL_GPIO_PULL_NO,LL_GPIO_AF_1,  0 );//GpioInit( &AntSwitchTxBoost, RADIO_ANT_SWITCH_TX_BOOST, PIN_ANALOGIC, PIN_OPEN_DRAIN, PIN_NO_PULL, 0 );//GpioInit( &AntSwitchTxRfo, RADIO_ANT_SWITCH_TX_RFO, PIN_ANALOGIC, PIN_OPEN_DRAIN, PIN_NO_PULL, 0 );
}void SX1276SetAntSw( uint8_t opMode )
{uint8_t paConfig =  SX1276Read( REG_PACONFIG );switch( opMode ){case RFLR_OPMODE_TRANSMITTER:
//        if( ( paConfig & RF_PACONFIG_PASELECT_PABOOST ) == RF_PACONFIG_PASELECT_PABOOST )
//        {
//            GpioWrite( &AntSwitchTxBoost, 1 );
//        }
//        else
//        {
//            GpioWrite( &AntSwitchTxRfo, 1 );
//        }GpioWrite( &AntSwitchRx, 0 );break;case RFLR_OPMODE_RECEIVER:case RFLR_OPMODE_RECEIVER_SINGLE:case RFLR_OPMODE_CAD:default:GpioWrite( &AntSwitchRx, 1 );break;}
}

还有一个最重要的一点就是对radio抽象接口的实例化:

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,NULL, // void ( *IrqProcess )( void )NULL, // void ( *RxBoosted )( uint32_t timeout ) - SX126x OnlyNULL, // void ( *SetRxDutyCycle )( uint32_t rxTime, uint32_t sleepTime ) - SX126x Only
};

5)timer.c

由于我不用休眠低功耗模式,这个文件里面所有的函数全部为空函数即可(回头会讲解低功耗模式下的驱动移植)。

6)上层调用(参考LoRaMac-node-develop\src\apps\ping-pong\NucleoL476\main.c )

用户首先实现5个函数:

void OnTxDone( void )
{SET_EVENT(&tRfTxDone);
}void OnRxDone( uint8_t *payload, uint16_t size, int16_t rssi, int8_t snr )
{//Radio.Sleep( );BufferSize = size;memcpy( Buffer, payload, BufferSize );RssiValue = rssi;SnrValue = snr;SET_EVENT(&tRfRxDone);
}void OnTxTimeout( void )
{//Radio.Sleep( );
}void OnRxTimeout( void )
{//Radio.Sleep( );
}void OnRxError( void )
{//Radio.Sleep( );
}

只需要实现OnTxDone和OnRxDone即可,其中OnTxDone告知程序发送完成(如果用户没有

实现超时功能,这和OnTxTimeout一样空函数即可),OnRxDone主要是接收底层数据,并告知

应用程序,现有有数据需要处理。

初始化:

    // Radio initializationRadioEvents.TxDone = OnTxDone;RadioEvents.RxDone = OnRxDone;RadioEvents.TxTimeout = OnTxTimeout;RadioEvents.RxTimeout = OnRxTimeout;RadioEvents.RxError = OnRxError;Radio.Init( &RadioEvents );Radio.SetChannel( RF_FREQUENCY );#if defined( USE_MODEM_LORA )Radio.SetTxConfig( MODEM_LORA, TX_OUTPUT_POWER, 0, LORA_BANDWIDTH,LORA_SPREADING_FACTOR, LORA_CODINGRATE,LORA_PREAMBLE_LENGTH, LORA_FIX_LENGTH_PAYLOAD_ON,true, 0, 0, LORA_IQ_INVERSION_ON, 3000 );Radio.SetRxConfig( MODEM_LORA, LORA_BANDWIDTH, LORA_SPREADING_FACTOR,LORA_CODINGRATE, 0, LORA_PREAMBLE_LENGTH,LORA_SYMBOL_TIMEOUT, LORA_FIX_LENGTH_PAYLOAD_ON,0, true, 0, 0, LORA_IQ_INVERSION_ON, true );#elif defined( USE_MODEM_FSK )Radio.SetTxConfig( MODEM_FSK, TX_OUTPUT_POWER, FSK_FDEV, 0,FSK_DATARATE, 0,FSK_PREAMBLE_LENGTH, FSK_FIX_LENGTH_PAYLOAD_ON,true, 0, 0, 0, 3000 );Radio.SetRxConfig( MODEM_FSK, FSK_BANDWIDTH, FSK_DATARATE,0, FSK_AFC_BANDWIDTH, FSK_PREAMBLE_LENGTH,0, FSK_FIX_LENGTH_PAYLOAD_ON, 0, true,0, 0,false, true );#else#error "Please define a frequency band in the compiler options."
#endif

设置为接收:

Radio.Rx( RX_TIMEOUT_VALUE );接收完成后,如果是连续接收模式,不需要再次设置Radio.Rx( RX_TIMEOUT_VALUE )

如果要发送数据:

Radio.Send( Buffer, BufferSize );发送完成记得Radio.Rx( RX_TIMEOUT_VALUE );可以写到OnTxDone里面

至此,新驱动可以简单的收到了。

三、sx1276.c讲解

这个主要是sx1278的驱动文件,它有SX1276OnDio0Irq~SX1276OnDio5Irq分别对应DIO0~DIO5留个IO口,

由于IO口是电平变化,即低变高(可以配置为高变低),软件复位电平,因此我们可以采用中断或者查询模式

(因为不是脉冲方式,脉冲的话,只能用中断方式),新驱动是按着中断方式写的。

内部有三个超时事件:
/*!
 * 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 );

void SX1276OnTimeoutIrq( void* context )
{switch( SX1276.Settings.State ){case RF_RX_RUNNING:if( SX1276.Settings.Modem == MODEM_FSK ){SX1276.Settings.FskPacketHandler.PreambleDetected = false;SX1276.Settings.FskPacketHandler.SyncWordDetected = false;SX1276.Settings.FskPacketHandler.NbBytes = 0;SX1276.Settings.FskPacketHandler.Size = 0;// Clear IrqsSX1276Write( REG_IRQFLAGS1, RF_IRQFLAGS1_RSSI |RF_IRQFLAGS1_PREAMBLEDETECT |RF_IRQFLAGS1_SYNCADDRESSMATCH );SX1276Write( REG_IRQFLAGS2, RF_IRQFLAGS2_FIFOOVERRUN );if( SX1276.Settings.Fsk.RxContinuous == true ){// Continuous mode restart Rx chainSX1276Write( REG_RXCONFIG, SX1276Read( REG_RXCONFIG ) | RF_RXCONFIG_RESTARTRXWITHOUTPLLLOCK );TimerStart( &RxTimeoutSyncWord );}else{SX1276.Settings.State = RF_IDLE;TimerStop( &RxTimeoutSyncWord );}}if( ( RadioEvents != NULL ) && ( RadioEvents->RxTimeout != NULL ) ){RadioEvents->RxTimeout( );}break;case RF_TX_RUNNING:// Tx timeout shouldn't happen.// Reported issue of SPI data corruption resulting in TX TIMEOUT // is NOT related to a bug in radio transceiver.// It is mainly caused by improper PCB routing of SPI lines and/or// violation of SPI specifications.// To mitigate redesign, Semtech offers a workaround which resets// the radio transceiver and putting it into a known state.// BEGIN WORKAROUND// Reset the radioSX1276Reset( );// Calibrate Rx chainRxChainCalibration( );// Initialize radio default valuesSX1276SetOpMode( RF_OPMODE_SLEEP );for( uint8_t i = 0; i < sizeof( RadioRegsInit ) / sizeof( RadioRegisters_t ); i++ ){SX1276SetModem( RadioRegsInit[i].Modem );SX1276Write( RadioRegsInit[i].Addr, RadioRegsInit[i].Value );}SX1276SetModem( MODEM_FSK );// Restore previous network type setting.SX1276SetPublicNetwork( SX1276.Settings.LoRa.PublicNetwork );// END WORKAROUNDSX1276.Settings.State = RF_IDLE;if( ( RadioEvents != NULL ) && ( RadioEvents->TxTimeout != NULL ) ){RadioEvents->TxTimeout( );}break;default:break;}
}

Lora模式没有接收同步字超时,在接收超时时候,底层仅仅是调用了上层的OnRxTimeout()。

发送超时之后,对芯片进行了一系列初始化操作后,调用上层的OnTxTimeout(),注意这里把

芯片初始化为sleep模式了。(由于没有实现timer,因此这个函数没有调用)

关于sx1276的其他的函数,用户自己去看实现就好,不再讲解。

四、注意事项

1、操作flash,导致HardFault_Handler

原因是初始化时候操作了未初始化的指针。

2、Lora不能连续接收

通过示波器查看DIO0波形,发现接收完成后,并没有使IO口变低,一直维持高电平,导致

下个接收中断不能产生。

查看官方代码有清除IRQ标志:

// Clear Irq
                    SX1276Write( REG_LR_IRQFLAGS, RFLR_IRQFLAGS_RXDONE );

打印Log发现,执行这句,表示位还是1,所以我怀疑SPI驱动,但是读没有问题,查看Lora的SPI时序

查看sx1276.c文件:

void SX1276WriteBuffer( uint16_t addr, uint8_t *buffer, uint8_t size )
{uint8_t i;//NSS = 0;GpioWrite( &SX1276.Spi.Nss, 0 );SpiInOut( &SX1276.Spi, addr | 0x80 );for( i = 0; i < size; i++ ){SpiInOut( &SX1276.Spi, buffer[i] );}//NSS = 1;GpioWrite( &SX1276.Spi.Nss, 1 );
}void SX1276ReadBuffer( uint16_t addr, uint8_t *buffer, uint8_t size )
{uint8_t i;//NSS = 0;GpioWrite( &SX1276.Spi.Nss, 0 );SpiInOut( &SX1276.Spi, addr & 0x7F );for( i = 0; i < size; i++ ){buffer[i] = SpiInOut( &SX1276.Spi, 0 );}//NSS = 1;GpioWrite( &SX1276.Spi.Nss, 1 );
}

修改为:


void SX1276WriteBuffer( uint16_t addr, uint8_t *buffer, uint8_t size )
{uint8_t i;//NSS = 0;GpioWrite( &SX1276.Spi.Nss, 0 );Soft_delay_us(10);SpiInOut( &SX1276.Spi, addr | 0x80 );for( i = 0; i < size; i++ ){SpiInOut( &SX1276.Spi, buffer[i] );}//NSS = 1;Soft_delay_us(5);GpioWrite( &SX1276.Spi.Nss, 1 );
}void SX1276ReadBuffer( uint16_t addr, uint8_t *buffer, uint8_t size )
{uint8_t i;//NSS = 0;GpioWrite( &SX1276.Spi.Nss, 0 );Soft_delay_us(5);SpiInOut( &SX1276.Spi, addr & 0x7F );for( i = 0; i < size; i++ ){buffer[i] = SpiInOut( &SX1276.Spi, 0 );}//NSS = 1;Soft_delay_us(5);GpioWrite( &SX1276.Spi.Nss, 1 );
}

一切正常。

Lora1278驱动V4.4.2讲解一:驱动移植相关推荐

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

    最近有个项目,要做微型网关,对于尺寸.体积.功耗.成本以及开发周期有要求,方案基于以前的Lora网关为基础进行快速研发,里面唯一的难点就是用一个MCU驱动多个SX1278,通过对比SX1278的sx1 ...

  2. S3C2440上LCD驱动(FrameBuffer)实例开发讲解

    一.开发环境 主  机:VMWare--Fedora 9  开发板:Mini2440--64MB Nand, Kernel:2.6.30.4  编译器:arm-linux-gcc-4.3.2 二.背景 ...

  3. S3C2440上LCD驱动 (FrameBuffer)实例开发讲解

    1.S3C2440上LCD驱动 (FrameBuffer)实例开发讲解 其中的代码也可直接参考:drivers/video/s3c2410fb.c 以下为转载文章,文章原地址:http://blog. ...

  4. mtp usb驱动 v4.9 最新版

    软件大小:1.55MB 软件语言:简体中文 软件类别:硬件驱动 软件授权:免费版 应用平台:/Win8/Win7/WinXP mtpusb驱动MTP是传输媒体文件的协议,主要用来方便快捷的传输移动存储 ...

  5. fast无线网卡linux驱动,fast无线网卡驱动,详细教您fast无线网卡驱动

    当我们使用笔记本电脑工作的时候,如果发现没有无线网络,有些用户会选择使用无线网卡,有的用户会使用fast无线网卡来上网,但是有用户不知道怎么都无法上网,这个时候该怎么办呢?这多半是因为没有安装fast ...

  6. win10 android驱动问题,WIN10 64位 android驱动无法安装

    何如安装小米驱动1首先先下载小米驱动,肯定要去官方网站去下载.登录小米官网,选择服务--下载页面--手机驱动. 2驱动有两种驱动:1.MIUI V4或者V5 2.MIUI 2.3版本 3点击后选择下载 ...

  7. 嵌入式linux驱动开发实战教程,嵌入式Linux驱动开发实战视频教程

    嵌入式Linux驱动开发实战教程(内核驱动.看门狗技术.触摸屏.视频采集系统) 适合人群:高级 课时数量:109课时 用到技术:嵌入式 Linux 涉及项目:驱动开发.看门狗技术.触摸屏.视频采集 咨 ...

  8. 台式计算机用什么网卡,台式机怎样安装网卡驱动,教您电脑安装网卡驱动

    电脑存在的第一大意义是什么?没错,就是上网,如果电脑没有了网络了,我们该怎么去解决呢?首先得去检查原因,是病毒的,就杀毒,如果是没有正确安装网卡驱动就去安装,但是出于网友对安装网卡驱动不知道如何操作, ...

  9. wifi驱动的理解(1)——驱动架构

    在分析WIFI驱动前,分享一下个人对Linux驱动的一些了解,其实纵观Linux众多的设备驱动,几乎都是以总线为载体,所有的数据传输都是基于总线形式的,即使设备没有所谓的总线接口,但是Linux还是会 ...

最新文章

  1. 北大教授吴玺宏:从发声到语言,具身物理模型让NLP回到小数据时代
  2. 转载:sql2005 管道的另一端上无任何进程解决方法
  3. C++职工信息管理系统
  4. nginx通过反向代理实现未备案域名访问详解
  5. python3调用cpp的方法——python调用so
  6. thinkphp6获取字符串中的中文首字母
  7. 自定义view之刻度尺
  8. 对求一个数的各个位数的理解
  9. SpringCLoud+redis+es高并发项目《四》
  10. 今日头条的推荐阅读你为什么都喜欢?
  11. 马化腾:通向互联网未来的七个路标
  12. 广告刷屏世界杯,联想Filez助力海信全球营销运营
  13. BCB vs. VC++
  14. 什么措施可以防止高低温试验箱生锈?
  15. 彩虹代shua网仿小储云的模板 提前备份好数据
  16. 黑客黑了自己!恶意软件开发者不小心感染了自己的 PC,致数据泄漏
  17. python删除列索引_python中pandas.DataFrame的简单操作方法(创建、索引、增添与删除)...
  18. ESP8266-Arduino编程实例-TSL2561亮度传感器驱动
  19. 2021-10-19 [可视化-经验]如何研读一篇可视化分析论文✈
  20. C++的XML编程经验――LIBXML2库使用指南

热门文章

  1. 基于apache搭建文件下载服务器
  2. 仿掘金社区全栈项目开发(二)-前端工程化
  3. 查看服务器GPU型号
  4. 高德地图不显示定位点
  5. 内推面试成功概率大吗?10年招聘HR告诉你关于内推的3个真相
  6. [Unity存档系统]简单介绍Unity常见存档系统二JSON以及使用方法
  7. 数据分析中常用分析思路对比分析解析(一)
  8. 实时车道线检测和智能告警 | 车距 + 弯道 + 车道线
  9. java全栈工程师前景,已整理成文档
  10. 与语音信号处理有关的java书籍_MATLAB在语音信号分析与合成中的应用 中文pdf扫描版[198MB]...