Skip to content

STM32F401RETX主控芯片,STM32培训结课项目

Notifications You must be signed in to change notification settings

HK-bruc1/SmartLock

Repository files navigation

智能锁项目笔记

目录

前置

更新字库后想通过软件复位重新加载字库

中途修改看门狗的重载值

两种串口发送判断的标志的区别

RFID射频识别

其他

前置

嵌入式项目中,逐个功能实现和搭建框架后再逐个功能实现的差别,不搭建框架的话,会时刻影响前面的代码,耦合度太高?

直接逐个功能实现的方式:

  1. 容易造成代码高度耦合。比如实现串口功能时直接写死了数据处理逻辑,后续添加其他功能时可能需要大量修改原有代码。
  2. 代码复用性差。类似的功能可能会在不同地方重复编写,导致代码冗余且难以维护。
  3. 扩展性受限。新增功能时经常需要修改现有代码,容易引入bug。

先搭建框架再实现功能的方式:

  1. 通过良好的架构设计(如分层架构),实现各模块解耦。比如:
    • 硬件抽象层(HAL)处理底层硬件
    • 驱动层处理具体外设
    • 应用层实现业务逻辑
  2. 定义清晰的接口,使各模块之间通过标准接口通信,而不是直接调用。
  3. 便于功能扩展和维护:
    • 新增功能时只需实现相应接口
    • 修改某个模块不会影响其他模块
    • 可以方便地复用已有模块

举个例子:假设要实现一个带显示功能的数据采集系统。

不使用框架的做法可能是:

void main() {
    while(1) {
        // 直接在一起写采集和显示逻辑
        data = adc_read();
        lcd_display(data);
    }
}

使用框架的做法:

// 数据采集模块
typedef struct {
    void (*init)(void);
    uint16_t (*read)(void);
} Sensor_t;

// 显示模块
typedef struct {
    void (*init)(void);
    void (*display)(uint16_t);
} Display_t;

void main() {
    Sensor_t sensor;
    Display_t display;
    
    // 初始化各模块
    sensor.init();
    display.init();
    
    while(1) {
        uint16_t data = sensor.read();
        display.display(data);
    }
}

经过搭建框架,可以实现分层结构?驱动层和应用层?两者的代码不一样吗?

驱动层(Driver Layer):

  1. 主要负责硬件的直接操作
  2. 提供硬件的基础功能接口
  3. 代码特点:
// 驱动层示例 - UART驱动
typedef struct {
    void (*init)(uint32_t baudrate);
    void (*send_byte)(uint8_t data);
    uint8_t (*receive_byte)(void);
    void (*enable_rx_interrupt)(void);
} UART_Driver_t;

// 具体实现
void UART_Init(uint32_t baudrate) {
    // 配置GPIO引脚
    GPIO_InitTypeDef gpio_init;
    gpio_init.Mode = GPIO_MODE_AF_PP;
    HAL_GPIO_Init(...);
    
    // 配置UART寄存器
    USART1->BRR = SystemCoreClock/baudrate;
    USART1->CR1 |= USART_CR1_UE | USART_CR1_TE;
}

应用层(Application Layer):

  1. 实现具体的业务逻辑
  2. 调用驱动层提供的接口
  3. 代码特点:
// 应用层示例 - 温度监控应用
typedef struct {
    float temp_threshold;
    void (*process_temp)(float temp);
    void (*alarm_handler)(void);
} TempMonitor_t;

// 具体实现
void TempMonitor_Process(float temp) {
    // 使用驱动层的接口发送数据
    char buf[32];
    sprintf(buf, "Temperature: %.1f\r\n", temp);
    
    // 通过UART发送温度数据
    for(int i = 0; buf[i]; i++) {
        uart.send_byte(buf[i]);
    }
    
    // 业务逻辑处理
    if(temp > temp_threshold) {
        alarm_handler();
    }
}

主要区别:

  1. 关注点不同
    • 驱动层关注如何操作硬件(寄存器配置等)
    • 应用层关注业务流程(数据处理、控制逻辑等)
  2. 依赖关系
    • 驱动层依赖硬件抽象层(HAL)
    • 应用层依赖驱动层提供的接口
  3. 可移植性
    • 驱动层与硬件平台紧密相关
    • 应用层相对独立,更容易移植

这样的分层设计有助于:

  • 代码复用(不同产品可以复用相同的驱动)
  • 便于测试(可以单独测试各层功能)
  • 提高可维护性(修改某层代码不影响其他层)

现在嵌入式系统开发方式有三种,寄存器,标准库,hal库他们之间有什么区别?

寄存器级开发:

// 直接操作寄存器的例子 - 配置GPIO
GPIOA->MODER |= (1 << 10);  // 设置PA5为输出模式
GPIOA->ODR |= (1 << 5);     // PA5输出高电平

特点:

  • 直接操作硬件寄存器,执行效率最高
  • 代码量小,程序空间占用少
  • 需要深入理解硬件架构和寄存器功能
  • 可移植性差,不同芯片的寄存器定义不同
  • 开发周期长,容易出错

标准库开发:

// 使用标准库的例子 - 配置GPIO
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_SetBits(GPIOA, GPIO_Pin_5);

特点:

  • 对寄存器操作进行了封装
  • 相比寄存器级开发更容易理解和使用
  • 仍需要了解部分底层细节
  • 可移植性一般,同系列芯片可以复用
  • 执行效率好,代码量适中

HAL库开发:

// 使用HAL库的例子 - 配置GPIO
GPIO_InitTypeDef GPIO_InitStruct = {0};
GPIO_InitStruct.Pin = GPIO_PIN_5;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_SET);

特点:

  • 最高层次的抽象,屏蔽了硬件细节
  • 代码可读性好,容易上手
  • 可移植性最好,跨平台开发方便
  • 执行效率较低,代码量大
  • 适合快速开发,不太注重性能的场合

选择建议:

  1. 性能要求高的项目(如高速数据采集):选择寄存器级开发
  2. 平衡开发效率和性能:选择标准库开发
  3. 注重开发效率和可移植性:选择HAL库开发

实际应用中可以混合使用:

// 混合使用的例子
void Init_Device(void) {
    // 普通GPIO用HAL库初始化
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
    
    // 高速处理的部分用寄存器直接操作
    GPIOB->BSRR = GPIO_PIN_5;
}

库函数的学习重点

  • 程序实现思路很重要,其他都可以使用库函数实现。

  • 如果不能理解库函数参数的含义,需要知道根据库函数溯源到对应的寄存器位,根据手册知道具体的含义。

  • 比如常见的串口配置思路:

    • 串口初始化:
      • GPIO控制器配置
      • 串口控制器配置
      • 用于中断接收的NVIC配置
    • 串口控制器的DMA使能可以在对应DMA功能函数时单独开启,这样就不用修改原来的代码了。

更新字库后想通过软件复位重新加载字库

在 STM32F401(以及其他基于 Cortex-M 的 STM32 系列)中,软件复位一般是通过系统控制寄存器(SCB,System Control Block)实现的。SCB 寄存器是 Cortex-M 内核的一部分,而不是外设寄存器,因此在参考手册中找不到专门的复位寄存器。

你可以通过调用 CMSIS 提供的内核函数,或者直接操作 SCB 寄存器来实现复位。

使用 CMSIS 内核函数

CMSIS 提供了标准化的函数,直接调用即可完成复位操作。这个函数是:

#include "stm32f4xx.h"

void NVIC_SystemReset(void);

调用方式:

int main(void) {
    // 初始化代码...

    // 执行软件复位
    NVIC_SystemReset();

    while (1) {
        // 不会执行到这里,因为系统已复位
    }
}

原理

NVIC_SystemReset() 函数底层操作 SCB 寄存器中的 AIRCR(Application Interrupt and Reset Control Register),设置复位位(SYSRESETREQ 位)来触发系统复位。

直接操作 SCB 寄存器(手册上没有,也不好配置)

如果不使用 CMSIS 函数,也可以直接手动配置 SCB->AIRCR 来实现复位。操作步骤如下:

#include "stm32f4xx.h"

#define AIRCR_VECTKEY_MASK    ((uint32_t)0x05FA0000) // 写入密钥值

void software_reset(void) {
    SCB->AIRCR = AIRCR_VECTKEY_MASK | (1 << SCB_AIRCR_SYSRESETREQ_Pos);
    while (1); // 等待复位
}

int main(void) {
    // 初始化代码...

    // 执行软件复位
    software_reset();

    while (1) {
        // 不会执行到这里,因为系统已复位
    }
}

注意

  1. AIRCR_VECTKEY_MASK 是一个写保护密钥,写入时必须使用 0x5FA,否则写入无效。
  2. 设置 SYSRESETREQ 位 (1 << SCB_AIRCR_SYSRESETREQ_Pos) 即触发软件复位。
  3. 复位后,所有外设和系统状态都会恢复为初始状态,类似于硬件复位。

总结

如果你使用的是官方的 STM32 HAL 库或标准库,直接调用 NVIC_SystemReset() 是最简单和推荐的方式。如果你想更底层地操作,可以直接配置 SCB 寄存器。两种方法效果完全相同。

中途修改看门狗的重载值

void iwdg_init(void)
{
    //时钟源配置-开启低速时钟
    RCC->CSR |= (1U<<0);
    //等待时钟稳定
    while(!(RCC->CSR & (1U<<1)));
    //解除写保护
    IWDG->KR = 0x5555;
    //设置分频为128分频,根据具体情况确定时间,32 kHz 低速内部 RC (LSI RC),该 RC 用于驱动独立看门狗
    //250次/s
    IWDG->PR = 5;     //直接赋值更清晰
    //重装载值设置为2500 (超时时间约10秒)
    IWDG->RLR = 2500;
    //重载到计数器,任何时候将关键字 0xAAAA 写到 IWWDG_KR 寄存器中, 
    //IWDG_RLR 的值就会被重载到计数器,从而避免产生看门狗复位。
    IWDG->KR = 0xAAAA;//此时写保护再次开启
    //启动看门狗
    IWDG->KR = 0xCCCC;
    
    printf("看门狗启动\r\n");
}


