Skocz do zawartości

Enkodery magnetyczne pololu - pomysł na program


simba92

Pomocna odpowiedź

Witam!

Aktualnie realizuje projekt dwukołowego robota mobilnego o napędzie różnicowym z enkoderami magnetycznymi na każdym z kół, konstrukcja mechaniczna została już zrealizowana, elektronika obejmująca projekt plytki drukowanej też, aktualnie programuje. Robot jest sterowany procesorem STM32f103vc i enkodery są podpięte pod TIMER3 tj. sygnały 1A(kanał CH4) i 1B ( CH3) oraz 2A (CH1) i 2B(CH2).

Moje pytanie do forumowiczów jest następujące : czy istnieje możliwość pomiaru ilości impulsów + kierunku obrotu dla każdego koła przy użyciu JEDNEGO TIMERA, jeśli tak to jak ?

🙁 Chyba nie przewidziałem opcji sterowania dwoma Timerami, na upartego mogę podpiąć się pod inny timer zmieniając połączenia kabli na płytce, wówczas miałbym do dyspozycji TIM3 + TIM2.

  • Lubię! 1
Link do komentarza
Share on other sites

Encoder mode jest tylko na dwóch kanałach, ale możesz na pozostałych dwóch odpalić przerwanie zewnętrzne EXTI i na nim liczyć zbocza.

Mam tak zrobione u siebie i działa bez problemowo

Link do komentarza
Share on other sites

ps19 napisałem poniższy kod do obsługi enkoderów, założenia są następujące:

1) kanały 1A i 1B obsługiwane są przez timer TIM8 w trybie Encoder Mode na kanałach CH1 i CH2

2) kanały 2A i 2B obsługiwane są przez timer TIM3 w trybie Encoder Mode na kanałach CH1 i CH2

i kod, który niestety nie działa to znaczy w pętli while() w main() robot powinien obrócić się dokładnie o 90 stopni po zliczeniu 585 impulsów przez jakikolwiek enkoder. ps19 mógłbyś rzucić okiem czy czegoś nie brakuje w kodzie ????

#include <stdint.h>
#include "stm32f10x.h"

#include "rcc_konfiguracja.h"
#include "init.h"
#include "silniki_sterowanie.h"

//definicje enkodera dla silnika1 - LEWY
#define silnik1_ENKODERY_GPIO_PORT					GPIOC
#define silnik1_ENKODERY_TIMER						TIM8
#define enkoder_1A_PIN							GPIO_Pin_6
#define enkoder_1B_PIN							GPIO_Pin_7


//definicje enkodera dla silnika2 - PRAWY
#define silnik2_ENKODERY_GPIO_PORT					GPIOA
#define silnik2_ENKODERY_TIMER						TIM3
#define enkoder_2A_PIN							GPIO_Pin_6
#define enkoder_2B_PIN							GPIO_Pin_7


//zmienne do obslugi enkoderow
static volatile int16_t old_silnik1_Enkoder;
static volatile int16_t old_silnik2_Enkoder;

static volatile int16_t silnik1_Enkoder;
static volatile int16_t silnik2_Enkoder;
static volatile int16_t enkoder_Suma;

volatile int16_t silnik1_Count;
volatile int16_t silnik2_Count;
volatile int32_t silnik1_Total;
volatile int32_t silnik2_Total;

void delay(int time)
{
   int i;
   for (i = 0; i < time * 4000; i++) {}
}

