news 2026/9/10 1:57:08

单总线温度传感器MY18E20驱动开发:从时序到MicroPython实现

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
单总线温度传感器MY18E20驱动开发:从时序到MicroPython实现

做嵌入式时间长了,你会发现很多“看起来不起眼”的传感器,反而最考功底。MY18E20就是典型代表,它是一颗单总线数字温度传感器,外观、引脚、命令字和你熟悉的DS18B20几乎一样,但价格更友好,不少量产项目都在用。别以为它简单,单总线协议的精髓全在时序里:一根数据线上要完成复位、应答、写0、写1、读数据,全靠几十微秒的窗口卡出来的,稍不留神就会读到85.0或者干脆识别不到设备。这篇文章我打算从MY18E20的底层协议讲起,手把手用MicroPython写一套驱动,既是给新人做协议教学,也是给老工程师一份可以直接用的代码。适合正在做温控、环境监测、IoT数据采集的朋友,也适合想彻底搞懂单总线原理的嵌入式爱好者。

1. 项目整体设计与思路拆解

1.1 MY18E20是什么,为什么值得自己写驱动

MY18E20是一颗支持1-Wire单总线接口的数字温度传感器,测温范围-55℃到+125℃,默认12位分辨率,测量精度标称±0.5℃,内部有唯一的64位ROM编码,所以理论上可以在一根总线上挂多个传感器,分别寻址。从驱动开发的角度看,它和DS18B20的寄存器、命令字、时序参数都高度兼容,买到手基本可以当作DS18B20来用。很多人图省事,直接用现成的库,但我更建议自己写一遍驱动。

原因很简单:第一,现成库把时序细节都隐藏了,出了问题你只能瞎猜;第二,不同开发板、不同MicroPython固件的延时精度不一样,库里的默认参数未必适合你的硬件;第三,单总线是软件时序协议,掌握了底层逻辑之后,遇到任何兼容芯片都能快速移植。我手写这套驱动的过程,本质上是把“照抄代码”变成“理解协议”,排障时会轻松很多。

另外一个现实问题是价格。MY18E20在批量采购时通常比同名进口型号便宜,性能也够用,很多做环境监测、冷链运输、智能家居的项目都在换这颗料。但便宜不意味着可以放松,单总线三个字看着简单,真正要在MicroPython这种解释型环境里跑稳,还是有不少细节需要抠。

1.2 为什么选择单总线协议,自己写驱动而不是直接调现成库

单总线的核心优势是省引脚。一个GPIO就能读温度,特别适合MCU资源紧张的场景。而且1-Wire总线支持多点挂载,用一条总线拉几十米,挂上十几个传感器,做分布测温非常方便。这也是它在农业大棚、机房温控、工业设备监测里长盛不衰的原因。

那为什么不直接调MicroPython自带的OneWire库呢?官方库确实是能跑的,但它有几个问题:一,它是一个通用驱动,围绕DS18B20、DS18S20设计,对MY18E20这类兼容芯片来说,可调参数很少;二,它内部帮你做了ROM搜索,但如果只是单点测温,这些额外逻辑反而增加了出错概率;三,也是最关键的,你很难在现有库之上做深度排障。比如读回来温度是85.0,你根本不知道是复位失败、命令没发对,还是时序偏差。自己写驱动之后,每个时隙都掌握在手里,逻辑分析仪一抓波形,哪个步骤有问题一目了然。

1.3 技术栈选型:MicroPython做底层时序行不行

很多工程师一听到“MicroPython写时序驱动”就摇头,觉得解释型语言速度太慢,不适合做微秒级操作。这个观念要分情况看。1-Wire协议的单bit时隙是60到120微秒,比I2C、SPI慢得多,MicroPython的GPIO操作加延时函数在绝大多数现代MCU上都能覆盖这个时间尺度。实测在ESP32、RP2040、STM32F103这几个常用平台上,只要关掉中断干扰,复位和读写时序都能稳定满足协议要求。

