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

Ich entwickle weiterhin eine vollstĂ€ndige Vorlagenbibliothek fĂŒr Stm32-Mikrocontroller. Im letzten Artikel habe ich ĂŒber die erfolgreiche (fast) Implementierung eines HID-GerĂ€ts gesprochen. Eine weitere beliebte USB-Klasse ist der Virtual COM Port (VCP) der CDC-Klasse. Die PopularitĂ€t erklĂ€rt sich aus der Tatsache, dass der Datenaustausch auf die gleiche Weise wie das ĂŒbliche und einfache serielle UART-Protokoll erfolgt, jedoch keine separate Konverter im GerĂ€t installiert werden muss.





Schnittstellen

Ein CDC-GerĂ€t muss zwei Schnittstellen unterstĂŒtzen: eine Schnittstelle zum Verwalten von Verbindungsparametern und eine Schnittstelle zum Datenaustausch.





Die Verwaltungsschnittstelle ist eine Erweiterung der Basisklasse der Schnittstelle, mit dem Unterschied, dass sie einen Endpunkt enthĂ€lt (obwohl Sie meines Wissens, ohne alle Funktionen unterstĂŒtzen zu mĂŒssen, ĂŒberhaupt auf den Endpunkt verzichten können) und a Satz von "Funktionen", die die FĂ€higkeiten des GerĂ€ts bestimmen. Im Rahmen der entwickelten Bibliothek wird diese Schnittstelle durch folgende Klasse dargestellt:





template <uint8_t _Number, uint8_t _AlternateSetting, uint8_t _SubClass, uint8_t _Protocol, typename _Ep0, typename _Endpoint, typename... _Functionals>
class CdcCommInterface : public Interface<_Number, _AlternateSetting, DeviceAndInterfaceClass::Comm, _SubClass, _Protocol, _Ep0, _Endpoint>
{
  using Base = Interface<_Number, _AlternateSetting, DeviceAndInterfaceClass::Comm, _SubClass, _Protocol, _Ep0, _Endpoint>;
  static LineCoding _lineCoding;
  ...
      
      



Im Basisfall sollte die Schnittstelle drei Setup-Pakete unterstĂŒtzen:





  • SET_LINE_CODING: Einstellen der Zeilenparameter: Baudrate, Stoppbits, ParitĂ€t, Datenbits. Einige Projekte, auf die ich abzielte ( dieses Projekt war die Hauptinspirationsquelle ), ignorieren dieses Paket. In diesem Fall weigern sich jedoch einige Terminals (z. B. Putty ) zu arbeiten.





  • GET_LINE_CODING: , .





