基于物联网技术的智慧病房管理系统(二)—— RTOS、AHT20 与 按钮功能实现
基于物联网技术的智慧病房管理系统(二)—— RTOS、AHT20 与 按钮功能实现
这里 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 代码
#include "rtthread.h"
#include "main.h"
#include "i2c.h"
#include "usart.h"
#include "gpio.h"
#include "stdio.h"
#include "AHT20.h"
struct rt_thread led1_thread;
struct rt_thread usart1_thread;
rt_uint8_t rt_led1_thread_stack[128];
rt_uint8_t rt_usart1_thread_stack[256];
void led1_task_entry(void *parameter);
void usart1_task_entry(void *parameter);
int fputc(int ch,FILE *f)
{
HAL_UART_Transmit(&huart1,(uint8_t *)&ch,1,0xFFFF);
//等待发送结束
while(__HAL_UART_GET_FLAG(&huart1,UART_FLAG_TC)!=SET){
}
return ch;
}
//初始化线程函数
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");
}
}
然后我们在主函数中引入 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;//无按键按下
}
由于我们整个系统是通过仿真模拟的形式进行测试,所以这里使用常规按键输入判断在 Proteus 中反应不够灵敏,所以这里采用中断的形式来实现按钮的效果,即按键触发外部中断:
这里我们需要将按钮设置好的 GPIO_Input 改为 GPIO_EXIT :
然后设计相关代码:
首先需要重写中断函数,这里可以看我们前面的博客:
这里直接给出代码:
__weak void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
/* Prevent unused argument(s) compilation warning */
UNUSED(GPIO_Pin);
/* NOTE: This function Should not be modified, when the callback is needed,
the HAL_GPIO_EXTI_Callback could be implemented in the user file
*/
if(GPIO_Pin == GPIO_PIN_3){
if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_3) == 0){
HAL_GPIO_TogglePin(GPIOA,GPIO_PIN_4);
}
}
}
现在我们的按键就灵敏好用了,这里要求是获取病人按下紧急呼叫按钮的信息,这里我们将按钮控制灯亮扩展到三个按钮和三个灯:
__weak void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
/* Prevent unused argument(s) compilation warning */
UNUSED(GPIO_Pin);
/* NOTE: This function Should not be modified, when the callback is needed,
the HAL_GPIO_EXTI_Callback could be implemented in the user file
*/
switch(GPIO_Pin){
case GPIO_PIN_3:{
HAL_GPIO_TogglePin(GPIOB,GPIO_PIN_13);
break;
}
case GPIO_PIN_4:{
HAL_GPIO_TogglePin(GPIOB,GPIO_PIN_14);
break;
}
case GPIO_PIN_5:{
HAL_GPIO_TogglePin(GPIOB,GPIO_PIN_15);
break;
}
}
}
这样我们就完成了对 RTOS 移植,AHT20 温湿度数据获取和按键功能的实现。