AVR (Arduino Uno) Serial.print и Serial.println печатают только 1 или 2 символа

c avr

Сначала я использовал Arduino IDE, но теперь я использую PlatformIO, который является потрясающим. Но у меня большие проблемы с Serial.print() и Serial.println(). Чтобы показать эту ошибку, у меня есть следующий пример кода:

#include <avr/io.h>
#include <util/delay.h>
#include "Arduino.h"
#include "main.h"

int main(int argc, char const *argv[]) {
  Serial.begin(9600);

  section5ex34();

  return 0;
}

void section5ex34() {
  // Инициализация
  DDRC = 0b00000100; // Порт C2 в качестве выходного, а остальные (включая порт C3) в качестве входных данных. (Порт C2 используется для светодиода, который показывает, когда считывается бит en)

  uint8_t b = 0b00000000;

  // здесь код считывает биты из PORTC3
  for (int i = 0; i < 8; i++) {
    _delay_ms(1000);
    PORTC |= _BV(PORTC2); // Сделать порт C2 высоким (указывает, что будет считан бит)

    if ((PINC & (0b1 << 3)) != 0) {
      b |= (1 << i);
    }

    _delay_ms(100); // Подождите, пока пользователь подготовит следующий бит (потому что теперь это делается вручную)
    PORTC &= ~_BV(PORTC2); // Затем сделайте порт C2 низким
  }

  Serial.println(b, HEX);
}

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

Когда я добавляю Serial.println("test"); после печати, это не печатается.

Или когда я меняю Serial.println(b, HEX); на Serial.println(b, BIN); он печатает 11.

Когда я заменяю эти 2 отпечатка на Serial.println("Hi test test."); он печатает Hi, поэтому кажется, что он может печатать только 2 символа.

Когда я добавляю Serial.println("Привет, мир. здравствуйте. привет"); четыре раза после Serial.begin(9600);печатается Привет, мир. h и ничего больше. Итак, здесь кажется, что он может печатать более 2 символов, но не все. Если я подожду несколько секунд, он добавит к этому элло, так что он скажет "Привет, мир". здравствуйте.

В исходном коде, когда я заменяю оператор ifна

if ((PINC & (0b1 << 3)) != 0) {
  b |= (1 << i);
  Serial.println("1");
} else {
  Serial.println("0");
}

и когда вход подключен к 5 В (так высоко), он печатает 1 один раз и больше ничего, даже шестнадцатеричное значение, когда чтение завершено.

Эта проблема сводит меня с ума.

У меня есть Arduino Uno, и я использую скорость передачи данных 9600, 8, N, 1.

Я надеюсь, что кто-нибудь сможет мне помочь с этим.

, 👍2


2 ответа


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

5

Здесь у вас есть две проблемы:

Во-первых, вы используете базовую библиотеку Arduino и не смогли ее инициализировать. Это делается автоматически, если вы полагаетесь на поставляемую ядром функцию main(), то есть придерживаетесь стиля кодирования setup()/loop() . Если вы пишете свой собственный main(), вам следует начать с вызова init() для инициализации ядра.

Обновление: в данном конкретном случае вы могли бы уйти, просто включив прерывания с помощью sei(). Но я считаю хорошей практикой инициализировать ядро, если вы когда-нибудь собираетесь его использовать.

Вторая проблема заключается в резком прекращении основного: Serial.print() и println() на самом деле не печатают на последовательный порт. Вместо этого они помещают символы для печати в выходной буфер и позволяют прерыванию позаботиться о фактической отправке их через UART.

Ваша программа заканчивается словами

return 0;

Это остановит его. Это остановит всю программу, включая задачи, управляемые прерываниями. Таким образом, вы увидите все символы, которые попали в UART, и ничего больше.

Если вы вместо этого завершите программу с помощью бесконечного цикла и

for (;;) ;

это должно работать так, как ожидалось.

,

Хм. Я очень надеялся, когда увидел это. В этом был какой-то смысл. Но , к сожалению , это ничего не меняет .., @Jeroen

@JeroenJK: Действительно, я забыл рассказать вам об инициализации ядра. См. Исправленный ответ., @Edgar Bonet

Да, sei(); работает вместе с бесконечным циклом. Спасибо!, @Jeroen


1

Arduino main() не так прост, как вы думаете. Если вы используете стандартные функции Arduino loop () и setup (), это может работать намного лучше.

Но если вам не разрешено использовать библиотеки Arduino, вы можете использовать это:

src/main.cpp

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

void section5ex34();
int uart_putchar(char c, FILE *stream);
void uart_init (unsigned long int baud);

int main(void) {
  uart_init(9600U);

  while(1) section5ex34();
  return 0;
}

void section5ex34() {
  // Инициализация
  DDRC = 0b00000100; // Порт C2 в качестве выходного, а остальные (включая порт C3) в качестве входных данных. (Порт C2 используется для светодиода, который показывает, когда считывается бит en)
  PORTC = 0b11111011; 

  uint8_t b = 0b00000000;

  // здесь код считывает биты из PORTC3
  for (int i = 0; i < 8; i++) {
    _delay_ms(100);
    PORTC |= _BV(PORTC2); // Сделать порт C2 высоким (указывает, что будет считан бит)

    if ((PINC & (0b1 << 3)) != 0) {
      b |= (1 << i);
    }

    _delay_ms(100); // Подождите, пока пользователь подготовит следующий бит (потому что теперь это делается вручную)
    PORTC &= ~_BV(PORTC2); // Затем сделайте порт C2 низким
  }
  printf("0x%02X and something loooooooooooong\n",b);
}

int uart_putchar(char c, FILE *stream) {
  if (c == '\n') uart_putchar('\r', stream);
  loop_until_bit_is_set(UCSR0A, UDRE0);
  UDR0 = c;
  return 0;
}

// функция для инициализации UART
void uart_init (unsigned long int baud) {
    unsigned int baudrate = F_CPU/(baud*16UL)-1;
    UBRR0H = (baudrate>>8);             // сдвинуть регистр вправо на 8 бит
    UBRR0L = baudrate;                  // установить скорость передачи данных в бодах
    UCSR0B|= (1<<TXEN0)|(1<<RXEN0);     // включить приемник и передатчик
    UCSR0C|= (1<<UCSZ00)|(1<<UCSZ01);   // 8-битный формат данных

    //// со статической переменной ФАЙЛА:
    // статический ФАЙЛ uart0fd;
    // fdev_setup_stream((&uart0fd), uart_putchar, NULL, _FDEV_SETUP_WRITE);
    // стандартный вывод = &uart0fd;

    //// или непосредственно с помощью динамически распределяемой ФАЙЛОВОЙ структуры:
    stdout = fdevopen(uart_putchar,NULL); // использует malloc/ calloc, это может быть неудобно
}
,

Ну, дело в том, что это упражнение для школы. Я отправил его, потому что отображается правильное шестнадцатеричное значение, так что на данный момент этого достаточно, но нам больше не разрешается использовать цикл и настройка. Так что, если есть другой способ заставить это работать, это было бы неплохо., @Jeroen

@JeroenJK: Вам не разрешено писать setup () и loop (), и все же вы можете использовать Serial? Для меня это имеет мало смысла: если _any_ функциональность Arduino core в порядке, то setup () и loop () являются наиболее стандартным способом его использования. Все остальное, включая мой ответ, - это просто халтура., @Edgar Bonet

@JeroenJK Ну, если я использую initVariant (), сборка завершается ошибкой при нескольких определениях main. Так что вам придется пропустить весь arduino целиком.h и использовать старые школьные регистры и так далее. Или [printf](http://www.nongnu.org/avr-libc/user-manual/group__avr__stdio.html ), но это не так просто..., @KIIV

@EdgarBonet Здесь вы совершенно правы. Я думаю, что на более позднем занятии они объяснят нам USART. Но мы можем использовать другие Arduino.он работает на протяжении всего курса, что странно, да. Сначала я использовал crosspack, который не мог включать Arduino.легко, но этого было недостаточно.., @Jeroen

@JeroenJK смотрите обновленный код, ему даже ничего не нужно от arduino, @KIIV

Ты потрясающая! Я обязательно постараюсь этим воспользоваться! Выглядит хорошо и даже не очень сложно., @Jeroen

@JeroenJK Единственным осложнением был C ++. Я должен использовать файл .c для FILE mystdout = FDEV_SETUP_STREAM(uart_putchar, NULL, _FDEV_SETUP_WRITE);потому что он просто не может быть скомпилирован на C ++., @KIIV

@KIIV: fdev_setup_stream() (нижний регистр) менее удобен, но он должен компилироваться на C ++., @Edgar Bonet

@EdgarBonet Вы правы, и это выглядит еще лучше с fdevopen., @KIIV