Задержка (delay) и buzzer

Dima85
Offline
Зарегистрирован: 07.01.2013

К моей Arduino Mega, к 7 порту подключен buzzer (пищалка). Включаю я его таким образом:

analogWrite(7, 252); // Almost any value can be used except 0 and 255
delay(210); // wait for a delayms ms
analogWrite(7, 0); // 0 turns it off

Как видно в этой конструкции есть delay который на несколько микро секунд останавливает работу всего Arduino. Как-то можно избавится от этой остановки?

maxi_10
Offline
Зарегистрирован: 05.01.2012
awer77
Offline
Зарегистрирован: 05.04.2013

 

День добрый.

у меня похожий вопрос:

каки способом поставить обратный отсчёт в микросекундах? либо каки способом написать самому такую функцию?

Вот часть моего кода где надо заменить delay на сountdown.

 

#include "ECU.h"

boolean geber_R = 2;
unsigned long takt;

void setup()
{
pinMode(geber_R, INPUT);
}

void loop()
{
takt = pulseIn(geber, HIGH);


if(takt > 1 && takt < 160000)
{
delayMicroseconds(560)
strake_R_01g (12);
}

if(takt > 160000 && takt < 320000)
{
delayMicroseconds(127)
strake_L_01g (13);
}
}

Заранее благодарен

Aleksej

step962
Offline
Зарегистрирован: 23.05.2011

awer77 пишет:

каки способом поставить обратный отсчёт в микросекундах? либо каки способом написать самому такую функцию?

Чтобы ответить на этот вопрос, необходимо понять, чем этот ваш countdown должен отличаться от банального delay.

Ведь это явно не общепринятый обратный отсчет времени - в микросекундном то диапазоне!

leshak
Offline
Зарегистрирован: 29.09.2011

>каки способом поставить обратный отсчёт в микросекундах? либо каки способом написать самому такую функцию?

Таким же как и в миллисекундах, только использовать, для получения времени не millis(), а micros()

Только учтите, что велкиой точности - вы уже не добъетесь.

kisoft
kisoft аватар
Offline
Зарегистрирован: 13.11.2012

Интересный вопрос, а у меня есть еще один вариант ответа.

Что такое обратный отсчет? Это фиксированный от какого то момента времени отсчет в обратную сторону.

Как можно это реализовать? Например так:

1. Зафиксировать начало отсчета (сохранить текущий micros())

2. В нужное время считать снова micros() и вычесть из зафиксированного значения (см.п.1) двойную разницу между считанным micros() и зафиксированным значением.

Мозг в куче? Если да, то вот формула:

unsigned long g_saveCounter = 0L;

void loop()
{
  if( что то произошло, пора начинать обратный отсчет )
  {
    g_saveCounter = micros();
  }
  if( произошло что то, когда нужно отобразить или использовать обратный отсчет )
  {
    unsigned long l_now = micros();
    unsigned long l_diff = g_saveCounter - 2 * (l_now - g_saveCounter);
    Serial.print( "Final countdown: " );
    Serial.println( l_diff );
  }
}

Конечно нужно помнить, что micros переполняется и переходит снова в ноль, но это совсем другая история.

 

awer77
Offline
Зарегистрирован: 05.04.2013

Благодарю за подсказки и ответы,

в этом случае идёт речь о блоке управления ДВС.

В первой стадии программирую корректировку зажигания и угол опережения зажигания вот таблица расчета угла: http://oi49.tinypic.com/107mm53.jpg
 

Способ обработки:

1. Измеряю интервал между импульсами ВМТ.

2. Выбираем из таблицы нужный угол в виде задержки.

3. Корректирую угол (вакум в коллекторе + температура + итд.)

4. Ставим таймер на время X из таблицы + корректировка.

5. Ну а потом транзистор катушка и поджигаю смесь.
 

Что требуется запрограммировать.

1. Array со значениями из из таблицы (имеется).

2. Функцию выдачи импульсов (имеется).

3. Задержку (не решил как)

4. Offset для корректировки зажигания (в работе).

4. Таймер для расчёта интервала (имеется).

 

leshak
Offline
Зарегистрирован: 29.09.2011

Если я правильно понял, во время задержки - вам не нужно "заниматся еще чем-то" (мерять датчики и т.п.). То есть скетч можно остановить банальным delay(). Только вам нужно в микросекундах. Значит используем
Arduino - DelayMicroseconds

