Конфликт SoftwareSerial и avr/interrupt.h

pryanic
Offline
Зарегистрирован: 04.09.2019

Здравствуйте, собственно сабж. Гуглить пытался, нашел даже похожую тему на этом форуме. Но разобраться пока не смог. 

Собственно SoftwareSerial включен в библиотеку <ModbusRtu.h>. С помощью avr/interrupt.h реализую прерывания PCINT1 и PCINT2. Как только добавляю в скетч функцию обработчика прерываний

ISR(PCINT1_vect){
  
}

ISR(PCINT2_vect){
 
}

При компиляции вываливается ошибка:

libraries\SoftwareSerial\SoftwareSerial.cpp.o (symbol from plugin): In function `SoftwareSerial::read()':

(.text+0x0): multiple definition of `__vector_4'

sketch\controller_01.ino.cpp.o (symbol from plugin):(.text+0x0): first defined here

libraries\SoftwareSerial\SoftwareSerial.cpp.o (symbol from plugin): In function `SoftwareSerial::read()':

(.text+0x0): multiple definition of `__vector_5'

sketch\controller_01.ino.cpp.o (symbol from plugin):(.text+0x0): first defined here

collect2.exe: error: ld returned 1 exit status

exit status 1
Ошибка компиляции для платы Arduino Nano.

 

Открывал исходники обоих библиотек ни __vector_4, ни __vector_5 там нет. Подскажите куда копать. 

 
pryanic
Offline
Зарегистрирован: 04.09.2019

Потупил еще файл SoftwareSerial.cpp

Там блин есть ссылки на PCINT:

digitalPinToPCICR(_receivePin) |= _BV(digitalPinToPCICRbit(_receivePin));

Похоже не получится совместно использовать эти прерывания с SoftwareSerial(((

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Если с умом, то получится.

ua6em
ua6em аватар
Offline
Зарегистрирован: 17.08.2016

ЕвгенийП пишет:

Если с умом, то получится.

не, не все здесь "Пётр Петрович Гарин", с его мозгом )))

pryanic
Offline
Зарегистрирован: 04.09.2019

Да уж, прогялдел обработчики прерываний в softwareserial.cpp. Все векторы обозначены. Ума не приложу как их второй раз заюзать.

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Два раза нельзя, надо эти модифицировать

ua6em
ua6em аватар
Offline
Зарегистрирован: 17.08.2016

ЕвгенийП пишет:

Два раза нельзя, надо эти модифицировать

Не подскажете, что именно, у меня такая же бодяга с CustomSofwareSerial, а хочется освободить любое из прерываний для энкодера

Здесь, или не только здесь?
 

//#if defined(PCINT0_vect)
//ISR(PCINT0_vect)
//{
 // CustomSoftwareSerial::handle_interrupt();
//}
//#endif

#if defined(PCINT1_vect)
ISR(PCINT1_vect)
{
  CustomSoftwareSerial::handle_interrupt();
}
#endif

#if defined(PCINT2_vect)
ISR(PCINT2_vect)
{
  CustomSoftwareSerial::handle_interrupt();
}
#endif

#if defined(PCINT3_vect)
ISR(PCINT3_vect)
{
  CustomSoftwareSerial::handle_interrupt();
}
#endif

Начинает компилироваться по крайней мере )))

Проверено! Работает!

wdrakula
wdrakula аватар
Offline
Зарегистрирован: 15.03.2016

ua6em пишет:

ЕвгенийП пишет:

Два раза нельзя, надо эти модифицировать

Не подскажете, что именно, у меня такая же бодяга с CustomSofwareSerial, а хочется освободить любое из прерываний для энкодера

Здесь, или не только здесь?
 

//#if defined(PCINT0_vect)
//ISR(PCINT0_vect)
//{
 // CustomSoftwareSerial::handle_interrupt();
//}
//#endif

