Этюды для начинающих: blink и без delay, и без millis

timofei
Offline
Зарегистрирован: 12.03.2016

задать новый long intervaly= 500;

ledState = HIGH;

if(currentMillis - previousMillis > intervaly)

ledState = LOW

у меня такое ощущение, что просто ветку засоряю. модератор проснется удалит однозначно

Arhat109-2
Offline
Зарегистрирован: 24.09.2015

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

FAlVik
Offline
Зарегистрирован: 09.09.2013

Товарищи Гуру, помогите...

Получил задание сконструировать на ардуине управление трехфазным двигателем с чистым синусом.

Высоковольтную часть я спроектировал, а вот с таймерами пока понять не могу.

Как из 62,5 кГц получить 50Гц?

Как "синхронизировать" фазы?

Можно использовать для трехфазного ШИМ 3 пина из 6?

ПС. хочу попользовать nano или mini.

ПСС. Теорию перелопатил. Гдето немного не догоняю.

На примерах научиться тоже не получается, тк подходы везде разные и пояснений не понятно что и зачем делается.

faeton
faeton аватар
Offline
Зарегистрирован: 21.03.2016

FAlVik пишет:

Товарищи Гуру, помогите...

Получил задание сконструировать на ардуине управление трехфазным двигателем с чистым синусом.

Высоковольтную часть я спроектировал, а вот с таймерами пока понять не могу.

Как из 62,5 кГц получить 50Гц?

Как "синхронизировать" фазы?

Можно использовать для трехфазного ШИМ 3 пина из 6?

ПС. хочу попользовать nano или mini.

ПСС. Теорию перелопатил. Гдето немного не догоняю.

На примерах научиться тоже не получается, тк подходы везде разные и пояснений не понятно что и зачем делается.

Как Вы синусоиды делаете? И зачем ШИМ? Можете показать высоковольтную часть? У меня предстоит задача сделать плавный пуск для трёхфазника в скором будущем... Кое какие мысли по этому поводу уже есть. :)

Синхронизировать фазы (сделать трёхфазный генератор синусоиды) достаточно просто :)

1. Заводим три переменные Phase1, Phase2, Phase3 с начальным значением 0, 120, 360.

2. Чтобы не считать синус на лету (а это сложно для AVR, тем более в прерывании), создаём массив из 360-ти значений sin*разрешение ЦАП (в мегах оно, кажется, 256 всего - я ещё плохо знаю AVRки) - т.е. на каждый градус фазы есть готовое значение для аналогового выхода  (порта). Его нужно посчитать заранее с округлением до разрядности порта и забить в целочисленный массив.
 
3. Пишем процедуру обработки прерывания таймера, которая покругу гоняет все три переменные (при достижении 360, сбрасывает в 0, иначе инкрементирует) и пишет в аналоговые порты выборку из массива, описанного в предыдущем пункте, значения по индексу этих переменных.

4. Настраиваем таймер, который за секунду делает 360*50=18000 тиков и заскаем его.

Получаем на трёх ногах синусоиду со сдвигом фаз на 120 горадусов. Если она получится слишком ступенчатая, то можно увелисить частоту таймера и, соответственно, размер таблицы синуса.

Чтобы регулировать амплитуду синусоид, можно ввести коэффицикнт при записи в порт.

Чтобы резать синусоиду по амплитуде=фазе, проверять значение переменной переменной перед записью в порт и писать туда "напряжение" или 0. При чём, контролировать переход через ноль элементарно он будет при 0 и ри 180 градусах (значениях крутимых переменных).

Думаю, идея понятна. :)

 

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

faeton пишет:

Думаю, идея понятна. :)

Понятна, только работать не будет. В этих контроллерах нет ЦАПа. АЦП есть, а ЦАПа нет. А то, что Вы собираетесь писать в аналоговые порты - на самом деле просто HIGH и LOW и ничего больше. Так что, так как Вы написали не получится. Разве что прикрутить внешний ЦАП и работать через него.

 

FAlVik
Offline
Зарегистрирован: 09.09.2013

Идея впринципе проста - ШИМ 2х3 выходов на (три фазы по два полупериода). Это управляет трехфазным мостом 12-вольтовым. А 12 вольт через трансформаторы будут 380В. Ну и трансформаторы также буду одновременно являться своеобразными ФНЧ.

faeton
faeton аватар
Offline
Зарегистрирован: 21.03.2016

ЕвгенийП пишет:

faeton пишет:

Думаю, идея понятна. :)

Понятна, только работать не будет. В этих контроллерах нет ЦАПа. АЦП есть, а ЦАПа нет. А то, что Вы собираетесь писать в аналоговые порты - на самом деле просто HIGH и LOW и ничего больше. Так что, так как Вы написали не получится. Разве что прикрутить внешний ЦАП и работать через него.

Оп! Как это нет? Как же оно яркость светодиодика меняет, получая разные значения в порт? ШИМ? Да и, если там ШИМ, то RC цепь сделает из него ЦАП. :)

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

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

faeton пишет:

Оп! Как это нет? 

Очень жаль, но нету там ЦАПа.

faeton пишет:

Как же оно яркость светодиодика меняет, получая разные значения в порт? ШИМ? 

