惊帆JF141心率血氧模块简单使用(STM32标准库代码)

2023-10-15 05:30

本文主要是介绍惊帆JF141心率血氧模块简单使用(STM32标准库代码),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

我的那个模组长这个样子

从它的外观上也可以看出,是用下面的这个模块采集心率和血氧数据,并通过上方的串口接口将采集到的数据发送出去的。

让我们看一下产品规格书

和我们的设想是一致的,所以通过此处,大概也就知道了,应该如何配置单片机的串口来实现通信了

这里我用的是PA9和PA10串口1

并且开启接收中断,用于及时的接收数据

void XU_Yang_Init(void)
{//开启GPIOA和USART1的时钟RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);//配置PA9GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9 ;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA,&GPIO_InitStructure);GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10 ;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA,&GPIO_InitStructure);//初始化USART1USART_InitTypeDef USART_InitStructure;USART_InitStructure.USART_BaudRate = 38400;//配置波特率(一般选择9600/115200等常用值)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;//选择数据位数(此处选择的是8位,因为没有校验位(不包含停止位和起始位))USART_Init(USART1,&USART_InitStructure);USART_ITConfig(USART1,USART_IT_RXNE,ENABLE);//使能中断(选择的是接收数据寄存器不为空中断)//配置优先级分组NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//初始化NVICNVIC_InitTypeDef NVIC_Initstructure;NVIC_Initstructure.NVIC_IRQChannel = USART1_IRQn;//选择USART1通道NVIC_Initstructure.NVIC_IRQChannelCmd = ENABLE;//开启选择的IRQn通道NVIC_Initstructure.NVIC_IRQChannelPreemptionPriority = 0;//设置抢占优先级NVIC_Initstructure.NVIC_IRQChannelSubPriority = 0;//响应优先级NVIC_Init(&NVIC_Initstructure);//开启USARTUSART_Cmd(USART1,ENABLE);
}

再往下看就是这个模组怎么用串口使用了,当然就是类似于WIFI模块的AT指令那样,给模组发一个指令来让模组工作或者停止工作。让我们看看产品说明书上怎么写的

看到这里我们也就清楚了,模组的指令分为三种分别是采集、体检、休眠。

在这里我们想要的是简单使用所以只关注最基础的采集指令就可以了。

当然了,笔者在看规格书的时候也顺便看了一点,体检指令就是集中采集数据并上传到云端进行分析,但是云端这个解析并没有在官网上面找到,所以我也就没有过多关注了,休眠功能的话,我建议将采集指令学习明白之后再尝试着加入休眠指令,要不然不就本末倒置了嘛!

我们知道了开关指令,当然就可以通过STM32的串口来发送指令达到效果啦!

需要指出的是在实际使用中虽然模组能近乎百分之百的响应指令,但是发送指令之后建议延时600ms左右(可以直接写到XU_Yang_SendByte(uint8_t Byte)函数中,笔者之前忘记了,大家自己添加一下吧),等待模组进入工作状态再进行读取

void XU_Yang_SendByte(uint8_t Byte)
{USART_SendData(USART1,Byte);while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);/*发送数据寄存器空标志位;若为RESET则发送数据寄存器非空,此时不能继续向该寄存器继续写数据,如果发送数据寄存器非空继续写数据,就会覆盖之前的数据导致发送的数据丢失一部分;*/
}
uint8_t XU_Yang_GetRxFlag(void)
{if(XU_Yang_RxFlag==1){return 1;}return 0;
}

好了言归正传既然我们知道了采集的开关指令,那么我们怎么知道接收的哪些是我们想要的心率和血样数据呢?

最简单的办法当然就是继续看产品规格书啦!

从中我们就可以了解到采集开启之后,模组会通过串口发送以0xFF为包头的实时数据包,里面不仅包括了心率和血氧数据,也包括了心跳的原始数据和微循环等数据,我们的目的是读取心率和血氧,所以重点读取这两个数据就可以了。

所以我们既然知道了接受的数据长什么样子,需要的数据在哪个位置,那么我们就可以来编写接收中断函数了。

这段代码的思路是检测到包头开始接受,接收到最大数量重新开始检测包头,如此循环接收数据并将数据放在数组里面方便调用。

串口代码是参考江科大的,所以也用了状态机的思路。

从产品规格书上我们可以看到,总共76个字节,但我们需要的只有仅仅两个字节,所以我就想着定义一个只有两个字节的数组直接用于接收需要的数据,但是使用的时候发现,干扰数据有点多,所以又选取了前两个心跳数据来用于数据有效性检测。当然这可能不是必须的,大家也不必在这里纠结过多。

