Mikrokontrolery - Jak zacząć?

... czyli zbiór praktycznej wiedzy dot. mikrokontrolerów.

środa, 16 marca 2011

DIY: Sterowanie silnikami krokowymi za pomocą Bluetooth i Android


Autor: Henio
Redakcja: Dondu

W tym artykule przedstawię sposób na sterowanie dwoma silnikami krokowymi unipolarnymi poprzez moduł bluetooth i system Android.

Projekt ten powstał (i dalej powstaje) do sterowania laserem. Jest to fajne działko pozwalające w miarę precyzyjnie sterować punktem na niewielkich odległościach.

Sterowanie może nie jest jeszcze doskonałe, ale można je wykorzystać do różnych celów, np. silniki mogą poruszać głowicą po szynach skierowaną w dół itp.

Część mechaniczna wygląda następująco:


Działko laserowe - wygląd części mechanicznej.
Wygląd części mechanicznej działka.

a w działaniu prezentuje się tak:






  

Sterowanie silnikami

Do projektu wykorzystałem dwa takie same silniki krokowe unipolarne, które pochodzą z drukarki. Cewki silnika są zasilane napięciem 12V i są sterowane poprzez tranzystory MOSFET z kanałem typu N "BS170".



Schemat podłączenia tranzystorów do silnika.


Tranzystory do cewek silnika są podłączone tak:


Podłączenie cewek silnika krokowego.
Podłączenie cewek silnika.


Jak wyznaczyć uzwojenia takiego silnika można przeczytać w artykule który znajduje się na blogu: Silnik krokowy

Sterowanie silnika odbywa się półkrokowo, poniższa tabela pokazuje sekwencje załączania tranzystorów.


Silnik krokowy - sekwencje sterowania.
Sekwencje sterowania

Polega to na tym że każda kolejna sekwencja jest wykonywana co 2ms. Tranzystory są tak włączane i wyłączane że każda z cewek jest zasilana przez 3 pełne sekwencje, z czego kolejna przeciwległa cewka jest już zasilana podczas ostatniej sekwencji wcześniejszej cewki.





Schemat i elementy

Teraz przedstawię schemat całego układu oraz plik Eagle do pobrania. Nie będę zamieszczał piku płytki ponieważ nie jest zbyt dobrze zrobiona, a poza tym każdy musi dopasować wyprowadzenia do silników.
Schemat do pobrania: Schemat


Schemat całego układu.

Jak widać na schemacie wykorzystany uC to ATmega32A, jednak podejrzewam że wszystko włącznie z kodem programu powinno pasować do ATmegi8 ponieważ wykorzystuję tylko interfejs USART, oraz TIMER0, a jak się nie mylę to rejestry są takie same, przynajmniej do USART na 100%. Na schemacie jest też złącze P/S2 Mouse, ale obecnie jest niewykorzystywane więc można sobie odpuścić. Wyświetlacz też nie jest wykorzystywany lecz można go zostawić, gdyż może się przydać do ewentualnej rozbudowy.

Niewykorzystane piny uC są podłączone do goldpinów w celu ewentualnej rozbudowy. Są też wypuszczone piny +5V, +12V oraz masy. Zastosowany moduł bluetooth to HC-05 przystosowany do poziomów napięć +5V. Podłączony jest też kwarc 16 MHz jednak obecnie wykorzystany jest wewnętrzny oscylator 8 MHz.


Wygląd płytki

Wygląd płytki




Kod programu

Opis działania programu jest zawarty w komentarzach, mam nadzieję że wszystko będzie czytelne i zrozumiałe. Oczywiście sposób komunikacji USART jest skopiowany z artykułu na blogu: RS-232: Komunikacja ATmega8 z komputerem

main.c
//częstotliwość zegara
//#define F_CPU 8000000UL
// Częstotliwość F_CPU ustaw w opcjach projektu zgodnie z:
// http://mikrokontrolery.blogspot.com/2011/03/fcpu-gcc-gdzie-definiowac.html

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

//definicja MOS N
#define MOS1 PA0
#define MOS2 PA1
#define MOS3 PA2
#define MOS4 PA3

#define MOS5 PA4
#define MOS6 PA5
#define MOS7 PA6
#define MOS8 PA7

#define LASER PB0           //definicja portu pod którym mam podpięty laser
//kiedy silniki są w ruchu na pinie jest "0" logiczne
//kiedy silniki stoją na pinie jest "1" logiczne