Он, родимый.

faeton пишет:

Да и, если там ШИМ, то RC цепь сделает из него ЦАП. :)

Ну, во-первых, это не отменяет того, что так как описано работать не будет, а во-вторых, это на бумаге так просто. Получить картинку на осциллографе можно, а вот использовать её на деле - выползет то, что нагрузочная способность RC-цепочки - никакая, потребуется операционный услитель или эммитерный повторитель. Затем выяснится, что синусоида приподнята на 2,5 вольта и надо резать постоянную составляющую. В общем, после долгих экспериментов, придём к тому, что надо просто использовать професиональное решение (например, микросхему ICL8038 если 100кГц хватает или XR-2206 (до 2МГц), или MAX038 (до 20МГц) и т.д. - их много).

 

 

faeton
faeton аватар
Offline
Зарегистрирован: 21.03.2016

ЕвгенийП пишет:

faeton пишет:

Оп! Как это нет? 

Очень жаль, но нету там ЦАПа.

faeton пишет:

Как же оно яркость светодиодика меняет, получая разные значения в порт? ШИМ? 

Он, родимый.

faeton пишет:

Да и, если там ШИМ, то RC цепь сделает из него ЦАП. :)

Ну, во-первых, это не отменяет того, что так как описано работать не будет, а во-вторых, это на бумаге так просто. Получить картинку на осциллографе можно, а вот использовать её на деле - выползет то, что нагрузочная способность RC-цепочки - никакая, потребуется операционный услитель или эммитерный повторитель. Затем выяснится, что синусоида приподнята на 2,5 вольта и надо резать постоянную составляющую. В общем, после долгих экспериментов, придём к тому, что надо просто использовать професиональное решение (например, микросхему ICL8038 если 100кГц хватает или XR-2206 (до 2МГц), или MAX038 (до 20МГц) и т.д. - их много).

 
Ну, готовую бисину всегда проще и лучше вкорячить с точки зрения разработчика... Тем не менее, силовой каскад не обязательно ставить после RC. Силовой каскад может с тем же успехом шуравать на транс и тем же самым ШИМ формировать на нагрузке уже синусоиду. :) И подрезать надо не только напряжение нуля, но и серединку синусоиды чтобы она была с отрицательными полуперодами. :)

 

nkk
nkk аватар
Offline
Зарегистрирован: 18.03.2016

При исользовании AnalogWrite(), как и многие, при регулировке скорости вращения вентилятора от компа через полевой npn-транзистор, получил писк.

Электролитический конденсатор (1000 uF) в параллель вентилятору решил проблему писка на высоких скоростях вращения вентилятора, но на низких транзистор дико грелся, аж дым (от флюса) пошел, чуть не перегорел... Флюс иногда полезно не смывать)))

Можно ли решить проблему не способом, описанным Вами, через настройку ШИМ, а как-то иначе? Что будет, если параллельно подключить два пина, на один подать AnalogWrite(), а второй tone(); или, может, как-то схему изменить?

схема типа такой + резистор на 6кОм у пина

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

А транзистор-то ещё жив? Качественный попался :) Кто же его так включает-то? Что за транзистор, кстати и каков ток мотора?

Вопрос про "что за транзистор" не праздный. На схеме нарисован биполярный, а тексте написано "полевой npn-транзистор", но полевых npn транзисторов не бывает в природе. Так что же за тразнистор у Вас?

Что до Вашего вопроса, Вы природу появления писка понимаете? Если понимаете, то ясно, что делать - увеличить частоту ШИМ, чтобы она ушла из звукового диапазона.

FAlVik
Offline
Зарегистрирован: 09.09.2013

ЕвгенийП пишет:

Вопрос про "что за транзистор" не праздный. На схеме нарисован биполярный, а тексте написано "полевой npn-транзистор", но полевых npn транзисторов не бывает в природе. Так что же за тразнистор у Вас?

Может просто имеется в виду p-channel?

FAlVik
Offline
Зарегистрирован: 09.09.2013

Кстати, помучавшись с рисованием синуса через прерывания и тд. выяснил что в режиме FastPWM на пинах проскакивает единичный сигнал, а это фигня, и перешел я на DDS и режим Fase correct .

И тепереча могу рисовать синал любой формы и любой частоты.

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

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

FAlVik пишет:

Может просто имеется в виду p-channel?

Я бы так и подумал, если бы на схеме не был нарисован биполярный.

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

FAlVik пишет:

И тепереча могу рисовать синал любой формы и любой частоты.

Ну, "любой частоты", это, конечно, образно. Какую максимальную частоту синусоиды можете получить? И каково при этом качество синусоиды? Если на Вашем осциллографие есть БПФ, можно посмотреть на картинку синусоиды и картинку БПФ на максимальной частоте? А то я тоже хотел сделать, но подумал, что диапазон частот будет слишком узкий и не стал. Може ошибался и у Вас получилось лучше?

nkk
nkk аватар
Offline
Зарегистрирован: 18.03.2016

nkk пишет:
через полевой npn-транзистор
Всё-таки биполярный! BC817-40.

