news 2026/10/5 7:59:33

MRAM工业嵌入式实战:MR25H40CDF与STM32F732IE驱动开发与掉电保护

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
MRAM工业嵌入式实战:MR25H40CDF与STM32F732IE驱动开发与掉电保护

1. 为什么MRAM在工业嵌入式场景里越来越受关注

1.1 从铁电到MRAM的存储选型变迁

做工业嵌入式的人都有一个共同的痛点:参数要频繁保存,掉电不能丢,写入寿命还得扛得住十年以上的现场运行。早些年大家习惯用EEPROM,比如AT24C系列,便宜、接口简单,但写入速度慢得让人着急,字节写入动辄5ms,整页擦写更是毫秒级起步。后来铁电存储器FRAM火了一阵,写入速度快、寿命近乎无限,但容量小、价格高,而且供应链一直不太稳定。

MRAM(磁阻随机存取存储器)算是这两年被工业圈越来越重视的一条路线。它的核心原理是用磁性隧道结的磁化方向来存储数据,而不是电荷。这个物理机制决定了几个关键特性:写入没有擦除周期概念,不需要先擦后写;写入能耗极低;数据保持能力在极端温度下依然可靠。MR25H40CDF就是Everspin出品的4Mbit SPI接口MRAM,工业级温度范围覆盖-40°C到+85°C,部分批次甚至能到+105°C。

我最初接触这颗芯片是因为一个电力监测终端的项目。现场设备安装在配电柜里,夏天柜内温度能到70°C以上,原来的FRAM方案在高温下偶尔出现数据校验失败。换成MR25H40CDF之后,连续跑了六个月的高低温循环测试,一次数据异常都没出现过。这个经历让我开始认真研究MRAM在嵌入式系统里的落地方式。

1.2 MR25H40CDF的核心参数与SPI接口特性

先把这颗芯片的关键参数摆出来,方便你判断是否适合自己的项目:

参数项规格
容量4Mbit(512K × 8位)
接口SPI,支持Mode 0和Mode 3
最高时钟40MHz
供电电压2.7V ~ 3.6V
写入寿命10^14次以上
数据保持20年以上
工作温度-40°C ~ +85°C(工业级)
封装8引脚DFN或SOIC

SPI接口这块需要特别注意。MR25H40CDF支持标准SPI模式0(CPOL=0,CPHA=0)和模式3(CPOL=1,CPHA=1),不支持模式1和模式2。很多STM32的HAL库默认配置是模式0,这个刚好匹配,但如果你用的是硬件SPI加DMA,初始化时一定要确认CPOL和CPHA的设置。

40MHz的时钟频率意味着理论最大数据传输率是5MB/s,实际有效速率取决于命令开销和总线周转。对于工业场景里常见的参数存储、日志记录、故障快照这些需求,这个速度完全够用。我实测过连续写入512字节的数据块,从发出写命令到CS拉高,整个过程大约在130微秒左右,比EEPROM快了将近两个数量级。

1.3 STM32F732IE作为主控的适配性分析

STM32F732IE属于STM32F7系列,Cortex-M7内核,主频216MHz,带1MB Flash和320KB RAM。选它来搭配MR25H40CDF有几个实际考量:

第一,F7系列的SPI外设支持最高50MHz的时钟,驱动MR25H40CDF的40MHz上限没有压力。第二,F732IE有多达6个SPI接口,方便你把MRAM挂在独立的SPI总线上,不和其它外设抢带宽。第三,F7系列带ART加速器和L1缓存,在处理SPI数据搬运时CPU占用率很低,适合跑RTOS或者复杂的工业协议栈。

我在一个运动控制卡的项目里就是用F732IE加两片MR25H40CDF,一片存运动参数和补偿表,一片存运行日志和故障记录。两片MRAM分别挂在SPI2和SPI4上,用DMA传输,CPU几乎不参与数据搬运,整个系统的实时性没有受到任何影响。

2. 硬件设计与SPI总线配置的关键细节

2.1 电路连接与PCB布局要点

MR25H40CDF的引脚定义很标准:CS、SCK、MOSI、MISO、VCC、GND,加上HOLD和WP两个控制引脚。在工业应用中,我建议把HOLD和WP都上拉到VCC,除非你确实需要硬件写保护功能。HOLD引脚如果悬空,在电磁干扰强的环境下可能被误触发,导致通信中断。

