Stm32_标准库_14_串口&蓝牙模块_解决手机与蓝牙模块数据传输的不完整性

由手机向蓝牙模块传输时间信息,Stm32获取信息并将已存在信息修改为传入信息

测试代码:

c 复制代码
#include "stm32f10x.h"    // Device header
#include "Delay.h"
#include "OLED.h"
#include "Serial.h"

uint16_t num = 0;
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
char News[100] = ""; 
uint8_t flag = 1;

/*初始化通用定时器TIM2*/
void Timer_Init(void){
	   RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);//APB1外设开启
	   
	   TIM_InternalClockConfig(TIM2);//选择内部时钟
	
	   /*初始化时基单元*/
	   TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
	   TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;//向上计数
	   TIM_TimeBaseInitStructure.TIM_Period = 10000 - 1;//ARR自动重装
	   TIM_TimeBaseInitStructure.TIM_Prescaler = 7200 - 1;//psc预分频器
	   TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;//高级计时器内容直接给零
	   //记录1s 
	   TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);//刚初始化完就会进中断
	   
	   TIM_ClearFlag(TIM2, TIM_FLAG_Update);//消除中断标志位
	   //使能更新中断
	   TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);
	
	   /*配置中断*/
	   NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//选择组2
	   NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;//定时器2在NVIC内的通道
	   NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
	   NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;
	   NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
		 NVIC_Init(&NVIC_InitStructure);
		 
		 TIM_Cmd(TIM2, ENABLE);//启动定时器
}
unsigned char time[] = {22, 59, 30};
unsigned int date[] = {2023, 12, 31};
char month[] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
void Show_Time(void){
	   OLED_ShowNum(1,1,time[0], 2);
	   OLED_ShowString(1, 3, ":");
	   OLED_ShowNum(1,4,time[1], 2);
	   OLED_ShowString(1, 6, ":");
	   OLED_ShowNum(1,7,time[2], 2);
}
void Show_Date(void){
	   OLED_ShowNum(2,1,date[0], 4);
	   OLED_ShowString(2, 5, "/");
	   OLED_ShowNum(2,6,date[1], 2);
	   OLED_ShowString(2, 8, "/");
	   OLED_ShowNum(2,9,date[2], 2);
}
void Time_Control(void){
	       time[2] = time[2] + 1;
        if(time[2] >= 60){
          time[2] = 0;
          time[1] = time[1] + 1;
          if(time[1] >= 60){
            time[1] = 0;
            time[0] = time[0] + 1;
            if(time[0] >= 24){
              time[0] = 0;
              date[2] = date[2] + 1;
   	          if(date[2] >= month[date[1]] + 1){
   		        date[2] = 1;
   		        date[1] = date[1] + 1;
   		        if(date[1] >= 13){
   		        	date[1] = 1;
   		        	date[0] = date[0] + 1;
   		        	if(date[0] >= 9999){
   		        		date[0] = 2023;
					   }
				   }
	           }
            }
         }
       }
				 
				
}

void TIM2_IRQHandler(void){//定时器2的中断函数,名字固定
	   if(TIM_GetITStatus(TIM2, TIM_IT_Update) == SET){
			  
			 TIM_ClearITPendingBit(TIM2, TIM_IT_Update);//清除标志位
			 Time_Control();
		 }
		  
}

void month2_Control(void){//判别闰平年 
	if((date[0] % 4 == 0 && date[0] % 100 != 0 )|| date[0] % 400 == 0) month[2] = 29;
	else month[2] = 28;
}

 
void Get_Hc05News(void){
	   uint32_t i = 0, j = 0;
	   while(j < 10000){//等待中断
	      while(Serial_GetRxFlag() == 1){//查看标志位并清除
					   News[i] =  Serial_GetRxData();//传入数据
					   i ++;
				     j = 0;
					   flag = 0;//标志传入了新数据
		    }
		    j ++;
		 }
}

