使用GD32F470的硬件SPI读写W25Q64

news2025/2/9 6:55:09

代码简单改下引脚定义便可以使用!

使用的单片机具体型号:GD32F470ZGT6

简单介绍下W25Q64:

/* W25Q64 性能参数 */
/* 容量:8MByte = 64Mbit */
/* 有128个块,每个块有64KByte */
/* 每个块有16个扇区,每个扇区有4KByte */
/* 每个扇区有16页,每个页有256Byte */
/* 最小擦除单位:扇区:4KByte */
/* 最大写入单位:页:256Byte */

 w25q64.c:

#include "bsp_w25q64.h"
#include "bsp_usart.h"
#include "systick.h"



#define uchar   uint8_t
#define ushort  uint16_t
#define ulong   uint32_t




/**********************************************************
 * 函 数 名 称:w25q64_init_config
 * 函 数 功 能:w25q64初始化
 * 传 入 参 数:无
 * 函 数 返 回:无
 * 作       者:LCKFB
 * 备       注:无
**********************************************************/
void w25q64_init_config(void)
{
    //SPI参数定义结构体
    spi_parameter_struct spi_init_struct;

    rcu_periph_clock_enable(W25Q64_SPI_CLK_RCU);  // 使用F端口
    rcu_periph_clock_enable(W25Q64_SPI_MISO_RCU);  // 使用F端口
    rcu_periph_clock_enable(W25Q64_SPI_MOSI_RCU);  // 使用F端口
    rcu_periph_clock_enable(W25Q64_SPI_RCU);     // 使能SPI4
    //引脚复用
    gpio_af_set(W25Q64_SPI_CLK_PORT, W25Q64_SPI_AF, W25Q64_SPI_CLK_PIN);
    gpio_af_set(W25Q64_SPI_MISO_PORT, W25Q64_SPI_AF, W25Q64_SPI_MISO_PIN);
    gpio_af_set(W25Q64_SPI_MOSI_PORT, W25Q64_SPI_AF, W25Q64_SPI_MOSI_PIN);
    //引脚模式
    gpio_mode_set(W25Q64_SPI_CLK_PORT, GPIO_MODE_AF, GPIO_PUPD_NONE, W25Q64_SPI_CLK_PIN);
    gpio_mode_set(W25Q64_SPI_MISO_PORT, GPIO_MODE_AF, GPIO_PUPD_NONE, W25Q64_SPI_MISO_PIN);
    gpio_mode_set(W25Q64_SPI_MOSI_PORT, GPIO_MODE_AF, GPIO_PUPD_NONE, W25Q64_SPI_MOSI_PIN);
    //输出模式
    gpio_output_options_set(W25Q64_SPI_CLK_PORT, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, W25Q64_SPI_CLK_PIN);
    gpio_output_options_set(W25Q64_SPI_MISO_PORT, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, W25Q64_SPI_MISO_PIN);
    gpio_output_options_set(W25Q64_SPI_MOSI_PORT, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, W25Q64_SPI_MOSI_PIN);

    //开启CS引脚时钟
    rcu_periph_clock_enable(W25Q64_SPI_CS_RCU);
    //配置CS引脚模式
    gpio_mode_set(W25Q64_SPI_CS_PORT, GPIO_MODE_OUTPUT, GPIO_PUPD_PULLUP, W25Q64_SPI_CS_PIN);
    //配置CS输出模式
    gpio_output_options_set(W25Q64_SPI_CS_PORT, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, W25Q64_SPI_CS_PIN);
    //W25Q64不选中
    gpio_bit_write(W25Q64_SPI_CS_PORT, W25Q64_SPI_CS_PIN, SET);

    spi_init_struct.trans_mode           = SPI_TRANSMODE_FULLDUPLEX;  	// 传输模式全双工
    spi_init_struct.device_mode          = SPI_MASTER;                	// 配置为主机
    spi_init_struct.frame_size           = SPI_FRAMESIZE_8BIT;        	// 8位数据
    spi_init_struct.clock_polarity_phase = SPI_CK_PL_LOW_PH_1EDGE;		//极性相位,模式3(1,1)
    spi_init_struct.nss                  = SPI_NSS_SOFT;              	//软件cs
    spi_init_struct.prescale             = SPI_PSC_32;                 	//SPI时钟预分频为32(SPI通信速度有讲究,如果太快,会导致波形和预期现象都出现偏差)
    spi_init_struct.endian               = SPI_ENDIAN_MSB;            	//高位在前
    //将参数填入SPI4
    spi_init(W25Q64_SPIx, &spi_init_struct);
    //使能SPI
    spi_enable(W25Q64_SPIx);

}


