Вопрос касательно быстрой остановки счетчика прерываний

Antero
Offline
Зарегистрирован: 13.04.2018

Здравствуйте.

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

возьмем простой пример 

volatile int a = 0; // переменная
int b = 500; // нужное число импульсов
 pinMode(3,INPUT); // нужный пин

 
 void setup()
 {
pinMode(13,OUTPUT);
digitalWrite(13,1);

attachInterrupt(1, imp_detect, RISING); } void loop() { if (a >= b) { digitalWrite(13,0); a = 0; } } void imp_detect() { a++; }

тут вроде бы все просто и диод погаснет отсчитав нужное кол-во импульсов, ОДНАКО если код значительно больше и в тушке loop куча всего - учитывая это разве погаснет диод в нужное время ? 

быть может проверку кол-ва и выключение диода можно как то переместить в функцию прерывания ?

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

Код у меня получается довольно большой, а учитывая большую скорость оборотов мотора я боюсь прогадать

Antero
Offline
Зарегистрирован: 13.04.2018

извините код коряво вставился

volatile int a = 0; // переменная
int b = 500; // нужное число импульсов
 pinMode(3,INPUT); // нужный пин

 
 void setup()
 {
    pinMode(13,OUTPUT);
   digitalWrite(13,1);
   attachInterrupt(1, imp_detect, RISING);
 }

 void loop()
 {
   if (a >= b)  { 
    
   digitalWrite(13,0);
      a = 0;
      }
 }

 void imp_detect()
 {
   a++;
 }

 

sadman41
Offline
Зарегистрирован: 19.10.2016

Для начала нужно понять, что "одновременно" и "в тот же момент" и "нужное время" - это исключительно человеческие фантазии. Оперируйте временем, в течении которого должно совершиться действие. Вполне может статься, что частота поступления импульсов меньше, чем частота прохождения loop().

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

Antero
Offline
Зарегистрирован: 13.04.2018

sadman41 пишет:

И, да, дернуть за ногу вполне себе можно и в прерывании. 

подскажите как это называется правильно (название команд) - я пробовал в теле прерывания использовать if и while но не получилось

nik182
Offline
Зарегистрирован: 04.05.2015

Прерывание потому и называется прерыванием, потому что за время порядка микросекунды прерывает выполнение программы. Тут главное долго в нём не сидеть и выходить до того, как оно сработает снова. Все команды в прерывании работают штатно. От длины основной программы не зависит, но только если в основной программе на долго не запрещаются прерывания. Приведите пример программы которая не получилась и что конкретно не получилось?   

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

Antero пишет:
я пробовал в теле прерывания использовать if и while но не получилось
Странно, показывайте как пробовали - не должно быть никаких проблем.

5N62V
Offline
Зарегистрирован: 25.02.2016

Antero пишет:

sadman41 пишет:

И, да, дернуть за ногу вполне себе можно и в прерывании. 

подскажите как это называется правильно (название команд) - я пробовал в теле прерывания использовать if и while но не получилось


В смысле не получилось? Должно получиться! Попробуйте b сделать константой, или именованной константой. А в теле обработчика поставьте условие if(a>b)PORTD=0b1000000;//включили7пин порта D.

Antero
Offline
Зарегистрирован: 13.04.2018

...

Я не знаю что произошло, однако сегодня действительно получилось - буквально пару дней назад я пробовал по всякому подставлять IF в функцию прерывания (также пробовал и различные другие методы которые приходили в голову) но программа просто "зависала" после одного прохода, а сегодня бац и заработало.

 

Antero
Offline
Зарегистрирован: 13.04.2018

не долго я радовался - дома на макетке все было норм но приехал на работу и подключил к бандуре и получил пшик.

проблема та же - мотор не останавливается в нужный момент.

пожалуйста посмотрите мой код - быть может я допустил нелепую ошибку:

#include <EEPROM.h> // библиотека для работы с памятью ардуинки
#include <Wire.h>
#include <LiquidCrystal_I2C.h> // Подключаем библиотеку индикатора и I2C
LiquidCrystal_I2C lcd(0x27, 16,2); //настройка экрана
#include <Keypad.h> // Подключаем библиотеку кнопок