Но... тут все очень сильно зависит от того сколько именно микросекунд вам нужно. Если там речь идет про единицы микросекуд и десятки - будет очень сложно. И наверное без оцилографа все это - не настроить.

На малых значениях - delayMicroseconds - может врать. Ну и нужно помнить, что в зависимости от платы, вся работа с микросекундами крато 4-рем ил 8-ми (то есть выставить задержку точно в 5-микросекунд - уже не получится никак).

Так же, скорее всего от digitalWrite отказыватся. На таких интервалах - она недопустимо медленная. Нужно Прямое управления выходами через регистры микроконтроллера Atmega

ну и возможно, прийдется, для повышения точности перейти на использование таймеров аппаратных. В легком случае - погуглить библиотеки таймеров готовые, в тяжолых - брать даташит, статьи и разбиратся со всеми этими "регистрами таймеров/пред-делителями и проч". Тут может оказать помощь книжечка Arduino Cookbook автора Michael Margolis  (нагугливатся на торрентах) - там вот как раз такие задачи и описаны. Вернее даны "рецепты-примеры" готовые (без особых теорий).

awer77
Offline
Зарегистрирован: 05.04.2013




День добрый,

наконец-то заработало, вот код:




const boolean sensor   = 2;
const boolean inductor = 13;
boolean       a        = 0;
unsigned long time_1   = 0;
unsigned long time_2   = 0;


void setup()
{
  pinMode(sensor, INPUT);
  pinMode(inductor, OUTPUT);
}

void loop()
{

  
if (digitalRead(sensor) == HIGH)
{
  time_1 = micros();
  time_2 = micros();
  time_1 = time_1 + 300000;
  time_2 = time_2 + 300100;
  a=1;
}

if(time_1 <= micros() && a == 1)
{
  digitalWrite(inductor,HIGH);
}

if(time_2 <= micros() && a == 1)
{
  digitalWrite(inductor,LOW);
  a = 0;
}
}

теперь буду разбираться с выходами.

leshak
Offline
Зарегистрирован: 29.09.2011

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

Смотрите. У вас time1 и time2 - unsigned long . Максимальное числе в него влазит 4,294,967,295 . То есть через 4294967295 микросекунд, наступит переполнение и начнется отсчет от нуля. 

Прикинем. Это 4294967 миллисекунд, 4295 секунды, 71 минута, или 1 час 11 минут.

То ест где-то через час непрерывной работы, ваша логика time_1 = time_1 + 300000; даст сбой  . В time1 в результате сложения, образуется цифра меньше чем  текущие время и условие time_1 <= micros()  срабоатет сразу, а не через 300 милисекунд, как вы ожидали.

К счастью, лечение тут простое. В time1 - нужно просто запоминать время. Не ничего не добавляя. А вот само условие переписать как

