Отсутствующие буферы с последовательной связью

В настоящее время я сталкиваюсь с проблемой последовательной связи с моим arduino MEGA 2560.

Мой проект заключается в сборе данных от кодировщика. Существует 2 сигнала : ACP и ARP. Для 1 вращения имеется 16 384 ACPs (счетчик) и 1 ARP (ссылка). Я должен хранить высокий уровень и длину КАЖДОГО ACP, и если есть ARP. Для приема этих сигналов я создал небольшую печатную плату с двухлинейным приемником (потому что на выходе кодера я получаю дифференциальные сигналы).

Для хранения этих данных я использую буфер из байтов. Мне нужно 3 байта для хранения как длины, так и высоты ACP и 1 ARP (я храню данные в ISR, см. Код ниже) :

buff1[i] = char((arpNbr << 7) +  (acpPeriod >> 8));
i++;
buff1[i] = char(acpHigh);
i++;
buff1[i] = char(acpPeriod);
i++;

Я использую 2 круговых буфера (когда 1 заполнен, я начинаю втискиваться в другой). Поскольку существуют различные скорости вращения, самая быстрая из них составляет 15 об / мин (вращение в минуту), поэтому 1 сигнал ACP (1 период) длится 244 секунды, а для 1 вращения требуется 4 секунды.

Для сбора данных я напрямую использую :

Последовательная запись(buff1, размер(buff1));

Вот код, который я использую :

#include <eRCaGuy_Timer2_Counter.h> //To use timer in ISRs

//Constants
#define NO_DEFAULT_LED_PIN            40
#define DEFAULT_LED_PIN               42
#define WRITE_LED_PIN                 44
#define REC_IN_PROGRESS_LED_PIN       50
#define ACP_LED                       46
#define ARP_LED                       48
#define ACP_PIN                       20
#define ARP_PIN                       21
#define STOP_REC_BUTTON_PIN           28

#define SIZE                          800
#define BAUDSPEED                     230400   

//Variables    
boolean recordingEnded = false;
volatile boolean writing = false;
volatile unsigned short arpNbr = 0;
volatile unsigned short acpHigh = 0;
volatile unsigned short acpPeriod = 0;
volatile int i = 0;
volatile bool change_buffer = 0;

char buff1[3 * SIZE];
char buff2[3 * SIZE];

//Variables for interruption routines
volatile unsigned long acpHighUs = 0UL; volatile unsigned long acpPeriodUs0 = 0UL; volatile unsigned long acpPeriodUs1 = 0UL;

/**************************************************************************************/
/* Initialization                                                                     */
/**************************************************************************************/
void setup()
{
  Serial.begin(BAUDSPEED);

  //Setup LED pins
  pinMode(DEFAULT_LED_PIN, OUTPUT);
  pinMode(NO_DEFAULT_LED_PIN, OUTPUT);
  pinMode(SD_WRITE_LED_PIN, OUTPUT);
  pinMode(REC_IN_PROGRESS_LED_PIN, OUTPUT);
  pinMode(ACP_LED, OUTPUT);
  pinMode(ARP_LED, OUTPUT);

  //Setup ACP and ARP inputs
  pinMode(ACP_PIN, INPUT_PULLUP);
  pinMode(ARP_PIN, INPUT_PULLUP);

  //Setup the button
  pinMode(STOP_REC_BUTTON_PIN, INPUT_PULLUP);

  //Light ALL LEDs during initialisation
  digitalWrite(DEFAULT_LED_PIN, HIGH);
  digitalWrite(NO_DEFAULT_LED_PIN, HIGH);
  digitalWrite(SD_WRITE_LED_PIN, HIGH);
  digitalWrite(REC_IN_PROGRESS_LED_PIN, HIGH);
  digitalWrite(ACP_LED, HIGH);
  digitalWrite(ARP_LED, HIGH);
  delay(500);

  //Setup the timer
  timer2.setup();

  //Setup is done
  digitalWrite(NO_DEFAULT_LED_PIN, LOW);
  digitalWrite(SD_WRITE_LED_PIN, LOW);
  digitalWrite(ACP_LED, LOW);
  digitalWrite(ARP_LED, LOW);

  //Attach interruption at the end of the initialisation (recording starts...)
  attachInterrupt(digitalPinToInterrupt(ACP_PIN), acpRising, RISING);
  attachInterrupt(digitalPinToInterrupt(ARP_PIN), arpRising, RISING);
}