void Array_NewsClear(void){//恢复数组初始化
	   uint16_t i = 0;
	   for(i = 0; i < 100; i ++) News[i] = '\0';
}

uint8_t StringLength(char * a){//计算数组长度函数
	      uint8_t length = 0;
	      uint8_t i = 0;
	      while(a[i] != '\0'){
					i ++;
					length ++;
				}
				return length;
}

uint8_t Check(char *a, uint8_t length){
	      if(length == 5 | length == 10){
					 return 1;
				}
				return 0;
}
 
int main(void){
	 
	OLED_Init();//初始化OLED
  Timer_Init();//开启计时器
	Serial_Init();//开启串口
	while(1){ 
		    Get_Hc05News();//时刻等待蓝牙传入数据
		     
		     if(flag == 0){//蓝牙传入了数据
					  //恢复标志位
					 if(Check(News, StringLength(News)) == 1){//若查看数据没有错误
						 OLED_ShowString(3, 1, "TRUE");
						 OLED_ShowString(4, 1, News);
						 Array_NewsClear();
					 }
					 else{
						   OLED_ShowString(3, 1, "FALSE");
						   OLED_ShowString(4, 1, News);
						   Array_NewsClear();
					 }
					 flag = 1;
				}
		    
	}
}

目前遇到的主要问题是OLED显示数字需要耗费时间,导致蓝牙模块传入信息不能及时抢占CPU导致数据漏传入,解决方法是修改蓝牙模块中断为更高级中断

------ 2023/10/15


为了解决上述问题首先要将蓝牙传数据模块的中断抢占优先级调高,这是为了防止计数器中断与其抢CPU

//将串口|蓝牙模块中断抢占优先级设置为3
NVIC_InitStructur.NVIC_IRQChannelPreemptionPriority = 3;
NVIC_InitStructur.NVIC_IRQChannelSubPriority = 1;

调整之后传输数据,明显丢包率小了许多,但仍有数据丢失

原因在于主程序,程序在未执行到if语句的时候是不会接受数据的

c 复制代码
while(1){ 
		
		if(Serial_GetRxFlag() == 1){
			 News = Serial_returnNews();
			 Delay_ms(100);
			 OLED_ShowString(3, 1, News);
			 Serial_RESETI();
			 free(News);//释放空间必须释放否者发生地址紊乱,直接卡机
		}
		Time_Show(time);
		Time_Show_Date(date);
		    
	}

但主程序又不能只执行这一个if语句
那么突破口在哪里呢?我又想到数据传输在蓝牙模块的中断函数里,这个函数很鸡肋每次只能接受一个字节,那么为何不能让他把所有数据都一次性接收呢!这就是突破口。

将数据都存入数组里

c 复制代码
void USART1_IRQHandler(void)//中断函数
{
	
	if (USART_GetITStatus(USART1, USART_IT_RXNE) == SET)
	{
		news[I] = USART_ReceiveData(USART1);//读数据
		Serial_RxFlag = 1;//至标志位为有数据
		I ++;
		USART_ClearITPendingBit(USART1, USART_IT_RXNE);
		 
	}
}

这样就不用被动等待主函数里执行到if语句再一个一个接受数据了!

还需要攻克的难题是如何将此数组返回至主函数

这里创建动态数组返回

c 复制代码
char * Serial_returnNews(void){//返还一个数组
	     char * array;
	     uint8_t i = 0;
	     array = (char *) malloc(sizeof(char) * 100);
	     while(news[i] != '\0'){
				    array[i] = news[i];
				    i ++;
			 }
			 OLED_ShowString(4,1,"array:");
			 OLED_ShowString(4,7,array);
			 return array;
}

主函数用一个char指针接收

但新的诡异问题又出现了,数据接收很正确,但是再连续接受几次数据后程序直接卡死,这让我百思不得其解

问题出在没将创建的动态数组释放掉!!!传入数据导致这些数据占据了大量空间,导致Stm32地址不够用!!因为上述的返回数组函数没办法释放数组,但这不代表在主函数里就不用释放

