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

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

测试代码:

#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);//选择组2NVIC_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();//初始化OLEDTimer_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语句的时候是不会接受数据的

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语句
那么突破口在哪里呢?我又想到数据传输在蓝牙模块的中断函数里,这个函数很鸡肋每次只能接受一个字节,那么为何不能让他把所有数据都一次性接收呢!这就是突破口。

将数据都存入数组里

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语句再一个一个接受数据了!

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

这里创建动态数组返回

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://主函数

#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();//初始化OLEDTime_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://串口初始化及传输数据函数等函数

#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){//初始化II = 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:

#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://计数器初始化,及显示时间日期等函数

#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);//选择组2NVIC_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:

//显示时间&日期
#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://需要用到的一些其他函数

#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:

#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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/139143.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

索尼PS5 Pro将搭载RDNA 3定制GPU 拥有30个WGP/60个CU,频率更高

此前有报道称&#xff0c;PlayStation 5 Pro已进入开发最后阶段&#xff0c;其内部代号为“Trinity”&#xff0c;搭载的SoC对应代号为“Viola”&#xff0c;遵循了索尼使用《黑客帝国》人物名字作为代号的做法。 近日&#xff0c;RedGamingTech透露&#xff0c;索尼PlayStatio…

vscode配置c++和opencv环境

因为想要用c刷题&#xff0c;但是之前的vs被重装的时候删除了&#xff0c;DEVc实在是不好看的界面&#xff0c;于是就想起了之前写html的vscode&#xff0c;没想到配置环境花了一整天&#xff0c;还总是报错&#xff0c;也许是电脑配置不一样&#xff0c;所以就出了问题吧&…

ubuntu20.04 nerf开山之作

源码 GitHub - yenchenlin/nerf-pytorch: A PyTorch implementation of NeRF (Neural Radiance Fields) that reproduces the results. 代码的相关解读 NeRF代码解读-相机参数与坐标系变换 - 知乎 原文题目&#xff1a;NeRF: Representing Scenes as Neural Radiance Field…

leetcode-200. 岛屿数量

1. 题目 leetcode题目链接 2. 解答 思路&#xff1a; 需要循环遍历每个节点&#xff1b;找到陆地&#xff0c;基于陆地开始遍历陆地的上下左右&#xff1b;数组dirm dirn就可以表示某个区域的上下左右&#xff1b;标记遍历过的节点&#xff1b;设计循环的退出条件&#xf…

【Django 01】环境搭配与项目配置

1. 介绍 https://github.com/Joe-2002/sweettalk-django4.2#readme Django 是一个使用 Python 编写的开源 Web 应用程序框架&#xff0c;它提供了一套用于快速开发安全、 可扩展和高效的 Web 应用程序的工具和功能。Django 基于 MVC&#xff08;Model-View-Controller&#xf…

Windows工业三防平板全功能NFC近距离感应一维/二维扫描

Windows系统工业三防平板电脑是一种在智慧工厂仓储物流、MES数采、车载设备、设备检测、自动化控制等领域广泛应用的先进设备。此外&#xff0c;它还在公共服务领域&#xff0c;如高速交通、物流运输、电力检测、公务执法、银行金融、船舶装备、户外勘测、建筑工程、汽车检测、…

使用vscode + vite + vue3+ element3 搭建vue3脚手架

技术栈 开发工具&#xff1a;VSCode 代码管理&#xff1a;Git 前端框架&#xff1a;Vue3 构建工具&#xff1a;Vite 路由&#xff1a;vue-router 状态管理&#xff1a;vuex AJAX&#xff1a;axios UI库&#xff1a;element-ui 3 数据模拟&#xff1a;mockjs css预处理&#xf…

0基础学习VR全景平台篇 第108篇:全景图细节处理(下,航拍)

上课&#xff01;全体起立~ 大家好&#xff0c;欢迎观看蛙色官方系列全景摄影课程&#xff01; &#xff08;调色前图库&#xff09; &#xff08;原图-大图&#xff09; 一、导入文件 单击右下角导入按钮&#xff0c;选择航拍图片所在文件夹&#xff0c;选择图片&#xff0…

零基础学习HTML5(列表、表格、表单)

01-列表 作用&#xff1a;布局内容排列整齐的区域。 列表分类&#xff1a;无序列表、有序列表、定义列表。 无序列表 作用&#xff1a;布局排列整齐的不需要规定顺序的区域。 标签&#xff1a;ul 嵌套 li&#xff0c;ul 是无序列表&#xff0c;li 是列表条目。 <ul>…

01. 汇编LED驱动实验

01. 汇编LED驱动实验 汇编原理分析为什么要学习Cortex—A汇编STM32IO初始化流程IMX6UL初始化流程 汇编基础处理器内部数据传输指令存储器访问指令 编写驱动编译程序烧写bin文件 汇编原理分析 为什么要学习Cortex—A汇编 需要用汇编初始化一些SOC外设使用汇编初始化DDR&#x…

【mfc/VS2022】计图实验:绘图工具设计知识笔记2

按钮添加处理程序 1.类视图找到对应类右击&#xff0c;类向导 2. 找到对应的的按钮id 如何将画出的两个相交的圆都显示出来&#xff0c;而不是重叠&#xff08;如下图&#xff09;隐藏了一条圆弧 问题如图&#xff1a; 因为矩形和圆心其实是个背景色的封闭图形&#xff0c;所…

如何修复-谷歌浏览器-打开任何一个网页都显示崩溃

早上上班&#xff0c;一如既往的使用谷歌浏览器时&#xff0c;发现异常。 首先是&#xff0c;右下角有个弹窗提示某某插件需要点击更新&#xff0c;然后点了也没用&#xff0c;然后就是打开任何网页都提示‘喔唷 崩溃了’的字眼。 奇怪呀&#xff0c;咱也是第一次碰见这种问题…