基于STM32的LoRa无线通信(AS32—TTL-1W)

目录

无线串口简介

项目简介

发送端代码

接收端代码

项目总结


前些天接触到一个小项目,需要使用无线传输的功能,不仅如此还需要远距离的通信,搜索资料后最终选择了泽耀科技的LoRa(AS32—TTL-1W)无线串口模块。之前使用的是100mW的无线模块,经拉距实测在非空旷地带通信距离不到800米。因此这次我选择了1W的无线串口,一般情况下功率越高通信距离就越远。这次还未尽量拉距测试,测试完成后我会继续写一篇测评文章。今天就给大家分享一些我开发的过程以及遇到的一些bug。

无线串口简介

我使用的泽耀科技生产的AS32—TTL-1W,单价55(不含天线),天线单买10元。价格还算公道,毕竟是LoRa模块,图便宜只能买到很多虚标的产品。

1.引脚介绍

该模块一个7个引脚,引脚功能图如下

 MD0,MD1引脚的作用就是修改模块工作状态的(如下图),如果刚开始学搞不懂这些工作状态是什么意思的情况下,收发数据的时候,把MD0,MD1接地即可使用。完成了基本的收发试验后,可以探索一下其它的功能。

 LoRa模块的RX,TX分别接到单片机TX,RX上,如下图。

AUX引脚是用于指示模块工作状态,用户唤醒外部 MCU,配合外部中断即可开发相应的功能。但还是那句话,初学者可以不用考虑这个引脚,给它悬空即可,不影响使用的。

剩下的就是VCC和GND了,商家的参考手册明确给出电源电压小于 4.5V,输出功率会有下降,但对接收功率影响较小。所以我给它接了5V。

2.上位机简介

使用泽耀科技开发的上位机可直接修改模块的波特率,地址,信道,传输方式等参数。最好可以配上泽耀科技生产的usb转ttl一起使用。把模块直接插入上面的单排座即可使用。(配置的时候记得拔掉两个黄色的跳帽)

 上位机如图所示

 其实如果只是在两个MCU实现简单的单点通信,直接用厂家的出厂设置的参数就足够了。

项目简介

 项目其实很简单,按下发射端精英板上的KEY_UP按键,接收端精英板的LED0闪一下。按下发射端精英板上的KEY1按键,接收端精英板的LED1闪一下。(代码是移植泽耀科技提供的demo改写的)

 发送端代码

main.c

#include "led.h"
#include "delay.h"
#include "key.h"
#include "sys.h"
#include "drv_uart.h"
#include "drv_led.h"
#include "drv_AS62.h" 

#define __AS62_TX_MODE__	 //模式选择,屏蔽即为接收模式						


#ifdef __AS62_TX_MODE__
	char *Signal_Go = "a";   //定义字符a
	char *Signal_Stop = "s"; //定义字符s
#else
	uint8_t go[32 ] ={ 'a'};
	uint8_t stop[ 32] ={ 's'};
	uint8_t Go_rx_buffer[ 100 ] = { 0 };
	uint8_t Stop_rx_buffer[100] = { 0 };
	uint8_t g_RxLength = 0;
#endif
 int main(void)
 { 
	int key=0;	
	Init_USART();	        //串口初始化
	ASxx_param_init( );     //模块参数初始化(定点模式,地址0x1234,信道0x17)
	drv_led_init();         //LED初始化
	delay_init();	    	//延时初始化
	KEY_Init();         	//按键初始化
    led_green_off();        
    led_red_off();          //初始化LED灭
	while(1)
	{
 		key=KEY_Scan(0);	//获取键值
		
		if(key==WKUP_PRES)
		{
			led_red_flashing();//LED每发送一次数据闪一下
			GO_On();          //发送字符a·····这里我使用了宏定义发送字符
			delay_ms(500);
			Send_Off;         //模块复位
			led_red_off();
			
		}
		if(key== KEY1_PRES)
		{
			led_green_flashing();//LED每发送一次数据闪一下
			Stop_On;             //发送字符b
			delay_ms(500); 
			Send_Off;            //模块复位
			led_green_off();
		}

	}	 
}

 LoRa.c(此段代码来自泽耀科技的demo)

