STM32给FPGA的外挂FLASH进行升级

STM32给FPGA的外挂FLASH进行升级

前言:

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

一、电路方案设计

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

二、软件写FLASH

1、mx25l.c

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

c 复制代码
#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线模式。

相关推荐
Wallace Zhang3 小时前
STM32F103_Bootloader程序开发11 - 实现 App 安全跳转至 Bootloader
stm32·嵌入式硬件·安全
GodKK老神灭3 小时前
STM32 CCR寄存器
stm32·单片机·嵌入式硬件
杰克逊的日记9 天前
MCU编程
单片机·嵌入式硬件
Python小老六9 天前
单片机测ntc热敏电阻的几种方法(软件)
数据库·单片机·嵌入式硬件
懒惰的bit9 天前
STM32F103C8T6 学习笔记摘要(四)
笔记·stm32·学习
尤老师FPGA10 天前
使用DDR4控制器实现多通道数据读写(十六)
fpga开发·ddr4
Suagrhaha10 天前
驱动入门的进一步深入
linux·嵌入式硬件·驱动
国科安芯10 天前
基于ASP4644多通道降压技术在电力监测系统中集成应用与发展前景
嵌入式硬件·硬件架构·硬件工程
Li Zi10 天前
STM32 ADC(DMA)双缓冲采集+串口USART(DMA)直接传输12位原始数据到上位机显示并保存WAV格式音频文件 收藏住绝对实用!!!
经验分享·stm32·单片机·嵌入式硬件