基于物联网技术的智慧病房管理系统

该文章详细介绍了基于STM32F103的物联网病房管理系统的硬件配置、RTOS移植、模块驱动和功能实现。系统包括温湿度监测、呼吸灯控制、按键处理、血氧与心率检测以及Modbus通信。通过RTOS实现多任务调度,利用I2C读取AHT20温湿度数据,定时器控制PWM灯光明暗,RTC进行定时开关灯,并通过UART与上位机通信。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

提示:以下是本篇文章正文内容,下面案例可供参考

一、系统要求

设计一个基于物联网技术的智慧病房管理系统。假设医院住院部的一层病房(走廊两边病房平行分布),病房数量最多60间,每间病房3个床位,编号从1~180号。每间病房可采用的设备如下:STM32F103开发板1块,房间温湿度采集模块1套(I2C接口,AHT20模块),房间自动灯光开关控制器(以PWM方式控制,每天早上7点渐亮,晚上22点渐灭),病人脉搏&血氧检测仪3套(UART接口输出脉搏+血氧的数字值),床头紧急呼叫按键开关3个(按下呼叫)。

每间病房的STM32F103开发板通过UART转485接口,以mobus组网方式,连接到护士监控室的PC电脑上(上位机)。PC电脑上可接收每间病房的温湿度数据(周期为5分钟)、床头紧急呼叫信号、病人脉搏血氧数据(正常状态下30分钟一次采集;当脉搏超过120或血氧值低于90时切换到危重状态下,实时采集),显示在屏幕上并且保存到MySQL数据库里。
设计要求:

1、写出系统设计方案,画出系统功能图。

2、画出病房stm32f103控制模块的电路原理图。

3、设计各功能模块的模拟实现软件,给出主要源代码。

4、总结本系统方案的技术优势和代码特点。

建议(加分项):

采用RTOS(ucos、RTthread-nano等)做多任务软件框架。

使用cubemx完成基本端口参数选择和设计。

采用stm32f103内部flash记录数据,或者其他设计或实现的亮点。

日期读取功能,利用stm32内部日历芯片。

上位机功能只需要设计,不须实现。串口,mobus数据接收可用串口助手等工具软件。但如果有能力设计上位机代码,也有奖励分。

二、问题分析

系统功能

1、RTOS 系统移植

2、温湿度模块数据获取

3、通过定时器定时启动呼吸灯

4、接收按键信号并进行消抖

5、血氧与心率检测

6、modbus 发送数据信息给上位机

系统功能具体内容与模块

1、使用 I2C 读取 AHT20 模块

2、TIM 定时器与 PWM 呼吸灯

3、按键信息接收与消抖

4、UART 转 485 接口与 modbus 通信

5、MAX30102 模块心率与血氧测量

三、基本配置

RCC 配置

在这里插入图片描述

SYS 配置

在这里插入图片描述

USART1 与 DMA 配置

在这里插入图片描述
在这里插入图片描述

I2C 配置

在这里插入图片描述

TIM3 配置

在这里插入图片描述

DMA 配置

在这里插入图片描述

TIM2 与 PWM 配置

在这里插入图片描述

NVIC 配置

在这里插入图片描述

GPIO 配置

在这里插入图片描述

时钟配置

在这里插入图片描述

引脚配置

在这里插入图片描述

四、RTOS 与 模块驱动 代码配置

RTOS 配置

这里选择引入 RT-Thread 轻量便捷 :

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

外部按钮配置

在这里插入图片描述
在这里插入图片描述

五、RTOS、AHT20 与 按钮功能实现

温湿度获取代码设计

首先我们需要引入温湿度驱动头文件:

#include "AHT20.h"

然后我们需要在进程中对温湿度传感器进行初始化,这里在初始化之后,不能立即获取温湿度,需要等待 2 秒或以上,确保数据正确性:

MX_GPIO_Init();
MX_I2C1_Init();
MX_USART1_UART_Init();
uint32_t CT_data[2]={0,0};	// 用于获取温湿度数据
volatile int  c1,t1;
rt_thread_delay(50);
AHT20_Init();
rt_thread_delay(2500);

然后我们需要获取经过 CRC 验证的温湿度数据,这里直接调用驱动函数即可:

while(1)
	{
		AHT20_Read_CTdata_crc(CT_data);       //经过CRC校验,读取AHT20的温度和湿度数据    推荐每隔大于1S读一次
		c1 = CT_data[0]*1000/1024/1024;  //计算得到湿度值c1(放大了10倍)
		t1 = CT_data[1]*2000/1024/1024-500;//计算得到温度值t1(放大了10倍)
		printf("正在检测");
		rt_thread_delay(100);
		printf(".");
		rt_thread_delay(100);
		printf(".");
		rt_thread_delay(100);
		printf(".");
		rt_thread_delay(100);
		printf(".");
		rt_thread_delay(100);
		printf(".");
		rt_thread_delay(100);
		printf(".");
		rt_thread_delay(100);
		printf(".");
		rt_thread_delay(100);
		printf(".");
		rt_thread_delay(100);
		printf(".");
		rt_thread_delay(100);
		printf(".");
		printf("\r\n");
		printf("温度:%d%d.%d",t1/100,(t1/10)%10,t1%10);	// 这里需要对温度进行计算后才能得到我们需要的温度值
		printf("湿度:%d%d.%d",c1/100,(c1/10)%10,c1%10); // 这里同样需要对适度进行计算
		printf("\r\n");
		printf("等待");
		rt_thread_delay(100);
		printf(".");
		rt_thread_delay(100);
		printf(".");
		rt_thread_delay(100);
		printf(".");
		rt_thread_delay(100);
		printf(".");
		rt_thread_delay(100);
		printf(".");
		rt_thread_delay(100);
		printf(".");
		rt_thread_delay(100);
		printf(".");
		rt_thread_delay(100);
		printf(".");
		rt_thread_delay(100);
		printf(".");
		rt_thread_delay(100);
		printf(".");
		printf("\r\n");
	}

RTOS 进程设计

首先新建 app_rt_thread.c 文件,然后引入头文件:

#include "rtthread.h"
#include "main.h"
#include "i2c.h"
#include "usart.h"
#include "gpio.h"
#include "stdio.h"
#include "AHT20.h"

然后我们需要设计进程,这里主进程先设置为串口发送,子进程设置两个,一个是获取温度参数,另一个是控制 PC13 引脚上的板载 LED ,后续会根据系统进程对进行设计:

//初始化线程函数
void MX_RT_Thread_Init(void)
{
	//初始化LED1线程
	rt_thread_init(&led1_thread,"led1",led1_task_entry,RT_NULL,&rt_led1_thread_stack[0],sizeof(rt_led1_thread_stack),3,20);
	//开启线程调度
	rt_thread_startup(&led1_thread);
	//初始化USART1线程
	rt_thread_init(&usart1_thread,"usart1",usart1_task_entry,RT_NULL,&rt_usart1_thread_stack[0],sizeof(rt_usart1_thread_stack),3,20);
	//开启线程调度
	rt_thread_startup(&usart1_thread);
}
 
//主任务
void MX_RT_Thread_Process(void)
{
	printf("Hello RT_Thread!!!\r\n");
	rt_thread_delay(2000);
}
 
//LED1任务
void led1_task_entry(void *parameter)
{
	while(1)
	{
		HAL_GPIO_WritePin(GPIOC,GPIO_PIN_13, GPIO_PIN_RESET);
		rt_thread_delay(500);
		HAL_GPIO_WritePin(GPIOC,GPIO_PIN_13, GPIO_PIN_SET);
		rt_thread_delay(500);
	}
}
//读取温度任务
void usart1_task_entry(void *parameter)
{
  MX_GPIO_Init();
  MX_I2C1_Init();
  MX_USART1_UART_Init();
	uint32_t CT_data[2]={0,0};	//
	volatile int  c1,t1;
	rt_thread_delay(50);
	AHT20_Init();
	rt_thread_delay(2500);
	
	while(1)
	{
		AHT20_Read_CTdata_crc(CT_data);       //经过CRC校验,读取AHT20的温度和湿度数据    推荐每隔大于1S读一次
		c1 = CT_data[0]*1000/1024/1024;  //计算得到湿度值c1(放大了10倍)
		t1 = CT_data[1]*2000/1024/1024-500;//计算得到温度值t1(放大了10倍)
		printf("正在检测");
		rt_thread_delay(100);
		printf(".");
		rt_thread_delay(100);
		printf(".");
		rt_thread_delay(100);
		printf(".");
		rt_thread_delay(100);
		printf(".");
		rt_thread_delay(100);
		printf(".");
		rt_thread_delay(100);
		printf(".");
		rt_thread_delay(100);
		printf(".");
		rt_thread_delay(100);
		printf(".");
		rt_thread_delay(100);
		printf(".");
		rt_thread_delay(100);
		printf(".");
		printf("\r\n");
		printf("温度:%d%d.%d",t1/100,(t1/10)%10,t1%10);	// 这里需要对温度进行计算后才能得到我们需要的温度值
		printf("湿度:%d%d.%d",c1/100,(c1/10)%10,c1%10); // 这里同样需要对适度进行计算
		printf("\r\n");
		printf("等待");
		rt_thread_delay(100);
		printf(".");
		rt_thread_delay(100);
		printf(".");
		rt_thread_delay(100);
		printf(".");
		rt_thread_delay(100);
		printf(".");
		rt_thread_delay(100);
		printf(".");
		rt_thread_delay(100);
		printf(".");
		rt_thread_delay(100);
		printf(".");
		rt_thread_delay(100);
		printf(".");
		rt_thread_delay(100);
		printf(".");
		rt_thread_delay(100);
		printf(".");
		printf("\r\n");
	}
}

