stm32之软件SPI读写W25Q64存储器应用案例

2024-09-07 09:44

本文主要是介绍stm32之软件SPI读写W25Q64存储器应用案例,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

系列文章目录

1. stm32之SPI通信协议


文章目录

  • 系列文章目录
  • 前言
  • 一、电路接线图
  • 二、应用案例代码
  • 三、应用案例分析
    • 3.1 SPI通信模块
    • 3.2 W25Q64模块
    • 3.3 主程序


前言

提示:本文主要用作在学习江科大自化协STM32入门教程后做的归纳总结笔记,旨在学习记录,如有侵权请联系作者

本案例使用软件SPI通信的方式实现了STM32与W25Q64 Flash存储器的通信,完成了常见的Flash存储器操作如读ID、页写、扇区擦除、读取数据等。


一、电路接线图

下图所示为W25Q64模块硬件接线图,左边是W25Q64模块作为从机,右边是stm32作为主机。为了方便下一章节硬件SPI的接线,这里直接就选择了硬件SPI1外设的接线方式。其中PA4对应主机的从机选择线SPI1_NSS连接到从机的CS引脚,PA5对应主机的时钟同步线SPI1_SCK连接到从机的CLK引脚,PA6对应主机的主机输入从机输出线SPI1_MISO连接到从机的DO引脚,PA7对应主机的主机输出从机输入线SPI1_MOSI连接到从机的DI引脚。最后,W25Q64模块的VCC和GND分别接到stm32的电源正负极进行供电。

在这里插入图片描述

二、应用案例代码

MySPI.h:

#ifndef __MYSPI_H
#define __MYSPI_Hvoid MySPI_Init(void);
void MySPI_Start(void);
void MySPI_Stop(void);
uint8_t MySPI_SwapByte(uint8_t ByteSend);#endif

MySPI.c:

#include "stm32f10x.h"                  // Device headervoid MySPI_W_SS(uint8_t BitValue)
{GPIO_WriteBit(GPIOA, GPIO_Pin_4, (BitAction)BitValue);
}void MySPI_W_SCK(uint8_t BitValue)
{GPIO_WriteBit(GPIOA, GPIO_Pin_5, (BitAction)BitValue);
}void MySPI_W_MOSI(uint8_t BitValue)
{GPIO_WriteBit(GPIOA, GPIO_Pin_7, (BitAction)BitValue);
}uint8_t MySPI_R_MISO(void)
{return GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_6);
}void MySPI_Init(void)
{RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4 | GPIO_Pin_5 | GPIO_Pin_7;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA, &GPIO_InitStructure);GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA, &GPIO_InitStructure);MySPI_W_SS(1);MySPI_W_SCK(0);
}void MySPI_Start(void)
{MySPI_W_SS(0);
}void MySPI_Stop(void)
{MySPI_W_SS(1);
}uint8_t MySPI_SwapByte(uint8_t ByteSend)
{uint8_t i, ByteReceive = 0x00;for (i = 0; i < 8; i ++){MySPI_W_MOSI(ByteSend & (0x80 >> i));MySPI_W_SCK(1);if (MySPI_R_MISO() == 1){ByteReceive |= (0x80 >> i);}MySPI_W_SCK(0);}return ByteReceive;
}

W25Q64.h:

#ifndef __W25Q64_H
#define __W25Q64_Hvoid W25Q64_Init(void);
void W25Q64_ReadID(uint8_t *MID, uint16_t *DID);
void W25Q64_PageProgram(uint32_t Address, uint8_t *DataArray, uint16_t Count);
void W25Q64_SectorErase(uint32_t Address);
void W25Q64_ReadData(uint32_t Address, uint8_t *DataArray, uint32_t Count);#endif

W25Q64_Ins.h:

#ifndef __W25Q64_INS_H
#define __W25Q64_INS_H#define W25Q64_WRITE_ENABLE							0x06
#define W25Q64_WRITE_DISABLE						0x04
#define W25Q64_READ_STATUS_REGISTER_1				0x05
#define W25Q64_READ_STATUS_REGISTER_2				0x35
#define W25Q64_WRITE_STATUS_REGISTER				0x01
#define W25Q64_PAGE_PROGRAM							0x02
#define W25Q64_QUAD_PAGE_PROGRAM					0x32
#define W25Q64_BLOCK_ERASE_64KB						0xD8
#define W25Q64_BLOCK_ERASE_32KB						0x52
#define W25Q64_SECTOR_ERASE_4KB						0x20
#define W25Q64_CHIP_ERASE							0xC7
#define W25Q64_ERASE_SUSPEND						0x75
#define W25Q64_ERASE_RESUME							0x7A
#define W25Q64_POWER_DOWN							0xB9
#define W25Q64_HIGH_PERFORMANCE_MODE				0xA3
#define W25Q64_CONTINUOUS_READ_MODE_RESET			0xFF
#define W25Q64_RELEASE_POWER_DOWN_HPM_DEVICE_ID		0xAB
#define W25Q64_MANUFACTURER_DEVICE_ID				0x90
#define W25Q64_READ_UNIQUE_ID						0x4B
#define W25Q64_JEDEC_ID								0x9F
#define W25Q64_READ_DATA							0x03
#define W25Q64_FAST_READ							0x0B
#define W25Q64_FAST_READ_DUAL_OUTPUT				0x3B
#define W25Q64_FAST_READ_DUAL_IO					0xBB
#define W25Q64_FAST_READ_QUAD_OUTPUT				0x6B
#define W25Q64_FAST_READ_QUAD_IO					0xEB
#define W25Q64_OCTAL_WORD_READ_QUAD_IO				0xE3#define W25Q64_DUMMY_BYTE							0xFF#endif

W25Q64.c:

#include "stm32f10x.h"                  // Device header
#include "MySPI.h"
#include "W25Q64_Ins.h"void W25Q64_Init(void)
{MySPI_Init();
}void W25Q64_ReadID(uint8_t *MID, uint16_t *DID)
{MySPI_Start();MySPI_SwapByte(W25Q64_JEDEC_ID);*MID = MySPI_SwapByte(W25Q64_DUMMY_BYTE);*DID = MySPI_SwapByte(W25Q64_DUMMY_BYTE);*DID <<= 8;*DID |= MySPI_SwapByte(W25Q64_DUMMY_BYTE);MySPI_Stop();
}void W25Q64_WriteEnable(void)
{MySPI_Start();MySPI_SwapByte(W25Q64_WRITE_ENABLE);MySPI_Stop();
}void W25Q64_WaitBusy(void)
{uint32_t Timeout;MySPI_Start();MySPI_SwapByte(W25Q64_READ_STATUS_REGISTER_1);Timeout = 100000;while ((MySPI_SwapByte(W25Q64_DUMMY_BYTE) & 0x01) == 0x01){Timeout --;if (Timeout == 0){break;}}MySPI_Stop();
}void W25Q64_PageProgram(uint32_t Address, uint8_t *DataArray, uint16_t Count)
{uint16_t i;W25Q64_WriteEnable();MySPI_Start();MySPI_SwapByte(W25Q64_PAGE_PROGRAM);MySPI_SwapByte(Address >> 16);MySPI_SwapByte(Address >> 8);MySPI_SwapByte(Address);for (i = 0; i < Count; i ++){MySPI_SwapByte(DataArray[i]);}MySPI_Stop();W25Q64_WaitBusy();
}void W25Q64_SectorErase(uint32_t Address)
{W25Q64_WriteEnable();MySPI_Start();MySPI_SwapByte(W25Q64_SECTOR_ERASE_4KB);MySPI_SwapByte(Address >> 16);MySPI_SwapByte(Address >> 8);MySPI_SwapByte(Address);MySPI_Stop();W25Q64_WaitBusy();
}void W25Q64_ReadData(uint32_t Address, uint8_t *DataArray, uint32_t Count)
{uint32_t i;MySPI_Start();MySPI_SwapByte(W25Q64_READ_DATA);MySPI_SwapByte(Address >> 16);MySPI_SwapByte(Address >> 8);MySPI_SwapByte(Address);for (i = 0; i < Count; i ++){DataArray[i] = MySPI_SwapByte(W25Q64_DUMMY_BYTE);}MySPI_Stop();
}

