Умножение частоты импульсов в 2.6 раза

steel780
Offline
Зарегистрирован: 25.01.2013
Возникла задача, применение автомобильное. Есть датчик скорости, есть блок управления от более свежей акпп, подробнее тут: http://www.drive2.ru/l/7396416/ . Необходимо увеличение частоты входных импульсов в 2.6 раза. Сигнал естественно меандр. Мысль выдала вот такой скетч:
 
long time=0;
void setup() {
}

void loop() {
 
  time=pulseIn(A2, HIGH,35000);
 
      if(time==0||time>30000) 
      {
            noTone(13);
      }
      
      else if(time<20500)
      {
      tone(A3,735000/time);
      }

}

 

Есть определенные недостатки, например функция tone выдает минимум 30Гц, мне необходимо от 16Гц, в выходном сигнале периодически встречаются провалы по частоте. Не часто, и не сильные, но есть. Значение 30000 вместе с цифрой 20500 образует гистерезис (частота датчика имеет некоторую неравномерность в начале. Из-за этого происходит чередование tone и noTone с большой скоростью). Прошу у Вас совета, как можно решить такую задачу? Приоретет отдается выходному сигналу, если несколько периодов входных импульсов будет припущено, это не принципиально.

Araris
Araris аватар
Offline
Зарегистрирован: 09.11.2012

Загляните в исходники tone(), разберитесь и сделайте на их базе свою функцию.

steel780
Offline
Зарегистрирован: 25.01.2013

Araris пишет:

Загляните в исходники tone(), разберитесь и сделайте на их базе свою функцию.

мне пока соображалка не позволяет)

Araris
Araris аватар
Offline
Зарегистрирован: 09.11.2012

Просто попробуйте.

Logik
Offline
Зарегистрирован: 05.08.2014

Я бы не стал с tone связыватся ни в каком виде. Делал бы дето так:

1. Считаем импульсы с входа по прерываниям. В обработчике прерывания инкрементим счетчик на 2.6. Подробней - ищи подсчет импульсов (фронтов) на входе по прерыванию и операции с фиксированной запятой

2. В лупе определяем полупериод выходных типа так если счетчик больше порога - полупериод сокращаем но не ниже некоторого допустимого минимального порога, если счетчик менше - полупериод увеличиваем, но не более максимального порога. Подробней  - изучить основы АСУ.

3 В лупе же проверяем истечение временного интервала полупериода, если он истек - меняем выходной на противоположный. Подробней - блинк без делея.

А вобще задачка интересная очень, комплексная, решите сами - можете считать себя программистом 8)

 

Denis_1704
Offline
Зарегистрирован: 28.05.2014

я бы написал на микрос по прицепу:

время = сигнал(время смены сигнала в микросекундак) * 2.6

а дальше генериш сигнал по времени 

steel780
Offline
Зарегистрирован: 25.01.2013

Первая версия у меня и была с прерыванием по входу, но при этом была сильная неравномерность выходных импульсов, т.к. выходная частота может достигать нескольких кгц, и прерывания тормозят луп. Пойду посмотрю на tone изнутри. В первом посте есть опечатка в коде, вместо noTone(13), должно быть noTone(A3). Переносил код с uno на micro, не заметил.

Logik
Offline
Зарегистрирован: 05.08.2014

steel780 пишет:

 прерывания тормозят луп.

8о Это что же в них надо было писать?!

trembo
trembo аватар
Offline
Зарегистрирован: 08.04.2011
dimax
dimax аватар
Offline
Зарегистрирован: 25.12.2013

steel780, я бы сделал на 2-х таймерах. От расчётов частоты и измерения микросов лучше отказаться, у МК есть гораздо более удобный параметр -количество тактов процессора. Один таймер считает количество тактов между импульсами, и между циклами в своём прерывании программирует второй таймер, который уже выдаёт чистый сигнал. Функция loop() тут даже не потребуется.

Logik
Offline
Зарегистрирован: 05.08.2014

dimax пишет:

steel780, я бы сделал на 2-х таймерах. От расчётов частоты и измерения микросов лучше отказаться, у МК есть гораздо более удобный параметр -количество тактов процессора. Один таймер считает количество тактов между импульсами, и между циклами в своём прерывании программирует второй таймер, который уже выдаёт чистый сигнал. Функция loop() тут даже не потребуется.

Не получится сделать стабильный меандр  на выходе. Коэффициент умножения не целый. И на один входной импульс прийдется выдавать 2 или 3 выходных в заданом соотношении, на выходе частота будет плавать.

dimax
dimax аватар
Offline
Зарегистрирован: 25.12.2013

Logik, почему? по моей задумке должно быть стабильно.  Предположим насчитал счётчик 10000 тактов, вывалился в прерывание, сказал второму таймеру что-б тот досчитал до 10000/2.6  А второй таймер настроить на перечитку регистра OCR только в конце цикла (TOP). Таким образом пока не поменяется входная частота выходная будет стабильна, т.к. результат 10000/2.6 будет всё время одинаковым. А как только поменяется частота, то таймер сначала закончит старый период, потом перечитает регистр, и уже создаст новый, т.е. максимально корректно отреагирует с задержкой всего лишь в пол-такта входного сигнала.

Logik
Offline
Зарегистрирован: 05.08.2014

Смотрим подробней. Пусть на входе стабильная частота и насчситали за импульс 10000 тактов, сказал формировать импульсы длительностью 10000/2.6 =3846 такта. За время следующего подсчета входного, т.е за 10000 тактов на выходе сформируются 2 выходных длительностью 3846+3846=7692. За оставшиеся 10000-7692=2308 на выходе будет еще 3846/2=1923 один уровень 3-го импульса и 2308-1923=385 второй уровен. Затем закончится 10000 тактов и все повторится. 3-й импульс - явно не меандр.  

Гадит дробность коэффициента. Можна попробовать умножать на 26 (или 13), а затем выходной делить уже на 10 (или 5). Тоесть на оди входной генерировать таймером 13 выходных, за ним "ставить" делитель на 5 выдающий на выход результат.

dimax
dimax аватар
Offline
Зарегистрирован: 25.12.2013

Logik, вы наверное не так меня поняли.. Или я что-то не понимаю, что то упускаю? Первый таймер в режиме счётчика только программирует длительность работы второго таймера-генератора, кроме как передать число 3846 он больше ничего не может. Второй таймер отсчитает 3846 тиков , инвертирует выход, и опять начнёт считать 3846 тиков. Сбить равномерность работы 2 таймера никак не возможно, он в независимости от чего бы то нибыло отсчитает положенное количество тактов. Я бы мог даже набрасать скетч, но вряд ли набор регистров таймера будет о чём-то наглядно свидетельствовать..

Logik
Offline
Зарегистрирован: 05.08.2014

Сбивать работу второго таймера будет первый. Сбивать каждые 10000 тактов, устанавливая новую длительность. Возможно вы предполагаете не обнулять текущее состояние таймера при установку новой длительности? 

dimax
dimax аватар
Offline
Зарегистрирован: 25.12.2013

Logik, зачем обнулять? Второй таймер не будет сбиваться. Он отсчитает положенное, потом перечитает регистр OCR,  и в следущий раз досчитает до нового значения, если оно изменилось.

Logik
Offline
Зарегистрирован: 05.08.2014

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

Смотрим поведение при изменении длительности на входе. Там вообще печалька. Допусти первый импульс 10000 тиков, второй в десять раз больше- 100000, потом импульсов нет. Типа машина тормозила и стала. Во время измерения первого импульса ничего не выводится (наверно. или выводится?). за время измерения второго импульса выводится 26 импульсов по 3846 тиков, затем выводится  некоторое количество импульсов, пока мы каким то образом не поймем что стоим. Т.е. мы стоим а на акпп идет сигнал что мы едем. За время двух импульсов на входе, т.е. 110000 тиков, на выходе было не менее 26 импульсов. Это явно не увеличение частотыв 2,6 раза.

Ещё думаю не лишнее.

