Функция, возвращающая массив байтов

ss7
Offline
Зарегистрирован: 30.06.2018

Мне нужно прочитать рисунок из подключенной spi flash памяти и вывести на lcd экран ардуинки нано.

По отдельности работает - рисунок заданный так выводится:

const unsigned char testimg [504] = {
0xC0, 0xC0, 0xE0 
....
}
  lcd.draw(testimg,  sizeof(testimg) / sizeof(testimg[0]), true);

Т.к. памяти мало и подключен чип на 4Мб, а размер картинки всегда известен (504 байта под размер дисплея), я просто записываю картинку как массив байтов под опеределённым номером и так же считываю. Функция для чтения 504 байт под определённым номером работает:

void read_from_file (int filenumber) {
  int byteNum = ((504*filenumber)-504);   // Get the number of 1st byte
  unsigned char testimg [504];
  Serial.println("Starting to read image ");
  for(int i = 0; i < 504; i++){      // Read 504 bytes
    Serial.print(flash.readByte(byteNum));
    Serial.print('.');
    byteNum++;
  }
Serial.println("Readen!");
}

Но если я пытаюсь прочитать картинку функцией, подгрузить заранее, а затем вывести на экран, то получается треш:

unsigned char rImage=read_from_file(1);
lcd.draw(rImage, 504, true);


unsigned char read_from_file (int filenumber) {
  int byteNum = ((504*filenumber)-504);   // Get the number of 1st byte
  unsigned char testimg [504];
  Serial.println("Starting to read image ");
  for(int i = 0; i < 504; i++){      // Write 504 bytes
    testimg[i]=flash.readByte(byteNum); 
    byteNum++;
  }
Serial.println("Readen!");
return testimg;
}

Как понимаю, это связано с тем, что для экономии памяти массив не живёт после отработки функции. Я буду считывать разные файлы в разное время. Мне нужно, чтобы всегда хранился подгруженный в массив в данный момент и можно было считать новый. Как правильно сделать?

b707
Offline
Зарегистрирован: 26.05.2017

ss7 пишет:

Как правильно сделать?

Сделать массив глобальным - то есть вынести строчку 7 наружу из функции. Но имейте в виду, что при таком решении массив всегда будет занимать в памяти 504 байта.

ss7
Offline
Зарегистрирован: 30.06.2018

Вынес. Так не работает. Я перепроверил выводом байтов на экран - файл записан корректно. Проверил выводом массива картинки - корректно выглядит этот массив.

При попытке отрисовать, передав из этой функции - выводится треш.

b707
Offline
Зарегистрирован: 26.05.2017

ss7 пишет:

Вынес. Так не работает.

не может быть :) Покажите код

ss7
Offline
Зарегистрирован: 30.06.2018
#include<SPIMemory.h>
#include <Nokia_LCD.h>
SPIFlash flash(10); 
const int FlashChipSelect = 10; // digital pin for flash chip CS pin

Nokia_LCD lcd(3 /* CLK */, 4 /* DIN */, 5 /* DC */, 6 /* CE */, 7 /* RST */);

unsigned char testimg [504];

void setup() {
  Serial.println("Accesing flash...");
  flash.begin();
  
  Serial.println();
  commandList(); // Print list of commands
      // Initialize the screen
  lcd.begin();
  // Set the contrast
  lcd.setContrast(60);  // Good values are usualy between 40 and 60
  // Clear the screen
  lcd.clear(false);

unsigned char rImage=read_from_file(1);
  lcd.draw(rImage, 504, true);
}

//////////////  Read picture from file
unsigned char read_from_file (int filenumber) {
  int byteNum = ((504*filenumber)-504);   // Get the number of 1st byte

  Serial.println("Starting to read image ");
  for(int i = 0; i < 504; i++){      // Read 504 bytes
    testimg[i]=flash.readByte(byteNum); 
    //Serial.print(testimg[i]);
    //Serial.print('.');
    byteNum++;
  }
Serial.println("Readen!");
return testimg;
}

Обратите внимание на закомментированные строчки - так байты выводятся в консоль правильные. Обратите внимание, что в самом первом посте у меня задано массивом и я могу обратиться к любому элементу, в этом варианте я не могу после выполнения функции к rImage[10] к примеру

sadman41
Offline
Зарегистрирован: 19.10.2016

Попытайтесь в draw() передавать глобальный массив, а не какой-то случайный байт.

b707
Offline
Зарегистрирован: 26.05.2017

О божеж мой... Все смешалось.

Если у вас  массив testimg [504] уже глобальный, зачем вы его возвращаете из функции и приравниваете к другому массиву? Работайте с ним напрямую:

#include<SPIMemory.h>
#include <Nokia_LCD.h>
SPIFlash flash(10); 
const int FlashChipSelect = 10; // digital pin for flash chip CS pin

Nokia_LCD lcd(3 /* CLK */, 4 /* DIN */, 5 /* DC */, 6 /* CE */, 7 /* RST */);

unsigned char testimg [504];

