Arduino Uno, ATMEGA328P
Пока пытаюсь реализовать навигацию по файловой системе SD-карты.
Вот скетч, без ассемблерной части.
#define K_UP 5
#define K_DOWN 3
#define K_ENTER 6
byte OledTxtBuff[5][23]; // Текстовый буфер дисплея 4*21 с лишней строкой (для организации в будущем плавного скроллинга) и дополнительными 2 байтами на строку
byte shift=0;
byte CursorX,CursorY;
int keyDown;
byte keyUp;
byte KeyPressed=0;
char FName[13];
const PROGMEM char inits1[]="Initializing SD card.";
const PROGMEM char inits2[]="initialization failed";
const PROGMEM char inits3[]="initialization done.";
const PROGMEM char NoFiles[]="No Files!!!";
const PROGMEM char Updir[]="[..]";
#include <SPI.h>
#include <SD.h>
File Dir,entry; // для экономии памяти эту хрень сделаем глобальной
extern "C" {
void OledInitialize();
void TxtRefresh();
void Pause(byte p);
}
void setup() {
OledInitialize();
pinMode(A1,INPUT_PULLUP);
pinMode(A2,INPUT_PULLUP);
pinMode(A3,INPUT_PULLUP);
CursorX=0; CursorY=1;
Serial.begin(9600);
StringOut(inits1);
if (!SD.begin(4)) {
CursorX=0;
CursorY=2;
StringOut(inits2);
while (1);
}
CursorX=0; CursorY=2;
StringOut(inits3);
}
void loop()
{
WaitKeyPress();
Dir=SD.open("/");
DirView("/",1);
}
// level=1 для корня, =0 для подпапок
void DirView(char * sroot, byte level)
{
Serial.print("after ");Serial.println(getRamFree());
int Selected=level;
byte Fpos=0;
// Вычисляем кол-во файлов в папке
int FCnt=0;
while (true) {
entry = Dir.openNextFile();
if (!entry) {break;}
FCnt++;
entry.close();
}
// если в корне нет ничего
if ((level==1) and (FCnt==0))
{
CursorX=0;
CursorY=5;
ClearBuffer();
StringOut(NoFiles);
while(1);
}
while (true)
{
// отрисовка фрагмента списка файлов
ClearBuffer();
Dir.rewindDirectory();
for (int k=1;k<Selected-Fpos;k++){Dir.openNextFile().close();};
for (CursorY=0; CursorY<4;CursorY++)
{
if (CursorY==Fpos) {OledTxtBuff[CursorY][3]=0x3D; OledTxtBuff[CursorY][4]=0x3E; } // рисуем стрелочку => для выбора
CursorX=5;
if ((Selected+CursorY-Fpos)==0) {TStringOut(Updir); continue;}
if (Selected+CursorY-Fpos>FCnt) {break;}
entry=Dir.openNextFile();
if (entry.isDirectory()) {TStringOut(entry.name());} else { strcpy(FName,entry.name()); DownCase(FName); TStringOut(FName);};
entry.close();
}
TxtRefresh();
// обработка навигации
WaitKeyPress();
if ((KeyPressed==K_UP) and (Selected>level)) {Selected--; if (Fpos>0) {Fpos--;}}
if ((KeyPressed==K_DOWN) and (Selected<FCnt)) {Selected++; if (Fpos<3) {Fpos++;}}
if (KeyPressed==K_ENTER)
{
if (Selected==0)
{
Dir.close(); return; // Возврат на предыдущий уровень
} else {
Dir.rewindDirectory();
for (int k=1;k<Selected;k++){Dir.openNextFile().close();}
entry=Dir.openNextFile();
if (entry.isDirectory())
{
// Готовимся к переходу на следующий уровень
Dir.close(); // Закрываем для экономии памяти. Путь к родительскому каталогу занимает меньше памяти. Из него и восстановим.
Dir=entry;
// Готовим путь для возврата. Пока он готовится на 1 шаг вперед, что явно лишнее. Нужно подумать и переделать.
char subroot[strlen(sroot)+strlen(Dir.name())+2];
strcpy(subroot,sroot);
strcat(subroot,Dir.name());
strcat(subroot,"/");
Serial.println(subroot);
Serial.print("before ");Serial.println(getRamFree());
DirView(subroot,0); // На следующий уровень вложенности
Dir=SD.open(sroot); // Возврат из подкаталога. Восстанавливаем подкаталог по его пути.
} else {
// Выбран файл, то нужно его обработать, пока не реализовано
entry.close();
}
}
}
}
}
void WaitKeyPress(void) {
KeyPressed=0;
while (KeyPressed==0) {KeyPressed=KeyPress();}
}
byte KeyPress(void) {
byte res=0;
byte KEY=((digitalRead(A3)<<2)|(digitalRead(A2)<<1)|digitalRead(A1))&7;
if ((KEY==K_UP) or (KEY==K_DOWN) or (KEY==K_ENTER)) {keyDown++; res=(keyDown==1&&keyUp==200 ? (KEY) :0); keyUp=0;} else {keyDown=0; keyUp++;}
if (keyDown>=15020) {keyDown=15000; res=KEY;}
if (keyUp>=200) {keyUp=200;}
Pause(0);
return res;
}
void StringOut(char *S) {
for (byte i=0;i<strlen(S);i++) {OledTxtBuff[CursorY][CursorX+i]=S[i];}
TxtRefresh();
}
void TStringOut(char *S) {
for (byte i=0;i<strlen(S);i++) {OledTxtBuff[CursorY][CursorX+i]=S[i];}
}
void StringOut(const char *S) {
for (byte i=0;i<strlen_P(S);i++) {OledTxtBuff[CursorY][CursorX+i]=pgm_read_byte(S+i);}
TxtRefresh();
}
void TStringOut(const char *S) {
for (byte i=0;i<strlen_P(S);i++) {OledTxtBuff[CursorY][CursorX+i]=pgm_read_byte(S+i);}
}
void DownCase(char * S){
for (byte i=0;i<strlen(S);i++) { if ((S[i]>0x3F) and (S[i]<0x60)) {S[i]=S[i]+0x20;}}
}
void ClearBuffer(void) {
for (byte i=0;i<5;i++) {
for (byte j=0;j<23;j++) {
OledTxtBuff[i][j]=0;
}
}
}
inline uint32_t getRamFree(void) {
extern uint16_t __heap_start, *__brkval;
uint16_t v;
return (uint32_t) &v - (__brkval == 0 ? (uint32_t) &__heap_start : (uint32_t) __brkval);
}
Замеряю кол-во свободной памяти до вызова функции и сразу же после вызова
При переходе на несколько уровней вложенности каталогов в СОМ порту получаю следующее:
after 891
/KATEG1/
before 860
after 790
/KATEG1/SUB2/
before 821
after 746
/KATEG1/SUB2/HHHH/
before 715
after 635
Видно, что при очередном вызове функции тратится от 70 до 80 байт
Куда может столько тратиться, если там нужно в стеке сохранять максимум 34 байта (32 регистра и адрес возврата)? Локальных переменных там мало, для них регистров более чем достаточно. В куче по идее должна размещаться единственная переменная subroot, но она создается между after и before, а в данном случае меня интересует что творится между before и after.
т.е. между этим:
...
Serial.print("before ");Serial.println(getRamFree());
DirView(subroot,0); // На следующий уровень вложенности
...
и этим:
void DirView(char * sroot, byte level)
{
Serial.print("after ");Serial.println(getRamFree());
...
Куда программа может расходовать столько памяти?