freeRTOS小项目练习—智能门锁

目录

一、模块验证 

1:RC522门禁模块

2:AS608指纹识别

3:添加CPU任务统计监控 

4:ESP8266-01S

5:舵机

二、系统整合


一、模块验证 

1:RC522门禁模块

        在搜寻模块的驱动的文章时找到了一个硬件SPI驱动,试运行了可信,但在第二天再次测试会卡死在硬件SPI不能正常工作。应该是硬件SPI的配置还有问题。后来参考普中的例程用模拟SPI模块工作正常。
        在硬件SPI驱动的读一次ID卡的测试函数时,RC522_Read_ID_Once(char *cardID),需要实现复位,再开启天线。在硬件SPI的博文中没有这段会到时卡无法读取到。

void RC522_Read_ID_Once(char *cardID)
{
	char Str1[20],Str2[20];
	u8 card_type[2];//卡片类型,2字节
	u8 card_ID[4];//卡序列号
	u8 statusRt;
	PcdReset(); //复位RC522
	PcdAntennaOff(); //关闭天线
	PcdAntennaOn(); //开启天线
	statusRt = PcdRequest(PICC_REQIDL, card_type);//寻未进入休眠的卡
	if(statusRt == MI_OK){//寻卡成功
		printf("寻卡成功~!\r\n");
		sprintf ( Str1, "card_type: %02X%02X",
					card_type [ 0 ],
					card_type [ 1 ]);
		printf ( "%s\r\n",Str1); 
		if( PcdAnticoll (card_ID) == MI_OK){//防冲撞成功
			sprintf ( Str2, "The Card ID is: %02X%02X%02X%02X",
					card_ID [ 0 ],
					card_ID [ 1 ],
					card_ID [ 2 ],
					card_ID [ 3 ] );
			printf ( "%s\r\n",Str2); 
			if(PcdSelect(card_ID) == MI_OK){
				printf("选卡成功!\r\n");
				if(PcdHalt() == MI_OK){
					printf("休眠成功!\r\n");
					sprintf ( cardID, "%02X%02X%02X%02X", card_ID [ 0 ], card_ID [ 1 ], card_ID [ 2 ], card_ID [ 3 ] );
						printf("read suc!\r\n");
				}
			}
		}
	}
}

分别创建LED任务与RC522读取任务

#include "system.h"
#include "SysTick.h"
#include "led.h"
#include "usart.h"
#include "FreeRTOS.h"
#include "task.h"
#include "rc522.h"


//任务优先级
#define START_TASK_PRIO		1
//任务堆栈大小	
#define START_STK_SIZE 		500  
//任务句柄
TaskHandle_t StartTask_Handler;
//任务函数
void start_task(void *pvParameters);

//任务优先级
#define LED_TASK_PRIO		2
//任务堆栈大小	
#define LED_STK_SIZE 		500  
//任务句柄
TaskHandle_t LED_Task_Handler;
//任务函数
void led_task(void *pvParameters);

//任务优先级
#define RC522_TASK_PRIO		3
//任务堆栈大小	
#define RC522_STK_SIZE 		500  
//任务句柄
TaskHandle_t RC522Task_Handler;
//任务函数
void RC522_task(void *pvParameters);

static void BSP_Init(void);



/*******************************************************************************
* 函 数 名         : main
* 函数功能		   : 主函数
* 输    入         : 无
* 输    出         : 无
*******************************************************************************/
int main()
{
	BSP_Init();
	
	//创建开始任务
    xTaskCreate((TaskFunction_t )start_task,            //任务函数
                (const char*    )"start_task",          //任务名称
                (uint16_t       )START_STK_SIZE,        //任务堆栈大小
                (void*          )NULL,                  //传递给任务函数的参数
                (UBaseType_t    )START_TASK_PRIO,       //任务优先级
                (TaskHandle_t*  )&StartTask_Handler);   //任务句柄              
    vTaskStartScheduler();          //开启任务调度
}

