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;
}
}