• Witaj na Forum Arduino Polska! Zapraszamy do rejestracji!
  • Znajdziesz tutaj wiele informacji na temat hardware / software.
Witaj! Logowanie Rejestracja


Ocena wątku:
  • 0 głosów - średnia: 0
  • 1
  • 2
  • 3
  • 4
  • 5
pulseIn przez cały czas zwraca wartość 0
#1
Przyznam szczerze, że siedzę nad kodem już dobrych parę dni, a nadal nie mogę znaleźć przyczyny błędnego działania. 

Na wstępie wspomnę w jakim celu ma służyć dany kod. Potrzebuję mierzyć czas wysokiego stanu sygnału. Przeglądając multum stron internetowych w poszukiwaniu rozwiązania, natknąłem się na funkcję pulseIn, która rzekomo powinna rozwiązać mój problem. Użyłem osobnego Arduino w celu symulowania sygnału - najprostszy możliwy schemat, oparty na funkcjach delay oraz digitalWrite - zapala i gasi diodę co sekundę. pulseIn powinien zwracać wartość 1000 ms za każdym razem, niestety tak nie jest. Poniżej załączam uproszczony kod, rejestrujący czas wysokiego stanu. 

Czego już próbowałem?

1. Wyczytałem, że funkcja pulseIn ma wbudowany tzw. "timeout", standardowo ustawiony na wartość 1000 ms. Jeżeli w ciągu jednej sekundy nie zarejestruje sekwencji stanów: low>high>low, zwraca wartość 0. Oczywiście wartość timeout'u można zmienić przykładowo na dwie sekundy wpisując: pulseIn(pin,HIGH,2000);


2. Zmieniałem wartość przerw pomiędzy stanem niskim i wysokim w symulowanym sygnale (korzystałem z różnych: 10ms, 100ms).

3. Symulowałem sygnał przy pomocy innych funkcji. Przykładowo standardowy PWM, "analogWrite" oraz "tone" - w tym przypadku pulseIn odczytuje jakieś wartości. W przypadku digitalWrite zwraca za każdym razem 0.

Kod:
float Th; // czas trwania stanu wysokiego (w tym czasie samochód spala 0,006 l)

void setup(){

 Serial.begin(9600);
 pinMode(7,INPUT); // wejście rejestrowania sygnału paliwomierza
}

void loop() {
 
/* Rejestracja i przetwarzanie sygnału z paliwomierza */
Th = pulseIn(7,HIGH); // pomiar czasu trwania pojedynczego stanu wysokiego na wejściu paliwomierza. W tym czasie samochód spala 0,006 l (jednostka [ms])
 Serial.println(Th, 4);

}
 
Odpowiedź
#2
Witam,
Czy zmienna Th ma na pewno być typu float? Bo mam poważne wątpliwości.
Alternatywa to pomiar za pomocą przerwan.
Pozdrawiam,
Tomek.
 
Odpowiedź
#3
Witam,

Uważam, że zmienna Th wręcz powinna być typu float. Zależy mi na wartościach dziesiętnych. Udało mi się uzyskać odczyt sygnału. Mój błąd - nie doczytałem dość istotnej informacji, że timeout jest liczony w mikrosekundach, nie milisekundach. 

Przyznam szczerze, że funkcja pulseIn w miarę precyzyjnie wychwytuje sygnały o wyższej częstotliwości. Gdy przychodzi do pomiaru niskich częstotliwości (poniżej 1Hz), dane mają niewiele wspólnego z rzeczywistymi wartościami. Chyba pozostaje wspomniane przez Ciebie alternatywne rozwiązanie z wykorzystaniem przerwań.
 
Odpowiedź
#4
Witam,
To, że chciałbyś żeby Th była float, a to jakiego typu zwracana jest wartość przez funkcję pulseIn trzeba ze sobą pogodzić!
Pozdrawiam,
Tomek
 
Odpowiedź
#5
(12-01-2018, 20:14)adixpogo napisał(a): Witam,

Uważam, że zmienna Th wręcz powinna być typu float. Zależy mi na wartościach dziesiętnych. Udało mi się uzyskać odczyt sygnału. Mój błąd - nie doczytałem dość istotnej informacji, że timeout jest liczony w mikrosekundach, nie milisekundach. 

Przyznam szczerze, że funkcja pulseIn w miarę precyzyjnie wychwytuje sygnały o wyższej częstotliwości. Gdy przychodzi do pomiaru niskich częstotliwości (poniżej 1Hz), dane mają niewiele wspólnego z rzeczywistymi wartościami. Chyba pozostaje wspomniane przez Ciebie alternatywne rozwiązanie z wykorzystaniem przerwań.

