零基础STM32入门教程--13-SPI 串行外设接口

专栏:STM32F103入门教程
环境:Keil‑MDK + STM32F103 标准库3.5版本

一、SPI

SPI:Serial Peripheral Interface,串行外设接口,高速同步串行通信,常用于 MCU 和外设:Flash、OLED、ADC、DAC、传感器、之间通信。
同步:有单独时钟线,收发双方时钟保持一致,不需要波特率配置(对比 UART 异步)。

四根信号线(标准 SPI)

SCLK:串行时钟,由主机输出,时钟信号,决定通信速度
MOSI:主机输出,从机输入 → 主机发数据给外设
MISO:主机输入,从机输出 → 外设发数据给主机
CS / NSS:片选,片选信号,一般低电平有效,选中某一个从设备
主机:控制时钟的设备(一般是 MCU)
从机:被控制的外设(Flash、传感器)
接线记忆
MOSI:Master Out Slave In 主机出‑从机进
MISO:Master In Slave Out 主机进‑从机出

多设备连接方式
1)独立片选(最常用)

SCLK、MOSI、MISO 全部并联到所有从机;每个从机使用独立 CS 引脚。
主机想和哪个设备通信,就把对应 CS 拉低,其余 CS 保持高。

SPI 4 种模式(CPOL、CPHA)

两个参数,决定时钟空闲电平、在哪一个边沿采样数据
CPOL:时钟极性
CPOL=0:空闲时 SCLK = 低电平
CPOL=1:空闲时 SCLK = 高电平
CPHA:时钟相位,数据采样边沿
CPHA=0:第一个时钟边沿采样数据
CPHA=1:第二个时钟边沿采样数据
组合 4 种模式:
Mode0:CPOL=0, CPHA=0 → 最常用(Flash、OLED 大多用模式 0)
Mode1:CPOL=0, CPHA=1
Mode2:CPOL=1, CPHA=0
Mode3:CPOL=1, CPHA=1
⚠️主机和从机必须模式一致,否则收到乱码。

通信过程(Mode0 举例)

主机把对应从机 CS 拉低,选中设备
主机产生 SCLK 时钟;在每个时钟周期,MOSI 发送 1bit,同时 MISO 接收 1bit
SPI 是全双工:发数据的同时也在收数据,8bit/16bit 为一组交换;你即使只想读数据,主机也要输出 dummy(填充)字节。
收发完成,CS 拉高释放从机。

关键点:SPI 没有应答 ACK,没有校验;你发出去,不知道对方有没有收到。对比 I2C 有应答。

工程常见坑

模式不对,数据全乱,Mode0 最多;买外设模块优先确认 SPI 模式。
SPI 全双工:读寄存器时,主机必须发送 dummy 字节,才会收到从机返回数据。
CS 时序:必须通信前拉低,通信结束再拉高;不要一直拉低。
高速 SPI,布线要短,远离开关电源噪声,否则丢数据。
SPI 没有硬件 CRC 校验,可靠性要求高时软件加校验。

SPI 对比 I2C、UART
对比项SPII2CUART
全称Serial Peripheral InterfaceInter-Integrated CircuitUniversal Asynchronous Receiver/Transmitter
信号线4 根(SCK、MOSI、MISO、CS),多从机时 CS 需增加2 根(SDA 数据线、SCL 时钟线)2 根(TX 发送、RX 接收),可共用 GND
同步/异步同步(依赖 SCK 时钟)同步(依赖 SCL 时钟)异步(无时钟线,靠双方约定波特率)
双工模式全双工(同时收发)半双工(SDA 线分时收发)全双工(TX/RX 独立,可同时收发)
典型速率最快,可达 几十 MHz中等,标准 100 kHz,快速 400 kHz,高速 3.4 MHz较慢,通常 9600 ~ 921600 bps(少数可达 Mbps)
主从关系一主多从,主机控制时钟和片选多主多从,支持总线仲裁无主从,点对点平等通信
设备寻址硬件片选线(CS/SS)选中从机软件 7 位或 10 位设备地址无寻址,直接点对点连接
应答机制无硬件 ACK(通常需软件确认)有 ACK/NACK(每字节都有应答)无硬件 ACK(可选软件校验,如校验和)
物理层电平TTL/CMOS(通常 3.3V/5V)开漏输出,需外接上拉电阻TTL/CMOS(通常 3.3V/5V)或 RS-232 电平
传输距离最短(板级通信,<30 cm)较短(板级或短距离,<1 m)较长(TTL 几米;RS-232 可达 15 m;RS-485 可达 1 km+)
协议复杂度简单(无起始/停止位,纯数据流)较复杂(起始/停止/应答/仲裁)最简单(只需约定波特率、数据位、校验、停止位)
多设备连接需多根 CS 线,扩展性一般总线型,扩展性好(最多 127/1023 个设备)只能点对点,多设备需额外切换电路
数据帧格式无固定帧格式,连续时钟移位起始位 + 地址 + 数据 + ACK + 停止位起始位 + 数据位 + 校验位 + 停止位
硬件开销引脚多,但无需上拉电阻引脚少,但需外接上拉电阻引脚少,最简单
典型应用Flash、显示屏、SD 卡、高速传感器EEPROM、RTC、温湿度传感器、OLED蓝牙模块、GPS、调试串口、RS-485 工业总线

