前言
平時我們可能很少去關注程式運作的時間,但是在一些情況下可能需要對程式進行一個整體的複盤、優化。那麼,程式運作的時間就是一個可以考慮的方面,可以測一下某些代碼塊、函數、算法的運作時間,然後整體考慮看看有沒有必要進行優化。
之前在某工控類項目中,我就有接到一個任務去測試程式中關鍵代碼的執行時間,并輸出報告。當時是使用一個GPIO+示波器進行測試的,也可以使用邏輯分析儀來測。
當時測量的方法很簡單:在要測試的代碼塊/函數之前設定該GPIO的電平為高電平,在要測試的代碼塊/函數之後設定該GPIO為低電平,使用示波器測高電平的時間,就知道了這一代碼塊/函數的運作時間。下面就通過執行個體來介紹一下這種簡單而有效的方法。
我這裡使用邏輯分析儀來測量,使用小熊派開發闆來驗證,小熊派的主要為STM32L431RCT6,系統時鐘設定為80MHz。
這裡順帶提一點題外話,之前有一些初學的讀者朋友問我說邏輯分析儀貴不貴。邏輯分析儀有貴的也有便宜的,貴則上千上萬元,便宜則有幾十、幾百。我覺得無論工作、還是學習,都有必要入手一個邏輯分析儀。
本篇筆記的測試用的邏輯分析儀就是某寶上二十幾塊錢買的,可以滿足平時的學習所用。條件有限的學生朋友可以入手。有條件的可以考慮入手幾百塊錢的。
GPIO+邏輯分析儀測時間
1、測量HAL_Delay函數
STM32的HAL庫有給我們提供一個HAL_Delay延時函數,這是一個ms級延時函數。這個延時函數依賴于系統滴答定時器,是以是一個比較精确的延時函數。這裡,我們就使用GPIO+邏輯分析儀的方法來測量一下這個延時函數。為了友善測試,我們在while死循環裡進行測量。
代碼:
測量結果:
可見,我們通過邏輯分析儀測出了HAL_Delay(100);運作的時間為100.4315ms,符合我們的預期。這裡高電平兩側其實就是低電平部分,隻不過低電平持續的時間太短了,在這裡看起來像一條豎線,我們放大來看看:
結果已經很準了,可以滿足平時的測量。這種測量很難保證百分之百的精确,小數點後面的那一部分可能是受很多不可控因素的影響,這不在我們本篇文章的讨論範圍之内。
我們是想通過這個示例來介紹這種測量方法的使用及證明這種方法是可行的。下面再繼續看兩個執行個體。
測量軟體延時函數
我們以前剛開始學單片機的時候,經常有用到一些粗略的延時函數,其實作方法就是循環執行n條空語句,以達到一個延時的效果。那麼,我們怎麼來構造一個us級或ms級的粗略延時函數(軟體延時函數)。我們之前看到的粗略延時函數類似這樣子:
這些函數裡面需要給出一些循環的次數,這個值是怎麼來确定的呢?比如上面這個函數中123這個值是怎麼來确定的?我們可以使用GPIO+邏輯分析儀的方法來進行一個簡單的确定。
确定1us:
不同的處理器,結果是不一樣的。針對小熊派開發闆(主要:STM32L431RCT6),循環運作15條空語句的時間實測結果是1.083us,這算是比較接近1us了。我們就運用這個結果來建構一個us級軟體延時函數如下:
接下來我們測一下soft_delay_us(100);實際運作了多長時間:
可見,結果差不多接近我們想要的結果。建構這樣的粗略延時函數可以使用這樣的方式來确定一些循環次數的值。
對比查表法與正常法的運作時間
在之前的文章:空間換時間,查表法的經典例子中,我們有說可以适當使用查表法降低程式的執行時間。這裡我們來實際測量對比一下那篇文章中查表法與正常法的優劣。
關鍵代碼:
/* 測試結果 */struct test_res{ unsigned int data; /* 資料 */ unsigned int count; /* 資料中1的個數 */};/* ============正常法============ */#if 1struct test_res get_test_res(unsigned int data){ /* 儲存測試結果 */ struct test_res res; /* 保證資料總會在0~0xf之間 */ // unsigned int temp = data & 0xf; unsigned int temp = data & 0xff; res.count = 0; res.data = temp; /* 循環判斷每一位 */ for (int i = 0; i < 16; i++) { if (temp & 0x01) { res.count++; } temp >>= 1; } return res;}#else/* ============查表法============ */int table[16] = {0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4};struct test_res get_test_res(unsigned int data){ /* 儲存測試結果 */ struct test_res res; /* 保證資料總會在0~0xf之間 */ unsigned int temp = data & 0xff; /* 擷取低4位中1的個數 */ unsigned int low_data = temp & 0xf; unsigned int low_cnt = table[low_data]; /* 擷取高4位中1的個數 */ unsigned int high_data = (temp >> 4) & 0xf; unsigned int high_cnt = table[high_data]; /* 結果 */ res.count = low_cnt + high_cnt; res.data = temp; return res;}#endifint main(void){ /* USER CODE BEGIN 1 */ struct test_res res = {0}; /* 省略部分代碼。。。。。。。。。 */ while (1) { /* USER CODE END WHILE */ /* USER CODE BEGIN 3 */ HAL_GPIO_WritePin(GPIOA, GPIO_PIN_2, GPIO_PIN_SET); res = get_test_res(30); HAL_GPIO_WritePin(GPIOA, GPIO_PIN_2, GPIO_PIN_RESET); } /* USER CODE END 3 */}
正常法程式的運作時間:
查表法程式的運作時間:
可見,這個例子中正常法程式運作時間約為2ns,而查表法程式運作時間約為500ns。查表法的程式運作之間僅為正常法的1/4,省下了3/4的時間。随着調用次數的增多,這裡的查表法的優勢越大。比如循環計算0~31這32個數中每一個數二進制位為1的個數,則相關代碼改為:
int i; while (1) { HAL_GPIO_WritePin(GPIOA, GPIO_PIN_2, GPIO_PIN_SET); for (i = 0; i < 32; i++) { res = get_test_res(i); } HAL_GPIO_WritePin(GPIOA, GPIO_PIN_2, GPIO_PIN_RESET); }
正常法:
查表法:
可見,随着調用次數的增多,查表法相對于正常法更省時,即查表法的優勢越大。
以上就是關于GPIO+邏輯分析儀測程式運作時間的幾個執行個體。下面順帶提一下使用MDK+ST-LINK測STM32程式運作時間的方法。
MDK+ST-LINK測時間
在使用MDK作為開發工具時,可以搭配一些仿真器來檢視程式執行時間。這裡通過執行個體來介紹MDK+ST-LINK測STM32程式運作時間的方法。
這裡重點是設定Trace裡面的系統核心時鐘,我們這裡使用的是小熊派開發闆(主要:STM32L431RCT6),并且配置的系統時鐘是80MHz:
是以在Trace中要設定為80MHz。這個得根據實際晶片的型号就需要根據進行修改,比如STM32F103系列預設是72MHz,STM32F429系列預設為180MHz等,根據實際進行修改。
下面我們通過線上調試、打斷點的方式看一下 HAL_Delay(1000);運作了多長時間:
可見程式運作到HAL_Delay(1000);前後的時間分别為:
前:0.00008964s後:1.00108161s
即HAL_Delay(1000);走過的時間約為1s,符合預期。
以上就是本次的分享,如有錯誤,歡迎指出!感謝閱讀