static uchar spi_read_write_byte(uchar dat)
{
    //等待发送缓冲区为空
    while(RESET == spi_i2s_flag_get(W25Q64_SPIx,  SPI_FLAG_TBE) );
    spi_i2s_data_transmit(W25Q64_SPIx, dat);
    //等待接收缓冲区为空
    while(RESET == spi_i2s_flag_get(W25Q64_SPIx,  SPI_FLAG_RBNE) );
    return spi_i2s_data_receive(W25Q64_SPIx);
}


ushort W25Q64_readID(void)
{
    ushort  temp = 0;
    W25Q64_CS(0);										// 片选W25Q64

    spi_read_write_byte(W25Q64_Manufacturer_Device_ID);//发送读取ID命令
    spi_read_write_byte(0x00);
    spi_read_write_byte(0x00);
    spi_read_write_byte(0x00);

    //接收数据
    temp |= spi_read_write_byte(0xFF) << 8;               // 读取 manufacturer ID,固定为EF
    temp |= spi_read_write_byte(0xFF);                    // 读取 device ID

	W25Q64_CS(1);										// 取消片选
    return temp;
}

static void W25Q64_write_enable(void)
{
	W25Q64_CS(0);
	spi_read_write_byte(W25Q64_Write_Enable);				// 发送写使能指令
	W25Q64_CS(1);
}


/**********************************************************
 * 函 数 名 称:W25Q64_wait_busy
 * 函 数 功 能:判断W25Q64是否忙
 * 传 入 参 数:无
 * 函 数 返 回:无
 * 作       者:LCKFB
 * 备       注:无
**********************************************************/
void W25Q64_wait_busy(void)
{
    unsigned char byte = 0;
    do
    {
        W25Q64_CS(0);
        spi_read_write_byte(W25Q64_Read_Status_register_1);
        byte = spi_read_write_byte(0Xff);
        W25Q64_CS(1);
    }
    while( ( byte & 0x01 ) == 1 );          // 如果得到的数据最低位是1,则等待
}


/**********************************************************
 * 函 数 名 称:W25Q64_erase_sector
 * 函 数 功 能:擦除一个扇区
 * 传 入 参 数:addr=擦除的扇区号
 * 函 数 返 回:无
 * 作       者:LCKFB
 * 备       注:addr=擦除的扇区号,范围=0~15
**********************************************************/
void W25Q64_erase_sector(ulong addr)
{
    addr *= 4096;
    W25Q64_write_enable();  //写使能
    W25Q64_wait_busy();     //判断忙
    W25Q64_CS(0);
    spi_read_write_byte(W25Q64_Sector_Erase_4KB);
    spi_read_write_byte((uchar)((addr) >> 16));
    spi_read_write_byte((uchar)((addr) >> 8));
    spi_read_write_byte((uchar)addr);
    W25Q64_CS(1);
}

// /**********************************************************
//  * 函 数 名 称:W25Q64_write
//  * 函 数 功 能:写数据到W25Q64进行保存
//  * 传 入 参 数:buffer=写入的数据内容	addr=写入地址	numbyte=写入数据的长度
//  * 函 数 返 回:无
//  * 作       者:LCKFB
//  * 备       注:无
// **********************************************************/
// void W25Q64_write(uchar *buffer, ulong addr, ushort numbyte)
// {
//     unsigned int i = 0;
//     W25Q64_erase_sector(addr / 4096); //擦除扇区数据,2^12=4096

//     //等待擦除完成
//     W25Q64_wait_busy();

