Некорректно измеряет частоту через прерывание, помогите

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

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

Дальше задача измерить частоту которая приходит с датчика скорости. Нашел код который меряет время между прерываниями при изменении уровня на ножке Д2. В целом код работает корректно, все меряет точно, мне нужно от 0 до 200гц. и на стрелку и порт выдает ± точное значение частоты, но!

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

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

Помогите пожалуйста решить проблему

//this is the maximum resolution of the PWM output.
const int maxAnalogRes = 255;

const int aircore1SinDirPin1 = 3;  //sin+ these two control the polarity to the “sine” coil
const int aircore1SinDirPin2 = 5;  //sin-
const int aircore1CosDirPin1 = 11; //cos+ these two control the polarity to the “cosine” coil
const int aircore1CosDirPin2 = 6;  //cos-

volatile unsigned long duration = 0;
volatile unsigned long lastMicros = 0;
volatile unsigned long frequency1;
volatile unsigned long frequency2;
volatile int frequency;

void setup()
{
Serial.begin(115200); // Инициализация порта, скорость 115200 бод [1, 3]

TCCR2B = TCCR2B & 0b11111000 | 0x01;
TCCR0B = TCCR0B & 0b11111000 | 0x01;
//Set the pins to OUTPUT
pinMode(aircore1SinDirPin1, OUTPUT);
pinMode(aircore1SinDirPin2, OUTPUT);
pinMode(aircore1CosDirPin1, OUTPUT);
pinMode(aircore1CosDirPin2, OUTPUT);

//прерывание по изменению сигнала на выводе 2
attachInterrupt(digitalPinToInterrupt(2), pulseCounter, RISING);

pinMode(2, INPUT_PULLUP);
}

void loop()
{
int val;
Serial.println(frequency);
if (duration > 0) {
val = map(frequency, 0, 200, -140, 300);
setMeterPosition(aircore1SinDirPin1, aircore1SinDirPin2, aircore1CosDirPin1, aircore1CosDirPin2, val);
}
delay(5000);
}

void pulseCounter() {

unsigned long currentMicros = millis();

duration = currentMicros - lastMicros;
frequency =  62500 / duration;
//frequency2 = frequency1*2;
//if (frequency >= frequency2 ) {
//  frequency = frequency/2;
//}
lastMicros = currentMicros;
//frequency1 = frequency;

}

//Код движения стелки логометра. работает идеально.
// setMeterPosition() - put it at the angle in radians
void setMeterPosition( int sinDirPin1, int sinDirPin2, int cosDirPin1, int cosDirPin2, float pos){
pos=pos/100;
// Calculate the voltage on the PWM pins based on the angle we want
float sinCoilValue = maxAnalogRessin(pos);
float cosCoilValue = maxAnalogRescos(pos);
int sinn=0;
int coss=0;
if (sinCoilValue<=0) {
sinn = 1;
}
if (cosCoilValue<=0) {
coss = 1;}
// take the absolute value because analogWrite doesn’t take negatives
sinCoilValue = abs(sinCoilValue);
cosCoilValue = abs(cosCoilValue);
// change the polarity of the coil depending on the sign of the voltage level
if (sinn==1) {
analogWrite(sinDirPin1, 0); //Change to LOW if using relays
analogWrite(sinDirPin2, sinCoilValue); //comment this line out if using relays
} else {
analogWrite(sinDirPin1, sinCoilValue); //Change to HIGH if using relays
analogWrite(sinDirPin2, 0); //comment this line out if using relays
}
if (coss==1) {
analogWrite(cosDirPin1, 0); //Change to LOW if using relays
analogWrite(cosDirPin2, cosCoilValue); //comment this line out if using relays
} else {
analogWrite(cosDirPin1, cosCoilValue); //Change to HIGH if using relays
analogWrite(cosDirPin2, 0); //comment this line out if using relays
}
}

Вот данные из порта. Меряет частоту 117 (реальная 120) , но периодически всплески до 236

236
117
117
117
117
117
236
117
117
117
117
117
117
117
117
236
117
117
236
117
117
117
117
117

Интересно, что при других реальных частотах - тоже будет 236 или именно в два раза больше реального?

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

Просто ради теста - если выкинуть из ЛУП вывод на катушки спидометра (строки 38-39) - исчезнут выбросы в 2 раза или нет?

МММ , дорогой! Это у ТС прерывание попадает на момент срабатывания прерывания таймера. Пока таймер не отключать так будет.
Плюс добавить рекурсивный фильтр плюс фильтр выбросов с окном, к примеру, +/- 20% от среднего.