void setup() {
  Serial.println("Accesing flash...");
  flash.begin();
  
  Serial.println();
  commandList(); // Print list of commands
      // Initialize the screen
  lcd.begin();
  // Set the contrast
  lcd.setContrast(60);  // Good values are usualy between 40 and 60
  // Clear the screen
  lcd.clear(false);


read_from_file(1);
lcd.draw(testimg, 504, true);
}

//////////////  Read picture from file
void read_from_file (int filenumber) {
  int byteNum = ((504*filenumber)-504);   // Get the number of 1st byte

  Serial.println("Starting to read image ");
  for(int i = 0; i < 504; i++){      // Read 504 bytes
    testimg[i]=flash.readByte(byteNum); 
    //Serial.print(testimg[i]);
    //Serial.print('.');
    byteNum++;
  }
Serial.println("Readen!");

}

 

ss7
Offline
Зарегистрирован: 30.06.2018

Так всё равно выводится грязь, но теперь другая. Вообще, каждый раз разная.

b707
Offline
Зарегистрирован: 26.05.2017

ss7 пишет:

Так всё равно выводится грязь, но теперь другая. Вообще, каждый раз разная.

как выводите? покажите

ss7
Offline
Зарегистрирован: 30.06.2018

Выяснил причину проблемы истинную (вывел на экран начальный массив, потом прочитанный циклом, который функция вернула - всё совпало). Вспомнил, где-то в описании библиотеки SPIMemory что-то было про использование нескольких устройств одновременно. Счас поужинаю и попробую разобраться, думаю тут собака зарыта, раз по отдельности работает.

ss7
Offline
Зарегистрирован: 30.06.2018

Создал отдельный топик в Hardware, проблема кроется в использовании двух SPI устройств и так просто не решается, этот тред исчерпан и закрыт.

andriano
andriano аватар
Offline
Зарегистрирован: 20.06.2015

ss7 пишет:

...этот тред исчерпан и закрыт.

Ну да, на вопрос темы ответс так и нет, а тред исчерпан.

Нет, для Ардуино в большинстве случаев наиболее разумен вариант, о котором здесь написано - глобальный массив. Но этот вариант совершенно не отвечает на вопрос темы.

А по теме, естественно, указатель. Только этот указатель должен указывать не на стек. Либо указатель передается из вызывающей функции, а вызываемая его только заполняет (оптимально, когда заранее известен размер). Либо вызываемая функция сама запрашивает динамическую память и возвращает указатель на нее (это оптимально, когда размер массива заранее не известен).

ss7
Offline
Зарегистрирован: 30.06.2018

andriano пишет:
Ну да, на вопрос темы ответс так и нет, а тред исчерпан.

Нет, для Ардуино в большинстве случаев наиболее разумен вариант, о котором здесь написано - глобальный массив. Но этот вариант совершенно не отвечает на вопрос темы.

В моём случае, проще и правда оказалось отдать 504 байта под глобавльный массив, т.к. картинок большое количество, они подгружаются в него динамически, а размер буфера экрана заранее известен. Сделал дополнительную функцию, которая проигрывает анимацию (нарезанную из гифки по кадрам), приняв начальный адрес и количество кадров.

В случае использования массива большого размера и без необходимости использовать дополнительную память, можно было бы сохранить данные в статической, указав PROGMEM в переменной. Видел сравнение скорости работы:

Testing SRAM vs PROGMEM speed:
   1) SRAM sprintf time is 10.34 us
   2) PROGMEM sprintf_P() time is 127.81 us
   3) PROGMEM String() time is 152.28 us
Done

Несмотря на более долгое время отклика, оно незначительно, т.к. ~0.1мс совершенно неощутимы. Но в моём случае <20K встроенной статической памяти мало, а 4Mb дополнительной - покрывает запросы на хранение картинок с лихвой.

Вообще, этот проект не несёт для меня какой-то практической пользы огромной - будет одна штуковина в подарок одному человеку, а делаю скорее чтобы разобраться в тонкостях работы ардуино, т.к. линукс и микрокомпьютеры к примеру я использую давно, а ардуино начал использовать не слишком много времени назад. Но цены на девайсы настолько разные, что палить из пушки по мухам - просто глупая расточительность. Пока переделал свой старый джойстик от денди на attiny85 за несколько часов на юсб (обошлось <100р) и сделал небольшой девайс с двустрочным дисплеем на нано.

 

andriano пишет:
А по теме, естественно, указатель. Только этот указатель должен указывать не на стек. Либо указатель передается из вызывающей функции, а вызываемая его только заполняет (оптимально, когда заранее известен размер). Либо вызываемая функция сама запрашивает динамическую память и возвращает указатель на нее (это оптимально, когда размер массива заранее не известен).

Можно пример кода с указателем на стек?

andriano
andriano аватар
Offline
Зарегистрирован: 20.06.2015

Пример кода, как делать не нужно?

Объявите локальную переменную и она будет размещена в стеке.

Кстати, я был излишне категоричен: если локальная переменная объявлена в вызывающей функции, то она, естественно, будет на стеке, но вариант окажется вполне работоспособным.