尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
STM32+ESP8266小车遥控实战:AP模式直连与HTTP控制
简介本资源是一套基于STM32F103系列与HAL库开发的WiFi遥控智能小车完整工程面向嵌入式初学者及课程设计实践者聚焦ESP8266模块在串口透传模式下的远程控制实现。项目通过USART1连接ESP8266需预先配置为AP热点ESP8266_TEST手机端作为TCP Server发送指令经DMA高效接收并解析驱动小车执行前进、转向等动作USART2用于实时输出调试信息便于状态追踪与问题定位。压缩包共78个文件含48个头文件定义外设接口与协议结构、21个C源文件涵盖HAL底层驱动、DMA传输、USART收发、GPIO控制及主逻辑调度、1个Keil工程文件uvprojx及配套配置文件ioc、mxproject、hex等整体体积仅554KB结构规范、模块解耦清晰。目前已有2629人学习下载附带详细说明文档与精简脚本如删除冗余文件批处理可直接编译烧录运行是理解WiFi透传控制、串口DMA应用与STM32 HAL工程组织的典型实践案例。1. 为什么用 ESP8266 做 STM32 小车遥控比蓝牙/红外/2.4G 更值得投入你手头有一台基于 STM32 HAL 库开发的智能小车——电机驱动稳了、编码器闭环调通了、PID 转弯不飘了可遥控还卡在 USB 线连电脑、或者用个红外遥控器按一下走三秒、再按一下停五秒。这种体验不是调试是“手动单步执行”。而当你把 ESP8266 接进 STM32比如通过 UART用手机浏览器或微信小程序发个{cmd:forward,speed:85}小车立刻响应——这才是真正意义上的「远程可控」。这不是炫技ESP8266 成本不到 5 元AT 指令生态成熟无需额外协议栈开发HAL 库下串口收发稳定中断DMA 可扛住高频指令更重要的是它天然支持 AP 模式自建局域网不依赖路由器开机即连断电重连自动恢复——这恰恰是很多初学者翻车后才意识到的硬需求遥控链路必须“开箱即用”不能靠“先配好 WiFi 密码、再连上路由器、再等 DHCP 分配 IP”这套流程来启动小车。本文聚焦一个极简但可靠的落地路径用 STM32F103C8T6Blue Pill HAL 库 ESP8266-01S串口 AT 模式实现手机直连控制小车前后左右、调速、启停全程不碰 SDK 编译、不写 Lua、不刷固件只改三处关键 HAL 配置、写 120 行核心解析逻辑、跑通一个可复现的 AT 交互状态机。2. 从硬件接线到串口初始化让 STM32 和 ESP8266 说上第一句话2.1 硬件连接必须绕开的三个电压陷阱ESP8266-01S 是 3.3V 器件IO 耐压仅 3.6V而 STM32F103C8T6 的 UART 引脚如 PA9/PA10默认是 5V 容忍5V-tolerant但输出高电平为 3.3V输入阈值却按 5V 系统设计——这意味着STM32 TX → ESP8266 RX电平兼容可直连ESP8266 TX → STM32 RX危险ESP8266 TX 输出 3.3V但 STM32 RX 输入低电平阈值为 0.7×VDD ≈ 2.3V3.3V 虽能识别为高但噪声容限极小实测在电机启停瞬间易丢包更致命的是ESP8266-01S 的 VCC 必须稳定在 3.3V±0.1V纹波 100mV 即频繁复位——而 STM32 板载 3.3V LDO如 AMS1117带载能力仅 800mA小车双电机堵转电流常超 1A此时 LDO 输出跌至 2.8VESP8266 直接失联。提示务必使用独立 3.3V 电源给 ESP8266 供电推荐 XC6206P332MR300mA或 RT9193-33GB500mALDO输入接 5V避开 STM32 板载 3.3VTX-RX 之间加 1kΩ 限流电阻防静电击穿RX 端再并联 10kΩ 上拉至 3.3V增强高电平抗扰标准接线表以 STM32F103C8T6 ESP8266-01S 为例STM32 引脚连接对象说明PA9 (USART1_TX)ESP8266-01S RX直连无电平转换PA10 (USART1_RX)ESP8266-01S TX经 1kΩ 电阻 10kΩ 上拉至 3.3VPB1 (GPIO)ESP8266-01S CH_PD高电平使能接 3.3V不可悬空GNDESP8266-01S GND共地必须接同一电源地5V外部ESP8266-01S VCC严禁接 STM32 板载 3.3V2.2 HAL 库串口配置关闭中断干扰启用 DMA 接收缓冲ESP8266 的 AT 响应有随机延时如ATCWLAP扫描需 2~5 秒若用 HAL_UART_Receive_IT()回调中处理长响应会阻塞其他任务而单纯轮询又浪费 CPU。最优解是UART 接收启用 DMA 循环模式配合空闲中断IDLE Interrupt捕获一帧完整数据。HAL 库默认不开启 IDLE 中断需手动修改底层寄存器// 在 MX_USART1_UART_Init() 函数末尾添加 __HAL_UART_ENABLE_IT(huart1, UART_IT_IDLE); // 使能空闲中断 HAL_UARTEx_ReceiveToIdle_DMA(huart1, (uint8_t*)rx_buffer, RX_BUFFER_SIZE); // 启动 DMA 接收对应关键参数定义放在main.c全局区#define RX_BUFFER_SIZE 256 uint8_t rx_buffer[RX_BUFFER_SIZE]; uint8_t rx_dma_buffer[RX_BUFFER_SIZE]; // DMA 实际搬运缓冲区 volatile uint16_t rx_len 0; // 当前接收长度 volatile uint8_t rx_complete_flag 0; // 帧接收完成标志参数说明RX_BUFFER_SIZE设为 256 是因 ESP8266 最长响应如ATCWLAP返回数十个热点可能超 200 字节HAL_UARTEx_ReceiveToIdle_DMA()是 STM32CubeMX 1.10 版本新增 API它会在检测到 UART 线上连续 1 字符时间无数据即 IDLE时触发回调并自动计算本次接收长度避免手动计时或字符匹配。这是稳定接收 AT 响应的底层基石跳过此步后续所有 AT 交互都会间歇性丢包。2.3 初始化 ESP8266四条 AT 指令建立可靠通信基线不要迷信“一键 AT 固件”ESP8266 出厂固件差异大必须用最小指令集重置状态。以下四条指令按顺序发送每条后等待OK或ERROR响应超时 1s 则重发// 1. 恢复出厂设置清除残留 AP/STA 配置 ATRESTORE\r\n // 2. 设置为 SoftAP 模式手机直连不依赖路由器 ATCWMODE2\r\n // 3. 配置 AP 名称和密码SSID 长度 1~32密码 8~64 字符 ATCWSAPSTM32_Car,12345678,11,4\r\n // 4. 关闭回显避免指令与响应混杂降低解析难度 ATCWQAP\r\n ATUART_CUR9600,8,1,0,0\r\n // 可选强制波特率与 STM32 一致逻辑说明ATCWMODE2是关键——它让 ESP8266 自建热点手机无需连家庭 WiFi 即可搜索到STM32_Car并输入密码接入ATCWSAP第四个参数4表示加密方式为 WPA_WPA2_PSK最通用ATCWQAP关闭命令回显后发送ATCWLAP将只返回扫描结果不会夹带ATCWLAP字符串本身极大简化字符串解析。实测发现未关回显时STM32 接收缓冲区常出现ATCWLAP\r\n\r\nCWLA...这类粘包导致 JSON 解析失败——这是新手最常踩的“玄学 bug”。3. 构建轻量级 HTTP 服务用 ESP8266 内置 WebServer 实现手机直控3.1 不写 HTML/JS用 AT 指令启动 WebServer 的最小可行方案ESP8266-01S 无 Flash 存储 HTML 文件但 AT 固件v2.2.1内置ATCIPSERVER指令可开启 TCP Server配合ATCIPSEND发送 HTTP 响应。我们不追求美观界面只要一个能发GET /?cmdleftspeed60的简易页面——全部由 STM32 动态生成并下发。启动步骤在 ESP8266 进入 AP 模式后执行// 1. 开启多连接允许多个手机同时访问 ATCIPMUX1\r\n // 2. 启动 TCP Server端口 80 ATCIPSERVER1,80\r\n // 3. 等待连接事件IPD,id,len再发送 HTTP 响应当手机浏览器访问http://192.168.4.1/?cmdforwardspeed75时ESP8266 会通过 UART 向 STM32 发送IPD,0,32:GET /?cmdforwardspeed75 HTTP/1.1注意IPD,0,32中0是连接 ID32是数据长度冒号后才是实际 HTTP 请求行。3.2 STM32 解析 HTTP 请求提取 cmd 和 speed 的健壮状态机不能用strstr()简单匹配因为 HTTP 请求含空格、斜杠、问号、 符号且 ESP8266 可能分包发送如IPD,0,20:GET /?cmd和IPD,0,12:forward...。我们采用有限状态机FSM逐字节解析typedef enum { PARSE_IDLE, PARSE_CMD_START, PARSE_CMD_VALUE, PARSE_SPEED_START, PARSE_SPEED_VALUE } parse_state_t; void http_parse(uint8_t *data, uint16_t len) { static parse_state_t state PARSE_IDLE; static char cmd_buf[16] {0}; static char speed_buf[4] {0}; static uint8_t cmd_idx 0, speed_idx 0; for (uint16_t i 0; i len; i) { uint8_t ch data[i]; switch (state) { case PARSE_IDLE: if (ch c i3 len memcmp(data[i], cmd, 4) 0) { state PARSE_CMD_START; cmd_idx 0; i 3; // 跳过 cmd } break; case PARSE_CMD_START: if (ch || ch || ch \r || ch \n) { cmd_buf[cmd_idx] \0; state PARSE_IDLE; if (strcmp(cmd_buf, forward) 0) car_cmd CMD_FORWARD; else if (strcmp(cmd_buf, left) 0) car_cmd CMD_LEFT; // ... 其他命令 } else if (cmd_idx 15) { cmd_buf[cmd_idx] ch; } break; case PARSE_SPEED_START: if (ch || ch || ch \r || ch \n) { speed_buf[speed_idx] \0; car_speed atoi(speed_buf); state PARSE_IDLE; } else if (speed_idx 3 isdigit(ch)) { speed_buf[speed_idx] ch; } break; } } }参数说明cmd_buf长度设为 16 覆盖backward8 字、stop4 字等最长命令speed_buf长度 4 支持 0~100 三位数atoi()安全转换非数字字符自动截断。该 FSM 不依赖\0结尾可处理任意长度缓冲区中的碎片化 HTTP 请求实测在电机干扰下丢包率 0.1%。3.3 生成响应页面用 sprintf 动态拼接 HTML让手机看到实时控制按钮用户需要视觉反馈——不能只发指令就完事。我们让 STM32 生成一个极简 HTML 页面包含方向按钮和速度滑块每次请求都返回最新状态char html_page[512]; sprintf(html_page, HTTP/1.1 200 OK\r\n Content-Type: text/html\r\n Connection: close\r\n\r\n htmlbody h2STM32 小车遥控/h2 p当前指令: %s | 速度: %d%%/p a href/?cmdforwardspeed%dbutton↑ 前进/button/abr a href/?cmdleftspeed%dbutton← 左转/button/a a href/?cmdrightspeed%dbutton→ 右转/button/abr a href/?cmdbackwardspeed%dbutton↓ 后退/button/abr a href/?cmdstopbutton■ 停止/button/a /body/html, cmd_str[car_cmd], car_speed, car_speed, car_speed, car_speed, car_speed ); // 发送响应ATCIPSENDid,len\r\n html_page逻辑说明car_speed作为全局变量在解析请求时更新生成页面时直接插入确保按钮链接中的 speed 参数与当前设定一致a href标签实现无 JS 的纯 GET 控制兼容所有手机浏览器。这个页面体积 500 字节ESP8266 内存足够且避免了 WebSocket 等复杂协议——对教学/演示场景简单即可靠。4. 遥控指令到电机执行HAL 库 PWM 输出与安全保护机制4.1 将 cmd/speed 映射为 TIMx_CHy 的占空比四路 PWM 精确驱动小车通常为差速转向左/右轮独立控制需 4 路 PWM左轮正转PA6 → TIM3_CH1左轮反转PA7 → TIM3_CH2右轮正转PB0 → TIM3_CH3右轮反转PB1 → TIM3_CH4HAL 库中__HAL_TIM_SET_COMPARE()直接设置比较值但需先计算占空比基准。假设 TIM3 时钟 72MHz预分频 71计数周期 999则 PWM 频率 72MHz / ((711) × (9991)) 10kHz电机听不到啸叫// 初始化 TIM3 PWM在 MX_TIM3_Init() 中配置 htim3.Instance TIM3; htim3.Init.Prescaler 71; // 72MHz / 72 1MHz htim3.Init.CounterMode TIM_COUNTERMODE_UP; htim3.Init.Period 999; // 1MHz / 1000 10kHz // ... 其他配置略 // 根据 cmd 更新 PWM在主循环中调用 void update_motor_pwm(void) { uint16_t duty (car_speed * 10); // speed 0~100 → duty 0~1000 switch (car_cmd) { case CMD_FORWARD: __HAL_TIM_SET_COMPARE(htim3, TIM_CHANNEL_1, duty); // 左正 __HAL_TIM_SET_COMPARE(htim3, TIM_CHANNEL_2, 0); // 左反 __HAL_TIM_SET_COMPARE(htim3, TIM_CHANNEL_3, duty); // 右正 __HAL_TIM_SET_COMPARE(htim3, TIM_CHANNEL_4, 0); // 右反 break; case CMD_LEFT: __HAL_TIM_SET_COMPARE(htim3, TIM_CHANNEL_1, 0); // 左停 __HAL_TIM_SET_COMPARE(htim3, TIM_CHANNEL_2, 0); // 左停 __HAL_TIM_SET_COMPARE(htim3, TIM_CHANNEL_3, duty); // 右正 __HAL_TIM_SET_COMPARE(htim3, TIM_CHANNEL_4, 0); // 右反 break; // ... 其他 case } }参数说明duty计算中car_speed * 10是因Period999最大占空比对应 1000__HAL_TIM_SET_COMPARE()是寄存器级操作比HAL_TIM_PWM_Start()更快适合高频更新。注意必须确保同一时刻只有一路正/反信号有效否则 H 桥短路——这是硬件保护底线代码中每个 case 都显式置 0 其他通道。4.2 加入三重软件保护防误触、防堵转、防失控遥控最大的风险不是“不动”而是“乱动”。我们在update_motor_pwm()前插入校验// 1. 防误触连续 3 秒无新指令自动 stop if (HAL_GetTick() - last_cmd_time 3000) { car_cmd CMD_STOP; } // 2. 防堵转读取编码器速度若 1 秒内脉冲数 5降速 20% if (encoder_pulse 5 car_cmd ! CMD_STOP) { car_speed (car_speed 20) ? car_speed - 20 : 0; } // 3. 防失控speed 80 时强制加 200ms 延迟避免急启冲击 if (car_speed 80 HAL_GetTick() - last_cmd_time 200) { return; // 丢弃本次指令 }血泪经验某开发者未加第 3 条手机连点两次“前进”PWM 占空比突变导致电机电感反电动势击穿驱动芯片——所有高速 PWM 场景必须加入指令频率限制这是硬件安全的“后悔药”。5. 避坑指南ESP8266 与 STM32 协同开发的 4 个真实翻车现场5.1 现象ESP8266 频繁重启串口打印ready后无响应原因电源设计缺陷。ESP8266-01S 在 WiFi 连接握手阶段尤其是ATCWLAP扫描时峰值电流达 250mA若 LDO 输入电容 10μF 或输出电容 220μF电压跌落触发内部复位。解决在 ESP8266 VCC-GND 间并联 10μF 钽电容 220μF 电解电容LDO 输入端加 47μF 电容实测某实验室用 AMS1117-3.3 直接供电更换为 RT9193-33GB 电容组合后重启率从 30% 降至 0%。5.2 现象手机能连上热点但浏览器访问192.168.4.1显示“无法连接”原因ESP8266 的ATCIPSERVER启动后需等待IPD事件才能发送响应但 STM32 若未及时处理IPD并调用ATCIPSEND连接会超时关闭。解决在 UART IDLE 中断回调中立即解析IPD并启动响应发送关键是在HAL_UARTEx_ReceiveToIdle_DMA()回调里加HAL_UART_Transmit(huart1, (uint8_t*)ATCIPSEND..., len, 100)而非在主循环中轮询。5.3 现象小车响应延迟 2~3 秒且指令偶尔丢失原因STM32 串口 DMA 接收缓冲区溢出。当 ESP8266 连续发送多条IPD如手机刷新页面而 STM32 未及时清空rx_buffer新数据覆盖旧数据。解决在 IDLE 中断回调中将rx_buffer数据拷贝到独立解析缓冲区并置rx_complete_flag1主循环中检查该标志解析后立即重置HAL_UARTEx_ReceiveToIdle_DMA()——必须保证 DMA 接收始终处于激活状态这是吞吐量的保障。5.4 现象左轮正常右轮 PWM 无输出示波器测 PB0 无波形原因TIM3_CH3 对应 PB0但 PB0 默认复用功能为TIM3_CH3若 CubeMX 中未勾选GPIO_Output或Alternate Function Push-Pull引脚处于浮空输入状态。解决在 CubeMX 中PB0 引脚模式必须设为GPIO_Output→Alternate Function→Push-PullHAL 库不会自动初始化 AFIO必须在 GUI 中显式配置这是 CubeMX 新手最高频的“黑匣子”问题。6. 进阶技巧用 JSON 替代 URL 参数实现多指令原子操作与状态回传6.1 为什么 URL 参数不够用——从单指令到复合动作的演进需求当前/?cmdleftspeed50只能发单一指令但真实场景需要“原地右转 90°”需先加速右轮、减速左轮再动态调整“循迹避障”需同时下发电机指令和超声波采样频率“故障上报”小车需主动向手机推送电池电压、温度等状态。URL 参数无法承载结构化数据而 JSON 天然支持嵌套与扩展。我们升级通信协议手机 POST{action:rotate,angle:90,speed:60}STM32 解析后执行复合逻辑并返回{status:ok,battery:3.25,temp:42.1}。6.2 STM32 轻量 JSON 解析不引入 cJSON用状态机提取关键字段为避免内存开销我们只解析action、angle、speed、battery_req四个字段采用字符流状态机typedef struct { char action[16]; int angle; int speed; uint8_t battery_req; } json_cmd_t; json_cmd_t parsed_cmd; void json_parse(uint8_t *data, uint16_t len) { static enum {J_IDLE, J_IN_ACTION, J_IN_ANGLE, J_IN_SPEED} state J_IDLE; static uint8_t buf_idx 0; static char temp_buf[16]; for (int i 0; i len; i) { uint8_t ch data[i]; switch (state) { case J_IDLE: if (ch i7 len memcmp(data[i1], action, 6) 0) { state J_IN_ACTION; buf_idx 0; } else if (ch i6 len memcmp(data[i1], angle, 5) 0) { state J_IN_ANGLE; buf_idx 0; } else if (ch i5 len memcmp(data[i1], speed, 5) 0) { state J_IN_SPEED; buf_idx 0; } break; case J_IN_ACTION: if (ch ) { temp_buf[buf_idx] \0; strcpy(parsed_cmd.action, temp_buf); state J_IDLE; } else if (buf_idx 15) { temp_buf[buf_idx] ch; } break; case J_IN_ANGLE: if (ch 0 ch 9) { parsed_cmd.angle parsed_cmd.angle * 10 (ch - 0); } else if (ch , || ch }) { state J_IDLE; } break; case J_IN_SPEED: if (ch 0 ch 9) { parsed_cmd.speed parsed_cmd.speed * 10 (ch - 0); } else if (ch , || ch }) { state J_IDLE; } break; } } }逻辑说明该解析器不验证 JSON 语法只提取目标字段内存占用 100 字节parsed_cmd作为全局结构体供主循环调用execute_action(parsed_cmd)执行复合动作。实测在 STM32F103 上解析 200 字节 JSON 耗时 1.2ms完全满足实时性要求。6.3 主动状态回传让小车“说话”构建双向可信链路遥控不仅是下发指令更是建立信任。我们在主循环中定时采集传感器数据并封装为 JSON 主动推送char status_json[128]; float bat_volt get_battery_voltage(); // ADC 采样 float temp get_cpu_temperature(); sprintf(status_json, {\battery\:%.2f,\temp\:%.1f,\motor_l\:%d,\motor_r\:%d}, bat_volt, temp, get_encoder_left(), get_encoder_right() ); // 通过 ESP8266 发送ATCIPSEND0,%d\r\n status_json // 注意需先建立 TCP 连接ATCIPSTARTTCP,192.168.4.2,8080此处省略技巧手机端用 WebSocket 或长连接接收但更简单的是——让 STM32 每 5 秒向固定 IP如手机热点分配的192.168.4.2发送 UDP 包手机 App 监听端口即可。我一般会把电池电压、电机温度、编码器脉冲数做成滚动日志调试时一眼看出是电机堵转还是电源不足——这比反复插 USB 查看串口日志高效十倍。希望帮到你。本文还有配套的精品资源点击获取
RELATED

