Skocz do zawartości

[C] Problem z poprawnymi wynikami z enkoderów optycznych


Pomocna odpowiedź

Napisano

Witam,

przejdę od razu do sedna. Enkodery optyczne firmy pololu(https://www.pololu.com/product/2590), które mam podłączone(podłączenie widoczne na załączonym zdjęciu) nie dają poprawnych wyników, liczby są przesyłane przez bluetooth i odczytuję je na telefonie. Co do tych wyników to czasami skaczą obracając w jedną stronę mam liczbę dodatnią a po chwili pojawia się ujemna. Obracając w lewo a potem w prawo wyniki albo się zmniejszają albo zwiększają.

Całością steruje atmega128. W schemacie jest dzielnik napięcia z oporników do zasilenia komparatora, ale aktualnie w testach mam podłączony zasilacz z możliwością regulowania, aby dostosować odpowiednie napięcie i ostatecznie dobrać poprawne wartości oporników jak już wszystko będzie poprawnie działać 😉

Main.c

#include <avr/io.h>   
#include <util/delay.h>
#include <avr/interrupt.h>
#include <avr/pgmspace.h>
#include <util/delay.h>

#include "hardware.h"
#include "uart.h"
#include "encoders.h"

volatile int encoder_R;

int main(void)
{

UART_init();
Encoder_init();

while(1)
{	
UART_putstr(" wynik ");
UART_putint(encoder_R);
UART_putstr("\n");
_delay_ms(10);	
}
}

encoders.h

/**
\file irleds.h
\brief Plik nagłówkowy biblioteki do obsługi enkoderow
*/
#ifndef ENCODERS_H_
#define ENCODERS_H_

#include <stdint.h>

/****************************************Defines**********************************************/

/// Rejestr obsługujący port do którego podłączone są enkodery
#define 	ENC_DDR     DDRE
/// Port do którego podłączeone są enkodery
#define 	ENC_PORT    PORTE
/// Maska lewego enkodera, wyjście A
#define		 ENC_LA		PE4
/// Maska lewego enkodera, wyjście B
#define		 ENC_LB		PE5
/// Maska prawego enkodera, wyjście A
#define		 ENC_RA		PE6
/// Maska prawego enkodera, wyjście B
#define		 ENC_RB     PE7

/*********************************Function Prototypes****************************************/

/**
\brief Funkcja inicjalizująca enkodery 
*/
void Encoder_init(void);

/**
\fn ISR(INT4_vect)
*/

#endif /* ENCODERS_H_ */

encoders.c

/**
\file encoders.c
@brief Plik źródłowy biblioteki do obsługi enkoderow
*/

#include "encoders.h"
#include "uart.h"
#include <avr/io.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <util/delay.h>
#include <avr/sfr_defs.h>
#include <avr/interrupt.h>

/*******************************Global Variables*****************************************************/

volatile int encoder_R=0;
volatile int encoder_L=0;

/************************************Functions*****************************************************/

void Encoder_init(void)
{
ENC_DDR &= ~(1<<ENC_LA) & ~(1<<ENC_LB) & ~(1<<ENC_RA) & ~(1<<ENC_RB); 
ENC_PORT |= (1<<ENC_LA) | (1<<ENC_LB) | (1<<ENC_RA) |(1<<ENC_RB);
EICRB |= (1<<ISC71) | (1<<ISC70) | (1<<ISC61) | (1<<ISC60) | (1<<ISC51) | (1<<ISC50) | (1<<ISC41) | (1<<ISC40);
EIMSK |= (1<<INT7) | (1<<INT6) | (1<<INT5) | (1<<INT4);

sei();
}

/*
ISR(INT4_vect)
{
if(( PINE & (1<<ENC_LA)) == 0 )
{
	encoder_R++;
	return;
}

if(( PINE & (1<<ENC_LA)))
{
	encoder_R--;
	return;
}
}

ISR(INT5_vect)
{
if(( PINE & (1<<ENC_LB)) == 0 )
{
	encoder_R++;
	return;
}

if(( PINE & (1<<ENC_LB)))
{
	encoder_R--;
	return;
}

}
*/

ISR(INT4_vect ) 
{ 
if(!bit_is_clear(PINE, PE5))
{ 
	encoder_R++;
} 
else
{ 
	encoder_R--;
} 
} 

ISR(INT5_vect ) 
{ 
if(!bit_is_clear(PINE, PE4))
{ 
	encoder_R++;
} 
else
{ 
	encoder_R--;
} 
} 

encoders.h

encoders.c

main.c

1. Zrobiłeś transmisję przez radio itp bajery a zabrakło mi podstawowego zdania: sprawdziłem, że sygnały z enkoderów są poprawne. Po co grzebać w programie i śledzić kod skoro nie wiesz czy wejście jest w porządku? Nie masz czasem 4 diodek by podłączyć je (przez oporniki) wprost do wyjść komparatorów? Przez powolne kręcenie kółkiem mógłbyś zweryfikować sygnały. Bez tego nie ma sensu dalsza praca. Oczywiście oscyloskop byłby tu niezastąpiony, ale skoro do tej pory go nie użyłeś, to pewnie go nie masz. Jeżeli na schemacie są gdzieś jakieś wolne LEDy - ich możesz użyć do pośredniego sprawdzenia. Napisz prosty kod przepisujący stany z PE4-5 lub PE6-7 na te diodki. Żadnych przerwań itp bzdur.

2. Nie dobierzesz nigdy dobrze oporników, bo spaprałeś dzielnik. Przez szeregowy kondensator prąd nie płynie więc i spadek napięcia na opornikach jest zerowy. Niezależnie jakie oporniki wstawisz i tak komparatory zobaczą 5V i działać nie będą. C15 musi być podłączony wprost do ich wejść a R3 wprost do masy.

3. Czy używając typów int wiesz jakiej są one prawdziwej długości w kodzie wynikowym? W AVR GCC pewnie są 16-bitowe a to oznacza, że procesor 8-bitowy nie umie manipulować nimi w jednym strzale. O ile inkrementacje i dekrementacje robisz w przerwaniach więc to nie ma znaczenia i bo z definicji są to wtedy operacje atomowe (niepodzielne), o tyle pobranie do wypisania już nie. Zastanów się co się stanie gdy procesor już pobrał z pamięci pierwszy bajt zmiennej encoder_R a jeszcze nie wziął drugiego i przyjdzie przerwanie od enkodera zmieniające jej zawartość. Jeżeli będzie to operacja zmieniająca starszy bajt, leżysz. Wypisana zostanie jakaś niepoprawna wartość, przemyśl to. Wszelki dostęp do takich obiektów musi odbywać się atomowo. Ponieważ nie warto (i nie wolno) wyłączać przerwań na całe wypisywanie zmiennej, trzeba pomóc sobie dodatkową zmienną, której przerwanie nie dotyka:

 int volatile tmp;
cli()
tmp=encoder_R;
sei()
UART_putstr(" wynik ");
UART_putint(tmp);
UART_putstr("\n");

Żaby nie zaśmiecać kodu wstawkami cli/sei możesz napisać prostą funkcję, która w sposób atomowy czyta lub pisze do wskazanej, takiej "niebezpiecznej" zmiennej, np:

int get_atomic(int* object_ptr)
{
  int tmp;
  cli();
  tmp = *object_ptr;
  sei();
  return(tmp);
}

a potem w programie:

UART_putint(get_atomic(&encoder_R));

Kapujesz?

1. Ma Pan rację z tym sprawdzeniem diodami. Jakoś nie wpadłem na pomysł, aby to w taki prosty sposób przetestować. Cały czas zastanawiałem się nad oscyloskopem, ale niestety go nie posiadam. Zastosowałem się do zaleceń producenta, że jak się nie ma oscyloskopu to optymalną odległością łopatek od czujników jest 0,5 mm i taką odległość zastosowałem.

2. Nie wiem czy dobrze zrozumiałem, ale chodzi Panu o coś takiego ?

3. Wydaje mi się, że sens całości zrozumiałem 😃

Zaraz postaram się zastosować do wskazówek i zobaczymy jak będzie 😉

🙂

A teraz opisz jak wyszły eksperymenty z wyświetlaniem na diodkach sygnałów z enkodera.

Pamiętaj, że to dopiero początek testów. Użyłeś komparatorów w najbardziej prymitywny sposób w jaki można sobie wyobrazić. W przypadku prostego wykrywania stanu to by zadziałało, ale gdy procesor ma liczyć zbocza możesz mieć problemy. Przejście napięcia z enkodera przez obszar aktywnej pracy komparatora (czyli przez okolicę napięcia odniesienia podawanego z dzielnika) będzie powodowało wiele wahnięć napięcia na pinie procesora i wiele przerwań. Moim zdaniem bez wprowadzenia histerezy się nie obejdzie. Przecież na sygnały z czujników optycznych będzie nałożonych mnóstwo śmieci, choćby pochodzących ze sztucznego oświetlenia w pomieszczeniu i nigdy nie będzie to przejście monotoniczne.

Tak więc następnym testem (bezoscyloskopowym) powinno być napisanie prostego programu wciągającego sygnał z jednego wejścia (np. PE4), czysto programowo wykrywającego zbocza i liczącego je. Jeśli przy wolnym obracaniu kółkiem zamiast spodziewanych np. 10 zliczeń dostaniesz 100 lub 1000, masz problem. Co prawda sama idea używania enkodera kwadraturowego i algorytm jego obsługi powinny być odporne na takie zjawiska, ale nawarstwianie się takiej ilości niepotrzebnych przerwań będzie bolesne dla procesora. I ciągle będziesz wiedział, że coś w sprzęcie jest nie tak - a to chyba nawet gorsze...

Czekam na wyniki.

Ja ze swojej strony mogę dodać sugestię rzetelnego przećwiczenia jakiegoś kursu elektroniki. Tworzenie układów elektronicznych to nie jest zabawa w zgadywanie (vide 3x błędne podłączenie kondensatora w dzielniku).

marek1707

Stwierdziłem, ze nie będę jak na razie testował obu enkoderów, zacznę od jednego. Podłączyłem do wyjść komparatora 2 diody.

Kręcąc w jedną stronę: L1 zgaszona, L2 zgaszona | L1 zgaszona, L2 zapalona | L1 zapalona, L2 zgaszona

Kręcąc w przeciwną: L1 zgaszona, L2 zgaszona | L1 zapalona, L2 zgaszona | L1 zgaszona, L2 zapalona

Ma Pan rację, ze światło trochę wpływa na działanie, ale jak już wszystko będzie działało przy nie zmieniającym się oświetleniu a w zmieniającym będą błędy to mam zamiar osłonić te enkodery aby pracowały w ciemności 🙂

Bądź aktywny - zaloguj się lub utwórz konto!

Tylko zarejestrowani użytkownicy mogą komentować zawartość tej strony

Utwórz konto w ~20 sekund!

Zarejestruj nowe konto, to proste!

Zarejestruj się »

Zaloguj się

Posiadasz własne konto? Użyj go!

Zaloguj się »
×
×
  • Utwórz nowe...