Pomiar czasu trwania impulsu PWM na Arduino

monitory, nagrywarki, itp

Moderatorzy: moderatorzy2014, moderatorzy

labo
Posty: 1150
Rejestracja: sobota 04 gru 2010, 21:19
Lokalizacja: Toruń

Pomiar czasu trwania impulsu PWM na Arduino

Post autor: labo »

Cześć, chciałem pomierzyć czasy PPM aparatury budując przełącznik 6 pozycyjny. Nie jestem specjalistą od Arduino, więc pytanie co poprawić i czy taki pomiar ma sens. Funkcja micros() dla Nano 16MHz ma dokładność 4uS. Robię 10 pomiarów i wyciągam średnią (w sumie to 11 i pierwszy odrzucany, bo pomiar mógł się zacząć w czasie trwania impulsu).
Wyniki już uśrednione z 10 pomiarów dla mojej aparatury dla kanału kraniec/center/kraniec są w następującym rozrzucie:
1061-1074 / 1487-1495 / 1910-1923

Pytanie czy te pomiary mają sens, czy można coś poprawić, przyspieszyć w kodzie, aby mieć tani miernik szerokości impulsu:

Poniżej program dla pastwienia się. ;)

Kod: Zaznacz cały

/*
Miernik PWM
Mierzy czas impulsu PWM kanału RC.
Program dokonuje 11 pomiarów (pierwszy odrzuca, 
bo pomiar mógł się zacząć, gdy impuls już trwał) 
i wyświetla pozostałe 10 na serial monitorze.
*/

// Ustaw pin do odczytu
const int PinOdczyt = 2;  

// deklaracja zmiennych:
int PPM[11]; //tabela na 11 pomiarów
int czas1, czas2, j=0;
int StanPinu = HIGH; 

void setup() {
  Serial.begin(9600);
  // initializacja pinu odczytu jako input:
  pinMode(PinOdczyt, INPUT);     
}

void loop(){

  if (StanPinu == LOW && digitalRead(PinOdczyt) == HIGH) {
    //Czyli stan był niski a pojawił sie wysoki
    czas1 = micros();   //odczytaj czas
    // odczytaj stan pinu
    StanPinu = digitalRead(PinOdczyt);
  } 
  else
    if (StanPinu == HIGH && digitalRead(PinOdczyt) == LOW) {
      // czyli impuls się skończył
      czas2 = micros();  //odczytaj czas
      // oblicz czas impulsu
      PPM[j] = czas2 - czas1;  //oblicz czas impulsu
      // odczytaj stan pinu
      StanPinu = digitalRead(PinOdczyt);  
      j++; 
    }

  
  //wyświetlaj czasy
  if (j==11) {
  for (byte i=1; i<=10; i++){
    Serial.print(F("Pomiar "));Serial.print(i);Serial.print(F(" : "));Serial.println(PPM[i]);
    j=0;
  }
  //Wyświetl wartość średnią:
  Serial.print(F("Pomiar sredni : "));Serial.println((PPM[1]+PPM[2]+PPM[3]+PPM[4]+PPM[5]+PPM[6]+PPM[7]+PPM[8]+PPM[9]+PPM[10])/10);
  }

}
Edit: Zmiana tytułu + poprawa kodu.
Ostatnio zmieniony środa 02 sty 2019, 20:32 przez labo, łącznie zmieniany 2 razy.
Awatar użytkownika
pawelsky
Posty: 9750
Rejestracja: środa 19 mar 2014, 02:03
Lokalizacja: Polska
Kontakt:

Re: Pomiar czasu trwania impulsu PPM na Arduino

Post autor: pawelsky »

labo pisze:Pytanie czy te pomiary mają sens
Nie, bo po pierwsze nie rozpoznajesz konca ramki, wiec tak naprawde nie wiesz wartosci jakich kanalow czytasz.

Po drugie wyswietlanie danych w tej samej petli w ktorej je czytasz wplywa na pomiar.