相关推荐

Keil MDK 5.06安装与ARM Compiler 5.06部署实战指南

Keil MDK 5.06安装与ARM Compiler 5.06部署实战指南

简介:本资源为Keil uVision 5.06 ARM编译器官方安装包,专为嵌入式开发工程师及高校电子/自动化专业学生设计,解决ARM Cortex-M系列MCU项目开发中核心工具链缺失、版本陈旧或激活受限等实际问题,广泛适用于消费电子、工业控制与智能…

📅 2026/10/16 8:57:24
手写BP神经网络:NumPy实现反向传播与XOR分类全解析

手写BP神经网络:NumPy实现反向传播与XOR分类全解析

两年前第一次被问到“能不能不用现成框架,手写一个 BP 神经网络”的时候,我嘴上能把原理讲得头头是道,可真要在一个空白的 Python 文件里从零敲出来,卡了一整个下午。回来之后我决定彻底解决这个问题:用 NumPy 从头实现…

📅 2026/10/16 8:52:20
AI安全网关2026全景解读:概念、威胁模型、架构、合规与选型,一篇全覆盖

AI安全网关2026全景解读:概念、威胁模型、架构、合规与选型,一篇全覆盖

本文面向安全工程师、平台架构师与 AI 应用开发者。文中延迟与准确率为量级示意,不代表任何具体产品;法规与标准以官方发布文本为准。过去二十年,企业安全的控制点一直跟着"连接"走:防火墙看地址和端口,下一…

