尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
STM32 HAL库驱动DHT11与OLED显示:从时序到避坑的完整指南
简介这份资源面向嵌入式与物联网方向的初学者及开发者围绕STM32 HAL库展开解决DHT11温湿度采集与OLED实时显示联动的实践问题。包内共193个文件以59个h头文件、29个c源文件、28个o目标文件及29个crf、30个d依赖文件为主另有ioc、uvprojx、uvoptx等工程配置与hex、axf、map等编译产物压缩包约6.9MB完整保留了从驱动到构建的工程结构。资源涵盖单总线时序读取40位数据、I2C/SPI驱动SSD1306显示、GPIO与时钟配置等知识点并包含stm32f0xx_hal_tim、i2c、uart、rcc等HAL模块源码便于对照理解底层实现。目前已有772人学习下载适合希望打通传感器采集与图形显示链路、积累嵌入式项目经验的读者参考。1. 从一块“读不出数”的 DHT11 说起STM32 HAL 库温湿度采集与 OLED 显示环境搭建很多人第一次把 DHT11 接到 STM32 上串口打印出来的不是温湿度而是一串255或者干脆卡死在while里等电平翻转。我见过太多这样的现场传感器买了、OLED 也焊了、CubeMX 工程也生成了结果 DHT11 就是不出数OLED 上永远显示Temp: --.- C。问题往往不在传感器本身而在时序、上拉电阻、HAL 库延时精度和单总线协议理解这几件事上。这套“HAL 库 DHT11 OLED”的组合在物联网课程设计和毕业设计里出现频率极高属于典型的“看起来简单、装起来玄学”的环境。这篇笔记面向的是手里已经拿到 STM32 开发板、DHT11 模块和 OLED 屏准备从零把工程跑起来的开发者尤其是用 CubeMX 生成 HAL 库工程、不想碰寄存器版本的人。我会把版本选择、工程配置、代码落地、验证方法和翻车救援按顺序写清楚照着走能跑通跑不通也能知道卡在哪一步。2. 环境准备与版本选择STM32CubeMX、HAL 库和 DHT11 模块怎么配2.1 硬件清单与模块差异先把东西列清楚不然后面配 GPIO 的时候容易对不上。这套环境需要的硬件不多但每一件都有版本或型号上的坑。硬件常见型号/规格注意点主控板STM32F103C8T6 最小系统板蓝色药丸板最常见注意晶振是 8MHz温湿度传感器DHT11 三针/四针模块三针模块自带 4.7k 上拉四针裸传感器要自己加显示模块0.96 寸 OLEDSSD1306 驱动分 I2C 和 SPI 两种I2C 接线少但地址要确认下载器ST-Link V2注意驱动装好设备管理器里能看到杜邦线母对母若干DHT11 数据线尽量短长了容易受干扰DHT11 有两种常见形态一种是裸的四针传感器DATA 引脚是开漏输出必须外接一个 4.7kΩ 到 10kΩ 的上拉电阻到 3.3V另一种是三针模块板上已经集成了上拉电阻直接插线就行。我一般会优先用三针模块省去焊电阻的麻烦也减少接触不良的概率。OLED 这边I2C 版本只需要 SCL、SDA 两根信号线地址通常是0x788 位写地址或0x3C7 位地址不同厂家写法不一样后面初始化失败多半是地址搞错了。2.2 软件版本选择与安装软件侧需要三样东西STM32CubeMX、对应的 HAL 库固件包、以及一个能编译下载的 IDE。版本选择上我踩过的坑是 CubeMX 版本和固件包版本不匹配生成代码时直接报错。STM32CubeMX建议用 6.10 或 6.11 这类较新的稳定版。6.x 系列对 F1 系列支持很成熟界面也比 5.x 顺手。安装时注意 Java 环境Windows 下安装包自带 JRELinux 下要自己装 OpenJDK 11 以上。HAL 固件包在 CubeMX 里通过Help - Manage embedded software packages下载STM32F1系列的最新版比如STM32Cube FW_F1 V1.8.5。不要用太老的 1.6 以下版本HAL 库的HAL_Delay和HAL_GetTick在旧版里有精度问题。IDEKeil MDK 5 或者 STM32CubeIDE 都行。CubeIDE 免费且和 CubeMX 集成好我一般推荐新手直接用 CubeIDE省去 Keil 的 Pack 安装和 License 问题。安装 CubeMX 时有个细节路径不要带中文和空格否则生成代码时可能找不到固件包。CubeIDE 安装同理工作空间路径也保持纯英文。这些看起来是小事但每年都有人的工程因为路径问题编译不过。2.3 CubeMX 工程创建与时钟配置打开 CubeMX新建工程在Part Number里搜STM32F103C8选中后开始配置。第一步是RCC设置把High Speed Clock (HSE)选为Crystal/Ceramic Resonator因为最小系统板外部是 8MHz 晶振。然后进Clock Configuration标签把HCLK拉到 72MHzCubeMX 会自动算好分频系数。这一步不做的话后面HAL_Delay的延时基准不对DHT11 的微秒级延时全乱。# 时钟配置要点在 CubeMX 界面操作这里用文字记录参数 # HSE: Crystal/Ceramic Resonator # PLL Source: HSE # PLL Mul: 9 # AHB Prescaler: 1 # APB1 Prescaler: 2 # APB2 Prescaler: 1 # 最终 HCLK 8MHz * 9 72MHz上面这组参数是 F103 跑 72MHz 的标准配法。PLL 倍频 9 倍AHB 不分频APB1 二分频得到 36MHz因为 APB1 最高 36MHzAPB2 不分频得到 72MHz。时钟配错的表现是串口乱码或者延时明显偏长DHT11 读取时机会全部错位。2.4 GPIO 与 I2C 外设配置DHT11 的数据引脚我一般接在PA0配置为GPIO_Output开漏输出初始电平高。为什么用开漏因为 DHT11 是单总线协议主机和从机要分时驱动同一条线开漏加上拉才能实现“线与”逻辑。如果你用的是三针模块模块内部已经有上拉CubeMX 里仍然要把引脚配成Output Open Drain。OLED 的 I2C 我接在PB6SCL和PB7SDA在 CubeMX 里把I2C1设为I2C模式速度用Standard Mode100kHz 就行SSD1306 支持 400kHz 但长线容易出错100kHz 更稳。配置完后检查GPIO Settings确认 I2C 引脚自动变成了Alternate Function Open Drain。// CubeMX 生成的 GPIO 初始化片段在 gpio.c 中 GPIO_InitStruct.Pin GPIO_PIN_0; GPIO_InitStruct.Mode GPIO_MODE_OUTPUT_OD; // 开漏输出 GPIO_InitStruct.Pull GPIO_PULLUP; // 内部上拉外部再加 4.7k 更稳 GPIO_InitStruct.Speed GPIO_SPEED_FREQ_HIGH; HAL_GPIO_Init(GPIOA, GPIO_InitStruct);这段代码里GPIO_MODE_OUTPUT_OD是关键配成推挽输出的话DHT11 拉低总线时主机会和从机打架读出来的数据全是错的。Pull选GPIO_PULLUP启用内部上拉但内部上拉电阻较大约 40k建议外部再并一个 4.7k 到 3.3V。3. DHT11 驱动代码落地微秒延时、单总线时序与数据校验3.1 微秒延时函数的实现HAL 库自带的HAL_Delay是毫秒级DHT11 的时序要求微秒级所以必须自己写一个delay_us。最常用的做法是用 SysTick 或者 DWT 计数器。我一般用 DWT因为不占用定时器资源精度也够。// dht11.c 中的微秒延时基于 DWT void DHT11_Delay_us(uint32_t us) { uint32_t start DWT-CYCCNT; uint32_t ticks us * (SystemCoreClock / 1000000); while ((DWT-CYCCNT - start) ticks); }DWT-CYCCNT是 Cortex-M3 内核的数据观察点计数器每个时钟周期加一。SystemCoreClock是 72MHz所以 1 微秒对应 72 个 tick。用之前要在main里使能 DWT// main.c 中使能 DWT CoreDebug-DEMCR | CoreDebug_DEMCR_TRCENA_Msk; DWT-CYCCNT 0; DWT-CTRL | DWT_CTRL_CYCCNTENA_Msk;这三行不写的话DWT-CYCCNT永远是 0delay_us会直接死循环。很多人卡在这里以为传感器坏了其实是 DWT 没开。3.2 单总线起始信号与响应检测DHT11 的通信流程是主机拉低总线至少 18ms然后拉高 20-40us接着释放总线等待 DHT11 响应。响应信号是 DHT11 先拉低 80us再拉高 80us之后开始传 40 位数据。// 主机发送起始信号 void DHT11_Start(void) { DHT11_Set_Pin_Output(); // 切换为输出 HAL_GPIO_WritePin(DHT11_PORT, DHT11_PIN, GPIO_PIN_RESET); HAL_Delay(20); // 拉低至少 18ms这里给 20ms HAL_GPIO_WritePin(DHT11_PORT, DHT11_PIN, GPIO_PIN_SET); DHT11_Delay_us(30); // 拉高 30us DHT11_Set_Pin_Input(); // 切换为输入释放总线 }HAL_Delay(20)是毫秒级这里用 HAL 库自带的就行因为 18ms 的要求不严格。拉高 30us 后必须把引脚切成输入模式否则主机会一直驱动总线DHT11 没法拉低回应。切换输入的函数就是改 GPIO 模式void DHT11_Set_Pin_Input(void) { GPIO_InitTypeDef GPIO_InitStruct {0}; GPIO_InitStruct.Pin DHT11_PIN; GPIO_InitStruct.Mode GPIO_MODE_INPUT; GPIO_InitStruct.Pull GPIO_PULLUP; HAL_GPIO_Init(DHT11_PORT, GPIO_InitStruct); }3.3 40 位数据的读取与校验DHT11 的 40 位数据是8 位湿度整数 8 位湿度小数 8 位温度整数 8 位温度小数 8 位校验和。校验和等于前四个字节相加的低 8 位。读取每一位的逻辑是等待 50us 低电平结束然后高电平持续 26-28us 表示 0持续 70us 表示 1。// 读取一个字节 uint8_t DHT11_Read_Byte(void) { uint8_t byte 0; for (int i 0; i 8; i) { while (HAL_GPIO_ReadPin(DHT11_PORT, DHT11_PIN) GPIO_PIN_RESET); // 等低电平结束 DHT11_Delay_us(40); // 延时 40us 后采样 byte 1; if (HAL_GPIO_ReadPin(DHT11_PORT, DHT11_PIN) GPIO_PIN_SET) { byte | 1; } while (HAL_GPIO_ReadPin(DHT11_PORT, DHT11_PIN) GPIO_PIN_SET); // 等高电平结束 } return byte; }DHT11_Delay_us(40)是采样点因为 0 的高电平只有 26-28us40us 后已经变低1 的高电平有 70us40us 时还是高。这个 40us 是经验值不同批次的 DHT11 可能有几微秒偏差如果读出来全是 0 或全是 1可以把 40 改成 35 或 45 试试。读完 5 个字节后做校验uint8_t checksum humi_int humi_dec temp_int temp_dec; if (checksum data[4]) { // 数据有效 } else { // 校验失败丢弃本次数据 }校验失败是常态尤其是杜邦线接触不良或者电源不稳的时候。我的做法是连续读三次取校验通过的那次三次都失败就返回错误码OLED 上显示Error。3.4 读取失败的重试与超时处理上面的while循环没有超时如果 DHT11 没接好程序会死在那里。生产环境里必须加超时计数uint32_t timeout 0; while (HAL_GPIO_ReadPin(DHT11_PORT, DHT11_PIN) GPIO_PIN_RESET) { if (timeout 10000) return 0; // 超时返回错误 }timeout的阈值根据主频调整72MHz 下 10000 次循环大约几毫秒足够覆盖正常的 80us 响应。超时后直接返回错误让上层决定是重试还是报错。这个改动看起来小但能让你的程序在传感器掉线时不会卡死OLED 刷新也不会停。4. OLED 显示驱动SSD1306 初始化、字库与刷新逻辑4.1 I2C 地址确认与初始化序列SSD1306 的 I2C 地址有0x78和0x7A两种常见写法取决于模块上SA0引脚的电平。0x78是 8 位写地址对应 7 位地址0x3C0x7A对应0x3D。HAL 库的HAL_I2C_Master_Transmit用的是 8 位地址所以直接填0x78或0x7A。#define OLED_ADDR 0x78 // 如果屏幕不亮改成 0x7A 试试 void OLED_Init(void) { HAL_Delay(100); // 上电后等 100msSSD1306 需要稳定时间 uint8_t cmd[] { 0xAE, // 关闭显示 0x20, 0x00, // 水平寻址模式 0xB0, // 页地址 0xC8, // COM 扫描方向 0x00, 0x10, // 列地址低位和高位 0x40, // 起始行 0x81, 0xFF, // 对比度 0xA1, // 段重映射 0xA6, // 正常显示 0xA8, 0x3F, // 多路复用比 0xA4, // 输出跟随 RAM 0xD3, 0x00, // 显示偏移 0xD5, 0x80, // 时钟分频 0xD9, 0xF1, // 预充电周期 0xDA, 0x12, // COM 硬件配置 0xDB, 0x40, // VCOMH 电压 0x8D, 0x14, // 电荷泵使能 0xAF // 开启显示 }; HAL_I2C_Master_Transmit(hi2c1, OLED_ADDR, cmd, sizeof(cmd), 100); }这段初始化序列里0x8D, 0x14是电荷泵使能不写的话屏幕可能亮但很暗或者完全不亮。0xAF是最后开启显示放在最后。如果屏幕一直不亮先确认地址再确认0x8D后面跟的是0x14而不是0x10。4.2 字库取模与显示函数OLED 显示文字需要字库常用的是 8x16 和 6x8 两种 ASCII 字库。取模软件用 PCtoLCD2002设置成“阴码、逐列式、顺向”生成的字库数组直接放到oledfont.h里。// 显示一个 8x16 的字符 void OLED_ShowChar(uint8_t x, uint8_t y, char chr) { uint8_t c chr - ; // 计算字库偏移 for (uint8_t i 0; i 8; i) { OLED_Write_Data(F8X16[c * 16 i]); // 上半部分 OLED_Write_Data(F8X16[c * 16 i 8]); // 下半部分 } }F8X16是字库数组每个字符占 16 字节前 8 字节是上半页后 8 字节是下半页。OLED_Write_Data就是往 I2C 发数据前面加0x40控制字节表示数据模式。显示温湿度的时候先把数值转成字符串char buf[20]; sprintf(buf, Temp: %d.%d C, temp_int, temp_dec); OLED_ShowString(0, 0, (uint8_t *)buf); sprintf(buf, Humi: %d.%d %%, humi_int, humi_dec); OLED_ShowString(0, 2, (uint8_t *)buf);sprintf在单片机里比较占资源但 F103C8T6 的 Flash 和 RAM 够用。如果嫌大可以用itoa自己拼字符串。4.3 刷新频率与 I2C 速度的取舍DHT11 的采样周期不能低于 1 秒所以 OLED 刷新也跟着 1 秒一次就行。I2C 速度用 100kHz 时刷一屏 128x64 大约需要 20-30ms完全来得及。如果 I2C 配到 400kHz刷新更快但杜邦线长的时候容易出错表现为屏幕局部花屏或者随机闪烁。我一般先用 100kHz 跑通再根据线长决定要不要提速。// 主循环中的刷新逻辑 while (1) { if (DHT11_Read_Data(temp_int, temp_dec, humi_int, humi_dec) 0) { OLED_Clear(); sprintf(buf, Temp: %d.%d C, temp_int, temp_dec); OLED_ShowString(0, 0, (uint8_t *)buf); sprintf(buf, Humi: %d.%d %%, humi_int, humi_dec); OLED_ShowString(0, 2, (uint8_t *)buf); } else { OLED_ShowString(0, 4, (uint8_t *)Sensor Error); } HAL_Delay(1000); // 1 秒刷新一次 }OLED_Clear每次刷新前清屏避免旧数据残留。如果不想闪屏可以只更新变化的部分但代码会复杂一些新手先用清屏方案。5. 避坑与排查DHT11 读数为 255、OLED 不亮、程序卡死的处理5.1 读数全是 255 或 0现象串口打印出来的温湿度全是255或者0校验和永远不对。原因最常见的是上拉电阻缺失或阻值不对。三针模块一般自带 4.7k 上拉但如果你的模块是裸传感器DATA 引脚没有上拉总线在空闲时不是高电平DHT11 无法拉低回应。另一个原因是delay_us不准采样点偏移。解决先用万用表量 DATA 引脚在空闲时是不是 3.3V如果不是加一个 4.7k 电阻到 3.3V。然后检查 DWT 是否使能DWT-CYCCNT是否在变化。如果都正常把DHT11_Delay_us(40)改成 35 或 45 试几次。5.2 OLED 完全不亮现象OLED 接上后没有任何显示背光也不亮。原因I2C 地址错误占一半初始化序列里电荷泵没使能占三成剩下的是接线问题。SSD1306 的地址0x78和0x7A很容易搞混尤其是模块背面丝印不清晰的时候。解决写一个 I2C 扫描程序遍历0x00到0x7F看哪个地址有应答。确认地址后检查初始化序列里有没有0x8D, 0x14。如果屏幕微亮但不显示内容多半是对比度或电荷泵问题。5.3 程序卡死在 while 等待循环现象程序下载后卡在DHT11_Read_Byte的while里看门狗复位或者直接死机。原因DHT11 没接好或者供电不足总线一直是被拉低状态while等不到高电平。另外如果 DHT11 的 VCC 接的是 5V 而 STM32 是 3.3V数据线电平不匹配也会读不到。解决给所有while等待加超时计数超时返回错误。确认 DHT11 的 VCC 是 3.3V 还是 5V如果是 5V 供电数据线要加电平转换或者串一个 1k 电阻。杜邦线换短的接触不良也会导致这个问题。5.4 校验和偶尔失败现象大部分时候读数正常但每隔几次就有一次校验失败OLED 上闪一下Error。原因DHT11 的采样间隔太短。DHT11 内部有个 1Hz 的采样周期如果两次读取间隔小于 1 秒第二次读到的可能是上一次的残留数据或者直接失败。解决主循环里HAL_Delay(1000)不能省至少等 1 秒再读下一次。如果还是偶尔失败连续读三次取有效值三次都失败才报错。这个策略在实际项目里很实用能过滤掉大部分偶发干扰。5.5 CubeMX 重新生成代码后用户代码丢失现象在 CubeMX 里改了个引脚配置重新生成代码发现自己写的 DHT11 和 OLED 代码全没了。原因CubeMX 只会保留/* USER CODE BEGIN */和/* USER CODE END */之间的内容写在别处的代码会被覆盖。解决所有用户代码都写在USER CODE注释块里。驱动文件单独建dht11.c和oled.c在main.c里只调用函数这样重新生成也不会丢。这个习惯我从第一次被覆盖后就养成了血泪经验。6. 装完怎么验证跑通从串口打印到 OLED 稳定显示的检查清单验证这套环境是否真正跑通不能只看 OLED 亮没亮要按信号链一层层确认。我一般会走一遍下面这个清单每一步都有明确的通过标准。检查项方法通过标准时钟配置串口打印SystemCoreClock输出 72000000DWT 延时翻转 GPIO示波器或逻辑分析仪测1us 延时误差小于 10%DHT11 响应读起始信号后的电平能看到 80us 低 80us 高数据校验连续读 10 次统计校验通过率通过率 90% 以上OLED 地址I2C 扫描在 0x78 或 0x7A 有应答显示刷新观察 1 分钟无花屏、无闪烁、数值随环境变化串口打印是最直接的验证手段。在main里加一句printf(SystemCoreClock: %lu\n, SystemCoreClock);重定向fputc到 USART1波特率 115200。如果打印出来不是 72000000说明时钟树配错了后面所有延时都会偏。DHT11 的响应信号用逻辑分析仪看最清楚没有的话可以用一个简单的示波器甚至用另一个 GPIO 翻转来间接判断。我一般会在DHT11_Start之后加一句翻转 LED 的代码如果 LED 闪了说明程序走到了响应检测没闪就是卡在起始信号之前。校验通过率的统计很能说明问题。写一个循环读 10 次每次把校验结果通过串口打印出来。如果 10 次里失败超过 3 次就要检查上拉电阻和线长。我遇到过一根 20cm 的杜邦线导致通过率只有 50%换成 10cm 后直接到 100%。OLED 的稳定性要观察一段时间。有些屏幕刚上电正常过几分钟开始花屏这是 I2C 速度太快或者电源纹波太大。把 I2C 降到 100kHzVCC 和 GND 之间并一个 100nF 电容基本能解决。最后一步是让整个系统连续跑 10 分钟OLED 上的温湿度数值应该随环境缓慢变化比如用手捂住 DHT11湿度会上升温度会向体温靠拢。如果数值一动不动检查DHT11_Read_Data的返回值是不是一直被忽略OLED 显示的是初始值而不是实时值。从那以后我每次搭这套环境都会先跑一遍串口打印时钟和 DWT 延时再上 DHT11 和 OLED。这个顺序能让我在 10 分钟内定位是配置问题还是硬件问题而不是对着不亮的屏幕瞎猜。希望帮到你。本文还有配套的精品资源点击获取
RELATED

