基于FreeRTOS和STM32F4的智能门锁系统项目

发布时间:2024年01月13日

一、前言

????????本项目目的在于学习记录和设计讨论,主要设计部分上传至Gitee,像是FreeRTOS和F4基础工程部分的代码没有上传,占用空间。基础框架我用的是正点原子的F4例程,FreeRTOS移植的10.5.1版本。因为不同部分的coding时间不同,所以会有不同风格的注释,读者见谅,哈哈哈哈。

????????我会尽可能把设计思想描述清楚,这样大家有建议和意见可以私信和我交流,或者在gitee提交,github暂时还没上传。

二、设计构思

????????综合之前写过的一些驱动,在加入实时操作系统后的项目更加符合智能门锁的实际需求。整体涉及到四个任务,分别是处理WIFI信息,识别FRID,OLED屏保和舵机开关门。还有很多辅助功能以后有空再添加。

? ? ? ? 1、通过WiFi发送开门指令,也可以将指令改为密码发送,不过目前还没有考虑安全传输的问题,所以采用简单的open字符串。

? ? ? ? 2、通过RC522芯片刷卡开门,目前只实现读取卡号实现开门,读者有兴趣可以自行加入卡内信息读取等内容。

? ? ? ? 3、键盘密码开门的方式没有写,实现起来应该也不麻烦。

? ? ? ? 4、OLED在成功开门时会显示欢迎语,分别对应不同的开门方式;空闲时会加载动图,实现屏保功能,动画内容大家可以跑跑看,我只能说我不是小黑子。

? ? ? ? 5、舵机的话比较简单,通过PWM控制角度,具体的调度方式可以看代码,我都有写注释。

三、源码架构

1、任务一:处理WIFI数据

? ? ? ? 这部分WIFI协议等底层细节没有深究,只是使用模块实现数据发送,并通过串口和单片机通信。具体分析可以看我单独分析的文章:

STM32+esp8266实现单片机与服务器的WiFi通信_基于stm32、esp8266及ov7670的无线图传下位机源码-CSDN博客icon-default.png?t=N7T8https://blog.csdn.net/plmm__/article/details/132409339?spm=1001.2014.3001.5502代码大体没有改动,可直接下载移植。

采用临界区保护任务不被打断,将事件置1。

/**
 * @brief       task1:处理esp8266接收到的数据
 * @param       pvParameters : 传入参数
 * @retval      无
 */
void task1(void * pvParameters)
{
	char *rx_data = NULL;/* 接收缓冲 */
	while(1)
	{
		if(esp8266wifi_rx_sta & 0X8000)//串口2esp收到的信息通过串口1打印
        {
			rx_data = pvPortCalloc(1, 32);/* 接收缓冲 */
	
			taskENTER_CRITICAL();
			{
				esp8266_solve_receive_data(rx_data, esp8266wifi_rx_buf);
				if(!strcmp((const char*)rx_data, "abc123"))
				{
					xEventGroupSetBits(EventGroup_Task, EVENTBIT_0);
				}
				esp8266wifi_rx_sta = 0;
			}
            taskEXIT_CRITICAL();

			vPortFree(rx_data);
        }
		vTaskDelay(100);
	}
}

2、任务二:RC522识别卡号

????????这部分我也单独写过文章进行分析,增加了几个函数,只识别出卡号,不进行后面的读写操作。

基于STM32HAL库,RC522(RFID)模块读写驱动,无线IC卡读写-CSDN博客icon-default.png?t=N7T8https://blog.csdn.net/plmm__/article/details/131930507?spm=1001.2014.3001.5502增加的函数:

/*
 * @brief       等待刷卡
 * @param       *data:要发送的数据
 * @param       ID:要收到数据的ID号
 * @retval      result:成功返回MI_OK,其他失败
 */
uint8_t Wait_RFID_card(void)
{
    /* 寻卡 */
    return PcdRequest(PICC_REQALL, IC_Type);
}


/*
 * @brief       读卡,返回卡号
 * @param       *data:要发送的数据
 * @param       ID:要收到数据的ID号
 * @retval      result:返回1成功,0则失败
 */
int8_t GET_card_ID(void)
{
    /* 防冲撞,读出卡号 */
    if(PcdAnticoll(IC_UID) != MI_OK)
    {
        printf("防冲撞失败,请重试\r\n");
        return -1;
    }

    /* 返回卡号 */
    return find_RFID_card();
}

/**
 * @brief       返回卡号
 * @param       *data:要发送的数据
 * @param       ID:要收到数据的ID号
 * @retval      成功返回卡号,失败返回 0
 */
uint8_t find_RFID_card(void)
{   
    uint8_t result = 0;
    for (uint8_t i = 0; i < CARD_NUM; i++)
    {
        for (uint8_t j = 0; j < 4; j++)/* 对比每个字节 */
        {
            if (IC_UID[j] != card_ID[i][j])
            {
                result = 0;
                break;
            }
            result = 1;
        }

        if (!result)
        {
            result = 0;
        }
        else/* 在数据库内 */
        {
            return i + 1;
        }
    }
    return 0;
}

