Некорректно отрабатывает таймер.

Discover0708
Offline
Зарегистрирован: 20.09.2020

Доброго времени суток. Помогите найти ошибку. Задача у меня такая. Есть датчик, который производит сигнал около 1500 Гц и мне этот сигнал нужно конвертировать с понижающим коэффициентом 9.35, т.е он должен уменьшить частоту в 9.35 раза. Имитируя датчик, подаю сигнал 1500 Гц, но на выходе получаю 60 Гц, если на входе устанавливаю до 300 ГЦ, то передаточное отношение, более менее совпадает...Причем , если в таймере жестко указать величину итерации, сигнал получается расчетный.

Вот мой код:

uint32_t tmr,src;
bool state;


void setup() {
  pinMode(5, OUTPUT);
  pinMode(A0, INPUT);
}

void loop() {
  src = pulseInLong(A0,LOW)/9;
  if (micros() - tmr >= src) { 
  tmr += src;
  state = !state;
  digitalWrite(5, state);
   }
}

 

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

Может я уже слабо соображаю сегодня, но, как минимум: зачем длительность импульса делить на 9, если частоту нужно уменьшить на 9,35?

"Более-менее" совпадает - это случайность.

И таймер тут уж точно невиновен.

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

Про скважность сигнала на входе и выходе забыли добавить.

Discover0708
Offline
Зарегистрирован: 20.09.2020

Делитель я произвольно поставил целочисленный. На самом деле, если частоту уменьшать , то период нужно увеличивать.т.е. умножать на коэффициент.
Скважность 50%. Выводил переменную "src " в порт - измерения и расчет производятся правильно. Какой-то ступор с digitalWrite, либо переменная src не должна быть произвольной....например, как delayMicroseconds только до 16383.
для проверки использую осциллограф.

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

Иногда потрясает глубина невежества. Браться за задачу не изучив исходные данные оборудования, а если изучил, то не применить для простейших прикидочных расчётов возможности применения - вызывает только желание сильно поглумиться. Таймер работает только с целыми числами. Если нужна какая то дробная величина (9.35), то надо сначала перевести всё в целый диапазон (935) а потом посмотреть, сможет ли таймер, тактируемый от кварца и имеющий дискретный шаг изменения выходной частоты как то вписаться в поставленную задачу.

Discover0708
Offline
Зарегистрирован: 20.09.2020

nik182 пишет:
Иногда потрясает глубина невежества. Браться за задачу не изучив исходные данные оборудования, а если изучил, то не применить для простейших прикидочных расчётов возможности применения - вызывает только желание сильно поглумиться. Таймер работает только с целыми числами. Если нужна какая то дробная величина (9.35), то надо сначала перевести всё в целый диапазон (935) а потом посмотреть, сможет ли таймер, тактируемый от кварца и имеющий дискретный шаг изменения выходной частоты как то вписаться в поставленную задачу.

Мне кажется, если мы работаем с микросекундами, то  дроби можно вообще не вводить...
 Почему ниже приведенный код работает, а тот который в первом посту нет?


uint32_t tmr,src;
bool state;


void setup() {
  pinMode(A4, OUTPUT);
  pinMode(5, INPUT);
}

void loop() {
  //src = pulseInLong(A0,LOW);
  src =438;
  if (micros() - tmr >= src) { 
  tmr += src;
  state = !state;
  digitalWrite(A4, state);
   }
}

 

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

Посмотрите внутрь функции pulseInLong - как она производит замер и подумайте - может ли остальной код выполняться быстрее, чем она сама.

Discover0708
Offline
Зарегистрирован: 20.09.2020

Я поставил искуственную задержку(  delayMicroseconds(500)), вместо pulseInLong.
И код сразу перестал работать. Как заставить задачи выполнятся параллельно?
Как заставить генерировать выход, пока ведется измерение?

Discover0708
Offline
Зарегистрирован: 20.09.2020