ЕвгенийП, ток мотора 14мА. Природу я понимаю, но не понимаю, что будет, если совместить analogWrite() с tone(). Может, что-то хорошее?

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

nkk,

а зчем совмещать её с tone? Частоту ШИМ шим можно и без легально менять. Здесь на форуме была отличная инструкция от dimax. Найти не могу, но там собственно и по дадашиту несложно разобраться как это делается. До 62кГц легко - а это уже далеко не звуковая частота.

Что касается включения транзистора. у Вас осциллограф есть? Если есть, попробуйте подключить мотор к БП и и посмотреть осциллографом что происходит в момент включения и выключения питания. А заодно просто на напряжение во время работы. Если нет осциллографа, скажите, я сдлеаю осциллограммы для Вас, у меня сегодня еслть время и натсроение хорошее :)

Это я к тому, что таким подключением, как у Вас Вы неминуемо убьёте транзистор если не сразу, так с небольшим временем. Но лучше Вам самому посмотреть почему. Мой внук сильно впечатлился этимим картинками и теперь в своей машинке включает всё как надо :)

nkk
nkk аватар
Offline
Зарегистрирован: 18.03.2016

Спасчибо, ЕвгенийП, по частоте ШИМ нагуглил - kazus.ru/forums/showthread.php?t=107888#post768273, диод припаял:

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

Конденсатор бы ещё параллельно мотору. Емкость, ну попробуйте 22nf, а вообще лучше посчитать из оборотов двигателя, если Вы их знаете.

nkk
nkk аватар
Offline
Зарегистрирован: 18.03.2016

А какая формула для рассчёта этой ёмкости? Может, там етсь встроенный конденсатор, мотор же - вентилятор с тремя контактами, такой:
Voltage: 12 V
Current: 0.16A
Speed: 2300

Ладно, подключил, всё понмально работало.

byte bSpeed,
  bNewSpeed = 128;      // Устанавливаемый уровень
void setup() {
  TCCR1B = TCCR1B & 0b11111000 | 1;
  pinMode(10, OUTPUT);
  pinMode(13, OUTPUT);
  pinMode(14, INPUT_PULLUP);
  pinMode(15, INPUT_PULLUP);
}

void loop() {
  if (!digitalRead(14) && bNewSpeed < 255)
    bNewSpeed++;
  else if (!digitalRead(15) && bNewSpeed > 0)
    bNewSpeed--;
  if (bSpeed != bNewSpeed) {
    digitalWrite(13, 1);
    analogWrite(10, bSpeed = bNewSpeed);
  }
  delay(100);
  digitalWrite(13, 0);
  delay(10);
}

Теперь надо будет для светодиода то же самое провернуть, но светодиод-то наверное будет Cree XM-L, там ток 3А. Можно же тупо в параллель транзисторы подключить, 4 штуки?

 

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

nkk пишет:

А какая формула для рассчёта этой ёмкости? Может, там етсь встроенный конденсатор, мотор же - вентилятор с тремя контактами, такой:
Voltage: 12 V
Current: 0.16A
Speed: 2300

Теперь надо будет для светодиода то же самое провернуть, но светодиод-то наверное будет Cree XM-L, там ток 3А. Можно же тупо в параллель транзисторы подключить, 4 штуки?

Формулу я не знаю. Каждый раз, когда я это делаю, я долго читаю разные источники, про то, что суть этого конденсатора не допустить искрения щёток коллектора и соответственно помех в эфир и преждевременного износа двигателя и, что частота там равна частоте вращения вала и, в конце, начитавшись этих умных, но бесполезных рассуждений, ставлю первый оказавшийся под рукой конденсатор от 10 до 100 нанофарад.

Кстати, на осциллографе очень хорошо видна разница между мотором с конденсатором и без, поэтому Вы легко можете проверить есть ли у Вас встроенный конденсатор. Просто включите мотор с внешним конденсатором и без - если втроенного нет, то разницу будет невозможно не заметить.

А про диод на 3 ампера и 10 ватт, ну, типа, теоретически можно, но я бы просто взял подходящий транзистор. Например, TIP126 (5А, 64 ватта) - цена вопроса даже при покупке в России всего 10 рублей за штуку. Нормальный дарлингтоновский транзистор с огромным коэффициентом усиления (1000) и встроенным защитным диодом. Он, правда PNP (по другому включать надо), но если Вас это смущает, возьмите его комплементарную пару - TIP121 - все то же самое, но NPN и цена правда не 10, а 16 рублей в том же магазине. Если будете брать, имейте в виду, что они дарлингтоновские, т.е. при расчёте резистора в базу, помните, что на них падаен не 0,7В, а 1,5 - 2,5. Хоте, при таком коэффициенте усиления ...

nkk
nkk аватар
Offline
Зарегистрирован: 18.03.2016

Спасибо... У меня этих  сто, попробую.

---

Попорбовал - минус один транзистор, затем, после следующего включения, минус один светодиод (а светодиод за $2 бакса Вам не транзистор за $0.006, а-ха-ха-а-а-а-а!), вероятно, из-за того, что не все проврда были одной длинны. Приедут дешёвые светодиоды на 12В (по $0.18) - попробую на них!

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