//独立喂狗程序
void iwdg_feed(void)
{
    IWDG->KR = 0xAAAA;
}


//若要更改重载值, IWDG_SR 中的 RVU 位必须为 0。
//RVU: 看门狗计数器重载值更新 (Watchdog counter reload value update)
//可通过硬件将该位置 1 以指示重载值正在更新。当在 VDD 电压域下完成重载值更新操作后
//(需要多达 5 个 RC 40 kHz 周期)毫秒级,会通过硬件将该位复位。
//重载值只有在 RVU 位为 0 时才可更新。直接修改重载值可能会失败,因为没有确认上一次更新是否完成
//IWDG_PR 和 IWDG_RLR 寄存器具有写访问保护。若要修改寄存器,必须首先对 IWDG_KR
//寄存器写入代码 0x5555。而写入其他值则会破坏该序列,从而使寄存器访问保护再次生效。
//这意味着重装载操作(即写入 0xAAAA)也会启动写保护功能。   
//解除写保护
IWDG->KR = 0x5555;
while(IWDG->SR & (1U<<1));    // 等待之前的重载值加载完成,避免新重载值被覆盖或者错乱
IWDG->RLR = 250;
IWDG->KR = 0xAAAA;

两种串口发送判断的标志的区别

标志位区别:

  • usart6_send_byte: 使用 TC (Transmission Complete) 标志位
  • USART6_SendChar: 使用 TXE (Transmit Data Register Empty) 标志位

检查时机:

  • TC: 检查整个数据帧(包括起始位、数据位、停止位)是否完全发送完成
  • TXE: 只检查发送数据寄存器是否为空,可以写入新数据

各自优势:

使用TC标志 (usart6_send_byte):

  • 可靠性更高,确保数据完全发送
  • 适合在以下场景使用:
    • UART关闭前的最后发送
    • 更改波特率前
    • 进入低功耗模式前
    • 对时序要求严格的场合

使用TXE标志 (USART6_SendChar):

  • 发送效率更高,尤其是连续发送时
  • 适合在以下场景使用:
    • 普通数据传输
    • 需要高速连续发送
    • 对实时性要求高的场合

在实际应用中,如无特殊要求,推荐使用TXE标志的方式,可以获得更好的传输效率。

RFID射频识别

什么是RFID?

RFID(Radio Frequency Identification,射频识别)是一种利用 无线电波 进行非接触式数据传输的技术,主要用于自动识别和跟踪目标对象(如物品、动物或人)。

  • 核心功能通过无线信号读取或写入附着在物体上的电子标签(Tag)中的数据。
  • 对比传统条形码无需视线对准、可批量读取、数据可更新、环境适应性强。

系统组成

RFID系统主要由三部分构成:

  1. 电子标签(Tag)
    • 包含芯片(存储数据)和天线(收发信号)。
    • 类型
      • 无源标签:依赖读写器发射的能量工作,无需电池,成本低,寿命长,但通信距离短(常见于门禁卡)。
      • 有源标签:自带电池,主动发射信号,通信距离远(可达百米),但成本高、寿命有限(如车辆追踪)。
      • 半无源标签:电池仅供电给芯片,通信仍依赖读写器能量。
  2. 读写器(Reader)
    • 通过天线发送射频信号,激活标签并读取/写入数据。
    • 可固定(如仓库入口)或手持(如物流扫描枪)。
  3. 天线与软件系统
    • 天线负责增强信号传输效率。
    • 软件系统处理数据,与数据库或物联网(IoT)平台集成。

工作原理

  1. 读写器通过天线发送特定频率的射频信号。
  2. 标签进入磁场范围后,天线接收能量激活芯片(无源标签)或直接响应(有源标签)。
  3. 标签将存储的数据(如ID、传感器信息)通过无线电波传回读写器。
  4. 读写器解码数据并传输至后台系统处理。

主要技术参数

  • 频率范围
    • 低频(LF, 125-134 kHz):短距离(<10 cm),抗干扰强,适用于动物识别。
    • 高频(HF, 13.56 MHz):中等距离(~1 m),广泛用于支付卡、图书管理。
    • 超高频(UHF, 860-960 MHz):长距离(数米至数十米),适合物流和零售库存。
    • 微波(2.45 GHz及以上):更长距离,多用于特定工业场景。
  • 通信协议:ISO 14443(近场通信)、EPC Gen2(物流标准)等。

典型应用场景

  • 物流与供应链:货物追踪、库存自动化管理。
  • 零售:无人收银(如Amazon Go)、防伪和防盗。
  • 医疗:患者身份识别、医疗器械追踪。
  • 交通:ETC自动收费、公交卡、共享单车锁。
  • 工业:设备维护记录、生产流程监控。
  • 智慧城市:垃圾分类管理、智能停车。

疑问1

常用的门禁卡里面没有电池,是属于无源标签吗? 超市里面的衣服商品上也有类似的标签,里面有线圈,没有电池,也是无源标签? 汽车的电子钥匙是属于有源标签吗?