PCB布局方面有几个踩过坑的地方值得说。SPI的SCK线是高速信号,40MHz下波长大约7.5米,虽然PCB走线远小于这个长度,但反射和串扰仍然需要注意。我的做法是:SCK和MOSI走线尽量短且等长,MISO单独走一层或者远离SCK;CS线不要和SCK平行走太长距离,否则CS的跳变会耦合到时钟上。

去耦电容这块,MR25H40CDF的VCC引脚旁边必须放一个0.1μF的陶瓷电容,位置越近越好,最好在5mm以内。如果MRAM和主控之间的连线超过10cm,建议再加一个1μF的电容。我在一个长线缆连接的场景里,因为去耦不到位,40MHz下误码率很高,降到20MHz才稳定,后来加了电容之后40MHz跑得很稳。

注意:MR25H40CDF的WP引脚是低电平有效,如果不需要写保护,直接上拉到VCC。但如果你把它接到GPIO上做软件控制,上电默认状态一定要确保是高电平,否则第一次写操作会失败。

2.2 STM32CubeMX中的SPI参数配置

用CubeMX配置SPI接口驱动MR25H40CDF,几个关键参数这样设置:

  • Mode:Full-Duplex Master
  • Data Size:8 Bits
  • Clock Polarity:Low(对应Mode 0)
  • Clock Phase:1 Edge(对应Mode 0)
  • NSS:Software(用GPIO手动控制CS)
  • Baud Rate Prescaler:根据主频计算,目标不超过40MHz
  • First Bit:MSB First

F732IE的SPI2挂在APB1总线上,时钟频率最高54MHz。如果APB1跑54MHz,分频系数选2就是27MHz,选4就是13.5MHz。我一般先用较低频率调通,再逐步提高。实测下来,MR25H40CDF在27MHz下非常稳定,40MHz需要看PCB质量。

CS引脚用GPIO控制而不是硬件NSS,原因是MR25H40CDF的CS需要在每个命令序列前后有明确的建立和保持时间。硬件NSS在某些STM32型号上时序不够灵活,软件控制更可靠。CS的拉低和拉高之间要保证至少20ns的建立时间,拉高后到下一次拉低至少需要40ns。

// SPI初始化结构体示例(HAL库) hspi2.Instance = SPI2; hspi2.Init.Mode = SPI_MODE_MASTER; hspi2.Init.Direction = SPI_DIRECTION_2LINES; hspi2.Init.DataSize = SPI_DATASIZE_8BIT; hspi2.Init.CLKPolarity = SPI_POLARITY_LOW; hspi2.Init.CLKPhase = SPI_PHASE_1EDGE; hspi2.Init.NSS = SPI_NSS_SOFT; hspi2.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_4; hspi2.Init.FirstBit = SPI_FIRSTBIT_MSB; hspi2.Init.TIMode = SPI_TIMODE_DISABLE; hspi2.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;

2.3 电源设计与抗干扰措施

工业现场的电源环境往往很恶劣,电机启停、继电器动作都会在电源线上产生尖峰。MR25H40CDF虽然本身抗干扰能力不错,但电源设计不到位照样会出问题。

我的做法是在MRAM的VCC入口处加一个磁珠(比如0603封装的1KΩ@100MHz),后面跟一个10μF的钽电容和一个0.1μF的陶瓷电容组成π型滤波。磁珠抑制高频噪声,钽电容提供储能,陶瓷电容滤高频。这套组合在多个工业项目里验证过,效果很稳。

如果MRAM和主控之间的地平面不完整,建议在SPI信号线上串22Ω到33Ω的电阻,抑制反射。这个电阻值不能太大,否则上升沿变缓,40MHz下可能采样出错。我一般先用33Ω,如果波形过冲严重再调整。

3. 驱动层实现与读写操作实战

3.1 MR25H40CDF的命令集解析

MR25H40CDF的命令集和标准SPI Flash很像,但有几个关键区别。先看常用命令:

命令名称命令码说明
WREN0x06写使能
WRDI0x04写禁止
RDSR0x05读状态寄存器
WRSR0x01写状态寄存器
READ0x03读数据
WRITE0x02写数据