Купите нормальный транзистор :)))

Short Circuit
Short Circuit аватар
Offline
Зарегистрирован: 17.05.2015

 если в девайсе имеем один светодиод и мигаем им по 2 - 3 - 4 раза для индикации режима работы устройства или еще как - то можно наверное управлять этим единственным светодиодом через tone(),   и никаких делеев не потребуется..   единственный недостаток - скважность импульса всегда одинакова ?

зато массу проблем решит?

Short Circuit
Short Circuit аватар
Offline
Зарегистрирован: 17.05.2015

спрошу сюда.

захотелось сделать вариант на задание количества миганий светодиодом,  пусть для начала с делеем:

 

void LEDS( quantity)
{
  for (int i = 0; i <= (int quantity); ++i) //мигаем светодиодом нужное количество раз.
  {
    digitalWrite(led, HIGH);  // мигнем перед засыпанием нескоько раз светодиодом
    delay(300);
    digitalWrite(led, LOW);
    delay(300);
  }
}

 

чтобы задать скажем void LEDS(5)  и светодиод 5 раз мигнул...

насколько понимаю ошибка в задании (int quantity)  или просто (quantity), правильно ее обьявить в инициализации. так и не догадался как правильно. Может кто подскажет?

 

 

Jeka_M
Jeka_M аватар
Offline
Зарегистрирован: 06.07.2014

Можно через define, если это значение в скетче менятся не будет

#define quantity 5

void loop() {
  LEDS();
}

void LEDS()
{
  for (int i = 0; i <= quantity; ++i) //мигаем светодиодом нужное количество раз.
  {
    digitalWrite(led, HIGH);  // мигнем перед засыпанием нескоько раз светодиодом
    delay(300);
    digitalWrite(led, LOW);
    delay(300);
  }
}

Если же хотите менять эту цифру и передавать функции в качестве параметра, то можно так:

void loop() {
  LEDS(5);
}

void LEDS(int quantity)
{
  for (int i = 0; i <= quantity; ++i) //мигаем светодиодом нужное количество раз.
  {
    digitalWrite(led, HIGH);  // мигнем перед засыпанием нескоько раз светодиодом
    delay(300);
    digitalWrite(led, LOW);
    delay(300);
  }
}

 

jeka_tm
jeka_tm аватар
Offline
Зарегистрирован: 19.05.2013
void LEDS( int quantity)
{
  for (int i = 0; i < quantity; i++) //мигаем светодиодом нужное количество раз.
  {
    digitalWrite(led, HIGH);  // мигнем перед засыпанием нескоько раз светодиодом
    delay(300);
    digitalWrite(led, LOW);
    delay(300);
  }
}

 

Short Circuit
Short Circuit аватар
Offline
Зарегистрирован: 17.05.2015

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

а так суть понял - спасибо.

Jeka_M
Jeka_M аватар
Offline
Зарегистрирован: 06.07.2014

Short Circuit пишет:

да, последний варианит скомпилировался, а дефайном не хочет обьявляться.

Дефайн тоже работает, в чём разница я выше написал и привёл два примера (если что, примеры не компилируюся, т.к. это только кусок кода ради примера).

Short Circuit
Short Circuit аватар
Offline
Зарегистрирован: 17.05.2015

я вставлял сразу в готовый вариант. но в работе пока не проверил.

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

Коллеги, я там в первом посте привёл калькулятор для таймера/счётчика 1. А тут мне для своих нужд потребовалось посчитать для таймера счётчика 2 (у него больше делителей). Модифицировал калькулятор. Теперь он считает для всех таймеров. Перед словом Prescaler в листинге указывает в скобках для каких таймеров этот результат применим. Вот модифицированный код:

/////////////////////////////////////////////////////////////////////////////////////////////////////
//
//	Калькулятор тактов и делителя частоты СТС режима таймера/счётчика микроконтроллера ATMega328
//	
//	По заданной желаемой частоте вычисляет количество тактов для всех делителей частоты.
// Перед словом Prescaler в скобках указывается для каких таймеров/счётчиков такой результат может быть получен.
//
//	Результат выдаётся для всех делителей частоты в порядке возрастания погрешности. Делители для
//	которых такая частота невозможна, печатаются последними.
//
//	Результат считается для текущей тактовой частоты микроконтроллера, на котором на котором
//	производится расчёт. Частоту, для которой считается результат, можно изменить - см. комментарий
//	в тексте скетча.
//	
//	Условия использования:
//	
//	- допускается свободное и безвозмездное использование скетча в коммерческих и некоммерческих целях;
//	- модификация допускается только с указанием факта (и автора) модификации в заголовке файла;
//	- не допускается использование в проектах, связанных с разработкой устройств, предназначенных для
//		совершения или подготовки терактов, незаконного сбора информации и любых иных устройств,
//		предназначенных для противозаконной деятельности.
//	
//	Ответственность и обязательства автора:
//
//	- каждый разработчик может использовать или не использовать данный код на свой страх и риск;
//	- автор не несёт решительно никакой ответственности за прямые или косвенные негативные или 
//		позитивные последствия использования или неиспользования данного кода;
//	- автор с благодарностью примет сообщения о проблемах, ошибках и т.п., а также предложения 
//		по совершенствованию скетча и постарается исправить код в кратчайшие сроки, однако 
//		никаких обязательств по оперативному исправлению на себя не берёт;
//	- автор по мере возможности, сил и времени готов ответить на вопросы пользователей, но
//		обязательств на этот счёт на себя не берёт.
//
//	Расширение обязательств и ответственности автора:
//
//	- если код предполагается использовать в серьёзном проекте, где обязательства автора по 
//		исправлению ошибок и предоставлению консультаций необходимы, следует связаться с автором
//		на предмет заключения гражданско-правового договора. Тогда обязанности и ответственность 
//		автора будут определяться не настоящей декларацией, а условиями договора
//		
//	Если данный код Вам пригодился как готовый скетч, или как учебное пособие, и у Вас возникло желание
//	материально поддержать автора (что ни в коей мере НЕ является обязательным условием использования 
//	кода), Вы можете воспользоваться службой WebMoney, кошелёк № R626206676373
//
//
#include <limits.h>