二、W25QXX

W25QXX 系列 SPI‑NOR Flash 简介
W25QXX 是华邦 Winbond 的串行 NOR Flash,广泛用于单片机外挂存储,保存配置、日志、字库、固件镜像;充电桩、工业设备大量使用。
注意:单位 Mbit(兆比特),除以 8 才是 MB(兆字节)。
1 基础参数
供电:2.7‑3.6V(3.3V 系统),部分子系列支持 1.8V
接口:标准 SPI(Mode0/Mode3)、Dual‑SPI 双路、Quad‑SPI 四路;标准 SPI 最高 80‑104MHz华邦电子
擦写寿命:每个扇区典型 10 万次;断电数据保存 20 年电子工程世…
封装:SOIC‑8、WSON、PDIP;最常用 SOIC‑8(如 W25Q80DVSIG)
功耗:工作电流几 mA.

2 存储组织结构(全系列统一)
页 Page:256 字节:编程写入最小单元,一次页编程最多写 256 字节;超过 256 会页回绕覆盖开头。
扇区 Sector:4KB:最小擦除单元,只能按扇区擦除(指令 0x20)。
块 Block:32KB / 64KB:大块擦除(0x52、0xD8)。
⚠️NOR Flash 特性:bit 只能 1→0;要把 0 变回 1,必须执行擦除,擦完该区域全部变成 0xFF,不能字节直接擦除

W25Q80 关键参数

参数数值
容量8M-bit = 1M-Byte
页大小256 Bytes
扇区(Sector)4 KB(16 页)
块(Block)32 KB / 64 KB
SPI 模式支持 Mode 0 (0,0) 和 Mode 3 (1,1),Mode 0 最常用
时钟频率标准读(03h) ≤50MHz;Fast Read 等 ≤80MHz (2.7V) / ≤104MHz (3.0V)
供电2.7V ~ 3.6V

硬件连接(STM32F103 ↔ W25Q80)

W25Q80 引脚功能接 STM32
/CS (1)片选,低有效任意 GPIO(软件控制,如 PA4)
DO/IO1 (2)数据输出SPI1_MISO (PA6) 或 SPI2_MISO (PB14)
/WP/IO2 (3)写保护接 VCC(3.3V)或 GPIO
GND (4)GND
DI/IO0 (5)数据输入SPI1_MOSI (PA7) 或 SPI2_MOSI (PB15)
CLK (6)时钟SPI1_SCK (PA5) 或 SPI2_SCK (PB13)
/HOLD/IO3 (7)暂停接 VCC(3.3V)或 GPIO
VCC (8)电源3.3V

在这里插入图片描述

核心指令集

指令码 名称 关键说明
0x06 WREN 写使能 写 / 擦除 / 写状态寄存器前必须发,WEL 置 1
0x04 WRDI 写禁止
0x05 RDSR1 读状态寄存器 1 bit0=BUSY 忙位;bit1=WEL 写使能锁存
0x35 RDSR2 读状态寄存器 2 包含 QE (Quad 使能位)
0x01 写状态寄存器 BV/DV 版本:后面跟 SR1、SR2 两字节;RV 版本分开 0x01 和 0x31
0x03 READ 读数据 24bit 地址,连续读,无需写使能
0x02 PAGE PROG 页编程 最多写 256 字节;写前必须擦除
0x20 SE 扇区擦除 4KB,擦完全部 0xFF
0xD8 BE64K 块擦除 64KB
0xC7 CE 全片擦除 耗时很长,数秒,尽量少用
0x9F RDID 读 JEDEC ID 识别芯片型号,驱动初始化必做