#define MOS1_ON PORTA |= (1<<MOS1);
#define MOS1_OFF PORTA &= ~(1<<MOS1);

#define MOS2_ON PORTA |= (1<<MOS2);
#define MOS2_OFF PORTA &= ~(1<<MOS2);

#define MOS3_ON PORTA |= (1<<MOS3);
#define MOS3_OFF PORTA &= ~(1<<MOS3);

#define MOS4_ON PORTA |= (1<<MOS4);
#define MOS4_OFF PORTA &= ~(1<<MOS4);

#define MOS5_ON PORTA |= (1<<MOS5);
#define MOS5_OFF PORTA &= ~(1<<MOS5);

#define MOS6_ON PORTA |= (1<<MOS6);
#define MOS6_OFF PORTA &= ~(1<<MOS6);

#define MOS7_ON PORTA |= (1<<MOS7);
#define MOS7_OFF PORTA &= ~(1<<MOS7);

#define MOS8_ON PORTA |= (1<<MOS8);
#define MOS8_OFF PORTA &= ~(1<<MOS8);

#define LASER_ON PORTB &= ~(1<<LASER);
#define LASER_OFF PORTB |= (1<<LASER);

#define LICZNIK 6 //max 90 (to zależy od silnika, przy moim jest 90) //6 = 2ms


//oznaczenia: zmienna kończy się na _v - zmienna sterująca silnikiem pionowym, _h - zmienna sterująca silnikiem poziomym;

#define OPOZNIENIE_V 0    //ile przerwań timera silnik ma czekać po każdym kroku silnika
#define OPOZNIENIE_H 0    //ustawiając zwalniamy pracę silnika

volatile int wait_time_h = 0; //ile już czekał
volatile int wait_time_v = 0;

volatile int seq_v =
  0;     //każdy krok silnika składa się z 9 'podkroków', zmienna ta przechowuje informację w którym 'podkroku' silnik się znajduje
volatile int seq_h = 0;

volatile int max_v =
  30;    //maksymalna ilość kroków silnika (maksymalne 'obrót' w jedną stronę)
volatile int max_h =
  100;   //ustawiamy tutaj "rozdzielczoć" np. przy wartociach 100x100 silnik będzie krelił po kwadracie, przy różnych silnikach lub przekładniach tutaj dowiadczalnie możemy wyskalować ich pracę do np. panoramy

volatile int aktualna_pozycja_v =
  0;  //aktualna pozycja w jakiej silnik się znajduje
volatile int aktualna_pozycja_h = 0;

volatile int docelowa_pozycja_v =
  0;  //docelowa pozycja na jaką silnik ma się przesunąć
volatile int docelowa_pozycja_h = 0;


char numbers[2][6];           //w tej tablicy przechowywane są odbierane liczby w postaci tekstowej
unsigned short number = 0, length[2] = {0, 0};   //number - zmienna określająca która liczba jest aktualnie odbierana, langth - długości odbieranych liczb
int vertical = 0, horizontal = 0; //zmienne przechowujące pozycję w postaci
                                  // int po przeanalizowaniu odebranego pakietu
short msg = 0; //zmienna ta służy do kontroli ilości otrzymanych danych w jednym
               // pakiecie (w jednym pakiecie przesyłana jest pozycja dla silnika
               //poziomego i pionowego więc po sparsowaniu pakietu ta zmienna
               //powinna wynosić 2, jeśli jest inna to pakiet jest ignorowany)

//zmienne dot. odbioru danych
volatile unsigned char odb_x;       //odebrana liczba X
volatile unsigned char odb_flaga
  =0;//flaga informująca main o odebraniu liczby

//=====================================
//================ LISTA ==============
//=====================================
//lista przechowująca kolejne punkty na które mają się przesunąć silniki. każdy element listy ma wskaźnik na kolejny element

typedef struct {        //struktura (nowy typ danych) przechowująca punkt na jaki mają się przesunąć silniki
  int pion;
  int poziom;
} point;

typedef struct {        //sktuktura przechowująca punkt na który ma się przesunąć silnik oraz wskaźnik do następnego elementu listy
  point punkt;
  struct node *next;
} node;

node *first = NULL;       //wskaźnik na pierwszy element listy
node *last = NULL;        //wskaźnik na ostatni element listy

point *tmp = NULL;        //zmienna tymczasowa przechowująca punkt;

