news 2026/9/8 13:27:37

单总线协议1-Wire深度解析:从物理层时序到ROM寻址与DS18B20驱动

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
单总线协议1-Wire深度解析:从物理层时序到ROM寻址与DS18B20驱动

做嵌入式这些年,各种通信协议接触过不少,但要说“极简主义”的程度,单总线协议(1-Wire)绝对排得上号。一根数据线加一根地线,就把物理层、链路层甚至供电一起解决了,而设备寻址又依赖一套 64 位 ROM 码机制。很多朋友第一次接触单总线,多半是因为 DS18B20 这颗温度传感器,调通时序容易,但真要把物理层设计、复位时序、ROM 搜索这些底层逻辑讲清楚,很多人其实是一知半解的状态。

这篇文章我想把单总线从物理层到 ROM 寻址完整拆开聊一遍。不管你是刚入门单片机想要搞懂时序,还是已经在项目里挂了十几个传感器想排查偶发通信问题,这篇文章都能给你一套可以直接用的思路。我会把每一段时序的参数为什么这么定、每一个寻址命令背后是怎么工作的,都用实际项目里的语言讲明白。

1. 一根数据线怎么搞定通信和供电:先从物理层设计说起

1.1 开漏输出和上拉电阻到底解决了什么问题

单总线协议最反直觉的地方,就是所有设备共用一根数据线,而且这根线在空闲时是高电平,通信时靠拉低来产生时序。这背后其实是开漏输出结构在起作用。

开漏输出的特点很简单:芯片内部只有一个下拉 MOS 管,能把总线拉低,但没有主动推高的能力。所以总线必须外接一个上拉电阻,空闲时靠电阻把电平拉回高。这样的好处是极大的——任何设备都只能把总线拉低,不能主动拉高,于是多个设备挂在同一条线上时,不会出现一个设备输出高、另一个设备输出低导致的短路问题。这跟 I2C 的总线设计思路是一脉相承的。

我在实际项目里见过不少人把单总线当作普通 GPIO 推挽输出来用,结果通信成功率时好时坏。原因就是推挽输出会让总线在高低电平之间来回驱动,一旦两个设备同时输出相反电平,轻则通信错误,重则烧毁引脚。所以硬件设计上,单总线节点必须开漏,外接上拉电阻的阻值一般取 4.7kΩ,这个值是通用推荐值,短距离一对一通信时很稳定。

上拉电阻的选型需要根据线缆长度和设备数量调整。短距离、单设备的场景用 4.7kΩ 没问题;如果总线拉长到几十米或者挂了十几个设备,总线电容变大,就要把上拉电阻降到 2.2kΩ 甚至 1kΩ,否则上升沿太慢会让时序窗口错位。当然电阻也不能太小,太小会增加总线低电平时的灌电流,对从设备的驱动能力要求就高了。

1.2 寄生供电:省一根线的关键设计

单总线还有一个很“抠”的设计叫寄生供电。传统传感器需要 VCC 和 GND 两根供电线,单总线的从设备却可以只靠数据线在空闲时的高电平来“偷电”。数据线为高时,从设备内部通过一个二极管给电容充电,数据线被拉低时,就靠电容里存储的电量维持工作。

这个设计在 DS18B20 这类低功耗器件上非常实用,三根线变成两根,接头少一路,防呆压力也小。但寄生供电并不是没有代价的——温度转换这类大电流操作期间,总线必须保持高电平,否则从设备会瞬间掉电复位。所以在做温度转换时,主机需要启动强上拉,直接把数据线拉到高电平并保持几百毫秒,转换完成后再恢复普通上拉模式。

我建议新手在做实验时优先使用外部供电,也就是给 DS18B20 的 VDD 引脚接 3.3V 或 5V,数据线只负责通信。因为寄生供电模式对时序和电源质量更敏感,一旦代码里强上拉时间不够,传感器表现就是读出 85℃ 或者干脆无应答,排查起来非常折腾。等整个驱动调稳定了,再考虑寄生供电来做两线方案。