三、STM32F103 SPI 设置相关

3.1 SPI 初始化结构体 SPI_InitTypeDef

typedef struct
{
  uint16_t SPI_Direction;           /*!< Specifies the SPI unidirectional or bidirectional data mode.
                                         This parameter can be a value of @ref SPI_data_direction */

  uint16_t SPI_Mode;                /*!< Specifies the SPI operating mode.
                                         This parameter can be a value of @ref SPI_mode */

  uint16_t SPI_DataSize;            /*!< Specifies the SPI data size.
                                         This parameter can be a value of @ref SPI_data_size */

  uint16_t SPI_CPOL;                /*!< Specifies the serial clock steady state.
                                         This parameter can be a value of @ref SPI_Clock_Polarity */

  uint16_t SPI_CPHA;                /*!< Specifies the clock active edge for the bit capture.
                                         This parameter can be a value of @ref SPI_Clock_Phase */

  uint16_t SPI_NSS;                 /*!< Specifies whether the NSS signal is managed by
                                         hardware (NSS pin) or by software using the SSI bit.
                                         This parameter can be a value of @ref SPI_Slave_Select_management */
 
  uint16_t SPI_BaudRatePrescaler;   /*!< Specifies the Baud Rate prescaler value which will be
                                         used to configure the transmit and receive SCK clock.
                                         This parameter can be a value of @ref SPI_BaudRate_Prescaler.
                                         @note The communication clock is derived from the master
                                               clock. The slave clock does not need to be set. */

  uint16_t SPI_FirstBit;            /*!< Specifies whether data transfers start from MSB or LSB bit.
                                         This parameter can be a value of @ref SPI_MSB_LSB_transmission */

  uint16_t SPI_CRCPolynomial;       /*!< Specifies the polynomial used for the CRC calculation. */
}SPI_InitTypeDef;

重要参数选项:
SPI_Direction 通信方向:全双工/单工
SPI_Direction_2Lines_FullDuplex 双线全双工(最常用)
SPI_Direction_2Lines_RxOnly 双线只接收
SPI_Direction_1Line_Tx 单线发送
SPI_Direction_1Line_Rx 单线接收
SPI_Mode SPI主机 / SPI从机
SPI_Mode_Master 主机模式 ✅
SPI_Mode_Slave 从机模式
SPI_DataSize 数据宽度 8bit /16bit
SPI_DataSize_8b 8 位(绝大多数外设)
SPI_DataSize_16b 16 位
SPI_CPOL CPOL 时钟极性
SPI_CPOL_Low CPOL=0,空闲 SCLK 低
SPI_CPOL_High CPOL=1,空闲 SCLK 高
SPI_CPHA CPHA 时钟相位
SPI_CPHA_1Edge CPHA=0,第一个边沿采样(Mode0)
SPI_CPHA_2Edge CPHA=1,第二个边沿采样
SPI Mode0:CPOL_Low + CPHA_1Edge
SPI_NSS NSS:硬件NSS / 软件NSS
SPI_NSS_Soft 软件 NSS(推荐):NSS 引脚作为普通 GPIO,软件控制 CS,内部 NSS 信号由软件管理
SPI_NSS_Hard 硬件 NSS:由硬件引脚自动管理 NSS,主机模式下很少用,容易踩坑。
SPI_BaudRatePrescaler 波特率预分频,SCLK = PCLK / 预分频
SPI1 时钟来自 APB2 (72M):
SPI_BaudRatePrescaler_2 36M;
Prescaler_4 18M;
Prescaler_8 9M;
Prescaler_16 4.5M;
Prescaler_32 2.25M;
Prescaler_64 1.125M;
Prescaler_128 562.5K;
Prescaler_256 281.2K.
外设速度不能太高,Flash 一般用 9M/4.5M,如果用杜邦线接使用最低频率,256分频
SPI_FirstBit MSB先行 / LSB先行
SPI_FirstBit_MSB 高位先发(绝大多数器件)
SPI_FirstBit_LSB 低位先发
SPI_CRCPolynomial CRC 多项式(通常不用)

核心库函数列表

