news 2026/10/3 15:35:08

GD32 CAN总线开发实战:从硬件连接到源码配置避坑指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
GD32 CAN总线开发实战:从硬件连接到源码配置避坑指南

聊到GD32,很多从51或者STM32转过来的朋友,第一反应都是“这不就是个国产替代嘛”。确实,GD32在很多引脚和底层寄存器上跟STM32有千丝万缕的关系,但只要真上手做过项目,你会发现区别远不止“换个Logo”这么简单,尤其是CAN总线这种对时序和稳定性要求很高的外设。

我最早接触GD32的CAN总线,是在一个车载仪表盘配套项目里。当时手里的样片是GD32F103系列,主控负责跟车身控制器通信,跑的是500kbps的CAN总线。说实话,第一次调的时候还是踩了不少坑——不是移植了STM32的库就万事大吉,时钟树不一样、复用映射不一样、库函数细节也有差异。这篇文章我把从环境搭建到源码配置的完整思路写出来,结合我自己实测过的工程配置,希望能帮你少走点弯路。

这篇文章适合谁?手里有GD32开发板、想跑通CAN通信但还没头绪的人;准备评估GD32的性价比、想看看CAN外设是否好上手的工程师;以及项目进度紧、需要快速从零把CAN调通的单片机开发者。我会按从硬件到软件、从原理到代码的顺序讲,尽量让不同基础的读者都能拿着直接用。

1. 内容整体设计与思路拆解

先定一个总体规划。玩转CAN总线,不正确理解为你只是调试一个“串口高级版”。CAN总线的难点在于:它是一套完整的、多层协议栈支撑的通信方案,涉及硬件收发器、控制器寄存器配置、位时序计算、报文过滤、错误处理等等。如果你的思路只是“发数据、收数据”,那后面遇到问题会非常难查。

1.1 项目功能定位与系统组成

一个最小的GD32 CAN通信系统,包括这样几部分:

  • 一个带CAN控制器的GD32芯片,比如GD32F103、GD32F303、GD32F450
  • 一个CAN收发器芯片,典型的是TJA1050、MCP2551或者SN65HVD230
  • 一根双绞线,两端匹配120欧终端电阻
  • 一个上位机或另一个CAN节点,用来验证收发

单片机的CAN控制器负责协议处理、滤波、错误检测,但它的电气信号是TTL级别的CAN_TX和CAN_RX,不是差分信号,所以必须外接CAN收发器,把TTL电平转成CAN_H和CAN_L的差分信号。很多新手在这里栽跟头——拿示波器去点CAN_TX引脚,看到了方波就以为信号是对的,其实总线上的波形完全不一样。

1.2 技术选型解析:为什么选GD32而不直接用STM32

既然很多代码都是兼容的,为什么要专门用GD32做项目?我个人的看法是:成本、供货和本地化支持这三个因素排在最前面。

GD32F103系列在功能上与同封装的STM32F103高度相似,但主频更高,比如GD32F103的最高主频能到108MHz(STM32F103是72MHz),Flash和SRAM的配比也有差异。更重要的是,GD32的生态已经比较完善了,官方提供了GD32F10x固件库、GD32 Embedded Builder集成环境,还有适配Keil、IAR的Pack包,基本能无缝上手。

当然,这里要强调一句:不能把GD32和STM32当成孪生兄弟看,在所有开发中都直接套用。我先提三个自己踩过的坑,后面再详细讲:

  • 时钟树不同。GD32的PLL倍频配置跟ST的库函数不通用,系统主频不同会导致CAN位时序的预分频设置完全不同。
  • GPIO复用映射有差异。CAN的某些引脚在这个型号上映射到了别的复用功能上,你如果不查数据手册,盲目按STM32的引脚配置,可能发不出去数据。
  • 库函数API细节有变化。比如GD32的can_parameter_struct、can_trasmit_message_struct这些结构体字段,和ST的HAL库有明显差别,直接替换不了。

所以这篇文章里的所有源码,我都会用GD32官方的标准外设库来写,不是拿ST改改名就贴上来,这点请你放心。

2. 硬件准备与协议原理先行:CAN总线的基础认知

软件写得再好,硬件接错了,信号也出不去。反过来,如果对CAN协议没有基本认知,你也看不懂发送和接收状态寄存器到底在报什么错。这两个部分必须结合起来学。

2.1 硬件连接的常见做法

给一个非常标准的节点连接方式,以GD32F103 + TJA1050为例:

GD32_PB8 (CAN0_RX) -> TJA1050 RXD GD32_PB9 (CAN0_TX) -> TJA1050 TXD TJA1050 CANH -> 总线CANH线 TJA1050 CANL -> 总线CANL线 TJA1050 VCC -> 5V(注意有的板子是3.3V版本) TJA1050 GND -> 公共地

两个终端电阻,每个节点不一定都要放,但在总线两端必须各有一个120欧电阻。很多实验板会把终端电阻直接做在板子上,如果你用两个带120欧电阻的板子相互通信,就等于并了60欧,会导致信号反射,通信质量反而变差。这时候你就得飞线把其中一个电阻断开。

收发器选型上,我做了一个简单对照:

型号供电电压速率特点
TJA10505V最高1Mbps最经典,应用广泛,但要注意5V与MCU电平匹配
MCP25515V最高1MbpsMicrochip家的老牌产品,耐压高
SN65HVD2303.3V最高1Mbps适合3.3V单片机,省去电平转换
TJA10423.3V/5V最高1Mbps低功耗待机改进版

GD32的IO耐压情况跟具体型号有关,如果你用5V的TJA1050,建议确认GPIO配置为开漏输出并接上拉,或者干脆选3.3V的收发器,更省心。我自己做实验最常用SN65HVD230,一根杜邦线连GD32开发板,不用操心电平问题。

2.2 CAN协议中的关键概念

有人说CAN协议复杂,我倒觉得它的核心逻辑特别像公司开会发言规则:

  • 所有人都能说话,但发言前先“听”总线上有没有人在说
  • 如果同时有人抢麦,优先级最高的ID获胜,其他人自动闭嘴等下一轮
  • 消息发出去之后,说的人要同时听,如果听到的内容和自己说的对不上,就知道出错了

技术上对应几个概念:

帧类型。日常用得最多的是数据帧,用来发真实数据。远程帧用来请求某个节点发送数据,有点像“点对点催更”。错误帧和过载帧是节点在异常时主动发出的,你可以不会主动构造它们,但要能通过错误状态寄存器判断是什么情况导致了错误帧。

仲裁机制。CAN总线上每个帧都有ID,ID数值越小优先级越高。当两个节点同时发数据,CAN控制器逐位比较电平,显性电平(逻辑0)会覆盖隐性电平(逻辑1),所以ID小的节点最终抢占总线。这也是为什么工程上通常把控制类消息的ID设得很小,比如0x001这种。

报文过滤。接收方不需要处理总线上所有报文,可以通过验收屏蔽寄存器,只接收关心ID的报文。这个功能放到GD32里就是CAN_FIFO的filter配置,可以配置成列表模式或掩码模式。

错误处理。CAN节点有三个状态:主动错误、被动错误、离线。如果一个节点错误累计值过高,它会自己退出总线,不影响其他节点通信。很多朋友调试时发现“只有我这块板子收不到数据,其他节点正常”,最可能就是这个节点进入了Bus-Off状态。

以上概念不必一次全懂,先知道这些名词对应的大概场景,后面结合寄存器配置再回头看,会感觉通透得多。

3. 工程环境搭建:从零跑通第一个CAN实例

这部分我不讲太多理论,直接把从开箱到第一个CAN Demo跑通的流程过一遍。环境不同,细节会有点差别,但通用逻辑一致。

3.1 开发环境与固件库准备

我常用的方案是Keil MDK配合GD32官方固件库。很多人问为什么不用GD32 Embedded Builder,其实那个工具官方做了很久,界面类似STM32CubeMX,生成代码的速度很快,但初学阶段我还是推荐Keil,原因很简单:网上资料多、调试器兼容性好、出问题好查。

需要准备的软件包:

  • Keil MDK 5.3x以上版本,装好对应器件支持包
  • GD32F10x固件库(从GD官网下载,或者直接在Gitee/GitHub上找官方镜像)
  • J-Link或者DAP-Link驱动,建议先用DAP-Link,便宜且供电方便

安装GD32器件Pack这一步,很多人会卡住。我建议不要直接双击Pack文件,而是打开Keil的Pack Installer,在“File -> Import”里导入GD32官方提供的Pack,这样可以自动处理依赖。有些旧版本Pack在导入时提示缺CMSIS版本,你就顺手把CMSIS的Pack也装一下。

3.2 新建工程时的关键配置