至此手机向蓝牙传输信息给Stm32的问题全部解决

全部工程代码:

main.c://主函数

c 复制代码
#include "stm32f10x.h"    // Device header
#include "Delay.h"
#include "OLED.h"
#include "Serial.h"
#include "Time.h"
#include "Function.h"
#include <stdio.h>
#include <stdlib.h>


char *News = NULL;//存数据
//uint8_t flag = 1;//标志位
unsigned char time[] = {22, 59, 30};
unsigned int date[] = {2023, 12, 31};
char month[] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
 

void TIM2_IRQHandler(void){//定时器2
	   //主要运用时间更新
	   if(TIM_GetITStatus(TIM2, TIM_IT_Update) == SET){
			  
			 TIM_ClearITPendingBit(TIM2, TIM_IT_Update);//清除标志位
			 Time_Control(time, month, date);
		 }
		  
}
 

 
int main(void){
	 
	OLED_Init();//初始化OLED
  Time_Init();//开启计时器
	Serial_Init();//开启串口
	while(1){ 
		
		if(Serial_GetRxFlag() == 1){
			 News = Serial_returnNews();
			 Delay_ms(100);//等待数据传输完
			 OLED_ShowString(3, 1, News);
			 Serial_RESETI();
			 free(News);//释放空间必须释放否者发生地址紊乱,直接卡机
		}
		Time_Show(time);
		Time_Show_Date(date);
		    
	}
}

Serial.c://串口初始化及传输数据函数等函数

c 复制代码
#include "stm32f10x.h"                  // Device header
#include <stdio.h>
#include <stdarg.h>
#include "Delay.h"
#include <stdlib.h>
#include "OLED.h"

uint8_t Serial_RxData;//存数据
uint8_t Serial_RxFlag;//标志位
GPIO_InitTypeDef GPIO_InitStructu;//GPIO
USART_InitTypeDef USART_InitStructure;//串口
NVIC_InitTypeDef NVIC_InitStructur;//中断
//extern uint8_t flag;//全局定义
char news[100] = "";//存数据
uint8_t I = 0;

void Serial_Init(void)
{
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
	
	 
	GPIO_InitStructu.GPIO_Mode = GPIO_Mode_AF_PP;
	GPIO_InitStructu.GPIO_Pin = GPIO_Pin_9;
	GPIO_InitStructu.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructu);
	
	GPIO_InitStructu.GPIO_Mode = GPIO_Mode_IPU;
	GPIO_InitStructu.GPIO_Pin = GPIO_Pin_10;
	GPIO_InitStructu.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructu);
	
	
	 
	USART_InitStructure.USART_BaudRate = 9600;
	USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
	USART_InitStructure.USART_Mode = USART_Mode_Tx | USART_Mode_Rx;
	USART_InitStructure.USART_Parity = USART_Parity_No;
	USART_InitStructure.USART_StopBits = USART_StopBits_1;
	USART_InitStructure.USART_WordLength = USART_WordLength_8b;
	USART_Init(USART1, &USART_InitStructure);
	
	
	
	 
	USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);
	
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//通道
	
	 
	NVIC_InitStructur.NVIC_IRQChannel = USART1_IRQn;//中断通道
	NVIC_InitStructur.NVIC_IRQChannelCmd = ENABLE;
	NVIC_InitStructur.NVIC_IRQChannelPreemptionPriority = 3;
	NVIC_InitStructur.NVIC_IRQChannelSubPriority = 1;
	NVIC_Init(&NVIC_InitStructur);
	
	USART_Cmd(USART1, ENABLE);
}


uint8_t Serial_GetRxFlag(void)//读取标志位后自动青除
{
	if (Serial_RxFlag == 1)
	{
		Serial_RxFlag = 0;
		return 1;
	}
	return 0;
}