#include "drv_AS62.h"


//模块配置参数数组
//改变模块参数,只需改变参数数组值,然后初始化即可
const uint8_t g_ASxx_Param_Config[ 6 ] = { 0xC0, 0x12, 0x34, 0x1A, 0x17, 0xC4 };			//定点模式
const uint8_t  g_ASxx_Config_Status_OK[ ] = { 0x4F, 0x4B, 0x0D, 0x0A };


/**
  * @brief :ASxx模块初始化
  * @param :无
  * @note  :按照默认参数初始化,修改默认参数表即可改变模块初始化参数
  * @retval:
  *        @ASxx_Write_OK 写入成功
  *        @ASxx_Write_ERROR 写入失败
  */
ASxxWriteStatusType ASxx_param_init( void )
{
	uint8_t i = 0;
	uint8_t Read_Buff[ 5 ] = { 0 };
	
	drv_uart_tx_bytes((uint8_t *)g_ASxx_Param_Config, 6 );
	drv_uart_rx_bytes( Read_Buff );
	
	for( i = 0; i < 4; i++ )
	{
		if( Read_Buff[ i ] != g_ASxx_Config_Status_OK[ i ] )
		{
			break;
		}
	}
	
	if( 4 == i )
	{
		return ASxx_Write_OK;		//配置成功
	}
	else
	{
		return ASxx_Write_ERROR;	//配置失败
	}
	
}

/**
  * @brief :ASxx模块读配置参数
  * @param :
  * @pReadBuffer:参数返回地址
  * @note  :无
  * @retval:无
  */
void ASxx_read_param( uint8_t *pReadBuffer )
{
	uint8_t Read_Cmd[ 3 ] = { 0xC1, 0xC1, 0xC1 };

	drv_uart_tx_bytes( Read_Cmd, 3 );
	drv_uart_rx_bytes( pReadBuffer );
	
}

/**
  * @brief :ASxx模块读取硬件版本号
  * @param :
  * @pReadBuffer:硬件版本号返回地址
  * @note  :无
  * @retval:无
  */
void ASxx_read_version( uint8_t *pReadBuffer )
{
	uint8_t Read_Cmd[ 3 ] = { 0xC3, 0xC3, 0xC3 };

	drv_uart_tx_bytes( Read_Cmd, 3 );
	drv_uart_rx_bytes( pReadBuffer );
}

/**
  * @brief :ASxx模块读取实际电压值
  * @param :
  * @pReadBuffer:电压值返回地址
  * @note  :无
  * @retval:无
  */
void ASxx_read_voltage( uint8_t *pReadBuffer )
{
	uint8_t Read_Cmd[ 3 ] = { 0xC5, 0xC5, 0xC5 };

	drv_uart_tx_bytes( Read_Cmd, 3 );
	drv_uart_rx_bytes( pReadBuffer );
}

/**
  * @brief :ASxx模块复位
  * @param :无
  * @note  :无
  * @retval:无
  */
void ASxx_reset( void )
{
	uint8_t Read_Cmd[ 3 ] = { 0xC4, 0xC4, 0xC4 };

	drv_uart_tx_bytes( Read_Cmd, 3 );
}

/**
  * @brief :ASxx模块发送数据(定点模式)
  * @param :
  *        @Addr_H:地址高位
  *        @Addr_L:地址低位
  *        @Channel:信道
  *        @pTxBuff:发送数据地址
  *        @Length:发送数据个数
  * @note  :定点模式 数据个数最29个
  * @retval:无
  */
void ASxx_tx_packet( uint8_t Addr_H, uint8_t Addr_L, uint8_t Channel, uint8_t *pTxBuff, uint8_t Length )
{
	uint8_t Header[ 3 ] = { 0 };
	
	Header[ 0 ] = Addr_H;
	Header[ 1 ] = Addr_L;
	Header[ 2 ] = Channel;
	
	drv_uart_tx_bytes( Header, 3 );
	//发送数据
	drv_uart_tx_bytes( pTxBuff, Length );
}