//     delay_ms(50);
//     W25Q64_write_enable();//写使能
//     W25Q64_wait_busy(); //忙检测
//     //写入数据
//     W25Q64_CS(0);
//     spi_read_write_byte(W25Q64_Page_Program);
//     spi_read_write_byte((uchar)((addr) >> 16));
//     spi_read_write_byte((uchar)((addr) >> 8));
//     spi_read_write_byte((uchar)addr);
//     for(i = 0; i < numbyte; i++)
//     {
//         spi_read_write_byte(buffer[i]);
//     }
//     W25Q64_CS(1);
// }

// /**********************************************************
//  * 函 数 名 称:W25Q64_read
//  * 函 数 功 能:读取W25Q64的数据
//  * 传 入 参 数:buffer=读出数据的保存地址  read_addr=读取地址		read_length=读去长度
//  * 函 数 返 回:无
//  * 作       者:LCKFB
//  * 备       注:无
// **********************************************************/
// void W25Q64_read(uchar *buffer, ulong read_addr, ushort read_length)
// {
//     ushort i;
//     //等待擦除完成
//     W25Q64_wait_busy();

//     W25Q64_CS(0);
//     spi_read_write_byte(W25Q64_Read_Data);
//     spi_read_write_byte((uchar)((read_addr) >> 16));
//     spi_read_write_byte((uchar)((read_addr) >> 8));
//     spi_read_write_byte((uchar)read_addr);
//     for(i = 0; i < read_length; i++)
//     {
//         buffer[i] = spi_read_write_byte(0XFF);
//     }
//     W25Q64_CS(1);
// }



/* 写入数据 */
/* buffer   : 字符数组 */
/* section  : 扇区,0-2047 */
/* page     : 页数,0-15 */
/* addr     : 具体地址,0-255 */
/* numbyte  : 写入多少个数据,0-255 */
uchar W25Q64_write(uchar *buffer, ushort section, uchar page, ushort addr, ushort numbyte)
{
    // 输入地址越界
    if((section < 0) || (section > 2047) || (page > 15) || (page < 0) || (addr >255) || (addr < 0))
    {
        return 1;
    }
    // 输入字节数超过每页的数量,会造成数据覆盖
    if(addr + numbyte > 255)
    {
        return 2;
    }

    ulong write_addr = (section * 4096) + (page * 256) + addr;
    W25Q64_erase_sector(section);   //擦除扇区数据,2^12=4096

    //等待擦除完成
    W25Q64_wait_busy();

    delay_ms(50);
    W25Q64_write_enable();          //写使能
    W25Q64_wait_busy();             //忙检测

    //写入数据
    W25Q64_CS(0);
    spi_read_write_byte(W25Q64_Page_Program);
    spi_read_write_byte((uchar)((write_addr) >> 16));
    spi_read_write_byte((uchar)((write_addr) >> 8));
    spi_read_write_byte((uchar)write_addr);
    for(ushort i = 0; i < numbyte; i++)
    {
        spi_read_write_byte(buffer[i]);
    }
    W25Q64_CS(1);

    return 0;
}

/* 写入数据 */
/* buffer   : 字符数组 */
/* section  : 扇区,0-2047 */
/* page     : 页数,0-15 */
/* addr     : 具体地址,0-255 */
/* numbyte  : 写入多少个数据,0-255 */
uchar W25Q64_read(uchar *buffer, ushort section, uchar page, ushort addr, ushort numbyte)
{
    // 输入地址越界
    if((section < 0) || (section > 2047) || (page > 15) || (page < 0) || (addr >255) || (addr < 0))
    {
        return 1;
    }
    // 输入字节数超过每页的数量,会造成数据错误
    if(addr + numbyte > 255)
    {
        return 2;
    }
    
    ulong read_addr = (section * 4096) + (page * 256) + addr;
    //等待擦除完成
    W25Q64_wait_busy();

    W25Q64_CS(0);
    spi_read_write_byte(W25Q64_Read_Data);
    spi_read_write_byte((uchar)((read_addr) >> 16));
    spi_read_write_byte((uchar)((read_addr) >> 8));
    spi_read_write_byte((uchar)read_addr);
    for(ushort i = 0; i < numbyte; i++)
    {
        buffer[i] = spi_read_write_byte(0XFF);
    }
    W25Q64_CS(1);

    return 0;
}


w25q64.h:

#ifndef _BSP_W25Q64_H__
#define _BSP_W25Q64_H__

#include "gd32f4xx.h"
#include "gd32f470_config.h"



/* W25Q64 性能参数 */
/* 容量:8MByte = 64Mbit */
/* 有128个块,每个块有64KByte */
/* 每个块有16个扇区,每个扇区有4KByte */
/* 每个扇区有16页,每个页有256Byte */
/* 最小擦除单位:扇区:4KByte */
/* 最大写入单位:页:256Byte */


//W25Q64指令表1
#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_Uuique_ID						      0x4B
#define W25Q64_JEDEC_ID								      0x9F

//W25Q64指令集表2(读指令)
#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_SPIx                	SPI4
#define W25Q64_SPI_AF              	GPIO_AF_5

#define W25Q64_SPI_RCU             	RCU_SPI4
#define W25Q64_SPI_CLK_RCU         	RCU_GPIOF
#define W25Q64_SPI_MISO_RCU        	RCU_GPIOF
#define W25Q64_SPI_MOSI_RCU        	RCU_GPIOF
#define W25Q64_SPI_CS_RCU        	RCU_GPIOF


#define W25Q64_SPI_CLK_PORT 	    GPIOF			    // LED GPIOB的端口
#define W25Q64_SPI_CLK_PIN 	    	GPIO_PIN_7          // LED GPIOB的引脚

#define W25Q64_SPI_MISO_PORT 	    GPIOF			    // LED GPIOB的端口  单片机的输入,接Flash的DO
#define W25Q64_SPI_MISO_PIN 	    GPIO_PIN_8          // LED GPIOB的引脚

#define W25Q64_SPI_MOSI_PORT 	    GPIOF			    // LED GPIOB的端口  单片机的输出,接Flash的DI
#define W25Q64_SPI_MOSI_PIN 	    GPIO_PIN_9          // LED GPIOB的引脚

#define W25Q64_SPI_CS_PORT 	    	GPIOF			    // LED GPIOB的端口
#define W25Q64_SPI_CS_PIN 	    	GPIO_PIN_6          // LED GPIOB的引脚

#define W25Q64_CS(state) ((state) ? gpio_bit_set(W25Q64_SPI_CS_PORT, W25Q64_SPI_CS_PIN) : gpio_bit_reset(W25Q64_SPI_CS_PORT, W25Q64_SPI_CS_PIN))


void w25q64_init_config(void);
ushort W25Q64_readID(void);

uchar W25Q64_write(uchar *buffer, ushort section, uchar page, ushort addr, ushort numbyte);
uchar W25Q64_read(uchar *buffer, ushort section, uchar page, ushort addr, ushort numbyte);




#endif

mian.c测试:

int main(void)
{
    systick_config();				// 滴答定时器 初始化
    nvic_priority_group_set(NVIC_PRIGROUP_PRE2_SUB2);  // 优先级分组(0-4, 0-4)
    Test_UART_Init(115200);			// 调试的 UART 初始化



    // w25q64_init_config();


    // uchar buff[4] = {0x00, 0x11, 0xFF, 0x80};
    // if(W25Q64_write(buff, 0, 0, 0, 4))
    // {
    //     printf("write error\n");
    // }

    // uchar rec_buff[4];
    // if(W25Q64_read(rec_buff, 0, 0, 0, 4))
    // {
    //     printf("read error\n");
    // }
    // printf("rec_buff: %x %x %x %x\n",rec_buff[0],rec_buff[1],rec_buff[2],rec_buff[3]);
    
    while(1)
    {


    }
}

        有一个地方需要注意一下,就是判断W25Q64忙不忙,这里采用死等的办法,没有做超时溢出,可能会卡死,并且Flash扇区擦除是非常耗时间的操作,几十个ms去死等,显然不靠谱,最好搞个定时器搞个标志位去查看,这里只是测试就懒得做那么多了。

        另外这种flash,只能从1变0,反之是不行的,所以在写数据前,必须擦除所写的扇区的所有数据。

        图片的现象,跟我给的代码实际现象不一样,大家复制改定义就行。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2295129.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

02为什么 OD门和 OC门输出必须加上拉电阻?