//开始任务任务函数
void start_task(void *pvParameters)
{
    taskENTER_CRITICAL();           //进入临界区
      
    //创建LED1任务
    xTaskCreate((TaskFunction_t )led_task,     
                (const char*    )"led_task",   
                (uint16_t       )LED_STK_SIZE, 
                (void*          )NULL,
                (UBaseType_t    )LED_TASK_PRIO,
                (TaskHandle_t*  )&LED_Task_Handler);
	printf("led_task创建成功\r\n");				
				
	//创建LED2任务
    xTaskCreate((TaskFunction_t )RC522_task,     
                (const char*    )"RC522_task",   
                (uint16_t       )RC522_STK_SIZE, 
                (void*          )NULL,
                (UBaseType_t    )RC522_TASK_PRIO,
                (TaskHandle_t*  )&RC522Task_Handler);
	printf("RC522_task创建成功\r\n");					
    vTaskDelete(StartTask_Handler); //删除开始任务
    taskEXIT_CRITICAL();            //退出临界区
} 

//LED1任务函数
void led_task(void *pvParameters)
{
    while(1)
    {
        led1=!led1;
        vTaskDelay(1000);
    }
}

//LED2任务函数
void RC522_task(void *pvParameters)
{
    char cid[4];
	while(1)
    {
        RC522_Read_ID_Once(cid);
		if(strlen(cid)>0)
		{
			printf("CID = %s\r\n",cid);
			memset(cid,0,sizeof(cid));
			
		}
        vTaskDelay(1000);
    }
}
//板级外设初始化
void BSP_Init(void)
{
	SysTick_Init(72);
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);//设置系统中断优先级分组4
	LED_Init();
	USART1_Init(115200);
	RC522_Init();

}


 参考文章: 

STM32使用硬件SPI驱动RC522门禁模块_rc522 spi_NameisBoy的博客-CSDN博客

2:AS608指纹识别

参照网上文章,花了一天时间跟跟着写了一遍驱动代码,验证模块功能。

1:温习STM32的串口配置

2:见到了goto函数的应用

        freeRTOS小项目练习—智能门锁_第1张图片

3:中断优先级,和外部中断的配置,模块用到了GPIO的外部中断。

4:定义宏函数开关中断

      #define OPEN_USART2_RECEIVE

                    USART_ITConfig(USART2,USART_IT_RXNE,ENABLE)

5:宏函数:meset函数:

        #define CLEAR_BUFFER

           {memset(as608_receive_data,'\0',sizeof(as608_receive_data));as608_receive_count = 0;}

6:strstr函数:

         strstr((char *)as608_receive_data,(char *)check_head);

7:驱动以及函数模块化的编写

8:统一的代码函数

freeRTOS小项目练习—智能门锁_第2张图片

9:函数参数为指针的指针:

freeRTOS小项目练习—智能门锁_第3张图片

freeRTOS小项目练习—智能门锁_第4张图片

freeRTOS小项目练习—智能门锁_第5张图片

https://blog.csdn.net/weixin_47457689/article/details/125013474

参考文章:基于STM32F103——AS608指纹模块+串口打印_cubemx as608_皮卡丘吉尔的博客-CSDN博客 

3:添加CPU任务统计监控 

1:使能FreeRTOSConfig.h中的

        //启用运行时间统计功能
        #define configGENERATE_RUN_TIME_STATS            1             
         //启用可视化跟踪调试
        #define configUSE_TRACE_FACILITY                       1  

2:并且实现两个宏定义

        配置一个高精度定时器/计数器提供时基

        #define portCONFIGURE_TIMER_FOR_RUN_TIME_STATS()  (CPU_RunTime = 0ul)

        读取时期的时间值

        #define portGET_RUN_TIME_COUNTER_VALUE()         CPU_RunTime

3:配置定时器在和一个外部全局变量

        volatile uint32_t CPU_RunTime = 0ul;

        在定时器中断函数递加CPU_RunTime

        void TIM6_IRQHandler(void)
        {
            if(TIM_GetITStatus(TIM6,TIM_IT_Update))
         {
                CPU_RunTime++;
          }
            TIM_ClearITPendingBit(TIM6,TIM_IT_Update);
        } 

