news 2026/10/6 9:04:13

STM32 CAN通讯过滤器寄存器详解:从配置到实战验证

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
STM32 CAN通讯过滤器寄存器详解:从配置到实战验证

做STM32的CAN通讯,十有八九会在过滤器这儿卡一下。我第一次调F103的bxCAN时,收发器、波特率、中断都配好了,发端数据照发,收端就是进不了接收中断。把过滤器关掉之后又反过来,总线上所有节点的帧全都涌进FIFO,程序大部分时间都在处理垃圾ID。后来把CAN_FxR1、CAN_FxR2、FMR、FS1R、FFA1R、FA1R这几个寄存器逐个手动配置,又用两块板子在真实总线上连着验证了一晚上,才算把STM32 CAN通讯过滤器寄存器的行为彻底摸清。这篇就从寄存器角度,把这些配置逻辑和验证结果完整记录下来,给正在调过滤器的朋友一份可以直接照着做的参考。

1. 为什么明明一行配置能解决的问题,我还是要翻开寄存器手册

很多教程里过滤器就一句话:调HAL_CAN_ConfigFilter,参数抄一下,完事。但实际项目里,一旦出现“过滤器配了好像没配”“只收到一部分帧”“CAN1和CAN2互相抢过滤器”这类问题,不搞懂寄存器就只能反复试错。

1.1 过滤器在CAN接收链路里的位置

bxCAN外设收到一帧报文后,不是直接扔进FIFO等CPU读,而是先过一次硬件过滤器。过滤器的作用就是根据标识符(标准帧的11位ID或扩展帧的29位ID)决定这帧要不要进FIFO、进FIFO0还是FIFO1。

链路大致是这样:总线电平 -> CAN内核解析 -> 验收过滤器 -> FIFO0/FIFO1 -> 中断或查询读取。过滤器位于内核和FIFO之间,匹配不上的帧在硬件层面就被丢掉了,CPU根本感知不到。这也正是过滤器的价值:总线上几百个ID,你只关心其中两三个,如果全收进来再靠软件判断,中断频率会高得离谱,还可能因为FIFO溢出丢掉真正关心的帧。

1.2 只有寄存器能解释清楚的几个场景

我用HAL或者标准库配过滤器,遇到过三类问题,每一类最后都得回到寄存器才能定位:

  • 过滤器配置成功,但某个bank就是不生效。回读FA1R才发现激活位没写进去,或者FMR的FINIT位没有按正确时序操作。
  • CAN1和CAN2同时用,结果CAN2一个过滤器都收不到。因为CAN2使用的bank范围和CAN1重叠,没有正确设置FMR里的CAN2SB。
  • Bootloader和App都要重建过滤规则。直接调HAL封装函数当然能初始化,但切换过程中寄存器处于什么状态、哪些bank被占,必须看得懂FMR/FA1R。

这类问题只看库函数的参数列表,永远找不到答案。寄存器才是底层唯一的事实标准。

1.3 本文的实验方法

后面的内容都基于STM32F103C8T6的bxCAN外设,两块板子通过TJA1050收发器实际对接,普通模式,500kbps。F105、F407这些带bxCAN的芯片逻辑一致;使用FDCAN的G系列/H7系列不要直接照抄,FDCAN的过滤器组织形式完全不同。

2. 从两组32位寄存器到六个控制寄存器:过滤器组的完整地图

STM32F103的bxCAN里有28个过滤器组,编号0到27。每个过滤器组内部有两个32位寄存器:CAN_FxR1和CAN_FxR2(x是组号),在标准外设库里对应CANx->sFilterRegister[bank].FR1和.FR2。这两个寄存器装的就是ID或掩码,而它工作在什么模式、什么位宽、挂哪个FIFO、有没有激活,由另外五个控制寄存器决定。

2.1 一个过滤器组内部:CAN_FxR1和CAN_FxR2的分工

一个过滤器组本质上就是一个8字节的存储单元,可以按两种位宽来用:

  • 32位位宽:两个寄存器合起来表示完整的一条过滤规则。列表模式时,FR1和FR2各存一个ID,两个槽位;掩码模式时,FR1存期望ID,FR2存掩码。
  • 16位位宽:FR1的高16位、低16位,FR2的高16位、低16位,一共四个16位槽位。列表模式可以放四条ID,掩码模式可以放两组“ID+掩码”。