Po trzecie po co wymyslac kolo od nowa, skoro istnieja juz gotowe biblioteki i to oparte na przerwaniach?
labo
Posty: 1150
Rejestracja: sobota 04 gru 2010, 21:19
Lokalizacja: Toruń

Re: Pomiar czasu trwania impulsu PPM na Arduino

Post autor: labo »

pawelsky pisze:Nie, bo po pierwsze nie rozpoznajesz konca ramki, wiec tak naprawde nie wiesz wartosci jakich kanalow czytasz.
Wiem na którym kanale, bo czytam długość na konkretnym kanale odbiornika w miejscu wyjścia na serwo, tam jest już jeden sygnał. Nie robię na S.Bus czy innym sumowanym na całej ramce.
pawelsky pisze:Po drugie wyswietlanie danych w tej samej petli w ktorej je czytasz wplywa na pomiar.
Nie wyświetlam w tej samej pętli w której czytam. Najpierw czytam, później wyświetlam, popatrz na program.
pawelsky pisze:Po trzecie po co wymyslac kolo od nowa, skoro istnieja juz gotowe biblioteki i to oparte na przerwaniach?
Jakiś przykład? Nie znalazłem, dlatego zacząłem pisać.


EDIT:
Dałem 100 pomiarów i wyświetlanie tylko średniej. Rozrzut teraz ok. 3uS.
1067-1070 / 1491-1493 / 1914-1917

Poniżej kod dla ciekawskich:

Kod: Zaznacz cały

/*
Miernik PWM
Mierzy czas impulsu PWM kanału RC na wyjściu serwa odbiornika.
Program dokonuje 101 pomiarów (pierwszy odrzuca,
bo pomiar mógł się zacząć, gdy impuls już trwał)
i wyświetla średnią ze 100 na serial monitorze.
*/

// Ustaw pin do odczytu
const int PinOdczyt = 2; 

// deklaracja zmiennych:
int PPM[101]; //tabela na 101 pomiarów
int czas1, czas2, j=0;
long srednia=0;
int StanPinu = HIGH;

void setup() {
  Serial.begin(9600);
  // initializacja pinu odczytu jako input:
  pinMode(PinOdczyt, INPUT);     
  Serial.println(F("Czekaj na pomiar ok. 4 sekundy"));
}

void loop(){
  if (StanPinu == LOW && digitalRead(PinOdczyt) == HIGH) {
    //Czyli stan był niski a pojawił sie wysoki
    czas1 = micros();   //odczytaj czas
    // odczytaj stan pinu
    StanPinu = digitalRead(PinOdczyt);
  }
  else
    if (StanPinu == HIGH && digitalRead(PinOdczyt) == LOW) {
      // czyli impuls się skończył
      czas2 = micros();  //odczytaj czas
      // oblicz czas impulsu
      PPM[j] = czas2 - czas1;  //oblicz czas impulsu
      // odczytaj stan pinu
      StanPinu = digitalRead(PinOdczyt); 
      j++;
    }

 
  //wyświetlaj czasy
  if (j==101) {
  for (byte i=1; i<=100; i++){
//    Serial.print(F("Pomiar "));Serial.print(i);Serial.print(F(" : "));Serial.println(PPM[i]);
    srednia=srednia+PPM[i];
  }
  //Wyświetl wartość średnią:
  Serial.print(F("Pomiar sredni : "));Serial.println(srednia/100);
  j=0;
  srednia = 0;
  }

}
Edit: Poprawa kodu
Ostatnio zmieniony środa 02 sty 2019, 20:32 przez labo, łącznie zmieniany 1 raz.
Awatar użytkownika
pawelsky
Posty: 9750
Rejestracja: środa 19 mar 2014, 02:03
Lokalizacja: Polska
Kontakt:

Re: Pomiar czasu trwania impulsu PPM na Arduino

Post autor: pawelsky »

labo pisze:Wiem na którym kanale
Nie wiesz, bo nie wiesz na ktory kanal w ramce PPM trafi Ci petla loop na poczatku. Moze na pierwszy, moze na drugi, a moze na inny. Czysty przypadek, bo nie wykrywasz poczatku/konca ramki PPM (ktora zawiera kilka kanalow w jednym).