这里直接给出完整代码如下:

点击查看完整 app_rt_thread.c 代码
然后我们在主函数中引入 RT-Thread 必要的头文件并引用函数:

#include "rtthread.h"
 
extern void MX_RT_Thread_Init(void);
extern void MX_RT_Thread_Process(void);

最后我们直接在主函数中对进程进行初始化,并运行即可,这里直接给出完整主函数:

int main(void)
{
  /* USER CODE BEGIN 1 */
 
  /* USER CODE END 1 */
 
  /* MCU Configuration--------------------------------------------------------*/
 
  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();
 
  /* USER CODE BEGIN Init */
 
  /* USER CODE END Init */
 
  /* Configure the system clock */
  SystemClock_Config();
 
  /* USER CODE BEGIN SysInit */
 
  /* USER CODE END SysInit */
 
  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_DMA_Init();
  MX_I2C1_Init();
  MX_TIM3_Init();
  MX_USART1_UART_Init();
  MX_TIM2_Init();
  /* USER CODE BEGIN 2 */
	
	MX_RT_Thread_Init();	// 初始化线程
 
  /* USER CODE END 2 */
 
  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
		
		MX_RT_Thread_Process();	// 执行主进程
		
    /* USER CODE END WHILE */
 
    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}

按钮的使用与消抖

首先我们设计按钮在 PA3 引脚,然后在设置 PA4 引脚上外接一个 LED ,这里先令按钮按下反转 PA4 引脚电平,即按下小灯泡点亮,再次按下,小灯泡熄灭,这里直接设计为一个新进程:

struct rt_thread btnclick_thread;
rt_uint8_t rt_btnclick_thread_stack[128];
void btnclick_task_entry(void *parameter);
 
void btnclick_task_entry(void *parameter){
	while(1){
		switch(KEY_Scan(0))
		{				 
			case KEY1_PRES:	
				HAL_GPIO_TogglePin(GPIOA,GPIO_PIN_4);
				break;
			default:
				break;
		}
	}
}

初始化线程:

//初始化线程
rt_thread_init(&btnclick_thread,"btnclick",btnclick_task_entry,RT_NULL,&rt_btnclick_thread_stack[0],sizeof(rt_btnclick_thread_stack),3,20);
//开启线程调度
rt_thread_startup(&btnclick_thread);

但是我们不进行按键消抖,这里按键非常容易误触,所以这里进行按键消抖:

#define KEY1  HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_3)	//读取按键1
#define KEY1_PRES 1		//KEY1按下
uint8_t KEY_Scan(uint8_t mode)
{
	static uint8_t key_up=1;//按键松开标志位
	if(key_up&&(KEY1==0))
	{
		HAL_Delay(10);//去抖动
		key_up=0;
		if(KEY1==0)return KEY1_PRES;
	}
	else if(KEY1==1)key_up=1;
 	return 0;//无按键按下
}

六、定时开关灯与信息获取

这里我们主要实现能够定时开关灯,并且能够获取如下信息:

1、病人按下紧急呼叫信号

2、定时获取病人脉搏血氧数据

定时开关灯

这里要求我们能够实现在早上 7 点开灯,晚上 22 点灭灯,所以我们就要设计 RTC 系统时间与日历读取,确定我们当前的时间与日期,然后判断时间数,最终通过 PWM 实现灯的渐亮与渐灭

这里首先我们需要配置 RTC:

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
这里先设置两个变量用于获取时间和日期:

RTC_DateTypeDef Date;
RTC_TimeTypeDef Time;

然后我们可以通过时间进行判断从而实现开关灯的效果:

void ledauto_task_entry(void *parameter){
	HAL_TIM_PWM_Start(&htim2,TIM_CHANNEL_2);
	while(1){
		HAL_RTC_GetTime(&hrtc,&Time,RTC_FORMAT_BIN);
		if(Time.Hours == 7&&Time.Minutes == 0&&Time.Seconds == 0){
			printf("开灯!");
			for(uint16_t i=1;i<500;i++){
				htim2.Instance->CCR2 = i;
				rt_thread_delay(5);
			}
		}
		else if(Time.Hours == 22&&Time.Minutes == 0&&Time.Seconds == 0){
			printf("关灯!");
			for(uint16_t i=499;i>=1;i--){
				htim2.Instance->CCR2 = i;
				rt_thread_delay(5);
			}
		}
	}
}

根据先前博客内容来写通过RTC读取日历和时间的函数:

void MX_RTC_Init(void)
{
 
  /* USER CODE BEGIN RTC_Init 0 */
 
  /* USER CODE END RTC_Init 0 */
 
  RTC_TimeTypeDef sTime = {0};
  RTC_DateTypeDef DateToUpdate = {0};
 
  /* USER CODE BEGIN RTC_Init 1 */
	
	__HAL_RCC_BKP_CLK_ENABLE();// 开启后背区域时钟
	__HAL_RCC_PWR_CLK_ENABLE();// 开启电源时钟
 
  /* USER CODE END RTC_Init 1 */
 
  /** Initialize RTC Only
  */
  hrtc.Instance = RTC;
  hrtc.Init.AsynchPrediv = RTC_AUTO_1_SECOND;
  hrtc.Init.OutPut = RTC_OUTPUTSOURCE_NONE;
  if (HAL_RTC_Init(&hrtc) != HAL_OK)
  {
    Error_Handler();
  }
 
  /* USER CODE BEGIN Check_RTC_BKUP */
 
	if(HAL_RTCEx_BKUPRead(&hrtc,RTC_BKP_DR1)!= 0x5051)
	{
	
  /* USER CODE END Check_RTC_BKUP */
 
  /** Initialize RTC and set the Time and Date
  */
  sTime.Hours = 0x7;
  sTime.Minutes = 0x0;
  sTime.Seconds = 0x0;
 
  if (HAL_RTC_SetTime(&hrtc, &sTime, RTC_FORMAT_BCD) != HAL_OK)
  {
    Error_Handler();
  }
  DateToUpdate.WeekDay = RTC_WEEKDAY_THURSDAY;
  DateToUpdate.Month = RTC_MONTH_JANUARY;
  DateToUpdate.Date = 0x12;
  DateToUpdate.Year = 0x23;
 
  if (HAL_RTC_SetDate(&hrtc, &DateToUpdate, RTC_FORMAT_BCD) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN RTC_Init 2 */
	__HAL_RTC_SECOND_ENABLE_IT(&hrtc,RTC_IT_SEC);	 //开启RTC时钟秒中断
	datebuff = DateToUpdate;  //把日期数据拷贝到自己定义的data中
	HAL_RTCEx_BKUPWrite(&hrtc, RTC_BKP_DR1, 0x5051);//向指定的后备区域寄存器写入数据
	HAL_RTCEx_BKUPWrite(&hrtc, RTC_BKP_DR2, (uint16_t)datebuff.Year);
	HAL_RTCEx_BKUPWrite(&hrtc, RTC_BKP_DR3, (uint16_t)datebuff.Month);
	HAL_RTCEx_BKUPWrite(&hrtc, RTC_BKP_DR4, (uint16_t)datebuff.Date);
	HAL_RTCEx_BKUPWrite(&hrtc, RTC_BKP_DR5, (uint16_t)datebuff.WeekDay);
  }
	else
	{
		datebuff.Year    = HAL_RTCEx_BKUPRead(&hrtc, RTC_BKP_DR2);
		datebuff.Month   = HAL_RTCEx_BKUPRead(&hrtc, RTC_BKP_DR3);
		datebuff.Date    = HAL_RTCEx_BKUPRead(&hrtc, RTC_BKP_DR4);
		datebuff.WeekDay = HAL_RTCEx_BKUPRead(&hrtc, RTC_BKP_DR5);
		DateToUpdate = datebuff;
		if (HAL_RTC_SetDate(&hrtc, &DateToUpdate, RTC_FORMAT_BIN) != HAL_OK)
		{
			Error_Handler();
		}
		__HAL_RTC_SECOND_ENABLE_IT(&hrtc,RTC_IT_SEC);	 //开启RTC时钟秒中断		
	}
	getRealTime();
  /* USER CODE END RTC_Init 2 */
 
}

然后我们需要在运行时,实时获取一次时间:

void getRealTime(void)
{
	HAL_RTC_GetTime(&hrtc, &time, RTC_FORMAT_BIN);
	HAL_RTC_GetDate(&hrtc, &datebuff, RTC_FORMAT_BIN);
	HAL_RTCEx_BKUPWrite(&hrtc, RTC_BKP_DR2, (uint16_t)datebuff.Year);
	HAL_RTCEx_BKUPWrite(&hrtc, RTC_BKP_DR3, (uint16_t)datebuff.Month);
	HAL_RTCEx_BKUPWrite(&hrtc, RTC_BKP_DR4, (uint16_t)datebuff.Date);
	HAL_RTCEx_BKUPWrite(&hrtc, RTC_BKP_DR5, (uint16_t)datebuff.WeekDay);
}
 
void HAL_RTCEx_RTCEventCallback(RTC_HandleTypeDef *hrtc)
{
	if(hrtc->Instance == RTC)
	{
		getRealTime();	
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值