witam, post bardzo stary, ale może ktoś tak jak ja przegląda stare archiwa w poszukiwaniu rozwiązania. Funkcja pulseln() zwraca wartość w mikrosekundach typu unsigned long.
Kod:
int pin = 7;
unsigned long duration;

void setup() {
  Serial.begin(9600);
  pinMode(pin, INPUT);
}

void loop() {
  duration = pulseIn(pin, HIGH);
  Serial.println(duration);
}

a tu przykład oparty na przerwaniach:
Kod:
/*
* Non-blocking pulseIn(): returns the pulse length in microseconds
* when the falling edge is detected. Otherwise returns 0.
*/
unsigned long read_pulse(int pin)
{
    static unsigned long rising_time;  // time of the rising edge
    static int last_state;             // previous pin state
    int state = digitalRead(pin);      // current pin state
    unsigned long pulse_length = 0;    // default return value

    // On rising edge: record current time.
    if (last_state == LOW && state == HIGH) {
        rising_time = micros();
    }

    // On falling edge: report pulse length.
    if (last_state == HIGH && state == LOW) {
        unsigned long falling_time = micros();
        pulse_length = falling_time - rising_time;
    }

    last_state = state;
    return pulse_length;
}
 
Odpowiedź
#6
Nie widzę w tym przykładzie żadnych przerwań. Ale użycie micros jest jak najbardziej OK, impulsy muszą być jedynie dłuższe znacząco niż pojedynczy czas trwania LOOP.
Miło być decenianym https://buycoffee.to/kaczakat
 
Odpowiedź
#7
Kod:
#include <avr/interrupt.h>
#include <stdbool.h>

uint16_t rising_capture = 0;
uint16_t falling_capture = 0;
uint16_t pulse_width = 0;
bool rising_edge = true;

ISR(TIMER1_CAPT_vect)
{
    switch(rising_edge){
        case true:
            TCNT1 = 0;               
            rising_capture = ICR1;   
            TCCR1B &= ~(1 << ICES1);
            rising_edge = false;   
            break;
       
        case false:
            falling_capture = ICR1;
            TCCR1B |= (1 << ICES1);
            rising_edge = true;   
            break;
    }
}

void setup()
{
    TCCR1B |= (1 << ICES1);
    TCCR1B |= (1 << CS10); 
    TIMSK1 |= (1 << ICIE1);
}
void loop()
{
        pulse_width = falling_capture - rising_capture;                                         
}

Nie wiem czy dobrze napisałem, bo dawno nie robiłem nic na AVR'ach.
Problem polega na tym, że bez normalnej obsługi przerwań, funkcja Serial.print zajmuje czas procesora, kiedy powinien wykrywać zbocza. Dla tego framework Arduino jest kiepski dal bardziej wymagających projektów.
Jeśli masz problem z kodem lub sprzętem, zadaj pytanie na forum. Nie odpowiadam na PW, jeśli nie dotyczą one spraw forum lub innych tematów prywatnych.

[Obrazek: SsIndaG.jpg]
 
Odpowiedź
#8
Nie uwzględniłeś przepełnienie licznika "ISR(TIMER1_COMPA_vect)" ICR1 będzie zwracał 1-65 536 cykli, nie sprawdzam jak ustawiłeś taktowanie zegara. Ale jak ustawiłeś zegar na 1ms to będzie można tylko zmierzyć sygnały 1-65 536 ms jak przekroczą te zakresy to będzie pokazywać bzdury.
Arduino zostało wymyślone po to, by robić dobrze jedną prostą rzecz – migać diodą. 
 
Odpowiedź
#9
(12-01-2018, 20:14)adixpogo napisał(a): Witam,


Przyznam szczerze, że funkcja pulseIn w miarę precyzyjnie wychwytuje sygnały o wyższej częstotliwości. Gdy przychodzi do pomiaru niskich częstotliwości (poniżej 1Hz), dane mają niewiele wspólnego z rzeczywistymi wartościami. Chyba pozostaje wspomniane przez Ciebie alternatywne rozwiązanie z wykorzystaniem przerwań.

pulseInLong

Niepotrzebne szukałeś multum stron - najlepiej zacząć od dokumentacji.
 
Odpowiedź
  


Skocz do:


Przeglądający: 1 gości