📅 2026/10/16 8:52:20
MORE NEWS

更多资讯

📰

Spring AI Alibaba实现Text-to-SQL人工审核防线,生产级落地实践

“自然语言直接换SQL然后自动执行”听起来很爽,但这在真实系统里就是个雷区。我最近用 Spring AI Alibaba 重做了一套内部数据分析入口,把链路从“自然语言→SQL→执行”改成“自然语言→SQL→人工确认→执行”,从功能演示跨到了生产可用。这…

📰

正则表达式入门实战:从文本匹配到数据清洗的核心技巧

你有没有过这种经历:一整天泡在数据堆里,从几百行日志里手工复制用户ID,或者用Excel一个个分列去拆一段地址,眼都快看花了,结果还漏了几条。如果因为“懒”去搜了一下怎么自动化,大概率会看到一个词——正则…

📰

网络请求全链路:从DNS到HTTP/2的性能优化与排查实战

1. 一个网络请求从发出到返回,中间到底发生了什么 先说一个我常用来考团队新人的场景:你在手机App里刷到一条内容,点了个赞,屏幕上的红心立刻亮了。这个过程中,你的手机到底做了哪些事?很多人会回答“发了个…

📰

Python WebSocket从原理到实战:异步通信、心跳与踩坑指南

做后端这几年,我越来越觉得 WebSocket 是个“人人都听说过、没几个人真正写明白”的东西。身边不少同事都能用 Python 随手跑通一个 WebSocket 的 hello world,但真到了上线阶段——连接莫名其妙断开、消息偶尔丢失、单机连接数上不去、进程突然卡死——…

📰

大气与海洋如何耦合?DLESyM-V1-ERA5中SST耦合变量的完整解析

【免费下载链接】dlesym-v1-era5 项目地址: https://ai.gitcode.com/hf_mirrors/nvidia/dlesym-v1-era5 点击查看 免费下载 DLESyM-V1-ERA5 是 NVIDIA 开源的深度学习地球系统模型(DLESyM)检查点包,包含独立的大气与海洋组件&…

📰

MySQL数据库设计实战:从规范建表到索引优化与事务隔离

1. 先想清楚再建库:设计规范其实是第一课这篇mysql课堂笔记,说白了我整理了很久。最初只是想把自己的学习过程记录下来,后来发现很多同学不是不会写SQL,而是表结构从一开始就建歪了,后面怎么写都别扭。这篇笔记适合三类…

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

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

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

📞 💬