智慧牧场项目笔记(LoRa开发低耗广域网)(2)

目录

LoRaPingPong系统设计

1、深入了解LoRa的技术原理

2、pingpong系统设计

3、系统功能开发

 LoRa串口透传开发

1、串口透传系统设计

1.1 串口透传系统设计需求

1.2 系统通信机制

1.3 系统业务流程

2、串口透传系统功能开发

2.1 串口功能开发

2.2 LCD功能开发

2.3 无线收发功能开发

2.4 主函数开发


LoRaPingPong系统设计

1、深入了解LoRa的技术原理

1.1 LoRa扩频通信原理(了解)

无线通信技术:  

模拟无线通信:收音机的例子  ,优点在于它的结构简单,易于实现。由于模拟信号容易受到噪声的影响,导致通信质量不高,且保密性较差。此外,模拟无线通信技术的频带利用率较低,不能满足现代社会对于高效率和高容量的通信需求。因此,随着数字技术的发展,模拟无线通信技术逐渐被数字无线通信技术所取代。关键步骤:检波、放大、功率放大;

数字无线通信:

 总结来说,将01信号转换为无线电磁波,有以下几种常见的调制方式:振幅位移键控、频率位移键控、相位位移键控。

无线通信的传播方式:简单来说就三种:(1)地波传播低于2MHZ(2)天波传播2MHZ~30MHZ(3)直线传播30MHZ以上

 其传播路径有:反射、散射、衍射。

下图是无线通信的噪声

所以出现了扩频通信技术,这种跳频技术可以有效地抗击干扰和实现加密(在各种类型的噪声和多径是真中获得免疫)

LoRa是通过增大信号的带宽来提升信号的质量。如下图,将用户数据与扩频数据异或得到发送数据,而发送数据实现了增大了带宽并提高了信号的增益(质量)

1.2 LoRa关键技术参数

(1)信号带宽BW:BW的增大,可以提高有效数据的速率以缩短时间,前提是牺牲部分接收灵敏度的代价。但对于LoRa芯片SX127x,LoRa带宽为双边带宽(全信道带宽),而FSK调制方式的BW是指单边带宽

(2)扩频因子:描述信号在传输过程中如何扩散的一种参数。在LoRa技术中,扩频因子决定了信号在传输过程中的扩频程度,即把每个信息位扩展成多个“符号”,这样做是为了提高信号的抗干扰能力和降低误码率,同时牺牲一定的传输速率。

(3)编码率:编码率(或信息率)是数据流中有用部分(非冗余)的比例。也就是说,有效数据位与传输的总数据位的比例。如果编码率是k/n,则对每k位有用信息,编码器总共产生n位的数据,其中n-k是多余的。LoRa采用循环纠错编码进行前向错误检测与纠错。使用该方式会产生传输开销。

Tip:循环纠错编码(是一种差错检测编码技术,核心思想是在发送方将数据流通过一个生成多项式进行处理,从而生成一个校验值(也称为CRC校验码或者校验位),然后将这个校验值附加到原始数据流的末尾,在接收方,同样使用这个生成多项式对这个附加了校验值的数据流进行处理,如果能够整除,则认为数据传输过程中没有发生错误;否则,就认为发生了错误)

LoRa符号速率Rs计算:
    Rs=BW/(2^SF)
LoRa数据速率DR计算:
    DR= SF*( BW/2^SF)*CR 

LoRaWAN主要使用了125kHz信号带宽设置,但其他专用协议可以利用其他的信号带宽(BW)设置。改变BW、SF和CR也就改变了链路预算和传输时间,需要在电池寿命和距离上做个权衡 

参数设置:

1.3 LoRa数据收发任务

 发送:                                                                        接收:

 

 为了实现数据的收发官方固件库提供了下图的事件处理任务:

2、pingpong系统设计

2.1 系统设计需求

将LoRa终端定义成两种角色:Master和Slave
Master主动发送PING数据,接收PONG数据
Slave如果接收到PING数据,回应PONG数据
终端在LCD屏幕上显示终端类型及收发数据包个数 

2.2 通信机制

2.3 系统业务流程       

(1)初始化

(2)Master

(3)Slave

 LoRa参数设置:

3、系统功能开发

3.1 IAR工程配置

3.2 搭建框架

(1)建立功能函数

建立四个函数:Master和Slave的LCD菜单显示信息、无线收发任务处理 

//主机显示任务
void MLCD_Task(void)
{

}
//从机显示任务
void SLCD_Task(void)
{

}
//主机无线任务
void Master_Task(void)
{

}
//从机无线任务
void Slave_Task(void)
{

}

(2)建立数据结构

声明全部变量、进行赋值初始化

#define BUFFERSIZE    4
uint16_t BufferSize = BUFFERSIZE;
uint8_t PING_Msg[] = "PING";
uint8_t PONG_Msg[] = "PONG";
uint8_t Buffer[BUFFERSIZE];

//设备类型判断
#ifdef MASTER
uint8_t EnableMaster = true;
#else
uint8_t EnableMaster = false;
#endif

//收发数据个数
uint32_t Master_Txnumber = 0;
uint32_t Master_Rxnumber = 0;

uint32_t Slave_Txnumber = 0;
uint32_t Slave_Rxnumber = 0;

//获取无线收发的数据结构指针
tRadioDriver *radio = NULL;

3.3 编码

(1)修改main函数以及将显示Master和Slave标题的函数移植到新建显示任务中

#ifdef MASTER
  Gui_DrawFont_GBK16(0, 16, BLACK, BLUE, "     Master     ");
#else
  Gui_DrawFont_GBK16(0, 16, BLACK, BLUE, "     Slave     ");
#endif

(2)完善上文新建任务函数

//主机显示任务
void MLCD_Show(void)
{
  uint8_t str[20] = {0};
  LCD_GPIO_Init();
  sprintf((char *)str, "%d", Master_Rxnumber);
  Gui_DrawFont_GBK16(45, 64, BLACK, RED, str);
  memset((char *)str, 0, strlen((char*)str));
  
  sprintf((char *)str, "%d", Master_Txnumber);
  Gui_DrawFont_GBK16(45, 80, BLACK, RED, str);
  
  HAL_SPI_DeInit(&hspi1);
  MX_SPI1_Init();
}

//从机显示任务
void SLCD_Show(void)
{
  uint8_t str[20] = {0};
  LCD_GPIO_Init();
  sprintf((char *)str, "%d", Slave_Rxnumber);
  Gui_DrawFont_GBK16(45, 64, BLACK, RED, str);
  memset((char *)str, 0, strlen((char*)str));
  
  sprintf((char *)str, "%d", Slave_Txnumber);
  Gui_DrawFont_GBK16(45, 80, BLACK, RED, str);
  
  HAL_SPI_DeInit(&hspi1);
  MX_SPI1_Init();
}

//主机无线任务
void Master_Task(void)
{
  switch(radio->Process())
  {
   case RF_TX_DONE :
     LedToggle(LED_TX);
     Master_Txnumber++;
     radio->StartRx();
     break;
   case RF_RX_DONE :
     radio->GetRxPacket(Buffer, &BufferSize);
     printf("Master_Task:RX____%s\n",Buffer);
     if(strncmp((const char*)Buffer, (const char*)PONG_Msg, strlen((const char*)PONG_Msg)) == 0)
     {
       LedToggle(LED_RX);
       Master_Rxnumber++;
       radio->SetTxPacket(PING_Msg, strlen((const char*)PING_Msg));
     }
     break;
   default :
     break;
    
  }
}

//从机无线任务
void Slave_Task(void)
{
  switch(radio->Process())
  {
   case RF_TX_DONE :
     LedToggle(LED_TX);
     Slave_Txnumber++;
     radio->StartRx();
     break;
   case RF_RX_DONE :
     radio->GetRxPacket(Buffer, &BufferSize);
     printf("Slave_Task:RX____%s\n",Buffer);
     if(strncmp((const char*)Buffer, (const char*)PING_Msg, strlen((const char*)PING_Msg)) == 0)
     {
       LedToggle(LED_RX);
       Slave_Rxnumber++;
       radio->SetTxPacket(PONG_Msg, strlen((const char*)PONG_Msg));
     }
     break;
   default :
     break;
    
  }
}

(3)main函数

int main(void)
{
  /* USER CODE BEGIN 1 */
  uint8_t RegVersion = 0;
  /* USER CODE END 1 */

  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_DMA_Init();
  MX_ADC_Init();
  MX_USART1_UART_Init();
  MX_SPI1_Init();
  /* USER CODE BEGIN 2 */
  Lcd_Init();
  
  showimage(gImage_logo);
  HAL_Delay(500);
  Lcd_Clear(RED);
  Gui_DrawFont_GBK16(0, 0, BLACK, BLUE, "  LoRa Topology  ");
#ifdef MASTER
  Gui_DrawFont_GBK16(0, 16, BLACK, BLUE, "     Master     ");
#else
  Gui_DrawFont_GBK16(0, 16, BLACK, BLUE, "     Slave     ");
#endif
  Gui_DrawFont_GBK16(2, 48, BLACK, RED, "SSID:");
  Gui_DrawFont_GBK16(45, 48, BLACK, RED, "30");
  Gui_DrawFont_GBK16(2, 64, BLACK, RED, "RX:");
  
  Gui_DrawFont_GBK16(2, 80, BLACK, RED, "TX:");
  
  //SPI重新初始化
  HAL_SPI_DeInit(&hspi1);
  MX_SPI1_Init();
  SX1276Read( REG_LR_VERSION, &RegVersion );
  if(RegVersion != 0x12)
  {
    printf("LoRa read failed!\r\n");
    printf("LoRa RegVersion:%d\r\n",RegVersion);
  }
  else
  {
    printf("LoRa read OK!\r\n");
    printf("LoRa RegVersion:%d\r\n",RegVersion);
  }
  printf("system is init\r\n");
  radio = RadioDriverInit();
  radio ->Init();
#ifdef MASTER
  radio->SetTxPacket(PING_Msg, strlen((const char*)PING_Msg));
  printf("I am Master!\r\n");
#else
  radio->StartRx();
  printf("I am Slave!\r\n");
#endif
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
    if(EnableMaster == true)
    {
      MLCD_Show();
      Master_Task();
    }
    else
    {
      SLCD_Show();
      Slave_Task();
    }
    
  }
  /* USER CODE END 3 */
}

(4)LoRa驱动源码修改

位于SX1276LoRaProcess()函数的case RFLR_STATE_RX_RUNNING的关于DIO2的判断语句以及case RFLR_STATE_TX_RUNNING的关于DIO2的判断语句不用需要注释,并且根据上图LoRa参数设置修改以下源码:

// Default settings
tLoRaSettings LoRaSettings =
{
    470000000,        // 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]
    7,                // 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]
    false,            // ImplicitHeaderOn [0: OFF, 1: ON]
    0,                // RxSingleOn [0: Continuous, 1 Single]
    0,                // FreqHopOn [0: OFF, 1: ON]
    4,                // HopPeriod Hops every frequency hopping period symbols
    1000,              // TxPacketTimeout
    1000,              // RxPacketTimeout
    4,              // PayloadLength (used for implicit header mode)
};

编译过程中遇到了以下报错

解决方法:添加sx1276Fsk.c和sx1276FskMisc.c文件到radio文件夹

 LoRa串口透传开发

1、串口透传系统设计

1.1 串口透传系统设计需求

 

将LoRa终端定义成两种角色:Master和Slave
一个模块发送任意字节长度(小于128Byte)数据,另一模块都可以接收到
PC机上通过串口调试助手实现接收和发送
终端在LCD屏幕上显示终端类型及收发数据包个数

1.2 系统通信机制

1.3 系统业务流程

(1)初始化

(2)主程序

(3)LCD任务

 

 (4)串口接收任务

(5)无线任务

2、串口透传系统功能开发

2.1 串口功能开发

(1)串口功能接口

 

/* uart.c文件USART1 中断初始化 */
    HAL_NVIC_SetPriority(USART1_IRQn, 0, 0);//设置中断优先级
    HAL_NVIC_EnableIRQ(USART1_IRQn);//打开全局中断

  //main.c->启动串口1,使能串口空闲中断  
  __HAL_UART_ENABLE_IT(&huart1,UART_IT_IDLE); 
  HAL_UART_Receive_DMA(&huart1,a_Usart1_RxBuffer,RXLENGHT);

//中断函数
void USART1_IRQHandler(void)
{
    /* USER CODE BEGIN USART1_IRQn 0 */
  uint32_t temp;
  
  //判断是否产生空闲中断
  if(__HAL_UART_GET_FLAG(&huart1,UART_FLAG_IDLE)  != RESET )
  {
    //清除中断标志
   __HAL_UART_CLEAR_IDLEFLAG(&huart1);
   temp = huart1.Instance->ISR;  
   temp = huart1.Instance->RDR; 
   HAL_UART_DMAStop(&huart1);  //停用DMA
   temp  = hdma_usart1_rx.Instance->CNDTR;             
   UsartType1.Usart_rx_len =  RXLENGHT - temp; 
   HAL_UART_RxCpltCallback(&huart1);
  }
  /* USER CODE END USART1_IRQn 0 */
  HAL_UART_IRQHandler(&huart1);
  /* USER CODE BEGIN USART1_IRQn 1 */

  /* USER CODE END USART1_IRQn 1 */
}

//中断回调函数
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *UartHandle)
{ 
  uint8_t Old_len = 0;
  
  if(UartHandle->Instance == USART1)//串口2接收数据向串口1发送
  {  
    Old_len = strlen(UsartType1.usartDMA_rxBuf);
    if(0 != Old_len) //如果DMA buff中还有数据,则接着存储在buff中
    {
      memcpy(&UsartType1.usartDMA_rxBuf[Old_len],a_Usart1_RxBuffer,UsartType1.Usart_rx_len);
      Old_len=0;
    }
    else  //接收数据串口1数据,
      memcpy(UsartType1.usartDMA_rxBuf,a_Usart1_RxBuffer,UsartType1.Usart_rx_len);
//      UsartxSendData_DMA(&huart1,a_Usart2_RxBuffer,UsartType2.Usart_rx_len);
      memset(a_Usart1_RxBuffer,0,UsartType1.Usart_rx_len);
      HAL_UART_Receive_DMA(&huart1,a_Usart1_RxBuffer,RXLENGHT);
      UsartType1.receive_flag =1; //接收标识位
  }
}

//串口数据获取
void UartDmaGet(void)
{
  if(UsartType1.receive_flag == 1)//如果过新的数据
  {

    //串口接收到的数据原封发给SX1278
    Radio->SetTxPacket(UsartType1.usartDMA_rxBuf, UsartType1.Usart_rx_len);
    
    memset(UsartType1.usartDMA_rxBuf,0,UsartType1.Usart_rx_len);
    UsartType1.receive_flag = 0; //接收数据标志清零,
  }
}

(2)串口数据结构体

 

#define RXLENGHT  128
#define RECEIVELEN 2048
#define USART_DMA_SENDING       1//发送未完成
#define USART_DMA_SENDOVER      0//发送完成
typedef struct
{
  uint8_t receive_flag ;//空闲接收标记
  uint8_t dmaSend_flag ;//发送完成标记
  uint16_t Usart_rx_len;//接收长度
  uint8_t usartDMA_rxBuf[RECEIVELEN];//DMA接收缓存
}USART_RECEIVETYPE;

extern USART_RECEIVETYPE UsartType1;
extern USART_RECEIVETYPE UsartType2;
全局变量
extern uint8_t a_Usart1_RxBuffer[RXLENGHT]; 

2.2 LCD功能开发

该部分代码与PINGPONG系统主机从机LCD显示代码一致 

2.3 无线收发功能开发

//接收数据包计数
void RxDataPacketNum(void)
{
  if(EnableMaster == true)
    Master_RxNumber++;
  else
    Slave_RxNumber++;
}
//发送数据包计数
void TxDataPacketNum(void)
{
  if(EnableMaster == true)
    Master_TxNumber++;
  else
    Slave_TxNumber++;
}
//读取sx127x射频射频数据
void Sx127xDataGet(void)
{
  switch( Radio->Process( ) )
  {
  case RF_RX_TIMEOUT:
    printf("RF_RX_TIMEOUT\n");
    break;
  case RF_RX_DONE:
    Radio->GetRxPacket( Buffer, ( uint16_t* )&BufferSize );
    if(EnableMaster == true)
      printf("master Rx__%s,__,%d,%d\n",Buffer,strlen((char*)Buffer),BufferSize);
    else
      printf("slave Rx__%s,__,%d,%d\n",Buffer,strlen((char*)Buffer),BufferSize);      
    if( BufferSize > 0 )//&& (BufferSize == strlen((char*)Buffer)))
    {
      //接收数据闪烁
      LedBlink( LED_RX );
      //计算接收数据的个数
      RxDataPacketNum();

      //清空sx127x接收缓冲区
      memset(Buffer,0,BufferSize );
    }            
    break;
  case RF_TX_DONE:
    //发送闪烁
    LedBlink( LED_TX );
    //计算发送数据的个数
    TxDataPacketNum();
    Radio->StartRx( );
    break;
  case RF_TX_TIMEOUT:
    printf("RF_TX_TIMEOUT\n");
    break; 
  default:
    break;
  }
}


//声明一个led闪烁的函数
void LedBlink( tLed led )
{
    LedPort[led]->ODR ^= LedPin[led]; 
    HAL_Delay(50); 
    LedPort[led]->ODR ^= LedPin[led]; 
}

2.4 主函数开发

在PingPong系统的开发代码修改

#define BUFFERSIZE    128

//设备类型判断
#if defined(MASTER)

static uint8_t EnableMaster = true;

#elif defined(SLAVE)

static uint8_t EnableMaster = false;

#endif

//收发数据个数
static uint32_t Master_Txnumber = 0;
static uint32_t Master_Rxnumber = 0;

static uint32_t Slave_Txnumber = 0;
static uint32_t Slave_Rxnumber = 0;

int main(void)
{
  /* USER CODE BEGIN 1 */
  uint8_t RegVersion = 0;
  /* USER CODE END 1 */

  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_DMA_Init();
  MX_ADC_Init();
  MX_USART1_UART_Init();
  MX_SPI1_Init();
  /* USER CODE BEGIN 2 */
  
  Lcd_Init();
  
  showimage(gImage_logo);
  printf("sss\r\n");
  HAL_Delay(500);
  Lcd_Clear(RED);
  Gui_DrawFont_GBK16(0, 0, BLACK, BLUE, "  LoRa Topology  ");
#ifdef MASTER
  Gui_DrawFont_GBK16(0, 16, BLACK, BLUE, "     Master     ");
#else
  Gui_DrawFont_GBK16(0, 16, BLACK, BLUE, "     Slave     ");
#endif
  Gui_DrawFont_GBK16(2, 48, BLACK, RED, "SSID:");
  Gui_DrawFont_GBK16(45, 48, BLACK, RED, "30");
  Gui_DrawFont_GBK16(2, 64, BLACK, RED, "RX:");
  
  Gui_DrawFont_GBK16(2, 80, BLACK, RED, "TX:");

  
  //SPI重新初始化
  HAL_SPI_DeInit(&hspi1);
  MX_SPI1_Init();
  //启动串口1,使能串口空闲中断  
  __HAL_UART_ENABLE_IT(&huart1,UART_IT_IDLE); 
  HAL_UART_Receive_DMA(&huart1,a_Usart1_RxBuffer,RXLENGHT); 
  SX1276Read( REG_LR_VERSION, &RegVersion );
  if(RegVersion != 0x12)
  {
    printf("LoRa read failed!\r\n");
    printf("LoRa RegVersion:%d\r\n",RegVersion);
  }
  else
  {
    printf("LoRa read OK!\r\n");
    printf("LoRa RegVersion:%d\r\n",RegVersion);
  }
  //读到版本号关闭灯
  LedOff( LED_RX );
  LedOff( LED_TX );
  LedOff( LED_NT );
  
  
  radio = RadioDriverInit();
  radio ->Init();
  
  printf("system is init\r\n");
  
  radio->StartRx();
  
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
    UartDmaGet();
    Sx127xDataGet();
    
    if(EnableMaster == true)
    {
      MLCD_Show();
    }
    else
    {
      SLCD_Show(); 
    }
    
  }
  /* USER CODE END 3 */
}

  • 8
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值