锐单电子商城 , 一站式电子元器件采购平台!
  • 电话:400-990-0325

Lora通信应用开发

时间:2022-12-17 05:30:00 lorawan无线温度传感器uhf射频连接器uhf

概述:

1.了解Lora技术基础知识

2.了解通信协议的用途

3.掌握Lora模块SPI配置方法

4.掌握简单Lora对传模块数据的方法

5.掌握Lora使用通信协议的方法

一、什么是LoRa

LoRa(LongRangeRadio,远距离无线电)是一种基于扩频技术的远距离无线传输技术LPWAN其中一种通信技术是Semtech公司创建的低功耗局域网无线标准。该方案为用户提供了实现远距离、低功耗无线通信的简单手段。它最大的特点是在相同的功耗条件下,它远离其他无线传输,实现了低功耗和远距离的统一。在相同的功耗下,它比传统的无线射频通信距离扩大了3~5倍。目前,LoRa主要在ISM主要包括433MHz、868MHz、915MHz等。
2.LoRa的特性
传输距离:城镇可达2~5km,郊区可达15km;
工作频率:ISM频段,包括433MHz、868MHz、915MHz等;
标准:IEEE802.15.4g;
调制方法:基于扩频技术,线性调制扩频(CSS)有前向纠错的变种(FEC)能力,是Semtech公司私有专利技术;
容量:一个LoRa网关可以连接成千上万的网关LoRa节点;
电池寿命:10年;
安全:AES128加密;
传输速率:几十到几百kbit/s,传输距离越低,速率越长。

二、Lora和LoraWAN

Lora是LoraWAN子集,Lora仅包括物理定义,LoraWAN包括链路层定义。其实LoraWAN它不是一个完整的通信协议,因为它只定义物理层和链路层,没有网络层和传输层,没有完美的功能,没有漫游,没有网络管理和其他重要的通信协议功能。

三、Lora模块

采用的LSD4RF-2F717N30是LoRaSX1278470M100mW基于标准模块Semtech射频集成芯片SX127X射频模块是高性能物联网无线收发器。

1.SX1276/77/78收发器

SX1276/77/78是137~1020MHz主要采用低功耗远距离收发器LoRa超长距离扩频通信采用远程调制解调器,抗干扰性强,能最大限度地减少电流消耗。
借助Semtech的LoRa专利调制技术,SX1276/77/78使用低成本的物体和材料可获得超过-148dBm高灵敏度。此外,高灵敏度和20dBm功率放大器的集成使这些设备的链路预算达到行业领先水平,成为远程传输和高可靠性应用的最佳选择。与传统的调制技术相比,LoRa调制技术在抗堵塞和选择性方面也具有明显的优势,解决了传统设计方案不能同时考虑距离、抗干扰和功耗的问题。
支持这些设备WM-BusIIEEE802.15.4g高性能等系统(G)FSK模型。与同类设备相比,SX在大幅降低电流消耗的基础上,1276/77/78显著优化了相位噪声、选择性、接收机线性、三级输人截取点(IIP3)等。
SX1276的带宽范围为7.8~500kHz,扩频因子为6~12,覆盖所有可用频段。SX1277段范围及1277SX1276相同,但扩频因子为6~9。SX选择1278带宽和扩频因子SX1276是一样的,但只覆盖UHF频段。
(1)关键产品特性
LoRa调制解调器;
最大链路预算可达168dB;
20dBm-100mW恒定的射频功率输出;
14dBm高效功率放大器;
可编程比率高达3000kbit/s;
高灵敏度:低至-148dBm;
高可靠性的前端:IIP3=-11dBm;
优异的阻塞特性;
9.9mA低接收电流,200nA寄存器保持电流;
分辨率为61Hz、全集成频率合成器;
支持FSK、GFSK、MSK、GMSK、LoRa及OOK调制方式:
时钟恢复采用内置同步;
前导码检测:
127dB的RSSI动态范围:
自动射频信号检测,CAD模型和超高速AFC;
带有CRC、高达256B数据包引擎;
内置温度传感器和低功率指示器。
(2)应用

远程灌溉系统;
自动抄表;
家庭和建筑自动化:
无线报警和安全系统;
工业监测与控制;