///////////////////////////////////////////////////////////////////////
//
// !!! ВАЖНО !!!
//	Результат считется для текущей тактовой частоты микроконтроллера, на котором на котором
//	производится расчёт. Она доступна в константе F_CPU.
//	Если необходимо посчитать для другой тактовой частоты, следует в строке ниже вместо F_CPU
//	указать желаемую тактовую частоту в герцах. Например,
// static const double fCPU = 8000000.0;	// 8МГц
//
static const double fCPU = F_CPU;

// Всего у таймера/счётчика может быть семь делителей частоты
#define	TOTAL_PRESCALERS	7	

//	Потоковая печать в Serial
template <typename T> inline Print & operator << (Print &s, T n) { s.print(n); return s; }

////////////////////////////////////////////
//
//	Класс, для расчёта количества тактов и делителя
//
//	Расчёт производится в функции Calculate по формуле 
//	со стр.123 ATmega48A/PA/88A/PA/168A/PA/328/P [DATASHEET]
//	Сначала производится прямой расчёт для данного делителя,
//	Затем, после округления, обратный и определяется разница
//	желаемой и полученной частоты.
//
class CalcResult : public Printable {
public:
	CalcResult(uint16_t p) {
		prescaler = p;
		valid = false;
	}

	void Calculate(const double freq) {
		frequency = floor(fCPU / (2.0 * prescaler * freq) - 0.5);
		if (frequency < 0.0 || frequency > (double)UINT_MAX) return;
		counter = (uint16_t) frequency;
		if ((prescaler==32 || prescaler==128) && (counter > 255)) return;
		valid = true;
		frequency = fCPU / (2.0 * prescaler * (1.0 + counter));
		difference = fabs(frequency - freq);
	}

protected:
	size_t printTo(Print& p) const {
		size_t res = 0;
		if (valid) {
			res += p.print((prescaler==32 || prescaler==128) ? "(2) " : ((counter > 255) ? "(1) " : "(0,1,2) "));
		}
		res += p.print("Prescaler: ");
		res += p.print(prescaler);
		if (! valid) return res + p.println(" - Not possible");
		res += p.print("; MaxValue: ");
		res += p.print(counter);
		res += p.print("; Frequency: ");
		res += p.print(frequency);
		res += p.print("Hz; Diff: ");
		res += p.print(difference);
		return res += p.println("Hz");
	}

private:
	uint16_t counter; 
	int16_t prescaler;
	double frequency;
	double difference;
	bool valid;

friend int cmp(const void *, const void *);	
};

/////////////////////////////////////////////////////////
//
//	Функция сравнения двух результатов для qsort
//	Правила: 
//	1. если для одного делителя результат получен, а для другого нет, то первый меньше второго
//	2. Из двух результатов меньше тот, у которого меньше difference (погрешность частоты)
//
static int cmp(const void * va, const void * vb) {
	const CalcResult * a = (const CalcResult *) va;
	const CalcResult * b = (const CalcResult *) vb;
	if (a->valid && ! b->valid) return -1;
	if (! a->valid && b->valid) return 1;
	if (! a->valid && ! b->valid) return 0;
	if (a->difference - b->difference < 0) return -1;
	if (a->difference - b->difference > 0) return 1;
	return 0;
}

////////////////////////////////////////////////////////////////||||||
//
//	расчёт для пяти делителей, сотрировка и вывод результатов в Serial
//
void CalculateParameters(const double frequency) {
	CalcResult results [TOTAL_PRESCALERS] = {
		CalcResult(1), CalcResult(8), CalcResult(32), CalcResult(64), CalcResult(128), CalcResult(256), CalcResult(1024) 
	};
	for (int8_t i = 0; i < TOTAL_PRESCALERS; i++) results[i].Calculate(frequency);
	qsort(results, TOTAL_PRESCALERS, sizeof(results[0]), cmp);
	for (int8_t i = 0; i < TOTAL_PRESCALERS; Serial << results[i++]);
}



////////////////////////////////////////////////////////////////||||||
//
//	инициализация
//
void setup() {
	Serial.begin(115200);
	Serial.setTimeout(LONG_MAX);	
}