  • SET_CONTROL_LINE_STATE: (RTS, DTR ..).





setup-:





switch (static_cast<CdcRequest>(setup->Request))
{
case CdcRequest::SetLineCoding:
  if(setup->Length == 7)
  {
    // Wait line coding
    _Ep0::SetOutDataTransferCallback([]{
      memcpy(&_lineCoding, reinterpret_cast<const void*>(_Ep0::RxBuffer), 7);
      _Ep0::ResetOutDataTransferCallback();
      _Ep0::SendZLP();
    });
    _Ep0::SetRxStatus(EndpointStatus::Valid);
  }
  break;
case CdcRequest::GetLineCoding:
  _Ep0::SendData(&_lineCoding, sizeof(LineCoding));
  break;
case CdcRequest::SetControlLineState:
  _Ep0::SendZLP();
  break;
default:
  break;
}
      
      



, , variadic-, :





static uint16_t FillDescriptor(InterfaceDescriptor* descriptor)
{
  uint16_t totalLength = sizeof(InterfaceDescriptor);
  
  *descriptor = InterfaceDescriptor {
    .Number = _Number,
    .AlternateSetting = _AlternateSetting,
    .EndpointsCount = Base::EndpointsCount,
    .Class = DeviceAndInterfaceClass::Comm,
    .SubClass = _SubClass,
    .Protocol = _Protocol
  };

  uint8_t* functionalDescriptors = reinterpret_cast<uint8_t*>(descriptor);

  ((totalLength += _Functionals::FillDescriptor(&functionalDescriptors[totalLength])), ...);

  EndpointDescriptor* endpointDescriptors = reinterpret_cast<EndpointDescriptor*>(&functionalDescriptors[totalLength]);
  totalLength += _Endpoint::FillDescriptor(endpointDescriptors);

  return totalLength;
}
      
      



, , , , ( ). :





template <uint8_t _Number, uint8_t _AlternateSetting, uint8_t _SubClass, uint8_t _Protocol, typename _Ep0, typename _Endpoint>
class CdcDataInterface : public Interface<_Number, _AlternateSetting, DeviceAndInterfaceClass::CdcData, _SubClass, _Protocol, _Ep0, _Endpoint>
{
  using Base = Interface<_Number, _AlternateSetting, DeviceAndInterfaceClass::CdcData, _SubClass, _Protocol, _Ep0, _Endpoint>;
  ...
      
      



CDC- , , 4 : Header, CallManagement, ACM, Union, :





template<uint8_t _Number, typename _Ep0, typename _Endpoint>
using DefaultCdcCommInterface = CdcCommInterface<_Number, 0, 0x02, 0x01, _Ep0, _Endpoint, HeaderFunctional, CallManagementFunctional, AcmFunctional, UnionFunctional>;
      
      



(Interrupt Bulk ), , , , :





using CdcCommEndpointBase = InEndpointBase<1, EndpointType::Interrupt, 8, 0xff>;
using CdcDataEndpointBase = BidirectionalEndpointBase<2, EndpointType::Bulk, 32, 0>;

using EpInitializer = EndpointsInitializer<DefaultEp0, CdcCommEndpointBase, CdcDataEndpointBase>;

using Ep0 = EpInitializer::ExtendEndpoint<DefaultEp0>;
using CdcCommEndpoint = EpInitializer::ExtendEndpoint<CdcCommEndpointBase>;
using CdcDataEndpoint = EpInitializer::ExtendEndpoint<CdcDataEndpointBase>;

using CdcComm = DefaultCdcCommInterface<0, Ep0, CdcCommEndpoint>;
using CdcData = CdcDataInterface<1, 0, 0, 0, Ep0, CdcDataEndpoint>;

using Config = Configuration<0, 250, false, false, CdcComm, CdcData>;
using MyDevice = Device<0x0200, DeviceAndInterfaceClass::Comm, 0, 0, 0x0483, 0x5711, 0, Ep0, Config>;
      
      



, ( ):





template<>
void CdcDataEndpoint::HandleRx()
{
  uint8_t* data = reinterpret_cast<uint8_t*>(CdcDataEndpoint::RxBuffer);
  uint8_t size = CdcDataEndpoint::RxBufferCount::Get();

  if(size > 0)
  {
    if(data[0] == '0')
    {
      Led::Clear();
      CdcDataEndpoint::SendData("LED is turn off\r\n", 17);
    }
    if(data[0] == '1')
    {
      Led::Set();
      CdcDataEndpoint::SendData("LED is turn on\r\n", 16);
    }
  }
  CdcDataEndpoint::SetRxStatus(EndpointStatus::Valid);
}
      
      



, - USB-, , .





, . , , Seale Logic , . , , , .





WireShark UsbPcap , , . , - . : : "!(usb.addr == "1.1.1" || usb.addr == "1.2.1" || usb.addr == "1.1.3" || usb.addr == "1.5.1" || usb.addr == "1.5.2" || ..)" ( , ). :





. , PID, GET_DEVICE_DESCRIPTOR. : "usb.idProduct == 0x5711". .





contains. , , (, , ). : "usb.addr contains "1.19"".





, UsbPcap , , .





usbpcap

SSD, Windows 10 To Go (Windows, ). Microsoft , . , , ( ) .





Windows "inaccessible boot device". , , . . , , . , WireShark usbpcap. , / usbpcap. LiveCD Windows . 100%, : Windows , usbpcap, USB, BSOD. , .





Ich habe den geschriebenen Code im Programm Terminal v1.9b getestet. Der Screenshot zeigt das Ergebnis des Sendens der Nachrichten "0" und "1" an das GerÀt.





Der vollstĂ€ndige Beispielcode kann im Repository angezeigt werden . Beispiel getestet auf STM32F072B-DISCO. Wie bei HID hat die umfangreiche Bibliothek (insbesondere der Endpoint Manager) die Implementierung der CDC-UnterstĂŒtzung erheblich vereinfacht, und die Fertigstellung dauerte etwa einen ganzen Tag. Als nĂ€chstes plane ich, eine weitere Klasse fĂŒr MassenspeichergerĂ€te hinzuzufĂŒgen, und ich kann dort wahrscheinlich aufhören. Fragen und Kommentare sind willkommen.








All Articles