4:调用接口函数获取任务信息

        vTaskList((char *)&CPU_RunInfo);        //获取任务列表

        vTaskGetRunTimeStats((char *)&CPU_RunInfo);        //获取任务运行状态

//CPU_task
void CPU_task(void *pvParameters)
{
	uint8_t CPU_RunInfo[400];
	
	while(1)
	{
		
		if(KEY_Scan(0) == KEY_UP_PRESS)
		{
		memset(CPU_RunInfo,0,sizeof(CPU_RunInfo));
		vTaskList((char *)&CPU_RunInfo);		//获取任务列表
		printf("---------------------------------------------\r\n");
		printf("任务名      任务状态 优先级   剩余栈 任务序号\r\n");
		printf("%s", CPU_RunInfo);
		printf("---------------------------------------------\r\n");
		
		memset(CPU_RunInfo,0,400);
		vTaskGetRunTimeStats((char *)&CPU_RunInfo);		//获取任务运行状态
		printf("任务名       运行计数         利用率\r\n");
		printf("%s", CPU_RunInfo);
		printf("---------------------------------------------\r\n");
		}
		vTaskDelay(100);	
	}

}

freeRTOS小项目练习—智能门锁_第6张图片

问题点:vTaskGetRunTimeStats((char *)&CPU_RunInfo);        //获取任务运行状态无法打印出来

freeRTOS小项目练习—智能门锁_第7张图片

#define tskBLOCKED_CHAR ( 'B' ) 任务阻塞

#define tskREADY_CHAR ( 'R' ) 任务就绪

#define tskDELETED_CHAR ( 'D' ) 任务删除

#define tskSUSPENDED_CHAR ( 'S' ) 任务挂起

排除后发现是定时器配置问题:开错了时钟线应该是RCC_APB1PeriphClockCmd

freeRTOS小项目练习—智能门锁_第8张图片

知识点补充:按键的扫描与长按短按(通过位带操作读取电平)

(1条消息) 按键实验_林中明月间的博客-CSDN博客

4:ESP8266-01S

第一次使用:用AT指令,ESP8266作为服务端模式下的wifi,让后在手机上发送相关的指令,esp8266接收后再通过串口发送给单片机 。

freeRTOS小项目练习—智能门锁_第9张图片

freeRTOS小项目练习—智能门锁_第10张图片

1:va_list,va_start,stm32串口格式化输出

        stm32的串口输出中,可以将串口寄存器->DR的数据通过一下形式格式化输出;

2:定时器在串口中断函数中的应用,判断接受到的是wifi发送的最后一条信息,因为回来的信息没有 \r\n。(即规定一段接收时间来接收,超过这个接收时间即认为已经传输完成)

        stm32配置wifi_wifi模块程序_Tang_mmm的博客-CSDN博客

3:用自定义的数据USART3_RX_STA(自己定义的一个u16数据非库函数)来标记串口接收状态

 if(USART3_RX_STA)
        {
            printf("%s\r\n",USART3_RX_BUF);
            memset(USART3_RX_BUF,0,sizeof(USART3_RX_BUF));
            USART3_RX_STA = 0;
        }

参考文章:

ESP8266与STM32_stm32 esp8266_小殷丫Coding的博客-CSDN博客

STM32基于hal库的串口printf格式化接收与发送_stm32 hal printf_Crazy_Summer_CSD的博客-CSDN博客

va_list,va_start,stm32串口格式化输出

【STM32基础】第二篇、STM32串口的使用_usart3_buff[i3]=='k_大屁桃的博客-CSDN博客  

【嵌入式模块】再探ESP8266,保姆级教程_arduino esp8266ex_记录无知岁月的博客-CSDN博客

TCP-IP AT 示例 - ESP32 - — ESP-AT 用户指南 latest 文档

5:舵机

1:用定时器合成PWM控制舵机

freeRTOS小项目练习—智能门锁_第11张图片

2:舵机工作在50Hz即20ms

        PWM_Init(20000-1,72-1);配置成50Hz的PWM 

 3:设计任意角的讲解

      void Set_Angle(float angle){
            int servo_temp;
            if(angle > 180) angle = 180;
            else if(angle < 0) angle = 0;
            servo_temp = angle*2000/180 + 500;
            TIM_SetCompare1(TIM3,servo_temp);}
      