一句话概括资源:一个组能装几条规则,取决于你是列表模式还是掩码模式,以及有没有把寄存器拆成16位槽。

2.2 五个控制寄存器各自管什么

28个过滤器组在内存里连续排列,每组固定占用8字节。第一组的FR1偏移是0x40,FR2是0x44,下一组直接加8,所以第n组的FR1偏移就是0x40 + n*8,FR2再加4。调试器里手工改寄存器时,这个偏移关系很有用。

过滤器组怎么工作,由下面这几个寄存器控制:

寄存器英文全称每个bit对应一个过滤器组,置1/置0的含义
FMRFilter Master Registerbit0=FINIT写保护;bits13:8=CAN2SB,CAN2起始组号
FM1RFilter Mode Register1=列表模式,0=掩码模式
FS1RFilter Scale Register1=32位位宽,0=16位位宽
FFA1RFilter FIFO Assignment Register1=关联FIFO1,0=关联FIFO0
FA1RFilter Activation Register1=激活该过滤器组,0=禁用

也就是说,你配置第7组过滤器时,就是操作第7个bit。比如想让第7组工作在32位列表模式并挂FIFO1,就要把FS1R的bit7置1、FM1R的bit7置1、FFA1R的bit7置1,最后FA1R的bit7也置1让它生效。

2.3 FINIT的写保护:改配置前必须跨过的门槛

FMR寄存器里最容易被忽略的就是bit0,FINIT。这款CAN控制器对过滤器相关的寄存器有一道写保护:只有FINIT等于1时,FM1R、FS1R、FFA1R、FA1R、CAN_FxR1、CAN_FxR2这些寄存器才允许被修改。FINIT等于0时,你往这些寄存器写值,写进去的等于空气。

标准库初始化函数内部会自动处理这个时序,但如果你在调试器里直接改寄存器做实验,或者自己写寄存器操作函数,就必须手动控制。正常顺序只有两步:先把FMR的FINIT置1,再配置其他寄存器;全部写完,最后把FINIT清0,配置才会在硬件层面真正生效。后面实验配置函数里我会把这个时序写进去。

2.4 CAN1和CAN2之间怎么分bank

F103的bxCAN比较特殊:28个过滤器组是CAN1和CAN2共用的,CAN1可以全用,CAN2只能用其中一部分。划分依据就是FMR寄存器里的CAN2SB字段,它指定CAN2从第几个组开始用。

比如设置CAN2SB=14,那0到13号组归CAN1,14到27号组归CAN2。CAN2SB复位默认值是0,这在只用一个CAN时没问题,但如果你初始化完CAN2发现过滤器完全不工作,第一反应就应该是检查CAN2SB有没有设置,而不是去调掩码。

3. 掩码模式、列表模式、标准帧、扩展帧:把过滤逻辑拆成位运算

过滤器寄存器里存的东西不神秘,本质就是位比较。只是这个位比较有几个参数容易绕晕,拆开看就清晰了。

3.1 列表模式:拿ID去和两个槽位挨个比

列表模式下的规则可以理解成一张名单。报文进来后,硬件把报文的标识符拿过来,和FR1、FR2里存的值逐一比较,只要完全等于其中一个,就放行。

这里说的“完全等于”是寄存器值的完全相等。标准帧ID是11位,扩展帧ID是29位,它们在寄存器里的摆放方式不同,所以配置时要把ID先转换成对应的寄存器值。FR1和FR2在列表模式下都是ID槽位,掩码概念不存在。比如我想只收两个标准帧ID:0x123和0x456,那FR1写标准帧0x123的寄存器值,FR2写标准帧0x456的寄存器值。

3.2 掩码模式:用1表示必须匹配

掩码模式把FR2从“第二个ID槽位”改成了“掩码”。掩码的每一位含义是:1代表这一位必须与FR1里对应位相同,0代表这一位无所谓。

举个例子。FR1的ID填0x100,FR2的掩码填0x7F8。0x7F8换算成二进制是bit10到bit3全为1,bit2到bit0全为0。这意味着报文ID的bit10到bit3必须等于0x100的这些位,而低3位不参与比较。所以0x100、0x103、0x107都能进,0x0FF进不来,因为它的高8位不对。这就是掩码模式做批量接收的基本思路。