steel780!!!! Вмешательство в конструкцию и работу основных узлов автомобиля не допустимо! Это угрожает жизни Вам, родным, близким и далеким людям. Факт того, что я обсуждаю техническую сторону вопроса не означает что я поддерживаю саму идею доработки авто. Любительская разработка не обеспечит эксплуатационную надежность во всем диапазоне условий. Подумайте что будет если отказ произойдет при выходе на обгон.

steel780
Offline
Зарегистрирован: 25.01.2013

Logik пишет:

steel780!!!! Вмешательство в конструкцию и работу основных узлов автомобиля не допустимо! Это угрожает жизни Вам, родным, близким и далеким людям. Факт того, что я обсуждаю техническую сторону вопроса не означает что я поддерживаю саму идею доработки авто. Любительская разработка не обеспечит эксплуатационную надежность во всем диапазоне условий. Подумайте что будет если отказ произойдет при выходе на обгон.

 

Да вроде в курсе) Ничего страшного не делаю) Если будет отказ, произойдет толчек в коробке и отключиться управление 4вд. 

 

Меня в целом устраивает как работает скетч, за исключением вот этого:

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

dimax
dimax аватар
Offline
Зарегистрирован: 25.12.2013

Сделал набросок -работает. Но это упрощённая версия, для низких частот нужно дорабатывать. В этом виде считает временной отрезок не более 4мс, т.е. примерно от 125Герц и выше.  И ещё не делал отслеживание наличия сигнала, т.е. если его не будет то второй таймер всё равно будет генерить сигнал по последним данным.  Выходной сигнал отличный, посмотрел на осцилле. Код для леонардо/micro. Меня просто чисто из спортивного интереса заинтересовала задачка. )

void setup(){
pinMode (4,INPUT); // вход сигнала
pinMode (5,OUTPUT); // выход сигнала
TCCR1B = 0; TCCR1A = 0; TCNT1 = 0;
TIMSK1 = (1<<ICIE1);//создавать прерывание от сигнала на пине ICP1
TCCR1B = (1<<ICNC1)|(1<<ICES1)|(1<<CS10);//div 1 активный -высокий, шумоподавление включено.
TCCR3A=(1<<COM3A0)|(1<<WGM31)|(1<<WGM30); // OC3A toggle 
TCCR3B=(1<<WGM33)|(1<<WGM32)|(1<<CS30); //div 1 Mode15
}

ISR (TIMER1_CAPT_vect) { 
OCR3A = ICR1/5.2; TCNT1 = 0; // full period=(2*ICR1)*2.6
}

 void loop(){}
steel780
Offline
Зарегистрирован: 25.01.2013

Думаю есть смысл брать минимальную либо среднюю длительность импульса за например  10 периодов.

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

Logik
Offline
Зарегистрирован: 05.08.2014

Причин такого может быть много. От аппаратных до програмных. Для начала попробуйте в самом конце loop поставить goto на его начало. Мелоч, но может поможет.

dimax
dimax аватар
Offline
Зарегистрирован: 25.12.2013

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

steel780
Offline
Зарегистрирован: 25.01.2013

dimax пишет:

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

 

будем пробовать Ваш, но мне кажеться что tone это тот же самый таймер

dimax
dimax аватар
Offline
Зарегистрирован: 25.12.2013

steel780, мой нужно дорабатывать. В принципе сделать, что б счётчик понимал сигнал от 0 Герц относительно легко, но остальное уже муторно.

Я в своё время изучил алгоритм tone(), таймер конечно обычный, но у него код довольно примитивный, очень плохо подходит для линейной генерации частоты. Собссно это и понятно, сделан то он просто как пищалка на спикер. Кстати перекинуть его на 16-битный таймер, что-б он пищал на частотах менее 30герц очень легко, я уже как то писал

dimax
dimax аватар
Offline
Зарегистрирован: 25.12.2013

steel780, дописал скетч, теперь работает от 0 герц до нескольких килогерц, потом начинает дрожать фаза. Осталось как я понимаю только включить отслеживание наличия сигнала ) Нумерация пинов для леонардо/микро