No chyba ze nie rozumiesz roznicy miedzy PPM a PWM...
labo pisze:Nie wyświetlam w tej samej pętli w której czytam. Najpierw czytam, później wyświetlam, popatrz na program.
Wszystko robisz w jednej i tej samej petli loop, co 11 jej obrotow zabierasz iles milisekund czasu na wyswietlenie danych, w tym czasie prawdopodobnie przegapiajac pare impulsow.
labo pisze:Jakiś przykład? Nie znalazłem,
Google Arduino PPM library
labo
Posty: 1150
Rejestracja: sobota 04 gru 2010, 21:19
Lokalizacja: Toruń

Re: Pomiar czasu trwania impulsu PWM na Arduino

Post autor: labo »

pawelsky pisze:
labo pisze:Wiem na którym kanale
Nie wiesz, bo nie wiesz na ktory kanal w ramce PPM trafi Ci petla loop na poczatku. Moze na pierwszy, moze na drugi, a moze na inny. Czysty przypadek, bo nie wykrywasz poczatku/konca ramki PPM (ktora zawiera kilka kanalow w jednym).

No chyba ze nie rozumiesz roznicy miedzy PPM a PWM...
Ups, no tak, cały czas myślę o PWM, sorry, mea culpa.
pawelsky pisze:
labo pisze:Nie wyświetlam w tej samej pętli w której czytam. Najpierw czytam, później wyświetlam, popatrz na program.
Wszystko robisz w jednej i tej samej petli loop, co 11 jej obrotow zabierasz iles milisekund czasu na wyswietlenie danych, w tym czasie prawdopodobnie przegapiajac pare impulsow.
W przypadku PWM nieistotnie i tak pierwszy po wznowieniu pomiaru jest odrzucany.
pawelsky pisze:
labo pisze:Jakiś przykład? Nie znalazłem,
Google Arduino PPM library
Dziękuję poszukam.

Zmienię też temat wątku na poprawniejszy.
Awatar użytkownika
pawelsky
Posty: 9750
Rejestracja: środa 19 mar 2014, 02:03
Lokalizacja: Polska
Kontakt:

Re: Pomiar czasu trwania impulsu PWM na Arduino

Post autor: pawelsky »

labo pisze:W przypadku PWM nieistotnie i tak pierwszy po wznowieniu pomiaru jest odrzucany.
W przypadku PWM owszem, ale Ty chciales mierzyc PPM :)
labo pisze:Dziękuję poszukam.
Tylko po co, skoro chcesz mierzyc PWM a nie PPM :) Pomijam juz ze do tego pomiaru wystarczy Ci metoda pulseIn
labo pisze:Zmienię też temat wątku na poprawniejszy.
Po co? Zeby wprowadzic jeszcze wiecej zamieszania w dyskusji? :)
labo
Posty: 1150
Rejestracja: sobota 04 gru 2010, 21:19
Lokalizacja: Toruń

Re: Pomiar czasu trwania impulsu PWM na Arduino

Post autor: labo »

pawelsky pisze:
labo pisze:W przypadku PWM nieistotnie i tak pierwszy po wznowieniu pomiaru jest odrzucany.
W przypadku PWM owszem, ale Ty chciales mierzyc PPM :)
Chcieć i mierzyć, to chciałem i mierzyłem PWM, tylko bez sensu pisałem PPM. :)
pawelsky pisze:
labo pisze:Zmienię też temat wątku na poprawniejszy.
Po co? Zeby wprowadzic jeszcze wiecej zamieszania w dyskusji? :)
Oj tam, oj tam ;)
pawelsky pisze:
labo pisze:Dziękuję poszukam.
Tylko po co, skoro chcesz mierzyc PWM a nie PPM :) Pomijam juz ze do tego pomiaru wystarczy Ci metoda pulseIn
Ooo, to jest dobre...

