Компилятор avr-gcc предлагает макрос F () как хороший способ определить строки в моих инструкциях и затем поместить строки в программную память. Однако строки в конечном итоге имеют тип __FlashStringHelper, и я получу сообщение об ошибке, если попытаюсь передать их функциям, ожидающим «const char *».
Я могу разыграть каждого из них, и код будет функционировать. Например, этот код будет работать:
int16_t LT_printf(const char *format, ...);
LT_printf((const char *)F("TESTING!\r\n"));
Я также могу определить функцию перегрузки, которая ничего не делает, кроме как получает __FlashStringHelper и затем превращает его в (const char *). Это также работает:
int16_t LT_printf(const __FlashStringHelper *format, ...);
LT_printf(F("TESTING!\r\n"));
Второе решение выполняется менее эффективно, чем первое, но по крайней мере у меня больше нет сотен приведений в моем коде.
Есть ли способ устранить приведения внутри каждого вызова функции, но все же не нужна функция перегрузки?
Отредактировано для добавления дополнительных примеров, которые создают (не то, чтобы я действительно делал какие-либо примеры … Мне просто интересен указатель на const __FlashStringHelper):
typedef struct
{
char test_string[20];
} TEST_STRUCT_TYPE;
const TEST_STRUCT_TYPE PROGMEM test_struct = {"STRUCT TESTING!\r\n"};
const uint8_t PROGMEM test_array[] = {'A', 'R', 'R', 'A', 'Y', ' ', 'T', 'E', 'S', 'T', 'I', 'N', 'G', '!', '\r', '\n', NULL};
const char PROGMEM test_string[] = {"TEST TESTING!\r\n"};
void test()
{
LT_printf(test_string); // doesn't need a cast
LT_printf((const char *)F("F TESTING!\r\n")); // these need a cast
LT_printf((const char *)&test_struct);
LT_printf((const char *)test_array);
LT_printf((PGM_P)F("F TESTING!\r\n")); // this is the cleaner cast
LT_printf((PGM_P)&test_struct);
LT_printf((PGM_P)test_array);
}
Это приводит к такому выводу:
TEST TESTING!
F TESTING!
STRUCT TESTING!
ARRAY TESTING!
F TESTING!
STRUCT TESTING!
ARRAY TESTING!
LT_printf(test_string); // doesn't need a cast
LT_printf((const char *)F("F TESTING!\r\n")); // these need a cast
LT_printf((const char *)&test_struct);
Вы не можете просто отбросить другой тип данных и ожидать, что он будет работать. Предполагая, что test_string
работает, то он ожидает печатать из ОЗУ, а не из PROGMEM, который находится в отдельном адресном пространстве.
Простое решение в вашем случае состоит в том, чтобы получить класс, который LT_printf
в (что бы это ни было) из Print
а потом print
, println
и т.д. будет просто работать.
Например, у меня есть библиотека, которая печатает на I2C LCD. Я извлекаю это из Print
как это:
class I2C_graphical_LCD_display : public Print
Теперь все, что нужно сделать вашему классу, это реализовать write
в вашем классе (т. е. писать один байт) и Print
класс заботится обо всем остальном: и запись из RAM, и через PROGMEM через F()
макро.
Мой ответ не касается как получить printf
работать, хотя вы могли бы посмотреть на этот.
Вам может быть лучше, глядя на Streaming библиотека, которая позволяет вам выводить поток в стиле C ++, а не использовать вывод в стиле printf.
Вот простой способ реализации printf (но только из оперативной памяти), добавив пару вспомогательных функций:
int getChar (FILE *fp)
{
while (!(Serial.available()));
return (Serial.read());
} // end of getChar
int putChar (char c, FILE *fp)
{
Serial.write (c);
return c;
} // end of putChar
void setup ()
{
Serial.begin(115200);
fdevopen (putChar, getChar);
} // end of setup
void loop ()
{
int temp = 30;
printf ("The temperature is %d degrees C.\n", temp);
delay (100);
} // end of loop
fdevopen
говорит стандартной библиотеке ввода-вывода, как получать и размещать символы. В этом конкретном случае я использую Serial
, но это может быть Serial1
или SoftwareSerial и др.
Если вы хотите использовать константы PROGMEM (т.е. F()
) тогда я думаю, что проще всего использовать потоковую библиотеку. например.
#include <Streaming.h>
void setup ()
{
Serial.begin (115200);
} // end of setup
void loop ()
{
int temp = 30;
Serial << F("The temperature is ") << temp << F(" degrees C") << endl;
delay (100);
} // end of loop
Это еще более компактно и экономит оперативную память, используя F()
макро. Это тоже довольно читабельно.
Других решений пока нет …