这里用的是野火霸道的板子,搭载了STM32F103ZET6. 目前所学知识尚且较为简单,这里的笔记仅仅记录一下学习过程中的代码,方便以后回头复习以及复制。主要参考资料——《零死角玩转 STM32F103—霸道_V2 开发板》,视频——150集-野火F103霸道/指南者视频教程-中级篇

硬件IIC读写EEPROM

原理参考:IIC完整讲解

STM32 BUG参考:

  1. STM32 I2C控制器使用库函数时卡在CheckEvent的解决建议
  2. STM32_I2C,不稳定,死在I2C_CheckEvent解决办法

所用EEPROM型号为AT24C02,有32页,每页8字节,需8位地址寻址。EEPROM部分的原理图如下所示。图中只有一个从机(即EEPROM),当实际电路中有多个从机时,SCL和SDA两根线上不需在R218、R219的基础上再加入上拉电阻。
EEPROM部分原理图

宏部分

#define EEPROM_I2Cx                 I2C1
#define EEPROM_I2C_ClockCmd         RCC_APB1PeriphClockCmd
#define EEPROM_I2C_CLK              RCC_APB1Periph_I2C1
#define EEPROM_I2C_GPIO_ClockCmd    RCC_APB2PeriphClockCmd
#define EEPROM_I2C_GPIO_CLK         RCC_APB2Periph_GPIOB
#define EEPROM_I2C_SCL_PORT         GPIOB
#define EEPROM_I2C_SCL_PIN          GPIO_Pin_6
#define EEPROM_I2C_SDA_PORT         GPIOB
#define EEPROM_I2C_SDA_PIN          GPIO_Pin_7
#define I2C_Speed       400000 // <= 400kbit
#define I2Cx_OWN_ADDR1  0x00 // STM32设备本身的地址
#define I2C_PageSize    8 // 根据AT24C01/02手册调整
#define EEPROM_ADDR     0xA0 // EEPROM设备8位地址
#define I2C_TIMEOUT_LOOP    0x2000 // 超时设置

核心函数

static uint32_t I2C_VAR_TIMEOUT = I2C_TIMEOUT_LOOP;

/**
 * @brief 初始化I2C EEPROM
 * 
 */
void I2C_EEPROM_Init(void){
    GPIO_InitTypeDef GPIO_InitStruct;
    I2C_InitTypeDef I2C_InitStruct;
    EEPROM_I2C_ClockCmd(EEPROM_I2C_CLK, ENABLE);
    EEPROM_I2C_GPIO_ClockCmd(EEPROM_I2C_GPIO_CLK, ENABLE);
    // GPIO初始化
    // SCL
    GPIO_InitStruct.GPIO_Pin = EEPROM_I2C_SCL_PIN; // SCL用于发送同步时钟脉冲信号
    GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_OD; // 开漏输出
    GPIO_Init(EEPROM_I2C_SCL_PORT, &GPIO_InitStruct);
    // SDA
    GPIO_InitStruct.GPIO_Pin = EEPROM_I2C_SDA_PIN;
    GPIO_Init(EEPROM_I2C_SDA_PORT, &GPIO_InitStruct);
    // I2C初始化
    I2C_InitStruct.I2C_Mode = I2C_Mode_I2C;
    I2C_InitStruct.I2C_DutyCycle = I2C_DutyCycle_2; // SCL低电平占空比
    I2C_InitStruct.I2C_OwnAddress1 = I2Cx_OWN_ADDR1;
    I2C_InitStruct.I2C_Ack = I2C_Ack_Enable;
    I2C_InitStruct.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;
    I2C_InitStruct.I2C_ClockSpeed = I2C_Speed;
    I2C_Init(EEPROM_I2Cx, &I2C_InitStruct);
    I2C_Cmd(EEPROM_I2Cx, ENABLE);
}

/**
 * @brief 超时自动执行函数
 * 
 * @return (uint8_t) 1
 */
static uint8_t I2C_TIMEOUT_Callback(void){
   printf("I2C TIMEOUT!\n");
   return 1;
}

/**
 * @brief 向EEPROM写入一个字节数据
 * 
 * @param addr 写入地址
 * @param data 写入数据
 * @return uint8_t 是否发生错误,1-错误 0-正常
 */