相关推荐

OpenClaw人人养虾实战:技能配置与智能体自动化架构解析

OpenClaw人人养虾实战:技能配置与智能体自动化架构解析

1. 项目缘起与整体思路先说结论:OpenClaw 这套“人人养虾”的玩法,本质上不是养虾,而是把智能体的“可扩展能力”拆成了一个个标准化的技能模块,然后通过配置把它们组装起来。标题里最值钱的两个字是“人人”——意思是这套东西的…

📅 2026/10/16 4:16:37
脑机接口的“事件视界”:技术现状与长寿可能性探析

脑机接口的“事件视界”:技术现状与长寿可能性探析

1. 从“事件视界”说起:为什么脑机接口成了长寿叙事的核心变量我最早看到“第一批活到1000岁的人可能已经诞生”这句话时,第一反应是嗤之以鼻。活到1000岁,这不是科幻小说里的设定吗?但顺着“脑机接口的事件视界”这个角度琢磨了一…

📅 2026/10/16 4:11:37
微博发布自动化实战:从选题规划到定时发布与数据复盘

微博发布自动化实战:从选题规划到定时发布与数据复盘

微博发布这件事,放在运营工作里,常被当成最没技术含量的一步:打开输入框,打字,点发送。但如果你需要让一个账号每天稳定输出、持续更新,很快就会意识到——发,从来不是问题;发什么、…