1)初始化函数
void SPI_Init(SPI_TypeDef* SPIx, SPI_InitTypeDef* SPI_InitStruct)
功能:按照结构体配置 SPI 寄存器。

2)使能关闭 SPI 外设

void SPI_Cmd(SPI_TypeDef* SPIx, FunctionalState NewState);
SPI_Cmd(SPI1, ENABLE); 打开 SPI;DISABLE关闭。

3)数据读写函数(最核心)

void SPI_I2S_SendData(SPI_TypeDef* SPIx, uint16_t Data)
向 SPI DR 寄存器写入数据,开始移位发送。SPI 是全双工,发送同时也会接收。

uint16_t SPI_I2S_ReceiveData(SPI_TypeDef* SPIx)
读取 DR 寄存器里面已经收到的数据。
⚠️F1 标准库没有封装 “同时收发” 函数,需要自己判断标志位实现。
4)状态标志位读取

FlagStatus SPI_I2S_GetFlagStatus(SPI_TypeDef* SPIx, uint16_t SPI_I2S_FLAG)
常用标志:
SPI_I2S_FLAG_TXE:发送寄存器为空,可以发送下一个数据 ✅
SPI_I2S_FLAG_RXNE:接收寄存器非空,已经收到数据 ✅
SPI_I2S_FLAG_BSY:SPI 忙标志!非常重要,BSY=1 代表通信还在进行,不能关闭 SPI,不能 CS 拉高。

void SPI_I2S_ClearFlag(SPI_TypeDef* SPIx, uint16_t SPI_I2S_FLAG)
清除标志位;TXE/RXNE 硬件自动清零,不需要手动清。

四、工程与程序编写

4.1工程复制

复制I2C工程文件夹为SPI文件夹,I2C.uvprojx复制为SPI.uvprojx。

4.2 添加驱动文件

  1. Hardware文件夹下新建Spi和W25qxx文件夹。分别添加spi.h,spi.c,w25qxx.h,w25qxx.c文件。

spi.h

#ifndef __SPI_H
#define __SPI_H

#include "stm32f10x.h"

// SPI1 软件NSS引脚 PA4
#define SPI1_CS_PIN     GPIO_Pin_4
#define SPI1_CS_PORT    GPIOA

// 软件CS控制
#define SPI1_CS_LOW()    GPIO_ResetBits(SPI1_CS_PORT, SPI1_CS_PIN)
#define SPI1_CS_HIGH()   GPIO_SetBits(SPI1_CS_PORT, SPI1_CS_PIN)


void spi1_init(void);
uint8_t spi1_read_write_byte(uint8_t dat);



#endif

spi.c

#include "spi.h"

/**
 * @brief SPI1初始化 软件NSS Mode0 8bit主机
 */
void spi1_init(void)
{
    GPIO_InitTypeDef gpio_init_struct;
    SPI_InitTypeDef  spi_init_struct;

    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_SPI1, ENABLE);

    // PA5 SCK  AF_PP ; PA7 MOSI AF_PP
    gpio_init_struct.GPIO_Pin = GPIO_Pin_5 | GPIO_Pin_7;
    gpio_init_struct.GPIO_Mode = GPIO_Mode_AF_PP;
    gpio_init_struct.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA, &gpio_init_struct);

    // PA6 MISO 浮空输入
    gpio_init_struct.GPIO_Pin = GPIO_Pin_6;
    gpio_init_struct.GPIO_Mode = GPIO_Mode_IN_FLOATING;
    GPIO_Init(GPIOA, &gpio_init_struct);

    // PA4 CS 软件NSS 普通推挽输出
    gpio_init_struct.GPIO_Pin = SPI1_CS_PIN;
    gpio_init_struct.GPIO_Mode = GPIO_Mode_Out_PP;
    gpio_init_struct.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(SPI1_CS_PORT, &gpio_init_struct);

    SPI1_CS_HIGH(); // 默认片选拉高

    // SPI参数配置
    spi_init_struct.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
    spi_init_struct.SPI_Mode      = SPI_Mode_Master;
    spi_init_struct.SPI_DataSize  = SPI_DataSize_8b;
    spi_init_struct.SPI_CPOL      = SPI_CPOL_Low;      // Mode0  空闲 SCLK 低,
    spi_init_struct.SPI_CPHA      = SPI_CPHA_1Edge;    // 第一个边沿采样
    spi_init_struct.SPI_NSS       = SPI_NSS_Soft;      // 软件NSS,不用硬件NSS
    spi_init_struct.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_256; //72M/256
    spi_init_struct.SPI_FirstBit  = SPI_FirstBit_MSB;
    spi_init_struct.SPI_CRCPolynomial = 7;

    SPI_Init(SPI1, &spi_init_struct);
    SPI_Cmd(SPI1, ENABLE);
}

