Comments 28
Я бы отдельно занялся бы созданием форматированной строки, например старый добрый sprintf использовал, или другие с++ аналоги (std::stringstream).
А потом бы отдельно отправлял строчку в сеть.
По отдельности эти задачи выглядят в разы проще.
Честно говоря не совсем понятно, зачем вообще нужно совмещать форматирование строки и отправку её в сеть.
У меня не стояло задачи отправки строки в сеть. Возможность перепаковывать — просто дополнительный презент.
Я бы отдельно занялся бы созданием форматированной строки, например старый добрый sprintf использовал, или другие с++ аналоги (std::stringstream).
Согласен. Но уже стакнулся, что некоторые библиотеки (особенно бесит, когда данные производителем в бинарном виде), используют printf для логирования данных.
Как правило в программах узкие места совершенно в других местах
Что бы не выделять отдельный буфер для отформатированной строки.
void myPrintf() {
char buf[размер]; // вот он здесь выделяется в стеке и потом освобождается.
Выделять же буфер размером в 2к наверное это перебор. Здесь требуется разумный подход к буферизации.
В принципе можно использользовать vsnprintf для написания своей myPrintf для вот такого использования:
Serial << "лала=" << myPrintf("%04d",myVar) << ", блабла" << endl;
Спасибо за статью!
Интересно, а каков истинно-верный способ перенаправления printf?
Скажем, в Кейле очень часто используется переопределение fputc
, а не _write
.
Интересно, а каков истинно-верный способ перенаправления printf?
Думаю, тут все на вкус… Keil, на сколько я помню (могу ошибаться) использует свои библиотеки и свой компилятор (который отличается значительно от gcc). Если не прав, то прошу поправить. Так что тут сложно сказать. Давно не работал с keil.
Компилятор и библиотека, конечно, свои, но стандартная библиотека на то и стандартная. Должны же, наверное, быть заранее предусмотренные места для переопределения.
1) если это tutorial, то чему он меня учит?
2) есть введение, должно быть заключение
Если не нравятся готовые printf.c, xprint и прочие, то в чем сложность написать его самому, если не полный, то лайтовый. Или доработать указанные выше? Плавающая точка, в принципе, несложно делается.
1) если это tutorial, то чему он меня учит?
Как запустить то, что работает под «большой ОС» из коробки на МК без лишних затрат и проблем с надежностью.
есть введение, должно быть заключение
Что-то как-то не подумал об этом. Но оно было бы бессмысленно — уровня «я привел самые частые грабли и краткие методы их решения».
Если не нравятся готовые printf.c, xprint и прочие, то в чем сложность написать его самому, если не полный, то лайтовый. Или доработать указанные выше? Плавающая точка, в принципе, несложно делается.
Сами функции всем устраивают. Вообще не люблю велосипеды по возможности писать. Не так давно писал статью о том, как можно запихать как можно больше стандартных библиотек в МК чтобы не писать своего, пусть и в ущерб памяти. Сейчас отрабатываю вариант комфортной отладки без лишней перепрошивки, но удобнее чем в той статье. Опишу, как обнаружу основные изъяны (если таковые будут).
Например, вот для вывода на дисплей LCD в заданных координатах:
#include <stdarg.h>
#define PRINTF_BUF 21
void printAt(int c, int r, const char *s, ...) {
char buf[PRINTF_BUF];
va_list ap;
va_start(ap, s);
lcd.setCursor(c, r); // Для аурта не нужно
vsnprintf(buf, sizeof(buf), s, ap);
lcd.print(buf); // Вот здесь Serial.print(buf) можно поставить.
va_end(ap);
}
Соответственно, можно сделать SerPrintf(const char *s, ...), которая будет выводить в уарт и не переопределять вообще ничего.
Дано: stm32f103 + freertos (ide: atollic true studio);
Задача: выводить раз в 10 секунд в UART строку сгенерированную sprintf с float значением. Функция как таск freertos; размер таска стека было от 128 до 1024 (-u_printf_float в линкере прописан);
проблема: либо сразу либо после 5-10 выполнений hard fault МК
Гугление навело на этот пост, но конкретно «как решить» тут нет. Понимаю что проблема со стеком.
Так, как решить?
Убедитесь, что:
- Стека задачи достаточно (тут я так понял вы используете snprintf. Пишете в буфер и потом буфер отсылаете. Ради интереса можно задать буфер сильно больше нужного.
- В прерываниях не происходит переполнения стека. Как стека задачи, в которой произошло прерывание, так и соседних задач. Может у вас есть задача, которая сбрасывает wdt, а в ней стек на 128 элементов. Появляется прерывание. Пишет 4 килобайна, вы возвращаетесь в задачу wdt, планировщик меняет задачу на другую и тут все падает.
- У вас достаточно места в «куче». Имею ввиду не freertos кучу, а та, что «не занятое место» между буферной зоной стека и bss областью. Ее пользует функция _sbrk. Она считает, что место от конца bss области до начала зарезервированного стека (физический конец оперативной памяти) принадлежит ей (функции _sbrk). Тут надо оставить хотя бы 1 кб. Реально используется около 500 байт, когда вы устанавливаете например единый буфер.
- Можно явно задать буфер, с которым будет работать printf/scanf. setvbuf функцией (помните, что когда вы их вызовите, случится еще дерганье sbrk функции, которая сохъст 500 байт в «куче» (та что не принадлежит freertos). Кстати. Устанавливать буферы надо до запуска планировщика.
Если это соблюдено, то можно попробовать:
Есть ли возможность добавить в текст некоторый ликбез по синтаксису и семантике языка *.ld файла?
Что значат эти точки, звёздочки, :, +=, функции AT() KEEP() ALIGN() > ,{,} и т.п. ?
Как отлаживать *.ld файлы? Как проверять, что *.ld скрипт правильно отработал?

На RISC-V snprintf() вместо float чисел всегда печатает "inf".
Вот такой тест не проходит.
bool test_snprintf_f(void) {
LOG_INFO(TEST, "%s()", __FUNCTION__);
bool res = true;
float val = 123.45;
strcpy(test_text, "");
sprintf(test_text, "val=%6.2f", val);
LOG_INFO(TEST, "test_text [%s]", test_text);
ASSERT_STREQ("val=123.45", test_text);
val = 543.21;
strcpy(test_text, "");
snprintf(test_text, sizeof(test_text), "val=%6.2f", val);
LOG_INFO(TEST, "test_text [%s]", test_text);
ASSERT_STREQ("val=543.21", test_text);
return res;
}
Какие ключи компилятору надо подать, чтобы libc подключила обработчик float чисел?
добавил
LDFLAGS += --specs=nano.specs
LDFLAGS += --specs=nosys.specs
LDFLAGS += -Wl,-u,vfprintf
LDFLAGS += -Wl,-u,_printf_float
LDFLAGS += -Wl,-u,_scanf_float
и не помогло
Программа зависает где-то в недрах printf
У Вас решение дано только для ARM Cortex-M. Это замечательно. Однако как быть с этой же ошибкой на RISC-V (RV32IMC)?
Самые частые грабли при использовании printf в программах под микроконтроллеры