我在Keil里新建GD32工程的固定套路:

  1. 在Project窗口里新建两个组:Library(存放固件库源文件和核心文件)、User(存放main.c等应用代码)。
  2. 把固件库里的gd32f10x_can.c、gd32f10x_gpio.c、gd32f10x_rcu.c、gd32f10x_usart.c(调试用)添加进去。
  3. 在C/C++选项卡里添加宏定义,这个很关键,比如我用的是GD32F103系列,那么需要定义GD32F10X_MD或者根据型号选择,具体看固件库手册,不定义这个宏会导致system_gd32f10x.c编译出错。
  4. 选择调试器的Flash下载算法,在“Utilities -> Settings”里加载对应型号的Programming Algorithm,如果算法不对,下载时就会提示No Algorithm found,不解决这个问题后面烧程序都是空谈。

3.3 J-Link烧录GD32的注意事项

用J-Link给GD32烧录,有一个小坑:J-Link默认可能将芯片识别成STM32F103,此时能连上内核,但写到Flash时会出错。解决办法是给J-Link添加GD32的Device描述,或者在Keil的Flash Download里配置GD32的算法文件。

如果你只是想快速测试,也可以用串口烧录。GD32支持串口ISP模式,BOOT0拉高,通过串口把HEX文件送进去。但这种方式适合固化程序,调试阶段还是得用调试器,否则printf是个大麻烦。

4. 核心源码解析:GD32的CAN外设配置与数据收发

下面进入正题。这段代码我直接给了完整的初始化、发送和接收逻辑。其实代码本身不复杂,复杂的是理解这个外设的工作逻辑。我会把每一步涉及到的寄存器构造讲清楚,而不是只让你抄代码。

4.1 初始化GPIO与CAN外设时钟

在GD32里,外设使用前必须先开时钟。很多问题就是漏掉了这一步,导致寄存器写不进值。

void can_gpio_config(void) { rcu_periph_clock_enable(RCU_GPIOB); rcu_periph_clock_enable(RCU_CAN0); gpio_af_set(GPIOB, GPIO_AF_9, GPIO_PIN_8); gpio_af_set(GPIOB, GPIO_AF_9, GPIO_PIN_9); gpio_mode_set(GPIOB, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_8); gpio_mode_set(GPIOB, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_9); }

注意,GPIO_AF_9这个复用号不是所有GD32系列都一样。GD32F10x系列里PB8/PB9复用CAN0,AF号是9;但如果你用的是GD32F30x或者GD32F4xx系列,可能要用PD0/PD1等引脚,且AF号不同。最稳妥的办法是查芯片数据手册里的“Alternate Function Mapping”表格。

另外,GPIO模式要设置成复用推挽,不要配成普通推挽输出。很多人在这里偷懒,直接配成GPIO_MODE_OUTPUT,结果CAN总是进不了正常通信状态,因为CAN控制器没法通过正确的电气特性操作总线。

4.2 CAN控制器初始化与位时序配置

这是整个配置中最重要的部分,直接决定波特率对不对、通信稳不稳。

void can_config(void) { can_parameter_struct can_parameter; can_deinit(CAN0); can_struct_para_init(CAN_INITIAL_STRUCT, &can_parameter); can_parameter.time_triggered = DISABLE; can_parameter.auto_bus_off_recovery = ENABLE; can_parameter.auto_wake_up = DISABLE; can_parameter.auto_retransmit = ENABLE; can_parameter.rec_fifo_overwrite = DISABLE; can_parameter.trans_fifo_order = DISABLE; can_parameter.working_mode = CAN_NORMAL_MODE; /* 位时序设置 */ can_parameter.resync_jump_width = CAN_BT_SJW_1TQ; can_parameter.time_segment_1 = CAN_BT_BS1_13TQ; can_parameter.time_segment_2 = CAN_BT_BS2_2TQ; can_parameter.prescaler = 9; can_init(CAN0, &can_parameter); }

这里如果你用的是500kbps的波特率、APB1经过分频后是72MHz,那总线位时间计算方式为:CAN时钟 = APB1时钟 / prescaler = 72MHz / 9 = 8MHz。一个位时间由同步段(1TQ) + BS1(13TQ) + BS2(2TQ) = 16TQ,所以实际波特率 = 8MHz / 16TQ = 500kHz,采样点 = (同步段 + BS1) / 整个位时间 = (1 + 13) / 16 = 87.5%。

有的朋友图省事,喜欢直接用库函数里的CAN_BAUDRATE参数,比如:

can_parameter.resync_jump_width = CAN_BT_SJW_1TQ; can_parameter.time_segment_1 = CAN_BT_BS1_13TQ; can_parameter.time_segment_2 = CAN_BT_BS2_2TQ; can_parameter.prescaler = 4;