不过也要承认边界:如果你做的是几万台的量产设备,对时序余量要求极高,或者还要同时跑无线协议栈,那最终驱动肯定要用C或者直接用硬件1-Wire外设。但作为原型验证、小批量产品、教学实验,MicroPython完全够用,而且开发速度快得多。这里多说一句,如果项目还想用U盘记录温度日志,可以找支持USB Host的MicroPython固件,在一些开发板上直接插U盘写CSV,非常舒服。后面扩展部分我会再提。

2. 单总线协议核心细节解析与实操要点

2.1 开漏结构与上拉:为什么不需要时钟线

理解单总线,首先得理解它的物理层。1-Wire总线只有一根数据线,没有时钟线,所有设备都是开漏输出,外部需要接一个上拉电阻把总线拉高到供电电压。开漏的意思是:设备只能主动把总线拉低,不能主动推高;需要输出高电平时,就释放总线,让上拉电阻把电平拉上去。你可能觉得这有点绕,但好处很明显:多个设备可以同时挂在一条线上,谁想发送0就拉低,不想发送就释放,不会出现推挽输出那种“一个推高一个拉低”的短路问题。

上拉电阻阻值对时序影响很大。典型值4.7kΩ,短距离、少设备时1kΩ到10kΩ都能工作,但阻值太大会让总线上升沿变慢,读时隙的采样点可能错过;太小则会增加功耗,而且设备拉低时电流过大。我一般先上4.7kΩ,如果线长超过2米,再考虑降阻值或者屏蔽线。还有一个细节是,如果MCU引脚内置了弱上拉,可以暂时不接外部电阻做验证,但正式电路一定要加。

2.2 复位脉冲与存在脉冲:握手的开始

单总线每一次完整通信,都必须先由主机发送一个复位脉冲,从机回应一个存在脉冲,这相当于双方握手。具体过程是:主机把总线拉低至少480微秒,然后释放。释放后,从机会等待15到60微秒,再把总线拉低60到240微秒,表示“我在”。主机在释放总线后大约60到70微秒时读取电平,如果读到低电平,说明总线上有设备应答;如果读到高电平,说明没有设备或者接线有问题。

这个握手在MicroPython里的实现很直接:拉低、延时、释放、延时、读引脚。需要注意两点:第一,拉低时间要留够余量,我习惯用500微秒而不是480微秒,避免编译器或者GPIO切换开销把时间吃掉;第二,存在脉冲的窗口很窄,MicroPython的延时函数本身有误差,所以我常把读取点放在60微秒附近,这个位置在存在脉冲的有效范围内,成功率最高。附上我用的一段复位函数:

def _reset(self): self.data = Pin(self.gpio, Pin.OUT) self.data.value(0) time.sleep_us(500) # 主机拉低复位脉冲 self.data = Pin(self.gpio, Pin.IN, Pin.PULL_UP) time.sleep_us(60) # 等待存在脉冲 presence = self.data.value() == 0 time.sleep_us(440) # 等存在脉冲结束 return presence

这个函数返回True表示检测到设备,False表示无应答。调试时先单独调用它,比直接读温度靠谱得多。

2.3 写时隙与读时隙:用时间窗口传递0和1

握手之后,所有数据都是通过时隙(Time Slot)来传输的。每个时隙至少持续60微秒,写数据和读数据都从主机拉低总线开始。区别在于拉低之后的行为。

写1时,主机先拉低总线,但必须在一个比较短的时间窗口内释放,让上拉电阻把总线拉高。DS18B20/ MY18E20的写时序规定,从写时隙开始到从机采样,低电平时间不能超过15微秒,否则从机会认为收到0。写0时,主机必须把总线一直拉低60到120微秒,整个时隙基本都保持低。用一句话记忆:1是高,所以要快速松手;0是低,所以要压住不放。

