Stm32 + USB auf C ++ - Vorlagen. Fortsetzung. Versteckt machen

Das letzte Mal zeigte eine Möglichkeit, Ressourcen zwischen Endpunkten zuzuweisen, nämlich EPnR-Register, Speicherpuffer für Deskriptoren und für die Puffer selbst. Ich schlage vor, das, was wir begonnen haben, fortzusetzen und die geschriebene Bibliothek am Beispiel eines einfachen HID-Geräts zu betrachten, mit dem Sie eine LED steuern können.





Unterbrechen Sie die Freigabe

Zusätzlich zu den zuvor angegebenen Ressourcen teilen sich die Endpunkte auch einen einzelnen Interrupt. Dementsprechend muss der allgemeine (Haupt-) Handler die Steuerung korrekt an den Interrupt-Handler des gewünschten Endpunkts übertragen. Die Endpunktnummer, auf die der Host zugreift, wird in den EP_ID-Bits des ISTR-Registers aufgezeichnet. Nach der Implementierungszeile einer Bibliothek mit vollständigen Vorlagen erhielt ich die folgende Klasse:





using EpRequestHandler = std::add_pointer_t<void()>;
template<typename...>
class EndpointHandlersBase;
template<typename... Endpoints, int8_t... Indexes>
class EndpointHandlersBase<TypeList<Endpoints...>, Int8_tArray<Indexes...>>
{
public:
  //    
  static constexpr EpRequestHandler _handlers[] = {Endpoints::Handler...};
  //  
  static constexpr int8_t _handlersIndexes[] = {Indexes...};
public:
  inline static void Handle(uint8_t number, EndpointDirection direction)
  {
    _handlers[_handlersIndexes[2 * number + (direction == EndpointDirection::Out ? 1 : 0)]]();
  }
};
      
      



Das Schlüsselelement der Klasse ist das Array _handlersIndexes , das die Endpunktnummer und -richtung einem bestimmten Handler zuordnet . Um dieses Array zu erhalten, wird eine spezielle Klasse implementiert:





template<int8_t Index, typename Endpoints>
class EndpointHandlersIndexes
{
  //      .
  using Predicate = Select<Index % 2 == 0, IsTxOrBidirectionalEndpointWithNumber<Index / 2>, IsRxOrBidirectionalEndpointWithNumber<Index / 2>>::value;
  static const int8_t EndpointIndex = Search<Predicate::template type, Endpoints>::value;
public:
  //           -1   .
  using type = typename Int8_tArray_InsertBack<typename EndpointHandlersIndexes<Index - 1, Endpoints>::type, EndpointIndex>::type;
};
template<typename Endpoints>
class EndpointHandlersIndexes<-1, Endpoints>
{
public:
  using type = Int8_tArray<>;
};
      
      



Übrigens impliziert diese Implementierung eine Empfehlung, Endpunkte mit Zahlen in der angegebenen Reihenfolge zu deklarieren, da die Größe des Arrays von Handlerindizes doppelt so groß ist wie die maximale Endpunktzahl.





Endpunktklasse

- : , :





template <uint8_t _Number, EndpointDirection _Direction, EndpointType _Type, uint16_t _MaxPacketSize, uint8_t _Interval>
class EndpointBase
...
      
      



, ( , ). - / (, Interrupt , Bulk - ) :





template <typename _Base, typename _Reg>
class Endpoint : public _Base
...
template<typename _Base, typename _Reg, uint32_t _TxBufferAddress, uint32_t _TxCountRegAddress, uint32_t _RxBufferAddress, uint32_t _RxCountRegAddress>
class BidirectionalEndpoint : public Endpoint<_Base, _Reg>
...
template<typename _Base, typename _Reg, uint32_t _Buffer0Address, uint32_t _Count0RegAddress, uint32_t _Buffer1Address, uint32_t _Count1RegAddress>
class BulkDoubleBufferedEndpoint : public Endpoint<_Base, _Reg>
      
      



: ( EPnR), , ( CTR_TX/RX, TX/RX_STATUS), .





, , ( ) , ( , variadic-, ):





template <uint8_t _Number, uint8_t _AlternateSetting = 0, uint8_t _Class = 0, uint8_t _SubClass = 0, uint8_t _Protocol = 0, typename... _Endpoints>
class Interface
{
public:
  using Endpoints = Zhele::TemplateUtils::TypeList<_Endpoints...>;
  static const uint8_t EndpointsCount = ((_Endpoints::Direction == EndpointDirection::Bidirectional ? 2 : 1) + ...);

  static void Reset()
  {
    (_Endpoints::Reset(), ...);
  }

  static uint16_t FillDescriptor(InterfaceDescriptor* descriptor)
  {
    uint16_t totalLength = sizeof(InterfaceDescriptor);

    *descriptor = InterfaceDescriptor {
      .Number = _Number,
      .AlternateSetting = _AlternateSetting,
      .EndpointsCount = EndpointsCount,
      .Class = _Class,
      .SubClass = _SubClass,
      .Protocol = _Protocol
    };
    
    EndpointDescriptor* endpointsDescriptors = reinterpret_cast<EndpointDescriptor*>(++descriptor);
    totalLength += (_Endpoints::FillDescriptor(endpointsDescriptors++) + ...);

    return totalLength;
  }
};
      
      



, . USB , /, . , - .





template <uint8_t _Number, uint8_t _MaxPower, bool _RemoteWakeup = false, bool _SelfPowered = false, typename... _Interfaces>
class Configuration
{
public:
  using Endpoints = Zhele::TemplateUtils::Append_t<typename _Interfaces::Endpoints...>;
  static void Reset()
  {
    (_Interfaces::Reset(), ...);
  }
...
      
      



, . , - ( , ) .





template<
  typename _Regs,
  IRQn_Type _IRQNumber,
  typename _ClockCtrl, 
  uint16_t _UsbVersion,
  DeviceClass _Class,
  uint8_t _SubClass,
  uint8_t _Protocol,
  uint16_t _VendorId,
  uint16_t _ProductId,
  uint16_t _DeviceReleaseNumber,
  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...>;

  // Replace Ep0 with this for correct handler register.
  using EpBufferManager = EndpointsManager<Append_t<_Ep0, Endpoints>>;
  using EpHandlers = EndpointHandlers<Append_t<This, Endpoints>>;
...
      
      



, :





static void CommonHandler()
{
  if(_Regs()->ISTR & USB_ISTR_RESET)
  {
    Reset();
  }
  if (_Regs()->ISTR & USB_ISTR_CTR)
  {
    uint8_t endpoint = _Regs()->ISTR & USB_ISTR_EP_ID;
    EpHandlers::Handle(endpoint, ((_Regs()->ISTR & USB_ISTR_DIR) != 0 ? EndpointDirection::Out : EndpointDirection::In));
  }
  NVIC_ClearPendingIRQ(_IRQNumber);
}
      
      



, Device , , , :





static void Handler()
{
  if(_Ep0::Reg::Get() & USB_EP_CTR_RX)
  {
    _Ep0::ClearCtrRx();
    if(_Ep0::Reg::Get() & USB_EP_SETUP)
    {
      SetupPacket* setup = reinterpret_cast<SetupPacket*>(_Ep0::RxBuffer);
      switch (setup->Request) {
      case StandartRequestCode::GetStatus: {
        uint16_t status = 0;
        _Ep0::Writer::SendData(&status, sizeof(status));
        break;
      }
      case StandartRequestCode::SetAddress: {
        TempAddressStorage = setup->Value;
        _Ep0::Writer::SendData(0);
        break;
      }
      case StandartRequestCode::GetDescriptor: {
        switch (static_cast<GetDescriptorParameter>(setup->Value)) {
        case GetDescriptorParameter::DeviceDescriptor: {
          DeviceDescriptor tempDeviceDescriptor;
          FillDescriptor(reinterpret_cast<DeviceDescriptor*>(&tempDeviceDescriptor));
          _Ep0::Writer::SendData(&tempDeviceDescriptor, setup->Length < sizeof(DeviceDescriptor) ? setup->Length : sizeof(DeviceDescriptor));
          break;
        }
        case GetDescriptorParameter::ConfigurationDescriptor: {
          uint8_t temp[64];
          uint16_t size = GetType<0, Configurations>::type::FillDescriptor(reinterpret_cast<ConfigurationDescriptor*>(&temp[0]));
          _Ep0::Writer::SendData(reinterpret_cast<ConfigurationDescriptor*>(&temp[0]), setup->Length < size ? setup->Length : size);
          break;
        }
        case GetDescriptorParameter::HidReportDescriptor: {
          uint16_t size = sizeof(GetType_t<0, Configurations>::HidReport::Data);
          _Ep0::Writer::SendData(GetType_t<0, Configurations>::HidReport::Data, setup->Length < size ? setup->Length : size);
          break;
        }
        default:
          _Ep0::SetTxStatus(EndpointStatus::Stall);
          break;
        }
        break;
      }
      case StandartRequestCode::GetConfiguration: {
        uint16_t configuration = 0;
        _Ep0::Writer::SendData(&configuration, 1);
        break;
      }
      case StandartRequestCode::SetConfiguration: {
        _Ep0::Writer::SendData(0);
        break;
      }
      default:
        _Ep0::SetTxStatus(EndpointStatus::Stall);
        break;
      }
    }
    _Ep0::SetRxStatus(EndpointStatus::Valid);
  }
  if(_Ep0::Reg::Get() & USB_EP_CTR_TX)
  {
    _Ep0::ClearCtrTx();
    if(TempAddressStorage != 0)
    {
      _Regs()->DADDR = USB_DADDR_EF | (TempAddressStorage & USB_DADDR_ADD);
      TempAddressStorage = 0;
    }
    _Ep0::SetRxStatus(EndpointStatus::Valid);
  }
}
      
      



HID

HID- - HID, HID - :





hid
template <uint8_t _Number, uint8_t _AlternateSetting, uint8_t _SubClass, uint8_t _Protocol, typename _Hid, typename... _Endpoints>
class HidInterface : public Interface<_Number, _AlternateSetting, 0x03, _SubClass, _Protocol, _Endpoints...>
{
  using Base = Interface<_Number, _AlternateSetting, 0x03, _SubClass, _Protocol, _Endpoints...>;
public:
  using Endpoints = Base::Endpoints;

  static uint16_t FillDescriptor(InterfaceDescriptor* descriptor)
  {
    uint16_t totalLength = sizeof(InterfaceDescriptor);

    *descriptor = InterfaceDescriptor {
      .Number = _Number,
      .AlternateSetting = _AlternateSetting,
      .EndpointsCount = Base::EndpointsCount,
      .Class = 0x03,
      .SubClass = _SubClass,
      .Protocol = _Protocol
    };
    _Hid* hidDescriptor = reinterpret_cast<_Hid*>(++descriptor);
    *hidDescriptor = _Hid {
    };
    uint8_t* reportsPart = reinterpret_cast<uint8_t*>(++hidDescriptor);
    uint16_t bytesWritten = _Hid::FillReports(reportsPart);

    totalLength += sizeof(_Hid) + bytesWritten;

    EndpointDescriptor* endpointsDescriptors = reinterpret_cast<EndpointDescriptor*>(&reportsPart[bytesWritten]);
    totalLength += (_Endpoints::FillDescriptor(endpointsDescriptors++) + ...);

    return totalLength;
  }
private:
};
      
      



, , , , HidInterface , , ().





HID-

, ( , BluePill) ( USB HID Demonstrator).





HID- Report, . :





using Report = HidReport<
  0x06, 0x00, 0xff,    // USAGE_PAGE (Generic Desktop)
  0x09, 0x01,          // USAGE (Vendor Usage 1)
  0xa1, 0x01,          // COLLECTION (Application)
  0x85, 0x01,          //   REPORT_ID (1)
  0x09, 0x01,          //   USAGE (Vendor Usage 1)
  0x15, 0x00,          //   LOGICAL_MINIMUM (0)
  0x25, 0x01,          //   LOGICAL_MAXIMUM (1)
  0x75, 0x08,          //   REPORT_SIZE (8)
  0x95, 0x01,          //   REPORT_COUNT (1)
  0xb1, 0x82,          //   FEATURE (Data,Var,Abs,Vol)
  0x85, 0x01,          //   REPORT_ID (1)
  0x09, 0x01,          //   USAGE (Vendor Usage 1)
  0x91, 0x82,          //   OUTPUT (Data,Var,Abs,Vol)
  0xc0                 // END_COLLECTION
>;
      
      



: , , :





using HidDesc = HidDescriptor<0x1001, Report>;

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

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>;
      
      



- , :





using Led = IO::Pc13Inv; // Inv - .

template<>
void LedsControlEp::Handler()
{
  LedsControlEp::ClearCtrRx();
  uint8_t* buffer = reinterpret_cast<uint8_t*>(LedsControlEp::Buffer);
  bool needSet = buffer[1] != 0;
  //       "STM32  USB-HID —  ".
  //       .
  switch(buffer[0])
  {
  case 1:
    needSet ? Led::Set() : Led::Clear();
    break;
  }
  LedsControlEp::SetRxStatus(EndpointStatus::Valid);
}
      
      



main.c Stm32f103 (-, ):





#include <clock.h>
#include <iopins.h>
#include <usb.h>

using namespace Zhele;
using namespace Zhele::Clock;
using namespace Zhele::IO;
using namespace Zhele::Usb;

using Report = HidReport<
  0x06, 0x00, 0xff,        // USAGE_PAGE (Generic Desktop)
  0x09, 0x01,          // USAGE (Vendor Usage 1)
  0xa1, 0x01,          // COLLECTION (Application)
  0x85, 0x01,          //   REPORT_ID (1)
  0x09, 0x01,          //   USAGE (Vendor Usage 1)
  0x15, 0x00,          //   LOGICAL_MINIMUM (0)
  0x25, 0x01,          //   LOGICAL_MAXIMUM (1)
  0x75, 0x08,          //   REPORT_SIZE (8)
  0x95, 0x01,          //   REPORT_COUNT (1)
  0xb1, 0x82,          //   FEATURE (Data,Var,Abs,Vol)
  0x85, 0x01,          //   REPORT_ID (1)
  0x09, 0x01,          //   USAGE (Vendor Usage 1)
  0x91, 0x82,          //   OUTPUT (Data,Var,Abs,Vol)
  0xc0               // END_COLLECTION
>;

using HidDesc = HidDescriptor<0x1001, Report>;

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

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>;

using Led = IO::Pc13Inv;

void ConfigureClock();
void ConfigureLeds();

int main()
{
  ConfigureClock();
  ConfigureLeds();

  Zhele::IO::Porta::Enable();
  MyDevice::Enable();

  for(;;)
  {
  }
}

void ConfigureClock()
{
  PllClock::SelectClockSource(PllClock::ClockSource::External);
  PllClock::SetMultiplier(9);
  Apb1Clock::SetPrescaler(Apb1Clock::Div2);
  SysClock::SelectClockSource(SysClock::Pll);
  MyDevice::SelectClockSource(Zhele::Usb::ClockSource::PllDividedOneAndHalf);
}

void ConfigureLeds()
{
  Led::Port::Enable();
  Led::SetConfiguration<Led::Configuration::Out>();
  Led::SetDriverType<Led::DriverType::PushPull>();
  Led::Set();
}

template<>
void LedsControlEp::Handler()
{
  LedsControlEp::ClearCtrRx();
  uint8_t* buffer = reinterpret_cast<uint8_t*>(LedsControlEp::Buffer);
  bool needSet = buffer[1] != 0;

  switch(buffer[0])
  {
  case 1:
    needSet ? Led::Set() : Led::Clear();
    break;
  }

  LedsControlEp::SetRxStatus(EndpointStatus::Valid);
}

extern "C" void USB_LP_IRQHandler()
{
  MyDevice::CommonHandler();
}
      
      



( " ", " " ..) , : . variadic- . , Og 2360 Flash 36 RAM ( Os 1712 , . , ), .





Vielen Dank an @RaJa für einen tollen Beitrag über HID . Außerdem gab es weniger als eine Woche vor dem Schreiben dieses Beitrags ein weiteres cooles HID- Material von @COKPOWEHEU . Ohne diese Beiträge hätte ich nichts gemeistert. Noch mehr Hilfe leisteten Benutzer aus dem Radiokot-Forum (COKPOWEHEU und VladislavS). Ich war angenehm überrascht von der Schnelligkeit der Antworten und dem Wunsch zu helfen.








All Articles