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

相关推荐
无人装备硬件开发爱好者43 分钟前
STM32G474 + 1.32 寸 OLED(128×96)俄罗斯方块游戏实现指南
stm32·嵌入式硬件·游戏
三佛科技-134163842121 小时前
SM2850P无电感离线稳压器 5V输出 典型应用电路分析(管脚、关键设计要点)
单片机·嵌入式硬件·物联网·智能家居·pcb工艺
dqsh063 小时前
关于STM32G474芯片有规律的自动重启的问题
stm32·单片机·嵌入式硬件·系统重启·原因解析
时空自由民.3 小时前
BLDC无刷直流电机作为发电机的波形图
单片机
yong99903 小时前
基于 STM32 的 4×4 矩阵键盘源码
stm32·矩阵·计算机外设
JSMSEMI113 小时前
JSM63006 5A 28V三相无刷电机驱动电路
单片机·嵌入式硬件
国产芯片设计3 小时前
【LCD驱动实战】单颗YL1621脚位不足?双芯片联动驱动方案详解
stm32·单片机·mcu·51单片机·硬件工程
bubiyoushang8886 小时前
基于 Freescale S12 单片机的 Bootloader 开发
单片机·嵌入式硬件·mongodb
笨笨小乌龟116 小时前
单片机的半主机模式与 MicroLib 机制(Keil UseMicroLIB)
stm32·单片机·嵌入式硬件
西瓜程序猿9 小时前
手机端现场收礼如何实时投屏?
测试工具·智能手机·创业创新·记了么·电子礼薄