门禁卡(无源标签)

  • 原理: 门禁卡内部嵌入了 无源RFID标签(通常为 高频HF,13.56 MHz)。
    • 卡片没有电池,依靠读写器(如门禁感应器)发射的电磁场能量激活芯片,并通过线圈天线返回数据(如卡号)。
    • 通信距离短(通常<10 cm),符合门禁场景的安全需求。
  • 典型应用: 公司门禁、校园一卡通、酒店房卡等。

超市商品标签(无源标签或EAS标签)

超市衣物上的标签可能分为两种技术:

(1) 无源RFID标签

  • 结构:包含芯片和线圈天线,无电池。
  • 功能
    • 用于库存管理和自助结账(如优衣库、Zara的RFID标签)。
    • 读写器可批量读取商品信息,提升效率。
  • 频率:多为超高频UHF(860-960 MHz),读取距离可达数米。

(2) EAS(电子商品防盗系统)标签

  • 结构:仅有线圈或磁性材料,无芯片(严格来说不属于RFID)。
  • 功能
    • 通过特定频率的电磁场触发警报(如未消磁时通过安检门会鸣响)。
    • 无法存储数据,仅用于防盗。
  • 注意:若标签含芯片且可读写数据(如价格、尺码),则属于无源RFID标签。

3. 汽车电子钥匙(混合技术)

汽车钥匙的技术分为两类,需具体区分:

(1) 传统遥控钥匙(有源标签)

  • 原理
    • 钥匙内置电池,按下按钮时主动发射无线电信号(频率通常为315 MHz或433 MHz)。
    • 属于 有源标签,通信距离较远(数十米)。
  • 功能:解锁/上锁车门、开启后备箱等。

(2) 无钥匙进入系统(Passive Keyless Entry, PKE)

  • 原理
    • 钥匙内置 无源RFID标签(低频LF,125 kHz)和 有源射频模块
    • 无源部分:车辆发射低频信号唤醒钥匙,钥匙通过反向散射返回认证信号(无需电池供能)。
      • 实现车钥匙靠近就能解锁
    • 有源部分:钥匙主动发射UHF信号控制车门(需电池供电)。
      • 主动使用遥控解锁
  • 特点
    • 看似“无源”,实为混合设计,依赖电池维持部分功能。
    • 若钥匙电池耗尽,仍可通过物理接触(如将钥匙贴近车门)利用无源RFID功能应急解锁。

总结对比

场景 标签类型 技术细节
门禁卡 无源RFID 依赖读写器供能,无电池,短距离通信。
超市商品标签 无源RFID/EAS 无电池,RFID用于数据存储,EAS仅用于防盗。
汽车电子钥匙 有源/混合设计 传统遥控钥匙为纯有源;无钥匙进入系统混合无源(认证)与有源(控制)技术。

补充说明

  • 超市RFID标签的防盗设计: 部分RFID标签会结合EAS功能,既存储商品信息,又触发防盗警报(如Decathlon的标签)。
  • 汽车钥匙的电池寿命: 无钥匙进入系统的钥匙电池通常可使用2-4年,电量不足时仪表盘会提示更换。

疑问2

像超市的那种标签里面只有线圈的,只能起到防盗功能?没有消磁经过匝道时就会触发警报 RFID技术的TAG是可以被读取数据和被写入的?无源TAG里面的结构也只有线圈吗?

超市防盗标签(EAS)与RFID标签的区别

(1) EAS防盗标签(仅线圈,无芯片)

  • 结构与原理
    • 内部仅包含 线圈或磁性材料(如声磁标签中的铁氧体条),没有芯片,无法存储数据。
    • 通过线圈与防盗门(EAS天线)的电磁场共振触发警报(未消磁时)。
    • 功能单一:仅用于防盗,无法记录商品信息或更新数据。
  • 消磁与激活
    • 收银台通过强磁场或特定频率信号对标签消磁/解扣(如硬标签的机械解锁)。
    • 若未消磁,通过安检门时会因电磁场干扰触发警报。

RFID标签(含芯片与天线)

  • 结构与原理
    • 包含 芯片(存储数据)天线(线圈或其他形式),无源标签依赖读写器供能。
    • 可读写数据:支持通过读写器读取或修改芯片中的信息(如商品价格、批次号)。
  • 功能扩展
    • 除防盗外,还可用于库存管理、自助结账、供应链追踪等。
    • 例如:优衣库的RFID标签同时支持防盗和库存统计。

RFID标签的数据读写能力

  • 支持读写
    • 可读可写(Read/Write Tag):允许多次修改数据(如服装尺码、生产日期)。
    • 只读(Read-Only Tag):出厂时固化ID,不可更改(如低成本的商品标识)。
  • 读写操作
    • 通过读写器发射的射频信号传递指令,芯片接收后执行数据擦写。
    • 例如:仓库中更新商品状态(“已出库”)。

无源RFID标签的内部结构

无源RFID标签并非“只有线圈”,其核心结构包括两部分:

(1) 芯片(IC)

  • 功能
    • 存储数据(ID、传感器信息等)。
    • 处理读写器的指令(如加密认证、数据读写)。
  • 能耗
    • 无源标签的芯片完全依赖读写器发射的电磁场能量驱动。