TIM_SetCompare1为设置占空比,我们设置的重载值Period是20000,即和这个20000相比比如servo_temp为10000,就是占空比50%的50Hz方波。

20000用来表示20ms,所以每1ms就是1000,。

舵机首先要在0°所以就是0.5ms*1000 = 500,然后最大角度是180°是2.5ms,即2.5*1000 = 2500,。2500扣除起始0°还剩2000,所以依靠这2000来调节舵机的任意角度。

对应servo_temp = angle*2000/180 + 500;

 

freeRTOS小项目练习—智能门锁_第12张图片

二、系统整合

简述框架

freeRTOS小项目练习—智能门锁_第13张图片

1:基于事件组进行任务间通信

2:各种中断优先级的配置

3:任务的优先级配置

4: 任务延时的时间间隔

main.c文件函数

#include "myconfig.h"
#include "FreeRTOS.h"
#include "task.h"
#include "event_groups.h"

unsigned char ID[4] = {0x7C,0X55,0X13,0XE0};

//START_TASK
#define START_TASK_PRIO		1
#define START_STK_SIZE 		512  
TaskHandle_t StartTask_Handler;
void start_task(void *pvParameters);

//LED_TASK
#define LED_TASK_PRIO		2	
#define LED_STK_SIZE 		512  
TaskHandle_t LED_Task_Handler;
void led_task(void *pvParameters);

//RCC_TASK
#define RC522_TASK_PRIO		3
#define RC522_STK_SIZE 		200  
TaskHandle_t RC522Task_Handler;
void RC522_task(void *pvParameters);

//AS608_TASK
#define AS608_TASK_PRIO     3
#define AS608_STK_SIZE      200
TaskHandle_t AS608_Task_Handler;
void AS608_task(void * pvParameters);
//这个不能怎么干测试用后面删掉
uint8_t handler_flag;

//CPU_Task
#define CPU_TASK_PRIO		3
#define CPU_STK_SIZE 		250  
TaskHandle_t CPUTask_Handler;
void CPU_task(void *pvParameters);

//ESP8266_Task
#define ESP8266_TASK_PRIO   3
#define ESP8266_STK_SIZE   512
TaskHandle_t ESP8266_Task_Handler;
void ESP8266_task(void* pvParameters);

//Servo_Task
#define SERVO_TASK_PRIO   4
#define SERVO_STK_SIZE   200
TaskHandle_t SERVO_Task_Handler;
void SERVO_task(void* pvParameters);

//EventGroup
EventGroupHandle_t Event_Handler = NULL;
#define EVENT1		(0X01<<0)
#define EVENT2		(0X01<<1)
#define EVENT3		(0X01<<2)

static void BSP_Init(void);
void Suspen_Servo(void);

u8 err=0;



/*******************************************************************************
* 函 数 名         : main
* 函数功能		   : 主函数
* 输    入         : 无
* 输    出         : 无
*******************************************************************************/
int main()
{
	BSP_Init();
	
	//创建开始任务
    xTaskCreate((TaskFunction_t )start_task,            //任务函数
                (const char*    )"start_task",          //任务名称
                (uint16_t       )START_STK_SIZE,        //任务堆栈大小
                (void*          )NULL,                  //传递给任务函数的参数
                (UBaseType_t    )START_TASK_PRIO,       //任务优先级
                (TaskHandle_t*  )&StartTask_Handler);   //任务句柄              
    vTaskStartScheduler();          //开启任务调度
}

