注意,Lora1278驱动sx12xxDrivers-V2.1.0,原厂已经不更新和维护了,反馈的任何软件问题,
原厂就是一句话升级新的驱动,新驱动下载地址:https://github.com/Lora-net/LoRaMac-node
频段划分:
射频输出:
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里面
至此,新驱动可以简单的收到了。
这个主要是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 );
}
一切正常。