uint8_t EEPROM_Byte_Write(u16 addr, u8 data){
    // 括号表示从机信号,A表示ACK,N表示NACK
    // S - addr - w - (A) - w_addr - (A) - data - (A) - P
    I2C_GenerateSTART(EEPROM_I2Cx, ENABLE);
    I2C_VAR_TIMEOUT = I2C_TIMEOUT_LOOP;
    while(I2C_CheckEvent(EEPROM_I2Cx, I2C_EVENT_MASTER_MODE_SELECT) == ERROR){ // EV5
        if ((I2C_VAR_TIMEOUT--) == 0){
            return I2C_TIMEOUT_Callback();
        }
    }
    I2C_Send7bitAddress(EEPROM_I2Cx, EEPROM_ADDR, I2C_Direction_Transmitter);
    I2C_VAR_TIMEOUT = I2C_TIMEOUT_LOOP;
    while(I2C_CheckEvent(EEPROM_I2Cx, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED) == ERROR){ // EV6
        if ((I2C_VAR_TIMEOUT--) == 0){
            return I2C_TIMEOUT_Callback();
        }
    }
    I2C_SendData(EEPROM_I2Cx, addr);
    I2C_VAR_TIMEOUT = I2C_TIMEOUT_LOOP;
    while(I2C_CheckEvent(EEPROM_I2Cx, I2C_EVENT_MASTER_BYTE_TRANSMITTING) == ERROR){ // EV8
        if ((I2C_VAR_TIMEOUT--) == 0){
            return I2C_TIMEOUT_Callback();
        }
    }
    I2C_SendData(EEPROM_I2Cx, data);
    I2C_VAR_TIMEOUT = I2C_TIMEOUT_LOOP;
    while(I2C_CheckEvent(EEPROM_I2Cx, I2C_EVENT_MASTER_BYTE_TRANSMITTED) == ERROR){
        if ((I2C_VAR_TIMEOUT--) == 0){
            return I2C_TIMEOUT_Callback();
        }
    }
    I2C_VAR_TIMEOUT = I2C_TIMEOUT_LOOP;
    I2C_GenerateSTOP(EEPROM_I2Cx, ENABLE);
    return 0;
}

/**
 * @brief 写入数组数据到EEPROM
 * 
 * @param addr 写入data的起始地址,使用 page write 要求addr % 8 == 0,与count无关。
 * @param data 数组地址
 * @param count 所用芯片要求count <= 8
 * @return uint8_t 是否发生错误,1-错误 0-正常
 */
uint8_t EEPROM_Page_Write(u16 addr, u8 *data, u8 count){
    // S - addr - w - (A) - w_addr - (A) - data - (A) - data - (A) - ... - data - (A) -  P
    I2C_GenerateSTART(EEPROM_I2Cx, ENABLE);
    I2C_VAR_TIMEOUT = I2C_TIMEOUT_LOOP;
    while(I2C_CheckEvent(EEPROM_I2Cx, I2C_EVENT_MASTER_MODE_SELECT) == ERROR){ // EV5
        if ((I2C_VAR_TIMEOUT--) == 0){
            return I2C_TIMEOUT_Callback();
        }
    }
    I2C_Send7bitAddress(EEPROM_I2Cx, EEPROM_ADDR, I2C_Direction_Transmitter);
    I2C_VAR_TIMEOUT = I2C_TIMEOUT_LOOP;
    while(I2C_CheckEvent(EEPROM_I2Cx, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED) == ERROR){ // EV6
        if ((I2C_VAR_TIMEOUT--) == 0){
            return I2C_TIMEOUT_Callback();
        }
    }
    I2C_SendData(EEPROM_I2Cx, addr);
    I2C_VAR_TIMEOUT = I2C_TIMEOUT_LOOP;
    while(I2C_CheckEvent(EEPROM_I2Cx, I2C_EVENT_MASTER_BYTE_TRANSMITTING) == ERROR){ // EV8
        if ((I2C_VAR_TIMEOUT--) == 0){
            return I2C_TIMEOUT_Callback();
        }
    }
    while (count){
        I2C_SendData(EEPROM_I2Cx, *data);
        I2C_VAR_TIMEOUT = I2C_TIMEOUT_LOOP;
        while(I2C_CheckEvent(EEPROM_I2Cx, I2C_EVENT_MASTER_BYTE_TRANSMITTED) == ERROR){
            if ((I2C_VAR_TIMEOUT--) == 0){
                return I2C_TIMEOUT_Callback();
            }
        }
        count--;
        data++;
    }
    I2C_GenerateSTOP(EEPROM_I2Cx, ENABLE);
    return 0;
}