void Enkodery_Init(void)
{
GPIO_InitTypeDef 			gpio;
TIM_TimeBaseInitTypeDef 		tim;
TIM_ICInitTypeDef  			incapture;





/*------ KONFIGURACJA kanałów 2A i 2B w trybie Encoder_Mode dla timera TIM3------ */

GPIO_StructInit(&gpio);
gpio.GPIO_Pin = enkoder_2A_PIN| enkoder_2B_PIN;
gpio.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_Init(silnik2_ENKODERY_GPIO_PORT, &gpio);

TIM_TimeBaseStructInit(&tim);
tim.TIM_CounterMode = TIM_CounterMode_Up;
tim.TIM_Prescaler = 0x0;
tim.TIM_Period = 0xffff;
TIM_TimeBaseInit(TIM3, &tim);

TIM_ICStructInit(&incapture);
incapture.TIM_Channel = TIM_Channel_1;
incapture.TIM_ICPolarity = TIM_ICPolarity_Rising;
incapture.TIM_ICPrescaler = TIM_ICPSC_DIV1;
incapture.TIM_ICSelection = TIM_ICSelection_DirectTI;
incapture.TIM_ICFilter = 0;
TIM_ICInit(TIM3, &incapture);

incapture.TIM_Channel = TIM_Channel_2;
TIM_ICInit(TIM3, &incapture);

TIM_EncoderInterfaceConfig(silnik2_ENKODERY_TIMER, TIM_EncoderMode_TI12, TIM_ICPolarity_Rising, TIM_ICPolarity_Rising);
TIM_SetAutoreload(silnik2_ENKODERY_TIMER,0xffff);
TIM_Cmd(silnik2_ENKODERY_TIMER, ENABLE);

/*----------------------------------------------------------------------------------*/



/*------ KONFIGURACJA kanałów 1A i 1B w trybie Encoder_Mode dla timera TIM8------ */

gpio.GPIO_Pin = enkoder_1A_PIN| enkoder_1B_PIN;
gpio.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_Init(silnik1_ENKODERY_GPIO_PORT, &gpio);  

tim.TIM_CounterMode = TIM_CounterMode_Up;
tim.TIM_Prescaler = 0x0;
tim.TIM_Period = 0xffff;
TIM_TimeBaseInit(TIM8, &tim);

incapture.TIM_Channel = TIM_Channel_1;
incapture.TIM_ICPolarity = TIM_ICPolarity_Rising;
incapture.TIM_ICPrescaler = TIM_ICPSC_DIV1;
incapture.TIM_ICSelection = TIM_ICSelection_DirectTI;
incapture.TIM_ICFilter = 0;
TIM_ICInit(TIM8, &incapture);

incapture.TIM_Channel = TIM_Channel_2;
TIM_ICInit(TIM8, &incapture);


TIM_EncoderInterfaceConfig(silnik1_ENKODERY_TIMER, TIM_EncoderMode_TI12, TIM_ICPolarity_Rising, TIM_ICPolarity_Rising);
TIM_SetAutoreload(silnik1_ENKODERY_TIMER,0xffff);
TIM_Cmd(silnik1_ENKODERY_TIMER, ENABLE);

/*----------------------------------------------------------------------------------*/



Enkodery_Reset();





}


void Enkodery_Reset(void)
{
__disable_irq();
old_silnik1_Enkoder = 0;
old_silnik1_Enkoder = 0;

silnik1_Enkoder = 0;  //POPRAWIONE
silnik2_Enkoder = 0;

silnik1_Total = 0;
silnik2_Total = 0;

TIM_SetCounter(silnik1_ENKODERY_TIMER, 0);
TIM_SetCounter(silnik2_ENKODERY_TIMER, 0);

__enable_irq();


}

void Enkodery_Read(void)
{	 
old_silnik1_Enkoder = silnik1_Enkoder;                          //POPRAWIONE
silnik1_Enkoder = TIM_GetCounter(silnik1_ENKODERY_TIMER);

old_silnik2_Enkoder = silnik2_Enkoder;
silnik2_Enkoder = TIM_GetCounter(silnik2_ENKODERY_TIMER);




silnik1_Count = silnik1_Enkoder - old_silnik1_Enkoder;
silnik2_Count = silnik2_Enkoder - old_silnik2_Enkoder;

silnik1_Total = silnik1_Total + silnik1_Count; //zmienne okreslajace sume zliczonych impulsow przez 
silnik2_Total = silnik2_Total + silnik2_Count;


}



int main(void)
{

RCC_Conf();
RCC_Periph_Conf();   //FUNKCJA wlaczajaca wszystkie potrzebne zegary
Gpio_Init();




   while(1)
   {


   	Enkodery_Init();


       while((silnik1_Total <= 585) && (silnik2_Total <= 585))
       {

       	robot_jazda(40,-40);  //funkcja odpowiedzialna za obrot robota, PS : napewno działa, robot obraca sie w kolko
       	Enkodery_Read();
}


   }
}






  • Lubię! 1
Link do komentarza
Share on other sites

    silnik1_Enkoder = TIM_GetCounter(silnik1_ENKODERY_TIMER); 
   old_silnik1_Enkoder = silnik1_Enkoder; 
   silnik1_Count = silnik1_Enkoder - old_silnik1_Enkoder; 

Czy to przypadkiem nie zwraca zawsze zera?

Link do komentarza
Share on other sites

Zarejestruj się lub zaloguj, aby ukryć tę reklamę.
Zarejestruj się lub zaloguj, aby ukryć tę reklamę.

jlcpcb.jpg

jlcpcb.jpg

