Thiếu memory khi lập trình Arduino và cách giải quyết.
Lacking memory with Arduino program.
Đây là một số gợi ý để giải quyết nếu bạn đã từng thấy warning dưới:
Have you ever seen any below warning?
- Low memory available, stability problems may occur.
- Not enough memory; see http://www.arduino.cc/en/Guide/Troubleshooting#size for tips on reducing your footprint.
If the answer is YES, i would like to suggest some solutions for saving memory in this articles.
Các nguyên nhân:
Some root causes:
1. Khai báo biến local tràn lan.
2. Dùng hàm lcd.print or serial.print tùy tiện.
3. Cấp phát động tùy tiện, ko tính toán kỹ lưỡng số lượng phần từ trong mảng cần dùng.
....
Một số gợi ý để tiết kiệm memory
Some suggestions to save memory.
- Sử dụng một số kỹ thuật như #pragma pack(), union, bit field, cấp phát động.
Using some C technique #pragma pack(), union, bit field, dynamic memory allocation.
- Sử dụng con trỏ một cách khéo léo.
Using smart pointer.
- Chuyển các chuỗi hiển thị vào flash, thay vì chạy từ RAM.
Moving constant string to flash.
- ...
Chi tiết:
Detail:
1.Sử dụng pragma pack để loại bỏ các byte thừa khi khai báo struct
Detail:
1.Sử dụng pragma pack để loại bỏ các byte thừa khi khai báo struct
1. Use #pragma pack(1) to byte align your structures
Ví dụ:
int calibration_req(){ int ret = -1; ret = system("/usr/sbin/touch-calibration &") ; return ret; }
"Shape" of Above structure in memory.
| 1 | 2 | 3 | 4 |
| AA(1) |padbyte |padbyte |padbyte |
| BB(1) | BB(2) | BB(3) | BB(4) |
| CC(1) |padbyte |padbyte |padbyte |
=> sizeof(Test) là 12 bytes, do hầu hết các compiler sẽ tự động thêm các byte (padding byte) để cho phù hợp với địa chỉ trong bộ nhớ. Ví dụ trên là cho bộ nhớ có địa chỉ 32 bit.
Nếu sử dụng #pragma pack(1)
| 1 |
| AA(1) |
| BB(1) |
| BB(2) |
| BB(3) |
| BB(4) |
| CC(1) |
Nếu sử dụng #pragma pack(2)
| 1 | 2 |
| AA(1) | padbyte |
| BB(1) | BB(2) |
| BB(3) | BB(4) |
| CC(1) | padbyte |
Cách dùng:
How to use:
How to use:
#pragma pack(1)
struct Test;
2. Sử dụng union cho các structure có các kiểu dữ liệu khác nhau
2. Use unions where a structure can contain different types of data
Ví dụ:
typedef struct {
int mode;
int blLevel;
} CMD_PRM_1_t;
typedef struct {
DateTime initDateTime;
char[32] alarmDispText;
} CMD_PRM_2_t;
typedef enum {
CMD_0 = 0,
CMD_1 = 1
} CMD_t;
typedef struct {
CMD_t command_id;
union{
CMD_PRM_1_t lcdSetting;
CMD_PRM_2_t alarmSetting;
} prm;
} CMD_INFO_t;
Cách khai bảo này có nhiều lợi điểm:
Advantage:
1.Linh hoạt trong việc thay đổi cấu trúc command ( bao gồm command_id và command_prm)
More flexibility to change command structure.
2. Chỉ dùng 1 struct CMD_INFO_t cho tất cả các hàm liên quan đến xử lý command.
Interface with only one struct CMD_INFO_t to react with any command.
3. Có thể define nhiều kiểu parameter trong union nhưng chỉ có MỘT member có thể có giá trị trong một thời điểm => chỉ sử dụng 1 memory location cho nhiều mục đích.
Able to declare multiple parameter in union. But only one member is set value in given time.
3. Sử dụng bit field để lưu các cờ và số nguyên nhỏ.
3. Use bit fields rather than ints to store flags and small integers.
Ví dụ:
unsigned int mainStatusFlag; //0: Idle, 1: Run, 2 OnHold
unsigned receiveDataFlag; //0: Receive, 1 Not receive
unsigned int ledState //0: green, 1:red, 2:violet, 3:yellow, 4:orange, 5:grey
=> 3 biến trên mất ít nhất 3 bytes.
struct
{
unsigned int maiStatusFlag:2;
unsigned int receiveDataFlag:1;
unsigned int ledState:3
unsigned int reserve:2
}Flag_t;
=> Cấu trúc trên tốn 1 byte bộ nhớ.
4. Sử dụng string pool để chứa các chuỗi const cần hiển thị.
4. Implement a string pool and use pointers.
Ví dụ khi ta muốn hiển thị một string lên lcd
Thông thường ta có thể viết lcd.print("Hello Trumpy");
Việc này tiêu tốn một lượng memory nhất định để store string "Hello Trumpy" trước khi hiển thị ra. Với những ứng dụng đỏi hỏi phải hiển thị string nhiều, nó sẽ đỏi hỏi kha khá memory.
Cách xử lý:
- Chuyển các string này vào bộ nhớ flash. Sau đó dùng con trỏ để trỏ đến đó mỗi khi cần hiển thị
Khai báo:
const char EN0 [] PROGMEM = "Hello Trumpy";
const char EN0 [] PROGMEM = "I love arduino";
const char* const librarieEN[] PROGMEM = {EN0, EN1};
Sử dụng:
lcd.print( (const __FlashStringHelper *) pgm_read_word(&(librarieEN[EN0])) );
lcd.print( (const __FlashStringHelper *) pgm_read_word(&(librarieEN[EN1])) );
Với cách này, memory sẽ giảm đáng kế do dư liệu hiển thị được đọc trực tiếp từ flash và dữ liệu hiển thị cũng được tổ chức tốt hơn.
5. Sử dụng cấp phát động. Tính toán kỹ lưỡng số lượng phần tử trước khi cấp phát bộ nhớ.
5. When using dynamic memory allocation, compute the number of elements required in advance to avoid reallocs.
Tham khảo
Reference
https://learn.adafruit.com/memories-of-an-arduino/optimizing-sram
Comments
Post a Comment