point *getPoint()         //funkcja zwracająca wskaźnik do point, zwraca ona pierwszy punkt z listy
{
  if(first == NULL) {     //jeśli lista jest pusta to zwraca NULL
    return NULL;
  } else {          //jeśli nie to zwraca pierwszy element
    return &(first->punkt);
  }
}

void addPoint(int pion, int poziom) //funkcja dodająca punkt na koniec listy
{
  if(last == NULL) {            //jeśli lista jest pusta to first i last == NULL
    first = malloc(sizeof(node)); //alokacja pamięci o rozmiarze jednego 
                                  //elementu (node) i przypisanie do wskaźnika
                                  //first
    first->punkt.pion = pion;     //przypisanie wartości do punktu
    first->punkt.poziom = poziom;
    first->next = NULL;     //lista ma tylko jeden element, więc wskaźnik
                            //next == NULL (wtedy wiemy że to jest koniec listy)
    last = first;           //lista ma tylko jeden element, więc first i last wskazuje na ten sam element, dlatego do last przypisujemy first;
  } else {
    last->next = malloc(sizeof(
                          node));  //lista nie była pusta więc pod wskaźnik next w ostatnim elemencie przypisujemy wskaźnik do nowo zaalokowanej pamięci dla nowego elementu
    last = last->next;          //teraz ostatnim elementem jest nowo zaalokowany element więc przypisujemy go do wskaźnika last
    last->punkt.pion = pion;      //przypisanie wartości do punktu
    last->punkt.poziom = poziom;
    last->next = NULL;    //last wskazuje na ostatni element więc wskaźnik 
                             //next w tym elemencie musi wskazywać NULL
  }
}

void removePoint()              //funkcja usuwająca pierwszy element z listy
{
  if(first != NULL) {           //jeśli lista nie jest pusta
    if(first->next != NULL) {     //jeśli lista ma więcej niż jeden element
      node *next = first->next;  //do tymczasowego wskaźnika przypisujemy
                                     // wskaźnik na następny element
      free(first);          //zwalniamy pamięć pierwszego elementu
      first = next;         //ten element co był następny jest teraz pierwszym elementem więc przypisujemy go do wskaźnika first
    } else {              //jeśli lista ma tylko jeden element
      free(first);          //zwalnia pamięć tego elementu
      first = NULL;         //przypisuje first i last NULL żeby było wiadomo że lista jest pusta
      last = NULL;
    }
  }
}

//=====================================
//=====================================
//=====================================

void usart_inicjuj(void)
{
  //definiowanie parametrów transmisji za pomocą makr zawartych w pliku
  //nagłówkowym setbaud.h. Jeżeli wybierzesz prędkość, która nie będzie
  //możliwa do realizacji otrzymasz ostrzeżenie:
  //#warning "Baud rate achieved is higher than allowed"

#define BAUD 9600        //tutaj podaj żądaną prędkość transmisji
#include <util/setbaud.h> //linkowanie tego pliku musi być
  //po zdefiniowaniu BAUD

  //ustaw obliczone przez makro wartości
  UBRRH = UBRRH_VALUE;
  UBRRL = UBRRL_VALUE;
#if USE_2X
  UCSRA |= (1<<U2X);
#else
  UCSRA &= ~(1<<U2X);
#endif


  //Ustawiamy pozostałe parametry moduł USART
  //U W A G A !!!
  //W ATmega8, aby zapisać do rejestru UCSRC należy ustawiać bit URSEL
  //zobacz także: http://mikrokontrolery.blogspot.com/2011/04/avr-czyhajace-pulapki.html#avr_pulapka_2
  UCSRC = (1<<URSEL) | (1<<UCSZ1) | (1<<UCSZ0);  //bitów danych: 8
  //bity stopu:  1
  //parzystość:  brak
  //włącz nadajnik i odbiornik oraz ich przerwania odbiornika
  //przerwania nadajnika włączamy w funkcji wyslij_wynik()
  UCSRB = (1<<TXEN) | (1<<RXEN) | (1<<RXCIE);
}


//--------------------------------------------------------------


ISR(USART_RXC_vect)
{
  //przerwanie generowane po odebraniu bajtu
  odb_x = UDR;   //zapamiętaj odebraną liczbę
  odb_flaga = 1; //ustaw flagę odbioru liczby dla main()
}


//--------------------------------------------------------------