四、SPI

1.SPI简介

LoRa芯片与MCU通过SPI进行通信。SPI(SerialPeripheralInterfaceBus)由摩托罗拉公司开发的高速全双工同步串行通信协议。SPI支持一主多从,类似于IC,但是又与I2C选通从设备的方式不同,IC通过发送从机地址选择从机,SPI它通过拉低连接到从机NSS引脚选择从机。SPI一般应用由四个引脚组成(一主一从):
SCLK(SerialClock):主机发出串行时钟;
MOSI(MasterOutput,SlaveInput):主机输出从机输入信号,由主机发出;
MISO(MasterInput,SlaveOutput):主机输入从机输出信号,由从机输出:NSS:由主机发出的信号选择,一般低电位有效。

2. SPI代码配置

(1)引脚初始化

通过SpiInit()实现此函数BoardDisableIrq( )中调用。

void SpiInit( Spi_t *obj, PinNames mosi, PinNames miso, PinNames sclk, PinNames nss )参数说明:Spi_t *obj:指向待初始化SPI结构体;PinNames mosi:主机输出从机输入引脚;PinNames miso:主机输入从机输出引脚;PinNames sclk:串行时钟引脚;PinNames nss:片选引脚;

void SpiInit( Spi_t *obj, PinNames mosi, PinNames miso, PinNames sclk, PinNames nss ) {     BoardDisableIrq( );//禁止中断      // Choose SPI interface according to the given pins     if( mosi == PA_7 )     {         __HAL_RCC_SPI1_FORCE_RESET( );         __HAL_RCC_SPI1_RELEASE_RESET( );          __HAL_RCC_SPI1_CLK_ENABLE( );          obj->Spi.Instance = ( SPI_TypeDef* )SPI1_BASE;//建立SPI,也就是obj就是SPI1     //将GPIO口初始化         GpioInit( &obj->Mosi, mosi, PIN_ALTERNATE_FCT, PIN_PUSH_PULL, PIN_PULL_DOWN, GPIO_AF5_SPI1 );         GpioInit( &obj->Miso, miso, PIN_ALTERNATE_FCT, PIN_PUSH_PULL, PIN_PULL_DOWN, GPIO_AF5_SPI1 );         GpioInit( &obj->Sclk, sclk, PIN_ALTERNATE_FCT, PIN_PUSH_PULL, PIN_PULL_DOWN, GPIO_AF5_SPI1 );         GpioInit( &obj->Nss, nss, PIN_ALTERNATE_FCT, PIN_PUSH_PULL, PIN_PULL_UP, GPIO_AF5_SPI1 );          if( nss == NC )         {             obj->Spi.Init.NSS = SPI_NSS_SOFT;//NSS软件单独控制选片信号             SpiFormat( obj, SPI_DATASIZE_8BIT, SPI_POLARITY_LOW, SPI_PHASE_1EDGE, 0 );//设置SPI主机模式配置通信模式         }         else         {             SpiFormat( obj, SPI_DATASIZE_8BIT, SPI_POLARITY_LOW, SPI_PHASE_1EDGE, 1 );//设置SPI通讯方式,配置为从机模式         }     }     else if( mosi == PB_15 )     {///初始化SPI2         __HAL_RCC_SPI2_FORCE_RESET( );         __HAL_RCC_SPI2_RELEASE_RESET( );          __HAL_RCC_SPI2_CLK_ENABLE( );          obj->Spi.Instance = ( SPI_TypeDef* )SPI2_BASE;//建立SPI obj也就是SPI2     //将GPIO口初始化         GpioInit( &obj->Mosi, mosi, PIN_ALTERNATE_FCT, PIN_PUSH_PULL, PIN_PULL_DOWN, GPIO_AF5_SPI2 );         GpioInit( &obj->Miso, miso, PIN_ALTERNATE_FCT, PIN_PUSH_PULL, PIN_PULL_DOWN, GPIO_AF5_SPI2 );         GpioInit( &obj->Sclk, sclk, PIN_ALTERNATE_FCT PIN_PUSH_PULL, PIN_PULL_DOWN, GPIO_AF5_SPI2 );
        GpioInit( &obj->Nss, nss, PIN_ALTERNATE_FCT, PIN_PUSH_PULL, PIN_PULL_UP, GPIO_AF5_SPI2 );

        if( nss == NC )
        {
            obj->Spi.Init.NSS = SPI_NSS_SOFT;//NSS片选信号由软件单独控制
            SpiFormat( obj, SPI_DATASIZE_8BIT, SPI_POLARITY_LOW, SPI_PHASE_1EDGE, 0 );//设置SPI通讯方式,配置为主机模式
        }
        else
        {
            SpiFormat( obj, SPI_DATASIZE_8BIT, SPI_POLARITY_LOW, SPI_PHASE_1EDGE, 1 );//设置SPI通讯方式,配置为从机模式
        }
    }
    SpiFrequency( obj, 10000000 );//设置SPI速度

    HAL_SPI_Init( &obj->Spi );//生效

    BoardEnableIrq( );//使能中断
}