1.3 为什么单总线能带几十个设备:器件挂载与负载计算

正常情况下,单总线协议允许在一条总线上挂载几十个从设备,因为每个设备都有独立的 64 位 ROM 码,主机可以通过寻址命令来点名通信。但是“允许挂载那么多”和“能稳定挂载那么多”是两回事。

限制主要来自两个方面。第一是总线电容,每增加一个设备、每一米线缆,都会增加总线上的等效电容,电容太大直接拖慢上升沿,破坏时序要求。第二是寄生供电时的电流预算,如果所有设备都靠寄生供电,总线上同时充电的电容会非常大,主机那点上拉能力根本喂不饱。

我在一个项目里试过单总线挂 24 个 DS18B20,线缆总长约 15 米,用的 4.7kΩ 上拉电阻,结果扫描 ROM 时偶发找不到设备,后来把上拉改成 2.2kΩ,并且每个传感器的供电线独立走 5V,问题才稳定解决。所以如果你要挂很多设备,记住一个原则:优先外部供电,其次根据总线长度和挂载数量调整上拉电阻,必要时用总线驱动器芯片做电平中继。

2. 读懂三种时隙:复位、写、读的时序细节与参数要求

2.1 复位和存在脉冲:主从设备之间的第一次握手

单总线的每一次通信事务,都是从复位脉冲开始的。主机把总线拉低至少 480μs,然后释放,上拉电阻把总线拉高。从设备检测到这个低电平脉冲后,会等待 15μs 到 60μs,然后主动把总线拉低 60μs 到 240μs,这个响应就是存在脉冲。

这一握手的意义在于,主机需要确认总线上到底有没有设备在线。如果主机发出复位脉冲后,在 480μs 内没有检测到存在脉冲,说明总线上没有设备或者设备已经挂了。注意这里的时间参数是宽松但不随意的——复位脉冲太短,从设备可能没识别;太长则会占用总线时间,影响后续时隙。实际代码里我习惯把复位低电平时间设为 600μs,释放后再等 100μs 再开始读存在脉冲,这样余量更大。

存在脉冲的检测窗口也是一个容易踩坑的地方。有些代码在释放总线后立刻开始采样,结果读到的是上拉电阻充电过程中的高电平,错过了从设备拉低的时间窗口。正确做法是释放总线后等待约 30μs,再持续采样总线状态,直到捕捉到低电平或者超时。

2.2 写时隙:用脉宽来编码 0 和 1

复位握手完成后,主机就开始给从设备发送命令,这就要用到写时隙。单总线的编码方式很有意思,不是靠电平高低,而是靠低电平持续的时间长短来区分 0 和 1。

写 0 时隙,主机需要拉低总线并保持 60μs 到 120μs,然后释放。写 1 时隙,主机只需要拉低总线 1μs 到 15μs,然后释放,让总线回到高电平。从设备在主机产生下降沿后的 15μs 到 60μs 窗口内采样总线状态,如果采样到低电平就认为是 0,采样到高电平就认为是 1。

这就是为什么写时隙的核心,在于控制拉低的时间而不是电平本身。我见过很多人写出的代码是直接GPIO_WriteLow然后延时,但他们忽略了一个关键点:两个相邻时隙之间必须保留至少 1μs 的恢复时间。如果上一个时隙的 0 拉低了 120μs,下一个时隙立刻开始,总线还没来得及被上拉电阻拉高,就可能把下一个 1 误判成 0。

写时隙还有一个容易忽视的细节:主机在写 1 时,拉低的那 1μs 到 15μs 非常短,如果使用的是带延时的 GPIO 操作库,这个延时可能被系统调度放大,变成几十微秒,那从设备就会把 1 判断成 0。所以底层驱动最好采用寄存器直接操作加精确的忙等待延时,不要在中断频繁的环境里做单总线时序。

2.3 读时隙:主机为什么必须主动拉低总线

单总线的读时隙是另一个容易让人困惑的地方。很多人会想,既然是主机读从设备的数据,那主机只要释放总线然后采样不就行了?但实际上,单总线的读操作也是由主机主动产生一个下降沿开始的。