/**************************************************************************************/
/* Main loop                                                                          */
/**************************************************************************************/
void loop()
{
  //Check if the user stops the recording
  if (digitalRead(STOP_REC_BUTTON_PIN) == LOW && !recordingEnded )
  {
    noInterrupts();
    delay(500);

    //Switch off the recording LED
    digitalWrite(SD_WRITE_LED_PIN, LOW);
    digitalWrite(REC_IN_PROGRESS_LED_PIN, LOW);
    digitalWrite(ACP_LED, LOW);
    digitalWrite(ARP_LED, LOW);

    return;
  }

  //Check if the writing should be done
  if (writing && !recordingEnded)
  {
    digitalWrite(WRITE_LED_PIN, HIGH);

    if (change_buffer) {
       Serial.write(buff1, sizeof(buff1));
    }
    else {
       Serial.write(buff2, sizeof(buff2));
    }

    digitalWrite(WRITE_LED_PIN, LOW);
    writing = false;
  }
}

/**************************************************************************************/
/* This method is called at the beginning of each ACP.                                */
/**************************************************************************************/
void acpRising()
{
  digitalWrite(ACP_LED, HIGH);
  acpHighUs = timer2.get_count();
  acpPeriodUs1 = timer2.get_count() / 2 - acpPeriodUs0;
  acpPeriodUs0 = timer2.get_count() / 2;

  acpPeriod = acpPeriodUs1;

  attachInterrupt(digitalPinToInterrupt(ACP_PIN), acpFalling, FALLING);    
}

/**************************************************************************************/
/* This method is called at the end of each ACP.                                      */
/**************************************************************************************/
void acpFalling()
{
  digitalWrite(ACP_LED, LOW);
  acpHighUs = (timer2.get_count() - acpHighUs) / 2;
  acpHigh = (unsigned short)acpHighUs;

  if (change_buffer) {
    buff1[i] = char((arpNbr << 7) +  (acpPeriod >> 8));
    i++;
    buff1[i] = char(acpHigh & 0xFF);
    i++;
    buff1[i] = char(acpPeriod & 0xFF);
    i++;

  }
  else  {
    buff2[i] = char((arpNbr << 7) +  (acpPeriod >> 8));    
    i++;
    buff2[i] = char(acpHigh & 0xFF);
    i++;
    buff2[i] = char(acpPeriod & 0xFF);
    i++;
  }

  arpNbr = 0;

  if (i == 3 * SIZE) {
    i = 0;
    change_buffer = !change_buffer;
    writing = true;
  }

  attachInterrupt(digitalPinToInterrupt(ACP_PIN), acpRising, RISING);
}

/**************************************************************************************/
/* This method is called at the beginning of each ARP.                                */
/**************************************************************************************/
void arpRising()
{
  digitalWrite(ARP_LED, HIGH);
  arpNbr = 1;

  attachInterrupt(digitalPinToInterrupt(ARP_PIN), arpFalling, FALLING);
}

/**************************************************************************************/
/* This method is called at the end of each ARP.                                */
/**************************************************************************************/    
void arpFalling() {
  digitalWrite(ARP_LED, LOW);
  attachInterrupt(digitalPinToInterrupt(ARP_PIN), arpRising, RISING);
}

Проблема :

Я использую программное обеспечение CoolTerm для сбора моих данных в файл .txt и анализа их с помощью Hexed.it.

Похоже, что через определенный период, например, через 6-10 минут, я теряю часть 1 буфера. Я проверил это, потому что, как я писал выше, мне нужно 3 байта для информации и в Hexed.it, Я увидел, что у меня было только 2 байта сразу, и после этого данные были правильными.

После глубокого анализа я увидел, что там была часть буфера и еще несколько (1-2), которые отсутствовали. Мне удалось увидеть это, потому что между 2 ARP (опорный сигнал кодера) не было 16384x3 байта.

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

Я также пробовал другие методы, такие как :

  • Запись на SD-карту;

  • Изменение размера буфера;

  • Изменение коэффициента бодрствования;

  • Используя метод опроса вместо ISR.

Но ничего убедительного ...

ИЗМЕНИТЬ:

Похоже, моя проблема возникла из-за программного обеспечения CoolTerm после исправления моего кода. Теперь я использую RealTerm, и я сделал запись продолжительностью 1 ночь, и все кажется идеальным. У меня нет никаких потерянных данных.

, 👍2

Обсуждение

Вы довольно часто подключаете прерывания, что, я бы сказал, по крайней мере необычно. Я не знаю, что делает attachInterrupt () в фоновом режиме, но если он использует динамическое распределение памяти, я могу себе представить, что его частый вызов может привести к проблемам., @Sim Son

Также вы не должны возвращаться из функции main (loop) в uC. Я не знаю, законно ли это, но в этом нет особого смысла, @Sim Son

