直接逐个功能实现的方式:
- 容易造成代码高度耦合。比如实现串口功能时直接写死了数据处理逻辑,后续添加其他功能时可能需要大量修改原有代码。
- 代码复用性差。类似的功能可能会在不同地方重复编写,导致代码冗余且难以维护。
- 扩展性受限。新增功能时经常需要修改现有代码,容易引入bug。
先搭建框架再实现功能的方式:
- 通过良好的架构设计(如分层架构),实现各模块解耦。比如:
- 硬件抽象层(HAL)处理底层硬件
- 驱动层处理具体外设
- 应用层实现业务逻辑
- 定义清晰的接口,使各模块之间通过标准接口通信,而不是直接调用。
- 便于功能扩展和维护:
- 新增功能时只需实现相应接口
- 修改某个模块不会影响其他模块
- 可以方便地复用已有模块
举个例子:假设要实现一个带显示功能的数据采集系统。
不使用框架的做法可能是:
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):
- 主要负责硬件的直接操作
- 提供硬件的基础功能接口
- 代码特点:
// 驱动层示例 - 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):
- 实现具体的业务逻辑
- 调用驱动层提供的接口
- 代码特点:
// 应用层示例 - 温度监控应用
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();
}
}
主要区别:
- 关注点不同
- 驱动层关注如何操作硬件(寄存器配置等)
- 应用层关注业务流程(数据处理、控制逻辑等)
- 依赖关系
- 驱动层依赖硬件抽象层(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);
特点:
- 最高层次的抽象,屏蔽了硬件细节
- 代码可读性好,容易上手
- 可移植性最好,跨平台开发方便
- 执行效率较低,代码量大
- 适合快速开发,不太注重性能的场合
选择建议:
- 性能要求高的项目(如高速数据采集):选择寄存器级开发
- 平衡开发效率和性能:选择标准库开发
- 注重开发效率和可移植性:选择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 提供了标准化的函数,直接调用即可完成复位操作。这个函数是:
#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
位)来触发系统复位。
如果不使用 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) {
// 不会执行到这里,因为系统已复位
}
}
注意
AIRCR_VECTKEY_MASK
是一个写保护密钥,写入时必须使用0x5FA
,否则写入无效。- 设置
SYSRESETREQ
位 (1 << SCB_AIRCR_SYSRESETREQ_Pos
) 即触发软件复位。 - 复位后,所有外设和系统状态都会恢复为初始状态,类似于硬件复位。
总结
如果你使用的是官方的 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(Radio Frequency Identification,射频识别)是一种利用 无线电波 进行非接触式数据传输的技术,主要用于自动识别和跟踪目标对象(如物品、动物或人)。
- 核心功能:通过无线信号读取或写入附着在物体上的电子标签(Tag)中的数据。
- 对比传统条形码:无需视线对准、可批量读取、数据可更新、环境适应性强。
RFID系统主要由三部分构成:
- 电子标签(Tag)
- 包含芯片(存储数据)和天线(收发信号)。
- 类型:
- 无源标签:依赖读写器发射的能量工作,无需电池,成本低,寿命长,但通信距离短(常见于门禁卡)。
- 有源标签:自带电池,主动发射信号,通信距离远(可达百米),但成本高、寿命有限(如车辆追踪)。
- 半无源标签:电池仅供电给芯片,通信仍依赖读写器能量。
- 读写器(Reader)
- 通过天线发送射频信号,激活标签并读取/写入数据。
- 可固定(如仓库入口)或手持(如物流扫描枪)。
- 天线与软件系统
- 天线负责增强信号传输效率。
- 软件系统处理数据,与数据库或物联网(IoT)平台集成。
- 读写器通过天线发送特定频率的射频信号。
- 标签进入磁场范围后,天线接收能量激活芯片(无源标签)或直接响应(有源标签)。
- 标签将存储的数据(如ID、传感器信息)通过无线电波传回读写器。
- 读写器解码数据并传输至后台系统处理。
- 频率范围:
- 低频(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自动收费、公交卡、共享单车锁。
- 工业:设备维护记录、生产流程监控。
- 智慧城市:垃圾分类管理、智能停车。
常用的门禁卡里面没有电池,是属于无源标签吗? 超市里面的衣服商品上也有类似的标签,里面有线圈,没有电池,也是无源标签? 汽车的电子钥匙是属于有源标签吗?
门禁卡(无源标签)
- 原理:
门禁卡内部嵌入了 无源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年,电量不足时仪表盘会提示更换。
像超市的那种标签里面只有线圈的,只能起到防盗功能?没有消磁经过匝道时就会触发警报 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功能即可读取生产信息。
无源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直接克隆。
总结来讲就是电子标签上也有天线比如线圈。外面的天线则用于增强读写器的信号的。 至于软件系统则是利用读写器进行读取标签数据和写入标签数据的,相当于实现业务价值
核心总结:RFID系统的核心组成与协作
1. 硬件部分
- 电子标签(Tag)
- 内部结构:
- 芯片(IC):存储数据(如唯一ID、状态信息)。
- 天线(线圈/偶极子):接收读写器能量并传输数据(无源标签依赖此能量激活)。
- 示例:门禁卡中的线圈天线、UHF标签的偶极子天线。
- 功能:作为数据载体,被动或主动响应读写器信号。
- 内部结构:
- 读写器(Reader)与外部天线
- 读写器:
- 控制射频信号的发射与接收,驱动标签通信。
- 支持数据解析(如EPC编码)、防碰撞算法(多标签同时读取)。
- 外部天线:
- 增强信号覆盖范围与稳定性(如扩大读取距离、适应复杂环境)。
- 类型选择:圆极化天线(标签方向随机) vs. 线性极化天线(定向精准)。
- 读写器:
2. 软件系统
软件系统是RFID的“大脑”,核心任务是将硬件采集的原始数据转化为业务价值:
- 数据读写控制:
- 通过读写器向标签发送指令,完成数据读取或写入(如更新库存状态)。
- 示例:仓库入口读写器扫描托盘标签,标记为“已入库”。
- 业务逻辑实现:
- 基础逻辑:
- 门禁系统:验证标签权限→触发开门/报警。
- 零售结算:读取商品标签→生成订单→扣减库存。
- 高级功能:
- 数据分析:根据标签位置生成热力图(如商场客流分析)。
- 自动化决策:库存低于阈值时自动触发补货流程。
- 基础逻辑:
- 系统分层架构:
- 中间件:
- 数据过滤(去重、纠错)、协议转换(兼容不同读写器品牌)。
- 示例:过滤无效标签信号,仅上传有效出入库记录。
- 数据库:
- 存储静态数据(标签ID关联的商品信息)和动态事件(如物流轨迹)。
- 应用层:
- 用户界面(如库存看板)、与外部系统集成(ERP、支付平台)。
- 中间件:
3. 协作流程示例:智能仓库管理
- 硬件交互:
- 托盘进入仓库时,固定读写器通过外部天线批量扫描标签,读取货物ID。
- 软件处理:
- 中间件过滤重复数据,校验标签合法性(如黑名单检测)。
- 应用系统更新库存数据库,触发自动化分拣任务。
- 业务输出:
- 仓库管理员通过UI查看实时库存,系统自动生成补货工单。
4. 关键补充
- 标签天线的设计差异:
- 低频/高频标签多用线圈天线(短距离、高稳定性),超高频标签多用偶极子天线(长距离、低成本)。
- 外部天线的部署策略:
- 多天线阵列:覆盖仓库出入口的多个角度,避免读取盲区。
- 功率调节:降低功率避免串读(如密集货架场景)。
- 软件安全设计:
- 数据加密:读写器与标签间采用AES加密,防止窃听。
- 权限分级:限制不同角色对标签数据的访问权限(如仅允许管理员写入)。
一句话总结
RFID系统通过标签天线与读写器天线完成能量与数据的无线交互,软件系统则在此基础之上,将原始信号转化为业务动作(如开门、结算、告警),驱动实际场景的自动化与智能化。
射频卡(RF卡)属于RFID系统中的电子标签,它是电子标签的一种具体应用形式。
射频卡的本质
射频卡(如门禁卡、公交卡、银行卡等)是一种封装成卡片形态的电子标签,其核心功能与RFID电子标签完全一致:
- 无源设计:依赖读写器发射的电磁波获取能量(被动式标签)。
- 无线通信:通过射频信号(如低频125kHz、高频13.56MHz、超高频等)与读写器交互。
- 数据存储:芯片中存储ID、加密信息或其他数据。
射频卡与电子标签的关系
- 包含关系:射频卡是电子标签的一种具体形态,而电子标签的形式更广泛(如贴纸、植入式标签、货物标签等)。
- 应用场景:射频卡多用于短距离身份识别(如门禁、支付),而其他形态的标签可能用于物流、仓储、动物追踪等场景。
区分其他概念
- 接触式IC卡(如SIM卡):需物理接触读写,不属于RFID。
- NFC技术:基于高频RFID(13.56MHz),可视为RFID的子集,兼容部分射频卡。
每个扇区中的块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)后方可执行:
- 读(Read):读取块内容。
- 写(Write):覆盖块数据。
- 加值(Increment):仅对数值块有效,增加存储值。
- 减值(Decrement):仅对数值块有效,减少存储值。
- 传输(Transfer):将临时寄存器中的值写入块。
- 中止(Halt):使卡片进入休眠状态。
安全注意事项
- 默认密码风险:出厂默认密码(如Key A=0xFFFFFFFFFFFF)需立即修改。
- 权限配置错误:错误的Access Bits可能导致扇区永久锁定。
- 独立密钥管理:每个扇区可设置独立密码(Key A/B),建议不同扇区使用不同密钥。
- 数值块保护:若数值块未配置正确权限,可能导致数据篡改。
典型应用场景
- 门禁系统:UID用于身份识别,数据块存储用户权限。
- 那扇门能进,那扇门不能进
- 电子钱包:数值块存储余额,通过加/减值操作扣费。
- 数据加密:敏感信息存储在独立扇区,配合高强度密钥。
总结
S50卡的存储结构通过 扇区-块 的层级设计实现了灵活的数据管理与权限控制。实际应用中需重点关注:
- Access Bits的配置,避免权限漏洞;
- 密钥的安全存储,防止未授权访问;
- 数值块的特殊操作逻辑,确保交易一致性。
在 MIFARE Classic 1K(S50) 中,密码A(Key A)和密码B(Key B)的验证逻辑及作用完全由 存取控制位(Access Bits) 的配置决定。并非所有操作都需要同时验证两个密码
密码A和密码B的核心作用
密码类型 | 核心作用 |
---|---|
Key A | - 默认用于认证扇区访问权限(出厂值通常为 0xFFFFFFFFFFFF ) - 可修改,但需谨慎操作避免锁定扇区 |
Key B | - 可选密码,提供额外的权限分层(例如区分管理员与普通用户) - 可完全禁用(通过存取控制位配置) |
是否需要同时验证两个密码?
- 不需要同时验证。每次操作仅需验证 Key A 或 Key B 中的一个,具体取决于 存取控制位 的配置。
- 例外情况:若存取控制位设置为某些特殊组合(如 Key A 仅用于认证,Key B 被禁用),则只能使用 Key A。
公开读 + 加密写(如公交卡余额扣费)
- 配置:存取控制位设为
0 0 0
- 操作逻辑:
- 读取余额:无需密码(直接读数据块)。
- 扣费(减值):需验证 Key B 才能执行操作。
双密码分层管理(如企业门禁系统)
- 配置:存取控制位设为
0 1 0
- 操作逻辑:
- 普通员工:使用 Key A 验证,仅能读取数据。
- 管理员:使用 Key B 验证,可修改权限或数据。
完全锁定(防篡改场景)
- 配置:存取控制位设为
1 1 1
- 结果:所有操作(包括读/写)均被禁止,扇区永久锁定(即使知道密码)。
我现在正在配置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
触发复位时,无需手动再写0
,因为硬件本身会完成复位信号的清除。
- 复位信号在触发后,硬件通常会自动将复位寄存器恢复为默认状态(
- 复位完成后,外设状态即退出复位:
- 复位寄存器的主要任务是产生一个复位信号,并不需要长时间保持复位状态。
- 写
1
后复位触发,寄存器会自动清零,从而让外设退出复位,回到工作状态。
- 硬件设计简化了用户操作:
- 如果每次复位都需要写
1
-> 写0
,不仅增加了代码复杂性,还可能导致用户误操作(比如漏写0
)。 - 自动清零的机制让复位逻辑对用户透明,降低了使用的复杂性。
- 如果每次复位都需要写
尽管复位寄存器自动清零,但库函数仍然需要先写 0
再写 1
,原因如下:
- 清除可能的遗留状态:
- 如果某次复位操作失败,或者某些原因导致复位位未被正确清零,直接写
1
可能会导致复位逻辑失效。 - 通过显式先写
0
,可以清除任何可能的残留复位状态。
- 如果某次复位操作失败,或者某些原因导致复位位未被正确清零,直接写
- 确保复位信号完整:
- 某些硬件逻辑需要完整的
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串口初始化
接收中断配置
串口发送一个指令函数
解析收到的数据包函数