Stm32 + USB pada template C ++

* Terima kasih kepada @ grafalex untuk ide rekaman yang keren





Tidak ada yang suka USB

Kemajuan dalam studi pemrograman mikrokontroler, saya menyadari kebutuhan untuk menguasai USB, karena tidak diragukan lagi merupakan antarmuka utama untuk koneksi perangkat non-sirkuit. Namun, ternyata tidak banyak materi yang relevan di dunia terbuka. Setelah menganalisis berbagai forum, saya merumuskan alasan berikut untuk tidak populernya USB dalam proyek:





  • @jaiprakash mengingatkan bahwa nilai VID wajib untuk perangkat USB harus dibeli dengan harga yang mahal.





  • Tidak adanya kebutuhan transmisi data berkecepatan tinggi di sebagian besar proyek.





  • Kompleksitas tinggi dari standar itu sendiri dan pengembangan dibandingkan dengan antarmuka UART yang sudah dikenal. Lebih murah menambahkan adaptor USB <-> UART yang sudah jadi ke perangkat.





  • Kurangnya keterampilan pengembangan driver Windows / Linux.





Akibatnya, sebagian besar pengembang lebih suka menggunakan UART (melalui konverter perangkat keras atau, paling banyak, dengan membuat perangkat VCP, yang kodenya berhasil dibuat oleh CubeMX). Saya memutuskan untuk mencoba memahami USB setidaknya pada tingkat dasar, melanjutkan penggunaan template bahasa C ++. Posting ini menjelaskan cara terapan untuk mengalokasikan sumber daya (yaitu memori buffer dan register) antara titik akhir perangkat.





Masalah duplikasi

Elemen utama dari program yang mengimplementasikan perangkat USB adalah Endpoint . Tuan rumah berkomunikasi dengan titik akhir tertentu. Perangkat harus berisi titik akhir dengan angka 0, di mana kontrol berlangsung, permintaan untuk berbagai penjelas pada tahap pencacahan, perintah untuk menetapkan alamat, memilih konfigurasi, dan semua kontrol lainnya. Rincian lebih lanjut tentang konsep titik akhir dan, pada prinsipnya, pengetahuan dasar tentang USB dapat ditemukan dalam terjemahan "USB di NutShell" pada sumber daya mikrosin (terima kasih banyak kepada teman-teman atas pekerjaan yang telah dilakukan, mereka melakukan pekerjaan yang sangat berguna) .





Stm32F0/F1 - Packet Memory Area (PMA), . USB- , , . , K, "" K+1, ... , N. ( N - ). : 100% .





, ( ) , runtime compile-time, :





  • . . (ADDRn_TX, COUNTn_TX, ADDRn_RX, COUNTn_RX), , runtime .





  • , EPnR ( , , ).





:





  1. (0..16).





  2. (Control, Interrupt, Bulk, Isochronous).





  3. (In, Out).





  4. .





, .





:





  1. (EPnR).





  2. .





  3. ( ).





: N . , , :





  1. , , .





  2. , .





  3. "" .





:





template<typename... AllEndpoints,
  typename... BidirectionalAndBulkDoubleBufferedEndpoints,
  typename... RxEndpoints,
  typename... BulkDoubleBufferedTxEndpoints>
class EndpointsManagerBase<TypeList<AllEndpoints...>,
  TypeList<BidirectionalAndBulkDoubleBufferedEndpoints...>,
  TypeList<RxEndpoints...>,
  TypeList<BulkDoubleBufferedTxEndpoints...>>
{
  //   
  using AllEndpointsList = TypeList<AllEndpoints...>;
  ///      
  static const auto BdtSize = 8 * (EndpointEPRn<GetType_t<sizeof...(AllEndpoints) - 1, AllEndpointsList>, AllEndpointsList>::RegisterNumber + 1);
  ///      
  template<typename Endpoint>
  static constexpr uint32_t BufferOffset = BdtSize + OffsetOfBuffer<TypeIndex<Endpoint, AllEndpointsList>::value, AllEndpointsList>::value;
  ///      
  template<typename Endpoint>
  static constexpr uint32_t BdtCellOffset =
    EndpointEPRn<Endpoint, AllEndpointsList>::RegisterNumber * 8
      + (Endpoint::Type == EndpointType::Control
      || Endpoint::Type == EndpointType::ControlStatusOut
      || Endpoint::Type == EndpointType::BulkDoubleBuffered
      || Endpoint::Direction == EndpointDirection::Out
      || Endpoint::Direction == EndpointDirection::Bidirectional
        ? 0
        : 4);
  ///    USB
  static const uint32_t BdtBase = PmaBufferBase;
public:
  /// ""  
  template<typename Endpoint>
  using ExtendEndpoint = 
    typename Select<Endpoint::Type == EndpointType::Control || Endpoint::Type == EndpointType::ControlStatusOut,
    ControlEndpoint<Endpoint,
      typename EndpointEPRn<Endpoint, TypeList<AllEndpoints...>>::type,
      PmaBufferBase + BufferOffset<Endpoint>, // TxBuffer
      PmaBufferBase + BdtCellOffset<Endpoint> + 2, // TxCount
      PmaBufferBase + BufferOffset<Endpoint> + Endpoint::MaxPacketSize, // RxBuffer
      PmaBufferBase + BdtCellOffset<Endpoint> + 6>, //RxCount
    typename Select<Endpoint::Direction == EndpointDirection::Bidirectional,
    BidirectionalEndpoint<Endpoint,
      typename EndpointEPRn<Endpoint, TypeList<AllEndpoints...>>::type,
      PmaBufferBase + BufferOffset<Endpoint>, // TxBuffer
      PmaBufferBase + BdtCellOffset<Endpoint> + 2, // TxCount
      PmaBufferBase + BufferOffset<Endpoint> + Endpoint::MaxPacketSize, // RxBuffer
      PmaBufferBase + BdtCellOffset<Endpoint> + 6>, //RxCount
    ... //       
    void>::value>::value;

  static void Init()
  {
    memset(reinterpret_cast<void*>(BdtBase), 0x00, BdtSize);
    //     
    ((*(reinterpret_cast<uint16_t*>(BdtBase + BdtCellOffset<AllEndpoints>)) = BufferOffset<AllEndpoints>), ...);
    //           
    ((*(reinterpret_cast<uint16_t*>(BdtBase + BdtCellOffset<BidirectionalAndBulkDoubleBufferedEndpoints> + 4)) = (BufferOffset<BidirectionalAndBulkDoubleBufferedEndpoints> + BidirectionalAndBulkDoubleBufferedEndpoints::MaxPacketSize)), ...);
    //  COUNTn_RX   (Rx, Out) 
    ((*(reinterpret_cast<uint16_t*>(BdtBase + BdtCellOffset<RxEndpoints> + 2)) = (RxEndpoints::MaxPacketSize <= 62
      ? (RxEndpoints::MaxPacketSize / 2) << 10
      : 0x8000 | (RxEndpoints::MaxPacketSize / 32) << 10)), ...);
    //  COUNTn_RX        
    ((*(reinterpret_cast<uint16_t*>(BdtBase + BdtCellOffset<BidirectionalAndBulkDoubleBufferedEndpoints> + 6)) = (BidirectionalAndBulkDoubleBufferedEndpoints::MaxPacketSize <= 62
      ? (BidirectionalAndBulkDoubleBufferedEndpoints::MaxPacketSize / 2) << 10
      : 0x8000 | (BidirectionalAndBulkDoubleBufferedEndpoints::MaxPacketSize / 32) << 10)), ...);

    //    COUNTn_RX  Tx     (,    ,     )
    ((*(reinterpret_cast<uint16_t*>(BdtBase + BdtCellOffset<BulkDoubleBufferedTxEndpoints> + 2)) = 0), ...);
    ((*(reinterpret_cast<uint16_t*>(BdtBase + BdtCellOffset<BulkDoubleBufferedTxEndpoints> + 6)) = 0), ...);
  }
};

