STM32+DX-LR01实现数据收发超详细版(解决串口发送不了、无数据、接收不了问题)

  • 硬件选择

MCU选用目前较受初学者欢迎的STM32F103C8T6。此MCU内部外设丰富,具有三个串口、四个定时器、两个IIC、两个SPI等等,选其作为小形项目或节点的主控制器是非常合适的。

通信模块选用大厦龙雀的LR01,其推出的初学者套餐也是非常友好。LR01具有体积小、通信距离远(可在城市中3.8km内通信)、抗干扰性强、应用场景广泛等特点,使用AT指令配置必要参数便可实现数据互传,非常适用于小型的通信项目。

点击查看详情(初次购买有优惠)

LR01有七个引脚,具体如下:

但本文只需串口即可实现,更多使用配置请参考手册

模块连接VCC—3.3-5V,GND—GND,RX—PA2(TX),TX—PA3(RX)。

  • AT配置

配置为MODE0透明传输,传输速率以及信道要一样。

 

  • 相关程序

串口配置

void USART2_Config(void)
{
	GPIO_InitTypeDef GPIO_InitStructure;
	USART_InitTypeDef USART_InitStructure;
	NVIC_InitTypeDef NVIC_InitStructure;
	// 打开串口GPIO的时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_AFIO, ENABLE);
	
	// 打开串口外设的时钟
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE);
	
	// 将USART Tx的GPIO配置为推挽复用模式
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);

	// 将USART Rx的GPIO配置为浮空输入模式
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
	GPIO_Init(GPIOA, &GPIO_InitStructure);

		/* 嵌套向量中断控制器组选择 */
  NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
  
  /* 配置USART为中断源 */
  NVIC_InitStructure.NVIC_IRQChannel = USART2_IRQn;
  /* 抢断优先级*/
  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 3;
  /* 子优先级 */
  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;
  /* 使能中断 */
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  /* 初始化配置NVIC */
  NVIC_Init(&NVIC_InitStructure);
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);// 串口中断优先级配置
		
	
	// 配置串口的工作参数
	// 配置波特率
	USART_InitStructure.USART_BaudRate = 115200;
	// 配置 针数据字长
	USART_InitStructure.USART_WordLength = USART_WordLength_8b;
	// 配置停止位
	USART_InitStructure.USART_StopBits = USART_StopBits_1;
	// 配置校验位
	USART_InitStructure.USART_Parity = USART_Parity_No ;
	// 配置硬件流控制
	USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
	// 配置工作模式,收发一起
	USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
	// 完成串口的初始化配置
	USART_Init(USART2, &USART_InitStructure);
	
	// 使能串口
	USART_Cmd(USART2, ENABLE);
	
	USART_ClearFlag(USART2, USART_FLAG_TC);//清除发送缓存区以防发送卡死
	// 使能串口接收中断 
	USART_ITConfig(USART2, USART_IT_RXNE, ENABLE);
	USART_ITConfig(USART2, USART_IT_IDLE, ENABLE);//开启IDLE中断,防止串口只接收第一个字节就停
	
}

发送函数

void Usart_SendString(USART_TypeDef *USARTx, char *str, unsigned short len)
{
	unsigned short count = 0;
	for(; count < len; count++)
	{
		USART_SendData(USARTx, *str++);								//发送数据
		while(RESET == USART_GetFlagStatus(USARTx, USART_FLAG_TC));		//等待发送完成
	}
	USART_ITConfig(USART2, USART_IT_RXNE, ENABLE);//发送完毕后打开串口接收中断
}

相关问题:为什么程序卡死在 while(RESET == USART_GetFlagStatus(USARTx, USART_FLAG_TC));?

肯定要检查配置,USART2是总线2,而引脚和复用功能的时钟是总线1,配置时别混了;引脚的配置也别忽略,PA3输入没有速度;还有查看串口是否使能,也就是这句USART_Cmd(USART2, ENABLE)。

串口中断

char RxData[10] = "";
int Receive_sum = 0;

void USART2_IRQHandler(void)
{
    if (USART_GetITStatus(USART2, USART_IT_RXNE) == SET)
    {
		USART_ClearITPendingBit(USART2, USART_IT_RXNE);
		RxData[Receive_sum++] = USART_ReceiveData(USART2);
    }
	if(USART_GetITStatus(USART2,USART_IT_IDLE) != RESET)//接收到一帧数据
	{
		USART2->SR;//先读SR
		USART2->DR;//再读DR
		Receive_sum = 0;//数组下标一定记得清零,不然会只有一次接收数据
		printf("%s", RxData);//打印看结果
	}
}

相关问题:为什么串口只能接收一次数据?或只能接收第一个字节?

只能接收一次数据是因为接收数组的下标未清零,导致数组容量溢出则无法获取数据。还可能是中断优先级低于USART1(打印使用)。

只能接收第一个字节是因为在中断函数中执行了逻辑运算代码,所以不要加入任何数据解析之外的逻辑(如延时函数、浮点数运算等等)。

因为发送和接收同时使用,串口的收发都是字符型的,所以在发送前进行格式转换并将接收中断关闭以防乱码。如:

	int i,len = 2;
	USART_ITConfig(DEBUG_USART2, USART_IT_RXNE, DISABLE);//关闭串口接收中断
		while(len --){
			sprintf(DATA_BUF,"XS001case total:%d out:%d",Total_p,out_p);//将数据转化为字符串	
			Usart_SendString(USART2, DATA_BUF, strlen(DATA_BUF));
			printf(DATA_BUF);
			printf("\r\n");
			strcpy(DATA_BUF, "");//发送完毕后将数组清零
			delay_ms(500);
		}

相关问题:发送无数据?或没收到?

实验后发现只进行一次数据发送,另一个LR01模块是收不到的,所以这里有个while,循环了两次也就是发送了两次。

LR01与LORA模块一样,具有三种通信模式:透明传输、定点传输、广播传输。定点传输与广播传输都需要以HEX形式发送,串口上勾选即可进行精准通信,但鄙人使用单片机转换了多种形式发送但接收端依旧收不到数据,所以使用了透明传输。(希望哪位大佬测试成功了教教我TAT

 要实现定点传输或准确接收就要做一些逻辑处理,如在发送的数据前加上标识符,并在接收端判断数据头,如符合了便进行下一步逻辑程序。

void receive_data(){
	
	int num1 = 0;
	if(RxData[0] == 'X' && RxData[1] == 'S'){
		printf(RxData);	
		sscanf(&RxData[3],"%d",&num1);
		Total_p = num1;
		strcpy(RxData, "");
	}
	
}

实现结果

如何文章对你有帮助,可以打赏吃颗糖吗~

技术交流:XXS05077(备注来意)

评论 10
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值