void Serial_GetRxFlag_SET(void){
	   Serial_RxFlag = 1;
}
uint8_t Serial_GetRxData(void)//返回一个字节
{
	return Serial_RxData;
}

/*void Serial_GetNews(char *News){
	   uint32_t i = 0, j = 0;
	   while(j < 10000){//等待中断
	      while(Serial_GetRxFlag() == 1){//查看标志位并清除
					   News[i] =  Serial_GetRxData();//传入数据
					   i ++;
				     j = 0;
					   flag = 0;//标志传入了新数据
		    }
		    j ++;
		 }
}
*/
char * Serial_returnNews(void){//返还一个数组
	     char * array;
	     uint8_t i = 0;
	     array = (char *) malloc(sizeof(char) * 100);
	     while(news[i] != '\0'){
				    array[i] = news[i];
				    i ++;
			 }
			 OLED_ShowString(4,1,"array:");
			 OLED_ShowString(4,7,array);
			 return array;
}

void Serial_RESETI(void){//初始化I
	   I = 0;
}

void USART1_IRQHandler(void)//中断函数
{
	
	if (USART_GetITStatus(USART1, USART_IT_RXNE) == SET)
	{
		news[I] = USART_ReceiveData(USART1);//读数据
		Serial_RxFlag = 1;//至标志位为有数据
		I ++;
		USART_ClearITPendingBit(USART1, USART_IT_RXNE);
		 
	}
}

Serial.h:

c 复制代码
#ifndef __SERIAL_H
#define __SERIAL_H
#include "stm32f10x.h"  
#include <stdio.h>

void Serial_Init(void);
void Serial_SendByte(uint8_t Byte);
void Serial_SendArray(uint8_t *Array, uint16_t Length);
void Serial_SendString(char *String);
void Serial_SendNumber(uint32_t Number, uint8_t Length);
void Serial_Printf(char *format, ...);
uint8_t Serial_GetRxFlag(void);
uint8_t Serial_GetRxData(void);
void Serial_GetRxFlag_SET(void);
//void Serial_GetNews(char *News);
char * Serial_returnNews(void);
void Serial_RESETI(void);

#endif

Time.c://计数器初始化,及显示时间日期等函数

c 复制代码
#include "stm32f10x.h"
#include "OLED.h"

TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
/*初始化通用定时器TIM2*/
void Time_Init(void){
	   RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);//APB1外设开启
	   
	   TIM_InternalClockConfig(TIM2);//选择内部时钟
	
	   /*初始化时基单元*/
	   TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
	   TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;//向上计数
	   TIM_TimeBaseInitStructure.TIM_Period = 10000 - 1;//ARR自动重装
	   TIM_TimeBaseInitStructure.TIM_Prescaler = 7200 - 1;//psc预分频器
	   TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;//高级计时器内容直接给零
	   //记录1s 
	   TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);//刚初始化完就会进中断
	   
	   TIM_ClearFlag(TIM2, TIM_FLAG_Update);//消除中断标志位
	   //使能更新中断
	   TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);
	
	   /*配置中断*/
	   NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//选择组2
	   NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;//定时器2在NVIC内的通道
	   NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
	   NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
	   NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
		 NVIC_Init(&NVIC_InitStructure);
		 
		 TIM_Cmd(TIM2, ENABLE);//启动定时器
}

void Time_Control(unsigned char *time, char *month, unsigned int *date){//更新时间
	       time[2] = time[2] + 1;
        if(time[2] >= 60){
          time[2] = 0;
          time[1] = time[1] + 1;
          if(time[1] >= 60){
            time[1] = 0;
            time[0] = time[0] + 1;
            if(time[0] >= 24){
              time[0] = 0;
              date[2] = date[2] + 1;
   	          if(date[2] >= month[date[1]] + 1){
   		        date[2] = 1;
   		        date[1] = date[1] + 1;
   		        if(date[1] >= 13){
   		        	date[1] = 1;
   		        	date[0] = date[0] + 1;
   		        	if(date[0] >= 9999){
   		        		date[0] = 2023;
					   }
				   }
	           }
            }
         }
       }
				 
				
}