如果你APB1不是72MHz,这个prescaler就不能照抄。最典型的错误就是:主频倍频配置不同,导致APB1是54MHz或者36MHz,但你仍然用prescaler为9的配置,实际波特率就完全不对。所以一定要先搞清楚自己主频是多少,再按公式反推prescaler,别做只会抄代码的“CV工程师”。

4.3 数据发送:检查邮箱、填入内容、请求发送

GD32的CAN外设和STM32一样,有3个发送邮箱。发送前需要找一个空闲邮箱,然后往里塞数据。

uint8_t can_send_data(uint32_t id, uint8_t *data, uint8_t len) { can_trasmit_message_struct transmit_message; transmit_message.tx_sfid = id; transmit_message.tx_efid = 0; transmit_message.tx_ff = CAN_FT_DATA; transmit_message.tx_ft = CAN_FT_STANDARD; transmit_message.tx_dlen = len; for (uint8_t i = 0; i < len; i++) { transmit_message.tx_data[i] = data[i]; } /* 返回邮箱号,0-2表示发送成功,255表示没有空闲邮箱 */ uint8_t mailbox = can_message_transmit(CAN0, &transmit_message); if (mailbox == CAN_MAILBOX0 || mailbox == CAN_MAILBOX1 || mailbox == CAN_MAILBOX2) { return 1; } return 0; }

这里有一个需要特别注意的点:can_message_transmit函数在标准库里返回的是邮箱编号,有的老版本返回状态,如果你用旧库,最好是实际打印一下这个返回值,确保它命中了邮箱号区间。

发送完成不代表总线上的其他节点已经正确接收了。GD32的CAN外设会自己处理仲裁和错误重发,除非发送失败,否则你不太需要关心总线细节。但如果你把auto_retransmit设置为DISABLE,发送失败后就不会自动重发,需要手动处理,这在高实时性冗余设计中是有意义的。

4.4 数据接收:轮询方式与中断方式

接收部分,我建议直接使用中断方式。虽然轮询代码看起来简单,但实际项目里你无法预测报文什么时候来,轮询会占用CPU大量时间。

先看轮询方式的接收核心代码:

void can_poll_receive(void) { can_receive_message_struct receive_message; if (can_receive_message_length_get(CAN0, CAN_FIFO0) != 0) { can_message_receive(CAN0, CAN_FIFO0, &receive_message); printf("Get ID: 0x%x, Len: %d\n", receive_message.rx_sfid, receive_message.rx_dlen); for (uint8_t i = 0; i < receive_message.rx_dlen; i++) { printf("0x%02x ", receive_message.rx_data[i]); } printf("\n"); } }

5. 中断接收还是DMA接收:我的实测结论与建议

很多初学者在CAN接收上会纠结“用中断还是DMA”。我直接用实测经验说结论:如果你不是在做高带宽持续接收,用中断就够了;如果要把CPU解放出来,再考虑DMA模式。

5.1 什么情况下选中断接收

CAN报文一个帧最多8字节数据,即使1ms一帧,每秒也就1000帧,对MCU来说中断压力其实不大。中断服务函数只需要把数据拷贝到应用层缓冲区,然后置一个标志位让主循环去处理。这种模式下功耗和实时性都很好。

void CAN0_RX0_IRQHandler(void) { can_receive_message_struct receive_message; can_message_receive(CAN0, CAN_FIFO0, &receive_message); /* 简单处理:直接把收到的数据写到一个全局结构体里 */ last_rx_id = receive_message.rx_sfid; last_rx_len = receive_message.rx_dlen; for (uint8_t i = 0; i < receive_message.rx_dlen; i++) { last_rx_data[i] = receive_message.rx_data[i]; } rx_flag = 1; }

这里我要提一个很多教程都没讲清楚的细节:CAN0_RX0_IRQHandler里的RX0指的是FIFO0的接收中断,不是第0通道。如果你配置了多个FIFO,那中断函数有CAN0_RX0_IRQHandler和CAN0_RX1_IRQHandler两个,别只开FIFO1却只在RX0中断里收数据,那就永远收不到。

5.2 什么情况下选DMA接收

DMA的核心价值是“搬运数据不占CPU”。但CAN控制器的接收流程是:总线数据进入CAN控制器内部FIFO,再由你通过寄存器读出到内存。DMA做的只是后一半搬运工作。

