尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
STM32驱动DHT11温湿度传感器:单总线时序与实战避坑指南
我最早接触DHT11是在帮一个学弟调试毕业设计的时候他的温湿度数据读出来永远是0整块板子连初始化都过不去。当时我第一反应是这模块不是有手就行结果连我自己也栽了一个下午。后来发现DHT11看着简单实际上它那个单总线协议对时序极度敏感稍微有点偏差就全军覆没。这篇教程就是把我从硬件选型、原理图设计、底层驱动到踩坑排查的完整经验整理出来给正在用STM32做温湿度采集的开发者尤其是用标准库或HAL库写驱动时总被时序问题折磨的人一条能直接走通的路。1. 为什么2024年还在用DHT11先从选型逻辑说起市面上温湿度传感器多得是SHT30、AHT20、BME280随便拉一个出来精度都比DHT11强。那为什么DHT11至今还是教学板、毕业设计、DIY小项目里的常客我自己的看法是它的定位从来就不是性能王者而是学习曲线最平滑的入门传感器。先看一张我常用的对比表型号精度温度/湿度通信方式典型价格驱动难度DHT11±2°C / ±5%RH单总线3-5元低DHT22±0.5°C / ±2%RH单总线10-15元低SHT30±0.3°C / ±2%RHI2C6-10元中AHT20±0.3°C / ±2%RHI2C3-6元中BME280±1°C / ±3%RHI2C/SPI10-20元中高I2C传感器确实更简单——主机发地址从机回数据时序标准不用微秒级掐表。但DHT11的单总线协议反而更能锻炼人对数字时序的敏感度。你在DHT11上学到的电平翻转、延时窗口、起始信号、响应时序后面玩DS18B20、红外遥控、甚至是自定义单总线协议时全部用得上。还有一个非常现实的原因DHT11模块版便宜到可以忽略成本坏了直接换不需要重新画板子。做产品选型我不会推荐DHT11但做学习项目、实验室原型验证、或者给学生练手它依然是最顺手的那个。另外一个常被忽略的点DHT11的单总线协议对MCU的GPIO操作能力有很好的测试作用。如果你的SPI、I2C经常莫名失败先用DHT11把引脚操作练熟再回头排查硬件问题往往事半功倍。2. 硬件设计的三件小事上拉电阻、电源去耦和布线细节很多新手直接买现成的DHT11模块插上杜邦线就开始写代码。但我建议至少要知道模块上那几个元件是干嘛的因为你迟早要自己画板子。2.1 上拉电阻为什么必须接阻值怎么选DHT11的数据引脚是开漏输出结构——它本身只能把电平拉低不能主动输出高电平。要让总线在空闲时处于高电平必须在数据线上接一个上拉电阻到VCC。模块版上通常已经焊好了上拉电阻一般4.7kΩ或10kΩ。如果你用的是散装DHT11传感器裸芯片只有四个引脚VCC、DATA、GND、还有一个NC空脚这时上拉电阻必须自己加。阻值选4.7k还是10kDHT11的单总线通信速率不高边沿要求也不苛刻4.7k、5.1k、10k都能正常工作。我习惯用4.7k理由有两个一是抗干扰能力比10k更强二是DHT11数据线稍长超过20cm时4.7k的上升沿陡峭度更好读取成功率更高。2.2 电源去耦一个104电容解决大问题单独给DHT11供电的场合VCC和GND之间跨接一个0.1μF104的瓷片电容再并联一个10μF电解电容会更好。这个不是玄学——DHT11在发出数据时内部电路会在每个bit的边沿产生短暂的电流冲击。如果电源纹波大这个冲击可能影响内部RC振荡器的稳定性导致输出的脉冲宽度漂移。脉冲宽度漂移的直接后果就是MCU误判bit是0还是1。特别是当你把DHT11和电机、继电器、蜂鸣器这类感性负载共用电源时不加去耦电容读取乱码的概率会明显上升。2.3 3.3V还是5V宽电压不是免死金牌模块版的DHT11蓝色那种内部通常带了一个电平转换电路可以工作在3.3V或5V。但如果你用的是散装传感器情况就不一样了。散装DHT11虽然标称是3.3V~5.5V宽电压供电但市面上不少批次实际上更适应5V逻辑。我实测过一批散装的DHT11在3.3V供电下输出高电平大约只有2.4V左右。STM32的GPIO输入高电平阈值是0.7×VDD也就是3.3V供电下需要超过2.31V才算高电平——2.4V勉强越过阈值但余量很小线一长就出错。我的建议是散装DHT11优先用5V供电数据线通过电阻分压比如1k2k或者电平转换芯片降到3.3V再接STM32这样最稳。模块版则无所谓3.3V直接干就行。2.4 布线长度和走线位置的影响传感器和MCU之间的杜邦线尽量控制在20cm以内。超过这个距离建议把上拉电阻的阻值适当调小到2.2k~3.3k补偿线缆电容对上升沿的拖累。还有个实战中容易踩的坑DHT11的数据线不要和SPI时钟线、USB差分线平行走太长距离。强耦合会让DHT11的时序出现毛刺单片机读到错误bit后依然可能通过校验因为校验位只有8位碰撞概率1/256导致温度突然跳变几十度。这种故障最难查因为绝大多数时间数据是正常的偶尔抽风一次。3. 通信协议拆解DHT11的单总线时序到底在说什么网上关于DHT11的时序分析文章不少但很多都只说拉低18ms然后读数据没有讲清楚协议的精髓。这里我从底层把整个通信过程完整拆一遍。3.1 一次完整通信的四个阶段DHT11的单总线通信以主机发起起始信号为开始整个过程分成四段主机发送起始信号主机把总线拉低至少18ms通常我拉低20ms然后释放总线拉高。这个低电平时间必须足够长让DHT11的内部电路检测到并进入工作状态。DHT11应答信号主机释放总线后DHT11会在20~40μs内把总线拉低持续约80μs表示我准备好了随后释放总线总线被上拉电阻拉高再持续约80μs准备发送数据。数据位传输40位数据按序发送每位都由一个50μs左右的低电平起始然后是高电平。高电平持续时间的长短决定了该位是0还是1。总线释放40位数据发送完后DHT11释放总线回到空闲状态等待下一次起始信号。3.2 位0和位1的区别一切都在于高电平宽度这是整个DHT11协议最核心也最容易搞混的点。每一位数据的格式是50μs低电平 一定的的高电平。逻辑0高电平持续约26~28μs逻辑1高电平持续约70μsMCU怎么判断收到的是0还是1没有捷径只能是先把低电平等完因为每一位都以50μs低电平开头但实际长度有波动26到38μs都算正常然后开始计时高电平的时间。如果高电平持续超过某个阈值通常取40~50μs判为1否则判为0。这里有一个很多教程没提到的关键点低电平的起始时刻并不精确所以不能等到低电平结束才采样而是在检测到下降沿后要等足够的时间跨过低电平区间再开始测量高电平宽度。3.3 40位数据布局与校验逻辑DHT11一次上报40位数据顺序是湿度整数部分8位湿度小数部分8位温度整数部分8位温度小数部分8位校验和8位所有数据都是高位在前MSB first。校验和数据等于前四个字节之和的低8位。比如你读到的40位是湿度整数0x1F十进制的31湿度小数0x00温度整数0x1C十进制的28温度小数0x00校验和0x3B验证一下0x1F 0x00 0x1C 0x00 0x3B校验和一致数据有效。如果校验失败说明本次读取过程中有时序错误直接丢弃重读即可。DHT11的更新周期是1Hz0.5Hz到2Hz不等重读间隔至少要等1秒以上否则大概率还是读到旧数据或错误数据。3.4 为什么DHT11的时序对延时函数这么敏感这里要解释一个问题为什么同样一份代码在别人的板子上跑得好好的在你的板子上就死机、卡死或者数据全零因为DHT11协议要求的延时是微秒级的26μs、50μs、80μs这取决于系统时钟频率8MHz还是72MHz精确延时数值完全不同编译器优化等级MCU执行循环的指令周期数中断是否干扰了时序很多人直接用了一个不精确的Delay函数比如for(i0; i30; i);这个循环在不同优化级别下耗时差异巨大。我在-O2优化下写的延时函数换到-O0就完全跑不通就是这个原因。4. 标准库实战从发送起始信号到校验完整驱动下面给一套我实测稳定的标准库驱动代码。开发环境是Keil MDK5 STM32F103C8T6蓝色Pill板系统时钟72MHz。核心逻辑是发送起始信号、读取响应、逐位采集40位数据、校验和确认。如果你的硬件配置和我不同重点看延时函数怎么改不要照抄延时数值。4.1 引脚配置与宏定义#include stm32f10x.h #include delay.h // 自己实现的延时函数提供 delay_us 和 delay_ms #define DHT11_GPIO_PORT GPIOB #define DHT11_GPIO_CLK RCC_APB2Periph_GPIOB #define DHT11_GPIO_PIN GPIO_Pin_0 // 两行关键宏切换输入输出模式 #define DHT11_OUT_MODE() { GPIOB-CRL 0xFFFFFFF0; GPIOB-CRL | 0x00000003; } // PB0 推挽输出50MHz #define DHT11_IN_MODE() { GPIOB-CRL 0xFFFFFFF0; GPIOB-CRL | 0x00000004; } // PB0 浮空输入 #define DHT11_SET_HIGH() GPIO_SetBits(DHT11_GPIO_PORT, DHT11_GPIO_PIN) #define DHT11_SET_LOW() GPIO_ResetBits(DHT11_GPIO_PORT, DHT11_GPIO_PIN) #define DHT11_READ() GPIO_ReadInputDataBit(DHT11_GPIO_PORT, DHT11_GPIO_PIN)这里有个细节DHT11的数据线在通信过程中需要切换方向——主机发送起始信号时要输出模式读取应答和数据时要切换为输入模式。GPIO方向切换是关键不要偷懒。在标准库中我通常直接操作CRL寄存器而不是调用GPIO_Init因为GPIO_Init函数体较长在需要快速切换模式的场景下效率偏低而且寄存器操作指令数固定时序更可控。4.2 微秒级延时函数不要裸用for循环我在delay.h里自己实现了微秒延时基于SysTick定时器精度在±2μs以内。DHT11对50μs级别的窗口要求没那么变态±2μs完全够用。这里给出一种基于SysTick的实现方式void delay_us(uint32_t nus) { uint32_t temp; SysTick-LOAD nus * (SystemCoreClock / 1000000); // 设置重装载值 SysTick-VAL 0; // 清空计数器 SysTick-CTRL | SysTick_CTRL_ENABLE_Msk; // 使能SysTick do { temp SysTick-CTRL; } while ((temp SysTick_CTRL_ENABLE_Msk) !(temp SysTick_CTRL_COUNTFLAG_Msk)); SysTick-CTRL ~SysTick_CTRL_ENABLE_Msk; // 关闭SysTick SysTick-VAL 0; }注意SystemCoreClock在标准库的system_stm32f10x.c中定义72MHz主频下就是72000000。这个延时函数会被频繁调用如果觉得每次都要开关SysTick开销太大也可以用DWT计数器或者裸机while递减配合校准。我主要图它直观、好移植。4.3 DHT11完整的读取函数uint8_t DHT11_ReadData(uint8_t *humidity_int, uint8_t *humidity_dec, uint8_t *temp_int, uint8_t *temp_dec) { uint8_t buf[5] {0, 0, 0, 0, 0}; uint8_t i, j; uint8_t retry 0; // 1. 主机发送起始信号拉低至少18ms DHT11_OUT_MODE(); DHT11_SET_LOW(); delay_ms(20); // 2. 释放总线模式切为输入 DHT11_SET_HIGH(); delay_us(30); // 释放后等待一小段时间让上拉电阻把电平拉高 DHT11_IN_MODE(); // 3. 等待DHT11应答先低后高各约80us retry 0; while (DHT11_READ() 1 retry 100) { // 等待低电平应答开始 retry; delay_us(1); } if (retry 100) return 1; // 超时传感器未响应 retry 0; while (DHT11_READ() 0 retry 100) { // 等待低电平结束 retry; delay_us(1); } if (retry 100) return 1; retry 0; while (DHT11_READ() 1 retry 100) { // 等待高电平结束准备读数据 retry; delay_us(1); } if (retry 100) return 1; // 4. 读取40位数据 for (j 0; j 5; j) { for (i 0; i 8; i) { // 跳过50us低电平 retry 0; while (DHT11_READ() 0 retry 80) { retry; delay_us(1); } // 测量高电平宽度 retry 0; while (DHT11_READ() 1 retry 80) { retry; delay_us(1); } // 如果高电平持续超过40us判为逻辑1 if (retry 40) { buf[j] | (0x80 i); // 注意高位在前 } } } // 5. 校验 if ((uint8_t)(buf[0] buf[1] buf[2] buf[3]) ! buf[4]) { return 2; // 校验失败 } *humidity_int buf[0]; *humidity_dec buf[1]; *temp_int buf[2]; *temp_dec buf[3]; return 0; }这段代码里的两个关键点为什么用retry 40而不是retry 35判1因为我的delay_us(1)实际耗时可能略大于1μs加上while循环的判断开销实际循环周期大约是1.5μs。如果我阈值设得太小逻辑0的高电平约26μs可能也被判成1。用40作为阈值实测在72MHz下区分度为逻辑0约17次循环逻辑1约46次循环非常清晰。为什么每个等待循环都有超时上限如果传感器没接好、损坏或者总线被拉死没有超时的while会变成死循环程序直接卡死。这也是很多人说DHT11卡死程序的根本原因。加超时是所有单总线驱动的基本素养。4.4 调用示例int main(void) { uint8_t hum_i, hum_d, temp_i, temp_d; uint8_t ret; delay_init(); // 时钟初始化 GPIO_Config(); // 端口时钟使能和引脚配置 while (1) { ret DHT11_ReadData(hum_i, hum_d, temp_i, temp_d); if (ret 0) { printf(Humidity: %d.%d%%RH Temp: %d.%d°C\r\n, hum_i, hum_d, temp_i, temp_d); } else { printf(DHT11 read error: %d\r\n, ret); } delay_ms(2000); // 注意间隔至少1秒DHT11更新速率限制 } }5. HAL库移植从标准库思维切换到HAL库的三个变化现在STM32CubeMX已经成为主流初始化工具很多人的工程直接从HAL库起步。HAL库封装了底层寄存器操作但这几个点会让DHT11的HAL实现踩坑。5.1 GPIO方向切换HAL函数的开销问题HAL库切换GPIO输入输出方向的标准写法是GPIO_InitTypeDef GPIO_InitStruct {0}; GPIO_InitStruct.Pin GPIO_PIN_0; GPIO_InitStruct.Mode GPIO_MODE_OUTPUT_PP; GPIO_InitStruct.Speed GPIO_SPEED_FREQ_HIGH; HAL_GPIO_Init(GPIOB, GPIO_InitStruct);问题在于HAL_GPIO_Init函数非常庞大它会重设整个GPIO配置寄存器的所有位每次调用耗时几十微秒。而DHT11协议里起始信号释放后只有20~40μs的应答窗口你用HAL_GPIO_Init切一次模式窗口就错过了。我的HAL库方案是初始化时用HAL正常配置切换方向时直接操作寄存器。在HAL库工程里GPIOB的CRL寄存器地址依然相同直接改寄存器完全兼容。#define DHT11_OUT_MODE() { GPIOB-CRL 0xFFFFFFF0; GPIOB-CRL | 0x00000003; } #define DHT11_IN_MODE() { GPIOB-CRL 0xFFFFFFF0; GPIOB-CRL | 0x00000004; }这种写法在HAL库工程里完全合法因为HAL库也是基于同一套寄存器映射的。我用它在STM32F407上验证过没问题。5.2 HAL_Delay的精度陷阱HAL提供的HAL_Delay()是基于SysTick中断实现的毫秒级延时。如果你要微秒级延时不要尝试用HAL_Delay拆——它的节拍就是1ms拆不出微秒精度。在HAL库工程里我依然使用自己实现的SysTick微秒延时。但要注意如果HAL已经占用了SysTickHAL_Init默认会你自己再用SysTick会冲突。解决办法有两种改HAL的时基源在CubeMX的SYS配置里把Timebase Source改成TIM6或其他定时器把SysTick腾出来给你的微秒延时用。用DWT计数器实现延时DWT是Cortex-M内核的调试组件有一个32位的CYCCNT寄存器专门用来记录CPU周期数不依赖SysTick也不受HAL影响void delay_us_DWT(uint32_t us) { CoreDebug-DEMCR | CoreDebug_DEMCR_TRCENA_Msk; DWT-CYCCNT 0; DWT-CTRL | DWT_CTRL_CYCCNTENA_Msk; uint32_t cycles us * (SystemCoreClock / 1000000); uint32_t start DWT-CYCCNT; while (DWT-CYCCNT - start cycles); }我后来在F4上长期使用DWT方案稳定性很好推荐直接把这段代码封装到你的bsp层里。5.3 中断优先级为什么你的数据偶尔跳变在标准库时代我很少遇到DHT11读取被中断干扰的情况因为F103本身没有多少中断源。但换到F4、H7后外设丰富如果DHT11读取过程中被UART中断、定时器中断打断几百微秒时序窗口就被破坏了。一个最典型的场景你用串口每秒打印一次温湿度同时DHT11每次读取都失败。原因是USART的TXE中断在DHT11读取的50μs高电平时间内频繁触发导致循环变长阈值判断全部失真。解决办法DHT11读取函数整体放进临界区读取前__disable_irq()读完后__enable_irq()。这个粗暴但有效因为DHT11一次完整读取只有几毫秒关中断对系统影响很小。如果你要读取的同时还要保证串口不丢数据把DHT11读取放到定时器中断里并且把该定时器中断优先级设为最低。最优雅的解法用DMA传输串口数据让串口中断根本不触发。6. 实测问题排查六种翻车现场与修复链路6.1 数据全为0或校验永远失败这是最常见的问题。排查链路应该是先确认接线VCC是3.3还是5VDATA接对引脚没有GND是不是共地如果模块输出电压低于STM32高电平阈值数据线就用示波器或万用表量高电平幅值。再查上拉裸传感器没有上拉电阻时数据线空闲状态是浮空的读回的值随机几乎必然失败。然后用逻辑分析仪或示波器看起始信号主机有没有正常拉低20ms释放后DHT11有没有拉低80μs作为应答如果完全没看到应答波形大概率传感器已经损坏或者供电异常。最后检查延时你用的延时准确吗用delay_us(50)实际延时了多少有时我直接用HAL_Delay(1)想顺便延时1微秒结果延时了1毫秒整个时序全部错乱。6.2 第一次读取卡死很多人写的主循环第一次调用读取函数就死机这是因为起始信号发出后DHT11没有响应程序在等待低电平的while(DHT11_READ()1)里死循环了。前面也提过所有等待循环必须加超时退出。没有超时机制的DHT11驱动代码网上能找到一大把抄的时候务必自己加上。6.3 一直返回超时错误但传感器是好的还有一种情况传感器在第一次成功读取后第二次读取必须间隔1秒以上。如果你在循环里50ms就调用一次读取DHT11根本来不及刷新数据会一直处在忙状态不响应起始信号。解决方案把读取间隔设为2秒。如果需要更快的刷新率考虑DHT22或SHT30它们在高速轮询下表现好得多。6.4 温度湿度偶发乱跳温度从25°C突然跳到80°C然后又跳回来这种偶发故障通常是电磁干扰或者电源毛刺。我曾经遇到过一个小项目DHT11数据线走到了一块开关电源附近只要电源启动温湿度就开始乱跳。后面加了一根屏蔽线把信号线用外皮接地包裹并在线路入口加了一个100nF滤波电容故障消失。另一个排查点检查你选择的GPIO是不是被其他外设复用。比如STM32F103的PB0在默认情况下是ADC的通道8输入引脚。如果你初始化了ADC外设但没有配置引脚复用DHT11读到的是被ADC内部电路干扰后的电平数据就会时好时坏。6.5 使用JTAG引脚导致的下载失败我见过一个典型案例有人把DHT11接在PA13JTMS引脚上然后发现程序下载不进去或者下载一次后再也连不上调试器。原因是PA13/PA14默认被JTAG占用你配置成普通GPIO后调试口被禁用SWD也就失效了。解决办法在代码里加上这个GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable, ENABLE);然后按复位键的同时点下载抢在用户程序运行前把芯片擦掉。或者干脆换一个不带调试功能的GPIO引脚别给自己挖坑。6.6 板子能跑但读出来是恒定的28°C 60%RH这个温度恒定地异常合理大概率是传感器没有真正更新数据——除了1秒间隔问题外还有可能是因为DHT11的DATA引脚和VCC之间短路了或者传感器内部晶振损坏。用另一块已知正常的模块交叉测试能快速定位问题。7. 进阶玩法一条总线上挂多个DHT11与低功耗设计DHT11的单总线协议从原理上讲一条总线上可以挂多个传感器因为它每个bit都有明确的电平窗口。但实际上很少人这么干原因有二一是DHT11没有芯片地址所有挂在同一总线上的传感器会同时响应数据互相冲突二是DHT11没有冲突检测机制不能像DS18B20那样通过序列号寻址。所以如果你想采集多个位置的温湿度我的建议是每个DHT11独占一个GPIO引脚读取逻辑完全独立。STM32的引脚资源充足几个传感器还占得起。引脚紧张时可以用一个8选1模拟开关比如CD4051轮流把不同DHT11的数据线接到同一个GPIO。注意切换后要给传感器留足够的稳定时间至少100ms避免电平冲突。如果你追求极低功耗DHT11本身就不是好选择——它的上拉电阻一直在耗电。低功耗场景我更推荐把DHT11的VCC引脚也接到MCU的GPIO上读取前拉高供电读完再拉低这样待机电流几乎为零。不过要提醒上电后DHT11需要1秒左右的稳定时间才能可靠通信这个特性决定了它做不了高频采集。8. 两个实战经验补充关于移植和PCB设计8.1 关于国产替代芯片的移植注意事项最近看到有人用APM32、GD32替代STM32跑DHT11驱动。这些芯片在引脚和寄存器层次上兼容STM32F1标准库代码基本可以直接用但有一个被我同事踩过的坑有一批GD32F103的GPIO速度档位和STM32定义不完全一样如果照搬标准库的GPIO初始化引脚速度配置可能没生效导致GPIO翻转速度不够DHT11起始信号的下降沿不够陡传感器完全收不到起始信号。解决办法很简单把GPIO速度档位强制设为最高档在标准库中是GPIO_Speed_50MHz在HAL库中是GPIO_SPEED_FREQ_HIGH。不要用LOW或MEDIUMDHT11对边沿斜率有隐性要求。另外如果碰到起始信号发出后传感器一直无响应的情况先把时间参数整体放大到1.5倍试试。部分国产芯片的指令执行周期和ST原厂有细微差异延时会偏短。8.2 PCB设计时给DHT11留的位置建议如果你在画板子给DHT11留位置时注意三点传感器开口方向要朝外方便空气流通不要在它正上方放遮挡物或大功率发热元件。数据线走线远离电源开关管和电感类器件实在躲不开就在数据线上串一个330Ω电阻限制高频干扰灌入。在DATA和GND之间加一个1nF的滤波电容。虽然这个电容会略微拖慢上升沿但只要总线长度不长小于10cm没影响。我在产品原型中验证过这个电容能显著减少数据跳变。9. 从DHT11到DHT22一个驱动函数的通用化改造最后说一个实用技巧如果你以后要升级到DHT22DHT11的驱动代码可以复用90%。DHT22的通信协议结构与DHT11几乎一致不同的地方在于数据位逻辑1的高电平宽度更长约75μs温湿度分辨率是16位前两个字节是湿度按0.1%RH为单位接下来两个字节是温度最高位是符号位按0.1°C为单位校验和同样是前四个字节之和的低8位所以你可以把读取函数抽象成typedef struct { float humidity; float temperature; } DHT_Data_t; uint8_t DHT_GenericRead(DHT_Data_t *data);在读取完40位原始数据后根据传感器类型用宏或参数区分做不同的数据解码。我当年在项目里就是靠这个抽象把DHT11/DHT22的驱动维护成本降到了零。这套改造做完你的温湿度模块基本就到了可复用到下一个项目的程度了。从最基础的时序原理到标准库和HAL库两套实现再到干扰排查和PCB设计注意事项DHT11这条线吃透之后再去碰其他单总线设备或者I2C传感器都会顺手很多。
RELATED