////////////////////////////////////////////////////////////////||||||
//
//	На каждом шаге производим расчёт для одного значения частоты
//
void loop() {
	Serial << "\nEnter desired frequency in Hz\n(use decimal point for fractions. I.e. 0.12 means 0,12Hz):\n";
	const double frequency = Serial.parseFloat();
	Serial << "\nResults for frequency: " << frequency << "Hz\n";
	if (frequency < 0) {
		Serial << "ERROR: frequency cannot be negative.\n";
	} else if (frequency > fCPU / 2.0) {
		Serial << "ERROR: frequency should not be greater then " << fCPU / 2.0 << "Hz\n";
	} else {
		CalculateParameters(frequency);
	}
}

//	собственно. всё.

 

andriano
andriano аватар
Offline
Зарегистрирован: 20.06.2015

У меня два вопроса:

1.

ЕвгенийП пишет:
Если у Вас 16МГц Ардуино...

Т.е. если Ардуино 84МГц, то во всех приведенных оценках вместо 16 нужно подставить 84?

 

2. Собственно, вопрос о генерации сигнала с произвольной скважностью здесь поднимался, но окончательного решения я так и не узрел.

Правильно ли я понимаю, что для этого нужно:

- настроить таймер 1 на генерацию сигнала на ноге 9 или 10 (правильно ли я понял, что другое ноги недоступны?),

- написать два прерывания и одно из них посадить на передний фронт, а другое - на задний (спад). Кстати, вроде, прерывания доступны только на ногах 2 и 3, а на остальных следует использовать PCINT?

- в одном из этих прерываний заносить в регистр OCR1A длительность отрицательной части периода, а в другом - положительной.

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

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

andriano пишет:

Т.е. если Ардуино 84МГц, то во всех приведенных оценках вместо 16 нужно подставить 84?

См. строки 48-58 в коде, там написано.

andriano пишет:

на ноге 9 или 10 (правильно ли я понял, что другое ноги недоступны?),

Если речь о таймере, то каждый обслуживает по две ноги (чаще всего). Вот табличка таймеры - ноги

Пины 5 и 6: timer0
Пины 9 и 10: timer1
Пины 11 и 3: timer2
 
На Arduino Mega 6 таймеров, причём пятый обслуживает три ноги.
 
Пины 4 и 13: timer0
Пины 11 и 12: timer1
Пины 9 и 10: timer2
Pin 2, 3 и 5: timer 3
Pin 6, 7 и 8: timer 4
Pin 46, 45 и 44: timer 5
 
Что до всего остального, это статья о CTC-режиме работы таймера. В этом режиме генерируется меандр и управлять скважность штатными средствами нельзя. Для управления скважностью есть режимы PWM (аж два).

 

Задернюк Владимир
Offline
Зарегистрирован: 23.07.2016

При написании программы столкнулся с нарушением временных интервалов на Arduino pro micro. С помощью тестов с выводом тактов на светодиодик поучил следующее : всё нормально сразу после загрузки скетча или после сброса внешней кнопкой (встроенной у pro micro нет). Однако, если подать питание любым способом - от USB, 5В или 9В - все итервалы удлиняются ровно в 8 раз. Это касается и функции delay и прерываний по всем трём таймерам. По-видимому всё портит загрузчик, который что-то там своё делает при каждой подаче питания.

Вопрос такой : есть ли общий делитель тактовой частоты с 16 до 2-х МГц и есть ли возможность его отключать в Arduino IDE ?

Задернюк Владимир
Offline
Зарегистрирован: 23.07.2016

Разобрался. Делитель есть и он может быть установлен

#include <avr/pover/h>

и вставить в setup​ команду

clock_prescale_set(clock_div_1);​

И никаких перепрошивок bootloader на родной от SparkFun

 

Pyotr
Offline
Зарегистрирован: 12.03.2014

Как ещё можно поморгать светиком без delay и без millis.

Используем TIMER1. Смотрим его настройки по умолчанию. Для этого загружаем скетч ниже.
 

void setup() { 
  Serial.begin(9600); 
  Serial.print("TCCR1A = ");  Serial.println(TCCR1A, BIN); 
  Serial.print("TCCR1B = ");  Serial.println(TCCR1B, BIN); 
  Serial.print("TIMSK1 = ");  Serial.println(TIMSK1, BIN); 
}
void loop(){}

На мониторе видим
TCCR1A = b1
TCCR1B = b11
TIMSK1 = 0
По даташиту или по картинке ниже смотрим как настроен Таймер1.

b1 - это двоичное представление числа. В монитор "b" не выводится. Это для наглядности/понятности/уточнения..
b1 или В1 равно  "00000001". Незначащие нули не печатаются.

Вот скетч, который выводит значения указанных регистров со всеми нулями.
Также показано как получаются значения, показанные на картинке синим цветом (полубайт = режим таймера) - "0000", "0001", "0010" и т.д.