@SimSon: 1. attachInterrupt() не выделяет память. 2. loop () - это не main(): возвращение из loop() совершенно нормально и ожидаемо., @Edgar Bonet

@EdgarBonet приятно знать! Будет ли это похоже на " while(1) loop();` в конце концов?, @Sim Son

@SimSon: main() [действительно вызывает loop() повторно](https://github.com/arduino/ArduinoCore-avr/blob/1.8.2/cores/arduino/main.cpp#L45-L48)., @Edgar Bonet

Что вы подразумеваете под”_а часть и другие буферы не передаются"? Продолжаете ли вы получать данные? Отличаются ли полученные данные от того, что вы ожидаете? Если да, то в чем же она отличается? Кроме того, какова типичная частота сигнала ACP?, @Edgar Bonet

@EdgarBonet Извините за создание нового ответа, но я не могу ответить на ваш назойливый. Я могу отредактировать его, но не могу ответить на него... Странно... Итак, для вашего ответа 2. Запись переходит в true в acpfalling ISR, после i = 0. 3. Спасибо, что видели это. Я провожу тест, чтобы понять, в чем проблема... Надеюсь, это разрешится 4. Я посмотрю, спасибо! 5. Использование char *buff = change_buffer ? buff1 : buff2; не займет ли это бесполезного времени ? Я имею в виду, если это просто вопрос чтения. Потому что, насколько я знаю, ISR должны быть как можно короче., @Greg_B

Сайты StackExchange не являются форумами. Вы не публикуете ответы ниже ответов и так далее, пожалуйста, возьмите [тур] (снова). -- Если вам нужны какие-то разъяснения, прокомментируйте ниже вопрос или неясный ответ. -- Если у вас появилась новая информация после прочтения комментария или ответа, отредактируйте свой вопрос. Слева под вашим вопросом есть эта маленькая ссылка., @the busybee

@EdgarBonet Я пытаюсь "добавить комментарий" к вашему комментарию к вашему комментарию, но не могу. он говорит мне: "Вам нужно иметь репутацию 50, чтобы комментировать". Как я могу справиться с этой ситуацией, чтобы ответить на ваш комментарий и не создавать глупые ответы все время ?... Извините за такой ответ ..., @Greg_B

@Greg_B - У вас есть 2 аккаунта с одинаковым именем, но 2 разных идентификатора. Если вы не можете прокомментировать, пожалуйста, отредактируйте вопрос с новой информацией., @VE7JRO


1 ответ


1

Не прямой ответ на ваши вопросы, но после прочтения вашего кода я хотел бы дать несколько комментариев:

  1. Вы не используете “круговые буферы”, вы выполняете двойную буферизацию. Если бы вы использовали круговой буфер, вам не понадобились бы два из них.
  2. Запись всегда ложна и, следовательно, не служит никакой цели.
  3. Когда запись и change_buffer оба верны, вы должны передать buff2, а не buff1, так как ISR теперь заполняет buff1.
  4. Слишком много глобалов. Переменные, используемые в одной функции, должны быть локальными для этой функции. Если вам нужно, чтобы значение сохранялось между вызовами, квалифицируйте переменную как статическую.
  5. Код, заполняющий буферы, был бы более понятным, ИМО, если бы вы избегали повторения. Например.
char *buff = change_buffer ? buff1 : buff2;
buff[i++] = (arpNbr << 7) | (acpPeriod >> 8);
buff[i++] = acpHigh & 0xFF;
buff[i++] = acpPeriod & 0xFF;

Обратите внимание, что buf[i++] является идиоматическим C/C++.

Теперь, что касается вашего конкретного вопроса, вы упомянули в комментарии, что “между 2 ARP не было 3x16384 байта”. Это вполне может быть следствием проблемы 3: вы отправляете байты не в правильном порядке, так как иногда (когда функция Serial.write() опережает ISR) вы можете отправлять старые данные.


Обновление: чтобы ответить на комментарий

char* бафф = перемена буфера ? buff1 : buff2; не займет бесполезного времени?

Не совсем. Тестирование change_buffer-это то, что вы должны сделать в любом случае. Назначение адреса буфера локальному указателю также выполняется компилятором под колпаком. В лучшем случаеон оптимизирует ваш код до чего-то вроде:

register char *Z;
if (change_buffer)
  Z = buff1 + i;
else
  Z = buff2 + i;
Z[0] = (arpNbr << 7) | (acpPeriod >> 8);
Z[1] = acpHigh & 0xFF;
Z[2] = acpPeriod & 0xFF;
i += 3;

Использование явного указателя buff дает аналогичный результат, так как компилятор выделит его в Z-регистр процессора. На самом деле, вы можете получить более короткий код, так как компилятор может не полностью исключить повторение.

,