volatile unsigned int icr_tic;
volatile unsigned int int_tic;
volatile unsigned long tic;
void setup(){
Serial.begin(9600);
pinMode (4,INPUT); // вход сигнала
pinMode (5,OUTPUT); // выход сигнала (PC6)
//настройка 16 бит таймера-счётчика 1 
TCCR1B = 0; TCCR1A = 0; TCNT1 = 0;
TIMSK1 = (1<<ICIE1)|(1<<TOIE1);//создавать прерывание от сигнала на пине ICP1
TCCR1B = (1<<ICNC1)|(1<<ICES1)|(1<<CS10);//div 1
//настройка 16 бит таймера-генератора 2
TCCR3A=0; TCCR3B=(1<<WGM32)|(1<<CS30); //div1 CTC
TIMSK3=1<<OCIE3A; //прерывание по переполнению OCR3A
}

ISR (TIMER1_CAPT_vect) { //прерывание захвата сигнала на входе ICP1
TCNT1 = 0; icr_tic=ICR1;
tic= ((uint32_t)int_tic<<16) | icr_tic; //подсчёт тиков
int_tic=0; }

ISR (TIMER1_OVF_vect) { int_tic++; } //прерывания счёта по переполнению uint
ISR (TIMER3_COMPA_vect) { //прерывание счёта тактов и управления выходом
static volatile unsigned long temp_ocr=tic/5.2; 
if (temp_ocr <= 65535) { OCR3A=temp_ocr;PORTC^=(1<<6);temp_ocr=tic/5.2; return;}
if (temp_ocr > 65535) {OCR3A=65535; temp_ocr=temp_ocr-65535;} 
}
  
void loop(){
}

 

steel780
Offline
Зарегистрирован: 25.01.2013

Огромное спасибо, но мне нужно немного времени, чтобы работаться в этом коде.

dimax
dimax аватар
Offline
Зарегистрирован: 25.12.2013

steel780, наверно сложно в чужом коде разбираться) Переменная tic содержит число тактов процессора за один период входящего сигнала. В переменной icr_tic лежит то-же самое, но при условии что тактов было не более, чем 65535. Если их было больше, то переменная int_tic записывает сколько раз счётчик переходил через максимум.  Довольно трудные строки 24-26. 16-битный таймер умеет считать только до 65535, поэтому если тактов было больше, то входя в прерывание эти строчки заставлят досчитать таймер до максимума , вычесть то, что уже оттикало, и когда пройдёт последний такт нога PC6 инвертируется. Фактически в коде два независимых устройства- частотомер и генератор, только им указывается частота не в герцах, а в тактах процессора. Если 16000000 /tic , то будет частота входного сигнала в герцах.

steel780
Offline
Зарегистрирован: 25.01.2013

Я имел ввиду скорее строки с 9й по 14ю :-)

steel780
Offline
Зарегистрирован: 25.01.2013

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

dimax
dimax аватар
Offline
Зарегистрирован: 25.12.2013

steel780, самое простое -попробовать присвоить в третьей строке скеча переменной tic какое-то стартовое значение, должно помочь. Только не больше, чем 65535*5.2 , иначе не попадёт под условие 25 строки, и сперва отстрочит какое-то количество импульсов на высокой частоте.

steel780
Offline
Зарегистрирован: 25.01.2013

Ближайшие у нулевой скорости оказалось значение 2200000:(

dimax
dimax аватар
Offline
Зарегистрирован: 25.12.2013

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

steel780
Offline
Зарегистрирован: 25.01.2013

Срабатывает думаю из-за того, что вход подтянут к +5, а с датчика идет минус. 

steel780
Offline
Зарегистрирован: 25.01.2013

2200000 хорошо помогло

dimax
dimax аватар
Offline
Зарегистрирован: 25.12.2013

steel780, вам удалось разобраться как всё это работает? В принципе скетч можно ещё совершенствовать. Например изначально я думал, что б выходной сигнал полность формировался после пол-такта входного. Но сделал после целого такта входного, так было проще.  Но качественно новый уровень возможен только на ардуино DUE , там 32-битные таймеры, отпадёт надобность вываливаться в прерывание по переполнению каждые 65535 тактов, и делать пересчёты, а значит резко вырастет качество сигнала.

steel780
Offline
Зарегистрирован: 25.01.2013

Меня смущают периодически вот такие моменты:

Может прерывания как-то тормозят друг друга?

dimax
dimax аватар
Offline
Зарегистрирован: 25.12.2013

steel780, прерывания конечно тормозят, но на выбросы они не способны. Они очень мелко гадят, в виде дрожания фазы. Возможно где-то ошибка, вопрос как вычислить откуда растут ноги. Если это глюк счётчика, значит иногда проскакивает tic размером сильно отличающийся от остальных. Нужно как-то логгировать все значения tic, что б потом проанализировать.. А возможно и генератор, в 25 строке не очень корректно всё сделано..

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

wss66
Offline
Зарегистрирован: 29.07.2017

Здравствуйте. Стоит таже задача что у ТС) Но в пограммировании полный ноль, если в адуиновском С еще как то то в оригинале или С++ не силен. Пишет ошибку на "TCCR3A=0; " я так понял что у меня UNO а код рассчитан под Леонардо. Это можно исправить?