(2) 天线

  • 形式
    • 低频/高频(LF/HF)标签:通常采用 线圈天线(如门禁卡的环形线圈)。
    • 超高频(UHF)标签:多使用 偶极子天线(长条形,适合远距离通信)。
  • 作用
    • 接收读写器的能量并激活芯片。
    • 将芯片处理后的数据通过电磁波反向散射回读写器。

4. 典型标签结构对比

标签类型 芯片 天线形式 功能 示例场景
EAS防盗标签 线圈或磁性材料 仅触发防盗警报 超市未消磁商品
无源RFID标签 线圈/偶极子 数据存储、读写、防盗 优衣库商品标签
有源RFID标签 复杂天线 主动发射信号,远距离通信 物流集装箱追踪

5. 补充说明

(1) RFID标签的防盗增强方案

部分RFID标签会结合EAS功能:

  • 双重模式标签
    • 平时作为RFID标签存储数据,若未结账则触发EAS防盗警报(如迪卡侬的标签)。
  • 软件防盗
    • 结账时系统自动将RFID标签状态标记为“已售出”,未售出商品经过出口时触发警报。

(2) 无源标签的微型化设计

  • 最新技术(如NFC标签)可将芯片和天线封装在超薄贴纸中(厚度<0.1mm),甚至嵌入纺织品。

应用场景示例

  • 超市智能结账: 顾客将购物车推过RFID读写区,系统批量读取商品信息并自动结算(无需逐个扫描)。
  • 服装品牌防伪: 通过加密的RFID标签验证商品真伪,消费者用手机NFC功能即可读取生产信息。

疑问3

无源TAG标签好像可以使用读写器来读取其中的数据以及写入相关数据,但是手机的NFC功能好像具备赋值无源标签的功能,但是有一些加密的就不行,能说一下原理吗?

你提到的现象涉及 RFID/NFC 标签的数据读写机制手机NFC的功能限制以及 加密技术的实现原理。以下详细解释:

无源标签的读写原理

(1) 基本读写流程

  • 读取数据: 读写器(或手机NFC)发射射频能量 → 标签天线接收能量激活芯片 → 芯片将存储的数据通过反向散射(Backscatter)传回读写器。
  • 写入数据: 读写器发送特定指令和待写入数据 → 标签芯片接收后修改存储区内容(需芯片支持可写权限)。

(2) 标签存储结构

  • 普通标签(如MIFARE Ultralight): 存储区开放,无需密钥即可读写(类似“明文存储”)。
  • 加密标签(如MIFARE Classic或DESFire): 存储区分权限控制,需通过密钥认证才能访问特定区域。

手机NFC的标签操作能力

手机NFC本质是一个 高频(13.56 MHz)RFID读写器,支持以下操作:

(1) 读取/写入非加密标签

  • 示例场景
    • 读取公交卡余额(公开数据区)。
    • 写入网址或文本到空白NFC标签(如智能海报)。
  • 限制: 若标签存储区未加密,手机可直接读写(依赖APP权限,如NFC Tools)。

(2) 无法破解加密标签

  • 加密标签的保护机制
    • 认证流程:读写器需先与标签进行密钥交换(如3DES、AES算法),验证通过后才能访问数据。
    • 密钥存储:密钥通常保存在芯片安全区,无法通过无线信号直接提取。
  • 手机NFC的限制
    • 普通手机APP无法暴力破解加密算法(受计算能力和协议限制)。
    • 若不知密钥,即使复制标签的物理信号(如UID),也无法访问加密数据区。

加密标签的典型技术

(1) 对称加密(如MIFARE Classic)

  • 原理: 读写器和标签使用相同的密钥进行认证(如MIFARE Classic的CRYPTO1算法)。
  • 漏洞: 早期算法(如CRYPTO1)已被破解,可通过特殊设备(如Proxmark3)嗅探通信并推算密钥。
  • 手机操作: 若已知密钥,可通过专业APP(如MIFARE Classic Tool)读写数据;否则无法复制完整数据。

(2) 非对称加密(如MIFARE DESFire)

  • 原理: 使用公私钥对(如RSA、ECC),密钥不可逆向推导,安全性更高。
  • 手机操作: 除非获得私钥授权,否则手机NFC无法读写加密数据区。

(3) 物理防护

  • 防篡改设计: 部分标签(如银行卡)的芯片会检测物理破解尝试(如侧信道攻击),触发自毁机制。

为什么有些标签可被手机“复制”?

(1) 仅复制UID(伪复制)

  • UID(唯一标识符): 部分标签的UID是明文且可模拟(如某些空白NFC标签)。
  • 实际效果: 手机可模拟相同UID,但若系统校验加密数据或动态密钥,模拟标签仍无法通过验证。 示例
    • 小区门禁卡若仅校验UID,手机模拟后可开门;
    • 若门禁系统同时校验加密数据,则模拟无效。

(2) 全数据克隆(需破解密钥)

  • 条件: 加密算法已被破解(如MIFARE Classic),且攻击者通过专业设备获取了密钥。
  • 手机操作: 将密钥和加密数据写入可编程标签(如NTAG21x),但需依赖外部破解工具。

