一、前言
本项目目的在于学习记录和设计讨论,主要设计部分上传至Gitee,像是FreeRTOS和F4基础工程部分的代码没有上传,占用空间。基础框架我用的是正点原子的F4例程,FreeRTOS移植的10.5.1版本。因为不同部分的coding时间不同,所以会有不同风格的注释,读者见谅,哈哈哈哈。
我会尽可能把设计思想描述清楚,这样大家有建议和意见可以私信和我交流,或者在github和gitee提交。
二、设计构思
综合之前写过的一些驱动,在加入实时操作系统后的项目更加符合智能门锁的实际需求。整体涉及到四个任务,分别是处理WIFI信息,识别FRID,OLED屏保和舵机开关门。还有很多辅助功能以后有空再添加。
1、通过WiFi发送开门指令,也可以将指令改为密码发送,不过目前还没有考虑安全传输的问题,所以采用简单的open字符串。
2、通过RC522芯片刷卡开门,目前只实现读取卡号实现开门,读者有兴趣可以自行加入卡内信息读取等内容。
3、键盘密码开门的方式没有写,实现起来应该也不麻烦。
4、OLED在成功开门时会显示欢迎语,分别对应不同的开门方式;空闲时会加载动图,实现屏保功能,动画内容大家可以跑跑看,我只能说我不是小黑子。
5、舵机的话比较简单,通过PWM控制角度,具体的调度方式可以看代码,我都有写注释。
三、源码架构
1、任务一:处理WIFI数据
这部分WIFI协议等底层细节没有深究,只是使用模块实现数据发送,并通过串口和单片机通信。具体分析可以看我单独分析的文章:
采用临界区保护任务不被打断,将事件置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识别卡号
这部分我也单独写过文章进行分析,增加了几个函数,只识别出卡号,不进行后面的读写操作。
/*
* @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、其他
其余未上传的代码与具体的板耦合较多,还请读者自行修改,有问题可以和我讨论,我也一起学习。
四、总结
总体来说项目比较简单,个人能力有限,以后有时间再继续完善,欢迎广大读者朋友私信交流,一起学习提升!
GitCode:
Github:
是不是该收拾一下了