Budowa modułu kontrolnego na USB przy pomocy mikrokontrolera AVR

bordeux

1 Słowa wstępu
2 Co nam potrzebne
3 Budujemy układ
     3.1 Schemat
4 Kofigurujemy Środowisko
5 Programujemy mikrokontroler
     5.2 Szybka teoria
          5.2.1 Wyjście
          5.2.2 Wejście
          5.2.3 Prosty program
     5.3 Piszemy program dla mikrokontrolera
          5.3.4 Tworzymy projekt
          5.3.5 Kod
          5.3.6 Konfiguracja VUSB
          5.3.7 Tworzymy sterowniki
6 Piszemy aplikację sterującą dla Windows
     6.4 Budujemy formę
     6.5 Programujemy
7 Efekt końcowy

Słowa wstępu

Artykuł został stworzony dla początkujących, dlatego czasami przesadnie opisuje niektóre czynności. Brakuje też fachowej terminologi, gdyż znam tylko praktykę, przez co starałem się w logiczny sposób wytłumaczyć niektóre kwestie. Chcę tutaj pokazać jak stworzyć komunikację pomiędzy komputer <--> mikrokontroler - jest to według mnie najużyteczniejsza rzecz, dzięki czemu zbudujemy podstawy do: np. nadajnika, odbiornika.

Co nam potrzebne


Nazwa Zdjęcie Opis Sugerowana cena (allegro.pl)
Programator AVR ISP ![programator.jpg](//static.4programmers.net/uploa[...]t/10114757224f3500e78594b.jpg) Bez tego się nie obejdzie. Służy do programowania mikrokontrolera. Ten na obrazku to "Programator USBasp ATMEGA ATMEL AVR ISP" 21zł
Gniazdo ISP ![isp_socket.png](//static.4programmers.net/uploa[...]nt/6393298214f3500d2a0354.png) Do podłączania programatora do naszego układu 1zł
Atmega168A-PU ![atmega.jpg](//static.4programmers.net/uploa[...]t/19827963304f3500c9e6462.jpg) Można kupić inny, ale wtedy musisz się zaznajomić się z PORTBmi. Ważną sprawą jest pamięć i taktowanie. Procesor musi taktować min. w 12mhz. Pamięć na program min. 5kb (tyle waży ten przykład) 15zł
Kwarc 12mHz ![kwarc.jpg](//static.4programmers.net/uploa[...]nt/9903792464f3500d8aabf6.jpg) W prawie każdym dobrym sklepie elektronicznym. Zawsze możesz wylutować z układów scalonych (ja wylutowałem z starego modemu) 2zł
Diody LED x6 ![diody.jpg](//static.4programmers.net/uploa[...]t/10350888624f3500cc2b60c.jpg) W każdym sklepie elektronicznym kupisz. 0.30zł/szt
Dioda Zenera x2 3V3 ![zenera.jpg](//static.4programmers.net/uploa[...]t/12323721734f35010707f53.jpg) W każdym sklepie elektronicznym kupisz. 0.15zł/szt
Rezystory x10 ![rezystor.jpg](//static.4programmers.net/uploa[...]t/12159849174f3500ff716c9.jpg) W każdym sklepie elektronicznym kupisz. 2x75R , 1x15R, 1x10kR, 6x1kR (do diód) 0.15zł/szt
Kondensator x3 ![kondensator.jpg](//static.4programmers.net/uploa[...]t/11759852554f3500d68923c.jpg) W każdym sklepie elektronicznym kupisz. 2x27pF (ceramiczny), 1x220uF(elektrolitowy) 0.15zł/szt
Kabel USB ![usb_kabel.jpg](//static.4programmers.net/uploa[...]nt/4615635154f3500fd0ac0a.jpg) Masz pewnie zepsutą myszkę lub inny sprzęt na USB. Coś znajdziesz. 0zł
Razem ok. 40zł

Budujemy układ

To już od ciebie i twojego doświadczenia zależy jak to będzie wyglądało. Amatorsko - na pająka. Możesz zbudować płytkę- lecz to są dodatkowe koszta - wiertła, laminat( plytka pcb) i b327 do wytrawiania miedzi. Jeśli na to się jednak zdecydujesz, to tutaj ściągniesz projekt schematu do programu DipTrace, dzięki któremu będziesz mógł wydrukować sobie płytkę.

Lecz jeśli naprawdę jesteś zielony w temacie elektroniki, podskocz z częściami i 4pakiem do sąsiada elektryka. Pewnie ci pomoże.

![ob1.jpg](//static.4programmers.net/uploa[...]52f5cac7f9-image(300x300).jpg) ![ob2.jpg](//static.4programmers.net/uploa[...]52f694c55f-image(300x300).jpg)

Schemat

Kofigurujemy Środowisko

W tym celu przygotowałem 54 minutowy materiał video. Pokazałem w nim od zera dalsze czynności (od zainstalowania Windowsa po efekt końcowy). W tym artykule zajmę się samym kodem.

Film
<h2>Co nam potrzebne</h2>
Nazwa Opis Download Moment w filmie
Eclipse Classic Środowisko programistyczne dla programowania mikrokontrolerów. Alternatywa dla środowiska WinAVR Download 00:00:02
WinAVR Kompilatory & biblioteki, narzędzia Download 00:00:38
MS Visual C++ Środowisko do programowania okienkowego w Windows. Wystarczy wersja Express.
Nie polecam MS Visual C++ 2010 - nie posiada funkcji IntelliSense, dzięki czemu programowanie stanie się praktycznie niemożliwe
Download 00:01:15
Java JDK Potrzebne do uruchomienia Eclipse Download 00:10:42
Eclipse AVR Plugin Plugin do Eclipse który umożliwia programowanie dla mikrokontrolerów Download 00:20:00
libusb-win32 Biblioteka dla C++, dzięki czemu nasz program na Windowsie będzie mógł porozumieć się z mikrokontrolerem. Wraz z biblioteką, jest generator sterowników. Download 00:05:15
vusb Biblioteka dla mikrokontrolera, dzięki czemu będziemy mogli obsłużyć protokół USB Download 00:05:05

Programujemy mikrokontroler

Zanim zaczniemy, musisz zaznajomić się z portami mikrokontrolera:

Szybka teoria

Jak widać na powyższym rysunku, procesor składa się 3 portów. Porty są oznaczone P[Litera portu][Bit/nóżka portu], czyli u nas:

  • PB</a>
  • PC</a>
  • PD</a>

Każdy port ma 8 nóżek (0-7), prócz PC (0-6), i każdy z nich może przyjąć stan wejścia/wyjścia.

Wyjście

Aby ustawić cały port B na stan wyjścia, należy w programie wywołać:

    DDRB = 0xFF; // binarnie 0b1111111 - DDR[LiterkaPortu]

Jeśli chcesz ustawić nóżki w stan wysoki :

    DDRB = 0xFF; // binarnie 0b1111111 
    PORTB = 0xFF; // binarnie 0b1111111 PORT[LiterkaPortu]

Oczywiście nie musi być cały port w stanie wysokim. Jeśli chcesz by nóżka 0, 1, 2 była w trybie wyjścia z stanem wysokim, wykonaj:

    DDRB = 0x07; // binarnie 0b00000111
    PORTB = 0x07; // binarnie 0b00000111

Czyli jak już zauważyłeś, ustawiamy porty poprzez 8bitowe dane, gdzie każdy bit odpowiada za nóżkę procesora.

Wejście

Oprócz wyjścia, możesz odczytywać dane z nóżek (wejście). Aby ustawić port w stan wejścia:

    DDRB = 0x00; // binarnie 0b0000000 - DDR[LiterkaPortu]

Jeśli chcesz odczytać dane z portu:

    DDRB = 0x00; // binarnie 0b0000000 - DDR[LiterkaPortu]
    twojazmienna = PINB; // PIN[LiterkaPortu]

To tyle z skróconej teori. Jeśli chcesz poszerzyć wiedzę, to skorzystaj z tej strony.

Prosty program

Prosty program to :

    #include <avr/io.h>
    #include <avr/interrupt.h>
    #include <avr/pgmspace.h>
    #include <avr/wdt.h>
    int main(void){
            DDRB=0xFF; // Ustawiamy port B w stan Wyjścia
            PORTB=0xFF; // Ustawaimy cały port B w stan Wysoki
             for(;;){ // to co programistów przeraża, w mikrokontrolerach jest normalną rzeczą - oznacza nieprzerwanie pracy mikrokontrolera
 
             }
    }

Jak to skompilować? Zobacz film od momentu 00:22:00

Piszemy program dla mikrokontrolera

Zanim wszystko zaczniemy, musisz mieć skonfigurowane środowisko: Jak to zrobić jest pokazane na filmie od momentu: 00:20:00 (zakładając, że Eclipse i JDK już ściągłeś i zainstalowałeś).

Tworzymy projekt

Uruchamiamy Eclipse, następnie File->New->Project. Wybieramy C/C++ -> C Project. Klikamy Dalej, w następnym formularzy uzupełniamy Nazwę projektu. Znów klilamy Dalej i odznaczamy "Debug", i znów Dalej. W kolejnym oknie wybieramy nasz mikrokontroler, i teraz WAŻNE: taktowanie procesora ustaw na 12mhz czyli na 12000000 hz

Kod

Utworzyliśmy projekt. Teraz musisz skopiować folder "usbdrv" do katalogu projektu. Następnie w projekcie utwórz plik main.c, i uzupełnij go kodem: ```c /* * main.c * * Created on: 09-02-2012 * Author: Bordeux */ #include <avr/io.h> // biblioteki do obsługi µKontrolera #include <avr/interrupt.h> #include <avr/pgmspace.h> #include <avr/wdt.h> #include "usbdrv/usbdrv.h" // biblioteka do obsługi USB #include "usbdrv/oddebug.h" // biblioteka do debugowania USB_PUBLIC uchar usbFunctionWrite(uchar *data, uchar len){ // return 1; } USB_PUBLIC uchar usbFunctionSetup(uchar data[8]){ // funkcja wykonywana, gdy µKontroler dostanie polecenie poprzez USB od komputera usbRequest_t *rq = (void *)data; static uchar replyBuf[255]; // bufer danych wyjściowych - odpowiedź µKontrolera na żądanie if(rq->bRequest == 0){ // rq->bRequest to ID żądania replyBuf[0] = 0x48; //h replyBuf[1] = 0x65; //e replyBuf[2] = 0x6c; //l replyBuf[3] = 0x6c; //l replyBuf[4] = 0x6f; //o replyBuf[5] = 0x20; //space replyBuf[6] = 0x77; //w replyBuf[7] = 0x6f; //o replyBuf[8] = 0x72; //r replyBuf[9] = 0x6c; //l replyBuf[10] = 0x64; //d replyBuf[11] = 0x21; //! //Wysyłamy odpowiedź: Hello world! return 12; //w wyniku funkcji podajemy długość buffora } if(rq->bRequest == 1){ // gdy numer żądania to 1, odpowiedz... DDRC=data[2]; //odczytaj dane wysłane z programu data[0], data[1] są zarezerwowane do komunikacji //Od data[2] zaczynają się nasze dane wysłane z aplikacji //W programie wysyłamy dane 1 bajtowe, ustawiając port C w stan wejścia/wyjścia PORTC=data[2]; // Ustawiamy wybrane porty w programie w stan Wysoki/niski replyBuf[0] = 0x64; //d replyBuf[1] = 0x6f; //o replyBuf[2] = 0x6e; //n replyBuf[3] = 0x65; //e replyBuf[4] = 0x21; //! //Odpowiedź naszego programu: Done! return 5; // rozmiar tablicy } if(rq->bRequest == 2){ // tutaj będziemy zczytywać aktualny stan portu wyjścia C replyBuf[0] = PORTC; //odczytanie stanu (nie wejścia - PINC) return 1; } replyBuf[0] = 0xff; // gdy komenda jest nieobsługiwana, odpowiedź: 0xFF; return 1; } int main(void){ wdt_enable(WDTO_1S); //Watchdog Timer - zegar pakietu kontrolnego //(mechanizm wykorzystywany do przełączenia zdarzenia czy //zaniechania procesu w momencie zerowania zegara) odDebugInit(); // włącz debugowanie usbInit(); // załaduj bibliotekę USB sei(); // Włączenie przerwań for(;;){ // pętla w nieskończoność, co oznacza że µKontroler nigdy nie zakończy pracy wdt_reset(); //resetuj Watch Dog usbPoll(); // Tutaj działa biblioteka VUSB } return 0; } ```

Konfiguracja VUSB

Przed kompilacją, musimy skonfigurować bibliotekę VUSB. W tym celu przechodzimy do katalogu vusb, który jest w naszym projekcie. Tworzymy kopię pliku usbconfig-prototype.h, nazywając nowy plik "usbconfig.h". Następnie go edytujemy. Po ```c #ifndef __usbconfig_h_included__ #define __usbconfig_h_included__ ``` Wstawiamy: ```c #define F_CPU 12000000 //taktowanie procesora w hz ``` Po tym odszukujemy: ```c /* ---------------------------- Hardware Config ---------------------------- */ ``` Zmieniamy dane na : ```c #define USB_CFG_IOPORTNAME D #define USB_CFG_DMINUS_BIT 3 #define USB_CFG_DPLUS_BIT 2 ``` Następnie szukamy: ```c /* -------------------------- Device Description --------------------------- */ ``` Ustawiamy: ```c #define USB_CFG_VENDOR_ID 0x18, 0x19 /* = 0x1918 = 6424 - ustawiamy ID producenta */ #define USB_CFG_DEVICE_ID 0x66, 0x0E /* = 0x0E66 = 3686 - ustawiamy ID urządzenia */ #define USB_CFG_VENDOR_NAME 'B', 'o', 'r', 'd', 'e', 'u', 'x', '.', 'N', 'E', 'T' /* Nazwa producenta */ #define USB_CFG_VENDOR_NAME_LEN 11 /* Długość nazwy producenta */ #define USB_CFG_DEVICE_NAME 'B', 'O', 'R', 'D', 'E', 'U', 'X', '-', 'D', 'X' /* Nazwa urządzenia */ #define USB_CFG_DEVICE_NAME_LEN 10 /* Długość nazwy urządzenia */ ``` Teraz możemy skompilować i wrzucić do µKontrolera, nie zapominając ustawić FuseBity na: ``` low = AF hight = DF ext. = 01 ```

Tworzymy sterowniki

Na szczęście nie musimy ich pisać od podstaw. Użyjemy biblioteki libusb-32. W katalogu tego projektu "libusb-win32-bin-1.2.6.0\bin" jest program "inf-wizard.exe", dzięki któremu wygenerujesz sterowniki.

Piszemy aplikację sterującą dla Windows

Nagrywając i pisząc ten tutorial, założyłem że port C ma 8 nóżek, lecz ma tylko 7 (0-6), z czego 6 do użytku (7 nóżka to reset). Jest to błąd, który nie przerywa programu, więc można go pominąć Uruchamiamy MS Visual C++, tworzymy nowy projekt. Kopiujemy libusb.lib z folderu "libusb-win32-bin-1.2.6.0\lib\msvc" oraz lusb0_usb.h z "libusb-win32-bin-1.2.6.0\include". Zmieniamy nazwę lusb0_usb.h na usb.h
Musimy teraz ustawić linker. Klikamy prawym przyciskiem na nasz projekt, następnie properties->Linker->Input . W additional dependencies dodajemy bibliotekę libusb.lib.
Skompiluj teraz projekt. Jeśli wyskoczy błąd: ``` Example_avr_usb.obj : error LNK2031: unable to generate p/invoke for "extern "C" void __clrcall usb_init(void)" (?usb_init@@$$J0YMXXZ); calling convention missing in metadata Example_avr_usb.obj : warning LNK4248: unresolved typeref token (0100002E) for 'usb_dev_handle'; image may not run Example_avr_usb.obj : error LNK2020: unresolved token (0A00001C) "extern "C" int __clrcall usb_control_msg(struct usb_dev_handle *,int,int,int,int,char *,int,int)" (?usb_control_msg@@$$J0YMHPAUusb_dev_handle@@[email protected]) Example_avr_usb.obj : error LNK2020: unresolved token (0A00001E) "extern "C" struct usb_dev_handle * __clrcall usb_open(struct usb_device *)" (?usb_open@@$$J0YMPAUusb_dev_handle@@PAUusb_device@@@Z) Example_avr_usb.obj : error LNK2020: unresolved token (0A00001F) "extern "C" int __clrcall usb_close(struct usb_dev_handle *)" (?usb_close@@$$J0YMHPAUusb_dev_handle@@@Z) Example_avr_usb.obj : error LNK2020: unresolved token (0A000022) "extern "C" struct usb_bus * __clrcall usb_get_busses(void)" (?usb_get_busses@@$$J0YMPAUusb_bus@@XZ) Example_avr_usb.obj : error LNK2020: unresolved token (0A000023) "extern "C" int __clrcall usb_find_devices(void)" (?usb_find_devices@@$$J0YMHXZ) Example_avr_usb.obj : error LNK2020: unresolved token (0A000024) "extern "C" int __clrcall usb_find_busses(void)" (?usb_find_busses@@$$J0YMHXZ) Example_avr_usb.obj : error LNK2020: unresolved token (0A000055) "extern "C" void __clrcall usb_init(void)" (?usb_init@@$$J0YMXXZ) Example_avr_usb.obj : error LNK2001: unresolved external symbol "extern "C" void __clrcall usb_init(void)" (?usb_init@@$$J0YMXXZ) Example_avr_usb.obj : error LNK2001: unresolved external symbol "extern "C" struct usb_dev_handle * __clrcall usb_open(struct usb_device *)" (?usb_open@@$$J0YMPAUusb_dev_handle@@PAUusb_device@@@Z) Example_avr_usb.obj : error LNK2001: unresolved external symbol "extern "C" int __clrcall usb_close(struct usb_dev_handle *)" (?usb_close@@$$J0YMHPAUusb_dev_handle@@@Z) Example_avr_usb.obj : error LNK2001: unresolved external symbol "extern "C" int __clrcall usb_find_busses(void)" (?usb_find_busses@@$$J0YMHXZ) Example_avr_usb.obj : error LNK2001: unresolved external symbol "extern "C" int __clrcall usb_find_devices(void)" (?usb_find_devices@@$$J0YMHXZ) Example_avr_usb.obj : error LNK2001: unresolved external symbol "extern "C" struct usb_bus * __clrcall usb_get_busses(void)" (?usb_get_busses@@$$J0YMPAUusb_bus@@XZ) Example_avr_usb.obj : error LNK2001: unresolved external symbol "extern "C" int __clrcall usb_control_msg(struct usb_dev_handle *,int,int,int,int,char *,int,int)" (?usb_control_msg@@$$J0YMHPAUusb_dev_handle@@[email protected]) C:\Users\Tester\Desktop\Programowanie AVR\Example_avr_usb\Release\Example_avr_usb.exe : fatal error LNK1120: 14 unresolved externals ``` To powtórz dwie pierwsze czynności, wejdź w General i w Common Language Runtime support ustaw Common Language Runtime Support (/clr)

Budujemy formę

Zbuduj formę, tak jak pokazałem Tutaj
Komponent Opis Nazwa
TabControl Posłuży do blokowania elementów wewnątrz tabControl1
Label Label do pokazywania informacji Podłączony/Niepodłączony label1
Checbox Do ustawiania bitów dla portu/ odczytywania portc[od 0 do 7]
Button Przycisk wyślij, do wysłania bitów button2
Button Przycisk odczytaj, do odczytywania stanu mikrokontrolera button3
ListBox Logi z pracy programu log_box

Programujemy

Nie będę pisać co i gdzie wkleić, bo jest to pokazane w filmie. Dlatego wkleję gotowy kod z obszernymi komentarzami co do poszczególnych elementów. form1.h ```cpp #pragma once #include "usb.h" // plik nagłówkowy z funkcjami do sterowania mikrokontrolerem #include <string> // potrzebne do konwersji hex->string #define VENDOR_ID 6424 // nasz VENDOR ID w systemie dziesiątkowym. Ten co ustawialiśmy w kodzie programu dla mikrokontrolera #define PRODUCT_ID 3686 // nasz PRODUCT ID. Tak samo jak u góry #define USB_COMMAND_SETBIT_C 1 //ID komendy, która służy do ustawienia portu - ustawione w kodzie programu dla mikrokont..... #define USB_COMMAND_READBIT_C 2 // ID komendy do odczytu #define USB_COMMAND_HELLO 0 // ID komendy dla Hello World! usb_dev_handle *usb_handle; // zmienna do przechowywania uchywtu podłączonego urządzenia bool is_connected = false; //zmienna informuje nas czy urządzenie jest podłaczone namespace Example_avr_usb { using namespace System; using namespace System::ComponentModel; using namespace System::Collections; using namespace System::Windows::Forms; using namespace System::Data; using namespace System::Drawing; /// <summary> /// Summary for Form1 /// /// WARNING: If you change the name of this class, you will need to change the /// 'Resource File Name' property for the managed resource compiler tool /// associated with all .resx files this class depends on. Otherwise, /// the designers will not be able to interact properly with localized /// resources associated with this form. /// </summary> public ref class Form1 : public System::Windows::Forms::Form { public: Form1(void) { InitializeComponent(); // //TODO: Add the constructor code here // usb_init(); //************************** Ładujemy bibliotekę libusb } protected: /// <summary> /// Clean up any resources being used. /// </summary> ~Form1() { if (components) { delete components; } } private: System::Windows::Forms::Button^ button1; protected: private: System::Windows::Forms::Label^ label1; private: System::Windows::Forms::ListBox^ log_box; private: System::Windows::Forms::Label^ label2; private: System::Windows::Forms::TabControl^ tabControl1; private: System::Windows::Forms::TabPage^ tabPage1; private: System::Windows::Forms::CheckBox^ portc7; private: System::Windows::Forms::CheckBox^ portc6; private: System::Windows::Forms::CheckBox^ portc5; private: System::Windows::Forms::CheckBox^ portc4; private: System::Windows::Forms::CheckBox^ portc3; private: System::Windows::Forms::CheckBox^ portc2; private: System::Windows::Forms::CheckBox^ portc1; private: System::Windows::Forms::CheckBox^ portc0; private: System::Windows::Forms::TabPage^ tabPage2; private: System::Windows::Forms::Button^ button3; private: System::Windows::Forms::Button^ button2; private: System::Windows::Forms::Timer^ timer1; private: System::ComponentModel::IContainer^ components; private: /// <summary> /// Required designer variable. /// </summary> public: void log_op(System::Object ^to_add){ //************************** Metoda posłuży do zapisywania komunikatów do Listboxa log_box->Items->Add(to_add); } public: int get_bit_val( int n, int bitPosition ) { //************************** metoda, dzięki której będziemy mogli pobierać x bit n liczby return ((n >> bitPosition) & 1); } #pragma region Windows Form Designer generated code /// <summary> /// Required method for Designer support - do not modify /// the contents of this method with the code editor. /// </summary> void InitializeComponent(void) { this->components = (gcnew System::ComponentModel::Container()); this->button1 = (gcnew System::Windows::Forms::Button()); this->label1 = (gcnew System::Windows::Forms::Label()); this->log_box = (gcnew System::Windows::Forms::ListBox()); this->label2 = (gcnew System::Windows::Forms::Label()); this->tabControl1 = (gcnew System::Windows::Forms::TabControl()); this->tabPage1 = (gcnew System::Windows::Forms::TabPage()); this->button3 = (gcnew System::Windows::Forms::Button()); this->button2 = (gcnew System::Windows::Forms::Button()); this->portc7 = (gcnew System::Windows::Forms::CheckBox()); this->portc6 = (gcnew System::Windows::Forms::CheckBox()); this->portc5 = (gcnew System::Windows::Forms::CheckBox()); this->portc4 = (gcnew System::Windows::Forms::CheckBox()); this->portc3 = (gcnew System::Windows::Forms::CheckBox()); this->portc2 = (gcnew System::Windows::Forms::CheckBox()); this->portc1 = (gcnew System::Windows::Forms::CheckBox()); this->portc0 = (gcnew System::Windows::Forms::CheckBox()); this->tabPage2 = (gcnew System::Windows::Forms::TabPage()); this->timer1 = (gcnew System::Windows::Forms::Timer(this->components)); this->tabControl1->SuspendLayout(); this->tabPage1->SuspendLayout(); this->SuspendLayout(); // // button1 // this->button1->Location = System::Drawing::Point(12, 22); this->button1->Name = L"button1"; this->button1->Size = System::Drawing::Size(152, 29); this->button1->TabIndex = 0; this->button1->Text = L"Podłącz!"; this->button1->UseVisualStyleBackColor = true; this->button1->Click += gcnew System::EventHandler(this, &Form1::button1_Click); // // label1 // this->label1->AutoSize = true; this->label1->ForeColor = System::Drawing::Color::Red; this->label1->Location = System::Drawing::Point(48, 6); this->label1->Name = L"label1"; this->label1->Size = System::Drawing::Size(85, 13); this->label1->TabIndex = 1; this->label1->Text = L"Nie podłączony!"; // // log_box // this->log_box->FormattingEnabled = true; this->log_box->Location = System::Drawing::Point(260, 19); this->log_box->Name = L"log_box"; this->log_box->Size = System::Drawing::Size(371, 212); this->log_box->TabIndex = 2; // // label2 // this->label2->AutoSize = true; this->label2->Location = System::Drawing::Point(257, 6); this->label2->Name = L"label2"; this->label2->Size = System::Drawing::Size(27, 13); this->label2->TabIndex = 3; this->label2->Text = L"Logi"; // // tabControl1 // this->tabControl1->Controls->Add(this->tabPage1); this->tabControl1->Controls->Add(this->tabPage2); this->tabControl1->Location = System::Drawing::Point(12, 57); this->tabControl1->Name = L"tabControl1"; this->tabControl1->SelectedIndex = 0; this->tabControl1->Size = System::Drawing::Size(242, 180); this->tabControl1->TabIndex = 4; // // tabPage1 // this->tabPage1->Controls->Add(this->button3); this->tabPage1->Controls->Add(this->button2); this->tabPage1->Controls->Add(this->portc7); this->tabPage1->Controls->Add(this->portc6); this->tabPage1->Controls->Add(this->portc5); this->tabPage1->Controls->Add(this->portc4); this->tabPage1->Controls->Add(this->portc3); this->tabPage1->Controls->Add(this->portc2); this->tabPage1->Controls->Add(this->portc1); this->tabPage1->Controls->Add(this->portc0); this->tabPage1->Location = System::Drawing::Point(4, 22); this->tabPage1->Name = L"tabPage1"; this->tabPage1->Padding = System::Windows::Forms::Padding(3); this->tabPage1->Size = System::Drawing::Size(234, 154); this->tabPage1->TabIndex = 0; this->tabPage1->Text = L"Port C"; this->tabPage1->UseVisualStyleBackColor = true; // // button3 // this->button3->Location = System::Drawing::Point(101, 98); this->button3->Name = L"button3"; this->button3->Size = System::Drawing::Size(114, 29); this->button3->TabIndex = 9; this->button3->Text = L"Odczytaj"; this->button3->UseVisualStyleBackColor = true; this->button3->Click += gcnew System::EventHandler(this, &Form1::button3_Click); // // button2 // this->button2->Location = System::Drawing::Point(101, 62); this->button2->Name = L"button2"; this->button2->Size = System::Drawing::Size(114, 30); this->button2->TabIndex = 8; this->button2->Text = L"Wyślij"; this->button2->UseVisualStyleBackColor = true; this->button2->Click += gcnew System::EventHandler(this, &Form1::button2_Click); // // portc7 // this->portc7->AutoSize = true; this->portc7->Location = System::Drawing::Point(101, 29); this->portc7->Name = L"portc7"; this->portc7->Size = System::Drawing::Size(61, 17); this->portc7->TabIndex = 7; this->portc7->Text = L"Port C7"; this->portc7->UseVisualStyleBackColor = true; // // portc6 // this->portc6->AutoSize = true; this->portc6->Location = System::Drawing::Point(101, 6); this->portc6->Name = L"portc6"; this->portc6->Size = System::Drawing::Size(61, 17); this->portc6->TabIndex = 6; this->portc6->Text = L"Port C6"; this->portc6->UseVisualStyleBackColor = true; // // portc5 // this->portc5->AutoSize = true; this->portc5->Location = System::Drawing::Point(6, 121); this->portc5->Name = L"portc5"; this->portc5->Size = System::Drawing::Size(61, 17); this->portc5->TabIndex = 5; this->portc5->Text = L"Port C5"; this->portc5->UseVisualStyleBackColor = true; // // portc4 // this->portc4->AutoSize = true; this->portc4->Location = System::Drawing::Point(6, 98); this->portc4->Name = L"portc4"; this->portc4->Size = System::Drawing::Size(61, 17); this->portc4->TabIndex = 4; this->portc4->Text = L"Port C4"; this->portc4->UseVisualStyleBackColor = true; // // portc3 // this->portc3->AutoSize = true; this->portc3->Location = System::Drawing::Point(6, 75); this->portc3->Name = L"portc3"; this->portc3->Size = System::Drawing::Size(61, 17); this->portc3->TabIndex = 3; this->portc3->Text = L"Port C3"; this->portc3->UseVisualStyleBackColor = true; // // portc2 // this->portc2->AutoSize = true; this->portc2->Location = System::Drawing::Point(6, 52); this->portc2->Name = L"portc2"; this->portc2->Size = System::Drawing::Size(61, 17); this->portc2->TabIndex = 2; this->portc2->Text = L"Port C2"; this->portc2->UseVisualStyleBackColor = true; // // portc1 // this->portc1->AutoSize = true; this->portc1->Location = System::Drawing::Point(6, 29); this->portc1->Name = L"portc1"; this->portc1->Size = System::Drawing::Size(61, 17); this->portc1->TabIndex = 1; this->portc1->Text = L"Port C1"; this->portc1->UseVisualStyleBackColor = true; // // portc0 // this->portc0->AutoSize = true; this->portc0->Location = System::Drawing::Point(6, 6); this->portc0->Name = L"portc0"; this->portc0->Size = System::Drawing::Size(61, 17); this->portc0->TabIndex = 0; this->portc0->Text = L"Port C0"; this->portc0->UseVisualStyleBackColor = true; // // tabPage2 // this->tabPage2->Location = System::Drawing::Point(4, 22); this->tabPage2->Name = L"tabPage2"; this->tabPage2->Padding = System::Windows::Forms::Padding(3); this->tabPage2->Size = System::Drawing::Size(234, 154); this->tabPage2->TabIndex = 1; this->tabPage2->Text = L"Port B"; this->tabPage2->UseVisualStyleBackColor = true; // // timer1 // this->timer1->Enabled = true; this->timer1->Tick += gcnew System::EventHandler(this, &Form1::timer1_Tick); // // Form1 // this->AutoScaleDimensions = System::Drawing::SizeF(6, 13); this->AutoScaleMode = System::Windows::Forms::AutoScaleMode::Font; this->ClientSize = System::Drawing::Size(643, 241); this->Controls->Add(this->tabControl1); this->Controls->Add(this->label2); this->Controls->Add(this->log_box); this->Controls->Add(this->label1); this->Controls->Add(this->button1); this->FormBorderStyle = System::Windows::Forms::FormBorderStyle::FixedDialog; this->Name = L"Form1"; this->Text = L"Sternik :)"; this->Load += gcnew System::EventHandler(this, &Form1::Form1_Load); this->tabControl1->ResumeLayout(false); this->tabPage1->ResumeLayout(false); this->tabPage1->PerformLayout(); this->ResumeLayout(false); this->PerformLayout(); } #pragma endregion private: System::Void Form1_Load(System::Object^ sender, System::EventArgs^ e) { } private: System::Void button1_Click(System::Object^ sender, System::EventArgs^ e) { this->log_op("------------------------------"); this->log_op("Search devices"); struct usb_bus *bus; //************************** zmienna przechowywająca porty USB struct usb_device *dev; //************************** zmienna przechowyująca Port USB z urządzeniem usb_find_busses(); //************************** szukamy portów USB usb_find_devices(); //************************** szukamy urządzeń for(bus=usb_get_busses(); bus; bus=bus->next){ //************************** Przeszukujemy wszystkie porty for(dev=bus->devices; dev; dev=dev->next){ //************************** i wszystkie urządzenia this->log_op("Found: idVendor: "+dev->descriptor.idVendor.ToString() +" Product ID"+dev->descriptor.idProduct.ToString()); //************************** Zapisujemy do logów że coś znaleziono if(dev->descriptor.idVendor == VENDOR_ID && dev->descriptor.idProduct == PRODUCT_ID){ //************************** Sprawdzamy czy to nasze urządzenie po idVendor i idProduct this->log_op("Selected device: idVendor: "+dev->descriptor.idVendor.ToString() +" Product ID"+dev->descriptor.idProduct.ToString()); ////************************** Powiadamiamy o tym if(is_connected){ //************************** Sprawdzamy czy jest połaczony z programem this->log_op("Disconnected"); //************************** Jak tak, to rozłączamy usb_close(usb_handle); //************************** Zamykamy połączenie usb_handle = NULL; //************************** Czyścimy uchwyt }else{ usb_handle = usb_open(dev); //************************** Jeśli nie, otwieramy połączenie z urządzeniem } } } } if(usb_handle){ //************************** sprawdzamy czy udało się połączyć is_connected = true; int nBytes; //************************** liczba bajtów, które przyjdą do nas z urządzenia char buffer[12]; //************************** buffer danych, które przyjdą do nas z urządzenia this->log_op("Start send command ID "+ USB_COMMAND_HELLO+"; Value: 0xFF; Mode char buffer"); nBytes = usb_control_msg(usb_handle, USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_ENDPOINT_IN, USB_COMMAND_HELLO, 0xFF, 0, (char *)buffer, sizeof(buffer), 5000); //************************** wysyłamy do naszego urządzenia komendę USB_COMMAND_HELLO , czyli ID : 0 , z danymi 0xFF. W rezultacie otrzymamy liczbe bajtów odpowiedzi this->log_op("Response is: "); this->log_op("Num of bytes:"+nBytes.ToString()); String^ response_string = gcnew String(buffer); //************************** zmieniamy buffer na string this->log_op("ASCI response: "+response_string->Substring(0, nBytes)); //************************** wyswietmy wynik }else{ is_connected = false; } } private: System::Void timer1_Tick(System::Object^ sender, System::EventArgs^ e) { //************************** timer ustaw na 100ms, odrazu ustaw na enabled if(is_connected){ this->label1->ForeColor = System::Drawing::Color::Green; this->label1->Text = L"Podłączony!"; this->tabControl1->Enabled = true; this->button1->Text = "Odłącz!"; }else{ this->label1->ForeColor = System::Drawing::Color::Red; this->label1->Text = L"Nie podłączony"; this->tabControl1->Enabled = false; this->button1->Text = "Podłącz!"; } } private: System::Void button2_Click(System::Object^ sender, System::EventArgs^ e) { //************************** przycisk do wysyłania danych o ustawieniu portu int to_send = 0x00; // dane do wysłania if(this->portc0->Checked){ to_send |= 0x01; //ustawiamy bit 0 na 1 } if(this->portc1->Checked){ to_send |= 0x02; //ustawiamy bit 1 na 1 } if(this->portc2->Checked){ to_send |= 0x04; //ustawiamy bit 1 na 1 ... i tak dalej } if(this->portc3->Checked){ to_send |= 0x08; } if(this->portc4->Checked){ to_send |= 0x10; } if(this->portc5->Checked){ to_send |= 0x20; } if(this->portc6->Checked){ to_send |= 0x40; } if(this->portc7->Checked){ to_send |= 0x80; //ustawiamy bit 7 na 1 } int nBytes; char buffer[32]; // buffer this->log_op("----- START SEND BITS -----"); this->log_op("Start send command ID "+ USB_COMMAND_SETBIT_C +"; Value: 0x"+to_send.ToString("X2")+"; Mode char buffer"); nBytes = usb_control_msg(usb_handle, USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_ENDPOINT_IN, USB_COMMAND_SETBIT_C, to_send, 0, (char *)buffer, sizeof(buffer), 5000); //wysyłamy komendę o ID 1, czyli USB_COMMAND_SETBIT_C, z danymi z zmiennej to_send this->log_op("Response is: "); this->log_op("Num of bytes:"+nBytes.ToString()); String^ response_string = gcnew String(buffer); //odpowiedź zmieniamy na ASCII this->log_op("ASCI response: "+response_string->Substring(0, nBytes)); } private: System::Void button3_Click(System::Object^ sender, System::EventArgs^ e) { // odczytujemy aktualny stan portu C int to_send = 0xFF; int nBytes; char buffer[32]; this->log_op("----- START READ PORTC -----"); this->log_op("Start send command ID "+ USB_COMMAND_READBIT_C +"; Value: 0x"+to_send.ToString("X2")+"; Mode char buffer"); nBytes = usb_control_msg(usb_handle, USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_ENDPOINT_IN, USB_COMMAND_READBIT_C, to_send, 0, (char *)buffer, sizeof(buffer), 5000);//wysyłamy komendę ID: 2, USB_COMMAND_READBIT_C this->log_op("Response is: "); this->log_op("Num of bytes:"+nBytes.ToString()); int response_int = buffer[0]; this->log_op("HEX response: 0x"+response_int.ToString("X2")); //otrzymany wynik pokazujemy w formacie HEX this->portc0->Checked = false; this->portc1->Checked = false; this->portc2->Checked = false; this->portc3->Checked = false; this->portc4->Checked = false; this->portc5->Checked = false; this->portc6->Checked = false; this->portc7->Checked = false; if(this->get_bit_val(response_int, 0)){ // i sprawdzamy czy bit 0 jest w stanie wysokim this->portc0->Checked = true; } if(this->get_bit_val(response_int, 1)){ //czy jest 1 bit w stanie wysokim... i tak dalej. this->portc1->Checked = true; } if(this->get_bit_val(response_int, 2)){ this->portc2->Checked = true; } if(this->get_bit_val(response_int, 3)){ this->portc3->Checked = true; } if(this->get_bit_val(response_int, 4)){ this->portc4->Checked = true; } if(this->get_bit_val(response_int, 5)){ this->portc5->Checked = true; } if(this->get_bit_val(response_int, 6)){ this->portc6->Checked = true; } if(this->get_bit_val(response_int, 7)){ this->portc7->Checked = true; } } }; } ```

Efekt końcowy

21075597354f352f863b10d.png

Autorzy: Programowanie: Krzysztof "Bordeux" Bednarczyk Elektronik: Łukasz Domański Gotowiec.zip

7 komentarzy

Miałem problem z powyższym kursem. Otóż zawiera on pewien mały błąd który niestety uniemożliwia prawidłową pracę mikrokontrolera.
W programie na ukontroler jest:

USB_PUBLIC uchar usbFunctionSetup(uchar data[8]){ // funkcja wykonywana, gdy µKontroler dostanie polecenie poprzez USB od komputera
usbRequest_t rq = (void )data;
static uchar replyBuf[255]; // bufer danych wyjściowych - odpowiedź µKontrolera na żądanie

    if(rq->bRequest == 0){                       // rq->bRequest to ID żądania 
  ...

itd.
A powinno być:

USB_PUBLIC uchar usbFunctionSetup(uchar data[8]){ // funkcja wykonywana, gdy µKontroler dostanie polecenie poprzez USB od komputera
usbRequest_t rq = (void )data;
static uchar replyBuf[255]; // bufer danych wyjściowych - odpowiedź µKontrolera na żądanie
usbMsgPtr = replyBuf;

    if(rq->bRequest == 0){                       // rq->bRequest to ID żądania
    replyBuf[0] = 0x48; //h

...

itd.

Brakuje więc linijki:
usbMsgPtr = replyBuf;

Pozdrawiam

Witam. Mam problem ze skompilowaniem programu dla mikrokontrolera, a mianowicie dostaje błąd
make:***[flash.elf] Error 1
(projekt nazwałem flash)
Jak sobie z tym poradzić? Makefile jest generowany przez eclipse, a wydaje mi się, że tam tkwi problem.
Druga sprawa. Patrzyłem na stronę domowa biblioteki V USB i natknąłem się tam na hardware, który wygląda inaczej jeśli chodzi o PINy D+ oraz D-
http://www.obdev.at/Images/vusb/circuit-zoomed.gif
PINy są zamienione. Co z tym zrobić?

@koloco_cnc: go to options -> General i w Common Language Runtime support and set Common Language Runtime Support (/clr) :)

warning LNK4248: unresolved typeref token (01000026) for 'usb_dev_handle'; image may not run

what should I do next ?

"Jeśli chcesz ustawić nóżki w stan wysoki (płynie napięcie)"

"Płynie napięcie" -- buuuahahaha :D

Drobna uwaga - zgodnie z datasheet warto dodać filtrowanie zasilania pinów mikrokontrolera, by nie narażać się na zbędne ryzyko nieprawidłowej pracy: http://mikrokontrolery.blogsp[...]asilanie-mikrokontrolera.html

taka mała uwaga - płynie prąd, a nie napięcie ;) Napięcie jest/się odkłada.