目录

19.01_SPI-读写SPI_FLASH

测试步骤

初始化SPI,并和SPI_FLASH通信。

实测数据

ID = ef4018

testflg = 2

测试通过

参考代码

19.01_SPI_SendData.c

/**
 * @brief SPI-读写SPI_FLASH
 * @details
 * 初始化SPI,并和SPI_FLASH通信。
 */
#include "lks32mc09x_lib.h"
uint8_t write_data[256] = {0};
uint8_t read_data[256] = {0};
volatile uint32_t testflg = 0; // 测试状态,0空闲 1进行中 2写入数据&读回数据一致 3写入数据&读回数据不一致
volatile uint32_t id;

void SoftDelaymS(uint32_t n);

uint32_t SPI_FLASH_ReadID(void);
void SPI_WaitForWriteEnd(void);
void SPI_FLASH_WriteEnable(void);
void SPI_FLASH_SectorErase(u32 SectorAddr);
void SPI_FLASH_BlockErase64k(u32 SectorAddr);
void SPI_Write_Data(uint32_t addr, uint8_t *writeBuff, uint32_t numByteToWrite);
void SPI_Read_Data(uint32_t addr, uint8_t *readBuff, uint32_t numByteToRead);

/**
 * @brief 主函数,用于测试HSI时钟输出及GPIO翻转
 */
int main(void)
{
    {
        SPI_InitTypeDef SPI_InitStruct;

        SPI_StructInit(&SPI_InitStruct);             // SPI结构体初始化
        SPI_InitStruct.Duplex = SPI_Full;            // 双工模式设置
        SPI_InitStruct.Mode = SPI_Master;            // master主机模式
        SPI_InitStruct.EN = ENABLE;                  // 使能SPI模块
        SPI_InitStruct.TRANS_MODE = SPI_DMA_DISABLE; // 选择SPI搬移方式,由MCU搬运数据到SPI
        SPI_InitStruct.Trig = 0;                     // 内部自动触发传输 
        SPI_InitStruct.ENDIAN = SPI_FIRSTSEND_MSB;   // 该字节先发送 LSB
        SPI_InitStruct.CPHA = 0;                     // 第一个沿为发送数据时刻
        SPI_InitStruct.CPOL = 0;                     // CLK默认高电平
        SPI_InitStruct.BaudRate = 100;                // SPI 传输速度 = 系统时钟 / (2*(BAUD + 1))
        SPI_InitStruct.ByteLength = 8;               // 发送8个字节
        SPI_InitStruct.IRQEna = DISABLE;             // 关闭SPI各中断
        SPI_Init(SPI0, &SPI_InitStruct);             // SPI初始化程序
    }
    GPIO_Config(GPIO1, GPIO_PinSource_1, GPIO_Mode_OUT, GPIO_AF_GPIO); // CS
    GPIO_Config(GPIO0, GPIO_PinSource_14, GPIO_Mode_OUT, GPIO_AF_SPI); // CLK 
    GPIO_Config(GPIO1, GPIO_PinSource_0, GPIO_Mode_IN, GPIO_AF_SPI);   // DI 
    GPIO_Config(GPIO0, GPIO_PinSource_15, GPIO_Mode_OUT, GPIO_AF_SPI); // DO 
    GPIO_SetBits(GPIO1, GPIO_Pin_1);
    for (int i = 0; i < 256; i++)
    {
        write_data[i] = i; // 确保每次写入的数据都不一样
    }
    SPI0->IE = BIT2;

    while (1)
    {
        if (testflg == 1)
        {
            id = SPI_FLASH_ReadID();
            id = SPI_FLASH_ReadID();
            SPI_FLASH_BlockErase64k(0);
            for (int len = 1; len < 256; len++) // 从1个数据到32个数据进行测试,以确保不同数据长度下i2c通信是否正常
            {
                for (int i = 0; i < len; i++)
                {
                    write_data[i] = i + len; // 确保每次写入的数据都不一样
                }
                SPI_Write_Data(len << 8, write_data, len);
                SPI_Read_Data(len << 8, read_data, len);
                for (int i = 0; i < len; i++)
                {
                    if (read_data[i] != write_data[i])
                    {
                        testflg = 3;
                    }
                }
            }
            if (testflg != 3)
            {
                testflg = 2; // 测试完成,并且没有出现错误
            }
        }
    }
}
uint8_t SPI_FLASH_Send_Byte(uint8_t val)
{
    SPI0->IE = BIT2;
    SPI0->TX_DATA = val;
    while ((SPI0->IE & BIT2) == 0)
        ;
    val = SPI0->RX_DATA;
    return val;
}
/**
 * @brief  读取设备ID
 * @return 设备ID
 */