main.c:

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "W25Q64.h"uint8_t MID;
uint16_t DID;uint8_t ArrayWrite[] = {0x01, 0x02, 0x03, 0x04};
uint8_t ArrayRead[4];int main(void)
{OLED_Init();W25Q64_Init();OLED_ShowString(1, 1, "MID:   DID:");OLED_ShowString(2, 1, "W:");OLED_ShowString(3, 1, "R:");W25Q64_ReadID(&MID, &DID);OLED_ShowHexNum(1, 5, MID, 2);OLED_ShowHexNum(1, 12, DID, 4);W25Q64_SectorErase(0x000000);W25Q64_PageProgram(0x000000, ArrayWrite, 4);W25Q64_ReadData(0x000000, ArrayRead, 4);OLED_ShowHexNum(2, 3, ArrayWrite[0], 2);OLED_ShowHexNum(2, 6, ArrayWrite[1], 2);OLED_ShowHexNum(2, 9, ArrayWrite[2], 2);OLED_ShowHexNum(2, 12, ArrayWrite[3], 2);OLED_ShowHexNum(3, 3, ArrayRead[0], 2);OLED_ShowHexNum(3, 6, ArrayRead[1], 2);OLED_ShowHexNum(3, 9, ArrayRead[2], 2);OLED_ShowHexNum(3, 12, ArrayRead[3], 2);while (1){}
}

完整工程:stm32之软件SPI读写W25Q64存储器

三、应用案例分析

从整体架构来看主要分为SPI通信模块、W25Q64模块,接下来重点分析一下SPI通信模块以及W25Q64模块。

3.1 SPI通信模块

SPI通信模块主要封装了一个模块初始化函数和三个时序单元模块。

SPI模块初始化函数

void MySPI_Init(void)
{RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4 | GPIO_Pin_5 | GPIO_Pin_7;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA, &GPIO_InitStructure);GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA, &GPIO_InitStructure);MySPI_W_SS(1);MySPI_W_SCK(0);
}

这个没什么好讲的了,都是一样的套路了。

这里要注意一下的就是PA4(SS)、PA5(SCK)、PA7(MOSI)引脚配置为推挽输出模式(GPIO_Mode_Out_PP),用于主设备主动输出信号。PA6(MISO)配置为上拉输入模式(GPIO_Mode_IPU),表示从设备的数据通过此引脚传输到主设备。

最后再设置一下总线的初始状态即可,设置 SS 引脚为高电平,即取消选择从设备,然后初始化时钟引脚为低电平。

三个时序单元模块:

三个时序单元分别是起始信号、终止信号以及交换一个字节。

起始信号、终止信号:

void MySPI_Start(void)
{MySPI_W_SS(0);
}void MySPI_Stop(void)
{MySPI_W_SS(1);
}

起始条件:SS从高电平切换到低电平
终止条件:SS从低电平切换到高电平

交换一个字节:

uint8_t MySPI_SwapByte(uint8_t ByteSend)
{uint8_t i, ByteReceive = 0x00;for (i = 0; i < 8; i ++){MySPI_W_MOSI(ByteSend & (0x80 >> i));MySPI_W_SCK(1);if (MySPI_R_MISO() == 1){ByteReceive |= (0x80 >> i);}MySPI_W_SCK(0);}return ByteReceive;
}

交换一个字节(模式0):
CPOL=0:空闲状态时,SCK为低电平
CPHA=0:SCK第一个边沿移入数据,第二个边沿移出数据

交换一个字节的时序这里用的是模式0。起始信号之后先立刻将数据的最高位B7移出,然后在SCK的第一个上升沿后读入B7,然后在SCK的第一个下降沿移出次高位B6,然后在SCK的第二个上升沿读入B6,然后在SCK的第二个下降沿移出B5…如此循环8次,最终在SCK的第八个上升沿读入B0完成一个字节的交换。