Teraz wyniki po 7-10 uS niższe niż wcześniejszą metodą... Są też bardziej powtarzalne, choć rozrzuty potrafią być w niektórych pomiarach 10uS.
Średnia ze 100 stoi w miejscu: 1061 / 1483 / 1904.
Ciekawe na ile różnica od idealnego 1000 / 1500 / 2000 wynika z metody pomiaru, a ile na prawdę daje aparatura.
Czy można zaufać temu programowi, czy bez analizatora stanów logicznych lub oscyloskopu ani rusz?

Program z PulseIn():

Kod: Zaznacz cały

/*
Miernik PWM
Mierzy czas impulsu PWM kanału RC.
Program dokonuje 101 pomiarów (pierwszy odrzuca,
bo pomiar mógł się zacząć, gdy impuls już trwał)
i wyświetla średnią ze 100 na serial monitorze.
*/

// Ustaw pin do odczytu
const int PinOdczyt = 2; 

// deklaracja zmiennych:
int PWM[100]; //tabela na 100 pomiarów
long srednia=0;

void setup() {
  Serial.begin(9600);
  // initializacja pinu odczytu jako input:
  pinMode(PinOdczyt, INPUT);     
  Serial.println(F("Czekaj na pomiar ok. 4 sekundy"));
}

void loop(){
  for (byte k=0; k<100; k++) {
    PWM[k] = pulseIn(PinOdczyt, HIGH);
  }

  //wyświetlaj czasy
  for (byte i=0; i<100; i++){
 //   Serial.print(F("Pomiar "));Serial.print(i);Serial.print(F(" : "));Serial.println(PWM[i]);
    srednia=srednia+PWM[i];
  }
  //Wyświetl wartość średnią:
  Serial.print(F("Pomiar sredni : "));Serial.println(srednia/100);
  srednia = 0;
}
Awatar użytkownika
Avatar
Posty: 127
Rejestracja: środa 12 sie 2015, 19:46
Lokalizacja: Ruda Śląska

Re: Pomiar czasu trwania impulsu PWM na Arduino

Post autor: Avatar »

Witaj,

Czytając temat postanowiłem pomóc, jakiś czas temu poskładałem mały migacz LED do drona/samolotu, na własne potrzeby.
Pomiaru impulsu PWM z kanału odbiornika dokonuje w przerwaniu. Pomiar bardzo dokładny. Instrukcje w pętli głównej nie wpływają na odczytane wartości.
Różnice pomiaru długości miałem rzędu 1-3 taktów zegara. Co w zupełności było wystarczające do moich potrzeb.

W twoim programie marnujesz dużo czasu na dokonanie pomiarów. Dokładając dodatkowe funkcje/instrukcje w pętli głównej prawdopodobnie pominiesz zmianę wartości kanału długości impulsu PWM.

Poniżej wstawka moich skromnych poczynań - nie jestem zawodowym programistą, więc może lepiej można by to napisać.
Ustawienia rejestrów EICRA itp. są dla płytki Arduino Nano v3 z procesorem ATMega328

Program był pisany w środowisku: WinAvr, później Atmel Studio 7.0 - w obu działało tak jak powinno

Wzorując się na poniższym kodzie dasz radę "przetłumaczyć" to na Arduino

Życzę powodzenia :-)

Kod: Zaznacz cały

// nadanie nazw prostych do funkcji np zatrzymanie timera, zmiana zbocza niskie/wysokie

// Definicja zbocz dla przerwania INT0
#define INT0_zbocze_narastajace EICRA = 0x03	//(1 << ISC00 |1 << ISC01)
#define INT0_zbocze_opadajace	EICRA = 0x02	//(1 << ISC01)
#define INT0_stan_niski			EICRA = 0x00

#define INT0_enable   			EIMSK = 1
#define INT0_disable  			EIMSK = 0

#define TIMER1_start			TCCR1B = 3 //fcpu  1/1; 2/8; 3/64; 4/256; 5/1024
#define TIMER1_stop				TCCR1B = 0