uint32_t SPI_FLASH_ReadID(void)
{
    uint32_t temp = 0, temp0 = 0, temp1 = 0, temp2 = 0;

    GPIO_ResetBits(GPIO1, GPIO_Pin_1); // 低电平片选有效,SPI通讯开始

    SPI_FLASH_Send_Byte(0x9f); // 发送读取ID指令

    temp0 = SPI_FLASH_Send_Byte(0x00); // 读取制造商ID(M7-M0)
    temp1 = SPI_FLASH_Send_Byte(0x00); // 读取芯片ID(ID15~ID8)
    temp2 = SPI_FLASH_Send_Byte(0x00); // 读取芯片ID(ID7~ID0)

    GPIO_SetBits(GPIO1, GPIO_Pin_1); // 停止SPI通讯

    temp = (temp0 << 16) | (temp1 << 8) | temp2; // 组合数据

    return temp;
}
/**
 * @brief 等待flash内部时序操作完成
 */
void SPI_WaitForWriteEnd(void)
{
    uint8_t status_reg = 0;

    GPIO_ResetBits(GPIO1, GPIO_Pin_1);

    SPI_FLASH_Send_Byte(0x05); // 发送“Read Status Register”指令

    do
    {
        SoftDelaymS(1);
        status_reg = SPI_FLASH_Send_Byte(0x00);
    } while ((status_reg & 0x01) == 1);

    GPIO_SetBits(GPIO1, GPIO_Pin_1);
}
/**
 * @brief  写使能
 */
void SPI_FLASH_WriteEnable(void)
{
    GPIO_ResetBits(GPIO1, GPIO_Pin_1);
    SPI_FLASH_Send_Byte(0x06); // 发送“Write Enable”指令,06h
    GPIO_SetBits(GPIO1, GPIO_Pin_1);
}

/**
 * @brief  块擦除(64k)
 * @param  SectorAddr 擦除地址
 */
void SPI_FLASH_BlockErase64k(u32 SectorAddr)
{
    SPI_WaitForWriteEnd();
    SPI_FLASH_WriteEnable();

    GPIO_ResetBits(GPIO1, GPIO_Pin_1);

    SPI_FLASH_Send_Byte(0xd8);
    SPI_FLASH_Send_Byte((SectorAddr & 0xFF0000) >> 16); // 高位擦除地址A23~A16
    SPI_FLASH_Send_Byte((SectorAddr & 0xFF00) >> 8);    // 中位擦除地址A15~A8
    SPI_FLASH_Send_Byte(SectorAddr & 0xFF);             // 低位擦除地址A7~A0

    GPIO_SetBits(GPIO1, GPIO_Pin_1);

    SPI_WaitForWriteEnd();
}
/**
 * @brief  扇区擦除
 * @param  SectorAddr 擦除地址
 */
void SPI_FLASH_SectorErase(u32 SectorAddr)
{
    SPI_FLASH_WriteEnable();
    SPI_WaitForWriteEnd();

    GPIO_ResetBits(GPIO1, GPIO_Pin_1);

    SPI_FLASH_Send_Byte(0x20);
    SPI_FLASH_Send_Byte((SectorAddr & 0xFF0000) >> 16); // 高位擦除地址A23~A16
    SPI_FLASH_Send_Byte((SectorAddr & 0xFF00) >> 8);    // 中位擦除地址A15~A8
    SPI_FLASH_Send_Byte(SectorAddr & 0xFF);             // 低位擦除地址A7~A0

    GPIO_SetBits(GPIO1, GPIO_Pin_1);

    SPI_WaitForWriteEnd();
}
/**
 * @brief  数据写入
 * @param addr 写入地址
 * @param writeBuff 包含数据的指针
 * @param numByteToWrite 写入字节数
 */
void SPI_Write_Data(uint32_t addr, uint8_t *writeBuff, uint32_t numByteToWrite)
{
    SPI_FLASH_WriteEnable();
    GPIO_ResetBits(GPIO1, GPIO_Pin_1);

    SPI_FLASH_Send_Byte(0x02); // 发送“Page Program”指令,02h

    SPI_FLASH_Send_Byte((addr >> 16) & 0xff);
    SPI_FLASH_Send_Byte((addr >> 8) & 0xff);
    SPI_FLASH_Send_Byte(addr & 0xff);

    while (numByteToWrite--)
    {
        SPI_FLASH_Send_Byte(*writeBuff);
        writeBuff++;
    }

    GPIO_SetBits(GPIO1, GPIO_Pin_1);

    SPI_WaitForWriteEnd();
}
/**
 * @brief  读取flash数据
 * @param addr 读取地址
 * @param readBuff 存放读出的数据的指针
 * @param numByteToRead 读出的字节数
 */
void SPI_Read_Data(uint32_t addr, uint8_t *readBuff, uint32_t numByteToRead)
{
    GPIO_ResetBits(GPIO1, GPIO_Pin_1);

    SPI_FLASH_Send_Byte(0x03);
    SPI_FLASH_Send_Byte((addr >> 16) & 0xff);
    SPI_FLASH_Send_Byte((addr >> 8) & 0xff);
    SPI_FLASH_Send_Byte(addr & 0xff);

    while (numByteToRead--)
    {
        *readBuff = SPI_FLASH_Send_Byte(0x00);
        readBuff++;
    }

    GPIO_SetBits(GPIO1, GPIO_Pin_1);
}

使用到的库函数

库函数部分代码

#define GPIO_AF_I2C 6
#define REG_RESET(reg,mask) reg &= ~(mask)
#define REG_SET(reg,mask) reg |= (mask)
#define REG_WRITE(reg,mask) reg = (mask)
#define SYS0 ((SYS_TypeDef *)(SYS_BASE))
#define SYS_MODULE_GPIO BIT11
#define SYS_MODULE_SPI0 BIT0
/**
 * @brief GPIO功能配置结构体句柄
 */
typedef struct GPIO_InitTypeDef {
    uint32_t GPIO_Pin;
    GPIO_Mode_TypeDef GPIO_Mode;
    GPIO_PuPd_TypeDef GPIO_PuPd;
    uint32_t GPIO_PODEna;
    uint32_t GPIO_PFLT;
};
/**
 * @brief GPIO模式选择枚举
 */
typedef enum GPIO_Mode_TypeDef {
    GPIO_Mode_IN = 0,
    GPIO_Mode_OUT = 1,
    GPIO_Mode_ANA = 2,
    GPIO_Mode_IO = 3,
}} {enum_name};
/**
 * @brief SPI模块初始化结构体
 */
typedef struct SPI_InitTypeDef {
    SPI_Duplex Duplex;
    uint8_t CS;
    SPI_Mode Mode;
    uint8_t CPHA;
    uint8_t CPOL;
    uint8_t ENDIAN;
    uint8_t EN;
    uint8_t IRQEna;
    uint8_t Trig;
    uint8_t TRANS_MODE;
    uint16_t BaudRate;
    uint8_t ByteLength;
};
/**
 * @brief  GPIO配置函数
 * @param  GPIOx: GPIO端口
 * @param  GPIO_PinSource: GPIO引脚
 * @param  mode: GPIO模式
 * @param  GPIO_AF_x: GPIO复用功能选择
 */
void GPIO_Config(GPIO_TypeDef *GPIOx, uint32_t GPIO_PinSource, GPIO_Mode_TypeDef mode, uint32_t GPIO_AF_x)
{
    GPIO_PinAFConfig(GPIOx, GPIO_PinSource, GPIO_AF_x);
    GPIO_InitTypeDef GPIO_InitStructure;
    GPIO_StructInit(&GPIO_InitStructure);
    GPIO_InitStructure.GPIO_Pin  = 1 << GPIO_PinSource;
    GPIO_InitStructure.GPIO_Mode = mode;
    if (GPIO_AF_x == GPIO_AF_I2C)
    {
        GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
    }
    else
    {
        __NOP();
    }
    GPIO_Init(GPIOx, &GPIO_InitStructure);
}
/**
 * @brief SPI初始化结构体默认配置函数
 * @param SPI_InitStruct 指向SPI初始化结构体的指针
 */
void SPI_StructInit(SPI_InitTypeDef *SPI_InitStruct)
{
    for (int i = 0; i < sizeof(SPI_InitTypeDef); i++)
    {
        ((uint8_t *)SPI_InitStruct)[i] = 0;
    }
}
/**
 * @brief SPI模块初始化函数
 * @param SPIx SPI模块指针
 * @param SPI_InitStruct 指向SPI初始化结构体的指针
 */
void SPI_Init(SPI_TypeDef *SPIx, SPI_InitTypeDef *SPI_InitStruct)
{
    // 使能SPI模块时钟
    SYS_ModuleClockCmd(SYS_MODULE_SPI0, ENABLE);

    // 配置SPI控制寄存器
    SPIx->CFG = (SPI_InitStruct->Duplex << 6) |
                (SPI_InitStruct->CS << 5) |
                (SPI_InitStruct->Mode << 4) |
                (SPI_InitStruct->CPHA << 3) |
                (SPI_InitStruct->CPOL << 2) |
                (SPI_InitStruct->ENDIAN << 1) |
                SPI_InitStruct->EN;

    // 配置SPI中断使能寄存器
    SPIx->IE = (SPI_InitStruct->IRQEna << 4) |
               (SPI_InitStruct->Trig << 3);

    // 配置SPI波特率
    SPIx->DIV = (SPI_InitStruct->TRANS_MODE << 15) |
                (SPI_InitStruct->BaudRate);

    // 配置SPI数据长度
    SPIx->SIZE = SPI_InitStruct->ByteLength;
}
/**
 * @brief 初始化GPIO
 * @param GPIOx GPIO模块指针
 * @param GPIO_InitStruct 指向包含初始化参数的GPIO_InitTypeDef结构体
 */
void GPIO_Init(GPIO_TypeDef *GPIOx, GPIO_InitTypeDef *GPIO_InitStruct)
{
    // 使能GPIO时钟
    SYS_ModuleClockCmd(SYS_MODULE_GPIO, ENABLE);

    // 配置引脚模式
    if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_IN)
    {
        GPIOx->PIE |= GPIO_InitStruct->GPIO_Pin;  // 打开输入使能
        GPIOx->POE &= ~GPIO_InitStruct->GPIO_Pin; // 关闭输出使能
        GPIOx->PUE &= ~GPIO_InitStruct->GPIO_Pin; // 关闭上拉使能
    }
    else if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_OUT)
    {
        GPIOx->PIE &= ~GPIO_InitStruct->GPIO_Pin; // 关闭输入使能
        GPIOx->POE |= GPIO_InitStruct->GPIO_Pin;  // 使能输出
    }
    else if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_IO)
    {
        GPIOx->PIE |= GPIO_InitStruct->GPIO_Pin; // 使能输入
        GPIOx->POE |= GPIO_InitStruct->GPIO_Pin; // 使能输出
    }
    else // GPIO_Mode_ANA
    {
        GPIOx->PIE &= ~GPIO_InitStruct->GPIO_Pin; // 关闭输入使能
        GPIOx->POE &= ~GPIO_InitStruct->GPIO_Pin; // 关闭输出使能
        GPIOx->PUE &= ~GPIO_InitStruct->GPIO_Pin; // 关闭上拉使能
    }

    if (GPIO_InitStruct->GPIO_PuPd == GPIO_PuPd_UP)
    {
        GPIOx->PUE |= GPIO_InitStruct->GPIO_Pin; // 使能上拉
    }
    else
    {
        GPIOx->PUE &= ~GPIO_InitStruct->GPIO_Pin; // 关闭上拉
    }
    // 配置开漏使能
    if (GPIO_InitStruct->GPIO_PODEna)
    {
        GPIOx->PODE |= GPIO_InitStruct->GPIO_Pin;
    }
    else
    {
        GPIOx->PODE &= ~GPIO_InitStruct->GPIO_Pin;
    }

    // 配置滤波使能
    if (GPIO_InitStruct->GPIO_PFLT)
    {
        GPIOx->PFLT |= GPIO_InitStruct->GPIO_Pin;
    }
    else
    {
        GPIOx->PFLT &= ~GPIO_InitStruct->GPIO_Pin;
    }
}
/**
 * @brief 初始化GPIO结构体为默认值
 * @param GPIO_InitStruct 指向要初始化的GPIO_InitTypeDef结构体
 */
void GPIO_StructInit(GPIO_InitTypeDef *GPIO_InitStruct)
{
    for (int i = 0; i < sizeof(GPIO_InitTypeDef); i++)
    {
        ((uint8_t *)GPIO_InitStruct)[i] = 0;
    }
}
/**
 * @brief 复位指定的GPIO引脚位
 * @param GPIOx GPIO模块指针
 * @param GPIO_Pin 要复位的引脚
 */
void GPIO_ResetBits(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin)
{
    GPIOx->PDO &= ~GPIO_Pin;
}
/**
 * @brief 数字模块时钟使能
 * @param nModule 模块编号
 * @param state 使能或禁用状态
 */
void SYS_ModuleClockCmd(uint32_t nModule, uint8_t state)
{
    REG_WRITE(SYS0->PROTECT, 0x7a83);
    if (state)
    {
        REG_SET(SYS0->CLK_FEN, nModule);
    }
    else
    {
        REG_RESET(SYS0->CLK_FEN, nModule);
    }
    REG_WRITE(SYS0->PROTECT, 0);
}
/**
 * @brief 设置指定的GPIO引脚位
 * @param GPIOx GPIO模块指针
 * @param GPIO_Pin 要设置的引脚
 */
void GPIO_SetBits(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin)
{
    GPIOx->PDO |= GPIO_Pin;
}
/**
 * @brief 配置GPIO引脚复用功能
 * @param GPIOx GPIO模块指针
 * @param GPIO_PinSource 引脚源
 * @param GPIO_AF 复用功能选择
 */
void GPIO_PinAFConfig(GPIO_TypeDef *GPIOx, uint32_t GPIO_PinSource, uint32_t GPIO_AF)
{
    uint8_t offset;
    uint8_t pins = GPIO_PinSource >> 2;
    offset       = ((GPIO_PinSource & 0x3) * 4);
    switch (pins)
    {
        case 0:
            GPIOx->F3210 = (GPIOx->F3210 & ~(0xf << offset)) | (GPIO_AF << offset);
            break;
        case 1:
            GPIOx->F7654 = (GPIOx->F7654 & ~(0xf << offset)) | (GPIO_AF << offset);
            break;
        case 2:
            GPIOx->FBA98 = (GPIOx->FBA98 & ~(0xf << offset)) | (GPIO_AF << offset);
            break;
        case 3:
            GPIOx->FFEDC = (GPIOx->FFEDC & ~(0xf << offset)) | (GPIO_AF << offset);
            break;
        default:
            break;
    }
}