Лазерный контроллер на Arduino Uno
---> Я пытаюсь создать контроллер с использованием Arduino Uno, который просто обнаруживает импульсы от генератора сигналов, имитирующего лазерную машину, а затем переключается между 4 состояниями конечного автомата на выходе предварительного усилителя. в этом предыдущем сообщении, поэтому соединения одинаковы, за исключением настроек генератора сигналов. Новые настройки генератора сигналов:
Тип: импульсный, частота: 20 Гц (50 мс), сила тока: 5,0 В размах, смещение: 0 В, длительность = 100 нс
---> Я пытаюсь добавить 5 вещей в проект, так что любой вклад приветствуется:
1) Инициализировать все 4 состояния конечного автомата в массив, чтобы пользователь мог определить столько состояний, сколько он хочет, из объявлений.
2) ДОБАВИТЬ цифровой антидребезг на входной контакт, LASER_PIN.
3)(сначала необходимо выполнить пункт №5) ДОБАВИТЬ сброс в систему, который возвращает программу в STATE_00 из того, что она делает.
4) Лучший код для ожидания поступления импульса от Laser перед переходом к конечному автомату в void loop().
5) (от этого зависит пункт №2) Полностью удалить задержку из функции CountPulses() и заменить ее функцией millis() чтобы разрешить использование кнопки сброса
---> Вот что у меня есть. Я начал с добавления 4 состояний конечного автомата и использования функции delay() в CountPulses(). работает точно так же, как и в предыдущем посте, и она работает нормально, и я могу переключать состояния при подсчете импульсов, однако, когда я добавил функцию millis() вместо delay(), как показано ниже в коде , я получаю только 0 с в подсчете импульсов, а конечный автомат застрял в STATE_00, поэтому я использовал пару операторы печати для отладки, и оказывается, что currentmillis() - startmillis() на самом деле меньше, чем период = 1000 мс, поэтому я застрял в том, как это настроить, и без этого я не могу двигаться вперед, любая помощь будет оценена. Мой код и результаты монитора последовательного порта показаны ниже:
--> ОБНОВЛЕНИЕ: элементы №3 и №5 работают отлично, см. обновленный код. для пункта № 4 я еще не понял этого. для пункта № 2, есть много библиотек для устранения дребезга, но не уверен, какие библиотеки (Button, Bounce и т. Д.) Больше всего доверяют сообществу arduino, любые предложения помогут.
-->ОБНОВЛЕНИЕ 2: код был обновлен со следующими изменениями: внешние прерывания используются для перезагрузки системы, если переключатель находится в НИЗКОМ состоянии, опрос с прерываниями используется для ожидания импульсов.
ОБНОВЛЕНИЕ ВОПРОСА: вопросы № 3, 4 и 5 были решены, и теперь осталось добавить в проект только два пункта с изменениями пункта № 1 и пункта № 2 на основе комментариев:
1) Я требую, чтобы конечный автомат делал следующее:
a) Объявлен в массиве, где пользователь может легко изменить порядок выполнения состояний. (например: состояние_2 --> состояние_1 --> состояние_3 --> состояние_0).
b) Пользователь может легко уменьшить количество состояний с MAX_VALUE = 4 до 1 состояния. (например, при MAX_VALUE = 3: состояние_0 --> состояние_1 --> состояние_2) .
2) ДОБАВЬТЕ цифровое устранение дребезга на входной контакт RESET_PIN. Примечание. Отказ от дребезга на LASER_PIN был ошибкой в понимании, основанном на комментариях, поэтому я обновил это.
Обновленный код с текущими результатами монитора последовательного порта показан ниже:
//Цели: Использовать вход от лазера для управления предварительным усилителем на АЦП. Мультиплексирование входов на предварительном усилителе
// Дата: 21.10.2019
//Тип: Импульсный, Частота: 20 Гц (50 мс), Ампер: 5,0 В пик-пик, Смещение: 500 мВ, Ширина = 100 нс
//------------------------------------- БИБЛИОТЕКИ ПРОЕКТА------------------------ -----------
#include <Bounce2.h>
#include <Arduino.h>
#include <avr/io.h>
#include <avr/wdt.h>
#include <avr/interrupt.h>
//-----------------------ОПРЕДЕЛЯЕТ---------- ------------------
// Объявить входной контакт лазера
#define LASER_PIN 2
// Объявление вывода сброса
#define RESET_PIN 3
//Определяем состояния машины
typedef enum {STATE_00, STATE_01, STATE_10, STATE_11} fsm_state_type;
fsm_state_type fsm_state = STATE_00;
// ---------------------------------------КОНСТАНТЫ (не меняются)---------------- ---------------------
const unsigned long period = 1000; //значение - количество миллисекунд
//---------------------------------------ПЕРЕМЕННЫЕ (будут меняться)------------------ --------------------
bool only_for_print = false;//используется только для операторов печати
int reset_switch = 1;//Начнем с ВЫСОКОГО уровня, чтобы избежать сброса
int PulseCount = 0; //Счетчик импульсов от X-RAY
int Output = 0;//Состояние переключателя на предусилителе
int wait = 0;//ждем подсчета импульсов
int N = 20;//нет. импульсов для подсчета перед переключением состояний
volatile int IRQcount = 0;
volatile boolean reset_flag = false;
unsigned long start_time = 0;
unsigned long current_time = 0;
//----------------------------ОПРЕДЕЛЯЕМЫЕ ПОЛЬЗОВАТЕЛЕМ ФУНКЦИИ-------------------------------- ----------------
void fsm();
void loop();
void setup();
void WDT_RESET();
void IRQcounter();
void CountPulses();
//-----------------------------ФУНКЦИИ ОТКЛОНЕНИЯ-------------------------------- ----------------------
//-------------------------------- ОСНОВНЫЕ НАСТРОЙКИ--------------- ------------------------
void setup()
{
Serial.begin(115200);
//Настройка вывода
pinMode(LASER_PIN, INPUT_PULLUP);
pinMode(RESET_PIN, INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(LASER_PIN), IRQcounter, RISING);//прикрепляем обработчик прерывания к входу лазера
attachInterrupt (digitalPinToInterrupt (RESET_PIN), RESET_ISR, FALLING); // прикрепляем обработчик прерывания к сбросу, ждем нажатия пользователем кнопки или переключателя
start_time = millis(); //начальное время начала
sei();//Включаем прерывания
WaitForPulses();//Ждет обнаружения 20 импульсов
}
//--------------------------------ГЛАВНЫЙ ЦИКЛ----------------------------- --------------------
void loop()
{
current_time = millis();
fsm();//Конечный автомат
}
//--------------------------------ФУНКЦИЯ СЧЕТА ИМПУЛЬСОВ-------------- -------------------------------
void CountPulses()
{
// текущее_время = millis();
if ((current_time - start_time) >= period)
{
start_time = current_time;
cli();//отключаем прерывания
PulseCount = IRQcount;
IRQcount = 0;
Serial.print(F("Pulse Count is = "));
Serial.println(PulseCount);
sei();//включаем прерывания
}
}
//--------------------------------ФУНКЦИЯ СОСТОЯНИЯ МАШИНЫ------------- -------------------------------
void fsm()
{
switch (fsm_state)
{
case STATE_00:
/////////Вывод инструкции только для отладки//////////
while (only_for_print == false)
{
Serial.println("The state is 00");
only_for_print = true;
}
///////// Настройка количества импульсов //////////////////
current_time = millis();
CountPulses();
Output = 0;
if (PulseCount == N)
{
PulseCount = 0;//Сброс счетчика импульсов
only_for_print = false; //используется только для печати статистики
fsm_state = STATE_01;//переключаемся в следующее состояние
}
break;
case STATE_01:
/////////Вывод инструкции только для отладки//////////
while (only_for_print == false)
{
Serial.println("The state is 01");
only_for_print = true;
}
///////// Настройка количества импульсов //////////////////
current_time = millis();
CountPulses();
Output = 2;
if (PulseCount == N)
{
PulseCount = 0;//Сброс счетчика импульсов
only_for_print = false; //используется только для печати статистики
fsm_state = STATE_10;//переключаемся в следующее состояние
}
break;
case STATE_10:
/////////Вывод инструкции только для отладки//////////
while (only_for_print == false)
{
Serial.println("The state is 10");
only_for_print = true;
}
///////// Настройка количества импульсов //////////////////
current_time = millis();
CountPulses();
Output = 3;
if (PulseCount == N)
{
PulseCount = 0;//Сброс счетчика импульсов
only_for_print = false; //используется только для печати статистики
fsm_state = STATE_11;//переключаемся в следующее состояние
}
break;
case STATE_11:
/////////Вывод инструкции только для отладки//////////
while (only_for_print == false)
{
Serial.println("The state is 11");
only_for_print = true;
}
///////// Настройка количества импульсов //////////////////
current_time = millis();
CountPulses();
Output = 4;
if (PulseCount == N)
{
PulseCount = 0;//Сброс счетчика импульсов
only_for_print = false; //используется только для печати статистики
fsm_state = STATE_00;//переключаемся в следующее состояние
}
break;
default:
fsm_state = STATE_00;
break;
}
}
//---------------------------------- ПЕРЕКЛЮЧАТЕЛЬ СБРОСА ISR----------- --------------------------
void RESET_ISR()
{
reset_flag = true;
if(reset_flag == true)
{
// Serial.println("Теперь система перезагрузится");// Только для отладки
reset_flag = false;//Сбросить флаг переключателя сброса
wdt_enable(WDTO_500MS);//сброс через 0,5 секунды
while (1)
{
// wdt_reset(); // раскомментировать, чтобы избежать перезагрузки
}
}
}
//-----------------------СЧЕТЧИК ИМПУЛЬСОВ ISR---------------------- ------------------
void IRQcounter()
{
IRQcount++;
}
//-----------------------ДОЖДАТЬСЯ ИМПУЛЬСОВ-------- ------------------
void WaitForPulses()
{
while(wait<20)
{
if (bit_is_set(EIFR, INTF0))
{
Serial.println("Pulse is detected ");
wait++;
}
}
wait = 0;//сброс
}
*/
[![отредактированное изображение][3]][3]
Ниже показан последовательный монитор с добавленным переключателем сброса на контакте 3 с использованием библиотеки сторожевого таймера.
Pulse is detected
Pulse is detected
Pulse is detected
Pulse is detected
Pulse is detected
Pulse is detected
Pulse is detected
Pulse is detected
Pulse is detected
Pulse is detected
Pulse is detected
Pulse is detected
Pulse is detected
Pulse is detected
Pulse is detected
Pulse is detected
Pulse is detected
Pulse is detected
Pulse is detected
Pulse is detected
The state is 00
Pulse Count is = 112
Pulse Count is = 20
The state is 01
Pulse Count is = 20
The state is 10
Pulse Count is = 20
The state is 11
Pulse Count is = 20
The state is 00
Pulse Count is = 20
The state is 01
Pulse Count is = 20
The state is 10
Pulse Count is = 20
The state is 11
Pulse Count is = 20
The state is 00
Pulse Count is = 20
The state is 01
Pulse Count is = 20
The state is 10
Pulse Count is = 20
The state is 11
Pulse Count is = 20
The state is 00
Pulse Count is = 20
The state is 01
Pulse Count is = 20
The state is 10
Pulse Count is = 20
The state is 11
Pulse Count is = 20
@sk95, 👍0
1 ответ
Лучший ответ:
Важное примечание:
После прочтения (еще раз) вашего другого вопроса и ответов на него: Вы подвергаете вход вашего Arduino отрицательному напряжению! Установите смещение сигнала на 2,5 В (средний между GND и VCC) и амплитудой до 5Vpp. Сигнал не должен быть ни ниже, чем GND, ни выше, чем VCC! Вход может выдержать это некоторое время, но при достаточном времени и мощности он будет поврежден.
Анализ вашей проблемы:
1. Измерение периода
Давайте взглянем на "сжатую" версию вашего тайм-кода:
void setup()
{
startMillis = millis();
}
void loop()
{
currentMillis = millis();
if (currentMillis - startMillis <= period)
{
startMillis = currentMillis;
}
}
Я уверен, что вы тоже это видите! Поскольку при первом вызове loop()
время, прошедшее с момента setup()
, мало, условие равно true
и startMillis
обновляется до текущего времени. Это повторяется снова и снова. Условие всегда будет оцениваться как true
.
2. Количество импульсов
В функции CountPulses()
счетчик сбрасывается в начале. Затем после некоторого неопределенного очень короткого промежутка времени (поскольку ваше if
-условие всегда true
) оно считывается. Вероятность того, что пульс будет подсчитан в этот промежуток времени, невелика.
Дополнительное примечание:
Еще один совет. Вы можете определить и использовать свои собственные типы данных, чтобы показать свои намерения. Например, состояния выглядят намного лучше, как показано ниже, и вы получите бесплатные сообщения об ошибках от анализаторов кода.
typedef enum {
STATE_00, STATE_01, STATE_10, STATE_11,
} fsm_state_type;
fsm_state_type fsm_state = STATE_00;
Обновление после добавления новой информации:
Элемент № 2
Как вы думаете, зачем нужно устранять дребезг лазерного импульса? Он генерируется механическим переключателем? Только те нуждаются в разоблачении. Если импульсы генерируются какой-либо электронной схемой, они должны быть чистыми.
Элемент № 4
Вы можете сбросить счетчик IRQ
и подождать, пока он подсчитывается.
Но поскольку это включает в себя отключение и включение прерывания и чтение int
только для 1 события, я бы предпочел установить флаг, если произойдет прерывание. Затем FSM может дождаться установки флага.
Другой альтернативой является прямой опрос входного контакта и ожидание переднего фронта. Но для этого пульс должен быть достаточно широким.
Как отметил @EdgarBonet, будет еще проще опросить флаг прерывания. Но IIRC прерывание должно быть отключено для этого, иначе вызов процедуры обслуживания прерывания очистит флаг, и основная программа не сможет его увидеть.
Лучшей альтернативой является опрос флага прерывания (например,
if (bit_is_set(EIFR, INTF0)) { ... }
). Тогда вам не нужен широкий импульс: как только оборудование обнаружит нарастающий фронт, флаг будет установлен и останется установленным до тех пор, пока вы его явно не очистите (EIFR = _BV(INTF0);
).
- Как сгенерировать аппаратное прерывание в mpu6050 для пробуждения Arduino из режима SLEEP_MODE_PWR_DOWN?
- Как вызвать функции C из скетча ардуино?
- Arduino непрерывно считывает значение АЦП с помощью прерывания
- max7219 связанный дисплей, показывающий зеркальный текст
- Как правильно использовать volatile переменные в Arduino?
- Пиринговая коммуникация
- Запуск С для ардуино
- Как связаться с ESP8266 ESP01, отправив данные через программный сериал на Arduino Uno?
вот где у меня возникают трудности с переходом от delay() к millis() : при добавлении функции delay() программа может подсчитывать количество импульсов, теперь удаляя ее и заменяя функцией millis(), программа работает слишком быстро для подсчета импульсов (всегда показывает 0 импульсов). Я пробовал снижать скорость передачи данных, но и менять настройки функции millis() с разными периодами, но это не помогает., @sk95
Что ж, отойдите от исходного кода и сделайте **дизайн**. Не программируйте случайно, думайте и планируйте. Вы знаете, что вам нужен какой-то определенный период, как показывает источник. Теперь используйте его. Что бы вы сделали *как человек*, чтобы решить эту задачу?
millis()
можно сравнить со взглядом на настенные часы., @the busybeeОтносительно «_Другой альтернативой является опрос входного контакта [...]. Но для этого импульс должен быть достаточно широким._”: Лучшей альтернативой является опрос флага_прерывания_ (т.е.
if (bit_is_set(EIFR, INTF0)) { ... }
). Тогда вам не нужен широкий импульс: как только оборудование обнаружит нарастающий фронт, флаг будет установлен и останется установленным до тех пор, пока вы не очистите его явно (EIFR = _BV(INTF0);
)., @Edgar Bonet@EdgarBonet Спасибо, что напомнили мне об этом. Я взял на себя смелость процитировать вас., @the busybee
Относительно «_IIRC прерывание должно быть отключено для этого [...]_»: вы правильно помните. ;-), @Edgar Bonet
Я немного запутался, когда я использую if (bit_is_set(EIFR, INTF0)) { ... } , он возвращает 0, который говорит мне, что флаг прерывания ITF0 не установлен в регистре EIFR. Чтобы проверить это, я поместил оператор печати в оператор if, который всегда выполняется, когда я включаю генератор сигналов, и прекращает выполнение, когда я его выключаю, что говорит мне, что ITF0 установлен и очищен, поэтому я не уверен, что происходит, какие-либо разъяснения?, @sk95
Не могли бы вы опубликовать код для этого?, @the busybee
О, и, пожалуйста, сделайте это, отредактировав свой вопрос и добавив вокруг него несколько информационных слов, чтобы другим не пришлось копаться в комментариях., @the busybee