至于关于代码部分的按位与(提取字节中的每一位)和按位或(设置字节中的某位)以及左移、右移操作的解析在我之前的一篇文章里已经详细地分析过了,如果有不懂的就翻回去看看就行了,这里就不再累述了。

文章传送门:计算机常见运算之左移操作、右移操作以及按位与、按位或

3.2 W25Q64模块

W25Q64模块主要由模块初始化函数、写使能、页编程(写入数据)、读取数据、忙等待、读取ID号以及扇区擦除等模块组成。

W25Q64模块初始化函数:

void W25Q64_Init(void)
{MySPI_Init();
}

调用MySPI_Init()函数,完成引脚配置和SPI的初始化。

写使能:

void W25Q64_WriteEnable(void)
{MySPI_Start();MySPI_SwapByte(W25Q64_WRITE_ENABLE);// 发送写使能命令MySPI_Stop();
}

发送写使能命令,以允许后续的写入或擦除操作。

页编程(写入数据):

void W25Q64_PageProgram(uint32_t Address, uint8_t *DataArray, uint16_t Count) 
{uint16_t i;W25Q64_WriteEnable(); // 写使能MySPI_Start();MySPI_SwapByte(W25Q64_PAGE_PROGRAM); // 发送页编程命令MySPI_SwapByte(Address >> 16); // 发送地址高8位MySPI_SwapByte(Address >> 8);  // 发送地址中8位MySPI_SwapByte(Address);       // 发送地址低8位for (i = 0; i < Count; i ++) { // 发送要写入的数据MySPI_SwapByte(DataArray[i]);}MySPI_Stop();W25Q64_WaitBusy(); // 等待存储器空闲
}

页编程操作首先通过W25Q64_WriteEnable()函数启用写操作,然后发送页编程命令W25Q64_PAGE_PROGRAM,接着发送地址,并逐字节将数据写入存储器。

关于依次提取3字节24位中的每一个字节的问题有几点要注意一下,假设我传入的地址是Address = 0x123456,那么我们就需要依次发送0x12、0x34、0x56,那如何提取呢?

我们可以这样操作:

原始值Address:0x123456
0001 0010 0011 0100 0101 0110

右移十六位—> 0x12
0000 0000 0000 0000 0001 0010

右移八位—> 0x1234
0000 0000 0001 0010 0011 0100

可以看到如果是右移八位,那么得到的是0x1234,但是我们想要的是0x34才对啊?但其实代码里这样写也是没问题的,因为MySPI_SwapByte函数的形参是uint8_t,传入0x1234就只会接收低八位的0x34,但是这样的话理解起来就有点那啥了。

其实我们还可以这样做,直接右移以后再做个按位与操作就可以了,比如MySPI_SwapByte((Address >> 8) & 0xFF);这样就直接得到0x34了,当然第三位也需要执行按位与操作,MySPI_SwapByte(Address & 0xFF);

两种方法都可以,我这里只是提一下。

读数据:

void W25Q64_ReadData(uint32_t Address, uint8_t *DataArray, uint32_t Count) 
{uint32_t i;MySPI_Start();MySPI_SwapByte(W25Q64_READ_DATA); // 发送读取数据命令MySPI_SwapByte(Address >> 16); // 发送地址高8位MySPI_SwapByte(Address >> 8);  // 发送地址中8位MySPI_SwapByte(Address);       // 发送地址低8位for (i = 0; i < Count; i ++) { // 读取数据DataArray[i] = MySPI_SwapByte(W25Q64_DUMMY_BYTE);}MySPI_Stop();
}

读取数据命令W25Q64_READ_DATA,发送要读取的地址,然后通过SPI逐字节读取指定数量的数据到DataArray中。

忙等待:

void W25Q64_WaitBusy(void)
{uint32_t Timeout;MySPI_Start();MySPI_SwapByte(W25Q64_READ_STATUS_REGISTER_1);// 读取状态寄存器1Timeout = 100000;while ((MySPI_SwapByte(W25Q64_DUMMY_BYTE) & 0x01) == 0x01){// 检查忙标志位Timeout --;if (Timeout == 0){break;}}MySPI_Stop();
}

发送读取状态寄存器1命令,进入循环等待存储器空闲(即状态寄存器的最低位变为0),避免在存储器仍处于忙碌时进行新的操作。

读厂商、设备ID:

void W25Q64_ReadID(uint8_t *MID, uint16_t *DID) 
{MySPI_Start(); // 片选低电平,开始通信MySPI_SwapByte(W25Q64_JEDEC_ID); // 发送读取ID命令*MID = MySPI_SwapByte(W25Q64_DUMMY_BYTE); // 接收制造商ID*DID = MySPI_SwapByte(W25Q64_DUMMY_BYTE); // 接收设备ID高8位*DID <<= 8;*DID |= MySPI_SwapByte(W25Q64_DUMMY_BYTE); // 接收设备ID低8位MySPI_Stop(); // 片选高电平,结束通信
}
  • 首先,调用MySPI_Start()开始SPI通信,然后发送读取设备ID命令W25Q64_JEDEC_ID。随后,通过MySPI_SwapByte函数接收制造商ID和设备ID。
  • 制造商ID(MID)为一个字节,设备ID(DID)为两个字节。

扇区擦除:

void W25Q64_SectorErase(uint32_t Address) 
{W25Q64_WriteEnable(); // 写使能MySPI_Start();MySPI_SwapByte(W25Q64_SECTOR_ERASE_4KB); // 发送扇区擦除命令MySPI_SwapByte(Address >> 16); // 发送地址高8位MySPI_SwapByte(Address >> 8);  // 发送地址中8位MySPI_SwapByte(Address);       // 发送地址低8位MySPI_Stop();W25Q64_WaitBusy(); // 等待存储器空闲
}

扇区擦除命令W25Q64_SECTOR_ERASE_4KB用于擦除特定地址所在的扇区,地址通过三次发送高、中、低8位地址来指定。

3.3 主程序

主程序整体代码逻辑大概是,首先读取从机的ID信息并显示在OLED屏幕上,然后执行一个扇区擦除操作,接着在该扇区中写入4个字节的数据,最后将写入的数据读取出来,并在OLED上显示。

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "W25Q64.h"uint8_t MID;// 制造商ID (Manufacturer ID)
uint16_t DID;// 设备ID (Device ID)uint8_t ArrayWrite[] = {0x01, 0x02, 0x03, 0x04};
uint8_t ArrayRead[4];int main(void)
{// 硬件初始化OLED_Init();W25Q64_Init();OLED_ShowString(1, 1, "MID:   DID:");OLED_ShowString(2, 1, "W:");OLED_ShowString(3, 1, "R:");// 读取并显示W25Q64的ID信息W25Q64_ReadID(&MID, &DID);OLED_ShowHexNum(1, 5, MID, 2);OLED_ShowHexNum(1, 12, DID, 4);// 擦除W25Q64的一个扇区并写入数据W25Q64_SectorErase(0x000000);W25Q64_PageProgram(0x000000, ArrayWrite, 4);// 读取数据并显示W25Q64_ReadData(0x000000, ArrayRead, 4);OLED_ShowHexNum(2, 3, ArrayWrite[0], 2);OLED_ShowHexNum(2, 6, ArrayWrite[1], 2);OLED_ShowHexNum(2, 9, ArrayWrite[2], 2);OLED_ShowHexNum(2, 12, ArrayWrite[3], 2);OLED_ShowHexNum(3, 3, ArrayRead[0], 2);OLED_ShowHexNum(3, 6, ArrayRead[1], 2);OLED_ShowHexNum(3, 9, ArrayRead[2], 2);OLED_ShowHexNum(3, 12, ArrayRead[3], 2);while (1){}
}

这篇关于stm32之软件SPI读写W25Q64存储器应用案例的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1144719

相关文章