#if defined(PCINT1_vect)
ISR(PCINT1_vect)
{
  CustomSoftwareSerial::handle_interrupt();
}
#endif

#if defined(PCINT2_vect)
ISR(PCINT2_vect)
{
  CustomSoftwareSerial::handle_interrupt();
}
#endif

#if defined(PCINT3_vect)
ISR(PCINT3_vect)
{
  CustomSoftwareSerial::handle_interrupt();
}
#endif

Начинает компилироваться по крайней мере )))

Проверено! Работает!

Заме так? Это грубо. Просто добавь свой хендлер к чужому.

#if defined(PCINT0_vect)
ISR(PCINT0_vect)
{
 CustomSoftwareSerial::handle_interrupt();
 UA6EM_PCINT0_handler();
}
#endif

Ведь если софт-сериал не "листен", то его хендлер нифига не работает - в него просто вход и тут же выход.

Лучше модифицируй чужой код не ломая его. Хотя дело твоё, конечно.

ua6em
ua6em аватар
Offline
Зарегистрирован: 17.08.2016

wdrakula пишет:

ua6em пишет:

ЕвгенийП пишет:

Два раза нельзя, надо эти модифицировать

Не подскажете, что именно, у меня такая же бодяга с CustomSofwareSerial, а хочется освободить любое из прерываний для энкодера

Здесь, или не только здесь?
 

//#if defined(PCINT0_vect)
//ISR(PCINT0_vect)
//{
 // CustomSoftwareSerial::handle_interrupt();
//}
//#endif

#if defined(PCINT1_vect)
ISR(PCINT1_vect)
{
  CustomSoftwareSerial::handle_interrupt();
}
#endif

#if defined(PCINT2_vect)
ISR(PCINT2_vect)
{
  CustomSoftwareSerial::handle_interrupt();
}
#endif

#if defined(PCINT3_vect)
ISR(PCINT3_vect)
{
  CustomSoftwareSerial::handle_interrupt();
}
#endif

Начинает компилироваться по крайней мере )))

Проверено! Работает!

Заме так? Это грубо. Просто добавь свой хендлер к чужому.

#if defined(PCINT0_vect)
ISR(PCINT0_vect)
{
 CustomSoftwareSerial::handle_interrupt();
 UA6EM_PCINT0_handler();
}
#endif

Ведь если софт-сериал не "листен", то его хендлер нифига не работает - в него просто вход и тут же выход.

Лучше модифицируй чужой код не ломая его. Хотя дело твоё, конечно.

благодарю конечно, но это выше моего понимания процессов происходящего... сейчас в теле программы вызываю так:
ISR(PCINT0_vect) {
  encoder.tick(); //
}

encoder.tick() это объект библиотеки RotaryEncoder, не понимаю, каким образом это скомпилируется

PS пишет - не задекларировано, если задекларировать, то и реализовывать надо внутри библиотеки либо как-то указывать, что функция внешняя...одни непонятки...

andycat
andycat аватар
Offline
Зарегистрирован: 07.09.2017

а перенести свои нужные пины на другой PCINT и нужный закомментировать в библиотеке, я так делал

#if defined(PCINT1_vect)
ISR(PCINT1_vect, ISR_ALIASOF(PCINT0_vect));
#endif

#if defined(PCINT2_vect)
ISR(PCINT2_vect, ISR_ALIASOF(PCINT0_vect));
#endif

#if defined(PCINT3_vect)
ISR(PCINT3_vect, ISR_ALIASOF(PCINT0_vect));
#endif

 

ua6em
ua6em аватар
Offline
Зарегистрирован: 17.08.2016

я так и сделал, высвободил PCINТ0, но Дракула говорит, что некомильфо и я с ним согласен, по нормальному надо дописать в нужное прерывание свой обработчик только как его сделать внешним, а совсем правильно дописать в эту функцию менеджер обработчиков, какому из обработчиков передавать управление, тогда и "овцы целы и волки сыты" но это совсем уровень не начинающего )))