读时隙稍微隐蔽一些。主机拉低总线至少1微秒后释放,从机会在释放后的15微秒内决定总线状态:如果要从机发0,从机就把总线拉低;如果从机发1,从机就保持释放。主机必须在时隙开始后约15微秒处采样总线电平,太早可能读到上一态,太晚可能读到下一个时隙的起始低电平。我把采样点放在释放后8微秒左右,因为在MicroPython里,从释放到读取还会经过几微秒的代码开销,实际采样点大致落在12到15微秒之间,刚好卡进窗口。

这里有个参数表,方便你对照调整:

操作主机拉低时间从机采样/动作推荐MicroPython参数
写11~15微秒采样窗口15~60微秒拉低8us后释放
写060~120微秒无需采样持续低65us后释放
读01微秒从机15us内拉低拉低3us后释放,8us后读
读11微秒从机15us内释放同上

2.4 ROM命令与暂存器布局:一次温度读取的完整流程

单总线的通信流程有两层命令:第一层是ROM命令,用来选择总线上的某一个设备;第二层是功能命令,用来操作被选中的设备。单设备场景最常用的是跳过ROM命令0xCC,意思是“不用管地址,所有设备都执行后面的命令”。多设备场景就需要读ROM命令0x33(只适合总线上只有一个设备)、匹配ROM命令0x55、搜索ROM命令0xF0。接下去是功能命令:0x44启动温度转换,0xBE读暂存器,0x4E写暂存器,0x48复制暂存器等。

MY18E20上电后,暂存器里默认温度值是85.0℃,这个很关键,因为它是一个典型故障码。如果你读到的温度一直卡在85.0,大概率不是温度真这么高,而是根本没有成功启动转换。完整的单次温度读取流程可以拆成这几步:

  1. 复位,等待存在脉冲。
  2. 发送0xCC跳过ROM。
  3. 发送0x44启动温度转换。
  4. 等待转换完成。12位分辨率需要约750毫秒,分辨率越低越快:9位约94毫秒,10位约188毫秒,11位约375毫秒。
  5. 再次复位。
  6. 发送0xCC跳过ROM。
  7. 发送0xBE读暂存器。
  8. 连续读取9个字节,其中前两个字节是温度值。
  9. 按分辨率计算实际温度。

暂存器的布局是:字节0为温度LSB,字节1为温度MSB,字节2和3是报警阈值,字节4是配置寄存器,字节5到7保留,字节8是CRC校验。温度计算时,把LSB和MSB拼成一个16位整数,如果是12位分辨率,低4位是小数部分,对应0.0625℃的步进。负数温度需要用符号扩展处理。后面驱动代码里我会详细演示。

3. 实操过程与核心环节实现:MicroPython驱动编写

3.1 开发环境与硬件接线准备

我用的开发板是ESP32和树莓派Pico各测了一遍,代码完全通用,只需要换GPIO号。元器件清单很简单:一块开发板、一颗MY18E20、一个4.7kΩ电阻、几根杜邦线。接线方式:MY18E20的VDD接3.3V,GND接GND,DQ数据线接到GPIO,同时DQ通过4.7kΩ电阻上拉到3.3V。注意如果是面包板,电阻要尽量靠近传感器一侧,减少寄生电容。

固件方面,直接从micropython官网下载对应板卡的bin或uf2,刷进去就行。如果你后面想用U盘或者USB键盘这类外设,就要找支持USB Host的MicroPython固件。这个需求不是必须的,单跑温度传感器不影响。

MicroPython里GPIO的编号要注意,ESP32大部分开发板使用GPIO数字编号,比如GPIO4就是Pin(4);树莓派Pico使用GPIO编号例如Pin(16)。我建议在初始化前先用万用表确认引脚,避免因为丝印含义不同接错线。接线完成后,先试一下GPIO能否置高置低,再连传感器,这是最基础的排错习惯。

3.2 从bit级函数开始:把协议变成代码