问题在于,CAN的报文是不定长的,DMA搬运完成后你可能无法预知应该读多少字节。所以在DMA模式下,常见做法是先把固定长度的头部信息读出来,根据数据长度再次触发DMA搬运剩余数据。这大大增加了复杂度。

我的个人建议:低于1Mbps的CAN总线应用,根本不需要DMA。只有当你有多个CAN通道、同时接收大量报文,CPU忙不过来时,才需要认真设计DMA接收流程。否则引入DMA反而会加大调试难度。

6. 常见问题与排查技巧实录:GD32 CAN调试避坑指南

这部分我整理了自己和身边朋友实际踩过的坑,比任何理论都值钱。你按照这个顺序查,基本能解决90%的CAN不通信问题。

6.1 连不上总线,示波器看不到波形

先确认GPIO复用配置正确,再确认CAN收发器供电正常。

我遇到过一次非常诡异的情况:代码在STM32上跑得好好的,移植到GD32上之后,CAN_H和CAN_L之间就是没有差分信号。后来查数据手册发现,该型号的PB9如果完全按STM32F103的配置,配置成了普通推挽输出,而不是复用功能,导致CAN_TX信号根本没连接到收发器。所以你要检查两件事:

  • 有没有调用gpio_af_set配置复用功能?
  • 有没有把GPIO模式设置成GPIO_MODE_AF?

这两个配置缺一个,收发器都收不到来自MCU的CAN信号。

6.2 用USB-CAN分析仪能收到自己发的数据,但另一个MCU节点收不到

这种现象多半是波特率不一致,或者采样点设置差别太大。

用逻辑分析仪抓一下总线上实际波形,测量一个位的实际时间,如果500kbps下实测位时间是2.5us,说明波特率确实不对。重新按APB1时钟计算prescaler即可。

另一个隐蔽的坑是:两个节点用了相同的波特率,但一个采样点设在50%,一个设在87.5%。在总线较长、信号上升沿不够陡时,就可能出现一个节点能正确采样、另一个节点频繁报错。调试时尽量统一采样点在75%到87.5%之间。

6.3 两个节点通信,一个节点报错,提示Bus-Off

Bus-Off的本质是发送错误计数超过255次,节点自动退出总线。

常规排查顺序:

  1. 先看这两块板子的地线是否共地?CAN总线虽然是差分传输,但收发器之间必须有公共参考地,否则共模电压会飘,导致总线干扰。
  2. 再查终端电阻是否匹配?两边总线端各一个120欧,不要多加,也不要漏加。
  3. 然后查波特率配置。把两个节点的CAN_BT寄存器值对比一下,看看是不是一个用8MHz CAN时钟,一个用9MHz CAN时钟。数值一样,但寄存器配置不同,算出来波特率就可能不一样。
  4. 最后检查节点是不是同时用两个ID发送?如果一个节点发送的ID恰好和另一个节点的应答帧冲突,也可能导致仲裁失败。

6.4 错误帧数量很多,通信间歇性失败

这通常和硬件走线、电磁环境有关。CAN总线要用双绞线,而且最好屏蔽层接地。实验阶段用杜邦线虽然能通,但距离长了以后输出波形质量急剧下降。

如果你只是做开发板调试,建议把CAN_H和CAN_L线绕在一起,做成简易双绞线,同时缩短总线长度。个人经验是:只要线尾的120欧电阻焊接牢靠,双绞线不合理也能通,但不稳定就是它带来的。

6.5 烧录配置的杂项问题

很多人会在工程里遇到Cannot access target或者No Cortex-M SW Device Found,这个和CAN本身关系不大,但几乎每个人都会遇到一次。先看调试器是否被正确识别,再看目标板供电是不是正常。如果之前有程序把SWD引脚复用了,确实会导致连不上调试器,这时候按住复位键再点下载,靠运气抢时间。

7. 实战扩展:多节点组网与数据解析

CAN的魅力在于多节点组网。我直接把经验再延伸一步,帮你把系统串起来。

7.1 多节点组网的地址分配

不要把ID理解为主机地址。CAN是广播式的,每个节点都能收到总线上所有报文,关键在于ID的优先级和内容属性。一般的工程习惯是:

  • 0x000~0x07F:控制类高优先级报文,周期发送
  • 0x080~0x0FF:状态类报文,周期发送
  • 0x100~0x1FF:诊断类报文,按需发送

例如发动机转速报文固定ID为0x0A1,每10ms发送一次,里面包含转速、水温、油量等参数。另一个节点想用这个数据,就配置CAN过滤器只接收0x0A1。

