stm32的const數據位於Flash上,Flash會比RAM大很多倍
///插播一條:我自己在今年年初錄製了一套還比較系統的入門單片機教程,想要的同學找我拿就行了免費的,私信我就可以哦~點我頭像黑色字體加我地球呺也能領取哦。最近比較閒,帶做畢設,帶學生參加省級或以上比賽///
01首先聊一聊
大家都知道進行單片機編程和計算機編程有個最大的差別就是單片機的資源非常的有限,並且對於大部分低端單片機而言都沒有作業系統。除了一些嵌入式級的晶片用了Linux系統外,其他大部分操作都是比較簡單的RTOS,可能還有一些簡單的應用或者晶片根本不用系統,直接是裸機程序。
不過,大局部單片機編程都與硬體密切的聯合,這樣工程師能夠對當前的項目對象有更多的把控才能和了解才能。但是由於它的簡略,我們平時在工作中往往須要控制一個項目標老本,對於單片機的選型和資源的評估都是非常謹慎;同樣隨著我們項目功能的不斷擴展,也會讓系統程序逐步變得龐大,這時候資源的使用就更須要節約點用了。
所以當資源受限制(一般的單片機RAM也就Kb級別),假如說單片機RAM不夠了,即便你有再牛的算法可能也沒法加入到項目中來,那麼有些同志們會問,那換晶片不就能夠了嗎?我只想說這位同志你想多了,對於不怎麼熱賣產品或者不規範的公司可能還允許你試一試,可是一般的公司項目卡著走的,換了主控晶片,暫且不說軟體上的移植工作,換了晶片老本上必定增加,產品的測試都得重新布局,老闆領導可不願意了。
那麼主控晶片換不了我們還有什麼辦法呢?那我們應該從原本的程序中擠出資源來使用了,下面我總結了幾種常總方法供大家參照。
02共聯體-union
union-共聯體,是C語言常用得關鍵字。從字面上的意思就是共有聯合在一起的意思,union所有的成員共有維護一段能夠內存空間,其內存的大小取決於所有成員中占用空間最大的成員。
union構造體由於是共用同一片內存能夠大大節省內存空間,那一般什麼情況下使用union?又或者union還有什麼特點?下面我將用幾點為大家解答。
1)所有的union的成員及自身的地址是一樣的。
2)union的存儲模型受大小端的影響,我們能夠通過下面的代碼進行測試。(假如輸出結果為1,表示小端模式,否則為大端模式)
大端模式(Big_endian):一個數據的高位元組存儲在低地址,低位元組存儲在高地址。其指針指向的首地址位於低地址。小端模式(Little_endian):一個數據的高位元組存儲在高地址,低位元組存儲在低地址。其指針指向的首地址位於高地址。
3)union不同於構造體struct,union對成員的變更可能會影響到其他成員變量,所以我們要構成一種互斥使用,假如說我們的順序執行其實就是每個代碼都是互斥的,所以我們能夠用union進行函數處理緩存等。(個人覺得也能夠認為是分時復用,並且是不會受內存初值影響的處理)
#include typedef union _tag_test{ char a; int b;}uTest;
uTest test;unsigned char Checktype(void);
int main(void){ printf("%x\n",(unsigned int)&test.a);
printf("%x\n",(unsigned int)&test.b);
printf("%x\n",(unsigned int)&test);
printf("%d\n",Checktype()); } unsigned char Checktype(void){ uTest chk; chk.b = 0x01;
if(chk.a == 0x01)return 1; return 0; }
03位域
位域可能對於初學者用得比較少,不過對於大局部參加工作的工程師應該屢見不鮮了,的確它也是我們省內存的神器。
由於在我們平時編程過程中,我們使用的變量與實際情況是息息相關的,就假如說開關的狀態,我們一般就是0或者是1分別表示翻開和關閉,那麼我們用一個bit就能表示,假設說我們用一個char來存儲就簡直浪費了7個bit,假如以後也有類似的的情況,那麼大局部內存都得不到有效的應用。所以C語言的位域就是用來攻克這個問題。
不過,我們須要注意如下幾點:
1)位域是在構造體中實現的,其中位域規定的長度不能超過所定義類型,且一個位域只能定義在同一個存儲單元中。
2)沒名位域的使用,能夠看下面的代碼。
3)由於位域與數據類型有關係,那麼他的內存占用情況也與平台的位數相關。(相關內容可網絡查找)
#include//結果:編譯通過//理由:常規形式(構造體占用兩個位元組)typedef struct _tag_test1{ char a:1; char b:1; char c:1; char d:6;}sTest1;
//結果:編譯沒法通過//理由:d的位域長度10超過了char類型長度/*typedef struct _tag_test2{ char a:1; char b:1; char c:1; char d:10;}sTest2;*
///結果:編譯可通過//理由:下面使用沒名位域,且占8個字節typedef struct _tag_test3{ int a:1; int b:1; int :0;
//沒名位域int c:1;}sTest3;int main(void){ printf("%d\n",sizeof(sTest1)); printf("%d\n",sizeof(sTest3)); printf; }
04構造對齊
構造體對齊問題可能大局部人關注的不是很多,可能在通訊領域進行內存的copy時候接觸得比較多。構造體對齊問題也是與平台相關,CPU為了提高訪問內存的效率,一次性可能讀取2個位元組,4個位元組,8個位元組等,所以編譯器會自動對構造體內存進行對齊。
廢話不多說,代碼說明一切:
#include#pragma pack(1)
//有位元組對齊預編譯結果為:12,8
//沒位元組對齊預編譯結果為:6,6
typedef struct _tag_test1{ char a; int b; char c; }STest1;
typedef struct _tag_test2{ int b; char a; char c; }STest2;
int main(void){ printf("%d\n",sizeof(STest1));
printf("%d\n",sizeof(STest2));
printf("最後一個bug\n"); }
算法優化其實主要是我們通過修改一些算法的實現一種效率與內存使用的一個均衡,我們都知道我們的算法都存在著複雜度的問題,我們大局部高效率的算法都是通過使用內存來換效率,也就是一種用空間換時長的概念。那麼當我們內存使用有限的時候我們能夠適當的用時長來換空間的方法,騰出更多的空間來實現更多的功能。
同樣,我們在進行相關設計的時候能夠儘量使用局部變量來減少全局變量的使用!
06利用const
1、const的使用
關於const的用法應該是老生常談的知識點了,假如還有不是特別清楚的小搭檔能夠參照<一文搞定C語言const關鍵字>一文,bug菌就不反覆造輪子了,直接以stm32單片機為例看看const變量的的存儲方式。
運行結果分析一下:
?對於stm32的所有存儲映像都在對應工程所編譯生成的.map文件中,對.map文件(其文件在工程目錄中)的熟悉度就在一定程度上彰顯你對stm32單片機的熟練程度。
?程序編譯成功以後,就能夠直接在map文件中查找const修飾的數組名。
?從上面我們了解到其stDevParam變量位置0x080016b8數據區且位於(.contdata段--只讀數據段)並占用了36個位元組,與我們串口輸出結果是相合乎的。
2、const數據的存儲
通過上面的測試程序顯示了const數據的存儲位置,那麼我們看一下該位置位於stm32的哪塊存儲區域,是RAM還是FLASH?
由於我們節省內存主要就是通過占用更小的RAM來實現相同的項目需求,那麼對於MCU而言最好就是的借助Flash,通過時長來置換空間,拿出對應的數據手冊看看這些存儲範圍是如何分配的。
上圖來源於ST手冊Memory Mapping
很明顯前一節測試的const stDevParam變量位置0x080016b8處,正益處於FLASH存儲位置,所以其並沒有占用RAM資源。
3、const數據使用
很多寫單片機程序的小搭檔都喜愛把一些只讀的變量用全局變量來保存,然而這些變量根本上只保存一些參數,這對於單片機的RAM資源是非常浪費的。
bug菌曾經接手過一個前同事項目,怎麼說呢?可能這個項目他也是接手別人的,該項目MCU還外部擴展了一個16M的SDRAM,大家都覺得反正RAM大,變量隨便定,也不去管數據範圍,動不動就float,double,真的是牛。
直到bug菌接手內存占用率已高達95%,後面稍微添加一些需求感覺RAM就要爆掉了,沒辦法這樣下去究竟會出問題,於是申請了代碼重構,通過優化代碼構造、設計等RAM占用率直接降到了50%左右,能夠想像一下之前的開發人員是多麼的任性。
所以,一句話說得好"前人栽樹,後人乘涼;前人挖坑,後人入fen"。前面我們分析了stm32的const數據位於Flash上,一般Flash都會比RAM大上好幾倍。
這樣對於一些預先設置好的參數等等都能夠整理以後統一放到類似於前面demo中這樣的構造體數組中,從而能夠大大減少對RAM的占用。
注意一點的是 :訪問RAM一般來說會比訪問Flash要快一些,然而大局部項目對於