写驱动最底层的是三个函数:复位、写一个bit、读一个bit。这三个函数直接决定上层能否工作,所以参数要反复调。为了避免不同MicroPython平台对Pin.OUT的推挽/开漏支持不一致,我采用动态切换输入输出来模拟开漏:需要拉低时设为输出并写0,需要释放时设为输入并开启内部上拉。即使有外部4.7kΩ上拉,内部上拉打开也没坏处,能提高边沿速度。

下面是读写bit的核心代码:

def _write_bit(self, bit): self.data = Pin(self.gpio, Pin.OUT) self.data.value(0) if bit: time.sleep_us(8) # 写1:短暂拉低后释放 self.data = Pin(self.gpio, Pin.IN, Pin.PULL_UP) time.sleep_us(60) # 保证写时隙长度 else: time.sleep_us(65) # 写0:持续拉低 self.data = Pin(self.gpio, Pin.IN, Pin.PULL_UP) time.sleep_us(5) def _read_bit(self): self.data = Pin(self.gpio, Pin.OUT) self.data.value(0) time.sleep_us(3) # 启动读时隙 self.data = Pin(self.gpio, Pin.IN, Pin.PULL_UP) time.sleep_us(8) # 让从机有时间控制总线 bit = self.data.value() time.sleep_us(50) # 时隙余量 return bit

注意写1时,拉低8微秒后释放,这个8微秒是关键。我在ESP32上测试时,如果把拉低时间加到15微秒以上,读回来的数据会整片变成0。写0时拉低65微秒,刚好在60到120微秒的范围内,然后释放5微秒给总线恢复。读bit时拉低3微秒,释放后等8微秒再采样,实测能稳定读到正确的0和1。

3.3 字节级封装与温度转换命令

有了bit函数,字节级操作就很简单了。1-Wire协议规定字节传输是低有效位在前(LSB first),所以读字节就是把8个bit依次按位移进一个整数;写字节就是把每个bit按位移出:

def _read_byte(self): value = 0 for i in range(8): value |= self._read_bit() << i return value def _write_byte(self, value): for i in range(8): self._write_bit((value >> i) & 1)

接着就可以封装命令了。启动温度转换的完整动作是:复位、跳过ROM、发0x44、等待750毫秒。读暂存器的动作是:复位、跳过ROM、发0xBE、连续读9个字节。这里我建议把转换和读取分开,不要揉在一个函数里,这样调试时能分别验证。

温度解析部分,先拼raw值:

raw = data[0] | (data[1] << 8)

如果最高位是1,说明是负温度,需要做符号扩展。最稳妥的写法是:

if raw & 0x8000: raw -= 0x10000 temp = raw * 0.0625

比如12位精度下,-0.5℃对应的raw值是0xFFF8,也就是-8,乘以0.0625就是-0.5℃。如果直接用无符号数换算,会得到4095.75℃,所以这一步不能省。

3.4 完整驱动类:一套可直接复用的MY18E20类

下面给出我整理后的完整类,包含CRC8校验,直接复制到项目里就能用。CRC8虽然会增加一点点计算时间,但能有效识别总线干扰,值得加上。

