实现RFID逻辑

This commit is contained in:
2026-04-14 23:40:46 +08:00
parent 65478d9f02
commit a6359364fd
7 changed files with 970 additions and 1 deletions

View File

@@ -58,6 +58,7 @@ target_sources(${CMAKE_PROJECT_NAME} PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}/Core/Bsp/bsp_pid.c
${CMAKE_CURRENT_SOURCE_DIR}/Core/Bsp/bsp_track_ir.c
${CMAKE_CURRENT_SOURCE_DIR}/Core/Bsp/bsp_sr04.c
${CMAKE_CURRENT_SOURCE_DIR}/Core/Bsp/bsp_rc522.c
)
# Add include paths

View File

@@ -1,5 +1,6 @@
#include "bsp_hall.h"
#include "bsp_sr04.h"
#include "bsp_rc522.h"
/* 霍尔传感器数据结构体定义描述 */
@@ -23,6 +24,7 @@ void hall_init(void)
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
hall_pulse_callback(GPIO_Pin);
rc522_irq_callback(GPIO_Pin);
}
/**

632
Core/Bsp/bsp_rc522.c Normal file
View File

@@ -0,0 +1,632 @@
#include "bsp_rc522.h"
#include <string.h>
// 站点UID白名单可扩展
static const uint8_t g_station_1_uid[] = STATION_1_UID;
static const uint8_t g_station_2_uid[] = STATION_2_UID;
station_id_t rc522_match_station(const uint8_t *uid, uint8_t uid_len)
{
if (uid == NULL || uid_len == 0) return STATION_NONE;
// 匹配站点1
if (uid_len == sizeof(g_station_1_uid) && memcmp(uid, g_station_1_uid, uid_len) == 0) {
return STATION_1;
}
// 匹配站点2
if (uid_len == sizeof(g_station_2_uid) && memcmp(uid, g_station_2_uid, uid_len) == 0) {
return STATION_2;
}
// 可继续添加更多站点匹配
return STATION_NONE;
}
#include "bsp_rc522.h"
#include "spi.h"
#include <string.h>
#include <stdio.h>
/* ========================= RC522 寄存器定义 ========================= */
#define RC522_REG_COMMAND 0x01U
#define RC522_REG_COM_I_EN 0x02U
#define RC522_REG_DIV_I_EN 0x03U
#define RC522_REG_COM_IRQ 0x04U
#define RC522_REG_DIV_IRQ 0x05U
#define RC522_REG_ERROR 0x06U
#define RC522_REG_STATUS2 0x08U
#define RC522_REG_FIFO_DATA 0x09U
#define RC522_REG_FIFO_LEVEL 0x0AU
#define RC522_REG_CONTROL 0x0CU
#define RC522_REG_BIT_FRAMING 0x0DU
#define RC522_REG_COLL 0x0EU
#define RC522_REG_MODE 0x11U
#define RC522_REG_TX_MODE 0x12U
#define RC522_REG_RX_MODE 0x13U
#define RC522_REG_TX_CONTROL 0x14U
#define RC522_REG_TX_ASK 0x15U
#define RC522_REG_CRC_RESULT_H 0x21U
#define RC522_REG_CRC_RESULT_L 0x22U
#define RC522_REG_T_MODE 0x2AU
#define RC522_REG_T_PRESCALER 0x2BU
#define RC522_REG_T_RELOAD_H 0x2CU
#define RC522_REG_T_RELOAD_L 0x2DU
/* ========================= RC522 命令定义 ========================= */
#define RC522_CMD_IDLE 0x00U
#define RC522_CMD_CALC_CRC 0x03U
#define RC522_CMD_TRANSCEIVE 0x0CU
#define RC522_CMD_SOFT_RESET 0x0FU
/* ========================= PICC 命令定义 ========================= */
#define PICC_CMD_REQA 0x26U
#define PICC_CMD_SEL_CL1 0x93U
#define PICC_CMD_SEL_CL2 0x95U
#define PICC_CMD_SEL_CL3 0x97U
#define PICC_CMD_HLTA 0x50U
#define PICC_CMD_CT 0x88U
#define RC522_SPI_TIMEOUT_MS 20U
static rc522_card_info_t g_last_card;
static uint32_t g_run_led_hold_until_ms = 0U;
static uint32_t g_last_new_event_tick_ms = 0U;
static volatile uint8_t g_irq_pending = 1U;
#if (RC522_RUN_LED_ON_LEVEL == GPIO_PIN_SET)
#define RC522_RUN_LED_OFF_LEVEL GPIO_PIN_RESET
#else
#define RC522_RUN_LED_OFF_LEVEL GPIO_PIN_SET
#endif
static void rc522_set_run_led(uint8_t on)
{
#if (RC522_RUN_LED_ENABLE == 1U)
HAL_GPIO_WritePin(RUN_LED_GPIO_Port,
RUN_LED_Pin,
(on != 0U) ? RC522_RUN_LED_ON_LEVEL : RC522_RUN_LED_OFF_LEVEL);
#else
(void)on;
#endif
}
static void rc522_update_run_led_state(void)
{
#if (RC522_RUN_LED_ENABLE == 1U)
uint32_t now = HAL_GetTick();
if ((int32_t)(g_run_led_hold_until_ms - now) > 0) {
rc522_set_run_led(1U);
} else {
rc522_set_run_led(0U);
}
#endif
}
static uint8_t rc522_uid_equal(const rc522_card_info_t *a, const rc522_card_info_t *b)
{
if (a == NULL || b == NULL) {
return 0U;
}
if (a->uid_len != b->uid_len) {
return 0U;
}
if (a->uid_len == 0U || a->uid_len > RC522_UID_MAX_LEN) {
return 0U;
}
if (memcmp(a->uid, b->uid, a->uid_len) != 0) {
return 0U;
}
return 1U;
}
static uint8_t rc522_cache_is_fresh(void)
{
if (g_last_card.valid == 0U) {
return 0U;
}
if ((HAL_GetTick() - g_last_card.last_seen_tick_ms) > RC522_CACHE_EXPIRE_MS) {
return 0U;
}
return 1U;
}
/* ------------------------- 底层 SPI 辅助 ------------------------- */
static void rc522_cs_low(void)
{
HAL_GPIO_WritePin(SDA_GPIO_Port, SDA_Pin, GPIO_PIN_RESET);
}
static void rc522_cs_high(void)
{
HAL_GPIO_WritePin(SDA_GPIO_Port, SDA_Pin, GPIO_PIN_SET);
}
static rc522_status_t rc522_write_reg(uint8_t reg, uint8_t value)
{
uint8_t tx[2];
tx[0] = (uint8_t)((reg << 1U) & 0x7EU);
tx[1] = value;
rc522_cs_low();
if (HAL_SPI_Transmit(&hspi1, tx, 2U, RC522_SPI_TIMEOUT_MS) != HAL_OK) {
rc522_cs_high();
return RC522_ERR_SPI;
}
rc522_cs_high();
return RC522_OK;
}
static rc522_status_t rc522_read_reg(uint8_t reg, uint8_t *value)
{
uint8_t tx[2];
uint8_t rx[2] = {0};
if (value == NULL) {
return RC522_ERR_PARAM;
}
tx[0] = (uint8_t)(((reg << 1U) & 0x7EU) | 0x80U);
tx[1] = 0x00U;
rc522_cs_low();
if (HAL_SPI_TransmitReceive(&hspi1, tx, rx, 2U, RC522_SPI_TIMEOUT_MS) != HAL_OK) {
rc522_cs_high();
return RC522_ERR_SPI;
}
rc522_cs_high();
*value = rx[1];
return RC522_OK;
}
static rc522_status_t rc522_set_bitmask(uint8_t reg, uint8_t mask)
{
uint8_t val = 0;
rc522_status_t st = rc522_read_reg(reg, &val);
if (st != RC522_OK) {
return st;
}
return rc522_write_reg(reg, (uint8_t)(val | mask));
}
static rc522_status_t rc522_clear_bitmask(uint8_t reg, uint8_t mask)
{
uint8_t val = 0;
rc522_status_t st = rc522_read_reg(reg, &val);
if (st != RC522_OK) {
return st;
}
return rc522_write_reg(reg, (uint8_t)(val & (uint8_t)(~mask)));
}
static rc522_status_t rc522_antenna_on(void)
{
return rc522_set_bitmask(RC522_REG_TX_CONTROL, 0x03U);
}
/* ------------------------- 核心通信流程 ------------------------- */
static rc522_status_t rc522_calculate_crc(const uint8_t *data, uint8_t len, uint8_t *crc_out)
{
uint8_t irq = 0;
uint32_t start = HAL_GetTick();
if (data == NULL || crc_out == NULL) {
return RC522_ERR_PARAM;
}
rc522_write_reg(RC522_REG_COMMAND, RC522_CMD_IDLE);
rc522_write_reg(RC522_REG_DIV_IRQ, 0x04U);
rc522_write_reg(RC522_REG_FIFO_LEVEL, 0x80U);
for (uint8_t i = 0; i < len; i++) {
rc522_write_reg(RC522_REG_FIFO_DATA, data[i]);
}
rc522_write_reg(RC522_REG_COMMAND, RC522_CMD_CALC_CRC);
do {
rc522_read_reg(RC522_REG_DIV_IRQ, &irq);
if ((HAL_GetTick() - start) > 20U) {
return RC522_ERR_TIMEOUT;
}
} while ((irq & 0x04U) == 0U);
rc522_read_reg(RC522_REG_CRC_RESULT_L, &crc_out[0]);
rc522_read_reg(RC522_REG_CRC_RESULT_H, &crc_out[1]);
return RC522_OK;
}
static rc522_status_t rc522_transceive(const uint8_t *send_data,
uint8_t send_len,
uint8_t *back_data,
uint8_t *back_len,
uint8_t valid_bits)
{
uint8_t irq_en = 0x77U;
uint8_t wait_irq = 0x30U;
uint8_t irq = 0;
uint8_t error = 0;
uint8_t fifo_level = 0;
uint8_t control = 0;
uint32_t start = HAL_GetTick();
if (send_data == NULL || send_len == 0U || back_len == NULL) {
return RC522_ERR_PARAM;
}
rc522_write_reg(RC522_REG_COMMAND, RC522_CMD_IDLE);
rc522_write_reg(RC522_REG_COM_I_EN, (uint8_t)(irq_en | 0x80U));
rc522_write_reg(RC522_REG_COM_IRQ, 0x7FU);
rc522_write_reg(RC522_REG_FIFO_LEVEL, 0x80U);
for (uint8_t i = 0; i < send_len; i++) {
rc522_write_reg(RC522_REG_FIFO_DATA, send_data[i]);
}
rc522_write_reg(RC522_REG_BIT_FRAMING, valid_bits);
rc522_write_reg(RC522_REG_COMMAND, RC522_CMD_TRANSCEIVE);
rc522_set_bitmask(RC522_REG_BIT_FRAMING, 0x80U);
do {
rc522_read_reg(RC522_REG_COM_IRQ, &irq);
if ((HAL_GetTick() - start) > 30U) {
rc522_clear_bitmask(RC522_REG_BIT_FRAMING, 0x80U);
return RC522_ERR_TIMEOUT;
}
} while ((irq & wait_irq) == 0U && (irq & 0x01U) == 0U);
rc522_clear_bitmask(RC522_REG_BIT_FRAMING, 0x80U);
rc522_read_reg(RC522_REG_ERROR, &error);
if ((error & 0x13U) != 0U) {
return RC522_ERR_INTERNAL;
}
rc522_read_reg(RC522_REG_FIFO_LEVEL, &fifo_level);
if (fifo_level == 0U) {
*back_len = 0U;
return RC522_NO_CARD;
}
if (back_data != NULL) {
uint8_t to_read = fifo_level;
if (to_read > *back_len) {
to_read = *back_len;
}
for (uint8_t i = 0; i < to_read; i++) {
rc522_read_reg(RC522_REG_FIFO_DATA, &back_data[i]);
}
*back_len = to_read;
} else {
*back_len = 0U;
}
rc522_read_reg(RC522_REG_CONTROL, &control);
(void)control;
return RC522_OK;
}
static rc522_status_t rc522_request_a(uint16_t *atqa)
{
uint8_t cmd = PICC_CMD_REQA;
uint8_t recv[2] = {0};
uint8_t recv_len = sizeof(recv);
if (atqa == NULL) {
return RC522_ERR_PARAM;
}
/* REQA 是 7bit 命令BitFramingReg 低 3 位写 0x07 */
rc522_status_t st = rc522_transceive(&cmd, 1U, recv, &recv_len, 0x07U);
if (st != RC522_OK) {
return st;
}
if (recv_len != 2U) {
return RC522_NO_CARD;
}
*atqa = (uint16_t)(((uint16_t)recv[0] << 8U) | recv[1]);
return RC522_OK;
}
static uint8_t rc522_get_sel_cmd_by_level(uint8_t level)
{
if (level == 1U) {
return PICC_CMD_SEL_CL1;
}
if (level == 2U) {
return PICC_CMD_SEL_CL2;
}
return PICC_CMD_SEL_CL3;
}
static rc522_status_t rc522_anticoll_level(uint8_t level, uint8_t uid_part[5])
{
uint8_t sel_cmd = rc522_get_sel_cmd_by_level(level);
uint8_t send_buf[2] = {sel_cmd, 0x20U};
uint8_t recv[5] = {0};
uint8_t recv_len = sizeof(recv);
rc522_status_t st = rc522_transceive(send_buf, 2U, recv, &recv_len, 0x00U);
if (st != RC522_OK) {
return st;
}
if (recv_len != 5U) {
return RC522_ERR_INTERNAL;
}
uint8_t bcc = (uint8_t)(recv[0] ^ recv[1] ^ recv[2] ^ recv[3]);
if (bcc != recv[4]) {
return RC522_ERR_BCC;
}
memcpy(uid_part, recv, 5U);
return RC522_OK;
}
static rc522_status_t rc522_select_level(uint8_t level, const uint8_t uid_part[5], uint8_t *sak)
{
uint8_t send_buf[9] = {0};
uint8_t crc[2] = {0};
uint8_t recv[3] = {0};
uint8_t recv_len = sizeof(recv);
if (sak == NULL) {
return RC522_ERR_PARAM;
}
send_buf[0] = rc522_get_sel_cmd_by_level(level);
send_buf[1] = 0x70U;
send_buf[2] = uid_part[0];
send_buf[3] = uid_part[1];
send_buf[4] = uid_part[2];
send_buf[5] = uid_part[3];
send_buf[6] = uid_part[4];
rc522_status_t st = rc522_calculate_crc(send_buf, 7U, crc);
if (st != RC522_OK) {
return st;
}
send_buf[7] = crc[0];
send_buf[8] = crc[1];
st = rc522_transceive(send_buf, 9U, recv, &recv_len, 0x00U);
if (st != RC522_OK) {
return st;
}
if (recv_len < 1U) {
return RC522_ERR_INTERNAL;
}
*sak = recv[0];
return RC522_OK;
}
static void rc522_halt(void)
{
uint8_t send_buf[4] = {PICC_CMD_HLTA, 0x00U, 0x00U, 0x00U};
uint8_t crc[2] = {0};
uint8_t recv_len = 0;
if (rc522_calculate_crc(send_buf, 2U, crc) != RC522_OK) {
return;
}
send_buf[2] = crc[0];
send_buf[3] = crc[1];
(void)rc522_transceive(send_buf, 4U, NULL, &recv_len, 0x00U);
}
/* ------------------------- 对外接口 ------------------------- */
rc522_status_t rc522_init(void)
{
memset(&g_last_card, 0, sizeof(g_last_card));
g_run_led_hold_until_ms = 0U;
g_last_new_event_tick_ms = 0U;
g_irq_pending = 1U;
/* SPI 片选空闲电平应为高 */
rc522_cs_high();
rc522_set_run_led(0U);
/* RST: 硬件复位脉冲 */
HAL_GPIO_WritePin(RST_GPIO_Port, RST_Pin, GPIO_PIN_RESET);
HAL_Delay(5);
HAL_GPIO_WritePin(RST_GPIO_Port, RST_Pin, GPIO_PIN_SET);
HAL_Delay(50);
/* 软复位 */
if (rc522_write_reg(RC522_REG_COMMAND, RC522_CMD_SOFT_RESET) != RC522_OK) {
return RC522_ERR_SPI;
}
HAL_Delay(50);
/* 定时器配置: 与常见 RC522 初始化配置兼容 */
rc522_write_reg(RC522_REG_T_MODE, 0x8DU);
rc522_write_reg(RC522_REG_T_PRESCALER, 0x3EU);
rc522_write_reg(RC522_REG_T_RELOAD_L, 30U);
rc522_write_reg(RC522_REG_T_RELOAD_H, 0U);
rc522_write_reg(RC522_REG_TX_ASK, 0x40U);
rc522_write_reg(RC522_REG_MODE, 0x3DU);
rc522_write_reg(RC522_REG_TX_MODE, 0x00U);
rc522_write_reg(RC522_REG_RX_MODE, 0x00U);
/* 开启天线 */
return rc522_antenna_on();
}
rc522_status_t rc522_poll(rc522_card_info_t *out_card)
{
rc522_card_info_t temp = {0};
uint8_t level_data[5] = {0};
uint8_t uid_pos = 0U;
uint8_t level = 1U;
rc522_status_t st;
rc522_update_run_led_state();
st = rc522_request_a(&temp.atqa);
if (st != RC522_OK) {
return st;
}
while (level <= 3U) {
st = rc522_anticoll_level(level, level_data);
if (st != RC522_OK) {
return st;
}
st = rc522_select_level(level, level_data, &temp.sak);
if (st != RC522_OK) {
return st;
}
if (level_data[0] == PICC_CMD_CT) {
if ((uid_pos + 3U) > RC522_UID_MAX_LEN) {
return RC522_ERR_INTERNAL;
}
temp.uid[uid_pos++] = level_data[1];
temp.uid[uid_pos++] = level_data[2];
temp.uid[uid_pos++] = level_data[3];
} else {
if ((uid_pos + 4U) > RC522_UID_MAX_LEN) {
return RC522_ERR_INTERNAL;
}
temp.uid[uid_pos++] = level_data[0];
temp.uid[uid_pos++] = level_data[1];
temp.uid[uid_pos++] = level_data[2];
temp.uid[uid_pos++] = level_data[3];
}
/* SAK bit2=1 表示还有下一层级 UID */
if ((temp.sak & 0x04U) == 0U) {
break;
}
level++;
}
if (uid_pos == 0U) {
return RC522_ERR_INTERNAL;
}
temp.valid = 1U;
temp.uid_len = uid_pos;
temp.last_seen_tick_ms = HAL_GetTick();
if (!rc522_uid_equal(&temp, &g_last_card)) {
g_last_new_event_tick_ms = temp.last_seen_tick_ms;
}
g_last_card = temp;
g_run_led_hold_until_ms = temp.last_seen_tick_ms + RC522_RUN_LED_HOLD_MS;
rc522_set_run_led(1U);
g_irq_pending = 0U;
if (out_card != NULL) {
*out_card = temp;
}
rc522_halt();
return RC522_OK;
}
uint8_t rc522_get_last_card(rc522_card_info_t *out_card)
{
if (out_card == NULL) {
return 0U;
}
if (!rc522_cache_is_fresh()) {
return 0U;
}
*out_card = g_last_card;
return 1U;
}
void rc522_clear_last_card(void)
{
memset(&g_last_card, 0, sizeof(g_last_card));
g_run_led_hold_until_ms = 0U;
g_last_new_event_tick_ms = 0U;
rc522_set_run_led(0U);
}
uint8_t rc522_has_valid_card(void)
{
return rc522_cache_is_fresh();
}
uint8_t rc522_get_new_card(rc522_card_info_t *out_card)
{
if (out_card == NULL) {
return 0U;
}
if (!rc522_cache_is_fresh()) {
return 0U;
}
if (g_last_new_event_tick_ms != g_last_card.last_seen_tick_ms) {
return 0U;
}
*out_card = g_last_card;
g_last_new_event_tick_ms = 0U;
return 1U;
}
uint8_t rc522_uid_to_string(const rc522_card_info_t *card, char *out_str, uint16_t out_len)
{
uint16_t need_len;
uint16_t pos = 0;
if (card == NULL || out_str == NULL || card->uid_len == 0U || card->uid_len > RC522_UID_MAX_LEN) {
return 0U;
}
/* 每字节2字符字节间空格(uid_len-1),再加结尾\0 */
need_len = (uint16_t)((card->uid_len * 2U) + (card->uid_len - 1U) + 1U);
if (out_len < need_len) {
return 0U;
}
for (uint8_t i = 0; i < card->uid_len; i++) {
int n = snprintf(&out_str[pos], (size_t)(out_len - pos), "%02X", card->uid[i]);
if (n <= 0) {
return 0U;
}
pos = (uint16_t)(pos + (uint16_t)n);
if (i != (uint8_t)(card->uid_len - 1U)) {
if (pos + 1U >= out_len) {
return 0U;
}
out_str[pos++] = ' ';
out_str[pos] = '\0';
}
}
return 1U;
}
rc522_status_t rc522_service(rc522_card_info_t *out_card)
{
rc522_update_run_led_state();
if (!rc522_cache_is_fresh()) {
g_last_card.valid = 0U;
}
#if (RC522_USE_IRQ_TRIGGER == 1U)
if (g_irq_pending == 0U) {
return RC522_NO_CARD;
}
#endif
return rc522_poll(out_card);
}
void rc522_irq_callback(uint16_t GPIO_Pin)
{
if (GPIO_Pin == IOR_Pin) {
g_irq_pending = 1U;
}
}