andycat
andycat аватар
Offline
Зарегистрирован: 07.09.2017

Тогда уж совсем радикально : использовать software serial только для отладки, в готовом устройстве убрать его, а с устройством работать по hw uart

 

ua6em
ua6em аватар
Offline
Зарегистрирован: 17.08.2016

andycat пишет:

Тогда уж совсем радикально : использовать software serial только для отладки, в готовом устройстве убрать его, а с устройством работать по hw uart

так библиотека от DetSimen и писалась в расчёте на то, что в режиме отладки будет использоваться CustomSoftwareSerial, а в боевом режиме, можно перекомпилировать на любой хардварный. вплоть до USB, для этого она завёрнута в поправленную библиотеку SomeSerial (поддержка 8N2)

#define SOFTSERIAL  // если определено используем SofwareSerial, 
                    // НАЗВАНИЕ не менять, используется в библиотеке
//#define SERIAL1   //328PB ONLY

#ifdef  SOFTSERIAL
ALSerial RadioPort(2, 3); // RX, TX
uniFT897D Radio(RadioPort);
#else
#ifdef SERIAL1
ALSerial RadioPort(&Serial1);
uniFT897D Radio(RadioPort);
#else
ALSerial RadioPort(&Serial);
uniFT897D Radio(RadioPort);
#endif
#endif

 

ЗЫ даже если использовать хардваре, компилятор всё одно будет орать на двойное определение

 

andycat
andycat аватар
Offline
Зарегистрирован: 07.09.2017

Тем более, какая разница комильфо или нет, все равно в рабочем режиме это использоваться не будет.

ua6em
ua6em аватар
Offline
Зарегистрирован: 17.08.2016

andycat пишет:
Тем более, какая разница комильфо или нет, все равно в рабочем режиме это использоваться не будет.

прерывание, это задел на возможные проблемы с энкодером, он будет давать от 400 до 1000 импульсов на оборот, боюсь будут пропуски...

andycat
andycat аватар
Offline
Зарегистрирован: 07.09.2017

Я вот так использую оба uart, в итоговом просто отключу лог


#if (defined(SHOW_LOG) && defined(HWUART_MODEM)) // в зависимости от настроек прописываем и инициализируем UARTы
#include <SoftwareSerial.h> // стандартная библиотека работы с SoftwareSerial
#ifdef DEBUG_MODE
SoftwareSerial swuart(_pinSwUART_RX, _pinSwUART_TX); // установка контактов
#else
SoftwareSerial swuart(pinSwUART_RX, pinSwUART_TX); // установка контактов
#endif
void initLogModemUART() {
  swuart.begin(9600);
  Serial.begin(MODEM_SPEED);
}
#elif defined(SHOW_LOG)
#include <SoftwareSerial.h> // стандартная библиотека работы с SoftwareSerial
#ifdef DEBUG_MODE
SoftwareSerial swuart(_pinSwUART_RX, _pinSwUART_TX); // установка контактов
#else
SoftwareSerial swuart(pinSwUART_RX, pinSwUART_TX); // установка контактов
#endif
void initLogModemUART() {
  swuart.begin(MODEM_SPEED);
  Serial.begin(9600);
}
#elif defined(HWUART_MODEM)
void initLogModemUART() {
  Serial.begin(MODEM_SPEED);
}
#else
#include <SoftwareSerial.h> // стандартная библиотека работы с SoftwareSerial
#ifdef DEBUG_MODE
SoftwareSerial swuart(_pinSwUART_RX, _pinSwUART_TX); // установка контактов
#else
SoftwareSerial swuart(pinSwUART_RX, pinSwUART_TX); // установка контактов
#endif
void initLogModemUART() {
  swuart.begin(MODEM_SPEED);
}
#endif

#ifdef SHOW_LOG
void logStrPrint(char * inStr) { // вывод строки в лог
#ifdef HWUART_MODEM
  swuart.print(inStr);
#else
  Serial.print(inStr);
#endif
}
void logWrite(byte inCh) { // вывод байта в лог
#ifdef HWUART_MODEM
  swuart.write((char)inCh);
#else
  Serial.write((char)inCh);
#endif
}
void logHexPrint(signed long inHex) { // вывод шестнадцатиричного числа в лог
  logWrite('x');
#ifdef HWUART_MODEM
  swuart.print(inHex, HEX);
#else
  Serial.print(inHex, HEX);
#endif
}
void logDecPrint(signed long inInt) { // вывод десятичного числа в лог
#ifdef HWUART_MODEM
  swuart.print(inInt, DEC);
#else
  Serial.print(inInt, DEC);
#endif
}
byte logAvailable() { // смотрит есть ли в логе приемные байты
#ifdef HWUART_MODEM
  return swuart.available();
#else
  return Serial.available();
#endif
}
byte logRead() { // чтение байта из лога
#ifdef HWUART_MODEM
  return swuart.read();
#else
  return Serial.read();
#endif
}
#endif

void modemStrPrint(char * inStr) { // строку в модем
#ifdef HWUART_MODEM
  Serial.print(inStr);
#else
  swuart.print(inStr);
#endif
}

void modemWrite(byte inCh) { // байт в модем
#ifdef HWUART_MODEM
  Serial.write((char)inCh);
#else
  swuart.write((char)inCh);
#endif
}

byte modemAvailable() { // есть ли данные в модеме
#ifdef HWUART_MODEM
  return Serial.available();
#else
  return swuart.available();
#endif
}

byte modemRead() { // чтение из модема
#ifdef HWUART_MODEM
  return Serial.read();
#else
  return swuart.read();
#endif
}

 

asam
asam аватар
Offline
Зарегистрирован: 12.12.2018

ua6em пишет:

 

прерывание, это задел на возможные проблемы с энкодером, он будет давать от 400 до 1000 импульсов на оборот, боюсь будут пропуски...

 

Тут не важно сколько на оборот. Тут важно сколько раз в секунду.

ua6em
ua6em аватар
Offline
Зарегистрирован: 17.08.2016

у тя всё просто, у меня всё сложнее, порт надо подсовывать другой библиотеке и, она должна его переварить, со всеми его параметрами...

ua6em
ua6em аватар
Offline
Зарегистрирован: 17.08.2016

asam пишет:

ua6em пишет:

 

прерывание, это задел на возможные проблемы с энкодером, он будет давать от 400 до 1000 импульсов на оборот, боюсь будут пропуски...

 

Тут не важно сколько на оборот. Тут важно сколько раз в секунду.

от 1000 до 5000

asam
asam аватар
Offline
Зарегистрирован: 12.12.2018

ua6em пишет:

asam пишет:

Тут не важно сколько на оборот. Тут важно сколько раз в секунду.

от 1000 до 5000

Ну, если делать по уму, то никаких проблем с пропусками при такой частоте быть не должно. Можно хоть INT0/1 использовать, хоть PCINT1/2/ Хотя на  INT0/1 проще - не надо с демультиплексированием заморачиваться.

ua6em
ua6em аватар
Offline
Зарегистрирован: 17.08.2016

asam пишет:

ua6em пишет:

asam пишет:

Тут не важно сколько на оборот. Тут важно сколько раз в секунду.

от 1000 до 5000

Ну, если делать по уму, то никаких проблем с пропусками при такой частоте быть не должно. Можно хоть INT0/1 использовать, хоть PCINT1/2/ Хотя на  INT0/1 проще - не надо с демультиплексированием заморачиваться.

Этим и занят, освобождением прерываний )))

asam
asam аватар
Offline
Зарегистрирован: 12.12.2018

ua6em пишет:

Ну, если делать по уму, то никаких проблем с пропусками при такой частоте быть не должно. Можно хоть INT0/1 использовать, хоть PCINT1/2/ Хотя на  INT0/1 проще - не надо с демультиплексированием заморачиваться.

Этим и занят, освобождением прерываний )))

[/quote]

А чего их освобождать? INT1 и так свободен. Если надо обязательно INT0 то можно взять другой софт сериал - https://github.com/PaulStoffregen/AltSoftSerial этот INT0 не использует

ua6em
ua6em аватар
Offline
Зарегистрирован: 17.08.2016

Другие на поддерживают передачу параметров порта, 8N2 в частности, у меня используется кастомный вариант оной либы

asam
asam аватар
Offline
Зарегистрирован: 12.12.2018

ua6em пишет:

Другие на поддерживают передачу параметров порта, 8N2 в частности, у меня используется кастомный вариант оной либы

Ну ОК, а чем INT1 не устраивает?

ua6em
ua6em аватар
Offline
Зарегистрирован: 17.08.2016

asam пишет:

ua6em пишет:

Другие на поддерживают передачу параметров порта, 8N2 в частности, у меня используется кастомный вариант оной либы

Ну ОК, а чем INT1 не устраивает?

заняты
В принципе понятно, что надо сделать, но хочется правильно, не верится, что компилятору нельзя сказать, что если если какой-то PCINT не используется то и конфликта из-за двойного определения нет )))

поправил библиотеку так, но что мне подсказывает, что тоже костыли:
 

#include <avr/interrupt.h>
#include <avr/pgmspace.h>
#include <Arduino.h>
#include <CustomSoftwareSerial.h>
#include <math.h>

extern void UA6EM_PCINT0_handler(void);


#if defined(PCINT0_vect)
ISR(PCINT0_vect)
{
  CustomSoftwareSerial::handle_interrupt();

  UA6EM_PCINT0_handler();
}
#endif

А в теле программы функция реализована так:
 

void UA6EM_PCINT0_handler(void){
encoder.tick(); 
}

setup(){

 PCICR |= (1 << PCIE0 /*D8-D13*/); 
 PCMSK0 |= (1 << PCINT0) | (1 << PCINT1);  // D8 и D9 на энкодер
}

осталось попробовать перенести софтовый сериал на пины этого же PCINT и напасматреть их совместную деятельность )))

Перенёс на D11 и D12, PCINT0 прекрасно разделяется между процедурами обработки софтового серийного порта и обработчиком Энкодера

ua6em
ua6em аватар
Offline
Зарегистрирован: 17.08.2016

подумал подумал, ничего умнее чем обернуть в кастомной библиотеке CustomSofwareSerial в такую конструкцию не придумал:  ЗЫ в SofwareSerial всё точь в точь )))
в файле CPP
 

#include <avr/interrupt.h>
#include <avr/pgmspace.h>
#include <Arduino.h>
#include <CustomSoftwareSerial.h>
#include <math.h>

#ifdef EXTERNALPCINT0
extern void UA6EM_PCINT0_handler(void);
#endif

.
.
.

#if defined(PCINT0_vect)
ISR(PCINT0_vect)
{
  CustomSoftwareSerial::handle_interrupt();
  #ifdef EXTERNALPCINT0
  UA6EM_PCINT0_handler();
  #endif
}
#endif

#if defined(PCINT1_vect)
ISR(PCINT1_vect)
{
  CustomSoftwareSerial::handle_interrupt();
}
#endif

#if defined(PCINT2_vect)
ISR(PCINT2_vect)
{
  CustomSoftwareSerial::handle_interrupt();
}
#endif

#if defined(PCINT3_vect)
ISR(PCINT3_vect)
{
  CustomSoftwareSerial::handle_interrupt();
}
#endif

Интересно, кто-то решил эту проблему по человечески?
 

ua6em
ua6em аватар
Offline
Зарегистрирован: 17.08.2016

Вопрос решения конфликта по фэншую всё ещё актуален! Может есть у кого, что сказать?