实际案例对比

标签类型 是否可手机复制? 原理说明
未加密门禁卡 ✅ 是 直接读取UID并模拟,无需密钥。
MIFARE Classic卡 ⚠️ 部分 若已知密钥或算法被破解,可克隆;否则仅能模拟UID。
银行卡(PayWave) ❌ 否 使用动态密钥和EMV协议,每次交易生成新密文,无法复制。
加密工牌(DESFire) ❌ 否 非对称加密+动态认证,手机无密钥无法克隆。

** 如何保护标签不被非法复制?**

  • 选择高安全等级标签: 如MIFARE DESFire EV2、NXP NTAG 424 DNA,支持AES加密和防中继攻击(Anti-Relay)。
  • 系统端增强校验: 不依赖UID,需验证加密数据或动态令牌(如一次一密)。
  • 物理绑定: 标签与物体融合(如嵌入商品内部),破坏即失效。

总结

  • 手机NFC可复制无源标签的条件: 标签未加密,或加密算法已被破解且密钥已知。
  • 加密标签的安全性: 依赖算法强度、密钥管理及系统设计,普通用户无法通过手机NFC直接克隆。

疑问4

总结来讲就是电子标签上也有天线比如线圈。外面的天线则用于增强读写器的信号的。 至于软件系统则是利用读写器进行读取标签数据和写入标签数据的,相当于实现业务价值

核心总结:RFID系统的核心组成与协作

1. 硬件部分

  1. 电子标签(Tag)
    • 内部结构
      • 芯片(IC):存储数据(如唯一ID、状态信息)。
      • 天线(线圈/偶极子):接收读写器能量并传输数据(无源标签依赖此能量激活)。
      • 示例:门禁卡中的线圈天线、UHF标签的偶极子天线。
    • 功能:作为数据载体,被动或主动响应读写器信号。
  2. 读写器(Reader)与外部天线
    • 读写器
      • 控制射频信号的发射与接收,驱动标签通信。
      • 支持数据解析(如EPC编码)、防碰撞算法(多标签同时读取)。
    • 外部天线
      • 增强信号覆盖范围与稳定性(如扩大读取距离、适应复杂环境)。
      • 类型选择:圆极化天线(标签方向随机) vs. 线性极化天线(定向精准)。

2. 软件系统

软件系统是RFID的“大脑”,核心任务是将硬件采集的原始数据转化为业务价值:

  1. 数据读写控制
    • 通过读写器向标签发送指令,完成数据读取或写入(如更新库存状态)。
    • 示例:仓库入口读写器扫描托盘标签,标记为“已入库”。
  2. 业务逻辑实现
    • 基础逻辑
      • 门禁系统:验证标签权限→触发开门/报警。
      • 零售结算:读取商品标签→生成订单→扣减库存。
    • 高级功能
      • 数据分析:根据标签位置生成热力图(如商场客流分析)。
      • 自动化决策:库存低于阈值时自动触发补货流程。
  3. 系统分层架构
    • 中间件
      • 数据过滤(去重、纠错)、协议转换(兼容不同读写器品牌)。
      • 示例:过滤无效标签信号,仅上传有效出入库记录。
    • 数据库
      • 存储静态数据(标签ID关联的商品信息)和动态事件(如物流轨迹)。
    • 应用层
      • 用户界面(如库存看板)、与外部系统集成(ERP、支付平台)。

3. 协作流程示例:智能仓库管理

  1. 硬件交互
    • 托盘进入仓库时,固定读写器通过外部天线批量扫描标签,读取货物ID。
  2. 软件处理
    • 中间件过滤重复数据,校验标签合法性(如黑名单检测)。
    • 应用系统更新库存数据库,触发自动化分拣任务。
  3. 业务输出
    • 仓库管理员通过UI查看实时库存,系统自动生成补货工单。

4. 关键补充

  • 标签天线的设计差异
    • 低频/高频标签多用线圈天线(短距离、高稳定性),超高频标签多用偶极子天线(长距离、低成本)。
  • 外部天线的部署策略
    • 多天线阵列:覆盖仓库出入口的多个角度,避免读取盲区。
    • 功率调节:降低功率避免串读(如密集货架场景)。
  • 软件安全设计
    • 数据加密:读写器与标签间采用AES加密,防止窃听。
    • 权限分级:限制不同角色对标签数据的访问权限(如仅允许管理员写入)。

一句话总结

RFID系统通过标签天线与读写器天线完成能量与数据的无线交互,软件系统则在此基础之上,将原始信号转化为业务动作(如开门、结算、告警),驱动实际场景的自动化与智能化。

射频卡属于RFID系统中的电子标签吗?

射频卡(RF卡)属于RFID系统中的电子标签,它是电子标签的一种具体应用形式。

射频卡的本质

射频卡(如门禁卡、公交卡、银行卡等)是一种封装成卡片形态的电子标签,其核心功能与RFID电子标签完全一致:

  • 无源设计:依赖读写器发射的电磁波获取能量(被动式标签)。
  • 无线通信:通过射频信号(如低频125kHz、高频13.56MHz、超高频等)与读写器交互。
  • 数据存储:芯片中存储ID、加密信息或其他数据。