/**
 * @brief 读取EEPROM
 * 
 * @param addr 读取地址
 * @param data 接收数据所用数组
 * @param count 接收数据量
 * @return uint8_t 是否发生错误,1-错误 0-正常
 */
uint8_t EEPROM_Read(u16 addr, u8 *data, u16 count){
    // S - addr - w - (A) - w_addr - (A) - \n
    // S - addr - r - (A) - (data) - N - P
    I2C_GenerateSTART(EEPROM_I2Cx, ENABLE);
    I2C_VAR_TIMEOUT = I2C_TIMEOUT_LOOP;
    while(I2C_CheckEvent(EEPROM_I2Cx, I2C_EVENT_MASTER_MODE_SELECT) == ERROR){ // EV5
        if ((I2C_VAR_TIMEOUT--) == 0){
            return I2C_TIMEOUT_Callback();
        }
    }
    I2C_Send7bitAddress(EEPROM_I2Cx, EEPROM_ADDR, I2C_Direction_Transmitter);
    I2C_VAR_TIMEOUT = I2C_TIMEOUT_LOOP;
    while(I2C_CheckEvent(EEPROM_I2Cx, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED) == ERROR){ // EV6
        if ((I2C_VAR_TIMEOUT--) == 0){
            return I2C_TIMEOUT_Callback();
        }
    }
    I2C_SendData(EEPROM_I2Cx, addr);
    I2C_VAR_TIMEOUT = I2C_TIMEOUT_LOOP;
    while(I2C_CheckEvent(EEPROM_I2Cx, I2C_EVENT_MASTER_BYTE_TRANSMITTING) == ERROR){ // EV8
        if ((I2C_VAR_TIMEOUT--) == 0){
            return I2C_TIMEOUT_Callback();
        }
    }
    // 二次起始信号
    I2C_GenerateSTART(EEPROM_I2Cx, ENABLE);
    I2C_VAR_TIMEOUT = I2C_TIMEOUT_LOOP;
    while(I2C_CheckEvent(EEPROM_I2Cx, I2C_EVENT_MASTER_MODE_SELECT) == ERROR){ // EV5
        if ((I2C_VAR_TIMEOUT--) == 0){
            return I2C_TIMEOUT_Callback();
        }
    }
    I2C_Send7bitAddress(EEPROM_I2Cx, EEPROM_ADDR, I2C_Direction_Receiver);
    I2C_VAR_TIMEOUT = I2C_TIMEOUT_LOOP;
    while(I2C_CheckEvent(EEPROM_I2Cx, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED) == ERROR){ //EV6
        if ((I2C_VAR_TIMEOUT--) == 0){
            return I2C_TIMEOUT_Callback();
        }
    }

    while(count){
        if (count == 1){ // 待读取的最后一个
            I2C_AcknowledgeConfig(EEPROM_I2Cx, DISABLE); // 产生NACK信号
        }
        I2C_VAR_TIMEOUT = I2C_TIMEOUT_LOOP;
        while(I2C_CheckEvent(EEPROM_I2Cx, I2C_EVENT_MASTER_BYTE_RECEIVED) == ERROR){ // EV7
            if ((I2C_VAR_TIMEOUT--) == 0){
                return I2C_TIMEOUT_Callback();
            }
        }
        *data = I2C_ReceiveData(EEPROM_I2Cx);
        data++;
        count--;
    }

    I2C_GenerateSTOP(EEPROM_I2Cx, ENABLE);
    I2C_AcknowledgeConfig(EEPROM_I2Cx, ENABLE); // 再设置回去,自动产生ACK信号
    return 0;
}

/**
 * @brief 等待EEPROM内部写入完成。
 * @return uint8_t 是否发生错误,1-错误 0-正常
 */