//在中断实现接收一个HEX数据包
void USART1_IRQHandler(void)
{static uint8_t RxState;//用来记录状态数据static uint8_t pRxPacket;//用来记录接收了几个数据if (USART_GetITStatus(USART1, USART_IT_RXNE) == SET)//再次确认标志位是否正确置位{uint8_t RxData = USART_ReceiveData(USART1);//读取接收数据寄存器中的数据,存储到uint8_t RxData中if(RxState == 0)//状态零(接收包头){if(RxData == 0xFF){RxState = 1;pRxPacket=2;}}else if(RxState == 1)//状态1(接收数据){	if(pRxPacket>=66 && pRxPacket<68){//将接收到的心率和血氧数据放在数组后两位XU_Yang_RxPacket[pRxPacket-64]=RxData;pRxPacket++;}else if(pRxPacket<2){//将接收到的监测数据有效性的数据放在数组前两位XU_Yang_RxPacket[pRxPacket]=RxData;pRxPacket++;}else if(pRxPacket>=2 && pRxPacket<66){pRxPacket++;}else if(pRxPacket>=68){pRxPacket=0;RxState=0;XU_Yang_RxFlag = 1;//将接收信号位置1,代表数据接收完成,可以读取}}USART_ClearITPendingBit(USART1, USART_IT_RXNE);//清除接收数据寄存器非空标志位}
}

到这里,我们从中断中就接收到了需要的数据,但是这些数据并不能直接使用,因为当你不放置手指时此时数据肯定是无效的,尽管你的手指放在上面,由于各种因素刚开始产生的数据当然也是我们不想要看到的,大家可以尝试一个下,用串口模块连接血氧模块,进行测试,会发现没有放手指时,心跳数据一般都是0XC4,当放置手指之后,按照规格书的说明读取对应位的数据也会发现这些数据有些违背常识。我们一般来说写一个驱动函数的目的就是方便主函数的调用,最好是那种,主函数一调用,就能知道准确的数据。所以以此想法为思路,我们便可以写一下我们的读取函数了。

首先就是读取数据的间隔,我们通过上一个图可以看出,实时数据包每隔1.64s发送一次,所以理论上我们对就收数据的调用间隔也应该是1.64s,但是我习惯让单片机找点事情做,所以就没有通过延时调整时间间隔,而是让单片机一直筛选数据,(当然了可能也有弊端,比如接收的数据只接收了前两位,然后刚好单片机新一轮的筛选数据开始进行,就会导致,前两位是这一次的数据,后两位是上一次的数据,就会导致这一次的数据被当成错误的给筛除掉,当然了我觉得我这样做的也是有好处的,既然正确的都能给筛除掉,那我读到数据的时候自然就是模组稳定之后的数据啦!哈哈哈哈)

说一下我筛出数据的思路:就是通过判断数组的前两位和后两位的约束条件判断是否采集成功。

前两位为心跳数据,当为0xC4时可以判定为采集失败,当后两位心率和血氧数据不在正常的范围同理也可以判断为采集失败,

在下面的代码中也可以看到我里面将发送开始与结束采集的命令注释掉了,因为本来想着读完数据之后就结束采集,但是,发现频繁的开关导致模组反应不及时,所以索性,就在主函数控制开启和关闭采集了。