主机的标准读时隙是这样:拉低总线至少 1μs,然后释放总线。如果从设备要发送 0,它会在主机释放总线后继续把总线拉低并保持到读时隙结束;如果从设备要发送 1,它就不做任何动作,让上拉电阻把总线拉回高。主机在下降沿产生后的 15μs 窗口内采样总线电平,就能读到从设备发送的比特。

这个机制跟写时隙共享同一个基本框架,区别在于写时隙的拉低时间决定数据,读时隙的拉低时间只是提供一个同步时钟,真正的数据由从设备在主机释放后的行为决定。在实际实现中,读时隙的采样点非常关键,我通常把采样点设在拉低释放后 10μs 左右,既能避开下降沿噪声,又能在从设备释放总线之前完成采样。

读时隙还有一个常见问题:主机在进行连续多位读取时,如果每读一位之后没有给总线足够的恢复时间,电容上残留的低电平会导致误读。每个读时隙之间至少留出 5μs 以上的间隔,实际驱动里我会把一位的读时隙总周期控制在 70μs 左右,读取一位后延时 20μs 再开始下一位。

2.4 时序参数的常见坑:严格窗口与容差设计

把上面的时序放在一起看,你会发现单总线的每个时间参数其实都有比较大的容差范围。比如写 0 时隙允许 60μs 到 120μs,写 1 时隙允许 1μs 到 15μs。但真正严格的是采样点,主机释放总线后的 15μs 左右这个采样窗口,直接决定读到的比特是否正确。

我在调 DS18B20 驱动时,遇到过最典型的问题就是“时隙总长度足够,但高低电平的划分点不对”。很多人写代码时只关注拉低延时,却没有确保每个时隙的总周期一致。如果写 1 时隙被一些额外的函数调用拖到 30μs,写 0 时隙又刚好卡在 60μs,两者之间只差 30μs,边缘窗口太小,很容易受干扰。所以我的习惯是让写 1 和写 0 的时隙总周期都保持在大约 70μs 到 100μs,让数据位之间有一个稳定的边界。

另外要注意的是,不同的从设备对时序的敏感度不一样。DS18B20 算是对时序容忍度比较好的,但有些型号的 EEPROM 和温度传感器对时序更挑剔。所以在正式项目里,最好用逻辑分析仪把波形抓下来,核对每个时隙的实际长度,不要只依赖代码里写的延时数值。延时的实际时间跟主频、编译器优化级别都有关系,不同环境下同样的延时函数可能差出好几微秒。

3. ROM 寻址原理:64 位序列号如何决定“叫谁谁答应”

3.1 64 位 ROM 码的组成:家族码、序列号、CRC

单总线之所以能在一根线上挂多个设备,靠的就是每个设备出厂时固化的 64 位 ROM 码。这 64 位分成三部分:最低 8 位是家族码,比如 DS18B20 的家族码是 0x28,DS2431 EEPROM 的家族码是 0x2D;中间 48 位是设备唯一的序列号;最高 8 位是前 56 位的 CRC 校验码。

主机在访问任何一个设备之前,都要先通过 ROM 相关命令来确定当前要对哪个设备通信。由于每颗芯片的 48 位序列号都是激光刻录的,全球唯一,所以理论上你可以在同一条总线上识别出任意数量的设备,只要它们的 ROM 码不冲突。

CRC 校验码的存在也很关键,它让主机在读完全部 64 位之后可以自行计算校验,确认读到的序列号没有被干扰。实际项目中,如果扫描设备时偶尔扫到错误的 ROM 码,多半就是通信过程中某一位被干扰翻转,而你没有做 CRC 校验,导致后续寻址全部失效。所以任何严谨的单总线驱动,读取 ROM 码后都应该做 CRC 校验再做后续操作。

3.2 匹配 ROM、跳过 ROM、搜索 ROM 的区别