(2)设置SPI通讯方式

     SpiFormat( Spi_t *obj, int8_t bits, int8_t cpol, int8_t cpha, int8_t slave )中各参数如下:

        Spi_t *obj:SPI结构体;

        int8_t bits:帧格式,选择8位

        int8_t cpol:设置时钟极性。这里是低电平

        int8_t slave:主从模式,0主,1从

 (3)Lora调制解调

(1)频率:频率建议在433MHZ附近,也430,431,432,用户根据设置频率确定合适的信道。

(2)发射功率:Lora发射功率由参数TX_OUTPUT_POWER;这个值越大,传输的距离越远,最大值不超过20dBm。

(3)Lora数据包结构:

 3.编写关键函数 NS_Radio.c

void NS_RadioEventsInit( void )//射频初始化函数
{
    // Radio initialization
    RadioEvents.TxDone = OnTxDone;
    RadioEvents.RxDone = OnRxDone;
    RadioEvents.TxTimeout = OnTxTimeout;
    RadioEvents.RxTimeout = OnRxTimeout;
    RadioEvents.RxError = OnRxError;

    Radio.Init( &RadioEvents );
}

void OnTxDone( void )//发送完成调用此函数
{
    Radio.Sleep( );
    Radio.Rx( RX_TIMEOUT_VALUE );
}

void OnRxDone( uint8_t *payload, uint16_t size, int16_t rssi, int8_t snr )//接收完成调用,读取数据、数据长度、信号强度、信噪比。
{
    Radio.Sleep( );
    LoRaBufferSize = size;
    memcpy( LoRaBuffer, payload, LoRaBufferSize );
    RssiValue = rssi;
    SnrValue = snr;
//    printf( "Rx=%s\r\nRssiValue=%d\r\nSnrValue=%d\r\n",LoRaBuffer,RssiValue,SnrValue );
    Radio.Rx( RX_TIMEOUT_VALUE );
}

void OnTxTimeout( void )//发送超时调用
{
    Radio.Sleep( );
    Radio.Rx( RX_TIMEOUT_VALUE );
}

void OnRxTimeout( void )//接收超时调用
{
    Radio.Sleep( );
    Radio.Rx( RX_TIMEOUT_VALUE );
}

void OnRxError( void )//接收错误调用
{
    Radio.Sleep( );
    Radio.Rx( RX_TIMEOUT_VALUE );
}

项目实验

        项目介绍:方圆5平方千米的植物园,粗放式管理,管委会要求对园区环境进行检测,温湿度光照等。要求:低成本,节约经费;先实现点对点,能够在上位机进行数据获取,后期升级位云平台获取。

      (1)  关键接口函数解析:

void LoRa_Send( uint8_t *TxBuffer, uint8_t len )//TxBuffer是一个指针,指向用户需要发送的Lora无线数据首地址。

{
    Radio.Send( TxBuffer, len);
}


void MyRadioRxDoneProcess( void )//接收Lora无线数据,用户需要在函数中解析无线数据的功能代码或函数。