//开始任务任务函数
void start_task(void *pvParameters)
{
    taskENTER_CRITICAL();           //进入临界区
      
    //创建LED1任务
    xTaskCreate((TaskFunction_t )led_task,     
                (const char*    )"led_task",   
                (uint16_t       )LED_STK_SIZE, 
                (void*          )NULL,
                (UBaseType_t    )LED_TASK_PRIO,
                (TaskHandle_t*  )&LED_Task_Handler);
	printf("led_task创建成功\r\n");				
				
	//创建RC522_task任务
    xTaskCreate((TaskFunction_t )RC522_task,     
                (const char*    )"RC522_task",   
                (uint16_t       )RC522_STK_SIZE, 
                (void*          )NULL,
                (UBaseType_t    )RC522_TASK_PRIO,
                (TaskHandle_t*  )&RC522Task_Handler);
	printf("RC522_task创建成功\r\n");					
    
    //创建As608_task任务
    xTaskCreate((TaskFunction_t)AS608_task,
                (const char*  )"AS608_task",
                (uint16_t     )AS608_STK_SIZE,
                (void*        )NULL,
                (UBaseType_t  )AS608_TASK_PRIO,
                (TaskHandle_t*)&AS608_Task_Handler);
    printf("AS608_task创建成功\r\n");
	//创建CPU_task任务
	xTaskCreate((TaskFunction_t)CPU_task,
				(const char *  )"CPU_task",
				(uint16_t      )CPU_STK_SIZE,
				(void *        )NULL,
				(UBaseType_t   )CPU_TASK_PRIO,
				(TaskHandle_t* )&CPUTask_Handler);
	printf("CPU_task创建成功\r\n");
    //创建ESP_8266任务
    xTaskCreate((TaskFunction_t)ESP8266_task,
                (const char*   )"ESP8266_task",
                (uint16_t      )ESP8266_STK_SIZE,
                (void*         )NULL,
                (UBaseType_t   )ESP8266_TASK_PRIO,
                (TaskHandle_t  )&ESP8266_Task_Handler);
    printf("ESP_8266task创建成功\r\n");	
    //创建SERVO_Task任务
    xTaskCreate((TaskFunction_t)SERVO_task,
                (const char*   )"SERVO_task",
                (uint16_t      )SERVO_STK_SIZE,
                (void*         )NULL,
                (UBaseType_t   )SERVO_TASK_PRIO,
                (TaskHandle_t  )&SERVO_Task_Handler);
    printf("SERVO_Tasktask创建成功\r\n");

	Event_Handler = xEventGroupCreate();
				
    vTaskDelete(StartTask_Handler); //删除开始任务
    taskEXIT_CRITICAL();            //退出临界区
} 

//LED1任务函数
void led_task(void *pvParameters)
{
    while(1)
    {
        LED1=!LED1;
        vTaskDelay(1000);
    }
}

//RC522_task任务函数
void RC522_task(void *pvParameters)
{
    unsigned char cid[4];
	while(1)
    {
		if(Request())
		{
			RC522_Read_ID_Once(cid);
			if((cid[0] == ID[0]) && (cid[1] == ID[1])&& (cid[2] == ID[2]) && (cid[3] == ID[3]))
			{
				printf("card succed!\r\n");
				xEventGroupSetBits(Event_Handler,EVENT1);
				delay_ms(1000);
				memset(cid,0,sizeof(cid));	
			}
			else{
				err++;
				Suspen_Servo();
			}
		}
       vTaskDelay(100);
    }
}

