Stm32 + USB en plantillas C ++. Continuación. Haciendo CDC

Continúo desarrollando una biblioteca de plantillas completamente para microcontroladores Stm32, en el último artículo hablé sobre la implementación exitosa (casi) de un dispositivo HID. Otra clase USB popular es el puerto COM virtual (VCP) de la clase CDC. La popularidad se explica por el hecho de que el intercambio de datos se lleva a cabo de la misma manera que el protocolo UART serie simple y habitual, sin embargo, elimina la necesidad de instalar un convertidor separado en el dispositivo.





Interfaces

Un dispositivo CDC debe admitir dos interfaces: una interfaz para administrar los parámetros de conexión y una interfaz para el intercambio de datos.





La interfaz de gestión es una extensión de la clase base de la interfaz, con la diferencia de que contiene un punto final (aunque, según tengo entendido, sin tener que admitir todas las capacidades, puede prescindir del punto final en absoluto) y un conjunto de "funcionalidades" que determinan las capacidades del dispositivo. En el marco de la biblioteca desarrollada, esta interfaz está representada por la siguiente clase:





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



En el caso básico, la interfaz debería admitir tres paquetes de instalación:





  • SET_LINE_CODING: configuración de los parámetros de la línea: velocidad en baudios, bits de parada, paridad, bits de datos. Algunos proyectos a los que estaba apuntando ( este proyecto fue la principal fuente de inspiración ) ignoran este paquete, sin embargo, en este caso, algunos terminales (por ejemplo, Putty ) se niegan a funcionar.





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





Probé el código escrito en el programa Terminal v1.9b, la captura de pantalla muestra el resultado de enviar mensajes "0" y "1" al dispositivo.





El código de ejemplo completo se puede ver en el repositorio . Ejemplo probado en STM32F072B-DISCO. Al igual que con HID, la biblioteca voluminosa (especialmente el administrador de puntos finales) hizo que fuera mucho más fácil implementar el soporte de CDC, y tardó aproximadamente un día completo en completarse. A continuación, planeo agregar otra clase de Dispositivo de almacenamiento masivo, y probablemente pueda detenerme allí. Preguntas y comentarios son bienvenidos.








All Articles