sadman41 пишет:
Посмотрите внутрь функции pulseInLong - как она производит замер и подумайте - может ли остальной код выполняться быстрее, чем она сама.

 

Представляете, как раз об этом подумал. Спасибо.

Если делать на прерываниях, то код наверное будет тоже тормозить, т.к входная частота  до 1500 Гц.
 

b707
Онлайн
Зарегистрирован: 26.05.2017

Discover0708 пишет:

Если делать на прерываниях, то код наверное будет тоже тормозить, т.к входная частота  до 1500 Гц.
 

откуда такой странный вывод? Это зависит от того, как напишете. Но вообще ардуина на прерываниях способна нормально измерять частоты в десятки и сотни КГц, а у вас всего-то...

Discover0708 пишет:

например, как delayMicroseconds только до 16383.

это что за магическое число такое? :)))

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

Таймер это аппаратное устройство. Его выход никак не зависит от того, что делает программа. Даже если программа зависнет таймер всё равно будет генерить выходной сигнал. Всё что ему надо - в регистр управления записать параметр деления. Это единицы микросекунд. Всё остальное время можно отдать на измерение входной частоты и расчёту делителя для таймера. Если измерение сделать на прерываниях, то процессор вообще можно на долго спать отпрвлять. И никаких тормозов.

wdrakula
wdrakula аватар
Offline
Зарегистрирован: 15.03.2016

Discover0708 пишет:

Я поставил искуственную задержку(  delayMicroseconds(500)), вместо pulseInLong.
И код сразу перестал работать. Как заставить задачи выполнятся параллельно?
Как заставить генерировать выход, пока ведется измерение?

На таймере1 измеряешь частоту через инпут-капча стандартным способом, таймер2 генерит частоту.  loop() - пустой, совсем пустой. Можно в нем чо-нить  умное написать. Слово какое-никакое. ;))

(я про 328 контроллер, ессно)

Discover0708
Offline
Зарегистрирован: 20.09.2020

nik182 пишет:
Таймер это аппаратное устройство. Его выход никак не зависит от того, что делает программа. Даже если программа зависнет таймер всё равно будет генерить выходной сигнал. Всё что ему надо - в регистр управления записать параметр деления. Это единицы микросекунд. Всё остальное время можно отдать на измерение входной частоты и расчёту делителя для таймера. Если измерение сделать на прерываниях, то процессор вообще можно на долго спать отпрвлять. И никаких тормозов.

Извините за неточность. таймером я назвал циклическое условие, например вот это.
 

if (micros() - tmr >=src ) { 
  tmr += src;
  state = !state;
  digitalWrite(A4, state);
    }

 

Discover0708
Offline
Зарегистрирован: 20.09.2020

Вот такой код у меня получился.
Вроде работает....)

volatile uint16_t counter=0;
uint32_t tmr2,tmr,src;
bool state;
const int k=10;

void setup() {
  pinMode(A4, OUTPUT);
  pinMode(3, INPUT);
  attachInterrupt(1,isr,FALLING);
  
}
void isr(){
  counter++;
}
void loop() {
  if (millis() - tmr2 >=1000) { 
  tmr2 += 1000;
  src=k*((1000000/counter)/2);  // вычисление длительности импульса иперемножение на коэфф.
  counter=0;
  }
 
  
if (micros() - tmr >=src ) { 
  tmr += src;
  state = !state;
  digitalWrite(A4, state);
    }
    
}

Можно ли к переменной uint32_t применять коэффициент с десятичной точкой, например 9.32?

Discover0708
Offline
Зарегистрирован: 20.09.2020

wdrakula пишет:

Discover0708 пишет:

Я поставил искуственную задержку(  delayMicroseconds(500)), вместо pulseInLong.
И код сразу перестал работать. Как заставить задачи выполнятся параллельно?
Как заставить генерировать выход, пока ведется измерение?