单总线协议定义了若干 ROM 命令,最常用的有三个:0x33 读 ROM、0x55 匹配 ROM、0xCC 跳过 ROM。这里的关键是理解它们在什么场景下使用。

读 ROM 命令只能用在总线上只有一个从设备时,直接从设备读取 64 位 ROM 码。如果总线上挂了多个设备,它们会同时向总线发送各自的位流,结果就是数据冲突,读到的内容毫无意义。跳过 ROM 命令则是明确的“我不关心你是谁,所有设备都听命令”,适合总线上确实只有一个设备,或者你想给所有设备广播一个命令的场景。

匹配 ROM 命令是每次通信的核心:主机先发送 0x55,然后紧接着发送一个 64 位的 ROM 码,总线上每个设备都会把这个 64 位数据跟自己的 ROM 码逐位比较,只有完全匹配的设备才会响应后续的命令,其他设备则保持静默。这个过程就像点名一样,喊到名字的人应答,没喊到的一动不动。

3.3 搜索 ROM 算法的核心思想:按位二分法定位设备

搜索 ROM 命令(0xF0)是最有意思的一条命令。它的作用是在总线上有多个设备的情况下,让主机逐个找出所有设备的 ROM 码。很多人在这一步直接卡住,因为不知道该怎样在同一根线上区分多个同时应答的设备。

其实搜索 ROM 的底层原理,我习惯把它理解成“每一位都进行两次读”。第一次读,总线上的所有设备会把自己该位的 ROM 码值放到总线上;第二次读,所有设备会把自己该位的反码放到总线上。如果总线上所有设备在这一位都是 0,那么第一次读到 0、第二次读到 1;如果所有设备在这一位都是 1,那么第一次读到 1、第二次读到 0;如果这一位上既有 0 又有 1,那么两次读到的都是 0,说明这里出现了一个分叉点。

主机记录下所有分叉点的位置,然后用类似深度优先搜索的方式,在每次分叉时选择一条分支继续深入,回溯时再走另一条分支。每完整走完一轮,就能得到一个设备的完整 ROM 码。这个过程本质上就是在 64 位寻址空间里做二分遍历,唯一的代价是需要多读一位反码来识别分叉位置。

我最初实现搜索 ROM 时逻辑绕了半天,后来想通一个诀窍:把搜索过程想象成走迷宫,ROM 码的每一位就是迷宫里的一个岔路口,分叉点就是“这一位既可以走 0 也可以走 1”的位置。你只需要维护一个数组记录每个分叉点是走 0 还是走 1,以及一个栈用来回溯,就能不重不漏地找完所有设备。

4. 手写单总线驱动:从 GPIO 模拟到完整通信流程

4.1 环境准备与引脚选择:硬件上的几个注意点

在动手写驱动之前,先把硬件环境准备好。我通常用 STM32 或者 ESP32 这类单片机来演示,因为 GPIO 翻转速度足够快,而且可以方便地接逻辑分析仪调试。选择一个支持开漏输出的引脚,外部接一个 4.7kΩ 上拉电阻到 3.3V 或 5V。

这里有个容易忽略的点:单总线的电平标准并不一定是 5V。DS18B20 的数据手册支持 3.0V 到 5.5V 供电,所以 3.3V 逻辑也是可以工作的。不过实际使用中,3.3V 供电时总线的噪声容限更低,如果线缆较长或者干扰较大,建议还是用 5V 供电并配上合适的电平转换。ESP32 的 GPIO 是 3.3V 电压域,直接驱动 DS18B20 没问题,但如果你用的是 5V 单片机的单向电平转换器,要特别注意转换器的方向,别把从设备的存在脉冲吃掉。

接线方式上,数据线尽量短一些,不要跟电机驱动线、电源线绑在一起走线。单总线对干扰的敏感程度不算低,长线走线时我吃过不少亏,后面会专门讲排查方法。

4.2 关键代码实现:时隙、复位、读写字节

下面我给出一份基于 GPIO 模拟的单总线驱动核心代码,语言用 C,方便移植到各种平台。重点看时序的延时方式和采样点的位置。