//sekwencja impulsów podawana na silnik krokowy
//po wywołaniu odpowiedniej funkcji każdy kolejny case będzie się wykonywał co jedno przerwanie timera (w tym przypadku 2ms)
//funkcja dla obrotów w lewo
void obroty_l(int seq)
{
  switch(seq) {
  case 0:
    MOS1_OFF;
    MOS2_OFF;
    MOS3_ON;
    MOS4_OFF;
    break;

  case 1:
    MOS1_OFF;
    MOS2_ON;
    MOS3_ON;
    MOS4_OFF;
    break;

  case 2:
    MOS1_OFF;
    MOS2_ON;
    MOS3_OFF;
    MOS4_OFF;
    break;

  case 3:
    MOS1_ON;
    MOS2_ON;
    MOS3_OFF;
    MOS4_OFF;
    break;

  case 4:
    MOS1_ON;
    MOS2_OFF;
    MOS3_OFF;
    MOS4_OFF;
    break;

  case 5:
    MOS1_ON;
    MOS2_OFF;
    MOS3_OFF;
    MOS4_ON;
    break;

  case 6:
    MOS1_OFF;
    MOS2_OFF;
    MOS3_OFF;
    MOS4_ON;
    break;

  case 7:
    MOS1_OFF;
    MOS2_OFF;
    MOS3_ON;
    MOS4_ON;
    break;

  case 8:
    MOS1_OFF;
    MOS2_OFF;
    MOS3_OFF;
    MOS4_OFF;
    break;

  default:
    MOS1_OFF;
    MOS2_OFF;
    MOS3_OFF;
    MOS4_OFF;
    break;
  }
}

//funkcja dla obrotów w prawo
void obroty_p(int seq)
{
  switch(seq) {
  case 0:
    MOS1_OFF;
    MOS2_OFF;
    MOS3_ON;
    MOS4_ON;
    break;

  case 1:
    MOS1_OFF;
    MOS2_OFF;
    MOS3_OFF;
    MOS4_ON;
    break;

  case 2:
    MOS1_ON;
    MOS2_OFF;
    MOS3_OFF;
    MOS4_ON;
    break;

  case 3:
    MOS1_ON;
    MOS2_OFF;
    MOS3_OFF;
    MOS4_OFF;
    break;

  case 4:
    MOS1_ON;
    MOS2_ON;
    MOS3_OFF;
    MOS4_OFF;
    break;

  case 5:
    MOS1_OFF;
    MOS2_ON;
    MOS3_OFF;
    MOS4_OFF;
    break;

  case 6:
    MOS1_OFF;
    MOS2_ON;
    MOS3_ON;
    MOS4_OFF;
    break;

  case 7:
    MOS1_OFF;
    MOS2_OFF;
    MOS3_ON;
    MOS4_OFF;
    break;

  case 8:
    MOS1_OFF;
    MOS2_OFF;
    MOS3_OFF;
    MOS4_OFF;
    break;

  default:
    MOS1_OFF;
    MOS2_OFF;
    MOS3_OFF;
    MOS4_OFF;
    break;
  }
}


//funkcja dla obrotów w dół
void obroty_d(int seq)
{
  switch(seq) {
  case 0:
    MOS5_OFF;
    MOS6_OFF;
    MOS7_ON;
    MOS8_OFF;
    break;

  case 1:
    MOS5_OFF;
    MOS6_ON;
    MOS7_ON;
    MOS8_OFF;
    break;

  case 2:
    MOS5_OFF;
    MOS6_ON;
    MOS7_OFF;
    MOS8_OFF;
    break;

  case 3:
    MOS5_ON;
    MOS6_ON;
    MOS7_OFF;
    MOS8_OFF;
    break;

  case 4:
    MOS5_ON;
    MOS6_OFF;
    MOS7_OFF;
    MOS8_OFF;
    break;

  case 5:
    MOS5_ON;
    MOS6_OFF;
    MOS7_OFF;
    MOS8_ON;
    break;

  case 6:
    MOS5_OFF;
    MOS6_OFF;
    MOS7_OFF;
    MOS8_ON;
    break;

  case 7:
    MOS5_OFF;
    MOS6_OFF;
    MOS7_ON;
    MOS8_ON;
    break;

  case 8:
    MOS5_OFF;
    MOS6_OFF;
    MOS7_OFF;
    MOS8_OFF;
    break;

  default:
    MOS5_OFF;
    MOS6_OFF;
    MOS7_OFF;
    MOS8_OFF;
    break;
  }
}