160
Core/Bsp/bsp_rc522.h Normal file
View File

@@ -0,0 +1,160 @@
#ifndef __BSP_RC522_H
#define __BSP_RC522_H
#include "main.h"
#ifdef __cplusplus
extern "C" {
#endif
/* RC522 典型 UID 最大长度为 10 字节 */
#define RC522_UID_MAX_LEN 10U
// ================== 站点UID白名单用户可根据实际卡片填写 ==================
// 站点1A卡片 UID: 29 AF 22 07
#define STATION_1_UID {0x29, 0xAF, 0x22, 0x07}
// 站点2B卡片 UID: 1B 45 F3 06
#define STATION_2_UID {0x1B, 0x45, 0xF3, 0x06}
// 站点枚举(可扩展)
typedef enum {
STATION_NONE = 0,
STATION_1 = 1, // 29 AF 22 07
STATION_2 = 2, // 1B 45 F3 06
// 可继续添加更多站点
} station_id_t;
/**
* @brief 根据UID判断属于哪个站点
* @param uid UID字节数组
* @param uid_len UID长度
* @return station_id_t 站点编号
*/
station_id_t rc522_match_station(const uint8_t *uid, uint8_t uid_len);
/*
* RUN 提示灯策略:
* 1) 轮询到卡片后点亮一段时间 (RC522_RUN_LED_HOLD_MS)
* 2) 超时自动熄灭
* 如需关闭该功能,设置 RC522_RUN_LED_ENABLE 为 0
*/
#ifndef RC522_RUN_LED_ENABLE
#define RC522_RUN_LED_ENABLE 1U
#endif
#ifndef RC522_RUN_LED_HOLD_MS
#define RC522_RUN_LED_HOLD_MS 300U
#endif
/* 按当前板卡默认 RUN_LED 低电平点亮 */
#ifndef RC522_RUN_LED_ON_LEVEL
#define RC522_RUN_LED_ON_LEVEL GPIO_PIN_RESET
#endif
/* 最近卡缓存超时时间:超过该时间认为缓存失效 */
#ifndef RC522_CACHE_EXPIRE_MS
#define RC522_CACHE_EXPIRE_MS 1500U
#endif
/*
* 是否使用 IRQ 触发后再读卡:
* 0 = 纯轮询(推荐先用)
* 1 = 依赖 IOR 中断触发(需要在 EXTI 回调中调用 rc522_irq_callback
*/
#ifndef RC522_USE_IRQ_TRIGGER
#define RC522_USE_IRQ_TRIGGER 0U
#endif
/**
* @brief RC522 读卡状态
*/
typedef enum {
RC522_OK = 0,
RC522_NO_CARD,
RC522_ERR_TIMEOUT,
RC522_ERR_CRC,
RC522_ERR_BCC,
RC522_ERR_SPI,
RC522_ERR_PARAM,
RC522_ERR_INTERNAL
} rc522_status_t;
/**
* @brief 最近一次读到的卡片信息
*/
typedef struct {
uint8_t valid; // 1: 有效卡信息, 0: 无效
uint8_t uid[RC522_UID_MAX_LEN]; // UID 原始字节
uint8_t uid_len; // UID 长度 (支持 4/7/10)
uint8_t sak; // Select Acknowledge
uint16_t atqa; // Answer To Request, 高字节在前
uint32_t last_seen_tick_ms; // 最近一次读到该卡的系统毫秒时间
} rc522_card_info_t;
/**
* @brief 初始化 RC522 模块 (SPI + 寄存器)
* @note SPI1 已由 CubeMX 初始化,本函数只做 RC522 芯片侧初始化。
*/
rc522_status_t rc522_init(void);
/**
* @brief 轮询一次读卡,并缓存结果
* @param out_card 可选输出,为 NULL 时仅更新内部缓存
* @return rc522_status_t
*/
rc522_status_t rc522_poll(rc522_card_info_t *out_card);
/**
* @brief RC522 周期服务函数(推荐在任务中调用)
* @param out_card 可选输出,为 NULL 时仅更新内部状态
* @return RC522_OK: 本次成功读到卡; RC522_NO_CARD: 当前无卡; 其他: 读卡异常
*/
rc522_status_t rc522_service(rc522_card_info_t *out_card);
/**
* @brief 获取最近一次成功读到的卡片信息
* @param out_card 输出参数
* @return 1: 成功复制, 0: 当前无有效卡信息
*/
uint8_t rc522_get_last_card(rc522_card_info_t *out_card);
/**
* @brief 当前是否存在有效缓存卡
* @return 1: 有效, 0: 无效
*/
uint8_t rc522_has_valid_card(void);
/**
* @brief 判断当前卡是否“新卡事件”(与上次输出不同)
* @param out_card 输出参数
* @return 1: 新卡, 0: 非新卡或无卡
*/
uint8_t rc522_get_new_card(rc522_card_info_t *out_card);
/**
* @brief 清除内部缓存的最近卡片信息
*/
void rc522_clear_last_card(void);
/**
* @brief 将 UID 格式化为 HEX 字符串 (例如 "DE AD BE EF")
* @param card 卡片信息
* @param out_str 输出缓冲区
* @param out_len 输出缓冲区长度
* @return 1: 成功, 0: 参数错误或缓冲区不足
*/
uint8_t rc522_uid_to_string(const rc522_card_info_t *card, char *out_str, uint16_t out_len);
/**
* @brief EXTI 回调入口(可选)
* @param GPIO_Pin EXTI 引脚号
*/
void rc522_irq_callback(uint16_t GPIO_Pin);
#ifdef __cplusplus
}
#endif
#endif /* __BSP_RC522_H */