На таймере1 измеряешь частоту через инпут-капча стандартным способом, таймер2 генерит частоту.  loop() - пустой, совсем пустой. Можно в нем чо-нить  умное написать. Слово какое-никакое. ;))

(я про 328 контроллер, ессно)


Чувствую это еще лучше. Не могли бы Вы показать пример или направить почитать ресурс с требуемой инфой.

Upper
Offline
Зарегистрирован: 23.06.2020

Если стараться не уходить от упрощений Ардуино, то можно генерировать частоту при помощи tone();

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

SLKH
Offline
Зарегистрирован: 17.08.2015

Параметр 9,35 - ни о чем.

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

wdrakula
wdrakula аватар
Offline
Зарегистрирован: 15.03.2016

Discover0708 пишет:

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

Я дико извиняюсь, но читать нужно ДШ на контроллер. ;))

А пример - пожалуйста!

#include <avr/interrupt.h>
#include <avr/io.h>
#include <avr/interrupt.h>

volatile uint16_t lastICR = 0;
volatile uint16_t vPeriod = 0;

//для двухбайтовой переменной уже нужен атомарный геттер
uint16_t getPeriod() {
  uint16_t v;
  uint8_t s = SREG;
  cli();
  v = vPeriod;
  SREG = s;
  return v;
}

// дистиллированный частотомер. от лучших сомелье.
ISR (TIMER1_CAPT_vect) {
  vPeriod = ICR1 - lastICR;
  lastICR =  ICR1;
}


void setup()
{
  pinMode(8, INPUT);


  TCCR1A = 0b00001100;//COM1A1:COM1A0:COM1B1:COM1B0:X:X:WGM11:WGM10
  TCCR1B = 0b11000100;//ICNC1:ICES1:X:WGM13:WGM12:CS12:CS11:CS10
  // Выводы отключены, режим 0 (просто счет), частота 16МГц/256, IC - rise, IC noise canc - yes
  TIMSK1 = 0b00100000; //x:x:ICIE:x:OCIEB:OCIEA:TOIE
  // прерывания по ICR

}


void loop(void) {
  static uint32_t om = millis();
  uint32_t nm = millis();

 
  // так как делитель таймера = 256, каждая едичка period равна 16 мкс
  uint16_t period = getPeriod();

  //Хорошо бы на время измерений запрещать прерывания Таймера0. 
  //Потом поймешь - заччем это нужно ;))) Когда глюки пойдут


  if ( nm - om > 1000) {
    //Раз в 1000- мс что-нить делаем.
    //можно менять параметры генерации на таймере 2,
    //поставь его в СТС Top=OCRA (Mode=2) и меняй OCRA с OC2B (pin D3 Nano) будешь снимать сингал
    //только правильно пересчитывай мкс в тики таймера и задай правильный делитель
    //для 1500/7 - примерно 200 Гц - делитель самый большой нужен - 1024. 
    //период станешь задавать с точностью 128 мкс, если я не наврал ;))
  }

//или просто такую низкою частоту генери через микрос() как желаешь


}

 

Discover0708
Offline
Зарегистрирован: 20.09.2020

wdrakula пишет:

Discover0708 пишет:

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

Я дико извиняюсь, но читать нужно ДШ на контроллер. ;))

А пример - пожалуйста!

#include <avr/interrupt.h>
#include <avr/io.h>
#include <avr/interrupt.h>

volatile uint16_t lastICR = 0;
volatile uint16_t vPeriod = 0;

//для двухбайтовой переменной уже нужен атомарный геттер
uint16_t getPeriod() {
  uint16_t v;
  uint8_t s = SREG;
  cli();
  v = vPeriod;
  SREG = s;
  return v;
}

// дистиллированный частотомер. от лучших сомелье.
ISR (TIMER1_CAPT_vect) {
  vPeriod = ICR1 - lastICR;
  lastICR =  ICR1;
}


