目录
一、模块验证
1:RC522门禁模块
2:AS608指纹识别
3:添加CPU任务统计监控
4:ESP8266-01S
5:舵机
二、系统整合
在搜寻模块的驱动的文章时找到了一个硬件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博客
参照网上文章,花了一天时间跟跟着写了一遍驱动代码,验证模块功能。
1:温习STM32的串口配置
2:见到了goto函数的应用
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:统一的代码函数
9:函数参数为指针的指针:
https://blog.csdn.net/weixin_47457689/article/details/125013474
参考文章:基于STM32F103——AS608指纹模块+串口打印_cubemx as608_皮卡丘吉尔的博客-CSDN博客
1:使能FreeRTOSConfig.h中的
//启用运行时间统计功能
#define configGENERATE_RUN_TIME_STATS 1
//启用可视化跟踪调试
#define configUSE_TRACE_FACILITY 12:并且实现两个宏定义
配置一个高精度定时器/计数器提供时基
#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);
}
}
问题点:vTaskGetRunTimeStats((char *)&CPU_RunInfo); //获取任务运行状态无法打印出来
#define tskBLOCKED_CHAR ( 'B' ) 任务阻塞
#define tskREADY_CHAR ( 'R' ) 任务就绪
#define tskDELETED_CHAR ( 'D' ) 任务删除
#define tskSUSPENDED_CHAR ( 'S' ) 任务挂起
排除后发现是定时器配置问题:开错了时钟线应该是RCC_APB1PeriphClockCmd
知识点补充:按键的扫描与长按短按(通过位带操作读取电平)
(1条消息) 按键实验_林中明月间的博客-CSDN博客
第一次使用:用AT指令,ESP8266作为服务端模式下的wifi,让后在手机上发送相关的指令,esp8266接收后再通过串口发送给单片机 。
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 文档
1:用定时器合成PWM控制舵机
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;
简述框架
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");
}
}
演示视频