Выбор и использование динамического последовательного порта без создания экземпляров всех объектов Serialx
У меня есть проект Arduino, который считывает файл конфигурации с SD, а затем получает данные на последовательный порт, указанный в файле конфигурации.
Поэтому мне нужно создать экземпляр Serial (Serial0), Serial1,..2,3 и т. д. на основе динамических данных.
Я использую mega2560 с 4x HardwareSerial. Serial (также Serial0) — это USB. Серийные номера 1, 2, 3 находятся на определенных аппаратных контактах.
Я начал с примерно такого кода:
uint8_t InitPort(uint8_t port) {
switch (port) {
case 0:
if(Serial.begin(9600) return 1;
break;
case 1:
if(Serial1.begin(9600) return 1;
break;
... etc...
default:
return 0;
}
Однако при этом создаются экземпляры всех объектов Serialx (каждый использует 157 байт данных/bss).
С тех пор я написал оболочку класса, которая использует ссылку на нужный последовательный объект в качестве параметра инициализации, но это просто подталкивает проблему к вызывающему коду.
Есть ли способ динамического создания объекта Serial с переданным номером порта, чтобы в итоге к нужному порту был подключен только один экземпляр?
@BrendanMcL, 👍2
Обсуждение1 ответ
Лучший ответ:
Вы можете создать экземпляр HardwareSerial
, ссылающийся на
порт, зависящий от параметра функции, например
void initPort(uint8_t port) {
switch (port) {
case 0: {
HardwareSerial serial(&UBRR0H, &UBRR0L,
&UCSR0A, &UCSR0B, &UCSR0C, &UDR0);
serial.begin(9600);
break;
}
case 1:
// и т. д...
}
}
Однако, поскольку таким образом можно создать только локальную переменную,
не очень полезно. В идеале вы хотели бы иметь глобальную переменную
для последовательного порта и инициализируйте его в initPort()
, как
HardwareSerial serial;
void initPort(uint8_t port) {
switch (port) {
case 0:
// Вызов вымышленного метода init().
serial.init(&UBRR0H, &UBRR0L,
&UCSR0A, &UCSR0B, &UCSR0C, &UDR0);
break;
case 1:
// и т. д...
}
serial.begin(9600);
}
Увы, это невозможно, так как класс HardwareSerial
не имеет значения по умолчанию.
конструктор. Это означает, что привязка объекта к аппаратному порту может
только после его создания.
Если не считать модификации ядра Arduino (добавление конструктора по умолчанию и
init()
), единственный вариант, который я вижу, это иметь глобальный
быть указателем. Затем в initPort()
объект
создается при инициализации указателя. Я попробовал этот подход в
пример программы ниже. Возникла небольшая техническая сложность
однако: поскольку объектные файлы из ядра определяют Serial
,
Мы надеемся, что Serail1
и т. д. не будут связаны с программой,
Определенные в нем подпрограммы обслуживания прерываний должны быть также определены в
сам скетч. И поскольку ISR не может быть привязан к прерыванию при запуске
время, ISR должны быть определены для всех портов, даже если только
один из них фактически используется.
Вот моя тестовая программа:
#include <HardwareSerial_private.h>
HardwareSerial *serial;
ISR(USART0_RX_vect) { serial->_rx_complete_irq(); }
ISR(USART1_RX_vect, ISR_ALIASOF(USART0_RX_vect));
ISR(USART2_RX_vect, ISR_ALIASOF(USART0_RX_vect));
ISR(USART3_RX_vect, ISR_ALIASOF(USART0_RX_vect));
ISR(USART0_UDRE_vect) { serial->_tx_udr_empty_irq(); }
ISR(USART1_UDRE_vect, ISR_ALIASOF(USART0_UDRE_vect));
ISR(USART2_UDRE_vect, ISR_ALIASOF(USART0_UDRE_vect));
ISR(USART3_UDRE_vect, ISR_ALIASOF(USART0_UDRE_vect));
void initPort(uint8_t port)
{
switch (port) {
case 0:
serial = new HardwareSerial(&UBRR0H, &UBRR0L,
&UCSR0A, &UCSR0B, &UCSR0C, &UDR0);
break;
case 1:
serial = new HardwareSerial(&UBRR1H, &UBRR1L,
&UCSR1A, &UCSR1B, &UCSR1C, &UDR1);
break;
case 2:
serial = new HardwareSerial(&UBRR2H, &UBRR2L,
&UCSR2A, &UCSR2B, &UCSR2C, &UDR2);
break;
case 3:
serial = new HardwareSerial(&UBRR3H, &UBRR3L,
&UCSR3A, &UCSR3B, &UCSR3C, &UDR3);
break;
}
serial->begin(9600);
}
void setup() {
initPort(0);
serial->println("Hello, World!");
}
void loop(){}
Следует отметить, что ISR не занимают столько места во флэш-памяти. На самом деле их всего два, хотя на каждый ссылается четыре. раз в таблице векторов прерываний.
Отлично, что вы поделились своим продвижением к ответу. Большое спасибо. Теперь для следующего углубленного изучения — правильного понимания ISR на arduino и изучения всех переменных U в классе HardwareSerial. Я так понимаю, это какие-то регистры?, @BrendanMcL
@BrendanMcL: Если вы хотите копнуть глубже, вам придется прочитать главу «USART» [техническое описание ATmega2560](http://ww1.microchip.com/downloads/en/DeviceDoc/Atmel-2549-8-bit). -AVR-Microcontroller-ATmega640-1280-1281-2560-2561_datasheet.pdf) и главу «Прерывания» [документации avr-libc](https://www.nongnu.org/avr-libc/user-manual /group__avr__interrupts.html)., @Edgar Bonet
@BrendanMcL: «Переменные U» — это ссылки на регистры аппаратного ввода-вывода. Например, UBRR0H
— это макрос, который расширяется до (*(uint8_t*)0xc5)
, где 0xc5
— это отображаемый в памяти адрес «старшего байта регистра скорости передачи данных USART0»., @Edgar Bonet
@Edgar_Bonet большое спасибо. Понял. Наличие такого большого количества отличных библиотек на платформе arduino — отличный плюс для быстрой работы. Недостатком является то, что он абстрагирует так много деталей, что делает его полным обучением, чтобы делать что-то за пределами спецификации., @BrendanMcL
- Как разделить входящую строку?
- Как вывести несколько переменных в строке?
- В чем разница между Serial.write и Serial.print? И когда они используются?
- Загрузка Arduino Nano дает ошибку: avrdude: stk500_recv(): programmer is not responding
- Программы построения последовательных данных
- Как узнать частоту дискретизации?
- Что такое Serial.begin(9600)?
- Очистить существующий массив при получении новой последовательной команды
Вы говорите об интерфейсах SoftwareSerial или аппаратных интерфейсах?, @chrisl
все они созданы, но оптимизированы, если код их не использует. класс Serial зависит от платы. на некоторых платах Serial — это USB, а Setial1 — это HardwareSerial. но на платах с 328p это что-то вроде
HardwareSerial Serial(&UBRRH, &UBRRL, &UCSRA, &UCSRB, &UCSRC, &UDR);
, @JurajСообщение отредактировано для уточнения mega2560 - аппаратный серийный номер., @BrendanMcL
@Juraj они не оптимизированы компоновщиком в моем приложении (в чем проблема), потому что компоновщик не знает, какой из них будет использоваться во время выполнения - это основано на данных конфигурации пользователя, считанных с SD. Таким образом, он включает в себя все 4 с ассоциированным использованием памяти, что является основной проблемой., @BrendanMcL
все они используются в вашем эскизе в некоторых случаях, поэтому должны быть в окончательной сборке., @Juraj
@TisteAndii, это не так сложно. только экземпляры должны создаваться динамически, как показывает Эдгар в ответе, @Juraj
но что такое 3 раза по 157 байт в 8 кБ SRAM Меги? :-), @Juraj
иногда это действительно важно :-) https://forum.arduino.cc/index.php?topic=622002.msg4215655#msg4215655, @Juraj