和SPI Flash最大的区别是:MRAM不需要擦除操作。Flash写之前必须先发擦除命令,等擦除完成才能写,而MRAM直接发WRITE命令就能写入。这个差异在驱动设计上影响很大,省掉了擦除状态轮询的环节,写入延迟从毫秒级降到微秒级。

状态寄存器的bit0是WEL(写使能锁存),每次写操作之前必须先发WREN把WEL置1,写完成后WEL自动清零。这个机制和Flash一样,是防止误写的第一道防线。

3.2 底层读写函数的实现

先写几个基础函数,后面所有操作都建立在这上面:

// CS控制宏 #define MRAM_CS_LOW() HAL_GPIO_WritePin(MRAM_CS_GPIO_Port, MRAM_CS_Pin, GPIO_PIN_RESET) #define MRAM_CS_HIGH() HAL_GPIO_WritePin(MRAM_CS_GPIO_Port, MRAM_CS_Pin, GPIO_PIN_SET) // 写使能 void MRAM_WriteEnable(void) { uint8_t cmd = 0x06; MRAM_CS_LOW(); HAL_SPI_Transmit(&hspi2, &cmd, 1, 100); MRAM_CS_HIGH(); } // 读状态寄存器 uint8_t MRAM_ReadStatus(void) { uint8_t cmd = 0x05; uint8_t status = 0; MRAM_CS_LOW(); HAL_SPI_Transmit(&hspi2, &cmd, 1, 100); HAL_SPI_Receive(&hspi2, &status, 1, 100); MRAM_CS_HIGH(); return status; } // 等待写完成(MRAM写入很快,但保险起见还是加一个) void MRAM_WaitReady(void) { uint32_t timeout = 10000; while ((MRAM_ReadStatus() & 0x01) && timeout--); }

读数据的函数:

void MRAM_Read(uint32_t addr, uint8_t *buf, uint32_t len) { uint8_t cmd[4]; cmd[0] = 0x03; cmd[1] = (addr >> 16) & 0xFF; cmd[2] = (addr >> 8) & 0xFF; cmd[3] = addr & 0xFF; MRAM_CS_LOW(); HAL_SPI_Transmit(&hspi2, cmd, 4, 100); HAL_SPI_Receive(&hspi2, buf, len, 1000); MRAM_CS_HIGH(); }

写数据的函数:

void MRAM_Write(uint32_t addr, uint8_t *buf, uint32_t len) { uint8_t cmd[4]; cmd[0] = 0x02; cmd[1] = (addr >> 16) & 0xFF; cmd[2] = (addr >> 8) & 0xFF; cmd[3] = addr & 0xFF; MRAM_WriteEnable(); MRAM_CS_LOW(); HAL_SPI_Transmit(&hspi2, cmd, 4, 100); HAL_SPI_Transmit(&hspi2, buf, len, 1000); MRAM_CS_HIGH(); MRAM_WaitReady(); }

这几个函数看起来简单,但有几个细节容易出错。第一,地址是24位的,因为4Mbit容量对应19位地址,但命令格式用24位,高位补零。第二,HAL_SPI_Transmit和HAL_SPI_Receive的超时时间要设够,40MHz下传512字节大约128微秒,但HAL库有开销,超时设1000ms比较保险。第三,CS的拉高必须在最后一个字节完全移出之后,HAL库函数返回时数据已经发送完成,可以直接拉高。

3.3 页写入与跨页处理

MR25H40CDF的写入没有页边界限制,这是和Flash的另一个重要区别。Flash通常有256字节的页限制,跨页写入会回卷覆盖,必须分页处理。MRAM可以连续写入任意长度,只要地址不超出容量范围。

但实际使用中我还是建议按块写入,比如每256字节或512字节作为一个记录单元。这样做的好处是便于管理,读取时也方便定位。我在一个数据记录仪项目里,把MRAM分成2048个256字节的块,每块存一条记录,块头带CRC校验和时间戳。写满之后循环覆盖最旧的块,实现了一个简单的环形日志。

跨页处理在MRAM上不需要特别考虑,但地址对齐仍然值得注意。如果写入的数据结构包含多字节变量,建议按4字节对齐存放,这样读取时可以直接用指针转换,效率更高。

4. 数据可靠性与掉电保护策略

4.1 CRC校验与数据完整性

