注意,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 connected
NC = (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){
//error
while(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
{
//error
while(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 Pin
GpioIoeInit( 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 Only
NULL, // 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 initialization
RadioEvents.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 Irqs
SX1276Write( 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 chain
SX1276Write( 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 radio
SX1276Reset( );
// Calibrate Rx chain
RxChainCalibration( );
// Initialize radio default values
SX1276SetOpMode( 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 WORKAROUND
SX1276.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 );
}
一切正常。