uint8_t EEPROM_Wait_For_Writing(void){
    do{
        I2C_GenerateSTART(EEPROM_I2Cx, ENABLE);
        I2C_VAR_TIMEOUT = I2C_TIMEOUT_LOOP;
        while(I2C_GetFlagStatus(EEPROM_I2Cx, I2C_FLAG_SB) == RESET){ // EV5
        // while(I2C_CheckEvent(EEPROM_I2Cx, I2C_EVENT_MASTER_MODE_SELECT) == ERROR); // 卡死
            if ((I2C_VAR_TIMEOUT--) == 0){
                return I2C_TIMEOUT_Callback();
            }
        }
        I2C_Send7bitAddress(EEPROM_I2Cx, EEPROM_ADDR, I2C_Direction_Transmitter);
    }while(I2C_GetFlagStatus(EEPROM_I2Cx, I2C_FLAG_ADDR) == RESET); // EV6
    // while(I2C_CheckEvent(EEPROM_I2Cx, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED) == ERROR); // 卡死
    I2C_GenerateSTOP(EEPROM_I2Cx, ENABLE);
    return 0;
}

主函数

int main(void){
    u32 i = 0;
    u8 readData[10] = {0};
    u8 writeData[8] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x00, 0x01, 0x02};
    USART_Config();
    I2C_EEPROM_Init();
    printf("Writing...\n");
    // EEPROM_Byte_Write(11, 0xff);
    // EEPROM_WAIT_Done();
    EEPROM_Page_Write(0, writeData, 8);
    EEPROM_Wait_For_Writing();
    EEPROM_Read(0, readData, 8);
    printf("Reading...\n");
    for (i = 0; i < 8; i++){
        printf("%x ", readData[i]);
    }
    while(1);
}

SPI读写FLASH

SPI与IIC较为类似。所用FLASH为W25Q64。FLASH部分的原理图如下。
FLASH部分的原理图

宏部分

// Basic
#define FLASH_SPIx          SPI1
#define FLASH_SPI_ClockCmd  RCC_APB2PeriphClockCmd
#define FLASH_SPI_CLK       RCC_APB2Periph_SPI1
// CS(Chip select)/SS/NSS (GPIO)
#define FLASH_SPI_CS_ClockCmd   RCC_APB2PeriphClockCmd
#define FLASH_SPI_CS_CLK        RCC_APB2Periph_GPIOA
#define FLASH_SPI_CS_PORT       GPIOA
#define FLASH_SPI_CS_PIN        GPIO_Pin_4
// SCK
#define FLASH_SPI_SCK_ClockCmd  RCC_APB2PeriphClockCmd
#define FLASH_SPI_SCK_CLK       RCC_APB2Periph_GPIOA
#define FLASH_SPI_SCK_PORT      GPIOA
#define FLASH_SPI_SCK_PIN       GPIO_Pin_5
// MISO
#define FLASH_SPI_MISO_ClockCmd  RCC_APB2PeriphClockCmd
#define FLASH_SPI_MISO_CLK       RCC_APB2Periph_GPIOA
#define FLASH_SPI_MISO_PORT      GPIOA
#define FLASH_SPI_MISO_PIN       GPIO_Pin_6
// MOSI
#define FLASH_SPI_MOSI_ClockCmd  RCC_APB2PeriphClockCmd
#define FLASH_SPI_MOSI_CLK       RCC_APB2Periph_GPIOA
#define FLASH_SPI_MOSI_PORT      GPIOA
#define FLASH_SPI_MOSI_PIN       GPIO_Pin_7

#define FLASH_SPI_S        GPIO_ResetBits(FLASH_SPI_CS_PORT, FLASH_SPI_CS_PIN)
#define FLASH_SPI_P        GPIO_SetBits(FLASH_SPI_CS_PORT, FLASH_SPI_CS_PIN)

#define SPI_FLAG_TIMEOUT    0x1000
#define FLASH_SPI_PageSize  256

