AVR (Arduino Uno) Serial.print и Serial.println печатают только 1 или 2 символа
Сначала я использовал 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.
Я надеюсь, что кто-нибудь сможет мне помочь с этим.
@Jeroen, 👍2
2 ответа
Лучший ответ:
Здесь у вас есть две проблемы:
Во-первых, вы используете базовую библиотеку Arduino и не смогли
ее инициализировать. Это делается автоматически, если вы полагаетесь на
поставляемую ядром функцию main()
, то есть придерживаетесь стиля кодирования setup()
/loop()
. Если вы пишете свой собственный main()
, вам следует начать с вызова
init()
для инициализации ядра.
Обновление: в данном конкретном случае вы могли бы уйти, просто включив
прерывания с помощью sei()
. Но я считаю хорошей практикой инициализировать
ядро, если вы когда-нибудь собираетесь его использовать.
Вторая проблема заключается в резком прекращении основного:
Serial.print()
и println()
на самом деле не печатают на последовательный порт.
Вместо этого они помещают символы для печати в выходной буфер и
позволяют прерыванию позаботиться о фактической отправке их через UART.
Ваша программа заканчивается словами
return 0;
Это остановит его. Это остановит всю программу, включая задачи, управляемые прерываниями. Таким образом, вы увидите все символы, которые попали в UART, и ничего больше.
Если вы вместо этого завершите программу с помощью бесконечного цикла и
for (;;) ;
это должно работать так, как ожидалось.
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
- Прерывание переполнения таймера AVR не работает
- Почему я получаю только первый байт адреса? (Протокол I2C)
- устаревшее преобразование из строковой константы в 'char*'
- Как запрограммировать ардуино на чистом C/C++?
- Количество элементов в массиве char
- В чем разница/связь между Arduino и AVR?
- Регистры ввода-вывода SAM3X8E (Arduino Due)
- как быстро loop() работает в Arduino
Хм. Я очень надеялся, когда увидел это. В этом был какой-то смысл. Но , к сожалению , это ничего не меняет .., @Jeroen
@JeroenJK: Действительно, я забыл рассказать вам об инициализации ядра. См. Исправленный ответ., @Edgar Bonet
Да,
sei();
работает вместе с бесконечным циклом. Спасибо!, @Jeroen