前言

最近在学习和摸索LoRa SX1278无线发射模块,其中学到了很多新知识和对SX1278也有了深一点的认识,现在将学习开发中遇到的问题、解决方法、调试完成和低功耗等内容分享出来,也是一种学习记录,方便日后有迹可循,再次学习。
本篇文章可能会比较粗暴一点,直接把需要注意的地方重点介绍一下,可能没有其他文章把每一个小知识点,专用名字都说的那么详尽,怕最后你看完了也不知道哪个是重点了;话不多说,直接来。

准备工具

1、SX1278芯片或者模块
2、主控EFM32(STM32、STM8均可,因为我使用模拟SPI,所以对芯片没要求,有GPIO口就可以)

专有名词

FHSS: 跳频扩频技术 ;
FIFO: 先进先出队列,这里代表队列寄存器

PA :功率放大器 ;
LNA :低噪声放大器;
SNR :信噪比 ;
SF :扩频因子 ;
PLL :锁相环 ;
CAD : 信道活动检测;
CR :编码率 ;
BW : 带宽 ;
RS :符号速率 ;
Preamble :序头;
其中SF 扩频因子是重点
因为扩频因子越大,传播时间越长。带宽低于62.5K时用TCXO做参考时钟源。在睡眠模式下通过配置寄存器RegOpMode 将FSK调制解调器切换成LoRa调制解调器。
在SX1276_LoRa.c文件中有相关参数的配置

tLoRaSettings LoRaSettings =
{470000000,        //434000000,        // RFFrequency                                                          //中心频率20,               // Power                                                                                    //发射功率9,                // SignalBw [0: 7.8kHz, 1: 10.4 kHz, 2: 15.6 kHz, 3: 20.8 kHz, 4: 31.2 kHz,                 //信号带宽// 5: 41.6 kHz, 6: 62.5 kHz, 7: 125 kHz, 8: 250 kHz, 9: 500 kHz, other: Reserved]12,                // SpreadingFactor [6: 64, 7: 128, 8: 256, 9: 512, 10: 1024, 11: 2048, 12: 4096  chips]    //扩频因子2,                // ErrorCoding [1: 4/5, 2: 4/6, 3: 4/7, 4: 4/8]                                             //编码率true,             // CrcOn [0: OFF, 1: ON]                                                                    //CRC校验开始false,            // ImplicitHeaderOn [0: OFF, 1: ON]                                                        //设置隐式报文包1,                // RxSingleOn [0: Continuous, 1 Single]                                                    //设置连续接收模式还是单一接收模式0,                // FreqHopOn [0: OFF, 1: ON]                                                               //关闭调频模式4,                // HopPeriod Hops every frequency hopping period symbols1000,              // TxPacketTimeout                                                                        //发送超时时间1000,              // RxPacketTimeout                                                                        //接收超时时间128,              // PayloadLength (used for implicit header mode)                                           //设置负载长度
};

注意:LoRaSettings结构体里面的配置根据你的需要修改,一般修改扩频因子,频率和发射功率等。

移植代码流程

1、首先我们需要有一个整体思路:
主机发送“PING”字符给从机,从机接收到来自主机的“PING”会回复主机一个“PONG”,如果主机没有收到从机的“PONG”,会进入发送超时,然后主机重新发送“PING”,整个流程就是这样循环下去。

通信的准备:

1、SPI初始化(重中之重

1.1、可以使用硬件SPI,也可以使用软件SPI;示例代码就是提供硬件SPI,移植性很差,换了芯片就需要更改读写函数,比较麻烦,所以我使用了软件SPI,容易移植。
1.2、网上下载一份SX1278的芯片说明文档、找到关于SPI通信的部分的时序图,如下图:

如果你不知道模块的SPI通信是什么极性,可以参考一下下面这篇文章的介绍。
https://blog.csdn.net/zwj695535100/article/details/107303648/
1.3、修改SPI读写函数
SX1278是四线的SPI,但是我读写整合成一个函数:

uint8_t SOFT_SPI_RW_MODE0( uint8_t write_dat )
{uint8_t i,temp = 0;
//    SPI_SCK_LOW;     //先将时钟线拉低for(i=0;i<8;i++){   SCLK_L;     //先将时钟线拉低if((write_dat&0x80)== 0x80)  //从高位发送{MOSI_H;}else{MOSI_L;}SCLK_H;  //将时钟线拉高,在时钟上升沿,数据发送到从设备write_dat<<=1;temp<<=1;if(MISO_R)   //读取从设备发射的数据{//write_dat|=0x01; temp++;}SCLK_L;     //在下降沿数据被读取到主机}return temp;         //返回读取到的数据
}

1.4、修改sx1276_Hal.c文件的读写buffer函数:
写函数

void SX1276WriteBuffer( uint8_t addr, uint8_t *buffer, uint8_t size )
{addr = addr | 0x80;CS_L;//NSS = 0;SOFT_SPI_RW_MODE0(addr);
//  spiNByteSend(buffer,size);for (uint8_t i = 0; i < size; i++) {SOFT_SPI_RW_MODE0(buffer[i]);}CS_H; //NSS = 1;
}

读函数

void SX1276ReadBuffer( uint8_t addr, uint8_t *buffer, uint8_t size )
{addr = addr & 0x7F;CS_L;//NSS = 0;SOFT_SPI_RW_MODE0(addr);for (uint8_t i = 0; i < size; i++) {buffer[i] = SOFT_SPI_RW_MODE0(0xff);}
//   spiNByteSend(buffer,size);CS_H; //NSS = 1;
}

注意:
1、CS片选管脚记得修改成你自己选择的GPIO口。
2、SPI初始化管脚这些太基础的就不说了,但是要注意配置输入和输出的关系。

这样我们的SPI初始化算完成了,这一步是最基础的,如果这一步出错后面就玩不下去了。

2、SX1278的初始化
   Radio = RadioDriverInit();Radio->Init();

2.1、RadioDriverInit();
此函数是用来芯片选型的,在官方例程或者网上其他地方下载的例程中,SX1278、SX1276、SX1272、SX1232都是通过此函数对选好的芯片类型进行初始化。
SX1278和SX1276是使用同一个配置!
2.2、Radio->Init();
此函数是对SX1278所有的配置参数进行配置,会调用SX1276Init()函数,如下:

void SX1276Init( void )
{// static uint8_t spi_test=0;
// Initialize FSK and LoRa registers structureSX1276 = ( tSX1276* )SX1276Regs;SX1276LR = ( tSX1276LR* )SX1276Regs;
//    memset(SX1276,(int)0,sizeof(tSX1276));
//    memset(SX1276LR,(int)0,sizeof(tSX1276LR));SX1276Reset( );SX1276LoRaSetOpMode(RFLR_OPMODE_STANDBY);
// GetOpMode_test = SX1276LoRaGetOpMode();
// REMARK: After radio reset the default modem is FSK
#if ( LORA == 0 ) LoRaOn = false;SX1276SetLoRaOn( LoRaOn );// Initialize FSK modemSX1276FskInit( );
#elseLoRaOn = true;LoRaOnState = false;SX1276SetLoRaOn( LoRaOn );// Initialize LoRa modemSX1276LoRaInit( );
#endif// SX1276LoRaSetPreambleLength(100);
}

2.2.1、SX1276LR = ( tSX1276LR* )SX1276Regs;
这个SX1276Regs是SX1278初始化正常接收返回参数存放的数组,SPI通信正常,它里面存放的码流应该如下:

2.2.2、复位函数:SX1276Reset( );

void SX1276Reset( void )
{uint32_t startTick;SX1276SetReset( RADIO_RESET_ON );// Wait 1msstartTick = GET_TICK_COUNT( );while( ( GET_TICK_COUNT( ) - startTick ) < TICK_RATE_MS(1) );    SX1276SetReset( RADIO_RESET_OFF );// Wait 6msstartTick = GET_TICK_COUNT( );while( ( GET_TICK_COUNT( ) - startTick ) < TICK_RATE_MS(6) );
}//嘀嗒定时器修改成你当前使用芯片的
#define GET_TICK_COUNT( )                           ( TickCounter )
void SysTick_Handler(void)
{TickCounter++;msTicks++;
}

注意:复位管脚需要修改和你对应的管脚,不然程序会在SX1276Reset跑死了。
2.2.3、映射函数
SX1276SetLoRaOn( LoRaOn )和SX1276LoRaInit( )函数
这两个函数一般不需要改动,里面都是Lora的一些基础参数的配置初始化,但是需要说一下IO口映射函数
在SX1276SetLoRaOn和sx1276_LoRa.c文件中的SX1276LoRaProcess函数都有映射函数。

                            // RxDone                    RxTimeout                   FhssChangeChannel           CadDone
SX1276LR->RegDioMapping1 = RFLR_DIOMAPPING1_DIO0_00 | RFLR_DIOMAPPING1_DIO1_00 | RFLR_DIOMAPPING1_DIO2_00 | RFLR_DIOMAPPING1_DIO3_00;// PllLock              ModeReady
SX1276LR->RegDioMapping2 = RFLR_DIOMAPPING2_DIO4_01 | RFLR_DIOMAPPING2_DIO5_00;
SX1276WriteBuffer( REG_LR_DIOMAPPING1, &SX1276LR->RegDioMapping1, 2 );                 //映射到对应的IO口,检测相应的IO口是否收发完成。

映射到IO口主要的作用是在SX1276LoRaProcess()函数中,当状态 RFLRState = RFLR_STATE_RX_RUNNING的时候,需要读取IO口是否有收到数据,才可以进入下一步,否则进入超时接收,会进入再次发送的流程,循环往复。
DIO可以设置不同的状态,在SX1278上有说明:

初始化的大致流程就这样差不多了,还有一些细节的问题就需要去需要再摸索一下,后续有更多更深的理解我会再写文章总结。

3、主函数
int main(void)
{HW_Int();//MCU初始化Radio = RadioDriverInit();Radio->Init();if(EnableMaster == true){TXBuffer[0] = 'P';TXBuffer[1] = 'I';TXBuffer[2] = 'N';TXBuffer[3] = 'G';crc_value=RadioComputeCRC(TXBuffer,4,CRC_TYPE_IBM);//计算得出要发送数据包CRC值TXBuffer[4]=crc_value>>8;TXBuffer[5]=crc_value;Radio->SetTxPacket(TXBuffer,6);//打包要发送的数据包}else{Radio->StartRx();}while(1){ if(EnableMaster == true){  OnMaster();}    else{OnSlave();}}
}

主函数开始就是把你用到的初始化好了,然后根据你是选择主机还是从机进入对应的函数,定义主机或者从机函数在文件头部;

bool  EnableMaster = true;//主从选择 true为主 false 为从

主机函数

/** Manages the master operation*/
void OnMaster( void )
{  switch(Radio->Process()){case RF_TX_DONE:Radio->StartRx( ); break;case RF_RX_DONE:Radio->GetRxPacket( RXBuffer, ( uint16_t* )&num_rx );if(num_rx > 0){crc_value=RXBuffer[num_rx-2];crc_value<<=8;crc_value|=RXBuffer[num_rx-1];if(crc_value==RadioComputeCRC(RXBuffer,num_rx-2,CRC_TYPE_IBM))//CRC check{if(strncmp(( const char* )RXBuffer, ( const char* )PongMsg,4)==0){LedToggle();//LED闪烁// Send the next PING frame            TXBuffer[0] = 'P';TXBuffer[1] = 'I';TXBuffer[2] = 'N';TXBuffer[3] = 'G';crc_value=RadioComputeCRC(TXBuffer,4,CRC_TYPE_IBM);//计算得出要发送数据包CRC值TXBuffer[4]=crc_value>>8;TXBuffer[5]=crc_value;Radio->SetTxPacket( TXBuffer, 6);    }}}            break;case RF_RX_TIMEOUT:// Send the next PING frameTXBuffer[0] = 'P';TXBuffer[1] = 'I';TXBuffer[2] = 'N';TXBuffer[3] = 'G';crc_value = RadioComputeCRC(TXBuffer,4,CRC_TYPE_IBM);//计算得出要发送数据包CRC值TXBuffer[4] = crc_value>>8;TXBuffer[5] = crc_value;Radio->SetTxPacket( TXBuffer, 6);break;default:break;}
}

运行主机函数,他会跑进去sx1276_LoRa.c文件中的SX1276LoRaProcess函数读取SX1278的状态机,根据当前的状态来进行对应的操作;它的包含的状态都在下面的结构体中:

typedef enum
{RFLR_STATE_IDLE,RFLR_STATE_RX_INIT,RFLR_STATE_RX_RUNNING,RFLR_STATE_RX_DONE,RFLR_STATE_RX_TIMEOUT,RFLR_STATE_TX_INIT,RFLR_STATE_TX_RUNNING,RFLR_STATE_TX_DONE,RFLR_STATE_TX_TIMEOUT,RFLR_STATE_CAD_INIT,RFLR_STATE_CAD_RUNNING,
}tRFLRStates;

在SX1276LoRaProcess函数中,一般都不需要修改,只需要注意一个地方,就是下面两个:
如果你是主机需要注意

 case RFLR_STATE_TX_RUNNING:if( DIO0 == 1 ) // TxDone{// Clear IrqSX1276Write( REG_LR_IRQFLAGS, RFLR_IRQFLAGS_TXDONE  );RFLRState = RFLR_STATE_TX_DONE;   }if( DIO2 == 1 ) // FHSS Changed Channel {if( LoRaSettings.FreqHopOn == true ){SX1276Read( REG_LR_HOPCHANNEL, &SX1276LR->RegHopChannel );SX1276LoRaSetRFFrequency( HoppingFrequencies[SX1276LR->RegHopChannel & RFLR_HOPCHANNEL_CHANNEL_MASK] );}// Clear IrqSX1276Write( REG_LR_IRQFLAGS, RFLR_IRQFLAGS_FHSSCHANGEDCHANNEL );}break;

如果你是从机需要注意

 case RFLR_STATE_RX_RUNNING:if( DIO0 == 1 ) // RxDone{RxTimeoutTimer = GET_TICK_COUNT( );if( LoRaSettings.FreqHopOn == true ){SX1276Read( REG_LR_HOPCHANNEL, &SX1276LR->RegHopChannel );SX1276LoRaSetRFFrequency( HoppingFrequencies[SX1276LR->RegHopChannel & RFLR_HOPCHANNEL_CHANNEL_MASK] );}// Clear IrqSX1276Write( REG_LR_IRQFLAGS, RFLR_IRQFLAGS_RXDONE  );RFLRState = RFLR_STATE_RX_DONE;}if( DIO2 == 1 ) // FHSS Changed Channel{RxTimeoutTimer = GET_TICK_COUNT( );if( LoRaSettings.FreqHopOn == true ){SX1276Read( REG_LR_HOPCHANNEL, &SX1276LR->RegHopChannel );SX1276LoRaSetRFFrequency( HoppingFrequencies[SX1276LR->RegHopChannel & RFLR_HOPCHANNEL_CHANNEL_MASK] );}// Clear IrqSX1276Write( REG_LR_IRQFLAGS, RFLR_IRQFLAGS_FHSSCHANGEDCHANNEL );// DebugRxGain = SX1276LoRaReadRxGain( );}if( LoRaSettings.RxSingleOn == true ) // Rx single mode{if( ( GET_TICK_COUNT( ) - RxTimeoutTimer ) > PacketTimeout ){RFLRState = RFLR_STATE_RX_TIMEOUT;}}break;

需要注意的就是Io1、Io2这些映射口的状态,如果你没有像上面的正确配置映射口,可能函数会一直在那里进行死循环操作,就无法进入下一步,最后通信失败;我的板子因为没有接Io1和Io2的引脚,所以我不能直接读取管脚状态进行下一步,但是SX1278手册里面有说到可以读取寄存器RegIrqFlags(0x12)也可以获取到是否可以正常发送或者接收
所以我把发射和接收的函数改造了一下,如下:

//主机
uint8_t flag = 0;
case RFLR_STATE_TX_RUNNING: SX1276Read( REG_LR_IRQFLAGS, &flag);                        //读取发送完成标志位if(flag & RFLR_IRQFLAGS_TXDONE)                            //判断发送完成标志位    {   // Clear IrqSX1276Write( REG_LR_IRQFLAGS, RFLR_IRQFLAGS_TXDONE);RFLRState = RFLR_STATE_TX_DONE;}break;

注意:IO2是FHSS Changed Channel,我没用到,所以我去改写,如果需要改写也是一样的,只需要修改 if(flag & RFLR_IRQFLAGS_TXDONE) ,与上对应的寄存器就好了。

//从机
uint8_t flag = 0;
case RFLR_STATE_RX_RUNNING:SX1276Read( REG_LR_IRQFLAGS, &flag);                        //读取发送完成标志位if(flag & RFLR_IRQFLAGS_RXDONE)                            //判断发送完成标志位  {RxTimeoutTimer = GET_TICK_COUNT( );if( LoRaSettings.FreqHopOn == true ){SX1276Read( REG_LR_HOPCHANNEL, &SX1276LR->RegHopChannel );SX1276LoRaSetRFFrequency( HoppingFrequencies[SX1276LR->RegHopChannel & RFLR_HOPCHANNEL_CHANNEL_MASK] );}// Clear IrqSX1276Write( REG_LR_IRQFLAGS, RFLR_IRQFLAGS_RXDONE  );RFLRState = RFLR_STATE_RX_DONE;}if( LoRaSettings.RxSingleOn == true ) // Rx single mode{uint32_t TimeCount = GET_TICK_COUNT( ) - RxTimeoutTimer;if( ( GET_TICK_COUNT( ) - RxTimeoutTimer ) > PacketTimeout ){RFLRState = RFLR_STATE_RX_TIMEOUT;               }}break;

从机的原理也是一样的,我就不罗嗦了,还有相关的宏定义是在头文件sx1276_LoRa.h

#define RFLR_IRQFLAGS_RXTIMEOUT                     0x80
#define RFLR_IRQFLAGS_RXDONE                        0x40
#define RFLR_IRQFLAGS_PAYLOADCRCERROR               0x20
#define RFLR_IRQFLAGS_VALIDHEADER                   0x10
#define RFLR_IRQFLAGS_TXDONE                        0x08
#define RFLR_IRQFLAGS_CADDONE                       0x04
#define RFLR_IRQFLAGS_FHSSCHANGEDCHANNEL            0x02
#define RFLR_IRQFLAGS_CADDETECTED                   0x01

上面的修改完之后,通信配置就基本完成,就应该能正常通信上了,如果没有通信上,就需要检查一下其他细节的问题,或者在底下评论一起探讨。
我将通信的buffer在串口上打印出来,因为我只用一台电脑,所以我只能切换不同串口来看打印信息:

总结

好了,以上就是我最近学习SX1278的学习心得和总结,分享出来主要是对自己学习过程中遇到的问题的一个梳理,还有就是让一些刚开始接触LoRa通信的同学提供一下帮助,更重要是方便以后再用到的时候,也可以有迹可循;如果文章有什么错误的地方或者有不明白的地方,欢迎评论区留言,我们一起探讨。

代码

源代码我晚一点分享链接,我自己移植的代码涉及到公司机密问题就不贴出来了。
源代码百度网盘:
链接:https://pan.baidu.com/s/1DZEGECa2WiUH4YsNbMp4yQ
提取码:gy3u

LoRa SX1278通信代码开发学习相关推荐

  1. LoRa SX1278/76驱动原理 附代码

    LoRa SX1278/76驱动原理 附代码 原理解释 LoRa 关键参数说明 前导码: 报头: 显式报头模式: 隐式报头模式: LoRa 调制解调: 扩频因子: 编码率: 信号带宽: 代码说明 SP ...

  2. Lora通信应用开发

    概述: 1.了解Lora技术的基本知识 2.了解通信协议用途 3.掌握Lora模块SPI配置方法 4.掌握简单的Lora模块数据对传方法 5.掌握Lora通讯协议使用方法 一.什么是LoRa LoRa ...

  3. c++服务器开发学习--02--MySQL,Redis,ASIO,iocp,TrinityCore代码结构,c++对象模型

    c++服务器开发学习--02--MySQL,Redis,ASIO,iocp,TrinityCore代码结构,c++对象模型 MySQL 问题 Redis Asio iocp TrinityCore代码 ...

  4. python 蓝牙开发_基于python实现蓝牙通信代码实例

    这篇文章主要介绍了基于python实现蓝牙通信代码实例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 安装和示例 linux下安装 sudo apt ...

  5. 游戏开发学习笔记——lua脚本语言——安装、汉化与小测试(解决lua运行代码乱码问题)

    游戏开发学习笔记--lua脚本语言--安装.汉化与小测试 FOR THE SIGMA FOR THE GTINDER FOR THE ROBOMASTER 简介: Lua 是一种轻量小巧的脚本语言,用 ...

  6. 基于LORA SX1278的温度监控控制系统开发设计-硬件方案设计

    本文的目的是设计一款基于lora无线通信的温度温度采集或者接收终端.首先进行硬件部分设计,完成的结果如下图: 结构分解: 为什么要用LORA,LORA的优势和缺点是什么: LORA是semtech公司 ...

  7. 探索学习和入门使用GitHub Copilot:提升代码开发的新利器

    目录 引言 1. 什么是GitHub Copilot? 2. 入门使用GitHub Copilot 3. GitHub Copilot的基础知识 4. GitHub Copilot的应用场景 结论 在 ...

  8. 低功耗局域网通信硬件之lora——sx1262-sx1276硬件设计开发

    1.lora简介 LoRa是semtech公司开发的一种低功耗局域网无线标准,其名称"LoRa"是远距离无线电(Long Range Radio),它最大特点就是在同样的功耗条件下 ...

  9. STM32使用LORA模块通信

    目录 一.简单了解 1.模块简介 2.硬件及功能 3.传输方式 二.模块上手 1.连接 2.编写代码 usart3.h usart3.c lora.h lora.c main.c 一.简单了解 本文以 ...

最新文章

  1. WSFC 仲裁模型选择
  2. sap 给集团分配一个逻辑系统
  3. Red hat linux 下配置Java环境(jdk)
  4. #舍得Share#Flash Media Server4.5迅雷高速下载地址by lwxshow
  5. C语言程序输入两行汉字,C语言多行输入问题集锦
  6. 堆排序(基于完全二叉树)
  7. 十大排序算法之归并排序
  8. python课程设计编写电子通讯录_用Python实现简单通讯录
  9. port wifi to ICS(4.0.3)
  10. 面试被问进线程的区别
  11. 【转】Git详解之四 服务器上的Git
  12. c#事件Unity与.Net对比
  13. ubuntu安装QQ教程
  14. 高中数学怎么学好我的数学学习方法
  15. 2022蓝帽杯初赛部分wp
  16. nginx url中带中文不能访问
  17. anguarjs 上传图片预览_前端战五渣学前端——FileReader预览本地文件
  18. 曲阜水利学校计算机96级聚会,曲阜水利学校50年校庆
  19. 乳腺结节属于癌前病变吗?
  20. 如果睡眠不足,我们的大脑会怎么样?

热门文章

  1. rm -rf 命令 与正则表达式
  2. 教程篇(7.0) 06. FortiGate基础架构 单点登录(FSSO) ❀ Fortinet 网络安全专家 NSE 4
  3. 关于网易2018实习生招聘的“道路布灯”问题
  4. 小米手机显示崩溃日志
  5. html dt和dd顺序,dl dt dd使用方法
  6. [ansible系列③]Ansible Inventory配置及详解
  7. TheTechBehindDx11UnrealEngineSamaritanDemo
  8. 一段代码之仿LOL移动方式
  9. 如何正确使用手机拍摄证件照
  10. 获取股票历史数据——数据采集(1)