// FLASH W25X CMD
#define W25X_WriteEnable      0x06
#define W25X_WriteDisable     0x04
#define W25X_ReadStatusReg    0x05
#define W25X_WriteStatusReg   0x01
#define W25X_ReadData         0x03
#define W25X_FastReadData     0x0B
#define W25X_FastReadDual     0x3B
#define W25X_PageProgram      0x02
#define W25X_BlockErase       0xD8
#define W25X_SectorErase      0x20
#define W25X_ChipErase        0xC7
#define W25X_PowerDown        0xB9
#define W25X_ReleasePowerDown 0xAB
#define W25X_DeviceID         0xAB
#define W25X_ManufactDeviceID 0x90
#define W25X_JedecDeviceID    0x9F
#define Dummy_Byte            0xFF

#define W25X_FLAG_BSY         0x01 // BUSY 位

核心部分

static uint32_t SPI_TIMEOUT;

void FLASH_SPI_Init(void){
    SPI_InitTypeDef SPI_struct;
    GPIO_InitTypeDef GPIO_struct;
    FLASH_SPI_ClockCmd(FLASH_SPI_CLK, ENABLE);
    FLASH_SPI_CS_ClockCmd(FLASH_SPI_CS_CLK, ENABLE);
    FLASH_SPI_CS_ClockCmd(FLASH_SPI_SCK_CLK, ENABLE);
    FLASH_SPI_CS_ClockCmd(FLASH_SPI_MISO_CLK, ENABLE);
    FLASH_SPI_CS_ClockCmd(FLASH_SPI_MOSI_CLK, ENABLE);
    // CS config
    GPIO_struct.GPIO_Pin = FLASH_SPI_CS_PIN;
    GPIO_struct.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_struct.GPIO_Mode = GPIO_Mode_Out_PP;
    GPIO_Init(FLASH_SPI_CS_PORT, &GPIO_struct);
    // SCK
    GPIO_struct.GPIO_Pin = FLASH_SPI_SCK_PIN;
    GPIO_struct.GPIO_Mode = GPIO_Mode_AF_PP;
    GPIO_Init(FLASH_SPI_SCK_PORT, &GPIO_struct);
    // MOSI
    GPIO_struct.GPIO_Pin = FLASH_SPI_MOSI_PIN;
    GPIO_Init(FLASH_SPI_MOSI_PORT, &GPIO_struct);
    // MISO
    GPIO_struct.GPIO_Pin = FLASH_SPI_MISO_PIN;
    GPIO_Init(FLASH_SPI_MISO_PORT, &GPIO_struct);
    FLASH_SPI_P;
    SPI_struct.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
    SPI_struct.SPI_Mode = SPI_Mode_Master;
    SPI_struct.SPI_DataSize = SPI_DataSize_8b;
    SPI_struct.SPI_CPOL = SPI_CPOL_High; // clock polarity 极性. 空闲时SCK为高电平
    SPI_struct.SPI_CPHA = SPI_CPHA_2Edge; // clock phase 相位. 数据捕获于第二个时钟沿
    SPI_struct.SPI_NSS = SPI_NSS_Soft; // NSS引脚软件控制
    SPI_struct.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_2; // SPI时钟2分频
    SPI_struct.SPI_FirstBit = SPI_FirstBit_MSB; // MSB先行 数据高位先发送
    SPI_struct.SPI_CRCPolynomial = 7; // CRC校验成员值 若要启用,还要用SPI_TransmitCRC和SPI_CalculateCRC函数来启用发送CRC功能
    SPI_Init(FLASH_SPIx, &SPI_struct);
    SPI_Cmd(FLASH_SPIx, ENABLE);
}


static uint8_t SPI_TIMEOUT_Callback(u8 code){
    printf("SPI_ERROR, Code: %d\n", code);
    return 1;
}


static void FLASH_ERROR_LOG(char* info){
    printf("FLASH Error log: %s\n", info);
}


/**
 * @brief 使用SPI发送1byte数据
 * 
 * @param data uint8_t
 * @return uint8_t 接收到的数据
 */
uint8_t FLASH_SPI_Send_Byte(u8 data){
    SPI_TIMEOUT = SPI_FLAG_TIMEOUT;
    while(SPI_I2S_GetFlagStatus(FLASH_SPIx, SPI_I2S_FLAG_TXE) == RESET){
        if ((SPI_TIMEOUT--) == 0) return SPI_TIMEOUT_Callback(0);
    }
    SPI_I2S_SendData(FLASH_SPIx, data);
    SPI_TIMEOUT = SPI_FLAG_TIMEOUT;
    while(SPI_I2S_GetFlagStatus(FLASH_SPIx, SPI_I2S_FLAG_RXNE) == RESET){
        if ((SPI_TIMEOUT--) == 0) return SPI_TIMEOUT_Callback(1);
    }
    return SPI_I2S_ReceiveData(FLASH_SPIx);
}