7.2 数据解析与PDU设计技巧

CAN帧里最多8字节的数据,如果数据量超过8字节,你需要自己定义拆包组包的规则。常见做法是采用类似UDS的协议:每一帧的前两个字节是服务ID和数据长度,后面最多6字节是有效数据。多帧传输时加序列号。

比如要传一个字符串“HelloCAN”,可以定义:

  • Byte0: 0x01 表示握手开始
  • Byte1: 0x07 表示长度
  • Byte2~Byte7: “HelloC”前6个字节

第二帧:

  • Byte0: 0x02 表示续帧
  • Byte1: 0x01 表示还剩余1个字节
  • Byte2: “AN”

接收端按这个规则重新拼接,就还原出原始字符串。

8. 调试心得收尾

玩GD32的CAN总线,说到底就是把三件事做好:时钟算准、GPIO复用对、位时序不出错。只要这三件事没问题,后面的数据收发就是水到渠成。如果你踩到问题,先用逻辑分析仪看物理波形,再查寄存器配置,不要一开始就怀疑芯片本身,GD32的CAN外设经过这么多项目验证,稳定性是没什么大问题的。

我个人在实际项目里的习惯是:每个板卡的CAN初始化代码里,预留一个自检模式。上电后如果检测到BOOT引脚为高,就进入CAN回环测试,自发自收,把接收到的数据通过串口打印出来。这样即使没有外部节点,也能快速验证硬件通路是否正常。建议你也把这个小功能做成模板,排查问题会方便很多。

最后再分享一个小技巧:调试CAN时别忘了多看错误状态寄存器。GD32库里有can_error_state_get之类的函数,能直接返回当前错误状态。通过它判断节点是在主动错误、被动错误还是离线状态,比你在那瞎猜快得多。后面如果你想往深处玩,可以继续研究多CAN通道网关、利用过滤器的掩码模式做复杂路由,这些都是从今天这个Demo能自然延伸出去的方向。

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

AI技术栈七层认知地图:从模型量化到落地红线

1. 这不是词典&#xff0c;是技术人的认知地图&#xff1a;为什么“AI概念大全”必须用“国庆7天”来组织“AI概念大全&#xff1a;技术人的国庆7天扫盲指南”——这个标题一出来&#xff0c;我就在团队内部 Slack 上被了三次。不是因为大家想学AI&#xff0c;而是因为所有人都…

作者头像 李华
网站建设 2026/10/3 15:33:12

Windows下用WSL2搭建OpenFOAM 7与blastFoam 2.0.0开发环境全指南

折腾过CFD的同学都懂&#xff0c;OpenFOAM这个生态在Windows上有多拧巴。跑个求解器需要Linux环境&#xff0c;虚拟机性能打折&#xff0c;双系统来回重启又太伤&#xff0c;直到WSL2成熟之后才终于有了一个算得上顺手的方案。这篇博文就以OpenFOAM 7搭配blastFoam 2.0.0为例&a…

作者头像 李华
网站建设 2026/10/3 15:31:50

激光器恒流驱动电路设计实战:精度、热稳定与纹波抑制

1. 这不是教科书里的电路图&#xff0c;而是一台激光器真正“活”起来的关键你拆开过手里的激光笔、光纤通信模块、或者工业打标机的驱动板吗&#xff1f;里面最不起眼、却最不容出错的&#xff0c;往往就是那块指甲盖大小的恒流驱动电路。它不发光&#xff0c;不散热&#xff…

作者头像 李华
网站建设 2026/10/3 15:31:45

HTML学习笔记.2

1 关于路径的分类&#xff1a; 路径分为两种&#xff0c;即相对路径和绝对路径&#xff0c;相对路径以当前位置作为参考点&#xff0c;当导入图片时&#xff0c;用./引入同一文件夹的内容&#xff0c;/引入下一级的内容&#xff0c;…/引入上一级的内容。当想使用的文件与参考…

作者头像 李华
网站建设 2026/10/3 15:31:44

大模型应用面试核心:RAG、上下文工程与多Agent实战解析

1. 这不是模拟面试&#xff0c;是真实战场上的技术切片回放“大模型应用开发面试全流程实录”——这标题里没有一个字在讲理论&#xff0c;全是实战信号。我带过37个AI工程团队&#xff0c;参与过217场大模型方向的技术面试&#xff0c;从一线工程师到CTO级岗位&#xff0c;见过…

作者头像 李华