#define BUS_PIN GPIO_PIN_5 #define BUS_PORT GPIOB #define BUS_HIGH() HAL_GPIO_WritePin(BUS_PORT, BUS_PIN, GPIO_PIN_SET) #define BUS_LOW() HAL_GPIO_WritePin(BUS_PORT, BUS_PIN, GPIO_PIN_RESET) #define BUS_READ() HAL_GPIO_ReadPin(BUS_PORT, BUS_PIN) #define DELAY_US(us) delay_us(us) void onewire_reset(void) { BUS_LOW(); DELAY_US(600); // 复位脉冲,拉低至少 480us,这里放 600us BUS_HIGH(); DELAY_US(60); // 等待从设备响应 } int onewire_read_bit(void) { int bit; BUS_LOW(); DELAY_US(5); // 读时隙起始,拉低 5us BUS_HIGH(); DELAY_US(8); // 释放后等约 8us,在 15us 窗口内采样 bit = BUS_READ(); DELAY_US(50); // 读完一位后等待时隙结束 return bit; } void onewire_write_bit(int bit) { if (bit) { BUS_LOW(); DELAY_US(5); // 写 1:只拉低很小时间 BUS_HIGH(); DELAY_US(65); // 时隙剩余时间 } else { BUS_LOW(); DELAY_US(65); // 写 0:保持拉低足够时间 BUS_HIGH(); DELAY_US(5); } } void onewire_write_byte(unsigned char data) { for (int i = 0; i < 8; i++) { onewire_write_bit((data >> i) & 0x01); } } unsigned char onewire_read_byte(void) { unsigned char data = 0; for (int i = 0; i < 8; i++) { if (onewire_read_bit()) { data |= (0x01 << i); } } return data; }

这份代码的核心就是保证每个时隙的总体长度均匀。读位的时候,采样点放在拉低释放后的 8μs 左右,这个位置在 15μs 窗口内,而且避开了紧贴下降沿的噪声。写位的时候,写 1 和写 0 的总周期都在 70μs 上下,既满足时序要求,又让相邻位之间的边界稳定。

延时函数本身也要注意。普通的HAL_Delay是以毫秒为单位的,完全不能用在这种微秒级时序里。STM32 上我一般用 DWT 或者定时器来做微秒延时,ESP32 上可以用ets_delay_us或者esp_rom_delay_us。在 Linux 用户空间做单总线模拟则要格外小心,系统调度的不确定性会让时序完全不可控,不建议这么做。

4.3 完整的温度采集流程:从复位到 CRC 校验

有了上面的底层函数,完整的 DS18B20 温度采集流程就很好理解了。标准的步骤是:复位、写跳过 ROM 命令 0xCC、写温度转换命令 0x44、等待转换完成、再复位、写跳过 ROM 命令、写读暂存器命令 0xBE、连续读取 9 个字节。