/**
  * @brief :ASxx模块接收数据(定点模式)
  * @param :无
  * @note  :定点模式 数据个数最29个
  * @retval:无
  */
uint8_t ASxx_rx_packet( uint8_t *pRxBuff )
{
	uint8_t Length = 0;
	
	Length = drv_uart_rx_bytes( pRxBuff );
	
	return Length;
}


usart.c

#include "drv_uart.h"


void Init_USART(void)
{
	 GPIO_InitTypeDef GPIO_Structure;
	 USART_InitTypeDef USART_Structure;
	 NVIC_InitTypeDef NVIC_Structure;
	 NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);

	 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
   RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);
	 RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);
	
	 //PA9 TX
	 GPIO_Structure.GPIO_Mode=GPIO_Mode_AF_PP;
	 GPIO_Structure.GPIO_Pin=GPIO_Pin_9;
	 GPIO_Structure.GPIO_Speed=GPIO_Speed_50MHz;
	
	 GPIO_Init(GPIOA,&GPIO_Structure);
	 //PA10 RX
	 GPIO_Structure.GPIO_Mode=GPIO_Mode_IN_FLOATING;
	 GPIO_Structure.GPIO_Pin=GPIO_Pin_10;

	 GPIO_Init(GPIOA,&GPIO_Structure);

	 USART_Structure.USART_BaudRate=115200;
	 USART_Structure.USART_HardwareFlowControl=USART_HardwareFlowControl_None;
	 USART_Structure.USART_Mode=USART_Mode_Rx|USART_Mode_Tx;
	 USART_Structure.USART_Parity=USART_Parity_No;  
	 USART_Structure.USART_StopBits=USART_StopBits_1;
	 USART_Structure.USART_WordLength=USART_WordLength_8b;
	 USART_ITConfig(USART1, USART_IT_RXNE,  ENABLE);

	 USART_Init(USART1, &USART_Structure);
   USART_Cmd(USART1, ENABLE);

   NVIC_Structure.NVIC_IRQChannel=USART1_IRQn;
	 NVIC_Structure.NVIC_IRQChannelCmd=ENABLE;
	 NVIC_Structure.NVIC_IRQChannelPreemptionPriority=1;
	 NVIC_Structure.NVIC_IRQChannelSubPriority=1;
   NVIC_Init(&NVIC_Structure);

}

/**
  * @brief :串口发送数据
  * @param :
  *			@TxBuffer:发送数据首地址
  *			@Length:数据长度
  * @note  :无
  * @retval:无
  */
void drv_uart_tx_bytes( uint8_t* TxBuffer, uint8_t Length )
{
	while( Length-- )
	{
		while( RESET == USART_GetFlagStatus( UART_PORT, USART_FLAG_TXE ));
		UART_PORT->DR = *TxBuffer;
		TxBuffer++;
	}
}

/**
  * @brief :串口接收数据
  * @param :
  *			@RxBuffer:发送数据首地址
  * @note  :无
  * @retval:接收到的字节个数
  */
uint8_t drv_uart_rx_bytes( uint8_t* RxBuffer )
{
	uint8_t l_RxLength = 0;
	uint16_t l_UartRxTimOut = 0x7FFF;
	
	while( l_UartRxTimOut-- )			//等待查询串口数据
	{
		if( RESET != USART_GetFlagStatus( UART_PORT, USART_FLAG_RXNE ))
		{
			*RxBuffer = (uint8_t)UART_PORT->DR;
			RxBuffer++;
			l_RxLength++;
			l_UartRxTimOut = 0x7FFF;	//接收到一个字符,回复等待时间
		}
		if( 100 == l_RxLength )
		{
			break;		//不能超过100个字节
		}
	}
	
	return l_RxLength;					//等待超时,数据接收完成
}

