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 terminologii, 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 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 6393298214f3500d2a0354.png Do podłączania programatora do naszego układu 1zł
Atmega168A-PU 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 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 10350888624f3500cc2b60c.jpg W każdym sklepie elektronicznym kupisz. 0.30zł/szt
Dioda Zenera x2 3V3 12323721734f35010707f53.jpg W każdym sklepie elektronicznym kupisz. 0.15zł/szt
Rezystory x10 12159849174f3500ff716c9.jpg W każdym sklepie elektronicznym kupisz. 2x75R , 1x15R, 1x10kR, 6x1kR (do diód) 0.15zł/szt
Kondensator x3 11759852554f3500d68923c.jpg W każdym sklepie elektronicznym kupisz. 2x27pF (ceramiczny), 1x220uF(elektrolitowy) 0.15zł/szt
Kabel USB 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.

10199513284f352f5cac7f9-image(300x300).jpg 3508169514f352f694c55f-image(300x300).jpg

Schemat

16655386104f34fd90e00a1.png

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 klikamy 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:

/*
 * 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

#ifndef __usbconfig_h_included__
#define __usbconfig_h_included__

Wstawiamy:

    #define  F_CPU   12000000 //taktowanie procesora w hz

Po tym odszukujemy:

/* ---------------------------- Hardware Config ---------------------------- */

Zmieniamy dane na :

#define USB_CFG_IOPORTNAME      D
#define USB_CFG_DMINUS_BIT      3
#define USB_CFG_DPLUS_BIT       2

Następnie szukamy:

/* -------------------------- Device Description --------------------------- */

Ustawiamy:

#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)" ([email protected]@$$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)" ([email protected]@[email protected]@[email protected])
Example_avr_usb.obj : error LNK2020: unresolved token (0A00001E) "extern "C" struct usb_dev_handle * __clrcall usb_open(struct usb_device *)" ([email protected]@[email protected]@[email protected]@@Z)
Example_avr_usb.obj : error LNK2020: unresolved token (0A00001F) "extern "C" int __clrcall usb_close(struct usb_dev_handle *)" ([email protected]@[email protected]@@Z)
Example_avr_usb.obj : error LNK2020: unresolved token (0A000022) "extern "C" struct usb_bus * __clrcall usb_get_busses(void)" ([email protected]@[email protected]@XZ)
Example_avr_usb.obj : error LNK2020: unresolved token (0A000023) "extern "C" int __clrcall usb_find_devices(void)" ([email protected]@$$J0YMHXZ)
Example_avr_usb.obj : error LNK2020: unresolved token (0A000024) "extern "C" int __clrcall usb_find_busses(void)" ([email protected]@$$J0YMHXZ)
Example_avr_usb.obj : error LNK2020: unresolved token (0A000055) "extern "C" void __clrcall usb_init(void)" ([email protected]@$$J0YMXXZ)
Example_avr_usb.obj : error LNK2001: unresolved external symbol "extern "C" void __clrcall usb_init(void)" ([email protected]@$$J0YMXXZ)
Example_avr_usb.obj : error LNK2001: unresolved external symbol "extern "C" struct usb_dev_handle * __clrcall usb_open(struct usb_device *)" ([email protected]@[email protected]@[email protected]@@Z)
Example_avr_usb.obj : error LNK2001: unresolved external symbol "extern "C" int __clrcall usb_close(struct usb_dev_handle *)" ([email protected]@[email protected]@@Z)
Example_avr_usb.obj : error LNK2001: unresolved external symbol "extern "C" int __clrcall usb_find_busses(void)" ([email protected]@$$J0YMHXZ)
Example_avr_usb.obj : error LNK2001: unresolved external symbol "extern "C" int __clrcall usb_find_devices(void)" ([email protected]@$$J0YMHXZ)
Example_avr_usb.obj : error LNK2001: unresolved external symbol "extern "C" struct usb_bus * __clrcall usb_get_busses(void)" ([email protected]@[email protected]@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)" ([email protected]@[email protected]@[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

20744070934f352f82a453c.png
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

#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

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

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

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

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

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

what should I do next ?

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

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ć?

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