volatile int kol=0; // переменная для подсчета прерываний
volatile int Lkol=3; // переменная для проверки фоторезистора

volatile uint16_t datal; // переменные хранящие 3 изменяемые позиции
volatile uint16_t datak;
volatile uint16_t datat;

// настройка матричной клавиатуры
const byte ROWS = 4; // 4 строки
const byte COLS = 4; // 4 столбца
char keys[ROWS][COLS] = {
  {'1','4','7','*'},
  {'2','5','8','0'},
  {'3','6','9','#'},
  {'A','B','C','D'}
}; 
byte rowPins[ROWS] = {7,6, 5, 4}; 
byte colPins[COLS] = {11, 10, 9, 8};  
Keypad keypad = Keypad( makeKeymap(keys), rowPins, colPins, ROWS, COLS );

byte menu=0; // переменная определяющая какое из меню отобразить на экране

boolean first=true;
// функции для преобразования чисел и удобной записи в память
void EEPROMWriteInt(int p_address, int p_value)
        {
        byte lowByte = ((p_value >> 0) & 0xFF);
        byte highByte = ((p_value >> 8) & 0xFF);

        EEPROM.write(p_address, lowByte);
        EEPROM.write(p_address + 1, highByte);
        }

//This function will read a 2 byte integer from the eeprom at the specified address and address + 1
unsigned int EEPROMReadInt(int p_address)
        {
        byte lowByte = EEPROM.read(p_address);
        byte highByte = EEPROM.read(p_address + 1);

        return ((lowByte << 0) & 0xFF) + ((highByte << 8) & 0xFF00);
        }


void setup()
{

datak=EEPROMReadInt(0); // присваиваем переменным данные из памяти
datal=EEPROMReadInt(4);
datat=EEPROMReadInt(8);
  lcd.init();                     
  lcd.backlight();// Включаем подсветку дисплея
  Serial.begin(9600);

  pinMode(3,INPUT_PULLUP); // сюда пойдет счетчик
  pinMode(13,OUTPUT); // пин для вкл\выкл мотора
  digitalWrite(13,LOW);
  detachInterrupt(1);
}

void loop(){
  // если фоторезистор перекрыт 
if (analogRead(0)<=datal){
  Lkol=0;
}

// если фоторезистор опять открыт
 if (Lkol==0&&analogRead(0)>=datal){
  Lkol=1;
  kol=0;
 lcd.clear();  //очистить дисплей
 attachInterrupt(1, blink, RISING); // запускаем прерывание для подсчета оборотов
  }

 // если фоторезистор перекрыт в первый раз то включаем мотор через определенное время
  if (Lkol==0){
  delay(datat);
  digitalWrite(13,HIGH);
   }

   
 // построение простого меню где при нажатии на кнопки ABC -отображается на экране строка для ввода значения переменных, при нажатии на * происходит обнуление переменной, а при нажатии на D сохраняются изменения и отображаются основные показатели  
  char key = keypad.getKey(); //опрос клавиатуры
     switch (key){
       case ('A'):
lcd.clear();  //очистить дисплей
menu=1;
lcd.setCursor(0, 0);   
lcd.print("Menu vibora ");
lcd.setCursor(0, 1);
lcd.print("kol-vo: ");
lcd.setCursor(9, 1);   
lcd.print(datak);          
    break;
     case ('B'):
lcd.clear();  
menu=2;
lcd.setCursor(0, 0);  
lcd.print("Menu vibora ");
lcd.setCursor(0, 1);
lcd.print("secund: ");
lcd.setCursor(9, 1);   
lcd.print(datat);           
    break;
     case ('C'):
lcd.clear();  
menu=4;
lcd.setCursor(0, 0);  
lcd.print("Menu vibora ");
lcd.setCursor(0, 1);
lcd.print("svet:  ");
lcd.setCursor(9, 1);  
lcd.print(datal);         
    break;
     case ('D'):
lcd.clear();  //очистить дисплей
EEPROMWriteInt(0, datak);
EEPROMWriteInt(4, datal);
EEPROMWriteInt(8, datat);
menu=0;
    break;
  }

  if(menu==1){
if(isDigit(key)){
  datak = datak*10+key-48;
  lcd.setCursor(9, 1);   
  lcd.print(datak);           
} 
}
  if(menu==1&&key=='*'){
 datak=0;
 lcd.setCursor(9, 1);   
  lcd.print(datak);         
  lcd.setCursor(7, 1); 
  lcd.print("         ");  
  lcd.setCursor(10, 1);   
}

  if(menu==2){
if(isDigit(key)){
  datat = datat*10+key-48;
  lcd.setCursor(9, 1);  
  lcd.print(datat);          
} 
}
  if(menu==2&&key=='*'){
  datat = 0;
  lcd.setCursor(9, 1);  
  lcd.print(datat);            
  lcd.setCursor(7, 1); 
  lcd.print("         ");  
  lcd.setCursor(10, 1);  
}

  if(menu==4){
if(isDigit(key)){
  datal = datal*10+key-48;
  lcd.setCursor(9, 1);   
  lcd.print(datal);           
} 
}
  if(menu==4&&key=='*'){
  datal = 0;
  lcd.setCursor(9, 1);  
  lcd.print(datal);         
  lcd.setCursor(7, 1); 
  lcd.print("         ");  
  lcd.setCursor(10, 1);  
}

if (menu==0){
lcd.setCursor(0, 0);  
lcd.print("Svet:" );
lcd.setCursor(5, 0);   
lcd.print(datal);
lcd.setCursor(9, 0);  
lcd.print("L- " );
lcd.setCursor(12, 0);  
lcd.print(analogRead(0));
lcd.setCursor(0, 1);
lcd.print("Shag:");
lcd.setCursor(5, 1);
lcd.print(datak);
lcd.setCursor(10, 1);
lcd.print("T:");
lcd.setCursor(12, 1);
lcd.print(datat);
}

}