void Time_month2_Control(char *month,unsigned int *date){//判别闰平年 
	if((date[0] % 4 == 0 && date[0] % 100 != 0 )|| date[0] % 400 == 0) month[2] = 29;
	else month[2] = 28;
}

void Time_Show(unsigned char *time){
	   OLED_ShowNum(1,1,time[0], 2);
	   OLED_ShowString(1, 3, ":");
	   OLED_ShowNum(1,4,time[1], 2);
	   OLED_ShowString(1, 6, ":");
	   OLED_ShowNum(1,7,time[2], 2);
}
void Time_Show_Date(unsigned int *date){
	   OLED_ShowNum(2,1,date[0], 4);
	   OLED_ShowString(2, 5, "/");
	   OLED_ShowNum(2,6,date[1], 2);
	   OLED_ShowString(2, 8, "/");
	   OLED_ShowNum(2,9,date[2], 2);
}

Time.h:

c 复制代码
//显示时间&日期
#ifndef __TIME_H
#define __TIME_H
#include "stm32f10x.h"  
#include <stdio.h>

void Time_Init(void);
void Time_Control(unsigned char *time, char *month, unsigned int *date);
void Time_month2_Control(char *month,unsigned int *date);
void Time_Show(unsigned char *time);
void Time_Show_Date(unsigned int *date);
	
#endif

Function.c://需要用到的一些其他函数

c 复制代码
#include "stm32f10x.h"
//一些函数的实现

void Function_ArrayClear(char *News){//恢复数组初始化
	   uint16_t i = 0;
	   for(i = 0; i < 100; i ++) News[i] = '\0';
}

uint8_t Function_ArrayLength(char * a){//计算数组长度函数
	      uint8_t length = 0;
	      uint8_t i = 0;
	      while(a[i] != '\0'){
					i ++;
					length ++;
				}
				return length;
}

uint8_t Check(char *a, uint8_t length){//查数据长度是否符合条件
	      if(length == 5 | length == 10){
					 return 1;
				}
				return 0;
}

Function.h:

c 复制代码
#ifndef __FUNCTION_H
#define __FUNCTION_H
#include "stm32f10x.h"  
#include <stdio.h>

void Function_ArrayClear(char *News);
uint8_t Function_ArrayLength(char * a);
uint8_t Check(char *a, uint8_t length);

#endif

总体效果:







------ 2023/10/16

相关推荐
musir116 分钟前
寄存器控制LED灯亮
单片机
JaneZJW23 分钟前
Proteus仿真——《51单片机AD和DA转换器的设计》
单片机·嵌入式硬件·51单片机·proteus
lee546211 小时前
苹果手机iOS18.2系统&苹果手机便签测评
智能手机
阿隆ALong2 小时前
云手机与Temu矩阵:跨境电商运营新引擎
线性代数·智能手机·矩阵
NEWEVA__zzera223 小时前
利用光耦来隔离485芯片与串口引脚,实现自动收发485电路
单片机·嵌入式硬件
m0_748240543 小时前
STM32第十一课:STM32-基于标准库的42步进电机的简单IO控制(附电机教程,看到即赚到)
stm32·单片机·嵌入式硬件
温柔的男孩像海洋丶3 小时前
vscode的keil assistant 中搜索不到全局变量
ide·vscode·单片机
沐欣工作室_lvyiyi3 小时前
基于单片机的多功能智能小车(论文+源码)
stm32·单片机·嵌入式硬件·毕业设计·单片机毕业设计
鸿喵小仙女4 小时前
C# WPF读写STM32/GD32单片机Flash数据
stm32·单片机·c#·wpf
lucy153027510794 小时前
MCU 功耗基准测试
科技·单片机·嵌入式硬件·智能家居·信号处理·工控主板