Saya terus mengembangkan pustaka template lengkap untuk mikrokontroler Stm32, di artikel terakhir saya berbicara tentang keberhasilan (hampir) implementasi perangkat HID. Kelas USB populer lainnya adalah Virtual COM Port (VCP) dari kelas CDC. Popularitasnya dijelaskan oleh fakta bahwa pertukaran data dilakukan dengan cara yang sama seperti protokol UART serial biasa dan sederhana, namun, ini menghilangkan kebutuhan untuk menginstal konverter terpisah di perangkat.
Antarmuka
Perangkat CDC harus mendukung dua antarmuka: antarmuka untuk mengelola parameter koneksi dan antarmuka untuk pertukaran data.
Antarmuka manajemen adalah perpanjangan dari kelas dasar antarmuka, dengan perbedaan bahwa itu berisi satu titik akhir (meskipun, sejauh yang saya mengerti, tanpa harus mendukung semua kemampuan, Anda dapat melakukannya tanpa titik akhir sama sekali) dan a kumpulan "fungsionalitas" yang menentukan kemampuan perangkat. Dalam kerangka pustaka yang dikembangkan, antarmuka ini diwakili oleh kelas berikut:
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;
...
Dalam kasus dasar, antarmuka harus mendukung tiga paket pengaturan:
SET_LINE_CODING: pengaturan parameter baris: Baudrate, Stop Bits, Parity, Data bits. Beberapa proyek yang saya targetkan ( proyek ini adalah sumber inspirasi utama ) mengabaikan paket ini, namun, dalam hal ini, beberapa terminal (misalnya Putty ) menolak untuk berfungsi.
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. , .
Saya menguji kode tertulis di program Terminal v1.9b, tangkapan layar menunjukkan hasil pengiriman pesan "0" dan "1" ke perangkat.
Kode contoh lengkap dapat dilihat di repositori . Contoh diuji pada STM32F072B-DISCO. Seperti HID, pustaka yang besar (terutama pengelola titik akhir) membuatnya lebih mudah untuk mengimplementasikan dukungan CDC, dan perlu waktu sekitar satu hari penuh untuk menyelesaikannya. Selanjutnya, saya berencana untuk menambahkan kelas Perangkat Penyimpanan Massal lainnya, dan saya mungkin bisa berhenti di situ. Pertanyaan dan komentar diterima.