{
    uint16_t BufferSize = 0;
    uint8_t RxBuffer[BUFFER_SIZE];

    BufferSize = ReadRadioRxBuffer( (uint8_t *)RxBuffer );
    if(BufferSize>0)
    {
        //用户在此处添加接收数据处理功能的代码
        ;
     printf("LoRa TempRh\r\n");
    hal_temHumInit();//初始化温湿度模块
    connectionreset();//重置温湿度模块IIC通信
    Tim3McuInit(1);//定时器初始化,设置定时中断1ms中断一次
        
    }
}

void PlatformInit(void)//硬件平台初始化
{
    // 开发板平台初始化
    BoardInitMcu();
    BoardInitPeriph();

    // 开发板设备初始化
    OLED_Init();//液晶初始化
    USART1_Init(115200);//串口1初始化
    OLED_Clear();
    OLED_InitView();//OLED屏幕显示初始信息
    
    printf("新大陆教育 LoRa \r\n");

    //Lora模块初始化
    NS_RadioInit( (uint32_t) RF_PING_PONG_FREQUENCY, (int8_t) TX_OUTPUT_POWER, (uint32_t) TX_TIMEOUT_VALUE, (uint32_t) RX_TIMEOUT_VALUE );
    
    //请在下方添加用户初始化代码
    
    
    
    //IWDG_PrmInit(2048);//独立看门狗初始化,超时设置为2048ms
}

//-----------------------------------------------------------
//根据通信协议制定响应命令结构
#define START_HEAD 0x55//帧头
#define CMD_READ   0x01//读数据
#define ACK_OK     0x00//响应OK
#define ACK_NONE   0x01//无数据
#define ACK_ERR    0x02//数据错误
//定义网络编号和设备地址
#define MY_NET_ID  0xD0C2       //网络ID
#define MY_ADDR    0x01         //设备地址

/*全局变量*/
int8_t temperature = 25;    //温度,单位:℃
int8_t humidity = 60;       //湿度,单位:%

//函数声明
void LoRa_Send( uint8_t *TxBuffer, uint8_t len );
void MyRadioRxDoneProcess( void );
void OLED_InitView(void);
void PlatformInit(void);


uint8_t CheckSum(uint8_t *buf, uint8_t len)//计算校验和
{
    uint8_t temp = 0;
    while(len--)
    {
        temp += *buf;
        buf++;
    }
    return (uint8_t)temp;
}