掩码计算是过滤器配置里最容易出错的地方,我自己的习惯是先在纸上把二进制写出来,别直接在十进制里心算。比如“收0x100到0x107”这类需求,0x100到0x107的区别只在bit2到bit0,掩码就是0x7F8。

3.3 标准帧和扩展帧在过滤器寄存器里的不同摆法

这是很多人栽跟头的核心原因。发送邮箱的ID寄存器里,标准帧ID是左移21位放进高位的,而过滤器寄存器里的标准帧ID是在低位对齐,直接放在bit10到bit0,扩展帧ID则是左移3位后存放在寄存器中。也就是说,你发送时用的ID数值不能直接拿来填过滤器,必须做一次换算。

我在实验里用到的换算规则:

  • 标准帧ID:过滤器值 = 标准ID本身,也就是(uint32_t)std_id & 0x7FF。
  • 扩展帧ID:过滤器值 =((uint32_t)ext_id << 3) | 0x04,其中左移3位是为29位ID腾出位置,bit2置1表示这是扩展帧。

对应的掩码:

  • 标准帧常用掩码:0x7FF,表示11位ID全比较。
  • 扩展帧常用掩码:0xFFFFFFFC,表示29位ID和IDE位全部参与比较,最低两位不关心。

3.4 模式选择的决策建议

实际项目里我一般按下面这张表来选:

使用场景推荐配置理由
只收一个确定的ID32位列表模式FR1和FR2各放一个,还能顺带收第二个
收一段连续的ID范围32位掩码模式用一个掩码覆盖整段,不用堆多个过滤器组
同时收标准帧和扩展帧32位掩码模式同组放宽匹配掩码不覆盖帧类型位,靠软件再判断
过滤器组不够用16位位宽把一组拆成四个槽位,但配置时要格外小心
CAN1和CAN2都有接收需求先分bank,再各配各的规则避免两组CAN互相覆盖

4. 实验验证:四种典型过滤配置在真实总线上跑一遍

原理归原理,最终还是要看实际效果。这一章的四个实验我都在两块STM32F103C8T6对接的真实总线上跑过,配置函数和结果都记录下来。

4.1 实验平台与基础初始化

两块板子都带TJA1050电平转换芯片,接线很简单:CANH接CANH,CANL接CANL,GND共地。短距离测试,两边都保留120Ω终端电阻也不会出问题。波特率统一配500kbps。

先初始化GPIO和CAN控制器。下面这段是标准外设库的写法,重点是APB1时钟36MHz下,预分频4、BS1=9tq、BS2=8tq,1+9+8=18tq,算出500kbps:

void CAN1_Init(void) { GPIO_InitTypeDef GPIO_InitStructure; CAN_InitTypeDef CAN_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_AFIO, ENABLE); RCC_APB1PeriphClockCmd(RCC_APB1Periph_CAN1, ENABLE); GPIO_PinRemapConfig(GPIO_Remap1_CAN1, DISABLE); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; GPIO_Init(GPIOA, &GPIO_InitStructure); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOA, &GPIO_InitStructure); CAN_InitStructure.CAN_TTCM = DISABLE; CAN_InitStructure.CAN_ABOM = ENABLE; CAN_InitStructure.CAN_AWUM = DISABLE; CAN_InitStructure.CAN_NART = DISABLE; CAN_InitStructure.CAN_RFLM = DISABLE; CAN_InitStructure.CAN_TXFP = DISABLE; CAN_InitStructure.CAN_Mode = CAN_Mode_Normal; CAN_InitStructure.CAN_SJW = CAN_SJW_1tq; CAN_InitStructure.CAN_BS1 = CAN_BS1_9tq; CAN_InitStructure.CAN_BS2 = CAN_BS2_8tq; CAN_InitStructure.CAN_Prescaler = 4; CAN_Init(CAN1, &CAN_InitStructure); }

注意CAN控制器初始化完成后要先做一次自测:发送一条报文,再确认能收到,确认收发通路没问题,再开始配置过滤器。

4.2 过滤配置函数:直接操作寄存器版本

我封了一个直接操作寄存器的函数,后面实验全部调用它。mode传0表示掩码模式,传1表示列表模式;scale传0表示16位,传1表示32位;fifo传0或1表示挂FIFO0还是FIFO1:

void CAN_FilterRegConfig(CAN_TypeDef *CANx, uint32_t bank, uint32_t mode, uint32_t scale, uint32_t fifo, uint32_t fr1, uint32_t fr2) { CANx->FMR |= CAN_FMR_FINIT; // 进入初始化模式,解除写保护 if (mode == 1) CANx->FM1R |= (1UL << bank); else CANx->FM1R &= ~(1UL << bank); if (scale == 1) CANx->FS1R |= (1UL << bank); else CANx->FS1R &= ~(1UL << bank); if (fifo == 1) CANx->FFA1R |= (1UL << bank); else CANx->FFA1R &= ~(1UL << bank); CANx->sFilterRegister[bank].FR1 = fr1; CANx->sFilterRegister[bank].FR2 = fr2; CANx->FA1R |= (1UL << bank); // 激活该过滤器组 CANx->FMR &= ~CAN_FMR_FINIT; // 退出初始化模式,配置生效 }

这个函数必须先进入FINIT初始化模式再改寄存器,最后激活并退出。如果觉得某次配置没生效,第一步就回读这几个寄存器看看,而不是去怀疑总线。

发消息的辅助函数:

void SendStd(uint16_t id) { CanTxMsg TxMsg; TxMsg.StdId = id; TxMsg.ExtId = 0; TxMsg.IDE = CAN_Id_Standard; TxMsg.RTR = CAN_RTR_Data; TxMsg.DLC = 1; TxMsg.Data[0] = 0xAA; while (CAN_Transmit(CAN1, &TxMsg) == CAN_TxStatus_Failed); } void SendExt(uint32_t id) { CanTxMsg TxMsg; TxMsg.StdId = 0; TxMsg.ExtId = id; TxMsg.IDE = CAN_Id_Extended; TxMsg.RTR = CAN_RTR_Data; TxMsg.DLC = 1; TxMsg.Data[0] = 0xBB; while (CAN_Transmit(CAN1, &TxMsg) == CAN_TxStatus_Failed); }

接收端用查询方式读FIFO,打印收到的ID和来源FIFO:

CanRxMsg RxMsg; if (CAN_MessagePending(CAN1, CAN_FIFO0) > 0) { CAN_Receive(CAN1, CAN_FIFO0, &RxMsg); if (RxMsg.IDE == CAN_Id_Standard) printf("[FIFO0] Std 0x%03X\r\n", RxMsg.StdId); else printf("[FIFO0] Ext 0x%08X\r\n", RxMsg.ExtId); } if (CAN_MessagePending(CAN1, CAN_FIFO1) > 0) { CAN_Receive(CAN1, CAN_FIFO1, &RxMsg); if (RxMsg.IDE == CAN_Id_Standard) printf("[FIFO1] Std 0x%03X\r\n", RxMsg.StdId); else printf("[FIFO1] Ext 0x%08X\r\n", RxMsg.ExtId); }

4.3 实验一:列表模式只收标准帧0x123

配置第0组过滤器,32位列表模式,挂FIFO0:

CAN_FilterRegConfig(CAN1, 0, 1, 1, 0, 0x0123, 0x0123);

发送端依次发送标准帧ID:0x123、0x124、0x100、0x456。每帧间隔100ms。接收端串口实际输出只有0x123,其余三帧全部被硬件丢弃。这说明列表模式下FR1和FR2就是两个精确匹配槽位。

实验记录:

发送ID期望结果实际结果
0x123收到收到
0x124丢弃丢弃
0x100丢弃丢弃
0x456丢弃丢弃

4.4 实验二:掩码模式收下0x100到0x107一组ID

同样用第0组,切换成32位掩码模式。FR1填0x100,FR2填0x7F8。0x7F8的bit10到bit3是1,低3位是0,表示ID高8位必须等于0x100的高8位,低3位任意:

CAN_FilterRegConfig(CAN1, 0, 0, 1, 0, 0x0100, 0x07F8);

发送端依次发送:0x0FF、0x100、0x103、0x107、0x108、0x7FF。接收端只收到0x100、0x103、0x107。0x108虽然只比0x107大1,但bit3已经变了,被掩码挡住,这正是掩码按位比较的特点。

发送ID期望结果实际结果
0x0FF丢弃丢弃
0x100收到收到
0x103收到收到
0x107收到收到
0x108丢弃丢弃
0x7FF丢弃丢弃

4.5 实验三:列表模式接收扩展帧ID 0x1234567

扩展帧29位ID左移3位再参与过滤。先算好寄存器值,0x1234567左移3位等于0x91A2B38,再加上bit2的IDE标志位,得到0x91A2B3C:

uint32_t ext_mask_val = (0x1234567UL << 3) | 0x04UL; CAN_FilterRegConfig(CAN1, 0, 1, 1, 0, ext_mask_val, ext_mask_val);

发送端发送扩展帧:0x1234567、0x1234568、0x2234567。接收端只收到0x1234567。注意发送时接收打印要用RxMsg.ExtId,不是StdId。

发送扩展ID期望结果实际结果
0x1234567收到收到
0x1234568丢弃丢弃
0x2234567丢弃丢弃

4.6 实验四:两个过滤器组分别绑定FIFO0和FIFO1

第0组列表模式收标准帧0x222,挂FIFO1;第1组掩码模式收0x300到0x307,挂FIFO0:

CAN_FilterRegConfig(CAN1, 0, 1, 1, 1, 0x0222, 0x0222); CAN_FilterRegConfig(CAN1, 1, 0, 1, 0, 0x0300, 0x07F8);

发送端交替发送0x222、0x300、0x304。接收端串口输出分别带上了[FIFO1]和[FIFO0]前缀,说明FFA1R的FIFO分配位工作正常。这个实验验证的不只是过滤,还有多优先级场景下的FIFO分流能力,比如高优先级报文进FIFO1、普通报文进FIFO0,后续读邮箱时可以直接按FIFO处理。

5. 实测中踩过的坑和排查思路

配置过滤器本身不复杂,真正复杂的是出错以后怎么定位。下面几个问题全是我在实验过程中真实碰到过的,按坑的典型度排个序。

5.1 坑1:没置FINIT就把配置项当摆设

有一次我在调试器里直接改FM1R,想看看列表模式效果,改了bit0为1,然后读回FM1R,发现还是0。一开始以为是硬件坏了,后来才意识到FMR的FINIT还是0,整个过滤器配置区是写保护的。

这个坑在调试器里特别容易踩,因为你写寄存器时不会报错,寄存器也不变,看起来就像没写一样。正确操作是先写CANx->FMR |= CAN_FMR_FINIT;,配置完再清掉。而且要注意,配置过程和退出必须在同一个临界区内完成,中间不要被中断打断去收发CAN帧,否则可能出现配置写到一半就开始过滤的竞态。

5.2 坑2:配置都对了,就是忘了FA1R激活

第二个高频坑是FS1R、FM1R、FFA1R、FR1、FR2全填好了,FIFO里就是没数据。排查到最后发现FA1R对应位还是0。

FA1R的每个位是过滤器的总开关。这个寄存器太不起眼,很容易在配置列表里漏掉。我的建议是把“激活”这一步写进配置函数里,而不是在主程序里调用完再单独激活。上面封装的CAN_FilterRegConfig函数最后一行就是激活,这样就不会漏。

5.3 坑3:32位掩码模式下把第二个寄存器当成ID填

列表模式用习惯了以后,切到掩码模式会下意识在FR2里也填一个期望ID。但掩码模式下FR2是掩码,不是ID槽位。如果你期望收两个特定ID,然后只改模式不改FR2,FR2就会变成另一个ID,结果两个ID都收不到,或者只收到一个,非常迷惑。

掩码模式下的FR2填的是位比较规则,1表示这几位必须匹配,0表示忽略。填值之前先把二进制在纸上写出来,别用十进制估算。

5.4 坑4:CAN2过滤器组被CAN1占光

只用CAN1的时候,28个组随便用,根本不会想到bank分配问题。一旦把CAN2也初始化了,CAN2的过滤器却怎么配都不生效,十有八九是FMR里CAN2SB没设。

复位后CAN2SB默认是0,意味着CAN2默认从第0组开始用,这和CAN1完全重叠。正常做法是在初始化CAN2过滤器之前,把CAN2SB设为14,让CAN2用第14到第27组。代码里就是修改FMR寄存器的高位:

CANx->FMR = (CANx->FMR & ~(0x3FUL << 8)) | (14UL << 8);

CAN2只占14个组,CAN1用0到13,分配完双方互不干扰。

5.5 快速判断过滤器是否生效的三步法

最后给一个我自己一直在用的排查套路。不管过滤器配置成什么样,先别急着怀疑总线、波特率、中断,按下面三步走:

第一步,把过滤器临时配成全接收。掩码模式,FR1填0,FR2填0,掩码全0意味着所有位都不比较,任何帧都能进。这时如果还收不到,问题就不在过滤器,而是总线或者CAN初始化。

第二步,改成目标过滤规则,发送一个期望ID,看FIFO有没有数据。没有的话回读寄存器:

printf("FM1R=%08X FS1R=%08X FFA1R=%08X FA1R=%08X\r\n", CAN1->FM1R, CAN1->FS1R, CAN1->FFA1R, CAN1->FA1R); printf("FR1=%08X FR2=%08X\r\n", CAN1->sFilterRegister[0].FR1, CAN1->sFilterRegister[0].FR2);

对比回读值和预期,通常能立刻发现问题,要么FINIT没退出,要么FA1R没置位,要么FR2算错。

第三步,发送一个不期望的邻近ID,确认FIFO里的计数不增加。这一步很多人会忽略,但“该收的收进来”和“不该收的挡在外面”是两件事,只验证一半不算完。

我自己现在配过滤器,已经习惯先把估算好的寄存器值写在注释里,再写配置代码。因为掩码这种东西,隔一个晚上再看就会觉得当时算错了。把计算过程留档,既能自检,也方便同事接手。

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

Qt 5.8.0三架构交叉编译实战:x64/x86/armhf与Cubietruck部署

开篇先交代一下背景。去年手上拿到一块Cubietruck&#xff08;就是那款全志A20双核的开发板&#xff09;&#xff0c;想在上面跑一套带界面的Qt应用&#xff0c;做一个小型的数据展示终端。A20是Cortex-A7核心&#xff0c;跑Debian系的armhf系统问题不大&#xff0c;但真正折腾…

作者头像 李华
网站建设 2026/10/6 9:04:11

PHP微信支付v3完整实例:从下单到回调全链路实战

简介&#xff1a;这份资源是面向PHP开发者的微信支付V3完整实例&#xff0c;适合需要为线上商城或线下场景接入微信支付、希望掌握V3新接口安全机制的初中级开发者。压缩包共16个文件&#xff0c;约61KB&#xff0c;以asp与php脚本为主&#xff0c;辅以txt说明、pem证书、mdb数…

作者头像 李华
网站建设 2026/10/6 9:03:21

SQL每日一题:从去重到慢查询优化的实战复盘指南

好的&#xff0c;遵照您的要求&#xff0c;我将仅依据提供的项目标题“sql每日一题”及相关关键词&#xff0c;撰写一篇符合所有规范的、直接可发布的Markdown格式博文。内容将完全围绕SQL学习与实操展开&#xff0c;不含任何违禁及敏感信息。 1. 为什么我坚持做“SQL每日一题…

作者头像 李华
网站建设 2026/10/6 9:02:58

video-scroll:滚动即播停滚即停的轻量实现与避坑指南

简介&#xff1a;这份资源是一套用于滚动开始与停止视频播放的轻量级 JavaScript 实现&#xff0c;面向需要为网页添加滚动触发视频控制逻辑的前端开发者&#xff0c;尤其适合刚接触 jQuery 与 DOM 事件、希望快速上手交互效果的入门与中级学习者。压缩包共 3 个文件&#xff0…

作者头像 李华
网站建设 2026/10/6 9:02:29

Anolis 8 静默安装 Oracle 11g 实战:依赖、内核参数与避坑指南

简介&#xff1a;这份资源面向需要在龙蜥Anolis操作系统上部署Oracle 11g数据库的运维与DBA人员&#xff0c;提供了一套可直接落地的安装与恢复方案。Anolis OS作为阿里云维护的企业级Linux发行版&#xff0c;是运行Oracle数据库的稳定基础环境&#xff0c;而该包通过自动化脚本…

作者头像 李华
网站建设 2026/10/6 9:01:54

YashanDB性能评估指南:6大核心指标与压测方法

YashanDB最近在国产基础软件圈子里存在感不低&#xff0c;厂商宣发材料里常出现“性能比肩国际主流数据库”这种话。但数据库选型这件事&#xff0c;光看PPT和跑分广告没有用&#xff0c;任何库到了手上都要先搭压测环境、把核心性能指标跑一遍&#xff0c;再决定能不能上生产。…

作者头像 李华