Stm32 + USB en plantillas C ++

* Gracias a @ grafalex por la genial idea de la cinta





A nadie le gusta el USB

Avanzando en el estudio de la programaci贸n de microcontroladores, me di cuenta de la necesidad de dominar el USB, ya que sin duda es la interfaz principal para la conexi贸n de dispositivos que no est谩n en circuito. Sin embargo, result贸 que no hay muchos materiales relevantes en el mundo abierto. Despu茅s de analizar varios foros, formul茅 las siguientes razones de la impopularidad de USB en los proyectos:





  • @jaiprakash record贸 que el valor VID obligatorio para un dispositivo USB debe comprarse por mucho dinero.





  • La ausencia de la necesidad de transmisi贸n de datos a alta velocidad en la mayor铆a de los proyectos.





  • La alta complejidad del est谩ndar en s铆 y el desarrollo en comparaci贸n con la conocida interfaz UART. Es m谩s econ贸mico agregar un adaptador USB <-> UART listo para usar al dispositivo.





  • Falta de habilidades de desarrollo de controladores para Windows / Linux.





Como resultado, los desarrolladores prefieren en su mayor铆a usar UART (a trav茅s de un convertidor de hardware o, como m谩ximo, creando un dispositivo VCP, cuyo c贸digo es generado con 茅xito por CubeMX). Decid铆 intentar entender USB al menos a un nivel b谩sico, continuando la l铆nea de usar las plantillas de lenguaje C ++. Esta publicaci贸n describe la forma aplicada de asignar recursos (es decir, memoria intermedia y registros) entre los puntos finales de los dispositivos.





Problema de duplicaci贸n

El elemento principal de un programa que implementa un dispositivo USB es un Endpoint . El host se comunica con un punto final espec铆fico. El dispositivo debe contener un punto final con el n煤mero 0, a trav茅s del cual se realiza el control, solicitudes de varios descriptores en la etapa de enumeraci贸n, comandos para asignar una direcci贸n, elegir una configuraci贸n y todos los dem谩s controles. M谩s detalles sobre el concepto de endpoints y, en principio, conocimientos b谩sicos de USB se pueden encontrar en la traducci贸n de "USB in NutShell" en el recurso microsin (muchas gracias a los chicos por el trabajo realizado, hicieron un trabajo muy 煤til) .





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





Esta publicaci贸n no se dedic贸 a la parte de la biblioteca relacionada con USB en general, sino a un m贸dulo peque帽o pero importante para distribuir recursos entre terminales. Estar铆a encantado de tener preguntas y comentarios.





Puede ver el c贸digo completo (estoy probando USB hasta ahora solo en F072RBT6, porque hay una discoteca con un miniusb soldado) aqu铆 . Espero vencer al USB en verano al menos para las series MK F0 y F1. Mir茅 F4: todo est谩 m谩s fresco all铆 (hay soporte OTG) y dif铆cil.








All Articles