工业现场的数据不能只靠存储介质的可靠性,软件层面的校验必须做。我在每个数据块后面加4字节CRC32,读取时先校验再使用。CRC32的计算可以用STM32的硬件CRC外设,速度很快,几乎不占CPU时间。

// 数据块结构示例 typedef struct { uint32_t timestamp; uint16_t data_len; uint8_t data[246]; uint32_t crc; } MRAM_Record_t; // 总共256字节

写入时先算好CRC填在末尾,读取时重新计算并比对。如果CRC不匹配,说明数据损坏,触发告警或者从备份块恢复。我在实际项目里遇到过因为电源纹波导致SPI通信误码的情况,有了CRC之后至少能发现错误,不会把脏数据当成有效数据用。

4.2 掉电检测与紧急写入

MRAM的写入速度极快,这给掉电保护带来了天然优势。但前提是系统能在掉电瞬间检测到并完成写入。我的做法是用一个比较器监测电源电压,当电压降到阈值以下时触发中断,在中断里把关键数据写入MRAM。

具体电路是用一个TLV3011比较器,参考电压设成3.0V,电源正常时是3.3V,比较器输出高。当电源掉到3.0V以下,比较器输出翻转,接到STM32的EXTI引脚。中断服务程序里立刻调用MRAM_Write把关键参数写进去。从检测到掉电到完成写入,整个过程大约200微秒,MRAM写入512字节只需要130微秒,时间足够。

这里有个细节:掉电后电源电压还会维持一段时间,因为板上的电容还在放电。这个维持时间取决于电容容量和系统功耗。我一般用100μF的电容,系统功耗50mA的话,从3.0V降到2.7V大约有6ms的窗口,足够完成写入。

提示:掉电中断的优先级要设成最高,并且中断服务程序里不要做任何耗时操作,直接写MRAM然后进入死循环等待电源完全掉电。

4.3 双备份与版本管理

对于特别关键的数据,比如校准参数、设备序列号、累计运行时间,我建议做双备份。MRAM容量有512KB,存两份完全没压力。具体做法是在地址0x00000和0x40000各存一份,写入时两份都写,读取时先读第一份,CRC校验失败再读第二份。

版本管理用一个递增的序列号,每次写入序列号加1。读取时比较两份的序列号,取较大的那份。这样即使其中一份在写入过程中掉电导致数据不完整,另一份仍然是有效的。

这个机制我在一个远程监测终端上用了三年多,现场设备经历了无数次意外断电,从来没有丢过关键参数。MRAM的写入原子性虽然不能保证整个数据块同时写入,但配合双备份和序列号,可靠性已经足够工业级应用。

5. 性能优化与常见问题排查

5.1 DMA传输与CPU占用率优化

用HAL_SPI_Transmit和HAL_SPI_Receive是阻塞式的,CPU要等数据传完才能干别的。对于大数据量传输,比如一次读几KB的日志,阻塞式会明显影响系统实时性。这时候用DMA就很有必要。

配置DMA的方式在CubeMX里勾选SPI的DMA请求,然后调用HAL_SPI_Transmit_DMA和HAL_SPI_Receive_DMA。注意DMA传输完成中断里要拉高CS,不能在调用DMA函数后立刻拉高,否则数据还没传完CS就释放了。