相关推荐

从startup.zip到EOCD报错:zip格式原理与修复实战指南

从startup.zip到EOCD报错:zip格式原理与修复实战指南

简介:面向Matlab GUI开发者,这份修复包专门解决使用MATLAB Compiler将GUI打包为exe后按钮无边框的异常问题。该问题源于Matlab编译器自身机制,与用户代码无关,通常在较旧版本(如R2015b)中更容易遇到。包内提…

📅 2026/9/9 5:35:05
C++分布式系统实战:网络、并发与一致性实现解析

C++分布式系统实战:网络、并发与一致性实现解析

我一直在用C写分布式系统里的底层组件,说实话,这类项目很少出现在日常的业务团队里,但只要你去翻那些追求极致性能的中间件代码,比如消息队列、存储引擎、分布式协调服务,C永远是最常见的那层底色。这篇文章不是来讲分…

📅 2026/9/9 5:30:05
Ollama本地部署大模型全攻略:从安装到API调用的完整实战

Ollama本地部署大模型全攻略:从安装到API调用的完整实战

如果你最近在关注大模型落地这件事,应该会注意到 Ollama 这个名字出现的频率越来越高。它不是一个公司推出的商业套件,而是一个开源的本地模型运行时,简单理解就是:把大模型跑在你自己电脑上,数据不出本机,…