任务2:

/**
 * @brief       task2:处理RFID刷卡认证
 * @param       pvParameters : 传入参数
 * @retval      无
 */
void task2(void * pvParameters)
{
	int8_t ID;
	while(1)
	{
		if(Wait_RFID_card() == MI_OK)
		{
			// vTaskSuspendAll();
			taskENTER_CRITICAL();
			{
				ID = GET_card_ID();
				if(ID == 0)
					//printf("验证失败, 无效卡\n");
					OLED_ShowString(1, 2, "ERROR card!", 16);
				if(ID > 0)
				{
					//printf("用户 %d, 欢迎回家!\n", ID);
					xEventGroupSetBits(EventGroup_Task, EVENTBIT_1);
				}
			}
            taskEXIT_CRITICAL();			
			// (void)xTaskResumeAll();
		}
		vTaskDelay(100);
	}
}

3、任务三:OLED屏保

????????这部分用到了任务通知和软件定时器,并挂起自身取消屏保刷新,最后采用绝对延时保证刷新时间保持一致。解除挂起由软件定时器的回调函数完成。这部分用到的FreeRTOS内容比较多,都有注释,欢迎读者给予改进建议。

? ? ? ? 任务三的挂起可以放到其他任务中,考虑到任务三的优先级最低,同时可以提高其他任务的执行效率,所以让任务三完成挂起自身并设置软件定时器。

/**
 * @brief       task3:空闲时OLED屏保,如果发生开门事件,则延时一段时间再刷新OLED
 * @param       pvParameters : 传入参数
 * @retval      无
 */
void task3(void * pvParameters)
{
	uint8_t i = 0;
	TickType_t pxPreviousWakeTime;
	while(1)
	{		
		/* 如果接收到任务通知,把通知值清 0,不阻塞等待 */
		if(ulTaskNotifyTake(pdTRUE, 0))
		{	
			/* 设置软件定时器,并挂起自身,超时再唤醒 */	
			xTimerStart(Timer1_Task, portMAX_DELAY);
			vTaskSuspend(NULL);/* 挂起自身 */
		}

		/* 记录当前时间 */
		pxPreviousWakeTime = xTaskGetTickCount();

		/* 屏保显示 */
		OLED_DrawBMP(0, 0, i++, frame_len, frame_width);
		OLED_Refresh_Gram();
		if(i >= page_sum)
			i = 0;

		/* 采用绝对延时,保证刷新时间可控 */
		vTaskDelayUntil(&pxPreviousWakeTime, refresh_speed);
	}
}


/* 软件定时器回调函数 */
void Timer1_Task_Callback(TimerHandle_t xTimer)
{
	/* 唤醒屏保显示 */
	vTaskResume(task3_handler);
}

4、任务四:舵机开门

? ? ? ? 这里采用FreeRTOS的事件标志组,来等待任意一个开门请求,等待时间为最大,就是阻塞等待。然后在屏幕上显示欢迎语,这里使用任务通知来让OLED屏保任务,挂起自身,让欢迎语保持显示一段时间。

/**
 * @brief       task4:舵机开关门
 * @param       pvParameters : 传入参数
 * @retval      无
 */
void task4(void * pvParameters)
{
	volatile EventBits_t EventValue;
	while(1)
	{		
		/* 等待其中一个事件完成,皆可启动舵机 */
		EventValue = xEventGroupWaitBits(EventGroup_Task, EVENTBIT_ALL, pdTRUE, pdFALSE, portMAX_DELAY);

		/* 保留欢迎语一段时间,再进行OLED屏保 */
		xTaskNotifyGive(task3_handler);

		OLED_Clear();

		switch (EventValue)
		{
			case EVENTBIT_0:
				//printf("WIFI开门成功\r\n");
				OLED_ShowString(1, 2, "WIFI open door!", 16);
				break;
			case EVENTBIT_1:
				//printf("刷卡开门成功\r\n");
				OLED_ShowString(1, 2, "RFID open door!", 16);
				break;
			case EVENTBIT_2:
				//printf("密码开门成功\r\n");
				OLED_ShowString(1, 2, "PassWord open door!", 16);
				break;
		}
		OLED_Refresh_Gram();

		/* 舵机偏转开门 */
		set_Angle(180);

		/* 保持开锁 5秒 */
		vTaskDelay(5000);

		/* 舵机偏转锁门 */
		set_Angle(0);

		vTaskDelay(10);
	}
}

5、其他

????????其余未上传的代码与具体的板耦合较多,还请读者自行修改,有问题可以和我讨论,我也一起学习。

四、总结

总体来说项目比较简单,个人能力有限,以后有时间再继续完善,欢迎广大读者朋友私信交流,一起学习提升!

gitee指路:

stm32: 一些stm32模块使用经验记录 - Gitee.comicon-default.png?t=N7T8https://gitee.com/lrf1125962926/stm32/tree/Housekeeper%2BFreeRTOS/

是不是该收拾一下了

文章来源:https://blog.csdn.net/plmm__/article/details/135547522
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。