摘 要
生物医学信号指标与人体的健康息息相关,只有掌握其中规律才能更好的解决人体的健康问题。随着电子信息技术的发展和医学的不断进步,人们对高精度便捷式生物医学信号电子检测设备的需求越来越高。而心率和体温又是人体的两个重要指标,基于此原因,本次综合设计按照现在发展的需要设计一款基于51单片机的心率体温计,主要用来监测人体的心率和体温。
本文提出了基于51的单片机的具有实时检测脉搏心率和体温的实用型脉搏心率、体温计。从可实现性和经济性的方面考虑,决定采用STC89C52单片机作为设计的主控芯片,使用心率传感器进行心率的采集,单片机通过对脉冲累加得到脉搏跳动次数,并且通过采用DS18B20温度传感器对当前体温的采集。采集信号经过单片机处理后输出给LCD1602显示模块实时显示当前心率和体温。通过播报按键进行对当前心率和体温的播报以及通过HC05蓝牙模块无线远程发送数据到手机端进行显示。经测试,系统工作正常,达到设计要求。
关键词: STC89C52单片机;心率体温;蓝牙模块;语音播报
1. 设计任务
1.1 任务描述
1.2 技术指标
1.3 难点分析
2. 程序代码如下
1. 设计任务
针对人体的生物医学信号体温、心率的检测,本文设计了一款能够测量脉搏心率、体温的简单实用型脉搏心率、体温计,能够居家测量自己的身体健康情况。
1.1 任务描述
设计一个具有实时检测脉搏心率和体温的实用型脉搏心率、体温计,设计要求具体如下:
1、能够测量人体1分钟的脉搏心率数,并以数字显示;
2、测量脉搏心率范围:50-180次/分,数据刷新时间< 3秒;
3、能够检测体温,检测范围为35°C-45°C,用数字显示,示数分辨率0.1°C。
4、设置脉搏心率上下限,当超过范围区间,蜂鸣器报警。
5、实现语音报数功能。
6、能够将数据定时上传给社区医生。
1.2 技术指标
评估该脉搏心率、体温计的好坏有以下指标:
1、单位时间内的脉搏心率数;
2、脉搏心率检测值的范围;
3、体温的范围;
4、脉搏心率测量值的刷新时间;
5、测得体温值的精确度;
6、按键调节脉搏心率上下限的响应速度。
1.3 难点分析
在设计实时脉搏心率、体温计过程中,遇到一些难点如下:
1、蓝牙模块HC-05和语音模块SYN6288都是采用TX和RX进行与STC89C52单片机数据传输,它们都是用串口发送,进行数据上传和语音播报。一开始,我通过串口定时发送数据通过蓝牙发送到手机端APP,在连上SYN6288语音模块后,发现播报总是播报到一半就重复下一个。之后我考虑在脉搏心率上下限的按键的基础上,增加了P1.3的按键来控制播报和数据上传,从而有效的解决这个问题。
2、数据上传手机端,开始我们的方案是采用ESP-8266 WIFI模块进行将51单片机的心率、体温数据上传到手机端APP,但是我们在对ESP-8266 WIFI模块进行AT测试的时候正常进行,可是通过手机APP与WIFI模块进行连接时,总是连接后几秒后断开。在我们小组商量考虑下,我们选择了蓝牙模块HC-05,我们对其也同样进行AT测试都非常成功,最终通过手机端APP对其进行蓝牙连接,我们测量的体温、脉搏心率数据成功地实时发送到手机端APP。
3、Keil程序的烧录以及可能在调试过程中出现的异常报错也是我们在设计过程中遇到的难点。
2 程序代码
2.1 user.c
#include <reg52.h>
#include "common.h"
#include "ADC0832.h"
#include "lcd1602.h"
#include "uart.h"
#include "DS18B20_One_1.h"
#define uchar unsigned char //宏定义"uchar"代替"unsigned char"。
#define uint unsigned int //宏定义"uint"用来定义无符号整型数。
#define false 0
#define true 1
//按键引脚定义
sbit key1 = P1^0;
sbit key2 = P1^1;
sbit key3 = P1^2;
sbit key4 = P1^3;
sbit BEEP = P2^0;//蜂鸣器
/*这些变量是不稳定的,因为它们在中断服务例程中使用!*/
volatile unsigned int BPM; // 用于保持脉心率
volatile unsigned long Signal; // 保存传入的原始数据
volatile unsigned int IBI = 600; // 保存心跳的之间的时间
volatile bit Pulse = false; // 脉搏波高时为真,脉搏波低时为假
volatile bit QS = false; // 当单片机识别到心跳,QS=1
volatile int rate[10];
volatile unsigned long sampleCounter = 0; // 用于确定脉冲定时
volatile unsigned long lastBeatTime = 0; // 用来寻找IBI
volatile int Peak =512; //用于在脉冲中寻找峰值
volatile int Trough = 512; // 用于在脉冲波中寻找波谷
volatile int thresh = 512; // 用于找瞬间瞬间的心跳
volatile int amp = 100; // 用于保持脉冲波形的振幅
volatile bit firstBeat = true;
volatile bit secondBeat = false; // 用于我们设置的合理的BPM速率阵列
bit shuaxin=0;
uchar heartrate = 0;
uchar heartrateMax = 150,heartrateMin = 60;//心率上下限
//short temperature = 0;//体温值
//short temperatureMax = 373,temperatureMin = 360;//体温上下限值
char SEND_BUF[]="心率:000,体温:00.0°
";
unsigned char setn = 0;
void Timer_Init(void) //定时器初始化,定时50ms
{
TMOD |= 0x11; //定时器0、定时器1工作方式1
TL0 = 0xCD; //2ms
TH0 = 0xF8;
TR0 = 1;
EA= 1;
ET0 = 1;
TL1 = 0x00; //20ms
TH1 = 0xB8;
TR1 = 1;
ET1 = 1;
}
void display_set_value()//显示设置的上下限
{
if(setn == 1)//心率上下限显示
{
lcd_write_char(3,1,heartrateMin/100+'0');
lcd_write_char(4,1,heartrateMin%100/10+'0');
lcd_write_char(5,1,heartrateMin%10+'0');
lcd_write_char(13,1,heartrateMax/100+'0');
lcd_write_char(14,1,heartrateMax%100/10+'0');
lcd_write_char(15,1,heartrateMax%10+'0');
lcd_write_com(0x80+0x40+5);//心率下限位置
lcd_write_com(0x0f);//光标闪烁
}
else if(setn == 2)//心率上下限显示
{
lcd_write_char(3,1,heartrateMin/100+'0');
lcd_write_char(4,1,heartrateMin%100/10+'0');
lcd_write_char(5,1,heartrateMin%10+'0');
lcd_write_char(13,1,heartrateMax/100+'0');
lcd_write_char(14,1,heartrateMax%100/10+'0');
lcd_write_char(15,1,heartrateMax%10+'0');
lcd_write_com(0x80+0x40+15);//心率上限位置
lcd_write_com(0x0f);//光标闪烁
}
else if(setn == 3)//温度下限
{
lcd_write_char(3,1,temperatureMin/100+'0');
lcd_write_char(4,1,temperatureMin%100/10+'0');
lcd_write_char(6,1,temperatureMin%10+'0');
lcd_write_char(12,1,temperatureMax/100+'0');
lcd_write_char(13,1,temperatureMax%100/10+'0');
lcd_write_char(15,1,temperatureMax%10+'0');
lcd_write_com(0x80+0x40+6);//温度下限位置
lcd_write_com(0x0f);//光标闪烁
}
else if(setn == 4)//温度上限
{
lcd_write_char(3,1,temperatureMin/100+'0');
lcd_write_char(4,1,temperatureMin%100/10+'0');
lcd_write_char(6,1,temperatureMin%10+'0');
lcd_write_char(12,1,temperatureMax/100+'0');
lcd_write_char(13,1,temperatureMax%100/10+'0');
lcd_write_char(15,1,temperatureMax%10+'0');
lcd_write_com(0x80+0x40+15);//温度上限位置
lcd_write_com(0x0f);//光标闪烁
}
}
void keyscan()//按键扫描
{
if(key1 == 0)//加键按下
{
delay_ms(10);//消抖
if(key1 == 0)
{
while(key1 == 0);
setn++;
if(setn > 2)//按下次数超过2次,退出设置
{
setn = 0;
lcd_write_com(0x0C);
lcd_write_str(0, 0, " Heartrate:000 ");
lcd_write_str(0, 1, " Temp:00.0 C ");
lcd_write_char(11,1,0xdf);
}
if(setn == 1)//显示设置心率
{
lcd_write_str(0, 0, " SET THE HEART ");
lcd_write_str(0, 1, "XL:000 XH:000");
}
if(setn == 3)//显示设置温度
{
lcd_write_str(0, 0, " SET THE TEMP ");
lcd_write_str(0, 1, "TL:00.0 TH:00.0");
lcd_write_char(10,1,0xdf);
}
display_set_value();
}
}
if(key2 == 0)//加键按下
{
delay_ms(100);//消抖
if(key2 == 0)
{
if(setn == 1)
{
if(heartrateMax-heartrateMin>1)heartrateMin++;//上限必须大于下限,下限才能加
display_set_value();//显示
}
if(setn == 2)
{
if(heartrateMax<120)heartrateMax++;//心率上限最大到120
display_set_value();
}
if(setn == 3)
{
if(heartrateMax-heartrateMin>1)temperatureMin++;//上限必须大于下限,下限才能加
display_set_value();
}
if(setn == 4)
{
if(temperatureMax<400)temperatureMax++;//温度上限最大为400
display_set_value();
}
}
}
if(key3 == 0)//减键按下
{
delay_ms(100);
if(key3 == 0)
{
if(setn == 1)
{
if(heartrateMin>0)heartrateMin--;//心率下限最小到0
display_set_value();//显示
}
if(setn == 2)
{
if(heartrateMax-heartrateMin>1)heartrateMax--;//上限必须大于下限,上限才能减
display_set_value();
}
if(setn == 3)
{
if(temperatureMin>0)temperatureMin--;//温度下限最小到0
display_set_value();//显示
}
if(setn == 4)
{
if(temperatureMax-temperatureMin>1)temperatureMax--;//上限必须大于下限,上限才能减
display_set_value();//显示
}
}
}
}
void main()
{
uint count=0;
char SEND_BUF[]="心率:000
";
lcd_init();//1602初始化
Timer_Init();//定时器初始化
lcd_write_str(0, 0, " Heartrate:000 ");
lcd_write_str(0, 1, " Temp:00.0 C ");
lcd_write_char(11,1,0xdf);
uart_init(9600);
while(1)
{
keyscan();//按键扫描
if(setn == 0)
{
if(count++>=1000)
{
count=0;
//显示心率体温值
lcd_write_char(12,0,heartrate/100+'0');
lcd_write_char(13,0,heartrate%100/10+'0');
lcd_write_char(14,0,heartrate%10+'0');
lcd_write_char(7,1,temperature/100+'0');
lcd_write_char(8,1,temperature%100/10+'0');
lcd_write_char(10,1,temperature%10+'0');
}
if(((heartrate<=heartrateMin||heartrate>=heartrateMax)&&heartrate!=0))//心率温度超限报警
{
BEEP = 1;
}
else
{
BEEP = 0;
}
}
if(key4 == 0)
{
lcd_write_char(12,0,heartrate/100+'0');
lcd_write_char(13,0,heartrate%100/10+'0');
lcd_write_char(14,0,heartrate%10+'0');
lcd_write_char(7,1,temperature/100+'0');
lcd_write_char(8,1,temperature%100/10+'0');
lcd_write_char(10,1,temperature%10+'0');
SEND_BUF[5]=heartrate/100+'0';
SEND_BUF[6]=heartrate%100/10+'0';
SEND_BUF[7]=heartrate%10+'0';
SEND_BUF[14]=temperature/100+'0';
SEND_BUF[15]=temperature%100/10+'0';
SEND_BUF[17]=temperature%10+'0';
uart_send(SEND_BUF,25);//串口发送
delay_ms(2500);
}
delay_ms(1);
}
}
// Timer 0中断子程序,每2MS中断一次,读取AD值,计算心率值
void Timer0_rountine(void) interrupt 1
{
int N;
unsigned char i;
// 保持最近10个IBI值的运行总数
unsigned int runningTotal = 0; //清除runningTotal变量
TL0 = 0xCD; //2ms
TH0 = 0xF8;
Signal = Read_ADC0832(0)<<2; // 读取心率传感器数据
sampleCounter += 2; // 用这个变量记录mS的时间
N = sampleCounter - lastBeatTime; // 计算出自上一节拍以来的时间
// 找出脉冲波的波峰和波谷
if(Signal < thresh && N > (IBI/5)*3){
// 通过等待最后一个IBI的3/5来避免二色噪声
if (Signal < Trough){
// T是波谷
Trough = Signal; // 跟踪脉波的最低点
}
}
if(Signal > thresh && Signal > Peak){
Peak = Signal; // P是峰值
} // 跟踪脉波的最高点
// 每当有脉冲时,信号的值就会激增
if (N > 250){
// 避免高频噪音
if ( (Signal > thresh) && (Pulse == false) && (N > (IBI/5)*3) ){
Pulse = true; // 当我们认为有脉搏时设置脉冲标志
IBI = sampleCounter - lastBeatTime;
lastBeatTime = sampleCounter;
if(secondBeat){
// 如果这是第二个节拍,如果secondBeat==1
secondBeat = false; // 清空
for(i=0; i<=9; i++){
rate[i] = IBI;
}
}
if(firstBeat){
// 如果这是我们第一次找到一个节拍,如果firstBeat == TRUE
firstBeat = false; // 清空
secondBeat = true; // 设置第二个节拍
EA=1; // 再次启用中断
return; // IBI值是不可靠的,丢弃
}
for(i=0; i<=8; i++){
// 在速率数组中移位数据
rate[i] = rate[i+1]; // 去掉最古老的IBI值
runningTotal += rate[i]; // 将9个最古老的IBI值相加
}
rate[9] = IBI; // 向速率阵列添加最新的IBI
runningTotal += rate[9]; // 将最新的IBI添加到runningTotal
runningTotal /= 10; // 计算最后10个IBI值的平均值
BPM = 60000/runningTotal; // 一分钟能有多少节拍?那是BPM !
if(BPM<=150 && BPM>=60) //限制BPM最高显示值,最低显示值
{
heartrate = BPM;
}
else heartrate=0;
QS = true; // 设置QS=1
}
}
if (Signal < thresh && Pulse == true){
// 当数值下降时,节拍就结束了
Pulse = false; // 重置Pulse标志,这样我们就可以再做一次
amp = Peak - Trough; // 得到脉冲波的振幅
thresh = amp/2 + Trough; // 设置阈值为振幅的50%
Peak = thresh; // 下次重新设置
Trough = thresh;
}
if (N > 2500){
// 如果2.5秒没有反应
thresh = 512; // 设置t默认
Peak = 512; // 设置P默认
Trough = 512; // 设置T默认
lastBeatTime = sampleCounter; // 将lastBeatTime更新到最新
firstBeat = true; //设置这些来避免噪音
secondBeat = false;
}
}
void Timer1_rountine(void) interrupt 3
{
static uchar count2=0;
TL1 = 0x00; //20ms
TH1 = 0xB8;
if (QS == true){
QS = false; // 下一次重置QS标志
}
if(count2++>=25)//500ms读取一次温度
{
count2 = 0 ;
temperature = DS18B20_TF()*10;//获取温度
}
}
2.2 ADC0832.c
#include"ADC0832.h"
#define uchar unsigned char
#define uint unsigned int
sbit ADC0832_CS =P3^5; //将CS位定义为P3.5引脚
sbit ADC0832_CLK=P3^6; //将CLK位定义为P3.6引脚
sbit ADC0832_DIO=P3^7; //将DIO位定义为P3.7引脚
/********************************************************************
* 名称 : dRead_ADC0832()
* 功能 : 将模拟信号转换成数字信号
* 输入 : Ch-通道选择
* 输出 : dat-转换值
*********************************************************************/
unsigned char Read_ADC0832(unsigned char Ch)
{
unsigned char i,dat; //定义变量
ADC0832_CS=1; //一个转换周期开始
ADC0832_CLK=0; //为第一个脉冲作准备
ADC0832_CS=0; //CS置0,片选有效
ADC0832_DIO=1; //DIO置1,规定的起始信号
ADC0832_CLK=1; //第一个脉冲
ADC0832_CLK=0; //第一个脉冲的下降沿,此前DIO必须是高电平
ADC0832_DIO=1; //DIO置1, 通道选择信号
ADC0832_CLK=1; //第二个脉冲,第2、3个脉冲下沉之前,DI必须跟别输入两位数据用于选择通道,这里选通道CH0
ADC0832_CLK=0; //第二个脉冲下降沿
ADC0832_DIO=Ch; //DI置0,选择通道0
ADC0832_CLK=1; //第三个脉冲
ADC0832_CLK=0; //第三个脉冲下降沿
ADC0832_DIO=1; //第三个脉冲下沉之后,输入端DIO失去作用,应置1
ADC0832_CLK=1; //第四个脉冲
for(i=0;i<8;i++) //高位在前
{
ADC0832_CLK=1; //第四个脉冲
ADC0832_CLK=0;
dat<<=1; //将下面储存的低位数据向右移
dat|=(unsigned char)ADC0832_DIO; //将输出数据DIO通过或运算储存在dat最低位
}
ADC0832_CS=1; //片选无效
return dat; //将读书的数据返回
}
2.3 lcd1602.c
#include <reg52.h>
#include <intrins.h>
#include "lcd1602.h"
#include "common.h"
sbit RS = P2^3;
sbit RW = P2^4;
sbit E = P2^5;
#define DataPort P0
void lcd_write_com(unsigned char byte)//写指令
{
RS = 0;
RW = 0;
E = 1;
DataPort = byte;
_nop_();
_nop_();
delay_ms(1);
E = 0;
}
void lcd_write_data(unsigned char byte)//写数据
{
RS = 1;
RW = 0;
E = 1;
DataPort = byte;
_nop_();
_nop_();
delay_ms(1);
E = 0;
}
void lcd_init()//1602初始化
{
delay_ms(5);
lcd_write_com(0x38);//设置16*2显示,5*7点阵,8位数据接口
delay_ms(5);
lcd_write_com(0x38);//设置16*2显示,5*7点阵,8位数据接口
delay_ms(5);
lcd_write_com(0x38);//设置16*2显示,5*7点阵,8位数据接口
delay_ms(5);
lcd_write_com(0x08); //关闭显示
delay_ms(5);
lcd_write_com(0x01);//清屏指令
delay_ms(5);
lcd_write_com(0x06);
delay_ms(5);
lcd_write_com(0x0c);
delay_ms(5);
}
void lcd_write_char(unsigned char x,unsigned char y,unsigned char byte)//在某一位置显示一字符
{
if(0 == y)
{
lcd_write_com(0x80 + x);
}
else if(1 == y)
{
lcd_write_com(0x80 + 0x40 + x);
}
lcd_write_data(byte);
}
void lcd_write_str(unsigned char x, unsigned char y, unsigned char *s)//在某一位置显示一字符串
{
if(0 == y)
{
lcd_write_com(0x80 + x);
}
else
{
lcd_write_com(0x80 + 0x40 + x);
}
while(*s)
{
lcd_write_data(*s);
s++;
}
}
2.4 uart.c
#include "reg52.h"
#include "common.h"
#include "uart.h"
/*定义串口模式*/
#define NONE_PARITY 0
#define ODD_PARITY 1
#define EVEN_PARITY 2
#define MARK_PARITY 3
#define SPACE_PARITY 4
#define PARITYBIT NONE_PARITY
//发送忙标志
bit busy;
//串口结构体变量
COMx_Define COM1;
//接收缓冲区变量
unsigned char xdata RX1_Buffer[COM_RX1_Lenth];//接受缓存区
unsigned short RX_frameTimeOut = DEFAULT_RX_TIMEOUT; //帧接收超时
void uart_int(void) interrupt 4 using 1
{
//接收一个字节完成中断
if(RI)
{
RI = 0;
if(COM1.B_RX_OK == 0)
{
//判断接收的缓冲区是否满
if(COM1.RX_Cnt >= COM_RX1_Lenth)
{
//接收完成置1。
COM1.B_RX_OK = 1;
}
else
{
//将接受的数据填入接收缓存。
RX1_Buffer[COM1.RX_Cnt++] = SBUF;
//重置两帧数据的时间间隔变量。
COM1.RX_TimeOut = RX_frameTimeOut;
}
}
}
//发送一个字节完成中断
if(TI)
{
TI = 0;
//清除发送忙标志位
busy = 0;
}
}
/*
*串口初始化
*/
void uart_init(unsigned int baud)
{
#if (PARITYBIT == NONE_PARITY)
SCON = 0x50;
#elif (PARITYBIT == ODD_PARITY) || (PARITYBIT == EVEN_PARITY) || (PARITYBIT == MARK_PARITY)
SCON = 0xda;
#elif (PARITYBIT == SPACE_PARITY)
SCON = 0xd2;
#endif
COM1.B_RX_OK = 0;
//使用定时器2,作为串口
TL2 = RCAP2L = (65536-(UART_FOSC/32/baud));
TH2 = RCAP2H = (65536-(UART_FOSC/32/baud)) >> 8;
T2CON = 0x34;
ES = 1;
EA = 1;
}
/*
*发送单个字函数
*/
void uart_write2buff(unsigned char dat)
{
while (busy);
ACC = dat;
if (P)
{
#if (PARITYBIT == ODD_PARITY)
TB8 = 0;
#elif (PARITYBIT == EVEN_PARITY)
TB8 = 1;
#endif
}
else
{
#if (PARITYBIT == ODD_PARITY)
TB8 = 1;
#elif (PARITYBIT == EVEN_PARITY)
TB8 = 0;
#endif
}
busy = 1;
SBUF = ACC;
}
/*
*uart_send
*发送指定长度的字符
*bufs 发送的数据
*len 要发生的长度
*/
void uart_send(unsigned char *bufs,unsigned char len)
{
if (len != SERIAL_AUTO_LEN)//这里SERIAL_AUTO_LEN最大为255,如果发送的字符串长度不超过255,都是按用户写入的字符长度发送
{
while (len--)//等待发送完成
{
uart_write2buff(*bufs);//发送单个字符
bufs ++;//指针加1
}
}
else//如果字长等于或超过255,不按用户写入字长发送
{
for (; *bufs != 0; bufs++) uart_write2buff(*bufs);//把字符逐个发送出去
}
}
暂无评论内容