Produkcja i montaż PCB - wybierz sprawdzone PCBWay!
   • Darmowe płytki dla studentów i projektów non-profit
   • Tylko 5$ za 10 prototypów PCB w 24 godziny
   • Usługa projektowania PCB na zlecenie
   • Montaż PCB od 30$ + bezpłatna dostawa i szablony
   • Darmowe narzędzie do podglądu plików Gerber
Zobacz również » Film z fabryki PCBWay

Elvis tak wartość zwracana zawsze byłaby zerowa, poprawiłem to. Efekt działania programu jest taki, że pętla

        
while((silnik2_Total <= 585))
       {
       	Enkodery_Read();
       	robot_jazda(40,-40);


       }

wykonuję się w nieskończoność, a silniki na dodatek przyspieszają !!!???.

Funkcja

       	robot_jazda(40,-40)

wysterowuję mostki TB6612 z wypełnieniem PWM 40 i - 40 , gdzie max. wypełnienie wynosi 200 - 1, a znak oznacza kierunek obrotów. Piszę program do obsługi już kilka dni na podstawie

m.in obsluga_enkoderów F4 , powinno działać 🙁

  • Lubię! 1
Link do komentarza
Share on other sites

A sprawdzałeś czy same enkodery działają? Może warto sprawdzić za pomocą debuggera czy zmienne mają oczekiwane wartości, ewentalnie wysyłać dane testowe przez port szeregowy?

Inna sprawa, to nie wiem czego oczekujesz po zliczeniu 585 impulsów, bo program który wkleiłeś po prostu zresetuje liczniki i będzie kręcił silnikami od początku

Link do komentarza
Share on other sites

Elvis sprawdzę działanie enkoderów wykorzystując port szeregowy i terminal, tak będzie najlepiej. Swoje pytanie odnośnie kodu wrzuciłem, bo liczyłem ,że może robię jakiś podstawowy błąd i ktoś mi go wytknie.

Inna sprawa, to nie wiem czego oczekujesz po zliczeniu 585 impulsów, bo program który wkleiłeś po prostu zresetuje liczniki i będzie kręcił silnikami od początku

Zgł

Po osiągnięciu wartości 585 impulsów robot obróci się w miejscu o dokładnie 90 stopni, obliczałem to na podstawie długości łuku po jakim porusza się koło o danej średnicy i obwodzie z nią związanym.
  • Lubię! 1
Link do komentarza
Share on other sites

Chodziło mi o to, że pętla zliczająca do 585 jest wewnątrz kolejnej pętli. Więc nawet jeśli enkodery działają ok, to doliczasz do 585 i co dalej? Nie masz instrukcji zatrzymującej silniki, zakładam, że robot_jazda() wystarczy wywołać raz i później PWM steruje silnikami do zatrzymania.

Więc nawet po zakończeniu wewnętrznej pętli silniki się kręcą, a kilka mikrosekund później program wraca do głównej pętli, wywołuje Enkodery_Init() i od początku liczy do 585. Może dlatego wygląda jakby ta pętla się nie kończyła? 🙂

Wydaje mi się, że robot_jazda(40, -40) wystarczy wywołać raz, później w pętli możesz poczekać na enkodery i zatrzymać silniki - może wywołując robot_jazda(0,0)?

Po zatrzymaniu silników musisz trochę poczekać, inaczej nawet nie zauważysz zmiany - możesz poczekać określony czas, albo i nieskończoność ale bez opóźnienia na pewno nie będzie działało zgodnie z oczwkiwaniami.

Link do komentarza
Share on other sites

Elvis poprawiłem i uprościłem program do minimum, generalnie chcę po prostu tylko uruchomić tryb enkoderów na liczniku TIM3 i funkcją

 printf("wartosc=%d\r\n",TIM_GetCounter(TIM3));

wyświetlić na terminalu wartość w liczniku. Przy pisaniu programu posługiwałem się m.in kursem STM32 F4, gdzie konfiguracja licznika w trybie enkodera https://forbot.pl/blog/artykuly/programowanie/kurs-stm32-f4-8-zaawansowane-funkcje-licznikow-id13473 jest banalna i zawiera ustawienie kilku parametrów nic więcej co w analogi do standardowej biblioteki powinno wyglądać tak samo. Poniżej kod programu obejmujący funkcje sterującą silnikami, konfiguracje transmisji przez interfejs UART i wspomnianych enkoderów tj. enkodera na silniku nr 2

#include "stm32f10x.h"
#include "math.h"
#include "stdint.h"
#include "stdio.h"


