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線模式。