基于51单片机的心率计仿真设计

1.本设计基于STC89C51/52(与AT89S51/52、AT89C51/52通用,可任选)单片机。
2.LCD1602液晶显示当前的心率,单位是心率/分钟
3.手指放到红外对管中,2秒内读出心率。
4.按键可以设置报警的上下限心率。


使用方法:
三个按键:一个设置,一个加,一个减。
按下设置的时候才可以加减。

由于仿真中没有红外,手指也模拟不了,其实就是单片机的IO口检测脉冲信号,那样用个方波信号模拟就ok


单片机源程序如下:

  1. #include <reg52.h>
  2. #include <intrins.h>         //                         包含头文件
  3. #define uint            unsigned int
  4. #define uchar           unsigned char
  5. #define ulong           unsigned long         //宏定义
  6. #define LCD_DATA        P0                                 //定义P0口为LCD_DATA
  7. sbit LCD_RS =P2^5;
  8. sbit LCD_RW =P2^6;
  9. sbit LCD_E  =P2^7;                                                 //定义LCD控制引脚
  10. sbit Xintiao =P1^0 ;                                         //脉搏检测输入端定义
  11. sbit speaker =P2^4;                                                 //蜂鸣器引脚定义
  12. void delay5ms(void);   //误差 0us
  13. void LCD_WriteData(uchar LCD_1602_DATA);         /********LCD1602数据写入***********/
  14. void LCD_WriteCom(uchar LCD_1602_COM);                 /********LCD1602命令写入***********/
  15. void lcd_1602_word(uchar Adress_Com,uchar Num_Adat,uchar *Adress_Data); /*1602字符显示函数,变量依次为字符显示首地址,显示字符长度,所显示的字符*/
  16. void InitLcd();//液晶初始化函数
  17. void Tim_Init();
  18. uchar Xintiao_Change=0;           //
  19. uint  Xintiao_Jishu;
  20. uchar stop;
  21. uchar View_Data[3];
  22. uchar View_L[3];
  23. uchar View_H[3];
  24. uchar Xintiao_H=100;        //脉搏上限
  25. uchar Xintiao_L=40;                //脉搏下限
  26. uchar Key_Change;
  27. uchar Key_Value;                //按键键值
  28. uchar View_Con;                        //设置的位(0正常工作,1设置上限,2设置下限)
  29. uchar View_Change;
  30. void main()          //主函数
  31. {
  32. InitLcd();
  33. Tim_Init();
  34. lcd_1602_word(0x80,16,"Heart Rate:     ");          //初始化显示
  35. TR0=1;
  36. TR1=1;                                  //打开定时器
  37. while(1)                          //进入循环
  38.   {
  39.    if(Key_Change)          //有按键按下并已经得出键值
  40.     {
  41.          Key_Change=0;          //将按键使能变量清零,等待下次按键按下
  42.          View_Change=1;
  43.          switch(Key_Value)                                //判断键值
  44.           {
  45.            case 1:                                                //设置键按下
  46.                  {
  47.                           View_Con++;                        //设置的位加
  48.                           if(View_Con==3)                //都设置好后将此变量清零
  49.                            View_Con=0;
  50.                           break;                                //跳出,下同
  51.                          }
  52.            case 2:                                                //加键按下
  53.                  {
  54.                           if(View_Con==2)                //判断是设置上限
  55.                            {
  56.                                    if(Xintiao_H<150)        //上限数值小于150
  57.                                  Xintiao_H++;                //上限+
  58.                            }
  59.                           if(View_Con==1)                //如果是设置下限
  60.                            {
  61.                                    if(Xintiao_L<Xintiao_H-1)//下限值小于上限-1(下限值不能超过上限)
  62.                                  Xintiao_L++;                //下限值加
  63.                            }
  64.                           break;
  65.                          }
  66.            case 3:                                                //减键按下
  67.                  {
  68.                           if(View_Con==2)                //设置上限
  69.                            {
  70.                                    if(Xintiao_H>Xintiao_L+1)//上限数据大于下限+1(同样上限值不能小于下限)
  71.                                  Xintiao_H--;                //上限数据减
  72.                            }
  73.                           if(View_Con==1)                //设置下限
  74.                            {
  75.                                    if(Xintiao_L>30)        //下限数据大于30时
  76.                                  Xintiao_L--;                //下限数据减
  77.                            }
  78.                           break;
  79.                          }
  80.          }
  81.         }
  82.    if(View_Change)//开始显示变量
  83.     {
  84.          View_Change=0;//变量清零
  85.          if(stop==0)                          //心率正常时
  86.           {
  87.            if(View_Data[0]==0x30) //最高位为0时不显示
  88.             View_Data[0]=' ';
  89.           }
  90.          else                                          //心率不正常(计数超过5000,也就是两次信号时间超过5s)不显示数据
  91.           {
  92.            View_Data[0]=' ';
  93.            View_Data[1]=' ';
  94.            View_Data[2]=' ';
  95.           }
  96.          switch(View_Con)
  97.           {
  98.            case 0: //正常显示
  99.                   {
  100.                            lcd_1602_word(0x80,16,"Heart Rate:     ");//显示一行数据
  101.                            lcd_1602_word(0xc0,16,"                ");//显示第二行数据
  102.                            lcd_1602_word(0xcd,3,View_Data);                         //第二行显示心率
  103.                            break;
  104.                           }
  105.            case 1: //设置下限时显示
  106.                   {
  107.                            lcd_1602_word(0x80,16,"Heart Rate:     ");//第一行显示心率
  108.                            lcd_1602_word(0x8d,3,View_Data);
  109.                           
  110.                            View_L[0]=Xintiao_L/100+0x30;                //将下限数据拆字
  111.                            View_L[1]=Xintiao_L%100/10+0x30;
  112.                            View_L[2]=Xintiao_L%10+0x30;
  113.                            if(View_L[0]==0x30)                                        //最高位为0时,不显示
  114.                             View_L[0]=' ';
  115.                           
  116.                            lcd_1602_word(0xC0,16,"Warning L :     ");//第二行显示下限数据
  117.                            lcd_1602_word(0xCd,3,View_L);
  118.                            break;
  119.                           }
  120.            case 2: //设置上限时显示(同上)
  121.                   {
  122.                            lcd_1602_word(0x80,16,"Heart Rate:     ");
  123.                            lcd_1602_word(0x8d,3,View_Data);
  124.                           
  125.                            View_H[0]=Xintiao_H/100+0x30;
  126.                            View_H[1]=Xintiao_H%100/10+0x30;
  127.                            View_H[2]=Xintiao_H%10+0x30;
  128.                            if(View_H[0]==0x30)
  129.                             View_H[0]=' ';
  130.                           
  131.                            lcd_1602_word(0xC0,16,"Warning H :     ");
  132.                            lcd_1602_word(0xCd,3,View_H);
  133.                            break;
  134.                           }
  135.           }
  136.         }
  137.   }
  138. }
  139. void Time1() interrupt 3                //定时器1服务函数
  140. {
  141.         static uchar Key_Con,Xintiao_Con;
  142.         TH1=0xd8;                   //10ms
  143.         TL1=0xf0;                   //重新赋初值
  144.         switch(Key_Con)   //无按键按下时此值为0
  145.         {
  146.                 case 0:                   //每10ms扫描此处
  147.                 {
  148.                         if((P3&0x07)!=0x07)//扫描按键是否有按下
  149.                         {
  150.                                 Key_Con++;                  //有按下此值加1,值为1
  151.                         }
  152.                         break;
  153.                 }
  154.                 case 1:                                          //10ms后二次进入中断后扫描此处(Key_Con为1)
  155.                 {
  156.                         if((P3&0x07)!=0x07)//第二次进入中断时,按键仍然是按下(起到按键延时去抖的作用)
  157.                         {
  158.                                 Key_Con++;                  //变量加1,值为2
  159.                                 switch(P3&0x07)  //判断是哪个按键按下
  160.                                 {
  161.                                         case 0x06:Key_Value=1;break;         //判断好按键后将键值赋值给变量Key_Value
  162.                                         case 0x05:Key_Value=2;break;
  163.                                         case 0x03:Key_Value=3;break;
  164.                                 }
  165.                         }
  166.                         else                                                                 //如果10ms时没有检测到按键按下(按下时间过短)
  167.                         {
  168.                                 Key_Con=0;                                                 //变量清零,重新检测按键
  169.                         }
  170.                         break;
  171.                 }
  172.                 case 2:                                                                         //20ms后检测按键
  173.                 {
  174.                         if((P3&0x07)==0x07)                                 //检测按键是否还是按下状态
  175.                         {
  176.                                 Key_Change=1;                                         //有按键按下使能变量,(此变量为1时才会处理键值数据)
  177.                                 Key_Con=0;                                                //变量清零,等待下次有按键按下
  178.                         }
  179.                         break;
  180.                 }
  181.         }
  182.        
  183.         switch (Xintiao_Con)//此处与上面按键的检测类似
  184.         {
  185.                 case 0:                         //默认Xintiao_Con是为0的
  186.                 {
  187.                         if(!Xintiao)//每10ms(上面的定时器)检测一次脉搏是否有信号
  188.                         {
  189.                                 Xintiao_Con++;//如果有信号,变量加一,程序就会往下走了
  190.                         }
  191.                         break;
  192.                 }
  193.                 case 1:
  194.                 {
  195.                         if(!Xintiao)           //每过10ms检测一下信号是否还存在
  196.                         {
  197.                                 Xintiao_Con++;//存在就加一
  198.                         }
  199.                         else
  200.                         {
  201.                                 Xintiao_Con=0;//如果不存在了,检测时间很短,说明检测到的不是脉搏信号,可能是其他干扰,将变量清零,跳出此次检测
  202.                         }
  203.                         break;
  204.                 }
  205.                 case 2:
  206.                 {
  207.                         if(!Xintiao)
  208.                         {
  209.                                 Xintiao_Con++;//存在就加一
  210.                         }
  211.                         else
  212.                         {
  213.                                 Xintiao_Con=0;//如果不存在了,检测时间很短,说明检测到的不是脉搏信号,可能是其他干扰,将变量清零,跳出此次检测
  214.                         }
  215.                         break;
  216.                 }
  217.                 case 3:
  218.                 {
  219.                         if(!Xintiao)
  220.                         {
  221.                                 Xintiao_Con++;//存在就加一
  222.                         }
  223.                         else
  224.                         {
  225.                                 Xintiao_Con=0;//如果不存在了,检测时间很短,说明检测到的不是脉搏信号,可能是其他干扰,将变量清零,跳出此次检测
  226.                         }
  227.                         break;
  228.                 }
  229.                 case 4:
  230.                 {
  231.                         if(Xintiao)//超过30ms有信号,判定此次是脉搏信号,然后当信号消失后,执行以下程序
  232.                         {
  233.                                 if(Xintiao_Change==1)//心率计原理为检测两次脉冲间隔时间计算心率,变量Xintiao_Change第一次脉冲时为0的,所有走下面的else,第二次走这里
  234.                                 {
  235.                                         View_Data[0]=(60000/Xintiao_Jishu)/100+0x30;                  //计算心跳并拆字显示:心跳计时是以1ms为单位,两次心跳中间计数如果是1000次,也就是1000*1ms=1000ms=1s
  236.                                         View_Data[1]=(60000/Xintiao_Jishu)%100/10+0x30;          //那么计算出的一分钟(60s)心跳数就是:60*1000/(1000*1ms)=60次          其中60是一分钟60s,1000是一秒有1000ms,1000是计数值,1是一次计数对应 的时间是1ms
  237.                                         View_Data[2]=(60000/Xintiao_Jishu)%10+0x30;                  //计算出的心跳数/100得到心跳的百位,%100是取余的,就是除以100的余数,再除以10就得到十位了,以此类推
  238.                                                                                                   //拆字后的单个数据+0x30的目的是得到对应数字的液晶显示码,数字0对应的液晶显示码是0x30,1是0x30+1,以此类推
  239.                                         if(((60000/Xintiao_Jishu)>=Xintiao_H)||((60000/Xintiao_Jishu)<=Xintiao_L))//心率不在范围内报警
  240.                                         speaker=0;                        //蜂鸣器响
  241.                                         else
  242.                                         speaker=1;                        //不响
  243.                                        
  244.                                         View_Change=1;           //计算出心率后启动显示
  245.                                         Xintiao_Jishu=0;           //心跳计数清零
  246.                                         Xintiao_Change=0;   //计算出心率后该变量清零,准备下次检测心率
  247.                                         stop=0;                           //计算出心率后stop清零
  248.                                 }
  249.                                 else//第一次脉冲时Xintiao_Change为0
  250.                                 {
  251.                                         Xintiao_Jishu=0;        //脉冲计时变量清零,开始计时
  252.                                         Xintiao_Change=1;//Xintiao_Change置1,准备第二次检测到脉冲时计算心率
  253.                                 }
  254.                                 Xintiao_Con=0;        //清零,准备检测下一次脉冲
  255.                                 break;
  256.                         }
  257.                 }
  258.         }
  259. }
  260. /**定时器T0工作函数**/
  261. void Time0() interrupt 1
  262. {
  263. TH0=0xfc;                   //1ms
  264. TL0=0x18;                   //重新赋初值
  265. Xintiao_Jishu++;  //心跳计数加
  266. if(Xintiao_Jishu==5000)//心跳计数大于5000
  267.   {
  268.    Xintiao_Jishu=0;                //数据清零
  269.    View_Change=1;                //显示位置1
  270.    Xintiao_Change=0;        //置零,准备再次检测
  271.    stop=1;           //心跳计数超过5000后说明心率不正常或者没有测出,stop置1
  272.    speaker=1;  //关闭蜂鸣器
  273.   }
  274. }
  275. /**定时器初始化函数**/
  276. void Tim_Init()
  277. {
  278. EA=1;                          //打开中断总开关
  279. ET0=1;                          //打开T0中断允许开关
  280. ET1=1;                          //打开T1中断允许开关
  281. TMOD=0x11;                  //设定定时器状态
  282. TH0=0xfc;                   //1ms
  283. TL0=0x18;                   //赋初值
  284. TH1=0xd8;                   //10ms
  285. TL1=0xf0;                   //赋初值
  286. }
  287. /**在指定地址显示指定数量的指定字符**/
  288. /**Adress_Com显示地址,Num_Adat显示字符数量,Adress_Data显示字符串内容**/
  289. void lcd_1602_word(uchar Adress_Com,uchar Num_Adat,uchar *Adress_Data)
  290. {
  291. uchar a=0;
  292. uchar Data_Word;
  293. LCD_WriteCom(Adress_Com); //选中地址
  294. for(a=0;a<Num_Adat;a++)   //for循环决定显示字符个数
  295.   {
  296.    Data_Word=*Adress_Data;          //读取字符串数据
  297.    LCD_WriteData(Data_Word);  //显示字符串
  298.    Adress_Data++;                          //显示地址加一
  299.   }
  300. }
  301. /***************1602函数*******************/
  302. void LCD_WriteData(uchar LCD_1602_DATA)         /********LCD1602数据写入***********/
  303. {
  304. delay5ms();  //操作前短暂延时,保证信号稳定
  305. LCD_E=0;
  306. LCD_RS=1;
  307. LCD_RW=0;
  308. _nop_();
  309. LCD_E=1;
  310. LCD_DATA=LCD_1602_DATA;
  311. LCD_E=0;
  312. …………………具体源程序链接
  313. https://download.csdn.net/download/tang2010up/89471756

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

数据结构十三:2 - 3树和红黑树

一开始就接触这五点&#xff0c;会让人云里雾里&#xff0c;不利于了解这个数据结构。因为这种先给定义在推导的方式并不适合学习。它没有介绍红黑树的来源&#xff0c;而只是给你生硬的定义。 而学习红黑树的最好学习资料就是大名鼎鼎的《算法4》&#xff0c;如下&#xff1a…

微型操作系统内核源码详解系列五(四):cm3下svc启动任务

系列一&#xff1a;微型操作系统内核源码详解系列一&#xff1a;rtos内核源码概论篇&#xff08;以freertos为例&#xff09;-CSDN博客 系列二&#xff1a;微型操作系统内核源码详解系列二&#xff1a;数据结构和对象篇&#xff08;以freertos为例&#xff09;-CSDN博客 系列…

LabVIEW电控旋翼测控系统

开发基于LabVIEW开发的电控旋翼测控系统&#xff0c;通过高效监控和控制提升旋翼系统的性能和安全性。系统集成了多种硬件设备&#xff0c;采用模块化设计&#xff0c;实现复杂的控制和数据处理功能&#xff0c;适用于现代航空航天领域。 项目背景 传统旋翼系统依赖机械和液压…

算法04 模拟算法之一维数组相关内容详解【C++实现】

大家好&#xff0c;我是bigbigli&#xff0c;模拟算法我们将分为几个章节来讲&#xff0c;今天我们只看一维数组相关的题目 目录 模拟的概念 训练&#xff1a;开关灯 解析 参考代码 训练&#xff1a;数组变化 解析 参考代码 训练&#xff1a;折叠游戏 解析 参考代码 …

vscode+picgo+gitee实现Markdown图床

vscode中编辑Markdown文件&#xff0c;复制的图片默认是保存在本地的。当文档上传csdn时&#xff0c;会提示图片无法识别 可以在gitee上创建图床仓库&#xff0c;使用picgo工具上传图片&#xff0c;在Markdown中插入gitee链接的方式来解决该问题。 一、 安装picgo工具 1.1 v…

IOS开发学习日记(十六)

目录 App间的唤起和通信 App跳转 通过Scheme唤起其他App Universal Link 组件化 App间的唤起和通信 App跳转 使用URL Scheme支持App启动、跳转及参数传递 分享 / 登陆 / 拉起App Store等 设置URL Type 在UIApplication中处理参数和业务逻辑 -(BOOL)application:(UIApp…

强化安全新篇章:韶关石油化工可燃气体报警器年检解析

韶关&#xff0c;这座位于广东省北部的城市&#xff0c;近年来在石油化工行业取得了显著的发展。 随着一批批大型石化企业的进驻和投产&#xff0c;韶关不仅成为了区域性的石化产业基地&#xff0c;也为地方经济带来了强劲的增长动力。 然而&#xff0c;随着石化产业的快速发…

Selenium WebDriver - 网络元素

本文翻译整理自&#xff1a;https://www.selenium.dev/documentation/webdriver/elements/ 文章目录 一、文件上传二、定位策略1、传统定位器2、创建定位器3、类名4、CSS选择器5、id6、NAME7、链接文本8、部分链接文本9、标签名称10、xpath11、相对定位器它是如何工作的可用相对…

2024最新版Python 3.12.4安装使用指南

2024最新版Python 3.12.4安装使用指南 2024最新版Python 3.12.4安装使用指南0. Python的受欢迎程度1. 安装最新版Python 3.12.42. 验证Python 3.12.4版本3. 验证Python功能4. 使用IDLE交互式开发模式5. 安装Python扩展库相关阅读&#xff1a; By Jackson 2024最新版Python 3.12…

【每日刷题】Day73

【每日刷题】Day73 &#x1f955;个人主页&#xff1a;开敲&#x1f349; &#x1f525;所属专栏&#xff1a;每日刷题&#x1f34d; &#x1f33c;文章目录&#x1f33c; 1. 2583. 二叉树中的第 K 大层和 - 力扣&#xff08;LeetCode&#xff09; 2. 1325. 删除给定值的叶子…

为 Android 应用打造精良的 Chrome OS 使用体验

override fun onKeyUp(code: Int, ev: KeyEvent?): Boolean { return when (code) { KeyEvent.KEYCODE_J -> { // Do something here true } else -> super.onKeyUp(code, ev) // 重要&#xff01;&#xff01; } } 注意我们标出 “重要” 的那一行代码。这行代…

同步FIFO

描述 根据题目提供的双口RAM代码和接口描述&#xff0c;实现同步FIFO&#xff0c;要求FIFO位宽和深度参数化可配置。 电路的接口如下图所示。 端口说明如下表。 双口RAM端口说明&#xff1a; 端口名 I/O 描述 wclk input 写数据时钟 wenc input 写使能 waddr input…

分布式架构的优势与实现

目录 前言1. 什么是分布式架构1.1 分布式架构的定义1.2 分布式架构的基本原理 2. 分布式架构的优势2.1 可扩展性2.2 容错性和高可用性2.3 性能优化2.4 灵活性和可维护性 3. 分布式架构的实现方法3.1 服务拆分3.1.1 功能拆分3.1.2 垂直拆分3.1.3 水平拆分 3.2 数据分布与存储3.2…

RabbitMQ消息队列 安装及基本介绍

一.MQ介绍 Message Queue &#xff08;MQ&#xff09;是一种跨进程的通信机制&#xff0c;用于在系统之间进行传递消息。MQ作为消息中间件&#xff0c;可以进行异步处理请求&#xff0c;从而减少请求响应时间和解耦 1.1 应用场景 1.1.1 系统之间通过MQ进行消息通信&#xff0…

使用Android Studio导入源码

2-1 基础准备工作 首先你得安装配置了Android Studio&#xff0c;具体不明白的参考《Android Studio入门到精通 》。 接着你得下载好了源码Code&#xff0c;至于如何下载这里不再说明&#xff0c;比较简单&#xff0c;上官网查看就行了。 其次你需要保证源码已经被编译生成了…

Scala运算符及流程控制

Scala运算符及流程控制 文章目录 Scala运算符及流程控制写在前面运算符算数运算符关系运算符赋值运算符逻辑运算符位运算符运算符本质 流程控制分支控制单分支双分支多分支 循环控制for循环while循环循环中断嵌套循环 写在前面 操作系统&#xff1a;Windows10JDK版本&#xff…

项目训练营第一天

项目训练营第一天 springboot后端环境搭建 1、首先需要找文章下载好tomcat、JDK、maven、mysql、IDEA。&#xff08;软件下载及环境变量配置略&#xff09; 2、在下载好的IDEA中&#xff0c;选择新建spring initial项目&#xff0c;选定java web&#xff0c;即可新建一个spri…

如何设置MySQL远程访问权限?

MySQL是一种流行的关系型数据库管理系统&#xff0c;它广泛应用于各种Web应用程序和数据驱动的应用中。在默认情况下&#xff0c;MySQL只允许本地访问&#xff0c;为了能够从远程服务器或客户端访问MySQL数据库&#xff0c;我们需要进行一些额外的设置和配置。 安装和配置MySQ…

OSPF 2类LSA详解

概述 上图为2类LSA : Network LSA 的报文格式 , 我们重点关注3个报文字段即可 , 其他内容没有实际的信息 Link State ID : DR的接口IP地址 Network Mask : 该MA网络的掩码 Attached Router : 连接在该MA网络的所有路由器的Router ID 2类LSA一定是DR产生的 , 关于OSPF DR的细节…

LeetCode 671.二叉树第二小的结点

这个题我们可以用数组辅助完成&#xff0c;然后进行排序后&#xff0c;再用再进行取值&#xff0c;这是我的代码块: /*** Definition for a binary tree node.* struct TreeNode {* int val;* struct TreeNode *left;* struct TreeNode *right;* };*/void Preorde…