Kami melakukan pengujian beban pustaka USB berkecepatan tinggi untuk STM32F103C8T6

Pada artikel sebelumnya, saya menunjukkan kecepatan maksimum bus USB untuk mikrokontroler STM32F103 dengan perpustakaan MiddleWare standar. Dalam komentar, saya diperlihatkan dua perpustakaan buatan sendiri sekaligus yang memeras semua jus dari USB FS. Tetapi penulis salah satu perpustakaan mengungkapkan gagasan bahwa mereka bekerja dengan cepat dengan cepat, dan seberapa dapat diandalkan tidak jelas. Dia pikir akan berguna untuk melakukan beberapa pengujian beban dengan beberapa data yang berguna. Hanya jika tidak hilang atau terdistorsi baru dapat dikatakan bahwa perpustakaan memiliki hak untuk hidup.







Tak perlu dikatakan, saya tidak sabar menunggu akhir pekan untuk melakukan cek saya. Mari kita lihat hasil tesnya. Dan untuk membuatnya lebih menarik, sepanjang jalan kita akan mempertimbangkan teknologi menampilkan variabel "on the fly", tanpa menghentikan inti prosesor. Nah, dan teknologi debugging visual dari file elf yang dikumpulkan oleh kompiler batch.



Pengujian seperti apa yang akan kami lakukan



Inti dari pengujian telah dibahas dalam komentar di artikel sebelumnya. Program PC asli mengirimkan data. Yang tidak penting. Data, dan hanya itu. Pengontrol menerima data ini dan mengabaikannya dengan aman. Karena masalahnya adalah kecepatan transmisi. Pustaka baru secara teoritis dapat melewatkan beberapa data, atau mencampur sepasang buffer. Oleh karena itu, alirannya harus berubah-ubah menurut waktu. Dalam kasus seperti itu, baik urutan pseudo-acak atau data tambahan dikirim.



Tidak ingin menghabiskan banyak waktu untuk menghasilkan urutan pseudo-acak untuk sumber dan tujuan (algoritma harus sama), saya membatasi diri pada kata 32-bit tambahan. Kerugian utama dari pendekatan ini adalah bahwa hingga tiga dari empat byte dapat bertepatan dalam paket tetangga. Tapi satu, dan yang pertama, akan berbeda. Jadi untuk tes hari ini, sepertinya bisa diterima oleh saya.



Faktanya adalah bahwa protokol USB adalah paket. Dan paket itu sendiri ditangkap di tingkat perangkat keras. Seharusnya tidak ada situasi di dalam blok yang byte rusak, dan kemudian data yang berguna dikirim lagi. Setidaknya karena masalah yang ingin kita tangkap di perpustakaan sekarang, ini tidak akan muncul. Jika data akan rusak, maka secara global. Jika data lama ditimpa dengan yang baru, maka byte pertama akan ditimpa terlebih dahulu, dan berbeda dalam paket yang berbeda.



Pada prinsipnya, setiap orang akan dapat menulis ulang kode saya ke versi data pengujian yang berbeda ... Hari ini, saya hanya akan mengirim kata 32-bit yang bertambah, dan setelah menerimanya, periksa apakah kenaikan itu berjalan tanpa merusak urutannya.



Bagaimana kami akan melacak hasilnya?



Bagaimana kita akan mengetahui bahwa semuanya bekerja? Satu LED tidak akan cukup. Nah, untuk menambahkan UART, Anda harus meng-hardcode kode orang lain. Anda dapat membuat kesalahan Anda. Mari kita gunakan fungsi yang sudah lama saya kenal, tetapi selalu menggunakannya hanya di lingkungan pengembangan Keil. Hari ini saya akan menunjukkan cara menggunakannya di Eclipse. Dari komentar hingga artikel terakhir, saya menyadari bahwa tidak semua orang tahu tentang teknologi ini.



Port debug JTAG memungkinkan pekerjaan hanya ketika inti prosesor dihentikan. Ini tidak dapat diterima untuk USB. Di sana dan selama operasi normal, pemberhentian penuh dengan batas waktu, dan dalam kasus kami, bahkan jika kami tidak menangkap batas waktu, kecepatannya mungkin diremehkan. Untungnya, port debug SWD memungkinkan Anda memantau memori dengan cepat. Kembali pada tahun 2016, saya memeriksa menggunakan osiloskop, yang memungkinkan Anda untuk mengatur sinkronisasi berdasarkan durasi pulsa, akses ke memori oleh SWD praktis tidak memperlambat inti prosesor. Tapi bagaimana kita menggunakannya?



Hal pertama yang akan kita andalkan hari ini adalah kemampuan CubeIDE (yang merupakan Eclipse yang didoping) untuk menampilkan variabel dengan cepat. Kami akan membuat grup variabel, di mana program akan menampilkan banyak informasi berguna, dan kami akan mulai melacaknya di layar. Banyak orang tahu tentang ini, tetapi sejauh ini tidak semua orang. Beri tahu semua orang sekarang.



Dan yang kedua adalah apa yang baru-baru ini saya dan teman-teman temukan. Tidak ada seorang pun di kantor kami yang mengetahui hal ini. Ternyata jika Anda membangun proyek dengan kompiler batch, menyematkan informasi debug Dwarf-2 ke dalamnya, maka file elf ini dapat dibuka di Eclipse untuk debugging dan Anda bisa mendapatkan tautan lengkap dengan sumbernya. Pada saat yang sama, sumber itu sendiri tidak perlu terhubung ke proyek.Debugger akan secara otomatis menarik jalur ke mereka dari informasi debug. Sekarang saya selalu melakukan itu. Proyek GCC atau Dentang dibangun, dan saya hanya menghubungkan elf ke Eclipse dan melacaknya, tanpa membuang waktu untuk melampirkan proyek itu sendiri ke Eclipse ini. Terkadang mereka bahkan mengirimi saya file elf yang dikumpulkan oleh rantai alat yang tidak ada di mesin saya. Bahkan dikompilasi di Linux (dan saya bekerja dengan Windows). Metode ini bekerja bahkan dalam kasus ini, selama proyek dikirim dalam set lengkap: elf dan sumbernya. Hari ini akan membantu kita untuk tidak menyelesaikan proyek penulis dalam hal strukturnya. Saya hanya akan membangun semuanya berdasarkan makefile "asli", dan kemudian terhubung dengan debugger ke file elf.



Kami berlatih untuk terhubung



Hal pertama yang perlu kita lakukan di CubeIDE adalah proyek untuk STM32F103. "Tunggu sebentar!", Pembaca yang penuh perhatian berseru ... "Penulis baru saja berjanji bahwa dia tidak perlu melakukan apa pun dengan proyek aslinya !!!" Tidak apa-apa. Ini adalah keunikan CubeIDE. Kami membutuhkan proyek untuk STM32F103. Apa saja. Yang utama adalah di bawah STM32F103. Kami menciptakannya, mengumpulkannya dan melupakannya. Apa yang ada di dalamnya tidak penting. Fakta keberadaannya di lingkungan pengembangan adalah penting.



Sekarang di CubeIDE kita pergi ke pengaturan debugger. Misalnya, seperti ini:







Kami tidak perlu menyimpang dengan pembuatan proyek kiri jika kami memilih item Debugging Perangkat Keras GDB. Saya selalu memilihnya di Eclipses biasa. Saya mencoba memilihnya di sini:







Sayang. Proyek kiri tidak akan diperlukan, tetapi fungsi menampilkan variabel secara real time dikatakan tidak tersedia. Oleh karena itu, sayangnya dan ah. Pilih Aplikasi STM32 Cortex-M C/C++. Saya sudah memiliki dua konfigurasi di sana. Sekarang, untuk memastikan bahwa saya tidak menipu Anda, saya akan membuat yang ketiga. Untuk melakukan ini, saya klik dua kali di sini:







Saya akan memberi nama konfigurasi Artikel:







Anda harus memilih jalur ke file elf:







Saya memilih jalur ini (seharusnya tidak ada huruf Rusia di mana pun di jalur):







Dan di sini kesalahannya berkedip . Ini dia, yang merah:







Untuk menghapusnya, saya harus memilih proyek yang terkait dengan STM32F103. Di sinilah Anda perlu menyodok ke Browse:







Dan pilih proyek kiri yang dibuat sebelumnya.







Merah (tanda kesalahan) hilang:







Ups! Pada gambar ini, Anda dapat melihat bahwa setelah memilih proyek, nama file elf melompat. Peri dari proyek ini telah terdaftar. Saya harus memilih yang saya butuhkan setelah menentukan proyek lagi. Tidak heran saya berlari melalui semua poin.



Karena kami tidak memiliki apa pun untuk dikumpulkan di sini (kami mengumpulkan semuanya dalam kumpulan), kami perlu mencentang kotak agar sistem tidak mencoba melakukan pekerjaan yang tidak berguna:







Pada tab ini - semuanya. Buka tab Debugger. Benar, tidak ada yang perlu diubah di sini. Setidaknya, jika semuanya diatur dengan cara yang sama:







Sebenarnya, Anda tidak perlu mengubah apa pun di tempat lain. Nah, mari kita mulai debugging?







Secara teknis, ya. Secara organisasi - pertama-tama kita harus menyiapkan kode yang akan kita jalankan.



Memeriksa perpustakaan pertama



Jadi, unduh proyek stm32samples / F1-nolib / CDC_ACM di master eddyem / stm32samples GitHub untuk kepengarangan EddyEm...



Jangan lupa tambahkan formasi debug information dwarf-2 ke makefile:





Sama dengan teksnya:

CFLAGS	+= -O2 -g -gdwarf-2 -D__thumb2__=1 -MD

      
      





Kami mulai mengedit kode.



Ada loop tak terbatas dalam fungsi utama (). Saya hanya akan meninggalkan tanduk-ya-kaki dari dia:

    while (1){
        IWDG->KR = IWDG_REFRESH; // refresh watchdog
        usb_proc();
        get_USB();
    }

      
      





Saya akan membuat fungsi get_USB () yang berfungsi seperti ini:

uint32_t loop = 0;
uint32_t errors = 0;
uint32_t errState = 0;
int32_t lastData = 0;
int32_t show = 0;
int32_t pkt = 0;

#define USBBUF 63
char tmpbuf[USBBUF+1];
int32_t* pData = (int32_t*) tmpbuf;
// usb getline
char *get_USB()
{
    int x = USB_receive((uint8_t*)tmpbuf) / sizeof(uint32_t);
    int i;

    show += 1;

    if(!x) return NULL;

    pkt += 1;
    //     -    
    //   !
    if (pData [0] == 0)
    {
         lastData = 0;
         errState = 0;
         loop += 1;
    }
    //    
    if (errState)
    {
         return NULL;
    }
    //   
    for (i=0;i<x;i++)
    {
          // !
          if (pData[i]!=lastData++)
          {
             //  !
             errState = 1;
             //   
             errors += 1;
             //     
             return NULL;
          }
    }
    //        
    return  NULL;
}

      
      





Sekelompok variabel global dibuat untuk memantaunya secara real time. Penduduk setempat hilang di setiap startup. Yang global terlihat selamanya.



Variabel show akan menunjukkan bahwa debugger benar-benar menampilkan semuanya. Itu bertambah setiap kali fungsi dimasukkan, apakah ada data atau tidak. Dan kami memanggil fungsi dalam loop tak terbatas sepanjang waktu.



Variabel pkt akan menunjukkan bahwa data tersebut benar-benar datang (awalnya bukan dari saya). Ini akan meningkat hanya jika kita tidak keluar karena fakta bahwa tidak ada apa-apa dari USB.



data terakhirakan menunjukkan berapa banyak yang telah kita hitung dalam tes. Ini akan memastikan bahwa kita benar-benar bekerja dengan blok data yang besar. Nilai variabel ini pada akhir pengujian menunjukkan ukuran blok dalam kata ganda. Untuk memahami berapa banyak byte yang telah berlalu, Anda perlu mengalikan nilainya dengan 4.



Loop akan meningkat ketika satu blok data tiba, mulai dari nol. Secara kasar, ini adalah nomor tes. Nah, atau nomor lari. Ketika saya mengumpulkan statistik untuk membuat plot, ada beberapa run ini. Ukuran yang berbeda dari blok yang diminta, dikalikan dengan pengulangan untuk rata-rata hasil.



errState- variabel tambahan yang mencegah munculnya bola salju kesalahan. Pada kesalahan pertama, ia terbang ke satu dan berhenti menganalisis data sebelum memulai tes baru.



kesalahan - penghitung kesalahan yang terjadi sekali. Pada awalnya, saya sendiri membuat kesalahan dalam logika, kemudian penghitung ini terus meningkat. Tetapi jika semuanya baik, seharusnya tidak meningkat.



Saya hampir lupa. Saya juga mengomentari tanda centang di fungsi USB_receive:





Sama dengan teks:

uint8_t USB_receive(uint8_t *buf){
    if(/*!usbON ||*/ !rxNE) return 0;
...

      
      







Bendera ini diatur ketika terminal mengatur kecepatan port COM virtual. Program pengujian saya, di sisi lain, membuka perangkat secara langsung melalui driver WinUSB dan tidak melakukan apa pun untuk menyesuaikan fungsionalitas CDC. Hal termudah untuk dilakukan adalah mengabaikan bendera ini.



Baik. Bangun proyek dengan kompiler batch dan jalankan di debugger CubeIDE. Seperti yang saya tahu, tidak semua pembaca menyukai gambar animasi. Untuk beberapa, mereka mengalihkan perhatian dari membaca teks. Tapi ini sangat bagus untuk dilihat.







Ini berdetak! Ini berdetak! Ini berdetak!



Baik. Tambahkan pengisian array ke program pengujian:

    QByteArray data;
    data.resize(totalSize);

    uint32_t* dwPtr = (uint32_t*) data.constData();
    for (uint32_t i = 0;i<totalSize/4;i++)
    {
        dwPtr[i] = i;
    }

      
      





Dan kami menjalankan tes. Kami mendapatkan keindahan semacam ini dalam variabel (kesalahan nol):







Dan ini adalah grafik kecepatan:







Nilainya sedikit lebih rendah daripada dalam operasi yang benar-benar tidak digunakan, tetapi masih menyenangkan. Secara umum, ST-LINK telah ditambahkan ke sistem saya, dan jumlah bit yang berjalan melalui USB tergantung pada data yang dipompa (terkadang bit sinkronisasi dapat dimasukkan).



Seekor kuda berbentuk bola bekerja dalam ruang hampa, tetapi bagaimana dengan yang asli?



Ada satu masalah potensial dengan keseluruhan sistem ini. Sekarang fungsi menerima data secara konstan dipanggil dalam infinite loop. Kami tidak memiliki pekerjaan khusus yang berguna saat ini. Dan jika mereka? Kemudian pemanggilan fungsi ini dapat terjadi tidak segera setelah kedatangan paket, tetapi dengan penundaan.



Apakah kita akan menguji opsi yang berbeda? Di pikiran - itu perlu, tetapi tidak ada waktu. Saat ini saya sibuk bekerja dengan pengontrol yang sama sekali berbeda. Dan kemudian saya hanya bersenang-senang di akhir pekan. Karena itu, kita akan pergi ke arah lain. Kami akan merekatkan penerimaan data dengan fakta kedatangan mereka.



Hal-hal ini dilakukan dengan interupsi. Tetapi di artikel terakhir dikatakan bahwa jika kita meregangkan pengendali interupsi USB, perangkat keras akan mulai mengirim NAK dan semua pesona perpustakaan yang bersangkutan akan menjadi sia-sia. Bagaimana kita mendapatkan interupsi, tetapi tidak berlama-lama di interupsi?



Nah, jalannya dikenal di sini. Di pengendali interupsi USB, kita harus membuatnya sehingga segera setelah keluar, interupsi juga akan dipicu, tetapi yang lain. Dan di sana kami akan dengan cepat, dengan latensi rendah yang dijamin, mengambil data dari buffer perangkat keras ke buffer internal kami. Apa gangguan untuk duduk? Memeriksa kode startup. Yaitu, penangan interupsi. Tugas kita adalah menemukan yang tidak terpakai.



Berikut adalah file yang menarik bagi kami

\ stm32samples-master \ F1-nolib \ inc \ startup \ vector.c



Biarkan saya dengan nakal meminjam interupsi dari UART ketiga. Sebenarnya, kami juga tidak menggunakan yang pertama. Tapi mungkin suatu saat nanti. Dan saya tidak pernah menggunakan yang ketiga dalam hidup saya. Oleh karena itu, secara pribadi, saya akan dengan nakal duduk di pawang khusus ini.



Begini penjelasannya : [NVIC_USART3_IRQ] = usart3_isr, \



Mengetahui namanya, buat fungsi di file main.c:

void usart3_isr()
{
    NVIC_ClearPendingIRQ(USART3_IRQn);
    get_USB();
}

      
      





Ini akan menjadi semacam fungsi panggilan balik. Dan itu akan memanggil kita kode yang baru saja kita tulis. Dan mari mengomentari panggilan ke get_USB () dalam infinite loop.



Sekarang kita perlu mengatur interupsi ini ke prioritas yang lebih rendah sehingga tidak mengganggu siapa pun. Dalam kehidupan nyata, Anda mungkin harus kreatif saat memilih prioritas. Tapi hari ini saya hanya akan mengambil yang kelima belas. Kami menambahkan kode berikut ke bagian inisialisasi dari fungsi utama ():

    NVIC_SetPriority(USART3_IRQn, 15);
    NVIC_EnableIRQ(USART3_IRQn);

      
      





Nah, sekarang sampai pada bagian yang menyenangkan. Di pengendali interupsi USB, tambahkan provokasi untuk memicu interupsi USART3, jika ada panggilan ke titik akhir kami:





Teks yang sama.
#include "stm32f10x.h"
โ€ฆ
void usb_lp_can_rx0_isr(){
   LED_off(LED0);
    if(USB->ISTR & USB_ISTR_RESET){
โ€ฆ
    }
    if(USB->ISTR & USB_ISTR_CTR){
        // EP number
        uint8_t n = USB->ISTR & USB_ISTR_EPID;

        if (n == 1)
        {
             NVIC_SetPendingIRQ(USART3_IRQn);
        }
        // copy status register
        uint16_t epstatus = USB->EPnR[n];
        // copy received bytes amount
โ€ฆ

      
      









Karena prioritasnya rendah, tidak akan terjadi apa-apa hingga interupsi USB berakhir. Tapi begitu itu berakhir, mereka akan segera menghubungi kami. Karena kita belum memiliki interupsi lain. Bahkan dengan prioritas kelima belas, kita akan menjadi VIP.



Kami meluncurkan. Pada awalnya menakutkan bahwa variabel acara tidak meningkat. Tapi itu biasa. Sekarang fungsi tersebut tidak dipanggil tanpa syarat, tetapi hanya setelah interupsi yang sebenarnya. Jadi kita harus mulai menguji.



Anda dapat menonton proses pengujian selamanya.







Dan inilah metrik kecepatan:







Memeriksa perpustakaan kedua



Sekarang kami memeriksa perpustakaan usb / 5.CDC_F1 di COKPOWEHEU / usb GitHub utama oleh COKPOWEHEU... Deskripsi perpustakaan ini dapat ditemukan di sini: USB pada register: STM32L1 / STM32F1 / . Di sinilah kami disediakan dengan fungsi panggilan balik untuk menangani aktivitas titik akhir. Di sini kita akan memperbaikinya. Variabel acara tidak lagi diperlukan. Kami selalu dipanggil pada saat kedatangan data. Jika tidak, kami mendapatkan kode yang hampir sama.

uint32_t loop = 0;
uint32_t errors = 0;
uint32_t errState = 0;
int32_t lastData = 0;
int32_t pkt = 0;

void data_out_callback(uint8_t epnum){
  int i;
uint8_t buf[ ENDP_DATA_SIZE ];
int32_t* pData = (int32_t*) buf;

  int len = usb_ep_read_double( ENDP_DATA_OUT, buf) / sizeof (uint32_t);
  if(len == 0)return;

    pkt += 1;
    //     -    
    //   !
    if (pData [0] == 0)
    {
         lastData = 0;
         errState = 0;
         loop += 1;
    }
    //    
    if (errState)
    {
         return NULL;
    }
    //   
    for (i=0;i<len;i++)
    {
          // !
          if (pData[i]!=lastData++)
          {
             //  !
             errState = 1;
             //   
             errors += 1;
             //     
             return NULL;
          }
    }


}

      
      





Saat memeriksa dengan saya, CubeIDE karena alasan tertentu salah menentukan alamat awal. Mungkin ada semacam ketidakcocokan dengan proyek "kiri" yang sama. Mari kita tunda ini untuk studi terpisah. Sampai saya mulai paham, tapi pas di awal saya masukkan nilai register PC yang benar. Kode berjalan dan mulai bekerja. Kami menjalankan tes. Jumlah kesalahan juga nol:







Kecepatannya juga lumayan:







Kesimpulan



Kedua perpustakaan USB Rusia mengatasi pengujian beban kasar. Tak satu pun dari mereka meninggalkan balapan. Benar, saya tahu secara langsung bahwa pengujian tidak membuktikan tidak adanya kesalahan, itu mengungkapkan keberadaan mereka. Tetapi tes yang dikutip secara khusus tidak mengungkapkan apa pun. Ini memberi harapan bahwa salah satu perpustakaan ini dapat digunakan.



Selama ini, kami telah menguasai penggantian output debug dengan memantau sejumlah variabel secara real time melalui port SWD. Secara kasar, kami juga menguasai debugging aplikasi batch-built apa pun di Eclipse, tetapi di sepanjang jalan, karena pencampuran kedua proyek, saya mengalami beberapa kesulitan yang harus saya atasi dengan memperbaiki register PC secara langsung. Tapi di Eclipse biasa, pencampuran semacam ini tidak diperlukan. Dan pada akhirnya, bahkan dengan bantuan sabit, palu, dan semacam ibu, tujuan akhir tetap tercapai. Debugging telah dilakukan. Pada saat yang sama, kode sumber pada Syakh masih ditampilkan di Eclipse.



kata penutup



Ketika artikel sudah ditulis, tetapi masih dalam proses mengunggah ke Habr, materi yang luar biasa muncul untuk kepenulisan DSarovsky... Di sana juga, akses ke USB diimplementasikan, tetapi ini dilakukan melalui perpustakaan yang dibuat dengan gaya favorit saya - gaya Konstantin Chizhov.



Saya hanya berkewajiban untuk mencatat keberadaan perpustakaan yang dibuat dalam versi yang begitu indah. Saat ini, kami memeriksa kinerjanya dengan pembuatnya dan menemukan bahwa sejauh ini kecepatannya biasa saja, tidak maksimal. Tetapi mungkin saja ketika Anda membaca baris-baris ini, itu sudah di-overclock. Karena itu, saya akan meninggalkan tautan ke sana di antara yang lainnya. Dia hanya harus lepas landas! Perpustakaan dalam gaya ini tidak bisa tidak lepas landas!



All Articles