📅 2026/10/16 4:11:37
MORE NEWS

更多资讯

📰

Spring Boot + Vue打造社区快递管理系统:从开题到落地全解析

做毕业设计或者课程项目开题的时候,最怕的就是题目选得又大又空,写起来没边,答辩的时候被老师一问就露馅。相比之下,“城南逸居快递之家管理系统”这种带着真实场景的题目,反而是很好落地的那种,因为它天然…

📰

告别404:公众号文章一键批量导出HTML与PDF的本地备份指南

做内容的人最怕的不是没人看,而是自己辛辛苦苦收藏的干货,某天忽然变成一个冷冰冰的 404。尤其是公众号里的文章,删除、迁移、账号异常、运营者手滑,任何一个小环节出问题,一篇文章就可能再也找不回来。我见过太多人等…

📰

STM32H7虚拟U盘方案:SDMMC+FATFS+USBMSC+FreeRTOS四件套实战

简介:面向嵌入式开发者的 STM32H7 虚拟U盘完整工程示例,集成 SDMMC、FATFS、USBMSC 与 FreeRTOS 四大模块,展示从 SD 卡高速读写到 USB Mass Storage 枚举的完整链路,适合学习存储接口、文件系统、USB 协议栈及多任务调度的中高级…

📰

JavaScript原型链一图搞懂:家族谱系法解析prototype与继承

1. 先聊聊“家族关系”这个比喻怎么来的JavaScript 的原型和原型链,几乎每个前端开发者在学了一段时间之后都会撞上这堵墙。你搜索“原型链面试题”,能翻出一大堆笔记和图解,但很多人看完依然是一脸茫然——prototype、__proto__、constructo…

📰

Java八大基本数据类型全解析:从内存占用到面试避坑

有些朋友学了几天 Java,还在犹豫先背知识点还是先敲代码,我觉得这问题本身就问反了。数据类型的核心用法就那么几条,你先把 8 大基本类型的特性、占位大小、取值范围、转换规则搞明白,后面读源码、调 bug、刷面试题都会顺手很多。…

📰

Spring Boot三层架构核心解析:职责边界、事务控制与常见坑

1. 三层架构的真面目:不只是分包那么简单我刚带项目组那会儿,最常被新人问的一个问题就是:“这代码到底该往哪一层写?”很多同学能背出“Controller是控制层、Service是业务层、Mapper是数据层”,可真到写代码的时候&a…

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

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

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

📞 💬