//AS608_task任务函数
void menu(void)
{
	printf("=============================\r\n");
	printf("\t1-添加指纹\r\n");
	printf("\t2-验证指纹\r\n");
	printf("\t3-删除指纹\r\n");
	printf("\t4-查询指纹个数\r\n");
	printf("=============================\r\n");
}
void AS608_task(void * pvParameters)
{
//    printf("      --指纹模块小Demo--\r\n");
//    menu();
    uint8_t ID = 0;
    while (1)
    {
		if(finger_status == FINGER_EXIST)
		{
			  if(Verify_Fingerprint())
			  {
					printf("Finger succed!\r\n");
					xEventGroupSetBits(Event_Handler,EVENT2);
			  }
			  else{
				err++;
				Suspen_Servo();
			  }
		}
		vTaskDelay(100);
#if 0

		switch (handler_flag)               
        {
        case 1:
            SCANF:
                printf("\t添加指纹\r\n");
                handler_flag = 0;
                ID = 0;
                printf("请输入ID号(1~9)\r\n");
                while (ID == 0)
                {
                    //等为输入添加指纹设置ID号
					ID = handler_flag;
                }
                if(ID > 0 && ID <10) printf("ID号:%d\r\n",ID);
                else                goto SCANF;
                if(Add_Fingerprint(ID)) printf("添加指纹失败\r\n");
                else                    printf("添加指纹成功\r\n");
                handler_flag =  0;
                menu();
        break;
        case 2:
            printf("\t验证指纹\r\n");
            Verify_Fingerprint();
            handler_flag = 0;
            menu();
        break;
        case 3:
            printf("\t删除指纹\r\n");
            Delete_Fingerprint();
            handler_flag = 0;
            menu();
        break;
        case 4:
            printf("\t查看指纹个数\r\n");
            Find_Fingerprints_Num();
            handler_flag = 0;
            menu();
        break;
        default:
        break;
        }

		vTaskDelay(100);
#endif
    }
}
//CPU_task
void CPU_task(void *pvParameters)
{
	uint8_t CPU_RunInfo[400];
	uint8_t key;
	while(1)
	{
		key=KEY_Scan(0);   //扫描按键
		
		if(key == KEY_UP_PRESS)
		{
		memset(CPU_RunInfo,0,sizeof(CPU_RunInfo));
		vTaskList((char *)&CPU_RunInfo);		//获取任务列表
		printf("---------------------------------------------\r\n");
		printf("任务名      任务状态 优先级   剩余栈 任务序号\r\n");
		printf("%s", CPU_RunInfo);
		printf("---------------------------------------------\r\n");
		
		memset(CPU_RunInfo,0,400);
		vTaskGetRunTimeStats((char *)&CPU_RunInfo);		//获取任务运行状态
		printf("任务名       运行计数         利用率\r\n");
		printf("%s", CPU_RunInfo);
		printf("---------------------------------------------\r\n");
		}
		 if(key == KEY0_PRESS)
		{
			printf("舵机任务恢复\r\n");
			vTaskResume(SERVO_Task_Handler);
			err = 0;
			TIM_Cmd(TIM4,DISABLE);
		}
		
		vTaskDelay(100);	
	}

}
//ESP8266_task
void ESP8266_task(void *pvParameters)
{
    while (1)
    {
        if(USART3_RX_STA)
        {
            printf("%s\r\n",USART3_RX_BUF);
			if(strstr((const char*)USART3_RX_BUF,"on"))
			{
				xEventGroupSetBits(Event_Handler,EVENT3);
			}
            memset(USART3_RX_BUF,0,sizeof(USART3_RX_BUF));
            USART3_RX_STA = 0;
        }
        vTaskDelay(100);
    }
}
//SERVO_Task
void SERVO_task(void *pvParameters)
{
    EventBits_t r_event;
	while (1)
    {
        r_event = xEventGroupWaitBits(Event_Handler,EVENT1|EVENT2|EVENT3,pdTRUE,pdFAIL,portMAX_DELAY);
		
		if((r_event & EVENT1) || (r_event & EVENT2) ||(r_event & EVENT3))
		{
		   Warn();
		   printf("OPEN\r\n");
		   Set_Angle(90);
           delay_ms(1000);
           Set_Angle(180);
           delay_ms(1000);
		   err = 0;
		}
        vTaskDelay(100);
    }
}
//板级外设初始化
void BSP_Init(void)
{
	SysTick_Init(72);
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);//设置系统中断优先级分组4
	LED_Init();
	USART1_Init(115200);
	USART2_Init(115200);
	USART3_Init(115200);
	RC522_Init();
    As608_Init();
	KEY_Init();
	ESP8266_Init();
	TIM6_Init(1000-1,72-1);
	TIM4_Init(1000-1,72-1);
	PWM_Init(20000-1,72-1);
	BEEP_Init();

}
//Suspen_Servo
void Suspen_Servo(void)
{
	printf("ERRO:%d",err);
	if(err == 3)
	{
		vTaskSuspend(SERVO_Task_Handler);
		TIM_Cmd(TIM4,ENABLE);
		printf("舵机任务挂起\r\n");
	}

}

演示视频

你可能感兴趣的:(开发语言,嵌入式硬件,单片机)