Diseño de esquema de sala inteligente basado en la tecnología integrada de Internet de las cosas


prefacio

1. Requisitos

Diseñe un sistema inteligente de gestión de barrios basado en la tecnología de Internet de las cosas. Suponga que hay salas de 1 piso en el departamento de hospitalización del hospital (las salas están distribuidas en paralelo a ambos lados del pasillo), y hay como máximo 60 salas, cada una con 3 camas, numeradas del 1 al 180. El equipo que se puede utilizar en cada sala es el siguiente: 1 placa de desarrollo STM32F103, 1 juego de módulo de adquisición de temperatura y humedad de la habitación (interfaz I2C, módulo AHT20), controlador de interruptor de luz automático de la habitación (controlado por PWM, se ilumina gradualmente a las 7: 00 todas las mañanas, las 22 en punto se desvanecen), 3 juegos de detectores de pulso y oxígeno en sangre del paciente (pulso de salida de interfaz UART + valor digital de oxígeno en sangre), 3 interruptores de botón de llamada de emergencia junto a la cama (presione para llamar).
La placa de desarrollo STM32F103 en cada sala está conectada a la PC (computadora superior) en la sala de monitoreo de enfermería a través de la interfaz UART a 485 en forma de red mobus. La computadora PC puede recibir los datos de temperatura y humedad de cada sala (el ciclo es de 5 minutos), la señal de llamada de emergencia al lado de la cama y los datos de pulso y oxígeno en sangre del paciente (recolectados una vez cada 30 minutos en condiciones normales; cuando el pulso excede 120 o el valor de oxígeno en sangre es inferior a 90) cambia al estado crítico, recolección en tiempo real), se muestra en la pantalla y se guarda en la base de datos MySQL.

2. Diseño del sistema

Para esta solución, la idea original es configurar un marco multitarea rtos y luego poner todos los marcos en los requisitos en el hilo para completar, incluida la lectura de temperatura y humedad AHT, lectura de datos de pulso y oxígeno en sangre, llamada de emergencia clave , luces del interruptor de sincronización y otras tareas.

3. Módulos funcionales

1. Detección de datos de pulso y oxígeno en sangre: cuando el pulso supera los 120 o el oxígeno en la sangre es inferior a 90, se recopilará en tiempo real; de lo contrario, se recopilará cada 30 minutos.
2. Presione el botón para realizar una llamada de emergencia: una interfaz GPIO juzga el nivel alto y bajo de la señal de entrada.
3. El módulo AHT20 lee la información de temperatura y humedad: una vez cada 5 minutos.
4. Luces de control PWM para encender y apagar a intervalos regulares: se iluminan gradualmente a las 7 de la mañana y se atenúan gradualmente a las 10 de la noche.
5. Realice la comunicación modbus a través de UART a 485.


3. Diagrama del módulo de funciones del sistema

inserte la descripción de la imagen aquí

1. Diagrama esquemático del módulo de control stm32

Diagrama esquemático mínimo STM32:

inserte la descripción de la imagen aquí
Diagrama esquemático AHT20:

inserte la descripción de la imagen aquí
Diagrama esquemático del circuito:
inserte la descripción de la imagen aquí

En segundo lugar, la realización de cada módulo funcional

1. La configuración básica de todo el sistema.

  • Configuración de RCC:

inserte la descripción de la imagen aquí

  • Configuración del sistema

inserte la descripción de la imagen aquí

  • UART establece el modo de puerto serie

inserte la descripción de la imagen aquí
inserte la descripción de la imagen aquí

  • I2C establece el modo DMA:

inserte la descripción de la imagen aquí
inserte la descripción de la imagen aquí

  • HORA3:

inserte la descripción de la imagen aquí
TIME2:
inserte la descripción de la imagen aquí
NVIC:
inserte la descripción de la imagen aquí
GPIO:
inserte la descripción de la imagen aquí
Configuración del árbol del reloj:
inserte la descripción de la imagen aquí
Transplante RT-thread Nano:
inserte la descripción de la imagen aquí

  • Tiempo de configuración:
    inserte la descripción de la imagen aquí

2. RTOS multitarea

Consulte el proceso de trasplante específico:
trasplante RT-thread Nano para completar un dispositivo esclavo de temperatura y humedad de la interfaz modbus, deje que la PC de la computadora superior obtenga la temperatura y la humedad a través del protocolo modbus

1. Hilo de diseño

  • Cree un nuevo archivo app_rt_thread.c en la carpeta Aplicación/USUARIO
    1. Importe los archivos de encabezado necesarios:
#include "rtthread.h"
#include "main.h"
#include "i2c.h"
#include "usart.h"
#include "gpio.h"
#include "stdio.h"
#include "AHT20.h"

Aquí configuramos 4 subprocesos, el proceso principal es enviar datos a la computadora host a través del puerto serie, y los subprocesos incluyen adquisición de temperatura y humedad, medición de datos de pulso y oxígeno en sangre, botón de llamada de emergencia y LED encendido y fuera.

 
struct rt_thread led1_thread;
rt_uint8_t rt_led1_thread_stack[128];
void led1_task_entry(void *parameter);

struct rt_thread usart1_thread;
rt_uint8_t rt_usart1_thread_stack[128];
void usart1_task_entry(void *parameter);

struct rt_thread usart2_thread;
rt_uint8_t rt_usart2_thread_stack[128];
void usart2_task_entry(void *parameter);

struct rt_thread button_thread;
rt_uint8_t rt_button_thread_stack[128];
void button_task_entry(void *parameter);
 
//初始化线程函数
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_init(&usart1_thread,"usart1",usart1_task_entry,RT_NULL,&rt_usart1_thread_stack[0],sizeof(rt_usart1_thread_stack),3,20);
 rt_thread_init(&usart2_thread,"usart2",usart2_task_entry,RT_NULL,&rt_usart2_thread_stack[0],sizeof(rt_usart2_thread_stack),3,20);
 rt_thread_init(&button_thread,"button",button_task_entry,RT_NULL,&rt_button_thread_stack[0],sizeof(rt_button_thread_stack),3,20);
	//开启线程调度
	rt_thread_startup(&led1_thread);
    rt_thread_startup(&usart1_thread);
    rt_thread_startup(&usart2_thread);
    rt_thread_startup(&button_thread);
}
 
//主任务
void MX_RT_Thread_Process(void)
{
    
    
	printf("系统正在运行!!!");
	rt_thread_delay(2000);
}
 
//LED1任务
void led1_task_entry(void *parameter)
{
    
    
}
//读取温湿度
void usart1_task_entry(void *parameter)
{
    
    
}
//读取脉搏&血氧
void usart2_task_entry(void *parameter)
{
    
    
}
//按键
void button_task_entry(void *parameter)
{
    
    
}

2. Configurar el código de función principal

1. Agregue archivos de encabezado e importe las funciones correspondientes en los archivos principal y c
inserte la descripción de la imagen aquí
2. Inicialice el hilo y ejecute el programa principal
inserte la descripción de la imagen aquí

3. Módulo de lectura de temperatura y humedad (I2C)

Proceso específico:
complete la recopilación de datos del sensor de temperatura y humedad AHT20 basado en el protocolo I2C a través de la configuración STM32Cube

Módulo de lectura de temperatura y humedad El sensor que utilizamos aquí es AHT20 y protocolo I2C . El protocolo de comunicación I2C (circuito interintegrado) fue desarrollado por Philps. Debido a que tiene pocos pines, implementación de hardware simple y gran escalabilidad, no requiere dispositivos transceptores externos como USART y CAN. Ahora se usa ampliamente en la comunicación
entre Múltiples circuitos integrados (IC) en un sistema.

1. Después de encender, espere un período de tiempo y espere a que el dispositivo funcione normalmente antes de leer la temperatura y la humedad:

  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);

2. Lea los datos de temperatura y humedad después de la verificación de CRC y luego retrase durante 5 minutos


    AHT20_Read_CTdata_crc(CT_data);  //crc校验后,读取AHT20的温度和湿度数据 
	  c1 = CT_data[0]*100*10/1024/1024;  //计算得到湿度值c1(放大了10倍)
	  t1 = CT_data[1]*200*10/1024/1024-500;//计算得到温度值t1(放大了10倍)	
		printf("湿度:%d%s",c1/10,"%");
	  printf("温度:%d%s",t1/10,"℃");
	  printf("\r\n");
		rt_thread_mdelay(300000);//线程睡眠大约5分钟,挂起执行其他操作

4. Luz del interruptor de sincronización LED (pwm)

Aquí usamos el temporizador time2 configurado anteriormente más PWM.

Obtener fecha y hora:

RTC_DateTypeDef Data; //Obtener estructura de fecha
RTC_TimeTypeDef Time; //Obtener estructura de tiempo

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){
    
    
			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){
    
    
			for(uint16_t i=499;i>=1;i--){
    
    //灯渐灭
				htim2.Instance->CCR2 = i;
				rt_thread_delay(5);
			}
		}
	}

