SoftwareSerial. Чтение из прерывания. Потеря данных

да ничего не меняет. Я просто все пытаюсь вам обьяснить, что обработка в прерывании вам вообще не нужна, а вы не верите.
Делайте loop() без задержек

мне просто не удается понять, если обработка так и так будет в loop, то какая разница сколько на мк аппаратных UART?

поясните. Я вообще не догоняю вопрос :slight_smile:

да верю я, верю)
Отписывалась об этом ранее

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

А если в loop я получаю этот байт через SoftwareSerial, то что мне и мк мешает его обработать? Какие потенциальные проблемы?

быстродействия не хватит + как выше уже написали, softserial и так через прерывания работает и отжирает нехилую мощность МК.

1 лайк

Посмотрел в исходники. Вижу, что садится исключительно на PCINT.

Стало быть - для его работы достаточно избегать длительной монополизации МК, например - висения в ISR. В противном случае SoftwareSerial::recv() при запоздалом вызове будет получать с пина неактуальные значения его состояния.

Аппаратный Serial сам собирает байт. МК, фактически, по прерыванию “байт принят” просто копирует его в буфер.
Софтварный сериал делает так:

   ...
    // Wait approximately 1/2 of a bit width to "center" the sample
    tunedDelay(_rx_delay_centering);
    DebugPulse(_DEBUG_PIN2, 1);

    // Read each of the 8 bits
    for (uint8_t i=8; i > 0; --i)
    {
      tunedDelay(_rx_delay_intrabit);
      d >>= 1;
      DebugPulse(_DEBUG_PIN2, 1);
      if (rx_pin_read())
        d |= 0x80;
    }

    if (_inverse_logic)
      d = ~d;
...

Ждёт, потом восемь раз читает пин с перекурами, играет с битами в bitwize, и, только потом, копирует его в буфер.
Если во время for() дернется какое-нибудь ISR и позанимает МК, пин успеет сто раз поменять своё состояние и байт будет собран неправильно. И тд и тп.

Хотя, что я брешу-то… recv() и сам из прерывания вызывается, т.е. никто его не прервет. Стало быть - проблема проявится, когда recv запустится невовремя, опоздает по какой-либо причине. Тогда байт и соберется неправильно. Вероятно поэтому и всё ломается в одном и том же месте - с какого-то момента recv() запаздывает с запуском.

@olonach1995 , коллеги всё правильно написали, добавить особо нечего.

Есть идея.

Если Вы так хотите обрабатывать приходящие байты в прерывании (и я Вас в этом понимаю), то почему бы не сделать наоборот? Принимать байты аппаратным сериалом в прерывании по готовности байта “USART Rx Complete” (а не по каждому дрыгу пина, как сейчас). А уж выдавать их программным, там всё пофиг.

Понятна идея? Подойдёт?

1 лайк

Из этого можно сделать очень простой вывод - СофтСериал и так работает через прерывание. поэтому правильнее ему просто не мешать. Два прерывания на одно и тоже событие приводят к тому, что они начинают конкурировать между собой, мешая друг другу.

То есть как вывод - идея ТС изначально неверная, дело даже не в том, что у нее не получается это реализовать, а в том, что ЭТО НЕ НУЖНО ДЕЛАТЬ.

“если решать головоломку” - то для начала нужно спросить ТС, зачем ей это нужно.

Насколько я понимаю, ей хочется сделать так, чтобы по приходу данных в порт основной loop() приостанавливался и сразу выполнялась какая обработка принятых данных.

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

Это как?

Пока писал вопрос - получил ответ - по таймеру.
Но это не тоже самое, что прерывание без задержки на факт окончания приема байта.

ну придумать-то можно много чего.
Можно в код СофтСериала добавить коллбек - вызов пользовательской функции по окончанию приема байта.

Добавка - хотя это скорее всего даст больше проблем, чем пользы. Юзеры обязательно натащат в коллбек функцию что-нибудь такое, что будет вешать весь контроллер…

аппартный уарт умеет на avr складывать битики в один байт, такой у него буфер, разработчик посчитал, что при тактовой 16 мегагерц буфера в один байт достаточно, чтобы успевать его оттуда забирать

Цель-то ясна - сразу по окончанию приема ответа от блюпуп-модуля перейти к ее анализу.

На этом этапе, полагаю, достаточно проверять только наличие последовательности \r\n , если ответ заканчивается ею, и выставлять флажок ready.
А далее в конечном автомате - слать команду, ждать флажок, анализировать ответ и тд.

А я делал как ТС.)) RX на INT и в прерывании до стопа. И даже до 76800 получилось при 2-х МИПС-ах всего.) На другой архитектуре.

Как я писала ранее, мне не удалось подружить rx tx порты платы с аналогичными на модуле. Но я еще к этому вернусь однажды. Так что спасибо за идею. Только эту готовность байта мне как уловить?

Вы имеете ввиду повесить прерывание по таймеру, которое будет проверять буфер на пустоту и, соответственно, обрабатывать его, если там что-то есть?

Смотря что имеется в виду под “обрабатывать”. Чем меньше времени на однопоточном процессоре проведено в ISR, тем меньше непонятных багов наловите.