完整教程下載地址:http://www.armbbs.cn/forum.php?mod=viewthread&tid=86980
第28章 STM32H7時間關鍵代碼在ITCM執行的超簡單方法
本章教程為大家分享一種時間關鍵代碼在ITCM執行的簡單方法,同時中斷向量表和變量放DTCM。
28.1 初學者重要提示
28.2 簡單實現方法
28.3 實驗例程說明(MDK)
28.4 總結
28.1 初學者重要提示
- 學習本章節前,務必優先學習第25章,了解TCM,SRAM等五塊內存區的基礎知識,比較重要。
- TCM : Tightly-Coupled Memory 緊密耦合內存 。ITCM用於指令,DTCM用於數據,特點是跟內核速度一樣(400MHz),而片上RAM的速度基本都達不到這個速度(200MHz)。很多時候我們希望將需要實時性的程序和變量分別放在ITCM和DTCM里面執行,本章就是解決這個問題。
- 實現方法比較簡單,基於MDK的Option選項設置下即可,無需操作分散加載。使用分散加載的好處是靈活,在設置復雜工程的內存映射方面比較方便。
- 實現這個功能的關鍵是要把所有程序都下載到Flash,系統上電后讓MDK中的庫函數去將所需的程序加載到RAM里面,用戶不要自己去加載,太麻煩。如果用戶自己去加載就得搞個bootloader加載應用程序到ITCM。這里所說的庫函數是MDK里面的__main封裝起來了。
28.2 簡單實現方法
28.2.1 第1步,設置DTCM
設置DTCM空間,前0x400大小的空間用於中斷向量表,所以這里從0x20000400開始,用於各種變量需求:
28.2.2 第2步,添加ITCM
ITCM的首地址是0x0000 0000,大小64KB:
28.2.3 第3步,選擇在ITCM執行的代碼
右擊MDK分組,選擇使用ITCM,這里設置了APP分組、BSP分組和SEGGER/HardFault分組。
以APP分組為例,設置方法如下:
BSP分組和SEGGER/HardFault分組也設置完畢后,可以看到小雪花標識
而進入main函數之前的所有代碼,含main函數所在的文件main.c切不要設置,這個之前的代碼我們都需要在flash里面執行。這些代碼僅執行一次以后不會執行,所以不用管他們,之后的所有代碼都可以放在ITCM里面。
28.2.4 第4步,復制中斷向量表到DTCM
前面三步設置完畢后,將中斷向量表從flash中復制到DTCM,主要存儲的DTCM地址要0x200對齊。
/* ********************************************************************************************************* * 函 數 名: main * 功能說明: 標准c程序入口。 * 形 參: 無 * 返 回 值: 無 ********************************************************************************************************* */ int main(void) { uint32_t *SouceAddr = (uint32_t *)FLASH_BANK1_BASE; uint32_t *DestAddr = (uint32_t *)D1_DTCMRAM_BASE; memcpy(DestAddr, SouceAddr, 0x400); /* 設置中斷向量表到ITCM里面 */ SCB->VTOR = D1_DTCMRAM_BASE; MainRAM(); }
至此就設置完畢了,另外注意以下兩點:
- 不限制設置分組,單獨設置一個C文件也是可以的。
- 如果大家將HAL_Driver分組也放在了ITCM里面,會有如下警告,這個不用管,是刪除了冗余函數。
28.3 實驗例程說明(MDK)
配套例子:
V7-007_時間關鍵代碼在ITCM執行的超簡單方法
實驗目的:
- 學習時間關鍵代碼在ITCM執行的超簡單方法,同時中斷向量表和變量放DTCM。
實驗內容:
- 系統上電后驅動了1個軟件定時器,每100ms翻轉一次LED2。
- 啟動1個TIM6周期性中斷,頻率10KHz,在中斷服務程序里面翻轉FMC擴展引腳20和23。
實驗操作:
- K1按鍵按下,開啟TIM6的周期性中斷。
- K2按鍵按下,關閉TIM6的周期性中斷。
上電后串口打印的信息:
波特率 115200,數據位 8,奇偶校驗位無,停止位 1
程序設計:
系統棧大小分配:
RAM空間用的DTCM:
硬件外設初始化
硬件外設的初始化是在 bsp.c 文件實現:
/* ********************************************************************************************************* * 函 數 名: bsp_Init * 功能說明: 初始化所有的硬件設備。該函數配置CPU寄存器和外設的寄存器並初始化一些全局變量。只需要調用一次 * 形 參:無 * 返 回 值: 無 ********************************************************************************************************* */ void bsp_Init(void) { /* 配置MPU */ MPU_Config(); /* 使能L1 Cache */ CPU_CACHE_Enable(); /* STM32H7xx HAL 庫初始化,此時系統用的還是H7自帶的64MHz,HSI時鍾: - 調用函數HAL_InitTick,初始化滴答時鍾中斷1ms。 - 設置NVIV優先級分組為4。 */ HAL_Init(); /* 配置系統時鍾到400MHz - 切換使用HSE。 - 此函數會更新全局變量SystemCoreClock,並重新配置HAL_InitTick。 */ SystemClock_Config(); /* Event Recorder: - 可用於代碼執行時間測量,MDK5.25及其以上版本才支持,IAR不支持。 - 默認不開啟,如果要使能此選項,務必看V7開發板用戶手冊第xx章 */ #if Enable_EventRecorder == 1 /* 初始化EventRecorder並開啟 */ EventRecorderInitialize(EventRecordAll, 1U); EventRecorderStart(); #endif bsp_InitKey(); /* 按鍵初始化,要放在滴答定時器之前,因為按鈕檢測是通過滴答定時器掃描 */ bsp_InitTimer(); /* 初始化滴答定時器 */ bsp_InitUart(); /* 初始化串口 */ bsp_InitExtIO(); /* 初始化FMC總線74HC574擴展IO. 必須在 bsp_InitLed()前執行 */ bsp_InitLed(); /* 初始化LED */ }
MPU配置和Cache配置:
數據Cache和指令Cache都開啟。配置了AXI SRAM區(本例子未用到AXI SRAM)和FMC的擴展IO區。
/* ********************************************************************************************************* * 函 數 名: MPU_Config * 功能說明: 配置MPU * 形 參: 無 * 返 回 值: 無 ********************************************************************************************************* */ static void MPU_Config( void ) { MPU_Region_InitTypeDef MPU_InitStruct; /* 禁止 MPU */ HAL_MPU_Disable(); /* 配置AXI SRAM的MPU屬性為Write back, Read allocate,Write allocate */ MPU_InitStruct.Enable = MPU_REGION_ENABLE; MPU_InitStruct.BaseAddress = 0x24000000; MPU_InitStruct.Size = MPU_REGION_SIZE_512KB; MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS; MPU_InitStruct.IsBufferable = MPU_ACCESS_BUFFERABLE; MPU_InitStruct.IsCacheable = MPU_ACCESS_CACHEABLE; MPU_InitStruct.IsShareable = MPU_ACCESS_NOT_SHAREABLE; MPU_InitStruct.Number = MPU_REGION_NUMBER0; MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL1; MPU_InitStruct.SubRegionDisable = 0x00; MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_ENABLE; HAL_MPU_ConfigRegion(&MPU_InitStruct); /* 配置FMC擴展IO的MPU屬性為Device或者Strongly Ordered */ MPU_InitStruct.Enable = MPU_REGION_ENABLE; MPU_InitStruct.BaseAddress = 0x60000000; MPU_InitStruct.Size = ARM_MPU_REGION_SIZE_64KB; MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS; MPU_InitStruct.IsBufferable = MPU_ACCESS_BUFFERABLE; MPU_InitStruct.IsCacheable = MPU_ACCESS_NOT_CACHEABLE; MPU_InitStruct.IsShareable = MPU_ACCESS_NOT_SHAREABLE; MPU_InitStruct.Number = MPU_REGION_NUMBER1; MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL0; MPU_InitStruct.SubRegionDisable = 0x00; MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_ENABLE; HAL_MPU_ConfigRegion(&MPU_InitStruct); /*使能 MPU */ HAL_MPU_Enable(MPU_PRIVILEGED_DEFAULT); } /* ********************************************************************************************************* * 函 數 名: CPU_CACHE_Enable * 功能說明: 使能L1 Cache * 形 參: 無 * 返 回 值: 無 ********************************************************************************************************* */ static void CPU_CACHE_Enable(void) { /* 使能 I-Cache */ SCB_EnableICache(); /* 使能 D-Cache */ SCB_EnableDCache(); }
主功能:
主程序實現如下操作:
- 系統上電后驅動了1個軟件定時器,每100ms翻轉一次LED2。
- 啟動1個TIM6周期性中斷,頻率10KHz,在中斷服務程序里面翻轉FMC擴展引腳20和23。
- K1按鍵按下,開啟TIM6的周期性中斷。
- K2按鍵按下,關閉TIM6的周期性中斷。
/* ********************************************************************************************************* * 函 數 名: main * 功能說明: 標准c程序入口。 * 形 參: 無 * 返 回 值: 無 ********************************************************************************************************* */ int main(void) { uint32_t *SouceAddr = (uint32_t *)FLASH_BANK1_BASE; uint32_t *DestAddr = (uint32_t *)D1_DTCMRAM_BASE; memcpy(DestAddr, SouceAddr, 0x400); /* 設置中斷向量表到ITCM里面 */ SCB->VTOR = D1_DTCMRAM_BASE; MainRAM(); } /* ********************************************************************************************************* * 函 數 名: MainRAM * 功能說明: c程序入口 * 形 參: 無 * 返 回 值: 錯誤代碼(無需處理) ********************************************************************************************************* */ int MainRAM(void) { uint8_t ucKeyCode; /* 按鍵代碼 */ bsp_Init(); /* 硬件初始化 */ PrintfLogo(); /* 打印例程名稱和版本等信息 */ PrintfHelp(); /* 打印操作提示 */ bsp_StartAutoTimer(0, 100); /* 啟動1個100ms的自動重裝的定時器 */ bsp_SetTIMforInt(TIM6, 10000, 2, 0); /* 設置為10KHz頻率定時器中斷*/ /* 進入主程序循環體 */ while (1) { bsp_Idle(); /* 這個函數在bsp.c文件。用戶可以修改這個函數實現CPU休眠和喂狗 */ /* 判斷定時器超時時間 */ if (bsp_CheckTimer(0)) { /* 每隔100ms 進來一次 */ bsp_LedToggle(2); } /* 按鍵濾波和檢測由后台systick中斷服務程序實現,我們只需要調用bsp_GetKey讀取鍵值即可。 */ ucKeyCode = bsp_GetKey(); /* 讀取鍵值, 無鍵按下時返回 KEY_NONE = 0 */ if (ucKeyCode != KEY_NONE) { switch (ucKeyCode) { case KEY_DOWN_K1: /* K1鍵按下,開啟TIM6的周期性中斷*/ TIM6->DIER |= TIM_IT_UPDATE; break; case KEY_DOWN_K2: /* K2鍵按下,關閉TIM6的周期性中斷*/ TIM6->DIER &= ~TIM_IT_UPDATE; break; default: /* 其它的鍵值不處理 */ break; } } } } /* ********************************************************************************************************* * 函 數 名: TIM6_DAC_IRQHandler * 功能說明: TIM6定時中斷服務程序 * 返 回 值: 無 ********************************************************************************************************* */ void TIM6_DAC_IRQHandler(void) { if((TIM6->SR & TIM_FLAG_UPDATE) != RESET) { /* 清除更新標志 */ TIM6->SR = ~ TIM_FLAG_UPDATE; /* 翻轉FMC擴展引腳20和23腳 */ HC574_TogglePin(GPIO_PIN_23); HC574_TogglePin(GPIO_PIN_20); } }
28.4 總結
本章節就為大家交流這么多,對速度有要求的應用部分,建議使用ITCM和DTCM來達到最高性能。