void ds18b20_start_convert(void) { onewire_reset(); onewire_write_byte(0xCC); // 跳过 ROM,假定总线上只有一个设备 onewire_write_byte(0x44); // 启动温度转换 } float ds18b20_read_temperature(void) { unsigned char data[9]; int temp_raw; onewire_reset(); onewire_write_byte(0xCC); // 跳过 ROM onewire_write_byte(0xBE); // 读暂存器 for (int i = 0; i < 9; i++) { data[i] = onewire_read_byte(); } temp_raw = data[0] | (data[1] << 8); return (float)temp_raw * 0.0625f; }

注意第 9 个字节就是 CRC,你可以用查表法或者逐位法对前 8 个字节算一遍 CRC,和读到的第 9 个字节比对,不一致就说明这次传输出错,应该丢弃数据重读。DS18B20 的 CRC 生成多项式是x^8 + x^5 + x^4 + 1,网上有很多现成实现,强烈建议在正式项目里加上这一步。

温度转换命令之后,如果是寄生供电模式,要把数据线完全拉高并保持至少 750ms,让传感器内部完成 ADC 转换。如果是外部供电,只要简单延时 750ms 即可。转换完成后,传感器会把自己内部的状态标志位置位,但读取这个标志位需要额外的读命令,所以我一般直接固定延时 750ms 或者根据分辨率调整延时,省去状态查询的复杂度。

5. 单总线项目实战中的高频问题与排查实录

5.1 读不到设备:优先检查时序和上拉电阻

单总线最常见的故障就是设备无应答。排查时我有一套固定的顺序:先看硬件,再看时序,最后看逻辑。

硬件上,先量数据线空闲电平,正常应该是高电平。如果量出来是 0V,检查上拉电阻是不是虚焊、接错,或者单片机引脚被复用成其他功能了。如果在数据线上量到 1.2V 左右的电压,多半是总线被某个设备拉住了,常见原因是设备进入了奇怪的闩锁状态,需要断电重新上电。

时序上,用逻辑分析仪抓复位波形,确认复位低电平时间是否足够,存在脉冲是否出现。如果存在脉冲太窄或者没有,先检查代码里的延时是否真的达到微秒级,很多开发板的延时函数在优化后会变成几毫秒,直接把通信搞死。

5.2 数据偶发错误:长线传输和寄生供电的干扰

如果设备能读到,但数据偶尔错误,问题通常出在信号完整性和供电上。长线传输时,总线上的分布电容会让上升沿变缓,导致从设备采样到错误电平。我的经验是,单总线超过 3 米后,上拉电阻就应该从 4.7kΩ 降到 2.2kΩ,如果超过 10 米,最好在总线末端加一个 74HC04 之类的缓冲器做信号整形。

我自己在一个项目里,线缆长度大概 20 米,用 4.7kΩ 上拉时,每读 100 次大概有 3 到 5 次 CRC 错误,换成 1kΩ 上拉后错误率降到千分之一以下。这个优化很简单,但效果好得惊人。

寄生供电带来的问题则是另一套思路。如果设备采用寄生供电模式,温度转换期间主机必须输出强上拉,否则传感器会中途掉电,转换结果永远是 85℃ 或者干脆无应答。最简单的解决办法就是改成外部供电,数据线只负责通信,这是我在正式产品里最推荐的方案。

5.3 多设备挂载:序列号冲突与总线长度控制

挂载多个设备时,最典型的故障是搜索 ROM 时设备数量不对,或者同一行扫描出重复的序列号。出现这个现象,首先要怀疑的是某些传感器的 ROM 码在读取时发生了错误,导致搜出来的序列号是错的,后续又把错误序列号当作真实设备来访问。

解决办法是扫描后立即做 CRC 校验,跳过校验失败的序列号。另外,如果总线上有设备损坏,它可能会让总线一直处于拉低状态,导致整个总线瘫痪。排查时需要逐段断开设备,确定是哪颗传感器把总线拖死了。

多设备的总线长度控制也很关键。因为所有设备共享一根数据线,线缆越长,回波和反射越严重。如果项目要求传感器分布在不同位置,我更建议把传感器分成几组,每组一条总线,分别接到单片机的不同引脚,而不是把所有传感器都串在一条超长总线上。

5.4 调试利器:用逻辑分析仪看波形

最后强烈建议所有做单总线开发的朋友,手边备一台逻辑分析仪。国产的十几块钱逻辑分析器配合开源软件,就能抓取几十兆采样率的波形,对单总线这种微秒级时序来说绰绰有余。

抓波形的方法很简单:把探针夹在数据线上,地线接 GND,然后运行你的代码,抓一段包含复位、命令、数据读写的完整波形。通过波形可以直观看到每个时隙的长度是否超标、采样点位置是否正确、上拉电阻是否合适。很多代码里调了半天延时都不稳定的问题,拿到波形图上一眼就能看出来。

我见过不少人凭感觉调参数,调一整天都调不好,结果用逻辑分析仪一抓,发现是写 1 时隙的拉低时间超过了 20μs,从设备已经把 1 误判成 0 了。这种问题不看波形基本没法定位。

6. 写在最后:我对单总线协议的几点使用心得

单总线协议看起来简单,实际项目里能挖的坑不少,但也有规律可循。这么多年用下来,我最深的体会是:时序类协议的第一原则是稳定,而不是最快。单总线的通信速率本身也就几十 kbps,把每个时隙做得宽裕一些,把采样点放在窗口中间,远比把时序压到极限更能保证长期可靠性。尤其是产品要过温湿度循环、振动测试的时候,时序余量就是你最后的救命稻草。

另外,单总线的官方规范文档里有大量时序参数表格,读起来枯燥,但排查问题时非常管用。我建议把 15μs 采样窗口、480μs 复位脉冲、60μs 写 0 时隙这些核心数字打印出来贴在工位上,调代码时随时对照,能省不少时间。

最后分享一个小技巧:如果你的系统里有多条单总线,每条线上设备不多,驱动代码可以只维护一套底层时序函数,把总线号和引脚作为参数传递。这样单片机上有几个 GPIO 就能接几路单总线,组网灵活性提高不少。我自己做环境监测系统时,就是用 4 路单总线分别接不同区域的传感器,每路串 6 到 8 个节点,比全部挂一条总线省了无数排查烦恼。

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

移远4G模组GobiNet驱动在Linux/Android平台的编译安装与排错实战

简介&#xff1a;移远通信GobiNet驱动V1.6.2.9&#xff0c;面向Linux与Android平台&#xff0c;用于驱动移远Gobi系列无线网卡模块&#xff0c;使系统能够识别并建立3G/4G/LTE移动数据连接。该驱动源码支持在Linux环境下编译生成Gobinet.ko内核模块&#xff0c;并已在海思平台实…

作者头像 李华
网站建设 2026/9/8 13:23:09

GPU利用率低下?从调度顺序优化入手,不买卡也能提升训练吞吐

GPU 采购单越堆越长&#xff0c;账单上的数字越来越吓人&#xff0c;但模型的训练时长却纹丝不动——这种荒诞感我太熟悉了。过去半年里我接手过好几个团队的项目&#xff0c;诊断到最后&#xff0c;绝大多数性能瓶颈都不在算力总量&#xff0c;而在调度顺序。GPU 数量从来不是…

作者头像 李华
网站建设 2026/9/8 13:22:45

【Vue3+Uni-app+Spring Boot】互联网医院电子处方前置合规审核与药品外延配送小程序系统设计与实现(含PRD/三端高保真源码/大屏)

【基于 Vue3 Uni-app Spring Boot 的互联网医院电子处方前置合规审核与药品外延配送小程序】基于 Vue3 Spring Boot 的设计与实现&#xff08;含PRD/三端高保真源码/大屏&#xff09; &#x1f916; AI合规声明&#xff1a;本文所述互联网医院处方监管与配送系统架构、前后端…

作者头像 李华
网站建设 2026/9/8 13:22:10

Game Boy自制游戏开发实战:GBDK工具链与ROM构建指南

《黑城堡 2》是一款完全在 Game Boy 平台上运行的自制游戏。如果你对“如何在只有 8 位 CPU、8KB 工作 RAM、160144 像素分辨率的古董掌机上做出一款能玩的动作游戏”这件事感兴趣&#xff0c;这篇文章正好适合你。 这次我们不聊模拟器上的 ROM 修改&#xff0c;而是从自制游戏…

作者头像 李华
网站建设 2026/9/8 13:21:35

FPGA 100G UDP协议栈移植实战:从开源方案到线速收包

先把结论放在前面&#xff1a;这个项目本身不复杂&#xff0c;但真正的复杂度全藏在“移植”两个字里。我从拿到一块带 QSFP28 光口的 UltraScale 板卡&#xff0c;到把开源 100G UDP 协议栈跑起来、双侧验证线速收包&#xff0c;前后折腾了大概两个礼拜。期间踩过了光模块兼容…

作者头像 李华
网站建设 2026/9/8 13:21:18

PixVerse深度图控制:AI图像生成空间布局精准实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华