//funkcja dla obrotów w górę
void obroty_g(int seq)
{
  switch(seq) {
  case 0:
    MOS5_OFF;
    MOS6_OFF;
    MOS7_ON;
    MOS8_ON;
    break;

  case 1:
    MOS5_OFF;
    MOS6_OFF;
    MOS7_OFF;
    MOS8_ON;
    break;

  case 2:
    MOS5_ON;
    MOS6_OFF;
    MOS7_OFF;
    MOS8_ON;
    break;

  case 3:
    MOS5_ON;
    MOS6_OFF;
    MOS7_OFF;
    MOS8_OFF;
    break;

  case 4:
    MOS5_ON;
    MOS6_ON;
    MOS7_OFF;
    MOS8_OFF;
    break;

  case 5:
    MOS5_OFF;
    MOS6_ON;
    MOS7_OFF;
    MOS8_OFF;
    break;

  case 6:
    MOS5_OFF;
    MOS6_ON;
    MOS7_ON;
    MOS8_OFF;
    break;

  case 7:
    MOS5_OFF;
    MOS6_OFF;
    MOS7_ON;
    MOS8_OFF;
    break;

  case 8:
    MOS5_OFF;
    MOS6_OFF;
    MOS7_OFF;
    MOS8_OFF;
    break;

  default:
    MOS5_OFF;
    MOS6_OFF;
    MOS7_OFF;
    MOS8_OFF;
    break;
  }
}

int parseInt(volatile char *tab, int lendth)  
//funkcja zmieniająca tekst na zmienną int. *tab wskaźnik na ten tekst, 
//lendth - długość tego tekstu
{
  int value = 0;           //ustawienie początkowej wartości na 0
  int multipler = 1;       //mnożnik
  int i;

  for(i=lendth-1; i>=0;
      --i) {            //lecimy od końca dopóki i nie będzie == 0
    if(tab[i] == '-') { //jeśli analizowany znak == '-'
                        //to znaczy że liczba jest ujemna więc 
                        //trzeba zmienić znak w value
      value *= -1;
    } else {
      value += (tab[i]-48) *  multipler;//jeśli nie to znaczy że jest to jakaś
           //liczba więc trzeba od tego znaku odjąć 48 (patrz tablica ascii 
           //liczba 0 ma wartość 48, 1 ma wartość 49 więc jak odejmiemy 48 
           //to będziemy mieć liczbę taką jaką chcemy), trzeba to pomnożyć
           //przez mnożnik pozycji
    }

    multipler *= 10;                //zwiększenie mnożnika
  }

  return value;
}

int main(void)
{

  LCD_Initalize();              //inicjalizacja LCD

  DDRA |= (1<<MOS1) | (1<<MOS2) | (1<<MOS3) | (1<<MOS4) | (1<<MOS5) |
          (1<<MOS6) | (1<<MOS7) | (1<<MOS8); // wyjscia
  DDRB |= (1<<LASER);

  //na początku wszystkie tranzystory wyłaczone
  MOS1_OFF;
  MOS2_OFF;
  MOS3_OFF;
  MOS4_OFF;
  MOS5_OFF;
  MOS6_OFF;
  MOS7_OFF;
  MOS8_OFF;

  LASER_OFF;

  TIMSK |= (1<<TOIE0);            //przerwanie overflow
  TCCR0 |= (1<<CS01) | (1<<CS00);       //prescaler 64
  TCNT0 = LICZNIK;              //poczatkowa wartosc licznika

  usart_inicjuj();              //inicjuj moduł USART (RS-232)
  sei();                    //globalne uruchomienie przerwań

  while(1) {

    if(odb_flaga) {             //jeśli odebrano jakiś znak
      switch(odb_x) {           //sprawdzenie jaki to znak
      case 'v':           //jeśli to v to
        number = 0;         //jest to liczba 0 w tablicy
        length[number] = 0;     //zerowanie długości tej liczby
        msg++;    //zwiększenie msg bo odebraliśmy liczbę v (tzn
                  //zaczynamy odbierać)
        break;

      case 'h':           //jeśli to h to
        number = 1;         //jest to liczba 1 w tablicy
        length[number] = 0;     //zerowanie długości tej liczby
        msg++;    //zwiększenie msg bo odebraliśmy liczbę h (tzn
                  //zaczynamy odbierać)
        break;

      case ':':   //jeśli jest to :
        number = 0;         //
        length[number] = 0;
        break;

      case ';':   //jeśli jest to ; to znaczy że zakończyliśmy 
                  //odbierać pakiet danych
        if(msg == 2) {        //jeśli w pakiecie były obydwie liczby
          vertical = parseInt(numbers[0], length[0]);  //wyciągnięcie pozycji v
          horizontal = parseInt(numbers[1], length[1]);//wyciągnięcie pozycji h
          addPoint(vertical, horizontal);//dodanie punktu z tymi pozycjami
                                         //do listy
        }

        msg = 0;         //wyzerowanie msg
        break;

      default:    //jeśli żaden z tych znaków to znaczy że jest to 
                  //pojedyncza liczba
        numbers[ number ][ length[ number ] ] =
          odb_x;   //przypisanie tej pojedynczej liczby do tablicy
                   // przechowującej całą liczbę
        length[ number ]++; //zwiększenie długości tej liczby
        break;
      }

      odb_flaga = 0;     //zerowanie odbioru znaku
    }
  }
}

