/**
 * @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);
}