/**********************************************************************************************
*函数:uint8_t *ExtractCmdframe(uint8_t *buf, uint8_t len, uint8_t CmdStart)
*功能:从一串数据中提取出命令帧出现起始地址
*输入:
*     uint8_t *buf,指向待提取的数据缓冲区地址
*     uint8_t len,缓冲区数据长度
*     uint8_t CmdStart,命令帧起始字节
*输出:无
*返回:返回首次出现命令帧的地址,若数据中无命令帧数据,则返回NULL
*特殊说明:无
**********************************************************************************************/
uint8_t *ExtractCmdframe(uint8_t *buf, uint8_t len, uint8_t CmdStart)
{
    uint8_t *point = NULL;
    uint8_t i;
    for(i=0; i

(2)请求命令参照数据通信结构,传感器接收网关读传感数据命令后,节点需要按照通信规约格式上报网关。请求命令解析数据代码如下:

//--------------------------------------------------------
//数据解析
void LoRa_DataParse( uint8_t *LoRaRxBuf, uint16_t len )
{
    uint8_t *DestData = NULL;
#define HEAD_DATA  *DestData     //帧头
#define CMD_DATA   *(DestData+1) //命令
#define NETH_DATA  *(DestData+2) //网络ID高字节  
#define NETL_DATA  *(DestData+3) //网络ID低字节  
#define ADDR_DATA  *(DestData+4) //地址  

#define ACK_DATA        *(DestData+5) //响应
#define LEN_DATA        *(DestData+6) //长度
#define DATASTAR_DATA   *(DestData+7) //数据域起始

    DestData = ExtractCmdframe((uint8_t *)LoRaRxBuf, len, START_HEAD);//输入数据 长度 帧给DestData
    if(DestData != NULL)//检索到数据帧头
    {
        if((DestData - LoRaRxBuf) > (len - 6)) return;//数据长度不足构成一帧完整数据
        if(CMD_DATA != CMD_READ) return;//命令错误
        if(CheckSum((uint8_t *)DestData, 5) != (*(DestData+5))) return;//校验不通过,仅适用于校验读数据命令的校验
        if(((((uint16_t)NETH_DATA)<<8)+NETL_DATA) != MY_NET_ID) return;//网络ID不一致
        //发送读响应
        if(ADDR_DATA != MY_ADDR) return;//地址不一致


/--------------------------------------------------------------------
//根据通信协议发送数据又称响应
        uint8_t RspBuf[BUFFER_SIZE]= {0};
        memset(RspBuf, '\0', BUFFER_SIZE);

        RspBuf[0]=START_HEAD;//帧头 
        RspBuf[1]=CMD_READ;//命令 ,0x01读传感数据 
        RspBuf[2]=(uint8_t)(MY_NET_ID>>8);//网络ID低字节  
        RspBuf[3]=(uint8_t)MY_NET_ID;//网络ID高字节  
        RspBuf[4]=MY_ADDR;//地址 
        RspBuf[5]=ACK_OK;//响应OK 0x00 
        sprintf((char *)(RspBuf+7),"temperature(℃):%d|humidity(%%):%d", temperature, humidity);//数据域,sprintf中,两个“%”表示输出“%”。在采集传感函数中已经将采集值放入这两个变量中,发送即可。
        RspBuf[6]=strlen((const char *)(RspBuf+7))+1;//数据域长度
        RspBuf[6+RspBuf[6]]=CheckSum((uint8_t *)RspBuf, 6+RspBuf[6]);

        Radio.Send( RspBuf, 7+RspBuf[6]);//发送响应数据
        GpioToggle( &Led1 );//发送数据切换亮灯指示
    }
}

(3)主机接收代码如下:

/**********************************************************************************************
*函数:void MyRadioRxDoneProcess( void )
*功能:无线模块数据接收完成处理进程函数
*输入:无
*输出:无
*返回:无
*特殊说明:接收到的无线数据保存在RxBuffer中,BufferSize为接收到的无线数据长度
**********************************************************************************************/
void MyRadioRxDoneProcess( void )
{
    uint16_t BufferSize = 0;
    uint8_t RxBuffer[BUFFER_SIZE];

    BufferSize = ReadRadioRxBuffer( (uint8_t *)RxBuffer );
    if(BufferSize>0)
    {
        //用户在此处添加接收数据处理功能的代码
        GpioToggle( &Led2 );//收到数据切换亮灯指示
        LoRa_DataParse( (uint8_t *)RxBuffer, BufferSize );//调用数据解析函数
    }
}

(4)主函数将硬件平台初始化,不断循环接收发送函数。

//-----------------------------获取传传感器数据------------------------
void LoRa_GetSensorDataProcess(void)
{
    const uint16_t time = 1000;
    if(User0Timer_MS > time) //1ms进中断User0Timer_MS ++;
    {
        User0Timer_MS = 0;
        uint16_t Temp, Rh;
        call_sht11((uint16_t *)(&Temp), (uint16_t *)(&Rh));//采集温湿度数据
        temperature = (int8_t)Temp;    //温度,单位:℃  将采集值传给前边定义的两个变量
        humidity = (int8_t)Rh;       //湿度,单位:%     将采集值传给前边定义的两个变量
//------------------oled显示--------------------------------------------
        char StrBuf[64]= {0};
        memset(StrBuf, '\0', 64);
        sprintf(StrBuf, " %d DegrCe",temperature);
        OLED_ShowString(0,4,(uint8_t *)StrBuf);//OLED显示当前温度
        memset(StrBuf, '\0', 64);
        sprintf(StrBuf, " %d %%",humidity);
        OLED_ShowString(0,6,(uint8_t *)StrBuf);//OLED显示当前相对湿度
    }
}


int main( void )
{
    PlatformInit();

    while( 1 )
    {
        //IWDG_PrmRefresh( );//喂独立看门狗
        MyRadioRxDoneProcess();//LoRa无线射频接收数据处理进程
        LoRa_GetSensorDataProcess();
    }
}

锐单商城拥有海量元器件数据手册IC替代型号,打造电子元器件IC百科大全!

相关文章