byte WGM11_WGM10;
byte WGM13_WGM12;
byte WGM13_WGM12_WGM11_WGM10;
byte i = 7;
void setup() { 
  Serial.begin(9600); 
  Serial.print("TCCR1A = "); 
  while(i && !(TCCR1A & (1<<(i--)))){
    Serial.print('0');
  }
  Serial.println(TCCR1A, BIN);
  
  i = 7;
  Serial.print("TCCR1B = ");
  while(i && !(TCCR1B & (1<<(i--)))){
    Serial.print('0');
  }
  Serial.println(TCCR1B, BIN);
  
  i = 7;
  Serial.print("TIMSK1 = ");  
  while(i && !(TIMSK1 & (1<<(i--)))){
    Serial.print('0');
  }
  Serial.println(TIMSK1, BIN); 
  WGM11_WGM10 = TCCR1A & B00000011;//b00000011=b11
  WGM13_WGM12 = TCCR1B & B00011000;//b00011000=b11000
  WGM13_WGM12_WGM11_WGM10 = (WGM13_WGM12>>1) | WGM11_WGM10;
  i = 3;
  Serial.print("WGM13_WGM12_WGM11_WGM10 = ");
  while(i && !(WGM13_WGM12_WGM11_WGM10 & (1<<(i--)))){
    Serial.print('0');
  }
  Serial.println(WGM13_WGM12_WGM11_WGM10, BIN); 
}
void loop(){}

В регистре TCCR1A нас интересует бит_0 и бит_1 (первый и второй, если по порядку считать). Бит_0 установлен, а бит_1 сброшен.
В регистре TCCR1В нас интересует бит_3 и бит_4. Их значения=0 - сброшены. 
Если записать значения битов WGM13, WGM12, WGM11, WGM10 в строчку, получим "0001", что и видно на картинке.

Предделитель 64, режим PWM to 255
Таймер тикает каждые 4 мкс и его переполнение наступит через 4*65536=262144 мкс = 0.26 сек. Это нам мало, поэтому ставим предделитель 1024. Теперь будет тикать через 64мкс. и переполнение через 4 сек(4194304мкс).
Счётный  регистр TCNT1 можно использовать также как функцию millis().

Наш блинк.
 

const byte ledPin = 13;
word prevTCNT1;
word timeBlink = 1000;//в мсек  (максимум 4194ms)
word totalTic;//15625 ==> 1000ms;  65535; ==> 4194ms максимум

void setup() { 
  Serial.begin(9600); 
  pinMode(ledPin, OUTPUT);
  TCCR1A = 0;
  TCCR1B = B101;//предделитель 1024; 1 тик = 64 мкс
  unsigned long howMuchTic = 0;        //расчитаем
  howMuchTic = 1000UL * timeBlink / 64;//сколько тиков в 1000 мсек
  // Serial.println(howMuchTic); 
  totalTic = howMuchTic;
  //Serial.println(totalTic);
  TCNT1 = 0; 
}

void loop() {
  static boolean state = 1;
  if(TCNT1 - prevTCNT1 >= totalTic){
    prevTCNT1 = TCNT1;
    digitalWrite(ledPin, state); 
    state = !state;
    Serial.println(millis()); 
  }
}

 

arduinec
Offline
Зарегистрирован: 01.09.2015
Sync
Offline
Зарегистрирован: 12.01.2016

ЕвгенийП пишет:

...

Если надо, я могу показать пример, но надо учитывать - таймеры товар дефицитный. Их всего три и один из них занят средой, если его обидеть, то перестанет работать millis и всё, что с нею связано. Другие частенько занимаются библиотеками и на них живут ШИМ и tone().  Так что тут надо осторожно, чтобы с другими кусками программы не подраться

...

Если можно. Хочется все таймеры притащить в один, который просто будет будет увеличивать счётчики каждую мс. А счётчики уже будут вызывать условия и сбрасываться в разных местах. Точнее уже сделал, но сам таймер сделан через задницу.

  if (millis() - previousMillis > 1){
    previousMillis = millis();
  }

Для моих целей вполне достаточно, но неправильно это. previousMillis естественно long, но всёравно не нравится, что таймер не "вечный". Попробвал через таймер воч дога сделать, всё работает, но там 1 мс не сделаешь.

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

Sync пишет:

  if (millis() - previousMillis > 1){

    previousMillis = millis();
  }

Для моих целей вполне достаточно, но неправильно это. previousMillis естественно long, но всёравно не нравится, что таймер не "вечный". 

Почему не вечный? Что с ним не так? Точность у него будет зависеть от времени работы loop, это да. Если loop не очень быстрый, то интервал может плавать. А в остальном проблемы не вижу. Что Вы понимаете по "невечностью"?

Sync
Offline
Зарегистрирован: 12.01.2016

Я имел ввиду, что со временем (пусть и очень большим) произойдёт переполнение previousMillis. Разве нет? Да и сам millis перезапустится.

С таймероми и запуском уже сам разобрался. И такое пожелание, что ли. Наткнулся у dimax в комментарии к одному примеру на отсылку на страницу даташита. Очень удобно, теперь там, где не особо понимаю или нужны "табличные" данные - в комментариях оставляю ссылку куда смотреть. А то читал-читал Вашу статью, в общем понимаю суть, но запись байт или 1<<COM1A0

в ступор вгоняет, всё равно чтоб понять, что там писать без даташита не обойтись.

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

