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****/