电路图如下:PI0使能,PC2MISO,PI3MOSI,PI1CLK;
对COBEMX进行初始化:
根据芯片手册配置传输模式:
轮询发送数据
KILE建立FLASH底层驱动.C文件
根据芯片手册编写函数:
读一个字节:W25X_SPIRWByte
/***************读一个字节****************/
uint8_t W25X_SPIRWByte(uint8_t TxData)
{
uint8_t RxData;
/*硬件通道,发送的数据,接收的数据,数据长度,超时时间*/
HAL_SPI_TransmitReceive(&hspi2, &TxData, &RxData, 1, 0xff);
return RxData;
}
芯片使能配置
/****************使能CS引脚******************/
#define FLASH_CS_Pin GPIO_PIN_0
#define FLASH_CS_GPIO_Port GPIOI
#define FLASH_CS_SET() HAL_GPIO_WritePin(FLASH_CS_GPIO_Port, FLASH_CS_Pin, GPIO_PIN_SET)
#define FLASH_CS_RESET() HAL_GPIO_WritePin(FLASH_CS_GPIO_Port, FLASH_CS_Pin, GPIO_PIN_RESET)
芯片写使能:void W25X_WriteEnable()
#define Write_Enable 0x06 //擦除指令
/***************flash写使能****************/
void W25X_WriteEnable()
{
FLASH_CS_RESET();//CS拉低使能芯片
W25X_SPIRWByte(Write_Enable);//写入指令
FLASH_CS_SET();
}
芯片读取状态等待:W25X_WaitBusy()
#define Read_Status_1 0x05 //写使能指令
/***************flash读取状态等待****************/
void W25X_WaitBusy()
{
uint8_t tmp;
while(1)
{
FLASH_CS_RESET();//CS拉低使能芯片
W25X_SPIRWByte(Read_Status_1);//写入指令
tmp = W25X_SPIRWByte(0xff);
FLASH_CS_SET();
if((tmp & 0x01) == 0)
{
return; //可加入超时跳出
}
}
}
擦除指令: W25X_Erase(uint32_t address)
擦除指令位0x20
#define Sector_Erase 0x20 //擦除指令
与0为0
/***************flash擦除****************/
//传入一个地址进行擦除
void W25X_Erase(uint32_t address)
{
FLASH_CS_RESET();//CS拉低使能芯片
W25X_SPIRWByte(Sector_Erase);//写入指令
W25X_SPIRWByte((address >> 16) & 0xff);//从高位开始写入24位地址
W25X_SPIRWByte((address >> 8) & 0xff);//从高位开始写入24位地址
W25X_SPIRWByte((address >> 0) & 0xff);//从高位开始写入24位地址
FLASH_CS_SET();
}
FLASH写页操作:W25X_WritePage(uint32_t address, uint8_t *pbuf, uint16_t len);
#define Page_Problem 0x02 //写页指令
/***************flash写页操作****************/
void W25X_WritePage(uint32_t address, uint8_t *pbuf, uint16_t len)
{
uint16_t i = 0;
W25X_WriteEnable();
FLASH_CS_RESET();//CS拉低使能芯片
W25X_SPIRWByte(Page_Problem);//写入指令
W25X_SPIRWByte((address >> 16) & 0xff);//从高位开始写入24位地址
W25X_SPIRWByte((address >> 8) & 0xff);//从高位开始写入24位地址
W25X_SPIRWByte((address >> 0) & 0xff);//从高位开始写入24位地址
for(i = 0; i<len; i++)
{
W25X_SPIRWByte(pbuf[i]);
}
FLASH_CS_SET();
W25X_WaitBusy();
}
FLASH读操作:W25X_ReadData(uint32_t address, uint8_t *pbuf, uint16_t len);
#define Read_Data 0x03 //读指令
/***************flash读操作****************/
void W25X_ReadData(uint32_t address, uint8_t *pbuf, uint16_t len)
{
uint16_t i = 0;
W25X_WriteEnable();
FLASH_CS_RESET();//CS拉低使能芯片
W25X_SPIRWByte(Read_Data);//写入指令
W25X_SPIRWByte((address >> 16) & 0xff);//从高位开始写入24位地址
W25X_SPIRWByte((address >> 8) & 0xff);//从高位开始写入24位地址
W25X_SPIRWByte((address >> 0) & 0xff);//从高位开始写入24位地址
for(i = 0; i<len; i++)
{
pbuf[i] = W25X_SPIRWByte(0xff); //传输任意数
}
FLASH_CS_SET();
W25X_WaitBusy();
}
讲解:
比如我要读一组数据,建立一个缓冲区 uint8_t buffer[256]; //准备一个页的缓冲区
向缓冲区写入一组数据: W25X_WritePage(0, buffer, 256); //写入缓冲区256个字节
0是起始地址,buffer是写入缓冲区,256是写入字节长度
我要写一组数据则是利用 W25X_ReadData(0, buffer, 256);
我们可以看到,实现FLASH的读写操作仅仅用了一个函数:W25X_SPIRWByte(uint8_t b)
而这个函数仅仅是由HAL_StatusTypeDef HAL_SPI_TransmitReceive(SPI_HandleTypeDef *hspi, uint8_t *pTxData, uint8_t *pRxData, uint16_t Size, uint32_t Timeout)这个函数实现的,这个函数实际上就实现了全双工通信,即主机和从机同步传输数据。换种方式来说就是SPI内部有一个移位寄存器,主机发送一个字节到从机,同时会把从机的一位数据移出,移出的数据同时转移给主机,即SPI通信是发送一位接收一位。
这个函数就是实现了发送一个数据同时接收一个数据这样一个功能,比如我接收一个数据只需要W25X_SPIRWByte(0xff),我们向从机发送0xff,这个值是任意的,因为我们只想要接收值,返回的值就是从机的值。那么如果是向从机发送值呢?只要把W25X_SPIRWByte(写上我们想要发送的值就好了)。
SPI+DMA处理读写FLASH
加入DMA之后程序上没什么太大的改动。