template<typename Endpoints>
using EndpointsManager = EndpointsManagerBase<SortedUniqueEndpoints<Endpoints>,
  typename Sample<IsBidirectionalOrBulkDoubleBufferedEndpoint, SortedUniqueEndpoints<Endpoints>>::type,
  typename Sample<IsOutEndpoint, SortedUniqueEndpoints<Endpoints>>::type,
  typename Sample<IsBulkDoubleBufferedTxEndpoint, SortedUniqueEndpoints<Endpoints>>::type>;

template<typename... Endpoints>
using EndpointsInitializer = EndpointsManagerBase<SortedUniqueEndpoints<TypeList<Endpoints...>>,
  TypeList<>,
  TypeList<>,
  TypeList<>>;
      
      



, :





  1. EndpointEPRn - , EPnR . : . , .





  2. BufferOffset - , . , N 0, ..., N-1.





  3. SortedUniqueEndpoints - , + . USB /, Device.





  4. IsBidirectionalOrBulkDoubleBufferedEndpoint, IsOutEndpoint, IsBulkDoubleBufferedTxEndpoint - .





:





using DefaultEp0 = ZeroEndpointBase<64>;
using LedsControlEpBase = OutEndpointBase<1, EndpointType::Interrupt, 64, 32>;
//         
using EpInitializer = EndpointsInitializer<DefaultEp0, LedsControlEpBase>;

// EpInitializer    .
//  ,   ,          
using Ep0 = EpInitializer::ExtendEndpoint<DefaultEp0>;
using LedsControlEp = EpInitializer::ExtendEndpoint<LedsControlEpBase>;
//  ,   .
using Hid = HidInterface<0, 0, 0, 0, HidDesc, LedsControlEp>;
using Config = HidConfiguration<0, 250, false, false, Report, Hid>;
using MyDevice = Device<0x0200, DeviceClass::InterfaceSpecified, 0, 0, 0x0483, 0x5711, 0, Ep0, Config>;
      
      



Device , :





template<
  ...
  typename _Ep0,
  typename... _Configurations>
  class DeviceBase : public _Ep0
{
  using This = DeviceBase<_Regs, _IRQNumber, _ClockCtrl, _UsbVersion, _Class, _SubClass, _Protocol, _VendorId, _ProductId, _DeviceReleaseNumber, _Ep0, _Configurations...>;
  using Endpoints = Append_t<typename _Configurations::Endpoints...>;
  using Configurations = TypeList<_Configurations...>;

  using EpBufferManager = EndpointsManager<Append_t<_Ep0, Endpoints>>;
  //  Device     
  using EpHandlers = EndpointHandlers<Append_t<This, Endpoints>>;
public:
  static void Enable()
  {
    _ClockCtrl::Enable();
    //        
    EpBufferManager::Init();
      
      



C++ :





  1. , , , ( HID-, , 2400 ).





  2. , .





  3. , , . "" USB.





  4. * . C++, , .





USB . , - - , USB, , - . , . , USB , , "" , .





Posting ini dikhususkan bukan untuk bagian pustaka yang terkait dengan USB secara umum, tetapi untuk modul kecil tapi penting untuk mendistribusikan sumber daya antar titik akhir. Saya akan dengan senang hati memiliki pertanyaan dan komentar.





Anda dapat melihat seluruh kode (saya menguji USB sejauh ini hanya pada F072RBT6, karena ada disko dengan miniusb yang disolder) di sini . Saya berharap bisa mengalahkan USB pada musim panas setidaknya untuk seri MK F0 dan F1. Saya melihat F4 - semuanya lebih dingin di sana (ada dukungan OTG) dan sulit.








All Articles