/**
 * @brief 读取一个字节的数据
 * 
 * @return uint8_t 
 */
uint8_t FLASH_SPI_Read_Byte(void){
    // STM32必须要发送数据才能使时钟变化,从而接收数据
    return FLASH_SPI_Send_Byte(Dummy_Byte);
}


void FLASH_SPI_WriteEnable(void){
    FLASH_SPI_S;
    FLASH_SPI_Send_Byte(W25X_WriteEnable);
    FLASH_SPI_P;
}


/**
 * @brief 读取FLASH的ID
 * 
 * @return uint32_t 
 */
uint32_t FLASH_SPI_ReadID(void){
    u32 ID = 0;
    FLASH_SPI_S;
    FLASH_SPI_Send_Byte(W25X_JedecDeviceID);
    ID = FLASH_SPI_Send_Byte(Dummy_Byte); // 00 00 00 AA
    ID <<= 8; // 00 00 00 AA << 8    ->    00 00 AA 00
    ID |= FLASH_SPI_Send_Byte(Dummy_Byte); // 00 00 AA 00 | 00 00 00 BB
    ID <<= 8; // 00 00 AA BB << 8    ->    00 AA BB 00
    ID |= FLASH_SPI_Send_Byte(Dummy_Byte); // 00 AA BB 00 | 00 00 00 CC
    FLASH_SPI_P;
    return ID; // 00 AA BB CC
}


/**
 * @brief 等待FLASH写入完成
 * 
 */
void FLASH_SPI_Wait_For_Writing(void){
    u8 Flash_status = 0;
    FLASH_SPI_S;
    FLASH_SPI_Send_Byte(W25X_ReadStatusReg);
    do{
        Flash_status = FLASH_SPI_Send_Byte(Dummy_Byte);
    }while((Flash_status & W25X_FLAG_BSY) == SET);
    FLASH_SPI_P;
}


/**
 * @brief 擦除扇区 
 * 
 * @param SectorAddr 扇区地址 (需对齐到4KB)
 */
void FLASH_SPI_SectorErase(u32 SectorAddr){
    FLASH_SPI_WriteEnable();
    FLASH_SPI_Wait_For_Writing();
    FLASH_SPI_S;
    FLASH_SPI_Send_Byte(W25X_SectorErase);
    // 从高位到低位发送擦除扇区地址
    FLASH_SPI_Send_Byte((SectorAddr & 0xFF0000) >> 16);
    FLASH_SPI_Send_Byte((SectorAddr & 0xFF00) >> 8);
    FLASH_SPI_Send_Byte(SectorAddr & 0xFF);
    FLASH_SPI_P;
    FLASH_SPI_Wait_For_Writing();
}


/**
 * @brief 按页写入数据 (调用前应先擦除扇区)
 * 
 * @param pBuffer 数据指针
 * @param WriteAddr 地址
 * @param ByteCount 长度 不可超过页大小
 */
void FLASH_SPI_Page_Write(u8* pBuffer, u32 WriteAddr, u16 ByteCount){
    FLASH_SPI_WriteEnable();
    FLASH_SPI_S;
    FLASH_SPI_Send_Byte(W25X_PageProgram);
    FLASH_SPI_Send_Byte((WriteAddr & 0xFF0000) >> 16);
    FLASH_SPI_Send_Byte((WriteAddr & 0xFF00) >> 8);
    FLASH_SPI_Send_Byte(WriteAddr & 0xFF);
    if (ByteCount > FLASH_SPI_PageSize){ // 写入256个字节限制
        ByteCount = FLASH_SPI_PageSize;
        FLASH_ERROR_LOG("FUNC: FLASH_SPI_Page_Write: ByteCount > FLASH_SPI_PageSize !");
    }
    while (ByteCount--){
        FLASH_SPI_Send_Byte(*pBuffer);
        pBuffer++;
    }
    FLASH_SPI_P;
    FLASH_SPI_Wait_For_Writing();
}


