Stm32 + USB on C ++テンプレート

*クールなテープのアイデアを提供してくれた@grafalexに感謝します





USBが好きな人はいません

プログラミングマイクロコントローラーの研究を進めていくうちに、USBは間違いなく非回路デバイス接続のメインインターフェイスであるため、マスターする必要があることに気づきました。しかし、オープンワールドには関連資料が少ないことがわかりました。さまざまなフォーラムを分析した後、プロジェクトでUSBが人気がない理由を次のように定式化しました。





  • @jaiprakashは、USBデバイスの必須のVID値を多額のお金で購入する必要があることを思い出させました。





  • ほとんどのプロジェクトで高速データ送信の必要性がありません。





  • おなじみのUARTインターフェースと比較して、規格自体と開発が非常に複雑です。既製のUSB <-> UARTアダプターをデバイスに追加する方が安価です。





  • Windows / Linuxドライバー開発スキルの欠如。





その結果、開発者はほとんどUARTを使用することを好みます(ハードウェアコンバーターを介して、またはせいぜい、コードがCubeMXによって正常に生成されるVCPデバイスを作成することによって)。私は、C ++言語テンプレートを使用するラインを継続しながら、少なくとも基本的なレベルでUSBを理解しようと決心しました。この投稿では、デバイスエンドポイント間でリソース(つまり、バッファメモリとレジスタ)を割り当てるための適用方法について説明します。





重複の問題

USBデバイスを実装するプログラムの主な要素はエンドポイントです。ホストは特定のエンドポイントと通信します。デバイスには、制御が行われる番号0のエンドポイント、列挙段階でのさまざまな記述子の要求、アドレスの割り当て、構成の選択、およびその他すべての制御が含まれている必要があります。エンドポイントの概念の詳細と、原則としてUSBの基本的な知識は、microsinリソースの「USBin NutShell」の翻訳に記載されています(作業を行ってくれた人たちに感謝します。彼らは非常に便利な仕事をしました)。 。





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 , , "" , .





この投稿は、一般的なUSBに関連するライブラリの一部ではなく、エンドポイント間でリソースを分散するための小さいながらも重要なモジュールに向けられました。質問やコメントをいただければ幸いです。





あなたは(はんだ付けミニUSBとディスコがありますので、私は、唯一のF072RBT6に、これまでUSBをテストしてい)コード全体を表示することができ、ここで少なくともMKシリーズF0とF1では夏までにUSBを打ち負かしたいと思っています。私はF4を見ました-すべてがそこに涼しく(OTGサポートがあります)そして難しいです。








All Articles