На любых значениях выбросы больше в 2 раза(

Это весь код, больше ничего нет

Выкинул код управления катушками, оставил только вывод в порт и прерывание - тож самое

Как отключать таймер? я там в коде закомментировал типа “фильтр” по значениям х2. С ним работает куда лучше, стреолка практически не дергается, иногда редкие подрагивания, попробую поработаьь в этом направлении. а совсем избавиться от этого глюка нельзя? без вот этих костылей с фильтрам?

врать нехорошо

нет такой функции в коде

я тоже так подумал, но при пропуске прерывания частота, вроде, должна уменьшаться, а не увеличиваться? Или у него 90% прерываний пропадает и только значения 236 - на самом деле истинные? :slight_smile:

И да, вдогонку, где у него вообще прерывание таймера в коде?

float sinCoilValue = maxAnalogRes * sin(pos);
float cosCoilValue = maxAnalogRes * cos(pos);

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

Может micros()? (46стр)

пробовал и микрос. выбросов больше становится

Вот еще раз весь код, больше ничего нет.

//this is the maximum resolution of the PWM output.
const int maxAnalogRes = 255;

const int aircore1SinDirPin1 = 3;  //sin+ these two control the polarity to the “sine” coil
const int aircore1SinDirPin2 = 5;  //sin-
const int aircore1CosDirPin1 = 11; //cos+ these two control the polarity to the “cosine” coil
const int aircore1CosDirPin2 = 6;  //cos-

volatile unsigned long duration = 0;
volatile unsigned long lastMicros = 0;
volatile unsigned long frequency1;
volatile unsigned long frequency2;
volatile int frequency;

void setup()
{
Serial.begin(115200); // Инициализация порта, скорость 115200 бод [1, 3]

TCCR2B = TCCR2B & 0b11111000 | 0x01;
TCCR0B = TCCR0B & 0b11111000 | 0x01;
//Set the pins to OUTPUT
pinMode(aircore1SinDirPin1, OUTPUT);
pinMode(aircore1SinDirPin2, OUTPUT);
pinMode(aircore1CosDirPin1, OUTPUT);
pinMode(aircore1CosDirPin2, OUTPUT);

//прерывание по изменению сигнала на выводе 2
attachInterrupt(digitalPinToInterrupt(2), pulseCounter, RISING);

pinMode(2, INPUT_PULLUP);
}

void loop()
{
int val;
Serial.println(frequency);
if (duration > 0) {
val = map(frequency, 0, 200, -140, 300);
setMeterPosition(aircore1SinDirPin1, aircore1SinDirPin2, aircore1CosDirPin1, aircore1CosDirPin2, val);
}
delay(5000);
}

void pulseCounter() {

unsigned long currentMicros = millis();

duration = currentMicros - lastMicros;
frequency =  62500 / duration;
frequency2 = frequency1 * 2;
if (frequency >= frequency2 ) {
frequency = frequency / 2;
}
lastMicros = currentMicros;
frequency1 = frequency;
}

//Код движения стелки логометра. работает идеально.
// setMeterPosition() - put it at the angle in radians
void setMeterPosition( int sinDirPin1, int sinDirPin2, int cosDirPin1, int cosDirPin2, float pos) {
pos = pos / 100;
// Calculate the voltage on the PWM pins based on the angle we want
float sinCoilValue = maxAnalogRes * sin(pos);
float cosCoilValue = maxAnalogRes * cos(pos);
int sinn = 0;
int coss = 0;
if (sinCoilValue <= 0) {
sinn = 1;
}
if (cosCoilValue <= 0) {
coss = 1;
}
// take the absolute value because analogWrite doesn’t take negatives
sinCoilValue = abs(sinCoilValue);
cosCoilValue = abs(cosCoilValue);
// change the polarity of the coil depending on the sign of the voltage level
if (sinn == 1) {
analogWrite(sinDirPin1, 0); //Change to LOW if using relays
analogWrite(sinDirPin2, sinCoilValue); //comment this line out if using relays
} else {
analogWrite(sinDirPin1, sinCoilValue); //Change to HIGH if using relays
analogWrite(sinDirPin2, 0); //comment this line out if using relays
}
if (coss == 1) {
analogWrite(cosDirPin1, 0); //Change to LOW if using relays
analogWrite(cosDirPin2, cosCoilValue); //comment this line out if using relays
} else {
analogWrite(cosDirPin1, cosCoilValue); //Change to HIGH if using relays
analogWrite(cosDirPin2, 0); //comment this line out if using relays
}
}

@sansan88
А что такое

откуда эти 65200 берутся?

вычислено эмпирическим путем. там если посмотреть строки 19 и 20 че-то колдуется с таймерами. это я взял готовый ког по управлению стрелкой логометра. отсюда Подключение логометра | Аппаратная платформа Arduino

если строки 19 и 20 убрать, то стрелка начинает жужжать, типа частота низкая или что хз. но тогда в 49 строке идеть 1000 вместо 62500. типа по формуле частота от периода.

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

Строки 50-56 - это что за шаманство с частотами? В первом коде они были закомментированны

@sansan88
Какая реальная частота входящих импульсов на D2 ?

@WladDrakula
Так какие прерывания ты имел в виду, поясни?

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

Вы сами пробовали найти причину? вам же это проще, у вас все под рукой…

  • какая частота входящих импульсов?
  • какой источник этих импульсов?
  • если проверяете прямо “в поле”, попробуйте запустить на столе с генератором импульсов вместо реального сигнала

Я думаю noInterrupts(); и Interrupts(); “спасут отца русской демократии” )))

1 лайк

Извини, отходил. Прерывания таймера, который миллис считает. Его же никто не отключал :wink:

да ну нафик :slight_smile: Не думаю что это причина глюков ТС.
При наложении прерываний одно из них ставится в очередь, но не пропадает совсем.

Ставлю на сообщение @BOOM (#18) как на разгадку.