AVR mega2560 printf() и fgetc() не работают

Я пытаюсь узнать о встроенном программировании, поэтому я купил arduino mega2560. Мне не очень понравилась среда разработки Arduino (потому что она кажется слишком простой и абстрактной, по крайней мере, для меня :)), поэтому я начал программировать ее, используя чистый C и набор инструментов avr (avrdude и подобные). Я пытаюсь написать программу, которая читает ваше имя, а затем мигает светодиодом для каждого символа вашего имени.

Основная схема программы такова:

  1. Поверните контакт светодиода в режим вывода
  2. **printf() "Введите свое имя"
  3. ** Прочитайте имя пользователя, используя fgetc, realloc и т. д. (я не использую scanf(), потому что scanf() с буфером может вызвать переполнение буфера)
  4. Мигать светодиодом для каждого символа в имени пользователя

Проблемы (обозначенные **) заключаются в том, что я не вижу вывод printf, а fgetc запрашивает ввод. Как это исправить?

Спасибо!

Обновлено: вот код:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <avr/io.h>
#include <util/delay.h>

#ifndef STDIN
#define STDIN 0
#endif

int main(void) {
        char *name = (char *)malloc((int)NULL);
        char c;
        int cnt;
        int i;

        cnt = 1;

        DDRB |= (1 << DDB7);

        printf("Enter your name: ");

        while ((c = fgetc(STDIN)) != '\n') {
                name = (char *)realloc(name, cnt);
                strncat(name, &c, 1);

                cnt++;
        }

        for (i = 0; i < strlen(name); ++i) {
                PORTB |= (1 << PB7);
                _delay_ms(1000);
                PORTB &= ~(1 << PB7);
        }

        return 0;
}

Покажите нам, что вы пробовали. В вашем вопросе не хватает деталей и деталей реализации.

Andrejs Cainikovs 10.01.2023 18:14

@AndrejsCainikovs Вы имеете в виду добавление кода? Что еще я должен добавить?

sag0li 10.01.2023 18:17

Шаг 1: char c; --> int c;, чтобы правильно сохранить 257 различных возвращаемых значений fgetc().

chux - Reinstate Monica 10.01.2023 18:22
strncat(name, &c, 1); плохо, так как name не указывает на строку.
chux - Reinstate Monica 10.01.2023 18:23

Программы Arduino обычно используют последовательный порт для ввода/вывода. Можете ли вы скомпилировать этот код без ошибок или предупреждений? У вас есть библиотека, которая реализует printf и fgetc? Динамическое выделение памяти может привести к проблемам во встроенных системах.

Bodo 10.01.2023 18:25

@Bodo Да, у меня нет ошибок. Я включил stdio.h, который реализует printf и fgetc.

sag0li 10.01.2023 18:29

Не имеет отношения к вашему вопросу, но динамическое выделение памяти и возврат из main() не совсем подходят для вашей платформы. Возврат из main в автономной среде определяется реализацией - ваша среда выполнения может бесконечно зацикливаться, сбрасываться или просто запускаться неизвестно куда и демонстрировать неопределенное поведение.

Clifford 11.01.2023 00:49
(char *)malloc((int)NULL) это ерунда. И использование malloc на MCU вообще бессмысленно. Почему мне не следует использовать динамическое выделение памяти во встраиваемых системах?
Lundin 11.01.2023 08:32

Пожалуйста, объясните, что вы ожидаете от своей ардуино (где вы печатаете и как вводите данные) и что вместо этого происходит. Я предполагаю, что у вас нет консольного терминала, подключенного к вашему Arduino, так где же ожидается ввод и где должен быть напечатан ваш вывод?

Luis Colorado 16.01.2023 08:18

Передача нулевого указателя на fgetc() вряд ли даст вам желаемые результаты.

Toby Speight 16.01.2023 18:06
Конечные и Readonly классы в PHP
Конечные и Readonly классы в PHP
В прошлом, когда вы не хотели, чтобы другие классы расширяли определенный класс, вы могли пометить его как final.
От React к React Native: Руководство для начинающих по разработке мобильных приложений с использованием React
От React к React Native: Руководство для начинающих по разработке мобильных приложений с использованием React
Если вы уже умеете работать с React, создание мобильных приложений для iOS и Android - это новое приключение, в котором вы сможете применить свои...
БЭМ: Конвенция об именовании CSS
БЭМ: Конвенция об именовании CSS
Я часто вижу беспорядочный код CSS, особенно если проект большой. Кроме того, я совершал эту ошибку в профессиональных или личных проектах и...
Революционная веб-разработка ServiceNow
Революционная веб-разработка ServiceNow
В быстро развивающемся мире веб-разработки ServiceNow для достижения успеха крайне важно оставаться на вершине последних тенденций и технологий. По...
Как добавить SEO(Search Engine Optimization) в наше веб-приложение и как это работает?
Как добавить SEO(Search Engine Optimization) в наше веб-приложение и как это работает?
Заголовок веб-страницы играет наиболее важную роль в SEO, он помогает поисковой системе понять, о чем ваш сайт.
Конфигурация Jest в angular
Конфигурация Jest в angular
В этой статье я рассказываю обо всех необходимых шагах, которые нужно выполнить при настройке jest в angular.
0
10
70
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

  1. char *name = (char *)malloc((int)NULL); в этом нет никакого смысла. Вы в основном выделили 0 байт для имени. Также не приводить результаты malloc.
  2. На этом микроконтроллере никогда не используйте динамическое распределение типа malloc. Это не персональный компьютер, и вам нужно забыть о многих «больших» компьютерных вещах;
  3. Какой у вас STDIN на такой цели?
  4. Какой у вас стандартный вывод на этом микро?
  5. strncat(name, &c, 1); это странно. И это не сработает, так как ни один из ваших параметров не является строкой C. Вы знаете, что такое последний параметр?
  6. strlen не будет работать, так как вы не передаете строку C.
Мне не очень понравилась IDE arduino (потому что она кажется слишком базовый и абстрактный, по крайней мере для меня :))

Я думаю, что на вашем уровне знаний это правильное направление. Arduino - это библиотеки C++ +, и это не «просто»

Да у меня ошибок нет. Я включил stdio.h, который реализует printf и fgetc

Добавление заголовочного файла не решает проблему. Вам нужно написать несколько низкоуровневых функций, чтобы stdio функции начали работать. stdout и stdin по умолчанию не обмениваются данными через UART. Подробнее: https://www.nongnu.org/avr-libc/user-manual/group__avr__stdio.html#ga4c04da4953607fa5fa4d3908fecde449

«Также не приводить результаты malloc». Это слизь для любителей Arduino, так что на самом деле это C++.

Lundin 11.01.2023 08:36

@Lundin он сказал, что не использует Arduino

0___________ 11.01.2023 09:35

Разве эти пользовательские библиотеки не являются частью библиотек Arduino? Или это просто что-то, поставляемое Atmel/Microchip?

Lundin 11.01.2023 09:38

@Lundin Я считаю, что avr-gcc поддерживается Microchip. Но (поскольку нет OP или файловой системы) они предоставляют только заглушки функций ввода-вывода (такие же, как ветка ARM). Вам нужно подключить его к реальному оборудованию

0___________ 11.01.2023 09:56

Ну 1. вообще не имеет смысла, не значит что это UB или ошибка.... Инициализирует NULL в name, так что хорошо когда доходит до realloc(). 2. Зачем ему реализовывать библиотеку malloc для этой платформы, если он не может ее использовать? Тебе не кажется, что ты слишком остро реагируешь? не должно существовать реализации malloc, разве он не получит ошибку компоновщика во время сборки? Я немного сбит с толку, поскольку я разрабатываю встроенный, и я вижу, что если у вас нет какой-либо доступной функции, вы получаете ошибку связывания.

Luis Colorado 16.01.2023 08:23

Потому что у вас очень ограниченный ресурс, и эта крошечная память будет мгновенно фрагментирована.

0___________ 16.01.2023 10:31
Ответ принят как подходящий

Библиотека stdio для микроконтроллеров, где stdout, stdin вообще не должны существовать, а там, где они есть, они обязательно зависят от аппаратного обеспечения и доступного ввода-вывода и являются дизайнерскими решениями разработчика проекта, а не ответственностью разработчика библиотеки.

Обычно для встроенной библиотеки будет слой переноса или «склеивания», чтобы сопоставить библиотеку с аппаратными зависимостями, и если вы не предоставите необходимые функции, они будут связаны с общими «ничего не делающими» заглушками.

Конкретные переопределения функций-заглушек, которые вам необходимо предоставить, будут зависеть от цепочки инструментов и, в частности, от используемой вами библиотеки C. И средства, с помощью которых вы их «устанавливаете», различаются в зависимости от цепочки инструментов. Некоторые используют «слабые ссылки», которые вы просто предоставляете переопределениям с той же сигнатурой, другие используют структуры с указателями функций на низкоуровневые функции ввода-вывода. Например, если вы используете AVR Libc , вам просто нужно предоставить функции get-char / put-char и использовать их в FILE структуре, назначенной потокам stdin, stdout, как описано в документации.

Например:

#include <stdio.h>

// Output character to stdout device
static int std_putchar(char c, FILE *stream)
{
    ...
    return 0;
}

// Input character from stdin device
static int std_getchar(char c, FILE *stream)
{
    ...
    return rxchar ;
}

// Create stdio stream
static FILE stdio_stream = FDEV_SETUP_STREAM( std_putchar, std_getchar,
                                              _FDEV_SETUP_RW ) ;

int main(void)
{
    stdout = &stdio_stream ;
    stdin = &stdio_stream ;
    stderr = &stdio_stream ;

    printf( "System Start - hello\n") ;

    // Echo input
    for(;;)
    {
        putchar( getchar() ) ;
    }
} 

Вы можете реализовать функции символьного ввода-вывода низкого уровня любым способом, который подходит для вашей системы, использовать UART, клавиатуру, ЖК-дисплей, интерфейс отладчика - что угодно.

Arduino fgetc() и printf() обычно считывают и печатают из последовательного порта, который вы должны инициализировать, чтобы установить скорость передачи данных, четность, стоповые биты и размер символов для работы (и вы, вероятно, не знаете об этом). Обычно для этого требуется запустить эмуляцию терминала последовательного соединения (и настроить его с той же скоростью передачи данных, четностью, стоповыми битами и размером символов), чтобы наблюдать за вводом/выводом Arduino. затем вы загружаете программное обеспечение в Arduino, запускаете эмуляцию терминала и нажимаете кнопку сброса (чтобы снова загрузить Arduino)

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

Я сожалею, что не рассказал немного больше о том, как настроить Arduino (и компьютер, чтобы он делал то, что вы хотите), но на YouTube есть много видеоуроков, которые покажут вам, как это сделать. Есть очень хорошие туториалы от Ben Eater (просто поищите их) и множество проектов (не тривиальных, их много)

ИМХО, вы слишком упростили свою цель и начали с гораздо более сложного проекта, чем вы можете просто проглотить одним выстрелом.

Другие вопросы по теме