//发送字符(想通过串口助手查看自己发送的数据可以调用这个函数)
void USART_SendByte_Init(USART_TypeDef* USARTx, uint16_t Data)
{
	
	USART_SendData(USARTx,Data);
  while(USART_GetFlagStatus(USARTx,USART_FLAG_TXE)==RESET);
	
}
//发送字符串(想通过串口助手查看自己发送的数据可以调用这个函数)
void USART_SendStr_Init(USART_TypeDef* USARTx,char *str)
{
	uint16_t i=0;
	do
	{
		USART_SendByte_Init(USARTx,*(str+i));
		i++;
	}while(*(str+i)!='\0');
	
	 while(USART_GetFlagStatus(USARTx,USART_FLAG_TC)==RESET);

}





接收端代码

main.c

#include "led.h"
#include "delay.h"
#include "sys.h"
#include "drv_uart.h"
#include "drv_led.h"
#include "drv_AS62.h" 

//#define __AS62_TX_MODE__		//接收模式开启							

void delay(uint16_t time)       //延时函数
{   
	uint16_t i=0;               
	while(time--)
	{
		i=12000;
		while(i--);
	}
	
}

#ifdef __AS62_TX_MODE__
	char *Signal_Go = "a";
	char *Signal_Stop = "s";
	
#else
	uint8_t go[ 8 ] ={ 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a' };
	uint8_t stop[ 8 ] ={ 's', 's', 's', 's', 's', 's', 's', 's' };
  uint8_t Go_rx_buffer[ 100 ] = { 0 };
	uint8_t Stop_rx_buffer[100] = { 0};
	uint8_t g_RxGO = 0;
  uint8_t g_RxSTOP = 0;
#endif

int main( void )
{	
    drv_uart_init();
	ASxx_param_init( );
	drv_led_init( );
  led_green_off();
  led_red_off( );
	while(1)
	{

	}

 }
 void USART1_IRQHandler (void)    //串口中断服务函数
 {
	 char temp=0;
	 if(USART_GetITStatus( USART1, USART_IT_RXNE)!=RESET)
    {
			temp =  USART_ReceiveData(USART1);
			if(temp=='a')
			{
	      led_green_flashing();        //接收到字符‘a’led闪一次

				delay(500);
				led_green_off();
			}		
			if(temp=='s')
			{
	      led_red_flashing();           //接收到字符‘b’led闪一次
				delay(500);
				led_red_off()	;
			}		
		}
 }




由于接收端的代码与发射端除了main函数之外,其它函数几乎一致,所以我这里只上传了main函数里的内容,如果想要完整代码可以私信我。

项目中遇到的小bug

在写接收端的串口中断服务函数的时候,在实现灯的闪烁的时候,开始我的延时函数调用的是定时器中断延时。但程序编译运行后发现程序根本无法执行闪烁的效果,接收到指定的字符后led一直保持常亮的状态。我百思不得其解,根据程序执行的逻辑从表面上看没毛病呀!尝试过各种猜想,一开始还傻傻的以为是中断标志位没有清除,但后来发现都不是。然后我抱着瞎猫碰到死耗子的心理,把定时器中断延时改成了传统上的“粗延时”,编译执行后居然成功了,接收到指定字符后可以实现闪烁了。这着实让我既惊讶又惊喜,这个折磨我这么多天的bug居然就这么解决了。但我到现在都不知道是什么原理,之前用Wemos实现串口中断的时候也是在串口中断服务函数中加入delay()后,程序也出现了类似的bug。难道串口中断服务函数就是这么奇葩吗?欢迎大佬前来指点!!!

项目总结

这个项目虽然简单,但我实现的过程中还是挺坎坷的。全网适用于泽耀科技产品的例程并不多,根据泽耀科技提供的demo写了快一个星期(毕竟本人也是单片机小白),网上大多数都是关于安信可的或者NFR2401相关的例程比较多一些。尤其是NFR2401的例程多到快烂大街了,所以想在短时间内开发出项目或是单片机基础薄弱的同学选择NFR2401开发也是一个不错之选。关于LoRa无线串口这个模块我了解的还仅仅是冰山一角,还有很多功能都没有开发出来,本人也才疏学浅,文章中如有错误欢迎大佬在评论区指正,如想要源代码的小伙伴也可以私信我。

猜你喜欢

转载自blog.csdn.net/aaaaaaaa123345/article/details/122380786
今日推荐