if(micros()-time1>=300000 && a1==1){

Такой вариант - не подвержен проблеме переполнения.

Кстати, как только вы в этой же манере перепишите и условие time2, вы увидите что вам вообщем-то и не нужно "две переменных", а достаточно одной time и двух условий

if(micros()-time>=300000...
if(micros()-time>=300100...
 

И еще. 

Не знаю специально или нет, но вы понимаете что у вас "отсчет времени" начнется только когда digitalRead(sensor) c HIGHT упадет в LOW? То есть не когда на сенсор прийдет импульс запустится таймер, а когда "волна спадет"? То есть к вашему таймеру, в неявном виде еще добавляется сама длина импульса. 

Что-бы "ловить фронт сигнала", а не спад,есть два подхода. Один из них - использовать attachInterrupt()

Второй - запоминать в конце loop текущией значение сенсора, и запускать таймер по условию

loop(){
  currentSensor=digitalRead(....
  if(prevSensor==LOW && currentSensor==HIGH){
     // запускаем таймер
  }


  prevSensor=currentSensor;
}

Я бы - использовал attachInterrupt. На таких малых интервалах, думаю через него точней будет.

 

awer77
Offline
Зарегистрирован: 05.04.2013

Спасибо за подсказки. Функция attachInterrupt() заработала не сразу, программа делала что хотела подавала хаотично сигнал на выход т.к. attachInterrupt() прерывает Loop() функцию помогли две команды:



void loop()
{
  noInterrupts();
  // critical, time-sensitive code here
  interrupts();
  // other code here
}

вот по этой ссылке можно узнать больше: http://www.arduino.cc/en/Reference/AttachInterrupt

теперь работает нормально, вот код:




boolean       help_a   = 0;
unsigned long time_1   = 0;

void time_set()
{
  time_1 = micros();
  help_a = 1;
}

void setup()
{
  DDRD = DDRD | B10000000;
  attachInterrupt(1, time_set, RISING);
}

void loop()
{
  
noInterrupts();

if(micros()-time_1 >= 300000 && help_a == 1)
{
  PORTD = B10000000;
}

if(micros()-time_1 >= 301000 && help_a == 1)
{
  PORTD = B00000000;
  help_a = 0;
}

interrupts();

}
leshak
Offline
Зарегистрирован: 29.09.2011

Это хорошо что вы нашли про запрет прерываний, но... что-то уже больно радикально. Похоже на избавление от занозы путем ампутации ноги. Возможно и нет другого решения, но "запрещать и не пущать" - лучше в последнюю очередь. Не будем уподоблятся политикам. При запрещенных прерываниях - у вас не идут часы (millis(), micsros()), Serial - теряет данные, импульсы - будут пропускатся.

Вообщем лучше, все-таки выяснить

а. Какому именно кусочку кода мешают прерывания и постаратся только его оборачивать в запрет. Только "самое критичное". Держать "в запрете прерываний" 90% процентов процессорного времени - это, мягко говоря - стремно.
б. Понять "а почему же они вообще у вас происходят"?  Откуда берутся сработки прерываний? Я же, так понимаю, сами импульсы идут реже чем 300 милисекунд? Следовательно, по идее они должны происходить только когда "loop крутится и ждет", а значит ничему они не помешают.

Есть подозрение что "мусорные сработки" провоцирует само включение индуктора. Есть возможность это отсделить, не происходит "нежданное" прерывание именно в момент дергания?

А еще вы забыли про volatile  (перечитайте attachInterrupt(), раздел "замечания по использованию").

И еще "напрямую" вы немного не аккуратно сбрасываете/выставляете биты. Возможно в вашем случае и не мешает это, но... лучше не привыкать.  Вот смотрите в строке 12, направление порта вы выставили "аккуратно", поменяли только тот бит который вам нужно, остальные оставили "как есть". А вот в строках 23,24 - ставите нужный вам бит и насильно обнуляете все остальные. Не глядя.

Кстати не обязательно "работать только напрямую" или "только чере digitalWrite" - вполне можно миксовать эти два подхода. Например использовать pinMode что-бы не хмурить мозг (все равно в этот момент скорость не важна), а уж сами ноги - дергать через PORTD. Не скажу что "миксовать нужно" (в единообразии - тоже есть плюсы), просто вот "есть такая лазейка для лени" :) 

 

awer77
Offline
Зарегистрирован: 05.04.2013

leshak пишет:

А еще вы забыли про volatile  (перечитайте attachInterrupt(), раздел "замечания по использованию").

Не получается применить attachInterrupt(), я переписал программу работает на первый взгляд корректно и исправил управление входами также добавил второй канал т.к двигатель имеет два цилиндра. Вот код:



const boolean sensor   = 3;
boolean       help_a   = 0;
boolean       help_b   = 0;
unsigned long time_1   = 0;

void setup()
{
  DDRD = DDRD | B11000000;
}

void loop()
{

if(digitalRead(sensor) && help_a == 0)
{
time_1 = micros();
help_a = 1;
}

if(micros()-time_1 >= 300000 && help_a == 1 && help_b == 0)
{
  PORTD = B00000000 ^ B10000000;
}

if(micros()-time_1 >= 311000 && help_a == 1 && help_b == 0)
{
  PORTD = B10000000 ^ B10000000;
  help_a = 0;
  help_b = 1;
}

if(micros()-time_1 >= 300000 && help_a == 1 && help_b == 1)
{
  PORTD = B00000000 ^ B01000000;
}

if(micros()-time_1 >= 311000 && help_a == 1 && help_b == 1)
{
  PORTD = B01000000 ^ B01000000;
  help_a = 0;
  help_b = 0;
}

}


Вопрос, можноли использовать регистры входов как условие? Например: if (PORTD == 00100000)

leshak
Offline
Зарегистрирован: 29.09.2011

>Вопрос, можноли использовать регистры входов как условие

Можно.

> if (PORTD == 00100000)

Будет глючить. Так вы проверяете состояние сразу всех битов. А так как остальные у вас выставлены в режим входа, то могут ловить помехи из эфира. И условие может сработать, а может и нет.  if(PORTD & B010) - проверяем только второй бит.

И у установкой/стяния битоввы на хомутали. Ну как минимум потому что опять устанавливаете PORTD не глядя какое у него текущие состояние. Посмотрите на свою же строку 8-мь. Вы там в регистре устанавливаете два бита. Все верно, почему же когда аналогичная задача - установить биты в строке 22 - у вас вдруг вылез Xor каких-то двух чисел?

Вообщем ставит бит так:

PORTD=PORTD | B10000000

Можно использовать сокращенный синтаксис, раз мы PORD и справа и слева используем.

PORTD|=B10000000

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

PORTD|=1<<7;

Теперь снимаем бит (xor у нас выйдет на сцену когда мы захотим "переключать биты", а не "выключить не смотря на текущие состояние).

PORTD=PORT & B01111111 // сбрасываем седьмой бит, все остальные оставляя как есть

Что-бы глазами не инвертировать число, можем  заставить комп сделать это

PORTD=PORT & ~ B10000000

И так же как делали это с установка

PORTD&=~B10000000

Или

PORTD&=~(1<<7)

 

И проверять в условии, установлен или нет - тоже можем так же

if(PORTD & (1<<7)) - проверим только седьмой бит

P.S. А в какой момент у вас происходит "мусорное срабатывание прерывания" - вы так и не сказали. То ли биты не те выставляли (с хором это было возможно), то ли вообще в схеме ошибка и ловили мусор из эфира, то ли дребезг у вас там.

leshak
Offline
Зарегистрирован: 29.09.2011

Кстати у вас видно много повторений micros()-time_1

loop(){
  unsigned long diff=micros()-time1;

  if(diff>300){....
  if(diff>500){.....

}

 

leshak
Offline
Зарегистрирован: 29.09.2011

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

#define INDUCTOR_1_ON PORTD|=(1<<7)   
#define INDUCTOR_1_OFF PORTD&=~(1<<7)
#define INDUCTOR_2_ON PORTD|=(1<<6)
#define INDUCTOR_2_OFF PORTD&=~(1<<6)

#define INDUCTOR_ALL_OFF PORTD&=~(B11<<6)

 

И включать выключать в коде так:

  INDUCTOR_1_ON; // включили первый
  INDUCTOR_2_OFF; // выключили второй
  
  INDUCTOR_ALL_OFF; // выключили сразу оба

 

awer77
Offline
Зарегистрирован: 05.04.2013

 

leshak пишет:

P.S. А в какой момент у вас происходит "мусорное срабатывание прерывания" - вы так и не сказали. То ли биты не те выставляли (с хором это было возможно), то ли вообще в схеме ошибка и ловили мусор из эфира, то ли дребезг у вас там.

С хором это было не связанно, т.к. я пробовал с командами digital.Read / Wtite. Толком честно говоря тоже не разбирался.

Теперь пытаюсь задать время цикла и задержку. Имеется таблица с расчитаными значениями задержки после поступления импульса на сенсор.

http://oi45.tinypic.com/oge8lx.jpg

Угол опережения на графике рассчитывается с помощью трёх квадратных уравнений. для оборотов 0-2000, 2000-3000, 3000-7000. Справится ли контролер если я буду каждый раз при поступление импульса рассчитывать угол в виде задержки ? или рассчитать заранее отрезки времени с интервалом в 200 оборотов и задать с помощью if(30000 >= time && time >= 35000) почти 40 условий? или можно ли задать условие в функции switch case в таком виде case 30000 >= time && time >= 35000:?

leshak
Offline
Зарегистрирован: 29.09.2011

> Толком честно говоря тоже не разбирался

Вместо запрета прерываний, можно еще в самом обработчике "давить лишние сработки". В самом time_set() то же делать if(micros()-time_1) меньше какая-то значения - не запускать таймер и не обновлять time_1 . Вообщем если с прошлого импульса прошло слишком мало времени - игнорировать этот.

Или - еще вариант. При сработке импульса. Делать detachInterrupt (прекращать слушать прерывание) и снова делать attachInterrupt только когда после того как погазили второй индуктор.

> Справится ли контролер если я буду каждый раз при поступление импульса рассчитывать угол в виде задержки

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

>или рассчитать заранее отрезки времени с интервалом

Да, я бы делал именно так.

>и задать с помощью if(30000 >= time && time >= 35000) почти 40 условий? или можно ли задать условие в функции switch case

Да вообщем-то "как вам удобней". Что if, что switch - это синтаксический сахар. В бинарные коды оно, скорее всего, одинаково скомпилируется. Так что выбор между ними - дело эстетики.

Но моя эстетика - говорит что раз это "табличные данные", то и в коде их лучше всего делать в виде таблицы. То есть - массива. И потом по этому массиву пробегатся for-ом. находить нужную строку. 

Что-бы не засорять скетч, я бы эту таблицу вынес в отдельный файл .h файл. И подключал его к основному скетчу через #include. Тогда эти цифры - не будут мозолить глаза. А если нужно - можно иметь несколько таблиц (несколько файлов) и подключать нужную из них просто меняя одну строчку. Может быть полезно при эксперементах/настройках.

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

P.S.S. А с битовыми операциями - разберитесь все-таки. Их нужно знать. Не только для прямой рабты с портами. Сделайте отдельный скетч, и попробуйте поигратся с разными числами/операцями выводя результат через Serial.println(val,BIN) и глядя что получается.

 

awer77
Offline
Зарегистрирован: 05.04.2013

 

День добрый,

спасибо за подсказки, буду разбираться.

leshak пишет:

Но моя эстетика - говорит что раз это "табличные данные", то и в коде их лучше всего делать в виде таблицы. То есть - массива. И потом по этому массиву пробегаться for-ом. находить нужную строку.

А это интересно, наверное попробую так посмотрим что получится.

leshak пишет:

P.S.S. А с битовыми операциями - разберитесь все-таки. Их нужно знать. Не только для прямой работы с портами. Сделайте отдельный скетч, и попробуйте поиграться с разными числами/операциями выводя результат через Serial.println(val,BIN) и глядя что получается.

а здесь я ссылался на вот эту ссылку http://arduino.cc/en/Reference/BitwiseAnd та как раз это объясняется, но похоже нужно ещё раз прочитать.

leshak пишет:

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

Стоит у меня в гараже мотоцикл 31 год уже ему. Когда я его покупал, т.к. у меня нет прав перегонял его мой знакомый а я ехал за ним. После этого наверно неделю проветривал машину от мотоциклетного дыма, он ведь двухтактный и работает на смеси масла и бензина. Тогда и возникла идея сделать мозги для двигателя. Которые управляют впрыском бензина и масла а также распределением зажигания и всего остального что радует водителя при воскресной прогулочной езде. Теперь собираю по кусочкам программу.Начал с зажигания т.к. особо переделывать двигатель не придется а потом уж все остальное. Есть страница где можно немного подсмотреть как это делается http://secu-3.org/ там даже код программы выложен.

 

awer77
Offline
Зарегистрирован: 05.04.2013

Если уж нато пошло можно и тему переименовать. Если модераторы непротив например на "2 stroke engine ECU"

awer77
Offline
Зарегистрирован: 05.04.2013

leshak пишет:

Но моя эстетика - говорит что раз это "табличные данные", то и в коде их лучше всего делать в виде таблицы. То есть - массива. И потом по этому массиву пробегатся for-ом. находить нужную строку. 

Интересный код получактся:






byte a = 0;
byte i = 0;

unsigned long strake_time = 0;

unsigned long strike_time_list[2][3] = {{100000,75000,60000},
                                       {5000   ,7000  ,9000  }};

takt = pulseIn(sensor, HIGH);

while (a = 1)
{
  if (takt >= strake_delay[i] && strake_delay[i] >= takt)
  {
    strake_time = strike_time[2][i];
    a = 1;
  }
  else i++;
}

 

leshak
Offline
Зарегистрирован: 29.09.2011

Даже не вникая. Строка 11.  Бесконечный цикл. Не путайте = и ==

MacSim
Offline
Зарегистрирован: 28.11.2012

что-то мудреное изобретаете.

по прерыванию с ноги инт обрабатывайте прерывание настраиваете, если надо таймер,  запускаете таймер.

по прерыванию от таймера выполняете действие, останавливаете таймер.

и пишите лучше, обращаясь напрямую к регистрам.

 

bwn
Offline
Зарегистрирован: 25.08.2014

Максим, ты шо? Три года прошло.))))