void obrot()             //funkcja obracająca silnikami
{

  if(wait_time_v >= OPOZNIENIE_V &&
      aktualna_pozycja_v <
      docelowa_pozycja_v) {    //jeśli silnik czekał już odpowiednią ilość
                               // przerwań i powinien się ruszyć do góry to
    obroty_g(seq_v);    //obrót silnika na podany 'podkrok'

    seq_v++;
    if(seq_v > 8) {     //jeśli silnik obrócił się o cały cykl to
      aktualna_pozycja_v++; //zwiększenie jego pozycji o 1
      seq_v = 0;        //wyzerowanie zmiennej 'podkroku'

      wait_time_v = 0;    //wyzerowanie czasu jaki silnik czeka po każdym kroku
    }
  } else if(wait_time_v >= OPOZNIENIE_V &&
            aktualna_pozycja_v >
            docelowa_pozycja_v) { //jeśli silnik czekał już odpowiednią ilość 
                                  //przerwań i powinien się ruszyć w dół to 
                                  //tak samo jak wyżej tylko przesunięcie
                                  //silnika w dół
    obroty_d(seq_v);

    seq_v++;
    if(seq_v > 8) {
      aktualna_pozycja_v--;
      seq_v = 0;

      wait_time_v = 0;
    }
  } else {  //jeśli silnik powinien jeszcze czekać lub jest już na pozycji
            //docelowej to zwiększenie licznika przechowującego czas jaki
            //silnik czeka
    wait_time_v++;
  }

  if(wait_time_h >= OPOZNIENIE_H &&
      aktualna_pozycja_h <
      docelowa_pozycja_h) {      //to samo co wyżej tylko dla drugiego silnika
    obroty_p(seq_h);

    seq_h++;
    if(seq_h > 8) {
      aktualna_pozycja_h++;
      seq_h = 0;

      wait_time_h = 0;
    }
  } else if(wait_time_h >= OPOZNIENIE_H &&
            aktualna_pozycja_h > docelowa_pozycja_h) {
    obroty_l(seq_h);

    seq_h++;
    if(seq_h > 8) {
      aktualna_pozycja_h--;
      seq_h = 0;

      wait_time_h = 0;
    }
  } else {
    wait_time_h++;
  }

  if(aktualna_pozycja_v == docelowa_pozycja_v &&
      aktualna_pozycja_h ==
      docelowa_pozycja_h) {  //jeśli obydwa silniki osiągnąły swój cel to:
    tmp = getPoint(); //pobranie następnego punktu z listy na który mają
                      //przesunąć się silniki

    if(tmp != NULL) { //jeśli w liście był następny punkt
      LASER_ON;   //włączenie lasera

      //przeskalowanie pozycji na jaki ma się silnik przemieścić
      docelowa_pozycja_v = (max_v * tmp->pion) / 100.0f;    
      docelowa_pozycja_h = (max_h * tmp->poziom) / 100.0f;

      seq_v = 0;        //wyzerowanie stanu silnika
      seq_h = 0;
      removePoint();    //usunięcie pierwszego elementu z listy
    } else {
      LASER_OFF;      //jeśli w liście nie było żadnego elementu
                      //to wyłączenie lasera
    }
  }
}

//-----------------------------//

ISR(TIMER0_OVF_vect)  //funkcja przerwania wykonywana co 2 ms
{
  TCNT0 = LICZNIK; //poczatkowa wartosc licznika

  obrot();
}
Do pobrania: main.c (kopia)

