- Schemat obwodu
- Generowanie sygnałów PWM na pinie GPIO do sterowania serwomotorem
- Programowanie PIC16F8771A dla ramienia robota
- Symulacja kodu ramienia robota PIC
- Projektowanie PCB przy użyciu EasyEDA
- Obliczanie i zamawianie próbek online
- Działanie ramienia robota PIC
Od linii montażowej przemysłu samochodowego po roboty telchirurgiczne w kosmosie - ramiona robotów można znaleźć wszędzie. Mechanizmy tych robotów są podobne do człowieka, którego można zaprogramować do podobnej funkcji i zwiększonych możliwości. Mogą być używane do wykonywania powtarzających się czynności szybciej i dokładniej niż ludzie lub mogą być używane w trudnych warunkach bez narażania życia ludzkiego. Zbudowaliśmy już ramię robota Record and Play przy użyciu Arduino, które można wyszkolić do wykonania określonego zadania i powtarzać w nieskończoność.
W tym samouczku użyjemy standardowego 8-bitowego mikrokontrolera PIC16F877A do sterowania tym samym ramieniem robota z potencjometrami. Wyzwaniem związanym z tym projektem jest to, że PIC16F877A ma tylko dwa piny obsługujące PWN, ale musimy sterować około 5 serwomotorami dla naszego robota, który wymaga 5 indywidualnych pinów PWM. Musimy więc wykorzystać piny GPIO i wygenerować sygnały PWM na pinach PIC GPIO za pomocą przerwania timera. Teraz, oczywiście, moglibyśmy przejść na lepszy mikrokontroler lub użyć układu scalonego de-multipleksera, aby znacznie ułatwić to zadanie. Mimo to warto dać temu projektowi szansę na naukę.
Struktura mechaniczna ramienia robota, którego używam w tym projekcie, została w całości wydrukowana w 3D dla mojego poprzedniego projektu; można znaleźć pełne pliki projektowe i procedurę montażu tutaj. Alternatywnie, jeśli nie masz drukarki 3D, możesz również zbudować proste ramię robota przy użyciu kartonów, jak pokazano w linku. Zakładając, że w jakiś sposób udało ci się zdobyć ramię robota, przejdźmy do projektu.
Schemat obwodu
Pełny schemat obwodu tego ramienia robota opartego na mikrokontrolerze PIC pokazano poniżej. Schemat został narysowany za pomocą EasyEDA.
Schemat obwodu jest dość prosty; cały projekt zasilany jest z zasilacza 12V. To 12 V jest następnie przekształcane na + 5 V za pomocą dwóch regulatorów napięcia 7805. Jeden jest oznaczony jako + 5 V, a drugi jako + 5 V (2). Powodem posiadania dwóch regulatorów jest to, że gdy serwo obraca się, pobiera dużo prądu, co powoduje spadek napięcia. Ten spadek napięcia zmusza PIC do ponownego uruchomienia, dlatego nie możemy obsługiwać zarówno PIC, jak i serwomotorów na tej samej szynie + 5V. Tak więc ten oznaczony jako + 5 V służy do zasilania mikrokontrolera PIC, wyświetlacza LCD i potencjometrów, a oddzielne wyjście regulatora, które jest oznaczone jako + 5 V (2), służy do zasilania silników serwo.
Pięć pinów wyjściowych potencjometrów, które zapewniają zmienne napięcie od 0 V do 5 V, jest podłączonych do analogowych pinów An0 do AN4 PIC. Ponieważ planujemy wykorzystanie timerów do generowania PWM, serwomotory można podłączyć do dowolnego pinu GPIO. Wybrałem piny od RD2 do RD6 dla serwomotorów, ale może to być dowolne wybrane GPIO.
Ponieważ program wymaga dużo debugowania, wyświetlacz LCD 16x2 jest również połączony z portem B PIC. Spowoduje to wyświetlenie cyklu roboczego serwomotorów, które są sterowane. Oprócz tego rozszerzyłem również połączenia dla wszystkich pinów GPIO i analogowych, na wypadek gdyby jakieś czujniki musiały być połączone w przyszłości. Wreszcie podłączyłem również pin programatora H1, aby bezpośrednio zaprogramować PIC z pickit3 za pomocą opcji programowania ICSP.
Generowanie sygnałów PWM na pinie GPIO do sterowania serwomotorem
Gdy obwód będzie gotowy, musimy dowiedzieć się, jak wygenerować sygnały PWN na pinie GPIO PIC, aby sterować serwomotorem. Zmęczyliśmy już coś podobnego przy użyciu metody przerwania Timer i udało się. Tutaj po prostu będziemy budować na nim, więc jeśli jesteś tu nowy, gorąco polecam przeczytanie tego poprzedniego samouczka, zanim przejdziesz dalej.
Wszystkie serwomotory hobby pracują z częstotliwością 50 Hz. Oznacza to, że jeden pełny cykl impulsów dla serwomotoru będzie wynosić 1/50 (F = 1 / T), czyli 20 ms. Z tych pełnych 20 ms sygnał sterujący wynosi tylko od 0 do 2 ms, podczas gdy reszta sygnału jest zawsze wyłączona. Poniższy rysunek pokazuje, jak czas włączenia zmienia się tylko w zakresie od 0 do 2 ms, aby obrócić silnik od 0 stopni do 180 stopni z całkowitego czasu trwania 20 ms.
Mając to na uwadze, musimy napisać program w taki sposób, aby PIC wczytywał od 0 do 1204 z potencjometru i mapował go na 0 do 100, co będzie cyklem pracy serwomotoru. Korzystając z tego cyklu pracy, możemy obliczyć czas włączenia serwomotoru. Następnie możemy zainicjować przerwanie timera, aby przepełniał się w regularnych odstępach czasu, tak aby działał podobnie do funkcji millis () w Arduino. Dzięki temu możemy przełączyć stan pinów GPIO na wysoki na żądany czas i wyłączyć go po 20 ms (jeden pełny cykl), a następnie powtórzyć ten sam proces. Teraz, gdy zrozumieliśmy logikę, przejdźmy do programu.
Programowanie PIC16F8771A dla ramienia robota
Jak zawsze kompletny program z filmem znajduje się na końcu tej strony, kod można również pobrać stąd ze wszystkimi potrzebnymi plikami. W tej sekcji omówimy logikę działania programu. Program wykorzystuje moduł ADC, moduł timera i moduł LCD do sterowania ramieniem robota. Jeśli nie wiesz, jak korzystać z funkcji ADC lub funkcji timera lub jak połączyć wyświetlacz LCD z PIC, możesz wrócić do odpowiednich łączy, aby się ich nauczyć. Poniższe wyjaśnienie podano przy założeniu, że czytelnik jest zaznajomiony z tymi pojęciami.
Konfiguracja portu zegara 0
Najważniejszą sekcją w kodzie jest ustawienie timera 0 na przepełnienie dla każdego określonego opóźnienia. Formuły do obliczenia tego opóźnienia można podać jako
Opóźnienie = ((256-REG_val) * (Prescal * 4)) / Fosc
Używając rejestru OPTION_REG i TMR0 ustawiliśmy Timer 0 tak, aby działał z preskalarną wartością 32, a wartość REG jest ustawiona na 248. Częstotliwość krystaliczna (Fosc) używana w naszym sprzęcie wynosi 20 MHz. Przy tych wartościach opóźnienie można obliczyć jako
Opóźnienie = ((256-248) * (32 * 4)) / (20000000) = 0,0000512 sekund (lub) = 0,05 ms
Więc teraz ustawiliśmy licznik czasu na przepełnienie co 0,05 ms. Kod umożliwiający to samo podano poniżej
/ ***** Konfiguracja portu dla timera ****** / OPTION_REG = 0b00000100; // Timer0 z zewnętrznym freq i 32 jako prescalar // Umożliwia również PULL UPs TMR0 = 248; // Załaduj wartość czasu dla 0.0001s; delayValue może zawierać się w przedziale 0-256, tylko TMR0IE = 1; // Włącz bit przerwania timera w rejestrze PIE1 GIE = 1; // Włącz globalne przerwanie PEIE = 1; // Włącz przerwanie peryferyjne / *********** ______ *********** /
Z całkowitego okna sterowania serwomotorem od 0 ms do 2 ms możemy sterować nim z rozdzielczością 0,05 ms, co pozwala nam mieć (2 / 0,05) 40 różnych położeń silnika w zakresie od 0 stopni do 180 stopni. Możesz jeszcze bardziej zmniejszyć tę wartość, jeśli MCU może ją obsługiwać, aby uzyskać więcej pozycji i precyzyjną kontrolę.
Procedura obsługi przerwania (ISR)
Teraz, gdy zegar 0 jest ustawiony na przepełnienie co 0,05 ms, będziemy mieć flagę przerwania TMR0IF ustawioną na 0,05 ms. Więc wewnątrz funkcji ISR możemy zresetować tę flagę i zwiększyć zmienną o nazwie count o jeden. Więc teraz ta zmienna będzie zwiększana o 1 co 0,05 ms.
void breaking timer_isr () { if (TMR0IF == 1) // Flaga timera została wyzwolona z powodu przepełnienia timera -> ustawiona na przepełnienie co 0,05 ms { TMR0 = 248; // Załaduj licznik czasu Wartość TMR0IF = 0; // Wyczyść licznik przerwania licznika czasu ++; // Zliczaj przyrosty o 1 na każde 0,05 ms }
Obliczanie cyklu pracy i czasu
Następnie musimy obliczyć cykl pracy i czas dla wszystkich pięciu serwomotorów. Mamy pięć serwomotorów, z których każdy służy do sterowania poszczególnymi sekcjami ramienia. Musimy więc odczytać wartość ADC wszystkich pięciu i obliczyć cykl pracy i czas dla każdego.
Wartość ADC będzie mieściła się w zakresie od 0 do 1024, co można przekształcić na 0% do 100% cyklu pracy, mnożąc po prostu 0,0976 (100/1024 = 0,0976) przez otrzymaną wartość. Ten cykl pracy od 0 do 100% należy następnie przekształcić w czas włączenia. Wiemy, że przy 100% cyklu pracy czas włączenia musi wynosić 2 ms (dla 180 stopni), więc pomnożenie 0,02 (2/100 = 0,02) przekształci 0 do 100 cyklu pracy na 0 do 2 ms. Ale wtedy liczba naszych zmiennych czasowych będzie zwiększana raz na 0,05 ms. Oznacza to, że wartość zliczenia będzie wynosić 20 (1 / 0,05 = 20) na każdy 1 ms. Musimy więc pomnożyć 20 przez 0,02, aby obliczyć dokładny czas dla naszego programu, który da nam wartość 0,4 (0,02 * 20 = 0,4). Kod dla tego samego jest pokazany poniżej, możesz go zobaczyć powtórzony 5 razy dla wszystkich 5 potencjometrów, używając pętli for. Wynikowe wartości są przechowywane w tablicy T_ON.
for (int pot_num = 0; pot_num <= 3; pot_num ++) { int Pev_val = T_ON; POT_val = (ADC_Read (pot_num)); // Odczytaj wartość POT używając ADC Duty_cycle = (POT_val * 0.0976); // Odwzoruj od 0 do 1024 do 0 do 100 T_ON = Duty_cycle * 0,4; // 20 * 0,02
Wybór silnika do obracania
Nie możemy sterować wszystkimi pięcioma silnikami razem, ponieważ spowoduje to znaczne spowolnienie całego mikrokontrolera przez kod ISR. Musimy więc obracać tylko jeden silnik serwo na raz. Aby wybrać serwomechanizm do obrócenia, mikrokontroler monitoruje czas włączenia wszystkich pięciu serwomotorów i porównuje go z poprzednim czasem. Jeśli nastąpi zmiana czasu włączenia, możemy wywnioskować, że określone serwo musi zostać przesunięte. Kod tego samego jest pokazany poniżej.
if (T_ON! = Pev_val) { Lcd_Clear (); servo = pot_num; Lcd_Set_Cursor (2,11); Lcd_Print_String ("S:"); Lcd_Print_Char (serwo + '0'); if (pot_num == 0) {Lcd_Set_Cursor (1,1); Lcd_Print_String ("A:");} else if (pot_num == 1) {Lcd_Set_Cursor (1,6); Lcd_Print_String ("B:");} else if (pot_num == 2) {Lcd_Set_Cursor (1,11); Lcd_Print_String ("C:");} else if (pot_num == 3) {Lcd_Set_Cursor (2,1); Lcd_Print_String ("D:");} else if (pot_num == 4) {Lcd_Set_Cursor (2,6); Lcd_Print_String ("E:");} char d2 = (Duty_cycle)% 10; char d1 = (Duty_cycle / 10)% 10; Lcd_Print_Char (d1 + '0'); Lcd_Print_Char (d2 + '0');
Cykl pracy serwomechanizmu drukujemy również na ekranie LCD, aby użytkownik był świadomy jego aktualnej pozycji. Na podstawie zmiany czasu włączenia zmienne serwomechanizm jest aktualizowany numerami od 0 do 4, z których każdy reprezentuje poszczególne silniki.
Sterowanie serwomotorem wewnątrz ISR
Wewnątrz ISR mamy zwiększaną liczbę zmiennych co 0,05 ms, co oznacza, że co 1 ms zmienna będzie zwiększana o 20. Używając tego musimy sterować pinami, aby wytworzyć sygnał PWM. Jeśli wartość zliczenia jest mniejsza niż czas włączenia, wówczas GPIO tego silnika jest włączane za pomocą poniższej linii
PORTD = PORTD - servo_code;
Tutaj tablica servo_code zawiera szczegóły pinów wszystkich pięciu serwomotorów i na podstawie wartości zmiennej servo, zostanie użyty kod dla tego konkretnego serwomotoru. Jest wtedy logicznie OR (-) z istniejącymi bitami PORTD, aby nie zakłócać wartości innego silnika i aktualizować tylko ten konkretny silnik. Podobnie do wyłączania szpilki
PORTD = PORTD & ~ (servo_code);
Odwróciliśmy wartość bitu za pomocą logicznego operatora odwrotnego (~), a następnie wykonaliśmy operację AND (&) na PORTD, aby wyłączyć tylko żądany pin, pozostawiając pozostałe piny w ich poprzednim stanie. Pełny fragment kodu pokazano poniżej.
void breaking timer_isr () { if (TMR0IF == 1) // Flaga timera została wyzwolona z powodu przepełnienia timera -> ustawione na przepełnienie co 0,05 ms { TMR0 = 248; // Załaduj licznik czasu Wartość TMR0IF = 0; // Wyczyść licznik przerwania licznika czasu ++; // Zliczaj przyrosty o 1 na każde 0,05 ms -> liczba będzie wynosić 20 na każdy 1 ms (0,05 / 1 = 20)) } int servo_code = {0b01000000, 0b00100000, 0b00010000, 0b00001000, 0b00000100}; if (count> = 20 * 20) count = 0; if (count <= (T_ON)) PORTD = PORTD - kod_serwo; else PORTD = PORTD & ~ (kod_serwera); }
Wiemy, że całkowity cykl musi trwać 20 ms, zanim pin GPIO zostanie ponownie włączony. Więc sprawdzamy, czy licznik przekroczył 20 ms, porównując wartość zliczenia z 400 (te same obliczenia, co omówiono powyżej), a jeśli tak, musimy ponownie zainicjować licznik, aby był zerowy.
Symulacja kodu ramienia robota PIC
Zawsze lepiej jest zasymulować kod przed przeniesieniem go na rzeczywisty sprzęt. Więc użyłem Proteusa do symulacji mojego kodu i zweryfikowałem, że działa poprawnie. Obwód używany do symulacji jest pokazany poniżej. Użyliśmy oscyloskopu do sprawdzenia, czy sygnały PWM są generowane zgodnie z wymaganiami. Możemy również sprawdzić, czy silnik LCD i serwomotor obracają się zgodnie z oczekiwaniami.
Jak widać wyświetlacze LCD cykl pracy silnika D będzie 07 w oparciu o wartość puli, która jest 3 rd silnika. Podobnie, jeśli przesuniesz inny garnek, cykl pracy tego potencjometru i jego numer silnika zostaną wyświetlone na wyświetlaczu LCD. Sygnał PWM pokazany na oscyloskopie pokazano poniżej.
Całkowity okres cyklu wynosi 22,2 ms przy użyciu opcji kursora na oscyloskopie, co jest bardzo zbliżone do żądanych 20 ms. W końcu jesteśmy pewni, że kod działa, więc aby kontynuować układ, możemy go przylutować na płytce perf lub użyć PCB. Nie będzie działać łatwo na płytce stykowej, ponieważ POT zawsze powoduje pewne problemy z powodu słabych połączeń.
Projektowanie PCB przy użyciu EasyEDA
Aby zaprojektować to ramię robota PIC, wybraliśmy internetowe narzędzie EDA o nazwie EasyEDA. Używam go już od dłuższego czasu i uważam go za bardzo wygodny ze względu na dużą dostępność powierzchni i łatwy w użyciu charakter. Po zaprojektowaniu PCB możemy zamówić próbki PCB, korzystając z ich tanich usług produkcji PCB. Oferują również usługi zaopatrzenia w komponenty, w przypadku których mają duże zapasy komponentów elektronicznych, a użytkownicy mogą zamówić wymagane komponenty wraz z zamówieniem PCB.
Projektując obwody i płytki drukowane, możesz również upublicznić swoje projekty obwodów i płytek drukowanych, aby inni użytkownicy mogli je kopiować lub edytować i mogli czerpać korzyści z Twojej pracy, upubliczniliśmy również nasze całe układy obwodów i PCB dla tego obwodu, sprawdź poniższy link:
easyeda.com/circuitdigest/pic-development-board-for-robotic-arm
Korzystając z tego linku, możesz bezpośrednio zamówić tę samą płytkę drukowaną, której używamy w tym projekcie i użyć jej. Po zakończeniu projektowania płytkę można wyświetlić jako model 3D, co będzie bardzo pomocne w wizualizacji wyglądu płyty po wykonaniu. Model 3D planszy, której używamy, pokazano poniżej. Oprócz tego możesz również wyświetlić górną i dolną warstwę planszy, aby sprawdzić, czy gładki ekran jest zgodny z oczekiwaniami.
Obliczanie i zamawianie próbek online
Po zakończeniu projektowania tej płytki PCB robota PIC można zamówić płytkę drukowaną za pośrednictwem witryny JLCPCB.com. Aby zamówić PCB w JLCPCB, potrzebujesz Gerber File. Aby pobrać pliki Gerber ze swojej płytki PCB, po prostu kliknij przycisk Generuj plik produkcyjny na stronie edytora EasyEDA, a następnie pobierz plik Gerber stamtąd lub kliknij Zamów w JLCPCB, jak pokazano na poniższym obrazku. Spowoduje to przekierowanie do JLCPCB.com, gdzie możesz wybrać liczbę PCB, które chcesz zamówić, ile warstw miedzi potrzebujesz, grubość PCB, wagę miedzi, a nawet kolor PCB, tak jak pokazano poniżej:
Po wybraniu wszystkich opcji kliknij „Zapisz w koszyku”, a zostaniesz przeniesiony do strony, na której możesz załadować swój plik Gerber pobrany z EasyEDA. Prześlij swój plik Gerber i kliknij „Zapisz w koszyku”. Na koniec kliknij Bezpiecznie kasy, aby sfinalizować zamówienie, a płytki PCB otrzymasz kilka dni później. Produkują PCB w bardzo niskiej cenie, która wynosi 2 dolary. Ich czas budowy jest również znacznie krótszy, czyli 48 godzin przy dostawie DHL w ciągu 3-5 dni, w zasadzie PCB otrzymasz w ciągu tygodnia od zamówienia.
Po zamówieniu PCB możesz sprawdzić postęp produkcji swojej PCB wraz z datą i godziną. Możesz to sprawdzić wchodząc na stronę Konta i klikając „Postęp produkcji”.
Po kilku dniach zamawiania PCB otrzymałem próbki PCB w ładnym opakowaniu, jak pokazano na poniższych zdjęciach.
A po otrzymaniu tych elementów przylutowałem wszystkie wymagane elementy na PCB. Przylutowałem też bezpośrednio POT, zamiast używać przewodów połączeniowych, ponieważ przewody żeńskie do żeńskiego, których początkowo użyłem, dawały dziwne analogowe napięcia wyjściowe, prawdopodobnie z powodu luźnych styków. Po złożeniu wszystkich komponentów moja płytka wyglądała mniej więcej tak.
Być może zauważyłeś, że na tej płycie jest tylko jeden 7805. To dlatego, że początkowo myślałem, że wystarczy regulator do zasilania zarówno PIC, jak i silnika serwo, a później zdałem sobie sprawę, że potrzebuję dwóch. Więc użyłem zewnętrznego obwodu do zasilania serwomotorów przez zielone przewody, które tutaj widzisz.
Niemniej jednak nie musisz się tym zbytnio martwić, ponieważ; Dokonałem teraz zmian w PCB. Możesz skorzystać ze zmodyfikowanej płytki drukowanej i przylutować oba regulatory na samej płycie.
Działanie ramienia robota PIC
Po całej męczącej pracy czas na spłatę. Przylutuj wszystkie komponenty na płycie i załaduj program do kontrolera PIC. Pełny kod jest podany poniżej lub można go pobrać stąd. Złącze programistyczne znajdujące się na płytce powinno pomóc w załadowaniu programu bezpośrednio za pomocą Pickit 3 bez większych kłopotów. Po załadowaniu programu powinieneś zobaczyć wyświetlacz LCD pokazujący aktualnie sterowane serwo. Aby dowiedzieć się więcej o programowaniu mikrokontrolera PIC, postępuj zgodnie z poprzednim samouczkiem.
Stamtąd możesz po prostu przekręcić potencjometr i sprawdzić, jak serwomotory reagują na każdy potencjometr. Kiedy zrozumiesz format, możesz sterować ramieniem robota, aby wykonywać dowolne czynności i dobrze się bawić. Pełne działanie projektu można znaleźć w filmie, do którego link znajduje się poniżej.
To znaczy, chłopaki, mają nadzieję, że zrozumieliście projekt i nauczyliście się z niego czegoś nowego. Jeśli masz jakieś pytania, zostaw je w sekcji komentarzy lub skorzystaj z forów do innych dyskusji technicznych.