/*** @brief       过滤无效数据,并返回数据是否采集成功(无效数据过多,代表采集失败,最多等待200次数据包),成功采集一次就跳出采集程序                * @param       无                  
* @retval       1 数据采集成功;0数据采集失败         */uint8_t XU_Yang_Receive(void)
{uint8_t  num=0;//接收到几个数据包
//	XU_Yang_SendByte(0x8A);//开始采集while(1){if(XU_Yang_GetRxFlag()==1){
////过滤无效数据if(XU_Yang_RxPacket[0]==0xC4 | XU_Yang_RxPacket[1]==0xC4 | (XU_Yang_RxPacket[2]<60 | XU_Yang_RxPacket[2]>130) | (XU_Yang_RxPacket[3]<80 | XU_Yang_RxPacket[3]>99) ){//数据无效,将无效数据清除for(uint8_t i=0;i<4;i++){XU_Yang_RxPacket[i]=0;}num++;//失败次数过多,采集失败if(num >200){delay_ms(10);//两次采集时间间隔过短导致了一直采集失败//原因未知//num=0;
//				XU_Yang_SendByte(0x88);//结束采集return 0;}}else//采集成功{num=0;
//			XU_Yang_SendByte(0x88);//结束采集return 1;}}}}

模块文件写好之后,主函数就简单多了

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "XU_Yang.h"
#include "Key.h"
uint8_t n;
//uint32_t u=0;
uint16_t s=650;
int main()
{/*现象:将手指放上去,刚开始时显示失败,等待大约15s左右,显示采集成功,采集成功后,数据更新也会加快,*/OLED_Init();XU_Yang_Init();OLED_ShowString(1,1,"RxPacket:");OLED_ShowString(2,1,"000ci   00%");XU_Yang_SendByte(0x8A);//开始采集while(1){while(s--){n = XU_Yang_Receive();}if(n==1){OLED_ShowString(3,1,"      ");OLED_ShowNum(2,1,XU_Yang_RxPacket[2],3);XU_Yang_RxPacket[2] = 0;OLED_ShowNum(2,9,XU_Yang_RxPacket[3],3);XU_Yang_RxPacket[3] = 0;OLED_ShowString(3,1,"Right!");}else if(n==0){OLED_ShowNum(2,1,0,3);OLED_ShowNum(2,9,0,3);OLED_ShowString(3,1,"      ");OLED_ShowString(3,1,"Lose!");}}
}​

下面时实际效果,用到的工程代码,和产品规格书我都会上传上去,有需要请自取哈

这篇关于惊帆JF141心率血氧模块简单使用(STM32标准库代码)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/215629

相关文章

Python Transformers库(NLP处理库)案例代码讲解

《PythonTransformers库(NLP处理库)案例代码讲解》本文介绍transformers库的全面讲解,包含基础知识、高级用法、案例代码及学习路径,内容经过组织,适合不同阶段的学习者,对... 目录一、基础知识1. Transformers 库简介2. 安装与环境配置3. 快速上手示例二、核心模

解决Maven项目idea找不到本地仓库jar包问题以及使用mvn install:install-file

《解决Maven项目idea找不到本地仓库jar包问题以及使用mvninstall:install-file》:本文主要介绍解决Maven项目idea找不到本地仓库jar包问题以及使用mvnin... 目录Maven项目idea找不到本地仓库jar包以及使用mvn install:install-file基

Python正则表达式语法及re模块中的常用函数详解

《Python正则表达式语法及re模块中的常用函数详解》这篇文章主要给大家介绍了关于Python正则表达式语法及re模块中常用函数的相关资料,正则表达式是一种强大的字符串处理工具,可以用于匹配、切分、... 目录概念、作用和步骤语法re模块中的常用函数总结 概念、作用和步骤概念: 本身也是一个字符串,其中

Python使用getopt处理命令行参数示例解析(最佳实践)

《Python使用getopt处理命令行参数示例解析(最佳实践)》getopt模块是Python标准库中一个简单但强大的命令行参数处理工具,它特别适合那些需要快速实现基本命令行参数解析的场景,或者需要... 目录为什么需要处理命令行参数?getopt模块基础实际应用示例与其他参数处理方式的比较常见问http

Python中的getopt模块用法小结

《Python中的getopt模块用法小结》getopt.getopt()函数是Python中用于解析命令行参数的标准库函数,该函数可以从命令行中提取选项和参数,并对它们进行处理,本文详细介绍了Pyt... 目录getopt模块介绍getopt.getopt函数的介绍getopt模块的常用用法getopt模

C 语言中enum枚举的定义和使用小结

《C语言中enum枚举的定义和使用小结》在C语言里,enum(枚举)是一种用户自定义的数据类型,它能够让你创建一组具名的整数常量,下面我会从定义、使用、特性等方面详细介绍enum,感兴趣的朋友一起看... 目录1、引言2、基本定义3、定义枚举变量4、自定义枚举常量的值5、枚举与switch语句结合使用6、枚

Java的栈与队列实现代码解析

《Java的栈与队列实现代码解析》栈是常见的线性数据结构,栈的特点是以先进后出的形式,后进先出,先进后出,分为栈底和栈顶,栈应用于内存的分配,表达式求值,存储临时的数据和方法的调用等,本文给大家介绍J... 目录栈的概念(Stack)栈的实现代码队列(Queue)模拟实现队列(双链表实现)循环队列(循环数组

使用Python从PPT文档中提取图片和图片信息(如坐标、宽度和高度等)

《使用Python从PPT文档中提取图片和图片信息(如坐标、宽度和高度等)》PPT是一种高效的信息展示工具,广泛应用于教育、商务和设计等多个领域,PPT文档中常常包含丰富的图片内容,这些图片不仅提升了... 目录一、引言二、环境与工具三、python 提取PPT背景图片3.1 提取幻灯片背景图片3.2 提取

使用Python实现图像LBP特征提取的操作方法

《使用Python实现图像LBP特征提取的操作方法》LBP特征叫做局部二值模式,常用于纹理特征提取,并在纹理分类中具有较强的区分能力,本文给大家介绍了如何使用Python实现图像LBP特征提取的操作方... 目录一、LBP特征介绍二、LBP特征描述三、一些改进版本的LBP1.圆形LBP算子2.旋转不变的LB

Maven的使用和配置国内源的保姆级教程

《Maven的使用和配置国内源的保姆级教程》Maven是⼀个项目管理工具,基于POM(ProjectObjectModel,项目对象模型)的概念,Maven可以通过一小段描述信息来管理项目的构建,报告... 目录1. 什么是Maven?2.创建⼀个Maven项目3.Maven 核心功能4.使用Maven H