STM32H7使用外部flash運行程序
在淘寶上買了一塊核心板,使用的STM32H7B0VBT6。
客服很盡責(zé),幫助了我很多。
H7系列的功能很強大,但是H7B0他有個問題,只有128k的內(nèi)部flash,這么強大的芯片只有這么小的flash,想搞個RTTreadOS都不行。無奈,智能選擇使用外部flash,好在核心板上有兩個W25Q64,一個SPI,一個QSPI足夠我們使用的。
思路
使用SPI總線的Flash模擬成U盤,然后把app的bin文件拷到模擬u盤中,通過SPI去讀取SPI內(nèi)保存的bin文件數(shù)據(jù),然后寫入緩存中,QSPI讀取緩存數(shù)據(jù)到QSPI中,最后執(zhí)行跳轉(zhuǎn)程序,運行app。
細節(jié)
1,使用stm32cubemx創(chuàng)建一個usb模板
2,編寫SPI,W25Q64驅(qū)動
void MX_SPI6_Init(void)
{
hspi6.Instance = SPI6; // 使用SPI6
hspi6.Init.Mode = SPI_MODE_MASTER; // 主機模式
hspi6.Init.Direction = SPI_DIRECTION_2LINES; // 雙線全雙工
hspi6.Init.DataSize = SPI_DATASIZE_8BIT; // 8位數(shù)據(jù)寬度
hspi6.Init.CLKPolarity = SPI_POLARITY_LOW; // CLK空閑時保持低電平
hspi6.Init.CLKPhase = SPI_PHASE_1EDGE; // 數(shù)據(jù)在CLK第一個邊沿有效
hspi6.Init.NSS = SPI_NSS_HARD_OUTPUT; // 使用硬件片選
// 這里將 pll2_q_ck 設(shè)置為 100M 作為 SPI6 的內(nèi)核時鐘,然后再經(jīng)過2分頻得到 50M 的SCK驅(qū)動時鐘
hspi6.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_2;
hspi6.Init.FirstBit = SPI_FIRSTBIT_MSB; // 高位在先
hspi6.Init.TIMode = SPI_TIMODE_DISABLE; // 禁止TI模式
hspi6.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE; // 禁止CRC
hspi6.Init.CRCPolynomial = 0x0; // CRC校驗項,這里用不到
hspi6.Init.NSSPMode = SPI_NSS_PULSE_DISABLE; // 不使用片選脈沖模式
hspi6.Init.NSSPolarity = SPI_NSS_POLARITY_LOW; // 片選低電平有效
hspi6.Init.FifoThreshold = SPI_FIFO_THRESHOLD_02DATA; // FIFO閾值
hspi6.Init.TxCRCInitializationPattern = SPI_CRC_INITIALIZATION_ALL_ZERO_PATTERN; // 發(fā)送端CRC初始化模式,這里用不到
hspi6.Init.RxCRCInitializationPattern = SPI_CRC_INITIALIZATION_ALL_ZERO_PATTERN; // 接收端CRC初始化模式,這里用不到
hspi6.Init.MasterSSIdleness = SPI_MASTER_SS_IDLENESS_00CYCLE; // 額外延遲周期為0
hspi6.Init.MasterInterDataIdleness = SPI_MASTER_INTERDATA_IDLENESS_00CYCLE; // 主機模式下,兩個數(shù)據(jù)幀之間的延遲周期
hspi6.Init.MasterReceiverAutoSusp = SPI_MASTER_RX_AUTOSUSP_DISABLE; // 禁止自動接收管理
hspi6.Init.MasterKeepIOState = SPI_MASTER_KEEP_IO_STATE_ENABLE; // 主機模式下,SPI當(dāng)前引腳狀態(tài)
hspi6.Init.IOSwap = SPI_IO_SWAP_DISABLE; // 不交換MOSI和MISO
HAL_SPI_Init(&hspi6);
}
3,編寫QSPI,W25Q64驅(qū)動
void MX_OCTOSPI1_Init(void)
{
OSPIM_CfgTypeDef sOspiManagerCfg = {0};
HAL_OSPI_DeInit(&hospi1); // 復(fù)位OSPI
hospi1.Instance = OCTOSPI1; // OSPI外設(shè)
hospi1.Init.ClockPrescaler = 2; // 時鐘分頻值,將OSPI內(nèi)核時鐘進行 2 分頻得到OSPI通信驅(qū)動時鐘
hospi1.Init.FifoThreshold = 8; // FIFO閾值
hospi1.Init.DualQuad = HAL_OSPI_DUALQUAD_DISABLE; // 禁止雙OSPI模式
hospi1.Init.MemoryType = HAL_OSPI_MEMTYPE_MICRON; // 存儲器模式,只有8線模式才會用到
hospi1.Init.DeviceSize = 23; // flash大小,核心板采用是8M字節(jié)的W25Q64,這里設(shè)置為23,即2^23
hospi1.Init.ChipSelectHighTime = 1; // 片選保持高電平的時間
hospi1.Init.FreeRunningClock = HAL_OSPI_FREERUNCLK_DISABLE; // 禁止自由時鐘模式
hospi1.Init.ClockMode = HAL_OSPI_CLOCK_MODE_3; // 模式3
hospi1.Init.WrapSize = HAL_OSPI_WRAP_NOT_SUPPORTED; // 不使用 wrap-size
hospi1.Init.SampleShifting = HAL_OSPI_SAMPLE_SHIFTING_HALFCYCLE; // 半個CLK周期之后進行采樣
hospi1.Init.DelayHoldQuarterCycle = HAL_OSPI_DHQC_DISABLE; // 不使用數(shù)據(jù)保持功能
hospi1.Init.ChipSelectBoundary = 0; // 禁止片選邊界功能
hospi1.Init.ClkChipSelectHighTime = 0; // 通信結(jié)束后 0 個CLK周期CS設(shè)置為高
hospi1.Init.DelayBlockBypass = HAL_OSPI_DELAY_BLOCK_BYPASSED; // 延時模塊旁路
hospi1.Init.MaxTran = 0; // 禁止通信管理功能
hospi1.Init.Refresh = 0; // 禁止刷新功能
HAL_OSPI_Init(&hospi1); // 初始化 OSPI1 設(shè)置
sOspiManagerCfg.ClkPort = 1; // OSPI引腳分配管理器設(shè)置,使用 Port1 的 CLK
sOspiManagerCfg.NCSPort = 1; // OSPI引腳分配管理器設(shè)置,使用 Port1 的 NCS
sOspiManagerCfg.IOLowPort = HAL_OSPIM_IOPORT_1_LOW; // OSPI引腳分配管理器設(shè)置,使用 Port1 的低4位引腳,IO[3:0]
HAL_OSPIM_Config(&hospi1, &sOspiManagerCfg, HAL_OSPI_TIMEOUT_DEFAULT_VALUE); // 初始化OSPI引腳分配管理器設(shè)置
}
4,主函數(shù)中初始化
int main(void)
{
SCB_EnableICache(); // 使能ICache
SCB_EnableDCache(); // 使能DCache
HAL_Init(); // 初始化HAL庫
SystemClock_Config(); // 配置系統(tǒng)時鐘,主頻280MHz
LED_Init(); // 初始化LED引腳
KEY_Init(); // 初始化按鍵引腳
USART1_Init(); // USART1初始化
MX_USB_DEVICE_Init(); // 初始化USB設(shè)備,識別SPIU盤
SPI_W25Qxx_Init(); //初始化SPI
OSPI_W25Qxx_Init(); // 初始化OSPI和W25Q64
Main_Menu(); //程序更新引導(dǎo)代碼
while (1)
{
LED1_Toggle;
HAL_Delay(100);
}
}
5,進入到Main_Menu(); 這個程序更新函數(shù)中后會進行選擇
void Main_Menu(void)
{
uint8_t key = 0;
uint8_t quit= 0 ;
// uint8_t download_flag=0;
printf("\r\n======================================================================");
printf("\r\n= (C) COPYRIGHT 2023 STMicroelectronics =");
printf("\r\n= =");
printf("\r\n= STM32H7xx In-Application Programming Application (Version 1.0.0) =");
printf("\r\n= =");
printf("\r\n= By GXY Application Team =");
printf("\r\n======================================================================");
printf("\r\n\r\n");
while (1)
{
printf("\r\n===================================");
printf("\r\n= 輸入序號選擇命令 =");
printf("\r\n= (1)SPI模擬U盤方式更新 =");
printf("\r\n= (2)SD卡方式更新 =");
printf("\r\n= (3)執(zhí)行跳轉(zhuǎn) =");
printf("\r\n= (4)退出更新 =");
printf("\r\n===================================");
printf("\r\n\r\n");
/* Clean the input path */
__HAL_UART_FLUSH_DRREGISTER(&huart1);
__HAL_UART_CLEAR_PEFLAG(&huart1);
__HAL_UART_CLEAR_FEFLAG(&huart1);
__HAL_UART_CLEAR_NEFLAG(&huart1);
__HAL_UART_CLEAR_OREFLAG(&huart1);
key=0;
/* Receive key */
HAL_UART_Receive(&huart1, &key, 1, RX_TIMEOUT);
switch (key)
{
case '1' :
/* Download user application in the Flash */
printf ("正在更新...\r\n");
if(SpiDownload())
{
printf ("更新完成,請執(zhí)行跳轉(zhuǎn)\r\n"); // 初始化成功
}
else printf ("重新上電更新\r\n");
break;
case '2' :
printf ("此功能未開發(fā),等待后續(xù)開發(fā)......\r\n"); // 初始化成功
break;
case '3' :
OSPI_W25Qxx_MemoryMappedMode(); // 配置QSPI為內(nèi)存映射模式
SCB_DisableICache(); // 關(guān)閉ICache
SCB_DisableDCache(); // 關(guān)閉Dcache
SysTick->CTRL = 0; // 關(guān)閉SysTick
SysTick->LOAD = 0; // 清零重載值
SysTick->VAL = 0; // 清零計數(shù)值
printf ("跳轉(zhuǎn)地址設(shè)置\r\n");
JumpToApplication = (pFunction) (*(__IO uint32_t*) (W25Qxx_Mem_Addr + 4)); // 設(shè)置起始地址
__set_MSP(*(__IO uint32_t*) W25Qxx_Mem_Addr); // 設(shè)置主堆棧指針
printf("跳轉(zhuǎn)到W25Q64運行用戶程序>>>\r\n\r\n");
JumpToApplication(); // 執(zhí)行跳轉(zhuǎn)
break;
case '4' :
quit =1;
break;
default:
printf("選擇輸入 1,2,3,4控制更新方式\r");
break;
}
if(quit)
break;
}
}
6,使用串口助手輸入1,進入spi模擬u盤方式更新
uint8_t SpiDownload(void)
{
int i;
if(OSPI_W25Qxx_BlockErase_1024K(W25Qxx_TestAddr)==OSPI_W25Qxx_OK)
{
// printf ("擦除FLASH完成\r\n"); // 初始化成功
for(i=0;i<32;i++)
{
SPI_W25Qxx_ReadBuffer(W25Qxx_ReadBuffer,USB_FAT_Addr+W25Qxx_NumByteToTest*i,W25Qxx_NumByteToTest); // 讀取數(shù)據(jù)
// printf ("讀取u盤數(shù)據(jù)進度:%d%%\r\n",i*100/31); // 初始化成功
if(OSPI_W25Qxx_OK==OSPI_W25Qxx_WriteBuffer(W25Qxx_ReadBuffer,W25Qxx_TestAddr+W25Qxx_NumByteToTest*i,W25Qxx_NumByteToTest))// 寫入數(shù)據(jù)
{
printf ("更新進度:%d%%\r\n",i*100/31); // 初始化成功
}
else
{
printf ("寫入失敗"); // 初始化成功
return 0;
}
}
}
return 1;
}
7,串口打印數(shù)據(jù)
[17:41:36.483]發(fā)→◇1
□
[17:41:36.487]收←◆正在更新...
[17:41:40.756]收←◆更新進度:0%
[17:41:40.815]收←◆更新進度:3%
更新進度:6%
更新進度:9%
更新進度:12%
[17:41:40.908]收←◆更新進度:16%
更新進度:19%
[17:41:40.950]收←◆更新進度:22%
更新進度:25%
更新進度:29%
更新進度:32%
更新進度:35%
[17:41:41.064]收←◆更新進度:38%
更新進度:41%
更新進度:45%
更新進度:48%
[17:41:41.153]收←◆更新進度:51%
更新進度:54%
更新進度:58%
更新進度:61%
更新進度:64%
更新進度:67%
更新進度:70%
[17:41:41.311]收←◆更新進度:74%
更新進度:77%
更新進度:80%
更新進度:83%
更新進度:87%
[17:41:41.424]收←◆更新進度:90%
更新進度:93%
[17:41:41.469]收←◆更新進度:96%
更新進度:100%
更新完成,請執(zhí)行跳轉(zhuǎn)
8,使用串口助手發(fā)送3,進行程序跳轉(zhuǎn)文章來源:http://www.zghlxwxcb.cn/news/detail-417138.html
[17:42:06.050]發(fā)→◇3
□
[17:42:06.053]收←◆跳轉(zhuǎn)地址設(shè)置
跳轉(zhuǎn)到W25Q64運行用戶程序>>>
程序運行中...
[17:42:07.059]收←◆程序運行中...
[17:42:08.060]收←◆程序運行中...
[17:42:09.062]收←◆程序運行中...
[17:42:10.063]收←◆程序運行中...
[17:42:11.066]收←◆程序運行中...
[17:42:12.067]收←◆程序運行中...
[17:42:13.070]收←◆程序運行中..
至此大功告成,整個工程都是例子程序改的,所以只是列出代表性的代碼,大家可以下載工程代碼參考,方便更新自己的程序。如有錯誤,多多指教。
核心板鏈接:https://item.taobao.com/item.htm?spm=a1z10.5-c-s.w4002-17800249301.10.75712bd3PuFnlt&id=658976139303
創(chuàng)建u盤模板工程參考鏈接:https://zhuanlan.zhihu.com/p/542651742
模擬U盤更新程序參考鏈接:https://blog.csdn.net/qq_44810226/article/details/127508789
整個工程代碼:https://download.csdn.net/download/qq_43296570/87459589文章來源地址http://www.zghlxwxcb.cn/news/detail-417138.html
到了這里,關(guān)于STM32H7使用外部flash運行程序的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!