void MRAM_Read_DMA(uint32_t addr, uint8_t *buf, uint32_t len) { uint8_t cmd[4]; cmd[0] = 0x03; cmd[1] = (addr >> 16) & 0xFF; cmd[2] = (addr >> 8) & 0xFF; cmd[3] = addr & 0xFF; MRAM_CS_LOW(); HAL_SPI_Transmit(&hspi2, cmd, 4, 100); // 命令用阻塞发送 HAL_SPI_Receive_DMA(&hspi2, buf, len); // 数据用DMA接收 // CS在DMA完成回调里拉高 } void HAL_SPI_RxCpltCallback(SPI_HandleTypeDef *hspi) { if (hspi->Instance == SPI2) { MRAM_CS_HIGH(); } }

实测下来,用DMA传输1KB数据,CPU占用时间从阻塞式的约200微秒降到不到10微秒,效果非常明显。

5.2 常见问题速查表

现象可能原因排查方法解决方案
读出的数据全是0xFFCS未正确拉低用示波器看CS波形检查GPIO配置和CS宏定义
写入后读出来不对SPI模式不匹配确认CPOL/CPHA设置改为Mode 0或Mode 3
高速下误码率高信号完整性差示波器看SCK和MISO波形降低时钟、加串阻、改善去耦
偶尔写入失败电源纹波大示波器看VCC纹波加磁珠和滤波电容
状态寄存器读不到WELWREN命令未生效读状态寄存器bit0检查WREN命令码和CS时序
DMA传输不完成DMA中断未使能检查NVIC配置使能DMA通道中断

5.3 实操心得与避坑经验

第一个坑是CS的时序。我最初用硬件NSS,发现每次传输之间CS会自己拉高,导致命令序列被打断。后来改成软件控制CS,问题解决。软件控制CS的另一个好处是可以精确控制CS的建立和保持时间。

第二个坑是SPI时钟相位。有一次用CubeMX默认配置,CPHA设成了2Edge,结果读出来全是乱码。MR25H40CDF只支持Mode 0和Mode 3,CubeMX里要手动改成1Edge。这个细节在数据手册里写得很清楚,但容易忽略。

第三个坑是写入后的状态轮询。MRAM写入虽然快,但并不是瞬间完成。如果连续写入多个数据块而不检查状态,偶尔会出现后一个写入覆盖前一个的情况。加上MRAM_WaitReady之后就没再出现过。

第四个坑是电源上电顺序。如果MRAM的VCC比STM32的VCC先上电,SPI引脚上会有电流倒灌,长期可能损坏芯片。我的做法是在SPI信号线上串100Ω电阻,或者用带使能端的LDO确保上电顺序。

6. 工业场景下的扩展应用思路

6.1 参数存储与配置管理

工业设备通常有大量的配置参数,比如PID系数、量程范围、报警阈值、通信地址等。这些参数的特点是写入不频繁但读取频繁,而且掉电不能丢。MRAM的读取速度和RAM相当,可以频繁读取而不影响寿命,非常适合这种场景。

我的做法是把所有参数打包成一个结构体,加上CRC和版本号,存在MRAM的固定地址。上电时读取并校验,如果校验失败就用默认值。参数修改时先写备份区,再写主区,确保任何时候至少有一份有效数据。

6.2 运行日志与故障录波

MRAM的快速写入特性让它很适合做故障录波。当系统检测到异常时,可以在几百微秒内把关键变量写入MRAM,记录故障前后的运行状态。这个速度是EEPROM和Flash做不到的。

我在一个伺服驱动器项目里用MRAM记录故障时的电流、位置、速度波形,采样率10kHz,每次录波1秒,总共10000个采样点,每个点4字节,一共40KB。MRAM写入40KB只需要大约10ms,完全可以在故障发生后、系统停机前完成。

6.3 与RTOS的配合使用

在跑FreeRTOS或者RT-Thread的系统里,MRAM的读写要注意任务优先级和互斥。如果多个任务同时访问SPI总线,必须加互斥锁,否则命令序列会交错,导致数据错乱。

我的做法是创建一个MRAM任务,所有读写请求通过消息队列发送给它,由它统一调度SPI总线。这样既保证了互斥,又可以利用DMA和任务通知实现异步读写,不阻塞其他任务。

// MRAM任务示例 void MRAM_Task(void *argument) { MRAM_Request_t req; for (;;) { if (xQueueReceive(mram_queue, &req, portMAX_DELAY) == pdTRUE) { if (req.op == MRAM_OP_READ) { MRAM_Read(req.addr, req.buf, req.len); } else { MRAM_Write(req.addr, req.buf, req.len); } xSemaphoreGive(req.done_sem); } } }

这套架构在一个多任务的数据采集系统里跑了两年多,没有出现过一次SPI冲突导致的数据异常。

6.4 温度补偿与长期稳定性

MRAM虽然温度特性比Flash好,但在极端温度下仍然需要关注。MR25H40CDF的工业级温度范围是-40°C到+85°C,在这个范围内数据保持和读写操作都是保证的。但如果你的设备工作在更宽的温度范围,比如户外基站设备可能到-40°C到+75°C,那就要做温度补偿。

我的做法是在MRAM旁边放一个数字温度传感器,比如TMP102,定期读取温度。如果温度超出MRAM的保证范围,就降低SPI时钟频率,增加写入后的校验次数。实测在-40°C下,40MHz时钟会出现偶发误码,降到20MHz就完全稳定。

长期稳定性方面,MRAM的10^14次写入寿命意味着即使每秒写一次,也能用三百万年。实际工业场景里写入频率远低于这个,所以寿命完全不是问题。真正需要关注的是数据保持能力,虽然标称20年,但在高温下会加速衰减。如果设备要求20年以上的数据保持,建议定期做数据刷新,比如每年把数据读出来重新写一遍。

7. 从选型到量产的完整检查清单

7.1 硬件设计检查项

在PCB投板之前,对照这个清单过一遍:

  • MRAM的VCC去耦电容是否在5mm以内
  • SPI信号线是否等长,SCK是否远离模拟信号
  • CS线是否远离SCK,避免耦合
  • HOLD和WP引脚是否上拉到VCC
  • 电源入口是否有磁珠和滤波电容
  • 如果长线连接,信号线上是否串了匹配电阻
  • 上电顺序是否确保主控先于MRAM上电

7.2 驱动调试检查项

驱动调通之前,逐项确认:

  • SPI模式是否为Mode 0或Mode 3
  • CS的建立和保持时间是否满足数据手册要求
  • WREN命令是否在每次写入前发送
  • 写入后是否等待状态寄存器WEL位清零
  • 读取数据是否做了CRC校验
  • DMA传输完成回调里是否正确拉高CS
  • 多任务环境下是否有互斥保护

7.3 量产测试检查项

小批量试产时,这些测试必须做:

  • 全温度范围读写测试(-40°C、25°C、85°C)
  • 反复掉电测试,验证数据不丢失
  • 长时间连续写入测试,验证无写入失败
  • SPI总线眼图测试,确认信号完整性
  • 电源纹波测试,确认在允许范围内
  • ESD测试,确认MRAM的ESD等级满足要求

这套检查清单是我从多个量产项目里总结出来的,每次新项目都照着过一遍,基本能避免90%以上的常见问题。MR25H40CDF加STM32F732IE这个组合,硬件设计不复杂,驱动也不难写,关键是把细节做到位。工业应用最怕的就是偶发故障,而偶发故障往往来自那些看起来不起眼的细节。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/5 7:59:28

插件机制与激活失败排查:从web boot到entry did not activate

大概两年前,我接手过一套插件化设计的前端应用,几乎每隔一两周就会有人截图贴一句“failed to load plugins web boot: 2 entries did not activate linxin666/dsh-p”过来问怎么回事。那时候我就发现,很多人对“插件”这个词的理解其实停留在…

作者头像 李华
网站建设 2026/10/5 7:59:02

插件机制与 failed to load plugins 排查实践

“plugins 到底能做什么?”这是几乎所有刚接触插件机制的人都会问的第一句话。我在嵌入式、前端和日常工具软件三个方向都折腾过插件系统,从 IAR 编译器里的扩展插件,到前端框架里动不动就报 failed to load plugins web boot 的加载器&…

作者头像 李华
网站建设 2026/10/5 7:58:44

基于SSD+VGG16的驾驶员疲劳检测系统实战指南

简介:本资源是一套基于Python与卷积神经网络实现的驾驶员疲劳检测与预警系统完整毕设项目,面向计算机、人工智能及相关专业本科生,适用于毕业设计、课程大作业及项目实战训练。系统融合人脸关键点识别、闭眼/打哈欠行为判别与实时预警功能&am…

作者头像 李华
网站建设 2026/10/5 7:58:34

FaceNet人脸嵌入考勤系统:从128维向量到L2距离决策

简介:本资源是一套面向计算机专业本科生的深度学习实战项目,聚焦人脸识别考勤系统开发,适用于毕业设计、课程设计及期末大作业等场景。项目基于FaceNet深度学习算法实现人脸特征提取与比对,完整覆盖人脸录入、实时识别、考勤统计、…

作者头像 李华
网站建设 2026/10/5 7:58:18

OpenShell免费开源工具:Windows 10/11找回经典开始菜单与个性化配置指南

这篇文章带你聊聊多数人老系统美化时第一个想到的免费开源工具——OpenShell。它在国内软件站有时候叫“Open-Shell”,沿袭自当年那个无人不知道的Classic Shell,作者换了一批人接手后继续维护,功能也从单纯还原经典开始菜单,逐步…

作者头像 李华