Отсутствующие буферы с последовательной связью
В настоящее время я сталкиваюсь с проблемой последовательной связи с моим 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 ночь, и все кажется идеальным. У меня нет никаких потерянных данных.
@Greg_B, 👍2
Обсуждение1 ответ
Не прямой ответ на ваши вопросы, но после прочтения вашего кода я хотел бы дать несколько комментариев:
- Вы не используете “круговые буферы”, вы выполняете двойную буферизацию. Если бы вы использовали круговой буфер, вам не понадобились бы два из них.
Запись
всегдаложна
и, следовательно, не служит никакой цели.- Когда
запись
иchange_buffer
оба верны, вы должны передатьbuff2
, а неbuff1
, так как ISR теперь заполняетbuff1
. - Слишком много глобалов. Переменные, используемые в одной
функции, должны быть локальными для этой функции. Если вам нужно, чтобы значение
сохранялось между вызовами, квалифицируйте переменную как
статическую
. - Код, заполняющий буферы, был бы более понятным, ИМО, если бы вы избегали повторения. Например.
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-регистр процессора. На самом деле, вы можете получить
более короткий код, так как компилятор может не полностью исключить
повторение.
- прерывание с кнопки и ожидание, пока на последовательный порт 1 поступит сообщение
- Как разделить входящую строку?
- Какова максимальная длина провода для последовательной связи между двумя Arduino?
- Последовательная связь между двумя Arduino (запрос и получение)
- Не нашел датчик отпечатков пальцев :( Arduino Mega 2560 Adafruit Fingerprint Sensor
- Отправка последовательных данных в прерывании
- Выводы прерываний Arduino Mega 2560 и отображение портов с помощью поворотного энкодера
- Серийное прерывание
Вы довольно часто подключаете прерывания, что, я бы сказал, по крайней мере необычно. Я не знаю, что делает 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