射频卡与电子标签的关系

  • 包含关系:射频卡是电子标签的一种具体形态,而电子标签的形式更广泛(如贴纸、植入式标签、货物标签等)。
  • 应用场景:射频卡多用于短距离身份识别(如门禁、支付),而其他形态的标签可能用于物流、仓储、动物追踪等场景。

区分其他概念

  • 接触式IC卡(如SIM卡):需物理接触读写,不属于RFID
  • NFC技术:基于高频RFID(13.56MHz),可视为RFID的子集,兼容部分射频卡。

S50射频卡(MIFARE Classic 1K)

每个扇区中的块3的存取控制权限(Access Bits)

控制块的 Access Bits(字节6~8)决定了数据块和控制块的操作权限。其通过 3字节(24位) 的二进制编码定义以下权限:

  • 对数据块(块0~2)的权限:读、写、加值、减值等。
  • 对控制块(块3)的权限:密码A/B的读取和修改权限。

存取控制位规则

  • 每个块(块0~3)对应 3个控制位(C1, C2, C3),共需 12位(3位×4块)。
  • 剩余的 12位(3字节=24位)中,部分用于校验和兼容性(例如确保存取控制位的合法性)。
  • 权限组合通过二进制逻辑表定义,自己取查表

操作类型

对数据块和控制块的操作需通过 密钥认证(Key A或Key B)后方可执行:

  1. 读(Read):读取块内容。
  2. 写(Write):覆盖块数据。
  3. 加值(Increment):仅对数值块有效,增加存储值。
  4. 减值(Decrement):仅对数值块有效,减少存储值。
  5. 传输(Transfer):将临时寄存器中的值写入块。
  6. 中止(Halt):使卡片进入休眠状态。

安全注意事项

  1. 默认密码风险:出厂默认密码(如Key A=0xFFFFFFFFFFFF)需立即修改。
  2. 权限配置错误:错误的Access Bits可能导致扇区永久锁定。
  3. 独立密钥管理:每个扇区可设置独立密码(Key A/B),建议不同扇区使用不同密钥。
  4. 数值块保护:若数值块未配置正确权限,可能导致数据篡改。

典型应用场景

  • 门禁系统:UID用于身份识别,数据块存储用户权限。
    • 那扇门能进,那扇门不能进
  • 电子钱包:数值块存储余额,通过加/减值操作扣费。
  • 数据加密:敏感信息存储在独立扇区,配合高强度密钥。

总结

S50卡的存储结构通过 扇区-块 的层级设计实现了灵活的数据管理与权限控制。实际应用中需重点关注:

  1. Access Bits的配置,避免权限漏洞;
  2. 密钥的安全存储,防止未授权访问;
  3. 数值块的特殊操作逻辑,确保交易一致性。

读写数据都要先验证密码A和B吗?他们的作用是什么?

MIFARE Classic 1K(S50) 中,密码A(Key A)和密码B(Key B)的验证逻辑及作用完全由 存取控制位(Access Bits) 的配置决定。并非所有操作都需要同时验证两个密码

密码A和密码B的核心作用

密码类型 核心作用
Key A - 默认用于认证扇区访问权限(出厂值通常为 0xFFFFFFFFFFFF) - 可修改,但需谨慎操作避免锁定扇区
Key B - 可选密码,提供额外的权限分层(例如区分管理员与普通用户) - 可完全禁用(通过存取控制位配置)

是否需要同时验证两个密码?

  • 不需要同时验证。每次操作仅需验证 Key AKey B 中的一个,具体取决于 存取控制位 的配置。
  • 例外情况:若存取控制位设置为某些特殊组合(如 Key A 仅用于认证,Key B 被禁用),则只能使用 Key A。

公开读 + 加密写(如公交卡余额扣费)

  • 配置:存取控制位设为 0 0 0
  • 操作逻辑
    • 读取余额:无需密码(直接读数据块)。
    • 扣费(减值):需验证 Key B 才能执行操作。

双密码分层管理(如企业门禁系统)

  • 配置:存取控制位设为 0 1 0
  • 操作逻辑
    • 普通员工:使用 Key A 验证,仅能读取数据。
    • 管理员:使用 Key B 验证,可修改权限或数据。

完全锁定(防篡改场景)

  • 配置:存取控制位设为 1 1 1
  • 结果:所有操作(包括读/写)均被禁止,扇区永久锁定(即使知道密码)。

RC522读写器配置思路(使用别人现成的库吧,配置有一点多)

我现在正在配置RC522读写器,我打算使用软件模拟SPI与它通信。 我已经配置好了SPI,但是不知道怎么配置RC522,没有大致的配置思路? 我打算用来读写MIFARE Std 1k(MF1 IC S50): 国内常称:MF1 S50

对于RC522配置,我建议按以下步骤进行:

初始化流程

  • 先复位RC522芯片

    • 读和写寄存器函数要先写好
  • 配置发送和接收的通信协议参数

  • 设置中断和超时参数

