Files
car_stm32f103vet6/Core/Bsp/bsp_rc522.c

633 lines
16 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#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;
}
}
/* 站点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 == 0U) {
return STATION_NONE;
}
if (uid_len == sizeof(g_station_1_uid) && memcmp(uid, g_station_1_uid, uid_len) == 0) {
return STATION_1;
}
if (uid_len == sizeof(g_station_2_uid) && memcmp(uid, g_station_2_uid, uid_len) == 0) {
return STATION_2;
}
return STATION_NONE;
}