#define silnik1_IN1							GPIO_Pin_14
#define silnik1_IN2							GPIO_Pin_15
#define silnik1_IN_GPIO						GPIOD
#define silnik1_PWM							GPIO_Pin_8

#define silnik2_IN1							GPIO_Pin_15
#define silnik2_IN2							GPIO_Pin_14
#define silnik2_IN_GPIO						GPIOE
#define silnik2_PWM							GPIO_Pin_9

#define silniki_PWM_GPIO						GPIOA
#define licznik_max							199


#define enkoder_2A_Pin						GPIO_Pin_6
#define enkoder_2B_Pin						GPIO_Pin_7
#define silnik2_ENKODERY_GPIO_Port			GPIOA




void robot_jazda(int lewy, int prawy)
{


if(lewy >= 0 )
{
	if(lewy > licznik_max)
		lewy = licznik_max;

	GPIO_SetBits(silnik1_IN_GPIO, silnik1_IN1);   //jazda do przodu lewe koło
	GPIO_ResetBits(silnik1_IN_GPIO, silnik1_IN2);


}
else
{
	if(lewy < -licznik_max)
		lewy = -licznik_max;

	GPIO_ResetBits(silnik1_IN_GPIO, silnik1_IN1);
	GPIO_SetBits(silnik1_IN_GPIO, silnik1_IN2);



}


if(prawy >= 0)					     //jazda do przodu prawe koło
{
	if(prawy > licznik_max)
		prawy = licznik_max;

	GPIO_SetBits(silnik2_IN_GPIO, silnik2_IN2);
	GPIO_ResetBits(silnik2_IN_GPIO, silnik2_IN1);


}
else
{
	if(prawy < -licznik_max)
		prawy = -licznik_max;

	GPIO_ResetBits(silnik2_IN_GPIO, silnik2_IN2);
	GPIO_SetBits(silnik2_IN_GPIO, silnik2_IN1);

}


TIM_SetCompare1(TIM1, fabs(lewy));
TIM_SetCompare2(TIM1, fabs(prawy));

}