void setup()
{
  pinMode(8, INPUT);


  TCCR1A = 0b00001100;//COM1A1:COM1A0:COM1B1:COM1B0:X:X:WGM11:WGM10
  TCCR1B = 0b11000100;//ICNC1:ICES1:X:WGM13:WGM12:CS12:CS11:CS10
  // Выводы отключены, режим 0 (просто счет), частота 16МГц/256, IC - rise, IC noise canc - yes
  TIMSK1 = 0b00100000; //x:x:ICIE:x:OCIEB:OCIEA:TOIE
  // прерывания по ICR

}


void loop(void) {
  static uint32_t om = millis();
  uint32_t nm = millis();

 
  // так как делитель таймера = 256, каждая едичка period равна 16 мкс
  uint16_t period = getPeriod();

  //Хорошо бы на время измерений запрещать прерывания Таймера0. 
  //Потом поймешь - заччем это нужно ;))) Когда глюки пойдут


  if ( nm - om > 1000) {
    //Раз в 1000- мс что-нить делаем.
    //можно менять параметры генерации на таймере 2,
    //поставь его в СТС Top=OCRA (Mode=2) и меняй OCRA с OC2B (pin D3 Nano) будешь снимать сингал
    //только правильно пересчитывай мкс в тики таймера и задай правильный делитель
    //для 1500/7 - примерно 200 Гц - делитель самый большой нужен - 1024. 
    //период станешь задавать с точностью 128 мкс, если я не наврал ;))
  }

//или просто такую низкою частоту генери через микрос() как желаешь


}

 

Целый день потратил на поиск ошибки. время упущено, оставлю пока, как есть...
 .Всем откликнувшимся спасибо. А тем кто подсказывает умные мысли - вдвойне!

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

Discover0708 пишет:

nik182 пишет:
Таймер это аппаратное устройство. Его выход никак не зависит от того, что делает программа. Даже если программа зависнет таймер всё равно будет генерить выходной сигнал. Всё что ему надо - в регистр управления записать параметр деления. Это единицы микросекунд. Всё остальное время можно отдать на измерение входной частоты и расчёту делителя для таймера. Если измерение сделать на прерываниях, то процессор вообще можно на долго спать отпрвлять. И никаких тормозов.

Извините за неточность. таймером я назвал циклическое условие, например вот это.
 

if (micros() - tmr >=src ) { 
  tmr += src;
  state = !state;
  digitalWrite(A4, state);
    }

 


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

Discover0708
Offline
Зарегистрирован: 20.09.2020

nik182 пишет:
Discover0708 пишет:

nik182 пишет:
Таймер это аппаратное устройство. Его выход никак не зависит от того, что делает программа. Даже если программа зависнет таймер всё равно будет генерить выходной сигнал. Всё что ему надо - в регистр управления записать параметр деления. Это единицы микросекунд. Всё остальное время можно отдать на измерение входной частоты и расчёту делителя для таймера. Если измерение сделать на прерываниях, то процессор вообще можно на долго спать отпрвлять. И никаких тормозов.

Извините за неточность. таймером я назвал циклическое условие, например вот это.
 

if (micros() - tmr >=src ) { 
  tmr += src;
  state = !state;
  digitalWrite(A4, state);
    }

 

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

Наверное, Вы правы!  Но для данной схемы - оно работает! И перемножение дробного коэффициента на uint32_t работает.  Входной сигнал достаточно инерционный и имеет диапазон от 0 до 1500 Гц (скваж 50). Выходной сигнал должен быть от 0 до 169 Гц (скваж 50). Я тестировал с  входным сигналом даже до 5 кГц - отрабатывает четко.
Если у Вас есть другое решение и Вам не жалко, то покажите как бы Вы это решали?

Описание проекта такое(на автомобиле испорчен механизм датчика скорости - не показывает спидометр).
Система ABS оснащена индуктивным датчиком скорости, который на максимальной скорости генерирует синус 1500Гц. сигнал будет браться оттуда.

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

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