Golang操作DuckDB实战案例分享

《Golang操作DuckDB实战案例分享》DuckDB是一个嵌入式SQL数据库引擎,它与众所周知的SQLite非常相似,但它是为olap风格的工作负载设计的,DuckDB支持各种数据类型和SQL特性... 目录DuckDB的主要优点环境准备初始化表和数据查询单行或多行错误处理和事务完整代码最后总结Duck

Python实现高效地读写大型文件

《Python实现高效地读写大型文件》Python如何读写的是大型文件,有没有什么方法来提高效率呢,这篇文章就来和大家聊聊如何在Python中高效地读写大型文件,需要的可以了解下... 目录一、逐行读取大型文件二、分块读取大型文件三、使用 mmap 模块进行内存映射文件操作(适用于大文件)四、使用 pand

C# 读写ini文件操作实现

《C#读写ini文件操作实现》本文主要介绍了C#读写ini文件操作实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧... 目录一、INI文件结构二、读取INI文件中的数据在C#应用程序中,常将INI文件作为配置文件,用于存储应用程序的

将Python应用部署到生产环境的小技巧分享

《将Python应用部署到生产环境的小技巧分享》文章主要讲述了在将Python应用程序部署到生产环境之前,需要进行的准备工作和最佳实践,包括心态调整、代码审查、测试覆盖率提升、配置文件优化、日志记录完... 目录部署前夜:从开发到生产的心理准备与检查清单环境搭建:打造稳固的应用运行平台自动化流水线:让部署像

MySQL不使用子查询的原因及优化案例

《MySQL不使用子查询的原因及优化案例》对于mysql,不推荐使用子查询,效率太差,执行子查询时,MYSQL需要创建临时表,查询完毕后再删除这些临时表,所以,子查询的速度会受到一定的影响,本文给大家... 目录不推荐使用子查询和JOIN的原因解决方案优化案例案例1:查询所有有库存的商品信息案例2:使用EX

Linux中Curl参数详解实践应用

《Linux中Curl参数详解实践应用》在现代网络开发和运维工作中,curl命令是一个不可或缺的工具,它是一个利用URL语法在命令行下工作的文件传输工具,支持多种协议,如HTTP、HTTPS、FTP等... 目录引言一、基础请求参数1. -X 或 --request2. -d 或 --data3. -H 或

在Ubuntu上部署SpringBoot应用的操作步骤

《在Ubuntu上部署SpringBoot应用的操作步骤》随着云计算和容器化技术的普及,Linux服务器已成为部署Web应用程序的主流平台之一,Java作为一种跨平台的编程语言,具有广泛的应用场景,本... 目录一、部署准备二、安装 Java 环境1. 安装 JDK2. 验证 Java 安装三、安装 mys

Python中构建终端应用界面利器Blessed模块的使用

《Python中构建终端应用界面利器Blessed模块的使用》Blessed库作为一个轻量级且功能强大的解决方案,开始在开发者中赢得口碑,今天,我们就一起来探索一下它是如何让终端UI开发变得轻松而高... 目录一、安装与配置:简单、快速、无障碍二、基本功能:从彩色文本到动态交互1. 显示基本内容2. 创建链

C#实现文件读写到SQLite数据库

《C#实现文件读写到SQLite数据库》这篇文章主要为大家详细介绍了使用C#将文件读写到SQLite数据库的几种方法,文中的示例代码讲解详细,感兴趣的小伙伴可以参考一下... 目录1. 使用 BLOB 存储文件2. 存储文件路径3. 分块存储文件《文件读写到SQLite数据库China编程的方法》博客中,介绍了文

Node.js 中 http 模块的深度剖析与实战应用小结

《Node.js中http模块的深度剖析与实战应用小结》本文详细介绍了Node.js中的http模块,从创建HTTP服务器、处理请求与响应,到获取请求参数,每个环节都通过代码示例进行解析,旨在帮... 目录Node.js 中 http 模块的深度剖析与实战应用一、引言二、创建 HTTP 服务器:基石搭建(一