本设计为我的LabVIEW课程大作业,利用proteus实验仿真软件设计一个了基于stc12c5a单片机的心率采集系统,并在PC机上的Windows环境下利用LabVIEW软件实现心率数据的处理与显示。以下为效果演示视频:
心率监测装置
一、设计简介
设计目标:利用proteus实验仿真软件设计一个基于stc12c5a单片机的心率采集系统,并在PC机上的Windows环境下利用LabVIEW软件实现心率数据的处理与显示。
实现功能:
1、利用pulse sensor心率脉冲传感器实现对血管脉动进行采集;
2、利单片机可以通过串口通讯向PC机传输数据;
3、在PC机上的Windows环境下利用LabVIEW软件实现对心率的监视;
4、单片机实时显示心率;
4、进行心率分析,对异常心率有报警提示功能;
5、心率数据进行保存与发送;
实验设备:
1、PC机
2、安装LabVIEW软件
3、安装Keil uVision4软件
4、51单片机
5、pulse sensor心率脉冲传感器
二、总体设计
本设计主要利用pulse sensor心率脉冲传感器通过血管搏动时造成透光率变化影响感光器件阻值进而影响输出电压来反应心率变化。
通过单片机对传感器传输的模拟量进行数模转换,采取定时器查询方式检测转换计算心率,通过串口通讯向LabVIEW传输数据,labview对数据进行解析运算实现对心率的实时显示、对异常心率有报警提示、心率数据进行保存与发送等功能。
三、硬件设计
硬件选择:
设备电路板:51开发板;
芯片选择 :STC12C5A60S2;
单片机显示:LCD1602;
心率采集传感器:pulse sensor心率脉冲传感器;
四、软件设计
软件部分分为单片机程序编程与LabVIEW程序编程。
LabVIEW上位机编程部分:
单片机程序:
main.c
//ADC PIN:P1.0
//SYSTEM CLOCK:11.0592MHz
//Baudrate:115200
//UART:P3.0/rxd P3.1/txd
#include
#include "stdio.h"
#include
#define false 0
#define true 1
#define FOSC 11059200L //系统时钟
#define BAUD 115200 //波特率
#define T0MS (65536-FOSC/12/500)//500HZ in 12T MODE
#define ADC_POWER 0x80 //ADC POWER CONTROL BIT
#define ADC_FLAG 0x10 //ADC COMPLETE FLAG
#define ADC_START 0x08; //ADC START CONTROL BIT
#define ADC_SPEEDLL 0x00 //540 CLOCKS
#define ADC_SPEEDL 0x20 //360 CLOCKS
#define ADC_SPEEDH 0x40 //180 CLOCKS
#define ADC_SPEEDHH 0x60 //90 CLOCKS
#define ADC_MASK 0x01
void UART_init(void);
void ADC_init(unsigned char channel);
void T0_init(void);
void sendDataToProcessing(char symbol, int dat);
void UART_send(char dat);
unsigned char PulsePin = 0; // P1.0为传感器输入口)
int fadeRate = 0;
volatile unsigned int BPM;
volatile unsigned int Signal;
volatile unsigned int IBI = 600;
volatile bit Pulse = false;
volatile bit QS = false;
volatile int rate[10];
volatile unsigned long sampleCounter = 0; // 用于确定脉冲时序
volatile unsigned long lastBeatTime = 0;
volatile int Peak =512;
volatile int Trough = 512;
volatile int thresh = 512;
volatile int amp = 100;
volatile bit firstBeat = true;
volatile bit secondBeat = false;
static unsigned char order=0;
unsigned char code ucForum0[]="Heart rate test";
unsigned char code ucForum1[]=" BPM: ";
unsigned char DisBuff[4]={0};
void sys_init()
{
UART_init();
ADC_init(PulsePin);
T0_init();
LCD1602_Init(); //液晶初始化
}
void main(void)
{
sys_init();
LCD1602_DisplayString(ucForum0); //显示的内容
LCD1602_MoveToPosition(1,0); //显示位置移动到指定位置
LCD1602_DisplayString(ucForum1); //显示的内容
while(1)
{
sendDataToProcessing('S', Signal); // 发送数据
if (QS == true)
{
fadeRate = 255;
sendDataToProcessing('B',BPM);
sendDataToProcessing('Q',IBI);
QS = false;
LCD1602_MoveToPosition(1,9);
LCD1602_DisplayString(DisBuff);
}
delay(138); // take a break 19.6ms
}
}
void sendDataToProcessing(char symbol, int dat )
{
putchar(symbol);
printf("%d\n",dat); //串口发送
}
void UART_init(void)
{
PCON &= 0x7f; //波特率不倍速
SCON = 0x50; //8位数据,可变波特率
BRT = 0xFD; //独立波特率产生器初值
AUXR |= 0x04; //时钟设置为1T模式
AUXR |= 0x01; //选择独立波特率产生器
AUXR |= 0x10; //启动波特率产生
}
char putchar(unsigned char dat)
{
TI=0;
SBUF=dat;
while(!TI);
TI=0;
return SBUF;
}
void T0_init(void)
{
TMOD |= 0x01; // 初始化定时器0,2mS一个中断。
TL0=T0MS;
TH0=T0MS>>8;
TR0=1;
ET0=1;
EA=1;
}
void ADC_init(unsigned char channel)
{
P1ASF=ADC_MASK<<channel; //启用PlusePin作为ADC输入
ADC_RES=0;
ADC_RESL=0;
AUXR1 |= 0x04; //调整ADC结果的格式
ADC_CONTR=channel|ADC_POWER|ADC_SPEEDLL|ADC_START; //打开ADC电源并开始转换
}
unsigned int analogRead(unsigned char channel)
{
unsigned int result;
ADC_CONTR &=!ADC_FLAG; //clear ADC FLAG
result=ADC_RES;
result=result<<8;
result+=ADC_RESL;
ADC_CONTR|=channel|ADC_POWER|ADC_SPEEDLL|ADC_START;
return result;
}
// Timer 0中断子程序,每2MS中断一次,读取AD值,计算心率值
void Timer0_rountine(void) interrupt 1
{
int N;
unsigned char i;
// 保持最后10个IBI值的总和
unsigned int runningTotal = 0;
EA=0;
TL0=T0MS;
TH0=T0MS>>8; //重新加载16位计时器0
Signal = analogRead(PulsePin);
sampleCounter += 2;
N = sampleCounter - lastBeatTime;
if(Signal < thresh && N > (IBI/5)*3)
{ // 通过等待最后一个IBI的3/5来避免二脉噪声 IBI
if (Signal < Trough)
{ //T是低谷
Trough = Signal; //跟踪脉搏波的最低点
}
}
if(Signal > thresh && Signal > Peak)
{ //避免噪音
Peak = Signal; // P是峰值
} // 跟踪脉搏波的最高点
// 每次有脉冲时,信号的值就会飙升
if (N > 250)
{ // avoid high frequency noise
if ( (Signal > thresh) && (Pulse == false) && (N > (IBI/5)*3) )
{
Pulse = true; // 当我们认为有脉冲时设置脉冲标志
IBI = sampleCounter - lastBeatTime; // 测量拍之间的时间(毫秒
lastBeatTime = sampleCounter; // 跟踪下一个脉冲的时间
if(secondBeat)
{ // 如果这是第二拍,如果secondBeat == TRUE
secondBeat = false; // 清除secondBeat标志
for(i=0; i<=9; i++)
{ // 启动时获得实际的BPM
rate[i] = IBI;
}
}
if(firstBeat)
{
firstBeat = false;
secondBeat = true;
EA=1;
return;
}
for(i=0; i<=8; i++)
{
rate[i] = rate[i+1];
runningTotal += rate[i];
}
rate[9] = IBI;
runningTotal += rate[9];
runningTotal /= 10;
BPM = 60000/runningTotal; // 一分钟能容纳多少拍子,BPM
if(BPM>200)BPM=200; //限制BPM最高显示值
if(BPM<30)BPM=30; //限制BPM最低显示值
DisBuff[2] = BPM%10+48; //取个位数
DisBuff[1] = BPM%100/10+48; //取十位数
DisBuff[0] = BPM/100+48; //百位数
if(DisBuff[0]==48)
DisBuff[0]=32;
QS = true; //标志
}
}
if (Signal < thresh && Pulse == true)
{ // 当值下降时,节拍结束
// blinkPin=1;
Pulse = false;
amp = Peak - Trough;
thresh = amp/2 + Trough;
Peak = thresh;
Trough = thresh;
}
if (N > 2500)
{ // 如果2.5秒不停地过去
thresh = 600; // 设置阈值默认值
Peak = 600; // 设置P默认值
Trough = 600;
lastBeatTime = sampleCounter;
firstBeat = true;
secondBeat = false;
}
EA=1;
}
LCD1602.c
#include
#include "stdio.h"
#include
sbit LCD1602_RS = P3^5; //位定义,液晶的数据/命令选择
sbit LCD1602_RW = P3^6; //位定义,液晶的读写选择
sbit LCD1602_EN = P3^4; //位定义,液晶使能信号
#define LCDPORT P0 //液晶的数据口
/******************************************************************************
函数功能:忙检测
*******************************************************************************/
void LCD1602_CheckBusy(void)
{
unsigned char i = 255;
LCDPORT = 0xFF; //读之前先置位,准备读取IO口数据
LCD1602_RS = 0;
LCD1602_RW = 1; //使液晶处于读数据状态
LCD1602_EN = 1; //使能液晶,高电平有效
while((i--) && (LCDPORT & 0x80)); //忙检测
LCD1602_EN = 0;
}
/******************************************************************************
向LCD1602液晶写入数据或者命令
*******************************************************************************/
void LCD1602_WriteInformation(unsigned char ucData,bit bComOrData)
{
LCD1602_CheckBusy(); //在写入数据或者命令前先进行忙检测
LCDPORT = ucData; //先将数据或者命令送至IO
LCD1602_RS = bComOrData; //确定是写入数据还是写命令
LCD1602_RW = 0; //使液晶处于写入信息状态
LCD1602_EN = 1; //使能液晶,高电平有效
LCD1602_EN = 0;
}
/******************************************************************************
LCD1602_Init液晶初始化函数
*******************************************************************************/
void LCD1602_Init(void)
{
LCD1602_WriteInformation(0x38,0);
delay(300);
LCD1602_WriteInformation(0x38,0);
delay(100);
LCD1602_WriteInformation(0x38,0);
delay(100);
LCD1602_WriteInformation(0x38,0); //写入命令,5x7点阵工作方式,8位数据接口
delay(100);
LCD1602_WriteInformation(0x0c,0); //显示设置,开显示,光标不显示,不闪烁
delay(20);
LCD1602_WriteInformation(0x01,0); //清屏指令
delay(20);
}
/******************************************************************************
LCD1602_MoveToPosition将液晶的光标移动到指定的位置
*******************************************************************************/
void LCD1602_MoveToPosition(unsigned char x,unsigned char y)
{
if(0 == x)
LCD1602_WriteInformation((0x80 | y),0); //光标定位到第一行的y列
if(1 == x)
LCD1602_WriteInformation((0xC0 | y),0); //光标定义到第二行的y列
}
/******************************************************************************
LCD1602在指定的位置上显示指定的字符
*******************************************************************************/
void LCD1602_DisplayOneCharOnAddr(unsigned char x,unsigned char y,unsigned char ucData)
{
LCD1602_MoveToPosition(x,y); //光标位置
LCD1602_WriteInformation(ucData,1); //写入数据
}
/******************************************************************************
LCD1602显示字符串
*******************************************************************************/
void LCD1602_DisplayString(unsigned char *ucStr)
{
while(*ucStr != '\0') //字符串结束之前,循环显示
{
LCD1602_WriteInformation(*ucStr,1); //依次写入每一个字符
ucStr++; //指针增加
}
}
/******************************************************************************
延时函数
*******************************************************************************/
void delay(unsigned int n)
{
unsigned int i,j;
for(i=0;i<n;i++)
for(j=0;j<100;j++);
}
LCD1602.h
#ifndef LCD1602_H
#define LCD1602_H
void delay(unsigned int uiCount); //延时函数
void LCD1602_CheckBusy(void); //液晶忙检测
void LCD1602_WriteInformation(unsigned char ucData,bit bComOrData); //在液晶上写数据或者写命令,0为命令,1为数据
void LCD1602_Init(void); //液晶初始化
void LCD1602_MoveToPosition(unsigned char x,unsigned char y); //液晶的坐标移动到指定位置
void LCD1602_DisplayOneCharOnAddr(unsigned char x,unsigned char y,unsigned char ucData);//在液晶指定位置显示字符
void LCD1602_DisplayString(unsigned char *ucStr); //在液晶上显示字符串
#endif