From a6359364fdc76162527923014154ca0e22e02699 Mon Sep 17 00:00:00 2001 From: wangbeihong Date: Tue, 14 Apr 2026 23:40:46 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AE=9E=E7=8E=B0RFID=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CMakeLists.txt | 1 + Core/Bsp/bsp_hall.c | 2 + Core/Bsp/bsp_rc522.c | 632 ++++++++++++++++++++++++++++++++++++++++ Core/Bsp/bsp_rc522.h | 160 ++++++++++ Core/Bsp/task_README.md | 122 ++++++++ Core/Src/freertos.c | 52 ++++ f103_car.ioc | 2 +- 7 files changed, 970 insertions(+), 1 deletion(-) create mode 100644 Core/Bsp/bsp_rc522.c create mode 100644 Core/Bsp/bsp_rc522.h create mode 100644 Core/Bsp/task_README.md diff --git a/CMakeLists.txt b/CMakeLists.txt index 0649569..33448d9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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 diff --git a/Core/Bsp/bsp_hall.c b/Core/Bsp/bsp_hall.c index 5c57c20..f0cf6ec 100644 --- a/Core/Bsp/bsp_hall.c +++ b/Core/Bsp/bsp_hall.c @@ -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); } /** diff --git a/Core/Bsp/bsp_rc522.c b/Core/Bsp/bsp_rc522.c new file mode 100644 index 0000000..68cf229 --- /dev/null +++ b/Core/Bsp/bsp_rc522.c @@ -0,0 +1,632 @@ +#include "bsp_rc522.h" +#include + +// 站点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 +#include + +/* ========================= 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; + } +} diff --git a/Core/Bsp/bsp_rc522.h b/Core/Bsp/bsp_rc522.h new file mode 100644 index 0000000..ea5cc12 --- /dev/null +++ b/Core/Bsp/bsp_rc522.h @@ -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白名单(用户可根据实际卡片填写) ================== + +// 站点1(A)卡片 UID: 29 AF 22 07 +#define STATION_1_UID {0x29, 0xAF, 0x22, 0x07} +// 站点2(B)卡片 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 */ diff --git a/Core/Bsp/task_README.md b/Core/Bsp/task_README.md new file mode 100644 index 0000000..db0820e --- /dev/null +++ b/Core/Bsp/task_README.md @@ -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` 用户区仅调用 diff --git a/Core/Src/freertos.c b/Core/Src/freertos.c index c4be722..3348c8f 100644 --- a/Core/Src/freertos.c +++ b/Core/Src/freertos.c @@ -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 */ diff --git a/f103_car.ioc b/f103_car.ioc index 850d569..4566575 100644 --- a/f103_car.ioc +++ b/f103_car.ioc @@ -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