为什么 OD&#xff08;开漏&#xff09;门和 OC&#xff08;开集&#xff09;门输出必须加上拉电阻&#xff1f; 1、首先一点&#xff0c;知道OD是说的MOS管&#xff0c;OC是说的三极管&#xff0c;二者的区别与联系大家应该都懂。 2、以OC门举例&#xff0c;芯片的OC门内部结…

AI方案调研与实践 (不定期补充)

目录 说明 1. AI云主机准备 1.1 Ollama配置 设置模型保存路径 配置模型驻留内存时间 查看GPU状况命令: nvidia-smi 2. Deepseek 2.1 安装与使用 3. LobeChat配置 参考 说明 调研并实例化各种AI方案&#xff0c;探索训练/使用方式的最佳实践。 1. AI云主机准备 可以去一…

人工智能大模型之模型蒸馏与知识蒸馏

一、背景介绍 随着人工智能技术的不断发展&#xff0c;大模型在各个领域的应用也越来越广泛。模型蒸馏&#xff08;Model Distillation&#xff09;和知识蒸馏&#xff08;Knowledge Distillation&#xff09;是两种非常重要的模型压缩技术&#xff0c;它们可以帮助我们将大型…

[手机Linux] onepluse6T 系统重新分区

一&#xff0c;刷入TWRP 1. 电脑下载 Fastboot 工具&#xff08;解压备用&#xff09;和对应机型 TWRP&#xff08;.img 后缀文件&#xff0c;将其放入前面解压的文件夹里&#xff09; 或者直接这里下载:TWRP 2. 将手机关机&#xff0c;长按音量上和下键 开机键 进入 fastbo…

k8s部署elasticsearch

前置环境&#xff1a;已部署k8s集群&#xff0c;ip地址为 192.168.10.1~192.168.10.5&#xff0c;总共5台机器。 1. 创建provisioner制备器&#xff08;如果已存在&#xff0c;则不需要&#xff09; 制备器的具体部署方式&#xff0c;参考我之前的文章&#xff1a;k8s部署rab…

本地部署DeepSeek

下载Docker Docker Desktop: The #1 Containerization Tool for Developers | Docker 下载安装ollama Download Ollama on macOS 下载完成后解压运行 终端输入 Ollama --version 输出对应版本号即为下载成功 如果没有弹出上述图片&#xff0c;浏览器输入http://localhos…

21.[前端开发]Day21-HTML5新增内容-CSS函数-BFC-媒体查询

王者荣耀-网页缩小的问题处理 为什么会产生这个问题&#xff1f;怎么去解决 可以给body设置最小宽度 1 HTML5新增元素 HTML5语义化元素 HTML5其他新增元素 2 Video、Audio元素 HTML5新增元素 - video video支持的视频格式 video的兼容性写法 HTML5新增元素 - audio audio…

nbmade-boot调用deepseek的api过程与显示

希望大家一起能参与我的新开源项目nbmade-boot: 宁波智能制造低代码实训平台 下面简单介绍调用最近大红的AI &#xff1a;deepseek的api过程与显示&#xff0c;包括前后端代码与效果图 一、后端代码 1、几个基础的java类 DeepSeekRequest .java package com.nbcio.demo.do…

Linux:安装 node 及 nvm node 版本管理工具(ubuntu )

目录 方法一&#xff1a;手动下载安装文件安装方法二&#xff1a;curl安装 方法一&#xff1a;手动下载安装文件安装 git clone 远程镜像 git clone https://gitee.com/mirrors/nvm安装 nvm bash install.sh刷新配置&#xff0c;使配置在终端生效 // 方法 1 source /root/.…

【多线程-第三天-NSOperation和GCD的区别 Objective-C语言】

一、我们来看NSOperation和GCD的区别 1.我们来对比一下,NSOperation和GCD, 那这个代码,我们都写过了, 我们来看一下它们的特点啊,首先来看GCD, 1)GCD是C语言的框架,是iOS4.0之后推出的,并且它的特点是,针对多核做了优化,可以充分利用CPU的多核,OK,这是GCD, 2…

【医院运营统计专题】2.运营统计:医院管理的“智慧大脑”