// собственно функция прерывания где происходит подсчет оборотов и остановка мотора
void blink()
{
kol++;
if (kol>=datak)
{
digitalWrite(13,LOW);
menu=0;  
detachInterrupt(1);
}
}

 

Antero
Offline
Зарегистрирован: 13.04.2018

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

ставлю я к примеру значение при котором должно произойти отключение равным 10 - но наглядно видно что кусок бумаги (а мотор крути валы что тянут бумагу) то проезжает 5 мм, то 15, то еще сколько то - и это при довольно таки низких оборотак мотора

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

 

вот как выглядет самодельный энкодер (датчик Sharp - дребезга нет)

https://b.radikal.ru/b02/1805/6f/864262100665.jpg

 

 

 

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

В перрывании Вы используете menu, но не объявили её volatile.

Никто не знает, чему у Вас равна datak в момент работы с прерыванием, если недавно отработала строка 137 (datak = 0;), то условие в прерывании не выполнится никогда.

Вообще, код слшком велик и "расхристан" для поиска таких тонких ляпов. Такой огромный код Вы не отладите. Сделайте так:

1. Из кода выбросьте всё. При это всё означает ВСЁ.

2. Оставьте константные (просто присваиниваем) переменные datak и kol.

3. В setup включите Ваш двигатель и откройте прерывание.

Больше в коде не должно быть ничего, от слова совсем. Хотя, нет, можете вставить в loop печать в сериал значения kol, чтобы видеть, как она меняется при оборотах.

Добейтесь, чтобы Ваш двигатель останавливался и только потом вставляйте то, что получилось, в большой код.

Antero
Offline
Зарегистрирован: 13.04.2018

до действительно про меню забыл, а про datak - там значение есть всегда отличное от 0, разве что кто то спец внесет изменение

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

Antero пишет:

datak - там значение есть всегда отличное от 0, 

Если выполнится строка 137, то оно станет 0

В любом случаем, сделайте как я сказал. Если на простейшем коде не будет работать, ищем аппаратные проблемы (например, прерывание может вадисть в больших количествах при работе двигателя). А если будет работать надёжно, то смотрим на остальной код.

Только печатайте в loop свою kol, чтобы видеть как часто прерывания валятся.