/**
 * @brief 不定量数据写入 (调用前应先擦除扇区)
 * 
 * @param pBuffer 数据指针
 * @param WriteAddr 地址
 * @param ByteCount 长度 (无限制)
 */
void FLASH_SPI_Buffer_Write(u8* pBuffer, u32 WriteAddr, u16 ByteCount){
    u8 NPages = 0, Nsingle = 0, Addr = 0, count = 0, tmp = 0;
    Addr = WriteAddr % FLASH_SPI_PageSize;
    count = FLASH_SPI_PageSize - Addr;
    NPages = ByteCount / FLASH_SPI_PageSize;
    Nsingle = ByteCount % FLASH_SPI_PageSize;
    if (Addr == 0){ // 地址对齐
        if (NPages == 0){ // 单页
            FLASH_SPI_Page_Write(pBuffer, WriteAddr, ByteCount);
        }else{ // 多页写入
            while (NPages--){
                FLASH_SPI_Page_Write(pBuffer, WriteAddr, FLASH_SPI_PageSize);
                WriteAddr += FLASH_SPI_PageSize;
                pBuffer += FLASH_SPI_PageSize;
            }
        }
    }else{ // 不对齐
        if (NPages == 0){
            if (Nsingle > count){
                tmp = Nsingle - count;
                FLASH_SPI_Page_Write(pBuffer, WriteAddr, count);
                WriteAddr += count;
                pBuffer += count;
                FLASH_SPI_Page_Write(pBuffer, WriteAddr, tmp);
            }else{
                FLASH_SPI_Page_Write(pBuffer, WriteAddr, ByteCount);
            }
        }else{
            ByteCount -= count;
            NPages = ByteCount / FLASH_SPI_PageSize;
            Nsingle = ByteCount % FLASH_SPI_PageSize;
            FLASH_SPI_Page_Write(pBuffer, WriteAddr, count);
            WriteAddr += count;
            pBuffer += count;
            while (NPages--){
                FLASH_SPI_Page_Write(pBuffer, WriteAddr, FLASH_SPI_PageSize);
                WriteAddr += FLASH_SPI_PageSize;
                pBuffer += FLASH_SPI_PageSize;
            }
            if (Nsingle != 0){ // 写完多余的页
                FLASH_SPI_Page_Write(pBuffer, WriteAddr, Nsingle);
            }
        }
    }
}



/**
 * @brief 读取FLASH
 * 
 * @param pBuffer 存储数据指针
 * @param ReadAddr 读取地址
 * @param ByteCount 读取长度
 */
void FLASH_SPI_Buffer_Read(u8* pBuffer, u32 ReadAddr, u16 ByteCount){
    FLASH_SPI_S;
    FLASH_SPI_Send_Byte(W25X_ReadData);
    FLASH_SPI_Send_Byte((ReadAddr & 0xFF0000) >> 16);
    FLASH_SPI_Send_Byte((ReadAddr & 0xFF00) >> 8);
    FLASH_SPI_Send_Byte(ReadAddr & 0xFF);
    while (ByteCount--){
        *pBuffer = FLASH_SPI_Send_Byte(Dummy_Byte);
        pBuffer++;
    }
    FLASH_SPI_P;
}

主函数

int main(void){
    u32 FLASH_ID;
    u8 TX_Buffer[64] = "Hello world.";
    u8 RX_Buffer[64] = {0};
    USART_Config();
    printf("start...\n");
    FLASH_SPI_Init();
    FLASH_ID = FLASH_SPI_ReadID();
    printf("Device ID: %x\n", FLASH_ID);
    if (FLASH_ID == 0xEF4017){
        printf("w25Q64 Detected.\n");
        FLASH_SPI_SectorErase(0);
        FLASH_SPI_Buffer_Write(TX_Buffer, 0, 13);
        printf("Write: %s\n", TX_Buffer);
        FLASH_SPI_Buffer_Read(RX_Buffer, 0, 13);
        printf("Read: %s\n", RX_Buffer);
    }else{
        printf("Error !\n");
    }
    printf("end...\n");
}

注:根据本FLASH芯片手册知,每个扇区大小为4kB,所以扇区0的地址为0,扇区1的地址为4096,以此类推。