int main(void)
{
 GPIO_InitTypeDef gpio;
 TIM_TimeBaseInitTypeDef tim;
 TIM_OCInitTypeDef  channel;
 USART_InitTypeDef uart;

 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA |  RCC_APB2Periph_GPIOD| RCC_APB2Periph_GPIOC| RCC_APB2Periph_GPIOE, ENABLE);
 RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1, ENABLE);
 RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
 RCC_APB1PeriphClockCmd(RCC_APB1Periph_UART4, ENABLE);
 RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);


 GPIO_StructInit(&gpio);
 gpio.GPIO_Pin = silnik1_IN1 | silnik1_IN2; // silnik1 wejscia sterujace IN_1 i IN_2
 gpio.GPIO_Mode = GPIO_Mode_Out_PP;
 GPIO_Init(silnik1_IN_GPIO, &gpio);

 gpio.GPIO_Pin = silnik2_IN1 | silnik2_IN2; // silnik2 wejscia sterujace IN_1 i IN_2
 gpio.GPIO_Mode = GPIO_Mode_Out_PP;
 GPIO_Init(silnik2_IN_GPIO, &gpio);

 gpio.GPIO_Pin = GPIO_Pin_5; // led test
 gpio.GPIO_Mode = GPIO_Mode_Out_PP;
 GPIO_Init(GPIOA, &gpio);



 gpio.GPIO_Pin = silnik1_PWM | silnik2_PWM;  // konfiguracja pinow dla trybu pwm
 gpio.GPIO_Speed = GPIO_Speed_50MHz;
 gpio.GPIO_Mode = GPIO_Mode_AF_PP;
 GPIO_Init(silniki_PWM_GPIO, &gpio);

 TIM_TimeBaseStructInit(&tim);
 tim.TIM_CounterMode = TIM_CounterMode_Up;
 tim.TIM_Prescaler = 800 - 1; //80khz
 tim.TIM_Period = 200 - 1;
 TIM_TimeBaseInit(TIM1,&tim);


 TIM_OCStructInit(&channel);
 channel.TIM_OCMode = TIM_OCMode_PWM1;
 channel.TIM_OutputState = TIM_OutputState_Enable;
 channel.TIM_OCNPolarity = TIM_OCPolarity_High;
 TIM_OC1Init(TIM1, &channel);
 TIM_OC1PreloadConfig(TIM1, TIM_OCPreload_Enable);

 channel.TIM_OCMode = TIM_OCMode_PWM1;
 channel.TIM_OutputState = TIM_OutputState_Enable;
 channel.TIM_OCNPolarity = TIM_OCPolarity_High;
 TIM_OC2Init(TIM1, &channel);
 TIM_OC2PreloadConfig(TIM1, TIM_OCPreload_Enable);


 TIM_ARRPreloadConfig(TIM1, ENABLE);
 TIM_CtrlPWMOutputs(TIM1, ENABLE);


 TIM_Cmd(TIM1, ENABLE);


 /*--------------Konfiguracja UART4---------------------*/
 gpio.GPIO_Pin = GPIO_Pin_10;
 gpio.GPIO_Mode = GPIO_Mode_AF_PP;
 gpio.GPIO_Speed = GPIO_Speed_50MHz;
 GPIO_Init(GPIOC, &gpio);

 gpio.GPIO_Pin = GPIO_Pin_11;
 gpio.GPIO_Mode = GPIO_Mode_IN_FLOATING;
 gpio.GPIO_Speed = GPIO_Speed_50MHz;
 GPIO_Init(GPIOC, &gpio);


 USART_StructInit(&uart);
 uart.USART_BaudRate = 9600;
 uart.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
 uart.USART_WordLength = USART_WordLength_8b;
 uart.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
 uart.USART_Parity = USART_Parity_No;
 uart.USART_StopBits = USART_StopBits_1;
 USART_Init(UART4, &uart);

 USART_Cmd(UART4, ENABLE);

 /*------------Konfiguracja uart---------------------------*/



 /*--------------KONFIGURACJA ENKODERA - prawy silnik(silnik2) - licznik TIM3 ---------------------*/

 gpio.GPIO_Pin = enkoder_2A_Pin | enkoder_2B_Pin;
 gpio.GPIO_Mode = GPIO_Mode_AF_PP;
 gpio.GPIO_Speed = GPIO_Speed_50MHz;
 GPIO_Init(silnik2_ENKODERY_GPIO_Port, &gpio);

 tim.TIM_CounterMode = TIM_CounterMode_Up;
 tim.TIM_Period = 0xffff; //wartość po której licznik się przepełnia 
 TIM_TimeBaseInit(TIM3,&tim);

 TIM_EncoderInterfaceConfig(TIM3, TIM_EncoderMode_TI12, TIM_ICPolarity_Rising, TIM_ICPolarity_Rising);
 TIM_Cmd(TIM3, ENABLE);


 /*--------------------------------------------------------*/





 while(1)
 {


	 robot_jazda(40,40); //wypełnienie PWM wynosi 40 dla obu silników, funkcja działa w momencie jej wywolania i koła kręcą się w nieskończoność

	 printf("wartosc=%d\r\n",TIM_GetCounter(TIM3)); //wyswietlana wartość wynosi 0, licznik nic nie zlicza, a powinnien ?!


 }

}

Poniżej zdjęcie z terminala :

Podsumowując :

1) licznik nic nie zlicza, może brakuje jakieś linijki kodu, etc ?!

2) połączenie fizyczne kanałów jest poprawne

[ Dodano: 15-08-2017, 19:16 ]

Uruchomiłem tryb enkoderów, brakowało konfiguracji kilku peryferiów m.in kontrolera przerwań i ustawienia wejść jako

 gpio.GPIO_Mode = GPIO_Mode_IN_FLOATING;

, później wrzucę pełen kod może komuś się przyda 🙂

Link do komentarza
Share on other sites

Dołącz do dyskusji, napisz odpowiedź!

Jeśli masz już konto to zaloguj się teraz, aby opublikować wiadomość jako Ty. Możesz też napisać teraz i zarejestrować się później.
Uwaga: wgrywanie zdjęć i załączników dostępne jest po zalogowaniu!

Anonim
Dołącz do dyskusji! Kliknij i zacznij pisać...

×   Wklejony jako tekst z formatowaniem.   Przywróć formatowanie

  Dozwolonych jest tylko 75 emoji.

×   Twój link będzie automatycznie osadzony.   Wyświetlać jako link

×   Twoja poprzednia zawartość została przywrócona.   Wyczyść edytor

×   Nie możesz wkleić zdjęć bezpośrednio. Prześlij lub wstaw obrazy z adresu URL.

×
×
  • Utwórz nowe...

Ważne informacje

Ta strona używa ciasteczek (cookies), dzięki którym może działać lepiej. Więcej na ten temat znajdziesz w Polityce Prywatności.