Как передать статический константный (программный) массив в функцию
Я внедряю чип синтезатора голоса. Чтобы построить фразу, я создаю список фонем, например:
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.
То, что у меня есть, компилируется, но, похоже, берет только одну фонему и повторяет ее снова и снова. Остальные теряются.
4 ответа
Лучший ответ:
Проблема в том, что когда вы вызываете 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));
}
}
У меня есть несколько подробных примеров доступа к 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 () { }
Как уже отмечалось, ваша проблема заключается в sizeof
, который вычисляется во время компиляции, а не при запуске кода.
Вам нужно передать размер массива в функцию. Это можно сделать разными способами, в зависимости от того, что еще вы делаете.
В c
наиболее распространенным методом является использование строки с завершающим нулем, но я подозреваю, что это может не сработать.
Вы можете сохранить размер каждой фразы в отдельном массиве и передать его как отдельный параметр или в стиле Pascal как байт счетчика в первом элементе фразы.
Если вы не против немного поработать с 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);
И шаблон выведет размер массива и вызовет нешаблонную функцию.
- Инициализировать и читайть из массива указателей PROGMEM на массивы PROGMEM.
- Можно ли хранить данные в программном пространстве вместо динамической памяти?
- Большие массивы приводят к сбою Arduino
- Создание массива с длиной, полученной из библиотеки
- Отправить массив 2d PROGMEM через SoftwareSerial
- Возможно ли иметь массив массивов int?
- Почему выходит ошибка: invalid conversion from 'const char*' to 'char' [-fpermissive]??
- Замена нескольких выводов pinMode() и digitalWrite() на массив
Лучше использовать
pgm_read_ptr
, а неpgm_read_word
, поскольку это лучше выражает намерение и не полагается на предположение, что указатель имеет тот же размер, что и слово. (Обычно он имеет тот же размер, что и слово, но это может быть верно не для всех плат.) Кроме того, вместо выполнения ненужного копирования вы можете воспользоваться типом__FlashStringHelper
Arduino., @PharapЗвучит достаточно разумно. Оригинал (который создает копию) может быть более общим (строка может понадобиться в контексте, где тип
__FlashStringHelper
не применяется), но я принимаю ваши предложенные изменения., @Nick Gammon