/**
 * @brief SPI1读写1字节,全双工
 * @param dat:要发送的数据
 * @retval 收到的数据
 */
uint8_t spi1_read_write_byte(uint8_t dat)
{
    u16 count = 0;
	while((SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) ) == RESET){
		count++;
		if(count>1000){//防止程序卡死
			return 0;
		}
	}
    SPI_I2S_SendData(SPI1, dat);

	count = 0;
    while((SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) ) == RESET){
		if(count>1000){//防止程序卡死
			return 0;
		}
	}
    return SPI_I2S_ReceiveData(SPI1);
}

w25qxx.h

#ifndef __W25QXX_H
#define __W25QXX_H

#include "stm32f10x.h"

// W25Qxx 指令
#define W25QXX_CMD_WREN        0x06    //写使能
#define W25QXX_CMD_WRDI        0x04    //写禁止
#define W25QXX_CMD_RDSR1       0x05    //读状态寄存器1
#define W25QXX_CMD_RDSR2       0x35    //读状态寄存器2
#define W25QXX_CMD_WRSR        0x01    //写状态寄存器(BV/DV:后接SR1,SR2两字节)
#define W25QXX_CMD_READ        0x03    //读数据
#define W25QXX_CMD_FASTREAD    0x0B    //快速读
#define W25QXX_CMD_PAGEPROG    0x02    //页编程
#define W25QXX_CMD_SECTORERASE 0x20    //4K扇区擦除
#define W25QXX_CMD_BLK64ERASE  0xD8    //64K块擦除
#define W25QXX_CMD_CHIPERASE   0xC7    //整片擦除
#define W25QXX_CMD_JEDECID     0x9F    //读JEDEC ID

// W25Q80 参数 8Mbit = 1MByte
#define W25Q80_PAGE_SIZE       256U
#define W25Q80_SECTOR_SIZE     4096U
#define W25Q80_SIZE            (1024U*1024U)

uint16_t w25qxx_read_jedec_id(void);
void w25qxx_wait_busy(void);
void w25qxx_write_enable(void);
void w25qxx_sector_erase(uint32_t addr);
void w25qxx_page_write(uint32_t addr, uint8_t *pbuf, uint16_t len);
void w25qxx_read(uint32_t addr, uint8_t *pbuf, uint16_t len);

#endif

w25qxx.c

#include "w25qxx.h"
#include "spi.h"
#include "delay.h"   //需要系统延时

/**
 * @brief 等待芯片BUSY位清零
 */
void w25qxx_wait_busy(void)
{
    uint8_t sr;
    while(1)
    {
        SPI1_CS_LOW();
        spi1_read_write_byte(W25QXX_CMD_RDSR1);
        sr = spi1_read_write_byte(0xFF);
        SPI1_CS_HIGH();

        if( (sr & 0x01) == 0 )
            break;
        delay_ms(1);
    }
}

/**
 * @brief 写使能
 */
void w25qxx_write_enable(void)
{
    SPI1_CS_LOW();
    spi1_read_write_byte(W25QXX_CMD_WREN);
    SPI1_CS_HIGH();
}

/**
 * @brief 读JEDEC ID
 * @retval 返回高字节+低字节,W25Q80返回0x4014
 */
uint16_t w25qxx_read_jedec_id(void)
{
    uint16_t id;
    uint8_t mfr,memtype,cap;

    SPI1_CS_LOW();
    spi1_read_write_byte(W25QXX_CMD_JEDECID);
    mfr    = spi1_read_write_byte(0xFF);
    memtype= spi1_read_write_byte(0xFF);
    cap    = spi1_read_write_byte(0xFF);
    SPI1_CS_HIGH();

    id = (memtype<<8) | cap;
    return id;
}

/**
 * @brief 扇区擦除 4K
 * @param addr:扇区内任意地址
 */