volatile uint8_t zbocze_INT = 0, wartosc_PWM = 0;
volatile uint8_t flaga_przerwania = 0; //0 - zbocze narastajace, 1 - zbocze opadajace, 2 - wylaczenie
volatile uint16_t dlugosc_impulsu = 0;

volatile uint8_t offset_do_tabeli = 0, pwm_petla = 0, pwm_timer = 0, max_pwm_timer = 0;


#define komp_3_6_pozycje 0 // 0 - kompilacja dla 3 pozycji, 1 kompilacja dla 6 pozycji

// W pętli głównej porównuje długość impulsu z kanału PWM z tym co jest poniżej i wykonywałem jakąś akcje
#if komp_3_6_pozycje == 0 // tutaj sa zmierzone granice dla przelacznika 3 pozycyjnego

//  247   375  503

	#define pos_1 247
	#define pos_2 375
	#define pos_3 503

	#define dead_band 30
#endif


#if komp_3_6_pozycje == 1 // tutaj dla 6-cio pozycyjnego
	#define pos_1 248
	#define pos_2 298
	#define pos_3 348
	#define pos_4 400
	#define pos_5 452
	#define pos_6 503
	#define dead_band 8
#endif



/*
0 - 248
1 - 298
2 - 348
3 - 400
4 - 452
5 - 503

*/

// Timer 0 - 8bit, generuje sygnal pwm na zadanych pinach
// Timer 1 - 16bit, zlicza dlugosc impulsu
// Timer 2 - 8bit, szybkosc zmiany odczytu z tabelek

// INT0 - reaguje na narastajace zbocze, wlacza timer 1, zmiana na opadajace zbocze, wylacza timer 1, zmiana na wysokie zbocze 


// Obsluga przerwania na ktorym wisi kanal RC
// najpierw pojawienie stanu narastajacego zwieksza flaga przerwania na 1
// jesli flaga = 1 -> startuje timer do pomiaru dlugosci impulsu
// zmiana reakcji na opadajace zbocze
// kolejne wejscie zwieksza flage
// jesli flaga = 2, wtedy zatrzymujemy timer, przepisujemy wartosc licznika - dl. impulsu
// zerujemy licznik timera
// zmieniam czulosc na zbocze narastaace
ISR(INT0_vect)
{
	flaga_przerwania++;

	if(flaga_przerwania == 1)
	{ TIMER1_start; INT0_zbocze_opadajace;}// start TIMER1 aby zliczał takty zegarowe, zmiana na jakie zbocze ma reagowac przerwanie
	
	
	if(flaga_przerwania == 2)
	{ 	TIMER1_stop;		// zatrzymanie zliczania dlugosci impulsu
		flaga_przerwania = 0;   // wyzerowanie zmiennej pomocniczej
		dlugosc_impulsu = TCNT1; // przepisanie ilosci zliczonych taktow zegara do zmiennej
		TCNT1 = 0;		 // wyzerowanie licznika impulsow
		INT0_zbocze_narastajace; // zmiana reakcji przerwania na zbocze narastajace
//		uart_dec(dlugosc_impulsu,1);	// tutaj wysylalem do portu szerogowego to co sie zliczylo
	}
}


// konfiguracja timera i przerwania zewnętrznego
// Ustawienie przerwania INT0
	// wywolanie przerwania steruje praca timera 1ktory odczytuje dl impulsu
		EIMSK |= 1 << INT0; // Włączenie przerwania na pinie INT0
		INT0_zbocze_narastajace;	// reakcja na zbocze narastajace
		//PORTD |= 1 << 2;


// Ustawienie timera1 - 16bit
	// uzywany do odczytu dlugosci impulsu z kanalu rc
		TCCR1A = 0; // Wyjscia OCx jako normalne porty a nie wyjscia PWM
		TCCR1B = 0; // Chwilowo wyłączony timer1



Aparatura: Taranis 9XD + pulpit
Modele: MPX Easy Glider 4; dronik 5" na ramie KHXC5 GEPRC
ODPOWIEDZ