Trzeba pamiętać aby ustawić fusebity na oscylator 8 MHz lub dostosować timer i prescaler.
Jak to zrobić oczywiście znajdziemy na blogu:






Aplikacja na Androida

Aplikację w formacie *.apk można pobrać stąd: LaserControllerV2.apk (kopia)

Aplikacja wysyła współrzędne ekranu w postaci np. :v20h90;

: - oznacza początek danych (dodany po to gdyż nie wiadomo czemu czasem gubiło literkę v)
v - oznacza punkt w pionie
h - oznacza punk w poziomie
; - oznacza koniec danych

Maksymalna wysyłana dana to: :v100h100;

więc zawsze można napisać program na PC.

W aplikacji lepiej nie trzymać długo palca na ekranie jednocześnie szybko nim poruszając. Po nagromadzeniu przez uC dużo danych lubi się zawiesić i przestaje reagować.

W aplikacji po wciśnięciu przycisku "Czyść" aplikacja wysyła dane: :v0h0;
oraz czyści ekran, co powoduje ustawienie pozycji początkowej.




Działanie

Na filmiku mam ustawione opóźnienie obydwu silników na 50 (czyli po każdej sekwencji czeka 100 ms) dlatego jest takie lekkie skakanie, przy 0 bardzo ładnie i płynnie wszystko chodzi.

Podczas testów zauważyłem, że nie chodzi to dokładnie równo, może to być spowodowane tym że mam różne przekładnie lub małymi błędami w programie.


Oceń artykuł.
Wasze opinie są dla nas ważne, gdyż pozwalają dopracować poszczególne artykuły.
Pozdrawiamy, Autorzy
Ten artykuł oceniam na:

8 komentarzy:

  1. Można prosić o schemat modułu z bluetooth?

    OdpowiedzUsuń
    Odpowiedzi
    1. Tutaj dokumentacja modułu:

      http://www.rasmicro.com/Bluetooth/EGBT-045MS-046S%20Bluetooth%20Module%20Manual%20rev%201r0.pdf

      i jak są rozmieszczone wyprowadzenia:

      http://imagizer.imageshack.us/a/img15/7514/4gre.jpg

      Usuń
  2. Zapomniałem sobie że aplikacja automatycznie łączy się z BT na podstawie jego adresu MAC (w tym przypadku z moim BT). Jak powstanie poprawiona wersja z szukaniem urządzeń to wstawię ją tutaj w komentarzu.

    OdpowiedzUsuń
    Odpowiedzi
    1. Daj znać mailowo albo przez formularz kontaktu i dodamy do artykułu.

      Usuń
  3. Witam, w jakim programie napisana jest aplikacja LaserControllerV2? Mógłbym prosić o kod źródłowy tej apki?

    OdpowiedzUsuń
  4. Witam. Projekt jest fajny jednak chciałbym go wykorzystać... zbudować podobny układ sterujący dwoma silnikami osobno. Tak by każdy dało się ustawic na konkretną "szybkość" ilość obrotów oraz aby były ograniczniki krancowe bo całość ma sterować "przesunięciem" elementu o żądaną odległość z żądaną prędkością. Tak by nie wyszło po za zakres. Oraz by można wartościami przesunięcia oraz szybkości dowolnie z poziomu programu sterowac oraz aby zadane kilka programów wykonywał po ich uruchomieniu z oprogramowania. Da się tak zmodyfikować aplikację i uklad (dodać ograniczniki krancowe) by można było tak sterować silnikami krokowymi. Pozdrawiam.

    OdpowiedzUsuń
  5. Ten komentarz został usunięty przez autora.

    OdpowiedzUsuń

Działy
Działy dodatkowe
Inne
O blogu




Dzisiaj
--> za darmo!!! <--
1. USBasp
2. microBOARD M8


Napisz artykuł
--> i wygraj nagrodę. <--


Co nowego na blogu?
Śledź naszego Facebook-a



Co nowego na blogu?
Śledź nas na Google+

/* 20140911 Wyłączona prawa kolumna */
  • 00

    dni

  • 00

    godzin

  • :
  • 00

    minut

  • :
  • 00

    sekund

Nie czekaj do ostatniego dnia!
Jakość opisu projektu także jest istotna (pkt 9.2 regulaminu).

Sponsorzy:

Zapamiętaj ten artykuł w moim prywatnym spisie treści.