📅 2026/9/9 5:30:05
MORE NEWS

更多资讯

📰

2026年大模型API聚合网关选型指南:从接口混乱到统一治理

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

📰

面试反问技巧:用精准提问获取真实信息,识别团队风险

1. 面试中的“反向拷打”:为什么你必须学会审问面试官面试到了尾声,面试官几乎都会问一句:“你有什么想问我的吗?”很多候选人在这时候要么说“没有了”,要么问一个无关痛痒的问题草草收场。我做了这么多年技术面试官&…

📰

SEO代做与网站优化的本质区别,如何避免踩坑

1. SEO代做和网站优化,到底差在哪SEO代做和网站优化这两个说法,在圈内经常被混着用,客户也经常搞不清。但作为长期做搜索流量的人,我得说这俩其实是两码事。简单理解:网站优化是一个过程,SEO代做是购买这门…

📰

Java数组从入门到进阶:定义、遍历、排序与常见坑全面解析

1. 先聊清楚:数组到底是干嘛的如果你刚开始学Java,数组大概率是你在循环、判断之外遇到的第一个“真正有点数据结构味道”的东西。很多新手学到这里会有一个疑问:我声明一堆变量不行吗?为什么非要搞个数组出来?假设你要…

📰

边缘计算规模落地指南:从选型到部署的实战避坑清单

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

📰

Composer依赖解析失败?ThinkPHP 8创建项目排查全攻略

错误信息这样写:composer create-project topthink/think tp8,然后等了几分钟结果砸来一句Your requirements could not be resolved to an installable set of packages.。如果你搜索过这个问题,大概率已经在网上翻到了各种“换镜像源”“清…

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

读完文章,想聊聊您的网站?

告诉我们您的行业与需求,资深顾问一对一梳理方案与报价,全程免费。

📞 💬