from machine import Pin import time class MY18E20: def __init__(self, gpio): self.gpio = gpio self.data = Pin(gpio, Pin.OUT) self.data.value(1) time.sleep_ms(10) def _reset(self): self.data = Pin(self.gpio, Pin.OUT) self.data.value(0) time.sleep_us(500) self.data = Pin(self.gpio, Pin.IN, Pin.PULL_UP) time.sleep_us(60) presence = self.data.value() == 0 time.sleep_us(440) return presence def _write_bit(self, bit): self.data = Pin(self.gpio, Pin.OUT) self.data.value(0) if bit: time.sleep_us(8) self.data = Pin(self.gpio, Pin.IN, Pin.PULL_UP) time.sleep_us(60) else: time.sleep_us(65) self.data = Pin(self.gpio, Pin.IN, Pin.PULL_UP) time.sleep_us(5) def _read_bit(self): self.data = Pin(self.gpio, Pin.OUT) self.data.value(0) time.sleep_us(3) self.data = Pin(self.gpio, Pin.IN, Pin.PULL_UP) time.sleep_us(8) bit = self.data.value() time.sleep_us(50) return bit def _read_byte(self): value = 0 for i in range(8): value |= self._read_bit() << i return value def _write_byte(self, value): for i in range(8): self._write_bit((value >> i) & 1) def _crc8(self, data): crc = 0 for byte in data: for _ in range(8): mix = (crc ^ byte) & 0x01 crc >>= 1 if mix: crc ^= 0x8C byte >>= 1 return crc def convert(self): self._reset() self._write_byte(0xCC) # 跳过ROM self._write_byte(0x44) # 启动温度转换 time.sleep_ms(750) def read_scratchpad(self): self._reset() self._write_byte(0xCC) # 跳过ROM self._write_byte(0xBE) # 读暂存器 data = bytearray(9) for i in range(9): data[i] = self._read_byte() return data def read_temp(self): self.convert() data = self.read_scratchpad() if self._crc8(data) != 0: return None raw = data[0] | (data[1] << 8) if raw & 0x8000: raw -= 0x10000 return raw * 0.0625

调用示例:

sensor = MY18E20(4) while True: t = sensor.read_temp() if t is not None: print("当前温度:{}℃".format(t)) else: print("读取失败") time.sleep_ms(1000)

这个类在ESP32和树莓派Pico上都能跑,唯一需要调整的是可能的时序参数。如果读回数据总是错,优先检查所有time.sleep_us的值,尤其是写1时那个8微秒。

3.5 调用示例与REPL调试技巧

MicroPython最方便的就是REPL。接好传感器后,可以先在REPL里手动执行底层函数排查:

s = MY18E20(4) s._reset() # 返回True说明检测到设备 s._write_byte(0xCC) s._write_byte(0x44) # 启动转换,然后等几秒 s._reset() s._write_byte(0xCC) s._write_byte(0xBE) s._read_byte() # 返回温度LSB s._read_byte() # 返回温度MSB

一段段敲完,基本能定位是复位失败还是读写时序问题。如果_reset()返回False,先查接线、上拉电阻和供电,不要再往下走。如果_read_byte()读到的全是0或255,说明bit时序需要调。

4. 常见问题与排查技巧实录

4.1 MicroPython时序不准怎么办

很多新人拿到驱动跑不起来,第一反应是“MicroPython不行”。实际上,绝大多数问题是延时函数受中断影响。MicroPython的time.sleep_us在调用时,如果有系统tick中断或者WiFi任务抢占,实际延时可能多出几十微秒,这在复位和读时隙里是致命伤。解决办法有两个:

一是关键时隙关中断。在复位和读bit时,用machine.disable_irq()machine.enable_irq()把中断临时关掉,等时序操作完成再打开。注意关闭时间要短,否则会影响系统心跳,但单总线的几个bit操作加起来也就几百微秒,问题不大。

二是用忙等替代sleep。在某些平台上,time.ticks_us()配合while循环,实际效果反而稳定。下面是一个通用忙等函数:

def delay_us(n): start = time.ticks_us() while time.ticks_diff(time.ticks_us(), start) < n: pass

用这个函数替换所有time.sleep_us,时序误差会小一些。缺点是CPU空转,但对短延时无所谓。

4.2 读回85.0或0xFF的排查

85.0这个值太有迷惑性了,第一次遇到的人都以为传感器坏了。其实它是MY18E20上电时暂存器的默认温度值,也就是说,芯片根本没有执行温度转换,或者转换结果没被正确读回来。常见原因有三个:一是启动转换命令没发出去,比如跳过ROM那一步失败;二是等待时间不足,12位分辨率需要750毫秒,你只等了100毫秒;三是每次读取前必须重新发一次转换命令,不能只复位后直接读暂存器。

0xFF则通常表示读时序出了偏差。我需要强调,读bit时采样点太早或者太晚都会读到1。太早,从机还没来得及拉低;太晚,可能已经进入下一个时隙。如果读到的字节固定为0xFF,说明采样点整体偏晚或偏早,试着把释放后的等待时间从8微秒改成5微秒,或者改成10微秒,观察结果。

4.3 多设备场景:ROM搜索与匹配

单设备用0xCC跳过ROM没问题,但总线上挂了多个MY18E20时,0xCC会让所有传感器同时响应,数据冲突。正确做法是先扫描出每个传感器的64位ROM地址,然后针对指定地址发送匹配ROM命令0x55,再接功能命令。

MicroPython的onewire模块里已经有地址扫描函数,可以直接借用。先扫描:

from machine import Pin from onewire import OneWire ow = OneWire(Pin(4)) roms = ow.scan() for rom in roms: print(rom.hex())

拿到地址后,在读取该设备温度时,发送0x55匹配ROM,然后逐个发送ROM地址字节。基于我们前面的驱动,可以扩展一个带地址参数的读取函数,核心是把跳过ROM替换成匹配ROM:

def read_temp_by_rom(self, rom): self._reset() self._write_byte(0x55) # MATCH ROM for b in rom: self._write_byte(b) self._write_byte(0x44) time.sleep_ms(750) self._reset() self._write_byte(0x55) for b in rom: self._write_byte(b) self._write_byte(0xBE) data = bytearray(9) for i in range(9): data[i] = self._read_byte() # 后续解析与read_temp一致

这样一条总线上挂多少设备都能分别读,唯一要注意的是扫描ROM时不可以同时有多个设备在初始化,否则搜索算法会出错。

4.4 硬件问题速查表

故障现象可能原因处理方式
_reset()一直返回False接线错误、DQ接触不良、没上拉电阻检查VDD/GND/DQ,加上4.7kΩ上拉
温度固定85.0转换未启动、等待时间不足、命令顺序错误确认走到0x44命令,并等待750ms
温度总是0xFF或异常大读写时序偏早偏晚、上拉电阻过大调读时隙采样点,上拉改用4.7kΩ
偶发CRC错误总线干扰、线缆过长、电源纹波缩短导线,加0.1uF去耦电容,提高采样次数
温度跳变、不连续接触不良、传感器供电不稳定重新插拔,排查面包板接触,必要时焊接

4.5 几条实战经验

第一,不要把read_temp()放在一个无限循环里以最高频率跑。转换本身需要750毫秒,频繁复位反而容易把总线状态搞乱,还会增加传感器自发热影响精度。建议轮询周期至少1秒。

第二,逻辑分析仪是单总线调试的神器。一个几十块的24MHz逻辑分析仪,就能把复位脉冲、存在脉冲、每个时隙的低电平时长看得清清楚楚。拿着波形和协议手册对比,10分钟就能定位问题,比盲调参数高效太多。

第三,MicroPython的报错不是万能的。时序错误不会抛出异常,只会返回错误数据,所以驱动里要有“读不到就返回None”的兜底逻辑,宁可在应用层多查几次,也不要让一个错误温度控制继电器动作。

5. 工程化经验与扩展方向

5.1 从驱动到产品:调度与功耗设计

驱动跑通只是第一步,真正做成产品还要考虑调度和功耗。电池供电的场景,推荐给MY18E20的VDD做一个MOS管开关,单片机休眠时彻底断电,需要采集时再上电。但注意上电后传感器需要几十毫秒稳定,不要立刻发命令。我通常的做法是上电后等待50毫秒再复位,然后启动转换,读取完成后立刻断电。这样平均功耗能压到很低。

如果系统里还有WiFi、蓝牙等任务,建议用定时器触发采集,不要在应用循环里用while True占住CPU。MicroPython的machine.Timer可以在后台周期执行,温度数据放到队列里,再由网络任务上报,这样不会因为温度采集阻塞其他功能。