Sync пишет:

Я имел ввиду, что со временем (пусть и очень большим) произойдёт переполнение previousMillis. Разве нет? Да и сам millis перезапустится.

И что? На данный код это не окажет никакого влияния - никаких сбоев в замере интервалов не будет, не беспокойтесь. Всё там нормально - такой код, он "вечный" :)

Sync
Offline
Зарегистрирован: 12.01.2016

Вы меня успокоили. Правда я не совсем (совсем не) понимаю почему сбоя не будет, ну да ладно. Может когда нибудь дойду до этого.

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

Sync пишет:

Вы меня успокоили. Правда я не совсем (совсем не) понимаю почему сбоя не будет, ну да ладно. Может когда нибудь дойду до этого.

Погуглите по словам "арифметика в дполнительно коде" и изучите.

Кстати, если написать не так, как у Вас, а вот так:

if (oldMillis + Interval >= millis()) ...

то проблемы будут.

Но у Вас там было правильно написано - не беспокойтесь.

Pyotr
Offline
Зарегистрирован: 12.03.2014

Sync пишет:

Вы меня успокоили. Правда я не совсем (совсем не) понимаю почему сбоя не будет, ну да ладно. Может когда нибудь дойду до этого.

Для простоты понимания возьмём тип byte - 8 бит.
byte a;
Писать в переменную а можно любое число, хоть миллиард, но запишется только остаток от деления на 256.
Другими словами: 
b любое целое = 10
а = b;
а = b + 256*n;
а = b%256;
а = b & B11111111;
a = b & 0xFF;
 Все записи равнозначны и в результате а = 10;
Поэтому, когда мы вычитаем из меньшего числа большее,
например 10-20, то это можно записать как
(10+256) - 20 если тип byte
(10+65536) - 20   для unsigned int

Можно представить, что уменьшаемое увеличивается до бесконечности, а мы работаем с его остатком от деления на 256 или 65536 или  4,294,967,296

 

Sync
Offline
Зарегистрирован: 12.01.2016

ЕвгенийП пишет:

Точность у него будет зависеть от времени работы loop, это да. Если loop не очень быстрый, то интервал может плавать.

Аналогичным таймером, только для micros у меня мерялась скорость (время между импульсами). Действительно плавало и сильно с определённой переодичностью. Переделал под эти цели timer1 - встала как вкопанная. Только почему-то не могу понять с кратностями. Ставлю частоту 1 МГц, выполнение остальных функций вообще глохнет. Ставлю 100 кГц - и пропорционально (в 10 раз) увеличиваю коэффициент в формуле расчёта, и на выходе при том же входном сигнале получаю другие числа. Самое инетересное, что изменение не в 10 раз, а раза в 2. Ладно, завтра буду разбираться. И видимо тогда же появятся новые вопросы.

ЕвгенийП пишет:

Погуглите по словам "арифметика в дполнительно коде" и изучите.

Спасибо, почитаю.

Pyotr пишет:

Для простоты понимания ..

Спасибо, весьма наглядно и понятно. Всё стало яснее.

Baks
Baks аватар
Offline
Зарегистрирован: 11.01.2016

Господа форумчане, вот пришла одна идея как обойтись без делай  и без милис и без таймера ))) типа самый умный :)

вот накидал скетч

#include <Arduino.h>
void setup() {
  // put your setup code here, to run once:
  Serial.begin(9600);
}
unsigned long  i = 0;
unsigned long mi = 0;
unsigned long y = 0;
void loop() {
  // put your main code here, to run repeatedly:
  i++;
  y++;
  if (millis() != mi)
  {
  mi = millis();
  i=0;
    Serial.print('mi =');
    Serial.println(mi);
	    Serial.print('i =');
	    Serial.println(i);
		 Serial.println(  );
  }

  if (y > 25000 )// точно не знаю но к примеру 1 минута = 25000 тактов процессора
  {
  y = 0;
  	    Serial.println(y);
  	    Serial.println( ' 1 Minuta END'  );
  }

}

смысл в чем, проц крутится со стабильной скоростью, если завести переменную и в теле loop()  и с каждым тактом увеличивать ее  то за 1 сек времени переменка увеличится, следовательно можно отслеживать увеличение этой переменки  и при входе в условие переменную обнулить

Что скажите?

Клапауций 234
Offline
Зарегистрирован: 24.10.2016

Baks пишет:

проц крутится со стабильной скоростью, если завести переменную и в теле loop()

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

lohmag
Offline
Зарегистрирован: 14.10.2016

Правильно ли я понимаю, что т.к. пины 9 и 10 управляются одним таймером, то выдвать разный шим сигнал на них нельзя? И получается  если таймера всего три, то можно только на 3 пинах из 6 выдавать шим сигналы с разной частотой? Как я понял адреса регистров оставшихся двух таймеров TCCR0A/B  для PIN 5,6 и TCCR2A/B для 3,11. Причем про 0 написано, что он еще используется для системного времени, это значит, что его лучше не трогать, иначе отвалиться что нибудь может?

alexbmd
Offline
Зарегистрирован: 15.01.2016
TCCR1A = 0x40; 
    TCCR1B = 0x09;  

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