关键寄存器配置

  • Mode寄存器:设置通信类型(TypeA/TypeB)
  • TxMode和RxMode寄存器:配置数据传输速率
  • RFCfg寄存器:设置接收增益和载波检测阈值

MIFARE Std 1k卡片特定配置

  • 设置CRC校验
  • 配置编码方式为Miller编码
  • 配置通信速率(106 kbps)

电源和天线配置

  • 配置天线驱动
  • 设置适当的发射功率
  • 启动RF场

其他

时钟总线上的外设复位寄存器

在很多硬件设计中,复位寄存器在触发复位之后,硬件会自动将其恢复为默认值 0,因此无需手动再写一次 0(不复位)。这种设计简化了复位操作流程,同时避免了冗余的写操作。

为什么库函数只需要“先写 0 再写 1”?

  1. 触发复位后,硬件自动清零:
    • 复位信号在触发后,硬件通常会自动将复位寄存器恢复为默认状态(0),以表明复位已经结束。
    • 这意味着写 1 触发复位时,无需手动再写 0,因为硬件本身会完成复位信号的清除。
  2. 复位完成后,外设状态即退出复位:
    • 复位寄存器的主要任务是产生一个复位信号,并不需要长时间保持复位状态。
    • 1 后复位触发,寄存器会自动清零,从而让外设退出复位,回到工作状态。
  3. 硬件设计简化了用户操作:
    • 如果每次复位都需要写 1 -> 写 0,不仅增加了代码复杂性,还可能导致用户误操作(比如漏写 0)。
    • 自动清零的机制让复位逻辑对用户透明,降低了使用的复杂性。

为什么还需要写 0 再写 1

尽管复位寄存器自动清零,但库函数仍然需要先写 0 再写 1,原因如下:

  1. 清除可能的遗留状态:
    • 如果某次复位操作失败,或者某些原因导致复位位未被正确清零,直接写 1 可能会导致复位逻辑失效。
    • 通过显式先写 0,可以清除任何可能的残留复位状态。
  2. 确保复位信号完整:
    • 某些硬件逻辑需要完整的 0 -> 1 的上升沿来触发复位。如果不先写 0,可能导致复位逻辑无法正常触发。

库函数开发的注意事项

事项1

  • 还是先有程序思路再写代码,因为有一些库函数也没有实现,就只能手动寄存器实现。
    • 比如USART_ClearITPendingBit(USART1, USART_IT_IDLE);清除不了,没有这样的参数。
    • 需要手动实现
  • 配置时有时候会跨函数,比如串口,GPIO口,串口,NVIC等,没有思路容易乱。

事项2

  • 串口通信时GPIO配置复用功能时,要在GPIO基本设置的前面,外设总线时钟使能的后面,否则会偷发一个数据

库函数的通用配置思路

比如定时器的延迟函数

void tim5Delay_Ms(u16 ms)
{
    // 使能TIM5的时钟
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM5, ENABLE);
    
    // 禁用定时器以进行配置
    TIM_Cmd(TIM5, DISABLE);
    
    // 配置TIM6的预分频器和自动重载值
    TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure = {0};
    //默认一个结构体配置
    TIM_TimeBaseStructInit(&TIM_TimeBaseStructure);
    
    //只需要配置需要的参数,其他就用默认的
    TIM_TimeBaseStructure.TIM_Prescaler = 8400 - 1;
    TIM_TimeBaseStructure.TIM_Period = ms * 10 - 1;
    // 选择向上计数模式
    TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
    //这里面也会产生一个更新事件,不用再写一个更新事件。
    TIM_TimeBaseInit(TIM5, &TIM_TimeBaseStructure);
    
    
    // 清除更新标志位
    TIM_ClearFlag(TIM5, TIM_FLAG_Update);
    
    // 使能定时器
    TIM_Cmd(TIM5, ENABLE);
    
    // 等待更新事件完成
    while(!TIM_GetFlagStatus(TIM5, TIM_FLAG_Update));
    
    // 可选:清除更新标志位
    TIM_ClearFlag(TIM5, TIM_FLAG_Update);
    // 禁用定时器以进行配置,关闭定时器或者设置单次计数模式
    TIM_Cmd(TIM5, DISABLE);
}

先整理好配置思路,按照思路调用对应函数即可。不相关参数,可以不赋值(结构体),也可以通过先默认初始化配置结构体,再根据需求修改特定参数的配置思路,可以使代码更简洁、易读,并减少配置错误的风险。这种方法适用于所有外设的配置,推荐在开发过程中广泛使用。

是否可以使用“|”一起配置?

建议都分开写避免错误,但是分开写时必须保证每配置一个都是被赋值到寄存器中的,不然会被覆盖。

GPIO_InitStruct.GPIO_Pin = GPIO_Pin_5;
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_6;
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_7;
GPIO_Init(GPIOA, &GPIO_InitStruct); // 只配置Pin7

指纹模块函数封装

GPIO口初始化
usart6串口初始化
接收中断配置
串口发送一个指令函数
解析收到的数据包函数

About

STM32F401RETX主控芯片,STM32培训结课项目

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages