物联网之LoRa开发与应用五(串口透传开发)

LoRa串口透传系统设计

内容概要:

1、串口透传系统设计需求

2、串口透传系统通信机制

3、串口透传系统业务流程

串口透传系统设计需求:

1、将LoRa终端定义成两种角色:Master(主机)和Slave(从机)

2、一个模块发送任意字节长度(小于128Byte)数据,另一模块都可以接收到

3、PC机上通过串口调试助手实现接收和发送

4、终端在LCD屏幕上显示终端类型及收发数据包个数

串口透传系统通信机制:

串口透传业务流程-初始化:

串口透传业务流程-主程序:

串口透传业务流程-LCD任务:

串口透传业务流程-串口接收任务:

串口透传业务流程-无线任务:

LoRa串口透传系统功能开发

内容概要:

1、串口功能开发

2、LCD功能开发

3、无线收发功能开发

串口功能接口:(这里介绍了相关函数和相关代码,里面使用到的一些变量在下面串口数据结构中会有说明)

​//启动串口1,使能串口空闲中断 。设置为空闲中断模式可以在整包之后才进行数据的收发,然后产生中断,避免重复单个数据的收发(减小开销)
__HAL_UART_ENABLE_IT(&huart1,UART_IT_IDLE);//注意:使能空闲中断后,不管有没有接收数据,会先触发一次中断,发送一次数据
HAL_UART_Receive_DMA(&huart1,a_Usart1_RxBuffer,RXLENGHT); //打开DMA接收,串口空闲中断模式配合DMA进行数据的接收
(以上两行代码功能:将uart1接收到的数据通过DMA传递给a_Usart1_RxBuffer,然后产生串口空闲中断,在中断中做进一步处理)

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 关键点1:以上两行代码用于清除DMA的接收中断(只需要读取一次ISR和RDR寄存器的值)
temp = hdma_usart1_rx.Instance->CNDTR;
UsartType1.Usart_rx_len = RXLENGHT - temp; /*CNDTR为DMA通道接收数据的计数器(注意是一个递减计数器,
所以需要将DMA的缓存区的总长度减去该计数器的值才是DMA通道接收数据的长度)*/
HAL_UART_RxCpltCallback(&huart1); //调用回调函数,在回调函数中具体实现数据的接收 关键点2
}
/* 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)//判断是否为USART1中断
{
Old_len = strlen(UsartType1.usartDMA_rxBuf);//读取发送缓冲区UsartType1.usartDMA_rxBuf的长度(如果为0则表示没有数据)
if(0 != Old_len) /*如果发送缓冲区中还有数据,则将DMA中接收缓冲区a_Usart1_RxBuffer的数据拷贝到发送缓冲区的末尾
(不覆盖发送缓冲区前面的数据)*/
{
memcpy(&UsartType1.usartDMA_rxBuf[Old_len],a_Usart1_RxBuffer,UsartType1.Usart_rx_len);
Old_len=0;
}
else /*如果发送缓冲区UsartType1.usartDMA_rxBuf中没有数据,则直接将DMA中接收缓冲区a_Usart1_RxBuffer的数据
拷贝到发送缓冲区的起始位置*/
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);//清空DMA中接收缓冲区a_Usart1_RxBuffer的数据
HAL_UART_Receive_DMA(&huart1,a_Usart1_RxBuffer,RXLENGHT);//继续打开DMA接收
UsartType1.receive_flag =1; //接收标识位,置1表示发送缓冲区中有新的数据,提醒相关函数将数据发送出去
}
}

//**********************************//
//
//函数名称:UartDmaGet
//
//函数描述:串口数据获取
//
//函数参数: 无
//
//返回值: 无
//
//创建者:
//*******************************//

void UartDmaGet(void) //串口接收任务处理函数
{
if(UsartType1.receive_flag == 1)//如果发送缓冲区中有新的数据,则将其发送给sx1278
{
//串口接收到的数据原封发给SX1278,sx1278通过无线将数据发送出去
Radio->SetTxPacket(UsartType1.usartDMA_rxBuf, UsartType1.Usart_rx_len);

memset(UsartType1.usartDMA_rxBuf,0,UsartType1.Usart_rx_len);
UsartType1.receive_flag = 0; //接收数据标志清零
}
}

串口数据结构:

上述代码中使用到的相关变量的定义如下:

#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];//无线发送缓存
}USART_RECEIVETYPE;

extern USART_RECEIVETYPE UsartType1;
extern USART_RECEIVETYPE UsartType2;
//
////局部变量
//extern uint8_t g_Usart1_RxBuffer[RXLENGHT];
//extern uint8_t g_Usart2_RxBuffer[RXLENGHT];
////全局变量
extern uint8_t a_Usart1_RxBuffer[RXLENGHT]; //DMA接收缓存
//extern uint8_t a_Usart2_RxBuffer[RXLENGHT];

LCD功能开发:略

无线收发功能开发:(略)

//**********************************//
//
//函数名称: RxDataPacketNum
//
//函数描述: 接收数据包计数
//
//函数参数: 无
//
//返回值: 无
//
//创建者:
//*******************************//

void RxDataPacketNum(void)
{
if(EnableMaster == true)
Master_RxNumber++;
else
Slave_RxNumber++;
}
//**********************************//
//
//函数名称: TxDataPacketNum
//
//函数描述: 发送数据包计数
//
//函数参数: 无
//
//返回值: 无
//
//创建者:
//*******************************//

void TxDataPacketNum(void)
{
if(EnableMaster == true)
Master_TxNumber++;
else
Slave_TxNumber++;
}

最后给出main.c文件的全部代码:

/**
******************************************************************************
* File Name : main.c
* Description : Main program body
******************************************************************************
** This notice applies to any and all portions of this file
* that are not between comment pairs USER CODE BEGIN and
* USER CODE END. Other portions of this file, whether
* inserted by the user or by software development tools
* are owned by their respective copyright owners.
*
* COPYRIGHT(c) 2018 STMicroelectronics
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. Neither the name of STMicroelectronics nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
******************************************************************************
*/
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "stm32f0xx_hal.h"
#include "adc.h"
#include "dma.h"
#include "spi.h"
#include "usart.h"
#include "gpio.h"
#include <stdio.h>
#include <string.h>
#include "lcd.h"
#include "led.h"

#include "logo.h"


//sx1278
#include "platform.h"
#include "radio.h"
#include "sx1276-Hal.h"
#include "sx1276-LoRa.h"
#include "sx1276-LoRaMisc.h"

/* USER CODE BEGIN Includes */

/* USER CODE END Includes */

/* Private variables ---------------------------------------------------------*/

#define BUFFER_SIZE 128

static uint16_t BufferSize = BUFFER_SIZE;
static uint8_t Buffer[BUFFER_SIZE];

#if defined(MASTER)

static uint8_t EnableMaster = true;

#elif defined(SLAVE)

static uint8_t EnableMaster = false;

#endif


tRadioDriver *Radio = NULL;

static uint32_t Master_RxNumber = 0;
static uint32_t Master_TxNumber = 0;

static uint32_t Slave_RxNumber = 0;
static uint32_t Slave_TxNumber = 0;

/* USER CODE BEGIN PV */
/* Private variables ---------------------------------------------------------*/

/* USER CODE END PV */

/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);

/* USER CODE BEGIN PFP */
/* Private function prototypes -----------------------------------------------*/

/* USER CODE END PFP */

/* USER CODE BEGIN 0 */
//**********************************//
//
//函数名称:UartDmaGet
//
//函数描述:串口数据获取
//
//函数参数: 无
//
//返回值: 无
//
//创建者:
//*******************************//

void UartDmaGet(void) //串口接收任务处理函数
{
if(UsartType1.receive_flag == 1)//如果发送缓冲区中有新的数据,则将其发送给sx1278
{

//串口接收到的数据原封发给SX1278,sx1278通过无线将数据发送出去
Radio->SetTxPacket(UsartType1.usartDMA_rxBuf, UsartType1.Usart_rx_len);

memset(UsartType1.usartDMA_rxBuf,0,UsartType1.Usart_rx_len);
UsartType1.receive_flag = 0; //接收数据标志清零,
}
}

//**********************************//
//
//函数名称: RxDataPacketNum
//
//函数描述: 接收数据包计数
//
//函数参数: 无
//
//返回值: 无
//
//创建者:
//*******************************//

void RxDataPacketNum(void)
{
if(EnableMaster == true)
Master_RxNumber++;
else
Slave_RxNumber++;
}
//**********************************//
//
//函数名称: TxDataPacketNum
//
//函数描述: 发送数据包计数
//
//函数参数: 无
//
//返回值: 无
//
//创建者:
//*******************************//

void TxDataPacketNum(void)
{
if(EnableMaster == true)
Master_TxNumber++;
else
Slave_TxNumber++;
}
//**********************************//
//
//函数名称: Sx127xDataGet
//
//函数描述: 读取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;
}
}


//**********************************//
//
//函数名称:MLCD_Show
//
//函数描述:LoRa主机屏幕显示数据
//
//函数参数:无
//
//返回值:无
//
//创建者:
//*******************************//

void MLCD_Show(void)
{

uint8_t str[20] = {0};

LCD_GPIO_Init();



sprintf((char*)str,"%d",Master_RxNumber);
Gui_DrawFont_GBK16(64,48,BLACK,YELLOW,str);

memset((char*)str,0,strlen((char*)str));


sprintf((char*)str,"%d",Master_TxNumber);
Gui_DrawFont_GBK16(64,64,BLACK,YELLOW,str);

HAL_SPI_DeInit(&hspi1);
MX_SPI1_Init();
}
//**********************************//
//
//函数名称:SLCD_Show
//
//函数描述:LoRa从机屏幕显示数据
//
//函数参数:无
//
//返回值: 无
//
//创建者:
//*******************************//

void SLCD_Show(void)
{


uint8_t str[20] = {0};

LCD_GPIO_Init();

sprintf((char*)str,"%d",Slave_RxNumber);
Gui_DrawFont_GBK16(64,48,BLACK,YELLOW,str);

memset((char*)str,0,strlen((char*)str));

sprintf((char*)str,"%d",Slave_TxNumber);
Gui_DrawFont_GBK16(64,64,BLACK,YELLOW,str);

HAL_SPI_DeInit(&hspi1);
MX_SPI1_Init();

}



/* USER CODE END 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();

Lcd_Init();
showimage(gImage_logo);
HAL_Delay(500);
Lcd_Clear(YELLOW);

Gui_DrawFont_GBK16(0,0,RED,GREEN," LoRa Topology ");
#if defined (SLAVE)
Gui_DrawFont_GBK16(0,16,RED,GREEN," Slave ");
#elif defined (MASTER)
Gui_DrawFont_GBK16(0,16,RED,GREEN," Master ");
#endif
Gui_DrawFont_GBK16(0,32,BLACK,YELLOW,"SSID:");
Gui_DrawFont_GBK16(64,32,BLACK,YELLOW,"30");

Gui_DrawFont_GBK16(0,48,BLACK,YELLOW,"RX:");
Gui_DrawFont_GBK16(0,64,BLACK,YELLOW,"TX:");


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 Error!\r\n");
printf("LoRa RegVersion = %d!\r\n",RegVersion);

}
else
{
printf("LoRa read Ok!\r\n");
printf("LoRa RegVersion = %d!\r\n",RegVersion);



}

//读到版本号后,关闭3种灯
LedOff(LED_RX);
LedOff(LED_TX);
LedOff(LED_NT);

Radio = RadioDriverInit();

Radio->Init();


printf("systerm init ok!");


Radio->StartRx( );//打开接收模式


/* USER CODE BEGIN 2 */

/* USER CODE END 2 */

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

/* USER CODE BEGIN 3 */

UartDmaGet();//串口接收任务处理函数

Sx127xDataGet();//读取sx127x射频射频数据
if(EnableMaster == true)
{
MLCD_Show();

}
else
{
SLCD_Show();
}

}
/* USER CODE END 3 */

}

/** System Clock Configuration
*/
void SystemClock_Config(void)
{

RCC_OscInitTypeDef RCC_OscInitStruct;
RCC_ClkInitTypeDef RCC_ClkInitStruct;
RCC_PeriphCLKInitTypeDef PeriphClkInit;

/**Initializes the CPU, AHB and APB busses clocks
*/
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI14|RCC_OSCILLATORTYPE_HSE;
RCC_OscInitStruct.HSEState = RCC_HSE_ON;
RCC_OscInitStruct.HSIState = RCC_HSI_ON;
RCC_OscInitStruct.HSI14State = RCC_HSI14_ON;
RCC_OscInitStruct.HSI14CalibrationValue = 16;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL6;
RCC_OscInitStruct.PLL.PREDIV = RCC_PREDIV_DIV1;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}

/**Initializes the CPU, AHB and APB busses clocks
*/
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
|RCC_CLOCKTYPE_PCLK1;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;

if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_1) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}

PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_USART1;
PeriphClkInit.Usart1ClockSelection = RCC_USART1CLKSOURCE_PCLK1;
if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}

/**Configure the Systick interrupt time
*/
HAL_SYSTICK_Config(HAL_RCC_GetHCLKFreq()/1000);

/**Configure the Systick
*/
HAL_SYSTICK_CLKSourceConfig(SYSTICK_CLKSOURCE_HCLK);

/* SysTick_IRQn interrupt configuration */
HAL_NVIC_SetPriority(SysTick_IRQn, 0, 0);
}

/* USER CODE BEGIN 4 */

/* USER CODE END 4 */

/**
* @brief This function is executed in case of error occurrence.
* @param None
* @retval None
*/
void _Error_Handler(char * file, int line)
{
/* USER CODE BEGIN Error_Handler_Debug */
/* User can add his own implementation to report the HAL error return state */
while(1)
{
}
/* USER CODE END Error_Handler_Debug */
}

#ifdef USE_FULL_ASSERT

/**
* @brief Reports the name of the source file and the source line number
* where the assert_param error has occurred.
* @param file: pointer to the source file name
* @param line: assert_param error line source number
* @retval None
*/
void assert_failed(uint8_t* file, uint32_t line)
{
/* USER CODE BEGIN 6 */
/* User can add his own implementation to report the file name and line number,
ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
/* USER CODE END 6 */

}

#endif


int fputc(int ch,FILE *f)
{

while((USART1->ISR&0X40) == 0);

USART1->TDR = (uint8_t)ch;

return ch;

}


/**
* @}
*/

/**
* @}
*/

/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/


---------------------
作者:许新天
来源:CSDN
原文:https://blog.csdn.net/weixin_39148042/article/details/81739617

说明:LPWA物联网应用站(LPWAP.com)通过公开互联网收集、整理并转载有关LPWA物联网应用解决方案,以供广大LPWA应用开发者和爱好者共同学习交流和参考运用到实际生产生活中。本站所有转载的文章、图片、音频、视频等资料的版权归版权所有人所有并衷心感谢您的付出,由于本站采纳的非本站原创文章及图片等内容无法一一联系确认版权者,如果本网所选内容的文章原创作者认为其作品不宜放在本站,请及时通过以下留言功能通知我们采取适当措施,避免给双方造成不必要的经济损失。如果您希望保留文章在本站,但希望文章末尾提供对作者的致谢或者产品、网站交换链接的,也请将需求写入以下留言栏中,谢谢您的支持。让我们共同努力,打造万物互联的未来美好生活!

您的留言或需求: