STM32给FPGA的外挂FLASH进行升级

发布于:2025-06-24 ⋅ 阅读:(15) ⋅ 点赞:(0)

前言:
一个复杂的嵌入式中,如果对某些实时性要求极高的情况下势必会使用到FPGA来保证,这里面牵扯到给FPGA的程序升级问题,一般采用负责逻辑处理的一个MCU来完成,实际就是对存储FPGA的这个flash进行读写操作。

一、电路方案设计

1、FLASH的(MOSI/MISO/CLK/CS)既要连接FPGA又要连接STM32,所以要设计一个单刀双置的开关。(注意:FLASH_D2和FLASH_D3是直接接入FPGA的,因为FPGA需要使用flash的四线模式)
在这里插入图片描述

二、软件写FLASH

1、mx25l.c

#include <stdio.h>
#include "mx25l.h"

/******************************************************
  * @brief  SPI写数据
  * @param  data 要写入mx25l的数据 
  * @retval 返回一个字节
******************************************************/
uint8_t SPI1_WriteByte(uint8_t data)
{	
	//等待发送完成
	while(SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET);
	//发送数据
	SPI_I2S_SendData(SPI1, data);
	//等待接收完成
	while(SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) == RESET);
	
	return SPI_I2S_ReceiveData(SPI1);	
}

/******************************************************
  * @brief  SPI读数据
  * @param  空
  * @retval 返回读取到的字节
******************************************************/
uint8_t SPI1_ReadByte(void)
{
	return(SPI1_WriteByte(0xff));
}

/********************************
  * @brief  mx25l的写使能
  * @param  None
  * @retval None
  ******************************/
void Write_enable(void)
{
	//CS=0, 选中芯片
	FLASH_CS_L();
	//MCU发送写使能命令0x06	SPI_Sendbyte
	SPI1_WriteByte(0x06);
	//CS=1;//释放芯片
	FLASH_CS_H();
}

/********************************
  * @brief  mx25l的写失能
  * @param  None
  * @retval None
  ******************************/
void Write_disable(void)
{
	//CS=0, 选中芯片
	FLASH_CS_L();
	//MCU发送写失能命令0x04	SPI_Sendbyte
	SPI1_WriteByte(0x04);
	//CS=1;//释放芯片
	FLASH_CS_H();
}

/****************************************
  * @brief  读mx25l状态寄存器
  * @param  None
  * @retval None
  **************************************/
void Read_ststus(void)
{
	u8 sta=0;//接收状态寄存器的值
	//CS=0;//选中芯片
	FLASH_CS_L();
	//MCU发送读状态命令	0x05
	SPI1_WriteByte(0x05);
	do{
		 sta = SPI1_ReadByte();
	}while((sta&(1<<0))==1);
	//CS=1;//释放芯片
	FLASH_CS_H();
}
#if 0
u8 SPI_Flash_ReadSR(void)   
{  
	u8 byte=0;   
	FLASH_CS_L();                          //使能器件   
	SPI1_WriteByte(0x05);              //发送读取状态寄存器命令    
	byte=SPI1_ReadByte();         //读取一个字节  
	FLASH_CS_H();                          //取消片选     
	return byte;   
} 
void SPI_Flash_Wait_Busy(void)  
{   
	while ((SPI_Flash_ReadSR()&0x01)==0x01);   // 等待WIP位清空
} 
#endif

/*************************************************************************
  * @brief  写mx25l状态寄存器QEbit,防止空板第一次下载程序FPGA无法启动现象
  * @param  None
  * @retval None
  ************************************************************************/
void QEBit_Set(void)
{
	u8 sta=0; // 接收状态寄存器的值
	
	//写使能
	Write_enable();
	//CS=0;//选中芯片
	FLASH_CS_L();
	//MCU发送读状态命令	0x05
	SPI1_WriteByte(0x05);
	sta = SPI1_ReadByte();
	//CS=1;//释放芯片
	FLASH_CS_H();
	
	sta |= 0x40;
	//CS=0;//选中芯片
	FLASH_CS_L();
	//MCU发送写状态命令	0x01
	SPI1_WriteByte(0x01);
	SPI1_WriteByte(sta);
	//CS=1;//释放芯片
	FLASH_CS_H();
	Write_disable();
}

/****************************************************
  * @brief  MCU 写数据到 mx25l
  * @param  addr 24位地址
  * @param  data 要写入的数据
  * @retval None
  * @attention 写数据之前一定要擦除芯片
  **************************************************/
void Mx25l_WriteByte(uint32_t addr, uint8_t data)
{	
	//打开写使能
	Write_enable();
	//CS=0;//选中芯片
	FLASH_CS_L();
	//MCU发送页编程命令  0x02
	SPI1_WriteByte(0x02);
	//发送24位地址
	SPI1_WriteByte((u8)(addr>>16));
	SPI1_WriteByte((u8)(addr>>8));
	SPI1_WriteByte((u8)(addr));
	//MCU发送写入的数据
	SPI1_WriteByte(data);
	//CS=1;//释放芯片
	FLASH_CS_H();
	//读状态
	Read_ststus();
}

/****************************************************
  * @brief  MCU页写数据到 mx25l
  * @param  pBuffer  要写的数组
  * @param  addr     24位地址
  * @param  Nb_bytes 要写入的数据长度
  * @retval None
  * @attention       写数据之前一定要擦除芯片
  **************************************************/
void Mx25l_Write_Page(u8* pBuffer, u32 addr, u16 Nb_bytes)
{
 	u16 i;  
    Write_enable(); //SET WEL 
	FLASH_CS_L(); //使能器件   
    SPI1_WriteByte(0x02); //发送写页命令   
    SPI1_WriteByte((u8)(addr>>16)); //发送24bit地址    
    SPI1_WriteByte((u8)(addr>>8));   
    SPI1_WriteByte((u8)(addr));   
    for(i=0; i<Nb_bytes; i++)
	{
		SPI1_WriteByte(pBuffer[i]); //循环写数  
	}
	FLASH_CS_H(); //取消片选 
	Read_ststus();	//等待写入结束
} 

/****************************************************
  * @brief  MCU 无校验写数据到 mx25l
  * @param  pBuffer  要写的数组
  * @param  addr     24位地址
  * @param  Nb_bytes 要写入的数据长度
  * @retval None
  * @attention       写数据之前一定要擦除芯片
  **************************************************/
void Mx25l_Write_NoCheck(u8* pBuffer,u32 addr,u16 Nb_bytes)   
{ 			 		 
	u16 PageRemain;	   
	PageRemain = 256 - addr%256; //单页剩余的字节数		
	
	if(Nb_bytes <= PageRemain) { //不大于256个字节
		Mx25l_Write_Page(pBuffer, addr, Nb_bytes); 
	}else {
		Mx25l_Write_Page(pBuffer, addr, PageRemain);
		addr += PageRemain;
		Mx25l_Write_Page(pBuffer+PageRemain, addr, Nb_bytes-PageRemain);
	}	    
} 

/*******************************************************
  * @brief  MCU从 mx25l 读数据
  * @param  addr 24位地址
  * @param  data 存放读出的数据
  * @param  size 要读的字节长度(不要太大,否则RAM承受不了)
  * @retval None
  *****************************************************/
void Mx25l_Read_data(u32 addr, u8 *data, u32 size)
{
	
	uint32_t i=0;//接收数据的循环变量
	//CS=0;//选中芯片
	FLASH_CS_L();
	//MCU发送读数据命令  0x03
	SPI1_WriteByte(0x03);
	//MCU发送开始读的地址
	SPI1_WriteByte((u8)(addr>>16));
	SPI1_WriteByte((u8)(addr>>8));
	SPI1_WriteByte((u8)(addr));
	//MCU接收数据
	for(i=0; i<size; i++)
	{
		data[i] = SPI1_ReadByte();
	}
	//CS=1;//释放芯片
	FLASH_CS_H();  
}

/******************************************************************
  * @brief  Sector Write
  * @param  nSector 第几个扇区
  * @param  pBuffer 数据
  * @retval None
  *****************************************************************/
void Mx25l_Write_Sector(uint32_t nSector, uint8_t* pBuffer)
{	
	int i,j;
	//扇区号转为地址
	nSector *= FLASH_SECTOR_SIZE;

	//一个扇区需要几个页
	for(j=0; j<FLASH_PAGES_PER_SECTOR; j++)
	{
		Write_enable();                  //SET WEL
		 
		FLASH_CS_L();
		SPI1_WriteByte(0x02);
	    SPI1_WriteByte((nSector >> 16) & 0xff);
	    SPI1_WriteByte((nSector >> 8) & 0xff);
	    SPI1_WriteByte(nSector & 0xff);
		
		for(i=0; i<FLASH_PAGE_SIZE; i++)
		{		
			SPI1_WriteByte(pBuffer[i]);
		}
				
		pBuffer += FLASH_PAGE_SIZE;
		nSector += FLASH_PAGE_SIZE;

		FLASH_CS_L();
		Read_ststus();
	}
}

/******************************************************************
  * @brief  Sector Read
  * @param  nSector 第几个扇区
  * @param  pBuffer 存放数据
  * @retval None
  *****************************************************************/
void Mx25l_Read_Sector(uint32_t nSector, u8* pBuffer)
{	
    uint16_t i;
	//扇区号转为地址
	nSector *= FLASH_SECTOR_SIZE;

	FLASH_CS_L();
	SPI1_WriteByte(0x03);
	SPI1_WriteByte((u8)(nSector >> 16));
	SPI1_WriteByte((u8)(nSector >> 8));
	SPI1_WriteByte((u8)(nSector));
	
	for(i=0;i<FLASH_SECTOR_SIZE;i++)
	{	
		pBuffer[i] = SPI1_ReadByte();
		//usb_printf("%02x, ", pBuffer[i]);
	}
	
	FLASH_CS_L();
	Read_ststus();
}

/*******************************
  * @brief  擦除整个芯片
  * @param  None
  * @retval None
  *****************************/
void Chip_erase(void)
{
	//printf("chip erase starting...\r\n");
	
	Write_enable();
	//CS=0;//选中芯片
	FLASH_CS_L();
	//MCU发送芯片擦除命令  0x60
	SPI1_WriteByte(0x60);
	//CS=1;//释放芯片
	FLASH_CS_H();
	//读状态
	Read_ststus();
	
	//printf("chip erase complete...\r\n");
}

2、mx25l.h

#ifndef  _MX25L_H_
#define  _MX25L_H_

#include "stm32f10x.h"
#include "stm32f10x_spi.h"

#define FLASH_PAGE_SIZE		256
#define FLASH_SECTOR_SIZE	4096
#define FLASH_SECTOR_COUNT	4096
#define FLASH_BLOCK_SIZE	65536
#define FLASH_PAGES_PER_SECTOR	FLASH_SECTOR_SIZE/FLASH_PAGE_SIZE
#define FLASH_CS_H()        GPIO_SetBits(GPIOA, GPIO_Pin_4)
#define FLASH_CS_L()        GPIO_ResetBits(GPIOA, GPIO_Pin_4)
uint8_t SPI1_WriteByte(uint8_t data);
uint8_t SPI1_ReadByte(void);
void Write_enable(void);
void Write_disable(void);
void Read_ststus(void);
void QEBit_Set(void);
void Mx25l_WriteByte(uint32_t addr,uint8_t data);
void Mx25l_Write_Page(u8* pBuffer, u32 addr, u16 Nb_bytes);
void Mx25l_Write_NoCheck(u8* pBuffer,u32 addr,u16 Nb_bytes);
void Mx25l_Read_data(uint32_t addr, uint8_t *data, uint32_t size);
void Mx25l_Write_Sector(uint32_t nSector, uint8_t* pBuffer);
void Mx25l_Read_Sector(uint32_t nSector, u8* pBuffer);
void Chip_erase(void);

#endif

三、解决第一次烧录后FPGA无法启动的问题

1、这个问题的原因是因为FPGA编译使用的flash是4线模式,而flash默认是1线模式,所以升级完之后调用QEBit_Set();配饰flash为4线模式。


网站公告

今日签到

点亮在社区的每一天
去签到