Как передать статический константный (программный) массив в функцию

Я внедряю чип синтезатора голоса. Чтобы построить фразу, я создаю список фонем, например:

static const uint8_t PROGMEM heybuddy[] = {
  pPA5, pHH1, pEY, pPA5,
  pBB2, pAX, pDD2, pIY, pPA5,pPA5,pPA5,
};

И чтобы воспроизвести их, я делаю следующее:

 for (size_t i=0; i<sizeof(heybuddy); i++){
    say(pgm_read_byte(heybuddy+i));
 }

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

//что я пробовал:
void sayPhrase(const uint8_t *phrase){
  int open = 0;
  for (size_t i=0; i<sizeof(*phrase); i++){

      if(pgm_read_byte(*phrase) != 4 && open == 0){
          Serial.println("mouth open");
          open = 1;
      } else if(pgm_read_byte(*phrase) == 4 && open == 1){
         Serial.println("mouth closed");
         open = 0;
      }
      say(pgm_read_byte(*phrase+i));
    } 
}

Но когда дело доходит до передачи статической константы, я теряюсь. Я пробовал передавать ее как ссылку с помощью *, но ничего из того, что я делаю, не работает — я просто не знаю, как правильно это сделать в C.

То, что у меня есть, компилируется, но, похоже, берет только одну фонему и повторяет ее снова и снова. Остальные теряются.

, 👍2


4 ответа


Лучший ответ:

1

Проблема в том, что когда вы вызываете sizeof(*phrase), возвращается 1, размер элемента массива uint8_t, результат разыменования вашего указателя. С другой стороны, если вы вызываете sizeof(phrase) внутри функции, вы получаете размер самого указателя, а не размер массива, как можно было бы ожидать. Когда вы передаете массив в функцию через указатель, функция не имеет понятия, передали ли вы ей начало массива или просто указатель на некоторую переменную uint8_t, поэтому она не может знать, каков размер; контекст был потерян.

Вместо этого следует вызвать sizeof(phrase) в той же области действия, где был объявлен и инициализирован массив, и передать результат функции, которая затем может использовать это значение в цикле for.

static const uint8_t PROGMEM heybuddy[] = {
  pPA5, pHH1, pEY, pPA5,
  pBB2, pAX, pDD2, pIY, pPA5,pPA5,pPA5,
};

size_t SIZE = sizeof(heybuddy);  // это нормально, так как каждый элемент = 1 байт

sayPhrase(phrase, SIZE);

Определение функции должно быть:

void sayPhrase(const uint8_t *phrase, size_t size){
  int open = 0;
  for (size_t i=0; i < size; i++){

      if(pgm_read_byte(phrase) != 4 && open == 0){
          Serial.println("mouth open");
          open = 1;
      } else if(pgm_read_byte(phrase) == 4 && open == 1){
         Serial.println("mouth closed");
         open = 0;
      }
      say(pgm_read_byte(phrase+i));
    } 
}
,

1

У меня есть несколько подробных примеров доступа к PROGMEM здесь.

Я адаптировал один из примеров, чтобы показать, как вы можете сделать что-то похожее на то, что вам нужно:

const int NUMBER_OF_ELEMENTS = 10;

const char Message0000 [] PROGMEM = "Twas bryllyg, and ye slythy toves"; 
const char Message0001 [] PROGMEM = "Did gyre and gymble"; 
const char Message0002 [] PROGMEM = "in ye wabe:"; 
const char Message0003 [] PROGMEM = "All mimsy were ye borogoves; And ye mome raths outgrabe."; 
const char Message0004 [] PROGMEM = "\"Beware the Jabberwock, my son! \n The jaws that bite, the claws that catch!"; 
const char Message0005 [] PROGMEM = "Beware the Jubjub bird, and shun\n The frumious Bandersnatch!\""; 
const char Message0006 [] PROGMEM = "He took his "; 
const char Message0007 [] PROGMEM = "vorpal sword in hand:"; 
const char Message0008 [] PROGMEM = "Long time the manxome foe he sought - "; 
const char Message0009 [] PROGMEM = "So rested he by the Tumtum tree, \n And stood awhile in thought."; 

const char * const messages[NUMBER_OF_ELEMENTS] PROGMEM = 
   { 
   Message0000, 
   Message0001, 
   Message0002, 
   Message0003, 
   Message0004, 
   Message0005, 
   Message0006, 
   Message0007, 
   Message0008, 
   Message0009, 
   };

void sayPhrase (const char * const * message)
  {
  const char * ptr = reinterpret_cast<const char *>(pgm_read_ptr (message));  // pointer to message
  Serial.println(reinterpret_cast<const __FlashStringHelper *>(ptr));    // and print it
  } // end of sayPhrase

void setup ()
  {
  Serial.begin (115200);
  Serial.println ();

  for (int i = 0; i < NUMBER_OF_ELEMENTS; i++)
    sayPhrase (&messages [i]);

  }  // end of setup

void loop () { } 
,

Лучше использовать pgm_read_ptr, а не pgm_read_word, поскольку это лучше выражает намерение и не полагается на предположение, что указатель имеет тот же размер, что и слово. (Обычно он имеет тот же размер, что и слово, но это может быть верно не для всех плат.) Кроме того, вместо выполнения ненужного копирования вы можете воспользоваться типом __FlashStringHelper Arduino., @Pharap

Звучит достаточно разумно. Оригинал (который создает копию) может быть более общим (строка может понадобиться в контексте, где тип __FlashStringHelper не применяется), но я принимаю ваши предложенные изменения., @Nick Gammon


0

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

Вам нужно передать размер массива в функцию. Это можно сделать разными способами, в зависимости от того, что еще вы делаете.

В c наиболее распространенным методом является использование строки с завершающим нулем, но я подозреваю, что это может не сработать.

Вы можете сохранить размер каждой фразы в отдельном массиве и передать его как отдельный параметр или в стиле Pascal как байт счетчика в первом элементе фразы.

,

1

Если вы не против немного поработать с C++:

void sayPhrase(const uint8_t * phrase, size_t size)
{
    bool open = false;
    for (size_t i = 0; i < size; ++i)
    {
        if(pgm_read_byte(phrase) != 4 && !open)
        {
            Serial.println("mouth open");
            open = true;
        }
        else if(pgm_read_byte(phrase) == 4 && open)
        {
            Serial.println("mouth closed");
            open = false;
        }
        say(pgm_read_byte(&phrase[i]));
    } 
}

template< size_t size >
void sayPhrase(const uint8_t (&phrase)[size])
{
    sayPhrase(phrase, size);
}

Который затем можно вызвать непосредственно в массиве:

sayPhrase(heybuddy);

И шаблон выведет размер массива и вызовет нешаблонную функцию.

,