void w25qxx_sector_erase(uint32_t addr)
{
    addr = addr & (~0xFFFU); // 强制对齐到4KB扇区首地址,低12位清零
	w25qxx_wait_busy();
    w25qxx_write_enable();

    SPI1_CS_LOW();
    spi1_read_write_byte(W25QXX_CMD_SECTORERASE);
    spi1_read_write_byte( (addr>>16) & 0xFF );
    spi1_read_write_byte( (addr>>8)  & 0xFF );
    spi1_read_write_byte( addr & 0xFF );
    SPI1_CS_HIGH();

    w25qxx_wait_busy();
}

/**
 * @brief 页写,一页最多256字节,不要跨页
 * @param addr 起始地址
 * @param pbuf 数据源
 * @param len 1~256
 */
void w25qxx_page_write(uint32_t addr, uint8_t *pbuf, uint16_t len)
{
    uint16_t i;
    if(len ==0 || len>256) return;

    w25qxx_wait_busy();
    w25qxx_write_enable();

    SPI1_CS_LOW();
    spi1_read_write_byte(W25QXX_CMD_PAGEPROG);
    spi1_read_write_byte( (addr>>16)&0xFF );
    spi1_read_write_byte( (addr>>8)&0xFF );
    spi1_read_write_byte( addr&0xFF );

    for(i=0;i<len;i++)
    {
        spi1_read_write_byte(pbuf[i]);
    }
    SPI1_CS_HIGH();

    w25qxx_wait_busy();
}

/**
 * @brief 连续读数据
 * @param addr 起始地址
 * @param pbuf 接收缓存
 * @param len 读取字节数
 */
void w25qxx_read(uint32_t addr, uint8_t *pbuf, uint16_t len)
{
    uint16_t i;
    SPI1_CS_LOW();
    spi1_read_write_byte(W25QXX_CMD_READ);
    spi1_read_write_byte( (addr>>16)&0xFF );
    spi1_read_write_byte( (addr>>8)&0xFF );
    spi1_read_write_byte( addr&0xFF );

    for(i=0;i<len;i++)
    {
        pbuf[i] = spi1_read_write_byte(0xFF);
    }
    SPI1_CS_HIGH();
}

main.c

#include "stm32f10x.h"  
#include "led.h"
#include "delay.h"
#include "key.h"
#include "usart.h"
#include "exti.h"
#include "time.h"
#include <stdio.h>
#include "i2c_sim.h"
#include "at24c02.h"
#include "spi.h"
#include "w25qxx.h"


u8 flash_read_buffer[16]={0};
u8 flash_write_buffer[16]={"w25q80 test!"};

int main(void)
{		
	u16 data16,i=0;
	u32 add =0;
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);// 设置中断优先级分组4
	led_init();
	delay_init();
	usart1_init(115200);
	//i2c_sim_init();
	//at24c02_test();
	spi1_init();
	printf("w25q80 test \r\n");
	data16 = w25qxx_read_jedec_id();
	printf("0x %04x  %d \r\n",data16,data16);	
	w25qxx_read(add,flash_read_buffer,16);
	printf("写之前读 %s\r\n",flash_read_buffer);

	while(1)
	{		
		delay_ms(1000);		
		i++;
		if(i==10){
			//w25qxx_sector_erase(add);//第一次写可以,写过一次后要先擦除才能写
			w25qxx_page_write(add,flash_write_buffer,sizeof(flash_write_buffer));
			w25qxx_read(add,flash_read_buffer,sizeof(flash_read_buffer));
			printf("写之后读 %s\r\n",flash_read_buffer);
			return 0;
		}		
	}
	
}


/*

w25q80 test 
0x 4014  16404 
写之前读 ????????????????
写之后读 w25q80 test!


*/



4.3 添加库文件,工程文件,和文件包含路径

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

4.4 编译烧录

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

串口助手设置的参数要和程序里面一致,选择串口要正确(插拨一下看电脑里面的设置管理器里面的端口)。

启动会打印W25Q80的id号,判断出型号确实是对的。下面是读和写的测试,可以得出写入和读出一致,写入成功了!
关于spi和w25qxx的内容还是很多的,上面只是简要介绍,还是要看芯片手册!

资料下载
参考资料网盘 :
链接 : https://pan.baidu.com/s/1BrH0JbUY6z7j7gAihTa4Dw
提取码: qnws

本节课完整工程例程网盘:
链接: https://pan.baidu.com/s/1rl6EIqi9m6kQ1JFjuSqINg
提取码: bumc

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

oshan2012

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值