Obtenga el tiempo real una vez como estándar antes de la inicialización:

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();	
	}
}

5. Pulsa el botón para darte cuenta de la señal de alarma.

Debido a que configuramos el pin PB5 como una entrada a voluntad, que es un botón, y el botón se presiona para enviar una señal de alarma. Aquí usamos los niveles alto y bajo de los pines PA4, PA5 y PA6 como señales de alarma de los tres botones:

Código del subproceso del botón:

while(1){
    
    
		switch(KEY_Scan(0))
		{
    
    				 
			case KEY1_PRES:	
				HAL_GPIO_TogglePin(GPIOA,GPIO_PIN_4);
				break;
			case KEY2_PRES:	
				HAL_GPIO_TogglePin(GPIOA,GPIO_PIN_5);
				break;
			case KEY3_PRES:	
				HAL_GPIO_TogglePin(GPIOA,GPIO_PIN_6);
				break;
			default:
				break;
		}
	}

Rebote clave:

uint8_t KEY_Scan(uint8_t mode)
{
    
    
	static uint8_t key_up=1;//按键松开标志位
	if(key_up&&((KEY1==0)||(KEY2==0)||(KEY3==0)))
	{
    
    
		HAL_Delay(10);//过滤抖动时间
		key_up=0;
		if(KEY1==0)return KEY1_PRES;
		if(KEY2==0)return KEY2_PRES;
		if(KEY3==0)return KEY3_PRES;
	}
	else if(KEY1==1&&KEY2==1&&KEY3==1)key_up=1;
 	return 0;//无按键按下
}

6. Lectura de datos de pulso y oxígeno en sangre

Nota: El pulsioxímetro aquí nunca se ha utilizado, y también debe comunicarse a través del puerto serial usando el protocolo modbus:

Para una comunicación específica de usart, consulte: Complete un programa de comunicación serial STM32 USART a través de stm32CubeMX
para definir las direcciones y variables de pulso y oxígeno en sangre:

#define REG_SPO2_ADDRESS     0x0001
#define REG_PULSE_RATE_ADDRESS  0x0002
uint16_t spo2_value;
uint16_t pulse_rate_value;

solicitud de registro de lectura modbus:

void modbus_handle_read_registers(uint8_t *data, uint16_t len)
{
    
    
    uint16_t start_address;
    uint16_t register_count;
    uint16_t i;
    // 解析读寄存器的请求报文
    start_address = (data[2] << 8) | data[3];
    register_count = (data[4] << 8) | data[5];
    // 根据地址和数量读取寄存器数据
    for (i = 0; i < register_count; i++)
    {
    
    
        if (start_address + i == REG_SPO2_ADDRESS)
        {
    
    
            data[9 + i * 2] = (spo2_value >> 8) & 0xFF;
            data[10 + i * 2] = spo2_value & 0xFF;
        }
        else if (start_address + i == REG_PULSE_RATE_ADDRESS)
        {
    
    
            data[9 + i * 2] = (pulse_rate_value >> 8) & 0xFF;
            data[10 + i * 2] = pulse_rate_value & 0xFF;
        }
    }

    // 发送读寄存器响应报文
    uart_send_data(data, 9 + register_count * 2);
}

Función de bucle principal:

void modbus_loop(void)
{
    
    
    uint8_t data[256];
    uint16_t len;
    uint16_t new_spo2;
    uint16_t new_pulse_rate;
    while (1)
    {
    
    
        // 等待接收数据
        len = uart_receive_data(data, sizeof(data));
        // 处理读寄存器请求
        if (data[0] == 0x01 && data[1] == 0x03)
        {
    
    
            modbus_handle_read_registers(data, len);
        }
        // 处理写寄存器请求
        else if (data[0] == 0x01 && data[1] == 0x10)
        {
    
    
            // 根据地址和数量读取寄存器数据
            new_spo2 = (data[9] << 8) | data[10];
            new_pulse_rate = (data[11] << 8) | data[12];
            update_spo2_and_pulse_rate(new_spo2, new_pulse_rate);
            // 发送写寄存器响应报文
            uart_send_data(data, 8);
        }
    }
}

Defina el estado de muestreo:

typedef enum {
    
    
    NORMAL,
    CRITICAL
} SamplingState;

SamplingState sampling_state = NORMAL;

Defina el pulso de tiempo:

#define NORMAL_SAMPLING_INTERVAL 1800 // 30 分钟
#define CRITICAL_SAMPLING_INTERVAL 10 // 10 秒、算作实时
#define CRITICAL_PULSE_RATE_THRESHOLD 120//脉搏120
#define CRITICAL_SPO2_THRESHOLD 90//血氧90

La función de devolución de llamada implementa el muestreo estado por estado:

void sampling_timer_callback(void)
{
    
    
    if (sampling_state == NORMAL)//正常状态
    {
    
    
        if (pulse_rate_value > CRITICAL_PULSE_RATE_THRESHOLD || spo2_value < CRITICAL_SPO2_THRESHOLD)
        {
    
    
            sampling_state = CRITICAL;
            timer_start(CRITICAL_SAMPLING_INTERVAL, sampling_timer_callback);
        }
        else
        {
    
    
            // 正常状态下采样
            send_request_to_spo2_pulse_sensor();
            timer_start(NORMAL_SAMPLING_INTERVAL, sampling_timer_callback);
        }
    }
    else
    {
    
    
        // 危重状态下采样
        send_request_to_spo2_pulse_sensor();
        if (pulse_rate_value <= CRITICAL_PULSE_RATE_THRESHOLD && spo2_value >= CRITICAL_SPO2_THRESHOLD)
        {
    
    
            sampling_state = NORMAL;
            timer_start(NORMAL_SAMPLING_INTERVAL, sampling_timer_callback);
        }
        else
        {
    
    
            timer_start(CRITICAL_SAMPLING_INTERVAL, sampling_timer_callback);
        }
    }
}

7. La interfaz UART a 485 se comunica con modbus

La comunicación RS-485 usa el puerto serie 3, aquí encontramos que hemos configurado un puerto serie menos, la tasa de baudios es 9600, y el DMA de envío y el DMA de recepción del puerto serie 3 están habilitados, y ambos seleccionan el modo normal.

El protocolo Modbus envía datos:

void RS485_BUS_SendData(u8 *buf, u8 len)
{
    
    
	u8 t;
	for(t=0;t<len;t++)
	{
    
    
		while(USART_GetFlagStatus(UART5,USART_FLAG_TC)==RESET);
		USART_SendData(UART5,buf[t]);
	}
	
	while(USART_GetFlagStatus(UART5,USART_FLAG_TC)==RESET);
	RS485_BUS_RXCNT = 0;
}

El protocolo modbus acepta datos:

void RS485_BUS_ReceiveData(u8 *buf,u8 *len)
{
    
    
	u8 rxlen=RS485_BUS_RXCNT;
	u8 i=0;
	*len=0;
	delay_ms(10);
	if((rxlen==RS485_BUS_RXCNT)&&rxlen)				//接收到了数据且接收完成
	{
    
    
		for(i=0;i<rxlen;i++)
		{
    
    
			buf[i]=RS485_BUS_RXBUF[i];
		}
		*len=RS485_BUS_RXCNT;
		RS485_BUS_RXCNT=0;
	}
}

3. Resumen

Este diseño de sistema adopta el marco multitarea de RTthread-nano, que aclara la ruta de todo el sistema a la vez, y también puede establecer el nivel de prioridad de los hilos, para planificar bien todo el sistema. Al mismo tiempo, con este procesamiento de tareas concurrentes de subprocesos múltiples, cada subproceso tiene su propio espacio de ejecución independiente, es decir, la pila de subprocesos, y cada subproceso se enfoca en hacer lo suyo sin interferir entre sí y tiene una gran independencia. Y cuando cada subproceso utiliza la función de retraso, el subproceso suspenderá la tarea y automáticamente permitirá que la CPU sea utilizada por otros subprocesos que la necesiten.Podemos usar de forma segura la función de retraso en el subproceso y, al mismo tiempo, la utilización de la CPU tiene ha mejorado mucho. El código de este sistema solo se divide en el marco multitarea de acuerdo con los módulos diseñados, y se muestra el código principal o parte de cada módulo correspondiente. De hecho, la implementación de cada módulo aún necesita Se necesita una cierta cantidad de tiempo para lograr y te encontrarás con varias dificultades. Aunque esta vez, la gran tarea es solo un diseño de esquema, pero el contenido cubre mucho del contenido de este semestre. Durante el proceso de diseño, descubrí que algunos lugares no eran tan impresionantes, así que revisé la parte correspondiente y luego miré hacia atrás y encontré comprensión. ¡Es diferente, pero todavía se siente bien!

Acho que você gosta

Origin blog.csdn.net/qq_52215423/article/details/128683302
Recomendado
Clasificación