STM32给FPGA的外挂FLASH进行升级
- 一、电路方案设计
- 二、软件写FLASH
- 三、解决第一次烧录后FPGA无法启动的问题
前言:
一个复杂的嵌入式中,如果对某些实时性要求极高的情况下势必会使用到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_SendbyteSPI1_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_SendbyteSPI1_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发送读状态命令 0x05SPI1_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发送读状态命令 0x05SPI1_WriteByte(0x05);sta = SPI1_ReadByte();//CS=1;//释放芯片FLASH_CS_H();sta |= 0x40;//CS=0;//选中芯片FLASH_CS_L();//MCU发送写状态命令 0x01SPI1_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发送页编程命令 0x02SPI1_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发送读数据命令 0x03SPI1_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 WELFLASH_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发送芯片擦除命令 0x60SPI1_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线模式。