5.2 可靠性与数据滤波

工业场景里,一次偶发的CRC错误不能直接当作故障处理。比较稳的写法是连续读三次,取中间值或者平均值。这里提供一个简单滑动滤波思路:

def read_stable_temp(sensor, times=3): vals = [] for _ in range(times): t = sensor.read_temp() if t is not None: vals.append(t) time.sleep_ms(100) if not vals: return None vals.sort() return vals[len(vals) // 2]

限幅滤波也可以:如果当前读数跟上次读数差超过5℃,先不要更新,连续两次超差再认为是真实跳变。这种做法在设备自发热、空调风直吹的场景下特别有效。

5.3 扩展想法:多路采集、日志记录与USB Host

驱动基础打好之后,扩展方向很多。一条总线上挂多个MY18E20做分布式测温,配合前面的ROM匹配函数,很容易扩展成多路采集。远程上报走MQTT,本地记录可以写文件到SD卡,甚至接U盘。

如果你用的是支持USB Host的MicroPython固件,可以直接把温度数据写成CSV存到U盘,这样既不用SD卡模块,也方便插到电脑上看曲线。对数据采集类项目来说,这个方案成本低、可维护性好。需要提醒的是,支持USB Host的固件选择要看具体板卡,不是每个开发板都有,使用前先确认外设功能和占用引脚。

5.4 最后一点体会

我真正把整套驱动跑稳,是在连续踩了“85.0温度”和“0xFF数据”两个坑之后。回头总结经验,最核心的不是代码本身,而是对时序窗口的理解。单总线协议没有时钟线,一切全靠主机的节奏控制,所以写驱动时要多看协议手册上的时间参数,别照搬别人的代码就完事。你在自己板子上遇到的时序问题,很可能就是某个延时差了十几微秒。用逻辑分析仪抓一圈波形,把每个时隙的宽度调到位,这套驱动就能稳定陪你跑很久。

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

CANN/GE模型执行配置设置

aclmdlSetExecConfigOpt 【免费下载链接】ge GE&#xff08;Graph Engine&#xff09;是面向昇腾的图编译器和执行器&#xff0c;提供了计算图优化、多流并行、内存复用和模型下沉等技术手段&#xff0c;加速模型执行效率&#xff0c;减少模型内存占用。 GE 提供对 PyTorch、Te…

作者头像 李华
网站建设 2026/9/10 1:55:27

comprehensive-rust 课程精讲:泛型上的 Trait Bound 与多态设计

comprehensive-rust 课程精讲&#xff1a;泛型上的 Trait Bound 与多态设计 【免费下载链接】comprehensive-rust This is the Rust course used by the Android team at Google. It provides you the material to quickly teach Rust. 项目地址: https://gitcode.com/GitHub…

作者头像 李华
网站建设 2026/9/10 1:55:19

样式冲突根治指南:Vue scoped与CSS Modules原理对比与实战

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

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

cann/ge Pyatc接口文档

Pyatc接口 【免费下载链接】ge GE&#xff08;Graph Engine&#xff09;是面向昇腾的图编译器和执行器&#xff0c;提供了计算图优化、多流并行、内存复用和模型下沉等技术手段&#xff0c;加速模型执行效率&#xff0c;减少模型内存占用。 GE 提供对 PyTorch、TensorFlow 前端…

作者头像 李华
网站建设 2026/9/10 1:53:35

Python接入QQ群机器人:从零搭建到部署的完整实践指南

从“想给群友整个活”开始&#xff0c;我花了两天时间把QQ群聊机器人搭了起来&#xff0c;用的就是官方开放的QQ开放平台和Python。坦率讲&#xff0c;这个方案比很多人想的要简单&#xff0c;但网上能查到的资料确实不集中&#xff0c;尤其是从零开始到“能跑起来”这一段&…

作者头像 李华