AVR存儲器簡介:
AVR 系列單片機內部有三種類型的被獨立編址的存儲器,它們分別為:
1、Flash 程序存儲器(即:程序存儲空間、閃存)
2、SRAM 數據存儲器(即:動態內存)
3、EEPROM 數據存儲器
單片機采用哈弗結構,將程序存儲器和數據存儲器分開,而數據存儲器RAM通常比較小,而程序存儲器Flash空間比較大,因此就需要將占用空間較大的不需要改變的數據放在Flash中。
比如需要單片機支持LCD顯示文字,就需要一個龐大的字體庫,可達到幾kb,這樣打的數據量放在RAM中是不合適的,只能放在Flash中。
pgmspace.h就提供了與之相關的讀寫操作。
問題導引:
編譯Arduino程序時,會提示:
而我們編譯時經常遇到的問題是:
【程序存儲空間】剩余很多,而【動態內存】不足,導致無法成功寫入。這個問題往往出現在聲明了數據“較大”的常量特別是數組的情況下。
解決方案:
為解決這個問題,我們可以將本來應該寫到【動態內存】的常量,寫入【程序存儲空間】,以達到節約【動態內存】空間的目的。
PROGMEM 關鍵字(或數據類型)使用到的庫:pgmspace.h
數據定義:
1、作為【全局】常量時,直接使用 PROGMEM 關鍵字即可, PROGMEM 關鍵字的位置比較隨意,但為了Arduino早期版本的兼容性,推薦放到后面。如:
2、作為【局部】常量時,需要配合 static 關鍵字使用,如:
3、另外一種定義形式,不用 PROGMEM 關鍵字,而是直接用 PROGMEM 數據類型,如:
4、字符串常量的定義
數據讀取:
到這里,我們的程序還不能正常工作。
因為當你向一個函數傳遞指向Flash的指針時,它會認為這是指向RAM的指針,從而在RAM中尋找數據,使得程序出錯。
所以還需要專門的函數來處理指向Flash的指針。
數據保存到程序存儲空間后,需要特殊的方法(函數)來讀取:
1、非數組常量的讀取方法
2、數組常量的讀取方法
3、字符串復制方法
strcpy_P 函數負責從【程序存儲空間】復制一個字符串到【動態內存】緩沖區"buffer"。
注意:復制時要確保緩沖區足夠大。
相關的處理函數:
關於使用F()宏:
通常我們都使用如下語句,進行串口輸出:
但這樣使用,每次調用時,都會先將數據保存在【動態內存】中。
當我們要輸出長的字符串時,就會占用很多的【動態內存】空間。
使用 F() 就可以很好的解決這個問題,F() 可以將字符串輕松的保存在FLASH中。
關於數據讀取
1、對於Flash中數組的處理,pgmspace.h也提供了幾個宏:
分別用來讀取地址addr處的1、2、4個字節和讀取浮點數。
考慮這樣一個問題,Flash中保存着若干個字符串,每個字符串的地址又以數組形式保存在Flash中,即:
要如何比較s1和s2呢?
首先需要讀取兩個字符串的地址,然后通過strcmp_P函數來比較字符串。
要注意,這兩個指針都是16位的,而不是8位!
所以,代碼應該是:
編譯器會對這樣的代碼給出一個警告,因為pgm_read_byte()得到的是16位整形,而從函數原型中可以看到,函數需要的是指針,可以用兩種方法消除這個警告:
(1)強制類型轉換
(2)使用另一個宏
2、讀取數據的問題
之前說到,指針是16位的,能尋址64kB的地址空間。
而在AVR的有些芯片上比如mega2560,Flash空間為256kB,超過64kB的空間將如何尋址呢?
pgmspace.h提供了兩種Flash尋址的宏:
一種是采用16位地址的短地址尋址,最多尋址64kB:
另一種是采用32位地址的長地址尋址,最多尋址4GB空間:
兩種尋址在性能上有所差別,短地址尋址速度要快很多,而且64kB也足夠使用了,因此默認使用短地址尋址:
當必須尋址超過64kB空間時,可以手動的使用長地址尋址。
————————————————
原文鏈接:https://blog.csdn.net/sdlgq/article/details/88720706