医院成本核算、绩效管理、运营统计、内部控制、管理会计专题索引 引言 在当今医疗行业快速发展的背景下,医院运营管理的科学性和有效性成为了决定医院竞争力和可持续发展能力的关键因素。运营统计作为医院管理的重要工具,通过对医院各类数据的收集、整理、分析和解读,为医…

Ollama 部署 DeepSeek-R1 及Open-WebUI

Ollama 部署 DeepSeek-R1 及Open-WebUI 文章目录 Ollama 部署 DeepSeek-R1 及Open-WebUI〇、说明为什么使用本方案 一、 安装Ollama1、主要特点&#xff1a;2、安装3、验证 二、Ollama 部署 DeepSeek1、部署2、模型选用3、Ollama 常用命令4、Ollama模型默认存储路径 安装open-w…

Vite 打包原理

&#x1f90d; 前端开发工程师、技术日更博主、已过CET6 &#x1f368; 阿珊和她的猫_CSDN博客专家、23年度博客之星前端领域TOP1 &#x1f560; 牛客高级专题作者、打造专栏《前端面试必备》 、《2024面试高频手撕题》 &#x1f35a; 蓝桥云课签约作者、上架课程《Vue.js 和 E…

【大模型】Ubuntu下安装ollama,DeepSseek-R1:32b的本地部署和运行

1 ollama 的安装与设置 ollama官网链接&#xff1a;https://ollama.com/ 在左上角的【Models】中展示了ollama支持的模型在正中间的【Download】中课可以下载支持平台中的安装包。   其安装和模型路径配置操作流程如下&#xff1a; ollama的安装 这里选择命令安装curl -fsSL …

蓝桥杯---力扣题库第38题目解析

文章目录 1.题目重述2.外观数列举例说明3.思路分析&#xff08;双指针模拟&#xff09;4.代码说明 1.题目重述 外观数列实际上就是给你一串数字&#xff0c;我们需要对于这个数据进行一个简单的描述罢了&#xff1b; 2.外观数列举例说明 外观数列都是从1开始的&#xff0c;也…

oCam:免费且强大的录屏软件

今天给大家推荐一个非常好的录屏软件。几乎可以满足你日常工作的需求。而且软件完全免费&#xff0c;没有任何的广告。 oCam&#xff1a;免费且强大的录屏软件 oCam是一款功能强大的免费录屏软件&#xff0c;支持屏幕录制、游戏录制和音频录制等多种模式&#xff0c;能够满足不…

【GitLab CI/CD 实践】从 0 到 1 搭建高效自动化部署流程

网罗开发 &#xff08;小红书、快手、视频号同名&#xff09; 大家好&#xff0c;我是 展菲&#xff0c;目前在上市企业从事人工智能项目研发管理工作&#xff0c;平时热衷于分享各种编程领域的软硬技能知识以及前沿技术&#xff0c;包括iOS、前端、Harmony OS、Java、Python等…

Django开发入门 – 1.搭建基于Python Web框架Django的IDE开发环境

Django开发入门 – 1.搭建基于Python Web框架Django的IDE开发环境 Build A Integrated Development Environment(IDE) for Python Web Framework - django By JacksonML 1. 获取及安装最新版Python 打开Chrome浏览器&#xff0c;访问Python官网链接&#xff1a;https://www…

【Java基础】序列化、反序列化和不可变类

Hi~&#xff01;这里是奋斗的明志&#xff0c;很荣幸您能阅读我的文章&#xff0c;诚请评论指点&#xff0c;欢迎欢迎 ~~ &#x1f331;&#x1f331;个人主页&#xff1a;奋斗的明志 &#x1f331;&#x1f331;所属专栏&#xff1a;Java基础面经 &#x1f4da;本系列文章为个…

USB子系统学习(四)使用libusb读取鼠标数据

文章目录 1、声明2、HID协议2.1、描述符2.2、鼠标数据格式 3、应用程序4、编译应用程序5、测试 1、声明 本文是在学习韦东山《驱动大全》USB子系统时&#xff0c;为梳理知识点和自己回看而记录&#xff0c;全部内容高度复制粘贴。 韦老师的《驱动大全》&#xff1a;商品详情 …