122
Core/Bsp/task_README.md Normal file
View File

@@ -0,0 +1,122 @@
# RC522 任务接入说明 (CubeMX 友好)
你要求不要直接改自动生成任务代码,避免下次 CubeMX 重新生成被覆盖。下面是推荐接入方式。
## 1. 已新增驱动文件
- `Core/Bsp/bsp_rc522.h`
- `Core/Bsp/bsp_rc522.c`
核心接口:
```c
rc522_status_t rc522_init(void);
rc522_status_t rc522_poll(rc522_card_info_t *out_card);
rc522_status_t rc522_service(rc522_card_info_t *out_card);
uint8_t rc522_get_last_card(rc522_card_info_t *out_card);
uint8_t rc522_has_valid_card(void);
uint8_t rc522_get_new_card(rc522_card_info_t *out_card);
void rc522_clear_last_card(void);
uint8_t rc522_uid_to_string(const rc522_card_info_t *card, char *out_str, uint16_t out_len);
void rc522_irq_callback(uint16_t GPIO_Pin);
```
RUN 灯提示(已内置在驱动里):
- 成功读到卡后自动点亮 `RUN_LED`
- 保持 `RC522_RUN_LED_HOLD_MS` 毫秒后自动熄灭
- 默认低电平点亮,可在 `bsp_rc522.h` 里改:
```c
#define RC522_RUN_LED_ENABLE 1U
#define RC522_RUN_LED_HOLD_MS 300U
#define RC522_RUN_LED_ON_LEVEL GPIO_PIN_RESET
#define RC522_CACHE_EXPIRE_MS 1500U
#define RC522_USE_IRQ_TRIGGER 0U
```
## 2. 硬件连接确认
按你现在配置:
- `SDA (CS)` -> `PC4` (输出)
- `RST` -> `PB0` (输出)
- `IOR (IRQ)` -> `PC5` (EXTI可选)
- `SPI1` -> `PA5/PA6/PA7`
> 注意:默认是 **轮询模式**`RC522_USE_IRQ_TRIGGER=0`),最稳。
> 如果你想改为中断触发读卡:设为 `1`,并确保 EXTI 回调里调用 `rc522_irq_callback(GPIO_Pin)`。
## 3. 在 CubeMX 的 freertos.c 用户区接入
### 3.1 头文件包含 (`USER CODE BEGIN Includes`)
```c
#include "bsp_rc522.h"
```
### 3.2 在初始化阶段调用 (`USER CODE BEGIN Init`)
```c
rc522_init();
```
### 3.3 新建一个低优先级任务(推荐 50~100ms
任务函数示例(你可以放在用户代码区):
```c
void rc522_task(void *argument)
{
rc522_card_info_t card;
char uid_str[3 * RC522_UID_MAX_LEN] = {0};
for (;;) {
rc522_status_t st = rc522_service(&card);
if (st == RC522_OK) {
if (rc522_uid_to_string(&card, uid_str, sizeof(uid_str))) {
printf("RFID UID: %s, SAK:0x%02X, ATQA:0x%04X\r\n",
uid_str, card.sak, card.atqa);
}
}
osDelay(100);
}
}
```
如果你只希望“新卡刷入时”触发一次业务,不重复触发:
```c
rc522_card_info_t new_card;
if (rc522_get_new_card(&new_card)) {
// 仅在识别到新 UID 时进入
}
```
## 4. 在业务代码中获取“最近一次卡片信息”
驱动会自动缓存最近一次成功读到的卡,可在任意位置读取:
```c
rc522_card_info_t last_card;
if (rc522_get_last_card(&last_card)) {
// last_card 有效
} else {
// 当前没有有效卡缓存
}
```
## 5. 常见问题
1. 一直读不到卡:
- 检查 RC522 供电是否 3.3V(不要 5V
- 检查 `SDA(PC4)` 是否被其他器件复用
- 确认 SPI1 时钟极性/相位为 `CPOL=0, CPHA=1Edge`
2. UID 偶发错误:
- 降低 SPI 速率(在 CubeMX 调大分频,例如 Prescaler=8
- 线长尽量短,地线要可靠
3. 任务被覆盖:
- 只在 `USER CODE BEGIN ... END` 区域放代码
- 或者把任务函数放到 `Core/Bsp` 内,由 `freertos.c` 用户区仅调用

View File

@@ -34,6 +34,7 @@
#include "bsp_hall.h" // 添加对 bsp_hall.h 的包含
#include "bsp_track_ir.h" // 添加对 bsp_track_ir.h 的包含
#include "bsp_sr04.h" // 添加超声波头文件
#include "bsp_rc522.h" // 添加 RFID 头文件
/* USER CODE END Includes */
/* Private typedef -----------------------------------------------------------*/
@@ -83,6 +84,13 @@ const osThreadAttr_t sr04Task_attributes = {
.stack_size = 128 * 4,
.priority = (osPriority_t) osPriorityLow,
};
/* Definitions for rc522Task */
osThreadId_t rc522TaskHandle;
const osThreadAttr_t rc522Task_attributes = {
.name = "rc522Task",
.stack_size = 128 * 4,
.priority = (osPriority_t) osPriorityBelowNormal,
};
/* Definitions for CmdQueue */
osMessageQueueId_t CmdQueueHandle;
const osMessageQueueAttr_t CmdQueue_attributes = {
@@ -107,6 +115,7 @@ void StartDefaultTask(void *argument);
void CarCtrl_Task(void *argument);
void speed_get(void *argument);
void sr04_task(void *argument);
void rc522_task(void *argument);
void MX_FREERTOS_Init(void); /* (MISRA C 2004 rule 8.1) */
@@ -126,6 +135,9 @@ void MX_FREERTOS_Init(void) {
hall_init(); // 初始化霍尔传感器
track_ir_init(); // 初始化轨迹红外传感器
sr04_init(); // 初始化超声波
rc522_init(); // 初始化 RFID 模块
/* USER CODE END Init */
@@ -162,6 +174,9 @@ void MX_FREERTOS_Init(void) {
/* creation of sr04Task */
sr04TaskHandle = osThreadNew(sr04_task, NULL, &sr04Task_attributes);
/* creation of rc522Task */
rc522TaskHandle = osThreadNew(rc522_task, NULL, &rc522Task_attributes);
/* USER CODE BEGIN RTOS_THREADS */
/* add threads, ... */
/* USER CODE END RTOS_THREADS */
@@ -256,6 +271,43 @@ void sr04_task(void *argument)
/* USER CODE END sr04_task */
}
/* USER CODE BEGIN Header_rc522_task */
/**
* @brief Function implementing the rc522Task thread.
* @param argument: Not used
* @retval None
*/
/* USER CODE END Header_rc522_task */
void rc522_task(void *argument)
{
/* USER CODE BEGIN rc522_task */
rc522_card_info_t card;
char uid_str[3 * RC522_UID_MAX_LEN] = {0};
station_id_t station = STATION_NONE;
/* Infinite loop */
for(;;)
{
rc522_status_t st = rc522_poll(&card);
if (st == RC522_OK) {
if (rc522_uid_to_string(&card, uid_str, sizeof(uid_str))) {
printf("RFID UID: %s, SAK:0x%02X, ATQA:0x%04X\r\n",
uid_str, card.sak, card.atqa);
}
// 站点匹配逻辑
station = rc522_match_station(card.uid, card.uid_len);
if (station == STATION_1) {
printf("到达站点1\r\n");
// 可在此处添加到站动作,如停车、蜂鸣等
} else if (station == STATION_2) {
printf("到达站点2\r\n");
// 可在此处添加到站动作,如停车、蜂鸣等
}
}
osDelay(100);
}
/* USER CODE END rc522_task */
}
/* Private application code --------------------------------------------------*/
/* USER CODE BEGIN Application */

View File

@@ -36,7 +36,7 @@ Dma.USART2_TX.2.RequestParameters=Instance,Direction,PeriphInc,MemInc,PeriphData
FREERTOS.FootprintOK=true
FREERTOS.IPParameters=Tasks01,FootprintOK,Queues01,configTOTAL_HEAP_SIZE
FREERTOS.Queues01=CmdQueue,16,16,1,Dynamic,NULL,NULL
FREERTOS.Tasks01=initTask,24,128,StartDefaultTask,Default,NULL,Dynamic,NULL,NULL;CarCtrlTask,24,256,CarCtrl_Task,As weak,NULL,Dynamic,NULL,NULL;timerTask,16,512,speed_get,As weak,NULL,Dynamic,NULL,NULL;sr04Task,8,128,sr04_task,Default,NULL,Dynamic,NULL,NULL
FREERTOS.Tasks01=initTask,24,128,StartDefaultTask,Default,NULL,Dynamic,NULL,NULL;CarCtrlTask,24,256,CarCtrl_Task,As weak,NULL,Dynamic,NULL,NULL;timerTask,16,512,speed_get,As weak,NULL,Dynamic,NULL,NULL;sr04Task,8,128,sr04_task,Default,NULL,Dynamic,NULL,NULL;rc522Task,16,128,rc522_task,Default,NULL,Dynamic,NULL,NULL
FREERTOS.configTOTAL_HEAP_SIZE=10000
File.Version=6
GPIO.groupedBy=Group By Peripherals