steel780
Offline
Зарегистрирован: 25.01.2013

Uno не имеет 4го таймера

wss66
Offline
Зарегистрирован: 29.07.2017

Задействовано 2 таймера, если я правильно понял. Адреса нужно изменить, а еще в уно только 1 16 бит, остальные 2 по 8 бит.

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

wss66, набираете в гугле AVR таймеры и читаете статьи по аппаратным таймерам. А также даташит на конкретный микроконтроллер (ATmega328P у UNO), в котором расписана вся переферия и настройки через регистры.

wunderley
Offline
Зарегистрирован: 18.08.2017

Здравствуйте, не могу понять откуда в первом посте взялась строчка
tone(A3,735000/time);
Tone-понятно, A3 - выход, а вот как получили 735000?
Если частота равна 1/T, то так как у нас меандр, то T=2*time ( time длина импульса, как я понял) А так как нам нужно умножить частоту на 2,6 то получаем:
2,6/(2*time)=1,3*time
Если перевести в микросекунды:
1300000*time
Как топикстартер получил 735000? Мне нужен умножитель на 1,5 не могу разобраться в его коде.

dimax
dimax аватар
Offline
Зарегистрирован: 25.12.2013

wunderley, т.е. вариант , что т.с. накосячил с рассчётом вы даже не рассматривали? :)

wunderley
Offline
Зарегистрирован: 18.08.2017

Хотел убедиться, что я правильно рассуждаю;)

dnstkrv
Offline
Зарегистрирован: 22.08.2019

Сможете мне помочь? У меня та же цель, только в наличии ардуино надо и умножитель нужен на 3. Четвертый день уже плохо сплю

DetSimen
DetSimen аватар
Онлайн
Зарегистрирован: 25.01.2017

Положы " ардуино надо" в памойку и спи спокойно.

Green
Offline
Зарегистрирован: 01.10.2015

Смогу. htpicc@gmail.com. Уже который день кушать не могу.

amgame
Offline
Зарегистрирован: 09.09.2019

Доброй ночи.
dimax
Помогите переделать ваш код под NANO. Голову сломал с регистрами и таймерами, но очень интересно разобраться.
спасибо.

dimax
dimax аватар
Offline
Зарегистрирован: 25.12.2013

amgame,  мк atmega32u4 (т.е. леонардо/микро/промикро)  был специально выбран для этой программы, т.к. обладает необходимыми ресурсами.  Переделывать имеет смысл только на stm32, у которого ещё больше аппаратных возможностей для этой задачи.

amgame
Offline
Зарегистрирован: 09.09.2019

dimax пишет:

amgame,  мк atmega32u4 (т.е. леонардо/микро/промикро)  был специально выбран для этой программы, т.к. обладает необходимыми ресурсами.  Переделывать имеет смысл только на stm32, у которого ещё больше аппаратных возможностей для этой задачи.

Понял. Тогда вопрос в следующем нужен только делитель импульсов с коэффициентом 3.5. Диапазон частот 56-230Hz. Вот их же как то делают на Attiny. Поможите с кодом.?

dimax
dimax аватар
Offline
Зарегистрирован: 25.12.2013

amgame,  делить частоту проще чем умножать. К сожалению нет,  мне не интересно тратить на это своё время.