Драйвер виртуального com порта stm32 windows 10

Here, we are discussing on “How to Download and Install STM32 Virtual COM Port Driver” in details and providing recommended procedures to do this.

Here, we are discussing on “How to Download and Install STM32 Virtual COM Port Driver on Windows” in details and providing some recommended steps/procedures to do this. Let’s starts the discussion.

What is STM32 Virtual COM Port Driver?

STM or STMicroelectronics is multinational electronics and semiconductor manufacturer. This company is manufacturer of “STM32 Chips” that is belongs to family of 32-bit microcontroller circuits. STM32 chips are grouped into relate series that are based around the same 32-bit ARM processor core like Cortex-M7F, Cortex-M4F, Cortex-M33F, cortext-M3, Cortex-M0+ or cotex-M0.

Each microcontroller have processor core, static RAM, flash memory, debugging interface and various peripherals. When we talk about “STM32 Virtual COM Port Driver”, it is software program developed by STMicroelectronics. This software is designed to connect to the internet and adds a Windows Firewall exception in order to do so without being interfaced with.

The setup package generally consists of 8 files and memory size is usually about 3.31MB. Overall usage of users who have this installed on their computer, most of them are using Windows 7 and Windows 10 Operating System. On other hand, about 25% of users of this software is belongs to United States and it is also famous in France and Germany.

If you have already installed this Virtual COM Port Driver in your computer, then you can see its path in main drive of your computer i.e., “C:Program filesSTMicroelectronicsSoftware STM32 Virtual COM Port Driver” with the estimated size of 3.31MB. However, the memory size can be vary according to version of Virtual COM Port Driver software.

[Tips & Tricks] How to Download and Install STM32 Virtual COM Port Driver on Windows 10?

Step 1: At first, you need to visit “STMicroelectronics Official website” and download the driver

Step 2: Once downloaded, double-click on “Virtual COM Port Driver Installation Package” or “Setup package”

Step 3: Follow on-screen instructions to finish the installation process

Step 4: Once done, restart your computer to save the changes.

Update or reinstall Virtual COM Port Driver [Manually]

Step 1: Open “Device Manager” and expand “Universal Serial Bus” and Ports (COM) category

Step 2: Right-click on problematic driver which shows “Yellow Exclamation Mark” on it and select “Update for Driver Software..”

Step 3: In next window, select “Search automatically for updated driver software”

Step 4: This process will install the latest driver software in your computer. Once done, restart your computer to save the changes

Update & reinstall STM32 Virtual COM Port Driver [Automatically]

Download software for drivers update

If you don’t have enough computer knowledge to update the driver manually, then you can go for automatic solution. You can use “Automatic Drivers Update Software” to update your drivers with just few clicks. This software will automatically recognize your System and find the latest drivers for it. To get this software, click on above link.

Conclusion

I am sure this article helped you to “How to download and Install STM32 Virtual COM Port Driver on Windows 10” with easy steps. If you have already downloaded, but facing problem with Virtual COM Port Driver, then you can update or reinstall it with the steps mentioned above. If you have any suggestions or queries regarding this, please write on comment box given below.

Is Your PC Behaving Abnormal & Needs Instant Optimzation?

We recommend you to choose Advanced System Repair Suite which is dedicated to offer complete options to optimize a PC, fix any Windows error, and remove malware threats in easy. The software is award winning and suggested as the best malware fix application supporting all Windows versions including XP/Vista/7/8/8.1/10. Just 3 steps to avail error free PC.

  1. Download Advanced System Repair and install on your PC. (Follow all on screen instructions when installer is executed)
  2. Click “Scan Your PC” button to scan all present issues, errors, junk files, and malware threats.
  3. Finally, click “Start Repair” to fix all detected problems in next few minutes.

Доброго всем дня, сегодняшняя статья будет посвящена реализации виртуального COM-порта для микроконтроллеров STM32. Эта тема уже поднималась на нашем сайте, но, в отличие от всех предыдущих случаев, сегодня мы будем производить все настройки при помощи STM32CubeMx. Собственно, стартуем.

И начинаем с действий, ставших привычными — создаем новый проект в CubeMx. Я буду использовать отладочную плату STM32F4Discovery и, соответственно, выбираю контроллер STM32F407VG при создании проекта.

С этим никаких сложностей возникнуть не должно, все-таки мы уже много раз проделывали это в предыдущих статьях, посвященных STM32CubeMx (статьи про STM32CubeMx), поэтому давайте сформулируем задачу, которую будем сегодня решать.

А задача проста — реализуем поддержку USB Virtual Com Port и отправим тестовый набор данных с платы на ПК. Таким образом, мы должны добиться определения нашей отладочной платы в системе как виртуального COM-порта. А открыв этот порт в какой-нибудь терминальной программе мы должны будем увидеть правильно принятые данные. Просто и наглядно, приступаем к осуществлению.

Первым делом включим поддержку USB в проекте. Для этого посещаем вкладку «Pinout & Configuration»:

USB device

Кроме того, задействуем внешний тактовый генератор (8 МГц), установленный на плате — ровно так же как мы делали в статье про тактирование:

HSE в STM32CubeMx

Осталось выбрать режим работы USB для нашего устройства:

USB CDC class

На этом первый этап настроек проекта закончен, переходим в окно настроек тактирования (вкладка «Clock Configuration»). Здесь нам необходимо обеспечить подачу ровно 48 Мгц для тактирования модуля USB, привожу полную схему с выставленными значениями:

Тактирование USB в STM32CubeMx

Среди многочисленных настроек USB давайте поменяем только PRODUCT_STRING, исключительно теста ради, проверим, как это сработает. Остальное пока не трогаем:

Настройки дескриптора.

В принципе, на этом все, можно смело переходить к генерации проекта и исходного кода…

Немного обождав, пока CubeMx завершит генерацию, получаем готовый проект, в котором выполнена вся необходимая инициализация. Давайте не будем вносить никаких изменений, а просто соберем проект и запрограммируем контроллер. После этого подключаем USB-кабель к плате и в диспетчере устройств видим новое устройство:

Виртуальный COM-порт в Windows.

Если мы зайдем в свойства, то можем увидеть там значения VID и PID, которые были установлены в настройках STM32CubeMx, а кроме того измененную нами строку PRODUCT_STRING:

USB Virtual COM Port на плате STM32F4Discovery.

Никаких сомнений в правильной работе сгенерированного проекта не остается 👍 Но это только часть задачи, нужно реализовать отправку данных. А необходимые для этого функции находятся в файле usbd_cdc_if.c:

  • CDC_Receive_FS() — для приема данных.
  • CDC_Transmit_FS() — для передачи данных.

Итак, давайте в основном цикле нашей программы будем раз в секунду отправлять 8 тестовых байт. Кстати для реализации простейших временных задержек в HAL присутствует функция HAL_Delay(). В качестве аргумента мы должны передавать количество миллисекунд. В общем, получаем такой код:

int main(void)
{
  /* USER CODE BEGIN 1 */

  /* USER CODE END 1 */

  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_USB_DEVICE_Init();
  /* USER CODE BEGIN 2 */
  
  uint8_t testDataToSend[8];
  for (uint8_t i = 0; i < 8; i++)
  {
    testDataToSend[i] = i;
  }

  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
    HAL_Delay(1000);
    CDC_Transmit_FS(testDataToSend, 8);
  }
  /* USER CODE END 3 */
}

Теперь остается только прошить контроллер, открыть терминал и убедиться, что данные принимаются верно. Так и делаем:

Реализация приема данных

Все работает отлично, как и планировалось. Собственно, на этой мажорной ноте мы заканчиваем сегодняшнюю статью, а вместе с ней и обсуждение реализации USB Virtual COM Port при помощи STM32CubeMx. В следующих статьях мы будем работать уже с другими режимами USB, так что оставайтесь на связи 🤝

Написать эту статью меня сподвигли те сложности, которые пришлось пройти в попытке разобраться, как же именно ядро контроллера STM32F103 работает с драйвером USB, который находится на борту. Имеющиеся туториалы (например, вот или вот) по созданию устройств, в том числе и композитных, в основном сфокусированы на особенностях использования библиотек. Я же хочу рассказать, как оно работает на уровне регистров.

Некоторое время назад мне потребовалось создать драйвер для адресных светодиодов. Это довольно большой проект, в котором есть целый ряд интересных аспектов, но в рамках этой статьи достаточно упомянуть, что, поскольку этот драйвер предназначается для людей творческих профессий и увлечений — бутафоров, декораторов или косплееров, которые далеко не всегда умеют в программирование, а тем более микроконтроллеров, да и сам я больше художник, внутрь устройства был помещён целый интерпретатор команд, подобных G-кодам, который читает с флэш-карты различные сценарии, исполняет их и даже сигнализирует об ошибках. Само собой, на различную периферию оставалось не так много места. И в это место нужно было как-то втиснуть в том числе и обработку команд, поступающих через USB, поскольку это удобно для пользователя.

И вот на этом месте оказалось, что места, как в ПЗУ, так и в ОЗУ для стандартных библиотек уже не остаётся: библиотеки часто пишутся так, чтобы отлавливать ошибки. Поэтому там, где достаточно присвоить пару заранее рассчитанных значений строго определённым регистрам возникают структуры из нескольких переменных и функции со множеством условных переходов. И те и другие пожирают довольно много памяти. Поверх этого библиотеки от STM сами по себе достаточно сложны по своей структуре. Переменные попрятаны в макросы, которые в свою очередь завёрнуты в функции, вызываемые через указатели. Так что пришлось во всё это влезать и разбираться.

Процесс работы с инициализированным USB довольно прост. Но вот процесс инициализации — это головоломка не для слабых умов. Чтобы понять, что там происходит, мне пришлось нарисовать довольно объёмную блок-схему, которая больше похожа на карту подземелья в D&D, чем на алгоритм. И прежде чем спуститься в это подземелье, нам понадобится следующее снаряжение.

Регистры

Итак, что нам понадобится для того, чтобы разобраться. Вообще, полезно иметь под рукой Reference Manual (DOC Id 13902, глава 22, страницы с 580 по 609). В этом документе содержатся адреса нужных для работы регистров и описание битов в этих регистрах. А также режимов работы этих битов (это важно). Для удобства программирования, я вынес эти адреса в макросы:

Макросы

	//макрос адресации
	#define REG(x)  (*((volatile unsigned int )(x)))
	
	//базовый адрес регистров драйвера
	#define USB_BASE_ADDR   0x40005C00
	//адрес начала области памяти драйвера USB          
	#define USB_PMA_ADDR    0x40006000
	
	//регистры состояния конечных точек
	#define EP0R	REG(USB_BASE_ADDR)
	#define EP1R	REG(0x40005C04)
	#define EP2R	REG(0x40005C08)
	#define EP3R	REG(0x40005C0C)
	#define EP4R	REG(0x40005C10)
	#define EP5R	REG(0x40005C14)
	#define EP6R	REG(0x40005C18)
	#define EP7R	REG(0x40005C1C)
	#define ENDPOINT(bEpNum)        REG(USB_BASE_ADDR + (bEpNum)*4)
	#define PMA_BUF(INum)        REG(USB_PMA_ADDR + (INum)4) 
	#define PMA_SBUF(SINum)        (((volatile unsigned short int *)(USB_PMA_ADDR + (SINum)*2)))
	
	//остальные регистры
	#define CNTR	REG(USB_BASE_ADDR + 0x40)   
	#define ISTR	REG(USB_BASE_ADDR + 0x44)   
	#define DADDR	REG(USB_BASE_ADDR + 0x4C) 
	#define BTABLE	REG(USB_BASE_ADDR + 0x50)

В этой части мы немного упрощаем себе жизнь, вызывая нужные нам регистры как переменные или работая с ними как с массивом:

  • CNTR — контролирует, какие из событий должны вызвать прерывание и обратить на себя внимание контроллера. Нам понадобятся 0x8000 (данные приняты) и 0x0400 (запрос на перезагрузку драйвера). Также есть события по переполнению буфера приёма (0x4000), отправке устройства в спящий режим (0x0800) и выхода из него (0x1000) и т.д.

  • ISTR — собственно, описание прерывания. Старшие биты (с 15 по 8) — соответствуют регистру CNTR. Младшие 4 указывают номер конечной точки, которая требует внимания и бит номер 4 (0x0010) — это направление: 1 означает, что данные принимаются от хоста (обычно компьютера, но сейчас это умеют и смартфоны) в контроллер. 0 — что хост ждёт данных от нас.

  • DADDR — младшие 7 бит хранят номер, который хост присвоил нашему устройству в текущей сессии. Изначально ноль. Старший бит указывает, что драйвер USB принципиально готов к работе.

  • BTABLE — смещение адреса описания конечных точек. Дело в том, что для работы нам нужно куда-то помещать данные — как принятые, так и те, которые мы будем отправлять. Местом для этого служит внутренняя память драйвера, Packet Memory (PMA). И для разметки этой памяти служит таблица, размещаемая в той же самой памяти начиная с позиции BTABLE. Эта таблица состоит из следующих полей: Адрес буфера отправки, количество байт, отправляемых на данном шаге, адрес буфера приёма, размер буфера приёма (и его заполненность).

  • Наконец, PMA_BUF и PMA_SBUF — та самая внутренняя память драйвера, всего 512 байт. PMA_BUF обращается к ней как к массиву DWORD, а PMA_SBUF обращается к ней как к массиву WORD. Адресация в этой области памяти идёт по словам, так что обратиться к отдельному байту всё равно не получится.

Конечные точки. Как видите, я обращаюсь к ним как по отдельности, так и как к элементам массива. В принципе достаточно второго, но поскольку в ряде случаев работа происходит с конкретной конечной точкой, то для наибольшей прозрачности проще сразу её обозначить.

Сами конечные точки — это дополнительный уровень разделения потоков данных, интерфейсы, через которые хост и контроллер будут общаться. Нулевая конечная точка EP0R получает и передаёт данные, необходимые для настройки. Остальные выполняют ту работу, которую мы собственно хотим получить от нашего устройства.

Запуск USB

Прежде чем начать работу, нам необходимо включить драйвер и подключить соответствующие ноги:

SystemInit();

//Это то, что находится в  файле system_stm32f10x.c стандартной библиотеки.

#include "stm32f10x.h"
/* Reset the RCC clock configuration to the default reset state(for debug purpose) */ 
/* Set HSION bit */ 
RCC->CR |= (uint32_t)0x00000001;
/* Reset SW, HPRE, PPRE1, PPRE2, ADCPRE and MCO bits */ 
RCC->CFGR &= (uint32_t)0xF8FF0000;
/* Reset HSEON, CSSON and PLLON bits */ 
RCC->CR &= (uint32_t)0xFEF6FFFF;
/* Reset HSEBYP bit */ 
RCC->CR &= (uint32_t)0xFFFBFFFF;
/* Reset PLLSRC, PLLXTPRE, PLLMUL and USBPRE/OTGFSPRE bits */ 
RCC->CFGR &= (uint32_t)0xFF80FFFF;
/* Disable all interrupts and clear pending bits  */ 
RCC->CIR = 0x009F0000;
/* Configure the System clock frequency, HCLK, PCLK2 and PCLK1 prescalers */ 
/* Configure the Flash Latency cycles and enable prefetch buffer */

__IO uint32_t StartUpCounter = 0, HSEStatus = 0;

/* SYSCLK, HCLK, PCLK2 and PCLK1 configuration ---------------------------*/ 
/* Enable HSE */ 
RCC->CR |= ((uint32_t)RCC_CR_HSEON); 
//  RCC->CR |= ((uint32_t)RCC_CR_HSION);

/* Wait till HSE is ready and if Time out is reached exit */ 
do { 
HSEStatus = RCC->CR & RCC_CR_HSERDY; 
//    HSEStatus = RCC->CR & RCC_CR_HSIRDY; 
StartUpCounter++; 
} while(((RCC->CR & RCC_CR_HSERDY) == 0) && (StartUpCounter < HSE_STARTUP_TIMEOUT));

if ((RCC->CR & RCC_CR_HSERDY) != RESET) //	  if ((RCC->CR & RCC_CR_HSIRDY) != RESET) 
{ 
//HSEStatus = (uint32_t)0x01;    
/* Enable Prefetch Buffer */
     FLASH->ACR |= FLASH_ACR_PRFTBE;      
/* Flash 2 wait state */
     FLASH->ACR &= (uint32_t)((uint32_t)~FLASH_ACR_LATENCY);
     FLASH->ACR |= (uint32_t)FLASH_ACR_LATENCY_2;       
/* HCLK = SYSCLK */     
RCC->CFGR |= (uint32_t)RCC_CFGR_HPRE_DIV1;      
/* PCLK2 = HCLK */     
RCC->CFGR |= (uint32_t)RCC_CFGR_PPRE2_DIV1;
      /* PCLK1 = HCLK */     
	  RCC->CFGR |= (uint32_t)RCC_CFGR_PPRE1_DIV2; 

    /*  PLL configuration: PLLCLK = HSE * 9 = 72 MHz */
	RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_PLLSRC | RCC_CFGR_PLLXTPRE |                                         RCC_CFGR_PLLMULL));     
	RCC->CFGR |= (uint32_t)(RCC_CFGR_PLLSRC_HSE | RCC_CFGR_PLLMULL9);   
	//PLLMULL9 - for 8MHz 
	//    RCC->CFGR |= (uint32_t)(RCC_CFGR_PLLSRC_HSI_Div2 | RCC_CFGR_PLLMULL9);   
	//PLLMULL9 - for 8MHz     
	/* Enable PLL */
	RCC->CR |= RCC_CR_PLLON;      
	/* Wait till PLL is ready */     
	while((RCC->CR & RCC_CR_PLLRDY) == 0)     {     }      /* Select PLL as system clock source */     
	RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_SW));     
	RCC->CFGR |= (uint32_t)RCC_CFGR_SW_PLL;      
	/* Wait till PLL is used as system clock source */     
	while ((RCC->CFGR & (uint32_t)RCC_CFGR_SWS) != (uint32_t)0x08)     {     } 
	}SCB->VTOR = FLASH_BASE | VECT_TAB_OFFSET; 
	/* Vector Table Relocation in Internal FLASH. */

USB_Init()

SystemInit();

//Содержимое SystemInit описано выше. 
//Сама функция находится в  файле system_stm32f10x.c стандартной библиотеки.

// Enable USB_DISCONNECT GPIO clock 
RCC->APB2ENR |= RCC_APB2Periph_GPIOF;

// Использование 11 пина как USB pull-up pin 
GPIOF->CRH &= 0xFFFF0FFF; 
GPIOF->CRH |= 0x00007000; 

//general output, open drain, 50MHz
//Конфигурация тактирования 
*(__IO uint32_t *) (0x424200D8) = 0;
//включить тактирование шины USB 
RCC->APB1ENR |=RCC_APB1Periph_USB;
//Приоритет прерывания (низший приоритет из возможных 0xf0, высший 0x10)
NVIC->IP[USB_LP_CAN1_RX0_IRQn] = 0xA0; 
//pending - будем проверять в цикле 
NVIC->ISER[(uint32_t)(USB_LP_CAN1_RX0_IRQn) >> 0x05] = (uint32_t)0x01 << (USB_LP_CAN1_RX0_IRQn & (uint8_t)0x1F);

USB_RESET();

После инициализации и включения тактирования остаётся только выполнить разметку PMA и разрешить USB принимать данные. За это отвечает функция USB_RESET():

USB_RESET()

#define EP0RX_OFFSET 	0x80
#define EP0TX_OFFSET 	0x40
#define EP1TX_OFFSET 	0xC0
#define EP2TX_OFFSET 	0x100
#define EP3RX_OFFSET 	0x110

BTABLE = 0;	// Адрес таблицы дискрипторов внутри PMA

//EP 0 - нулевая конечная точка 
	// ADDR_TX 
	PMA_BUF(0) = EP0TX_OFFSET; 
	// COUNT_TX = 0 нужно заполнять перед отправкой данных 
	PMA_BUF(1) = 0; 
	// ADDR_RX 
	PMA_BUF(2) = EP0RX_OFFSET; 
	// Указываем размер буфера приема 64 байта, BL_size = 1 (32 байта на блок), 2 штуки 
	PMA_BUF(3) = 0x8400;
	EP0R = ((EP0R ^ 0x3020) & 0x3030) | 0x0200; 

//EP 1 CDC TX 
	PMA_BUF(4) = EP1TX_OFFSET; 
	PMA_BUF(5) = 0; 
	PMA_BUF(6) = 0; 
	PMA_BUF(7) = 0; 
	ENDPOINT(1) = ((ENDPOINT(1) ^ 0x0020) & 0x3030);

//EP 2 CDC interrupt 
	PMA_BUF(8) = EP2TX_OFFSET; 
	PMA_BUF(9) = 0; 
	PMA_BUF(10) = 0; 
	PMA_BUF(11) = 0; 
	ENDPOINT(2) = ((ENDPOINT(2) ^ 0x0020) & 0x3030)|0x0600;

//EP 3 CDC RX 
	PMA_BUF(12) = 0; 
	PMA_BUF(13) = 0; 
	PMA_BUF(14) = EP3RX_OFFSET; 
	PMA_BUF(15) = 0x8400; 
	ENDPOINT(3) = ((ENDPOINT(3) ^ 0x3000) & 0x3030);

//Данные глобальные переменные нужны для формирования ответов на запросы хоста:
USB_Feature = 0xC0;
USB_Configuration = 0;
USB_Interface = 0;

//Обнуляем прерывание
ISTR = 0;
// Включаем модуль USB, адрес устройства 0
DADDR = 0x80;
// Дополнительно сбрасываем флаги прерываний(событий) USB
ISTR &= ~ISTR_RESET;

Здесь нам потребуются пояснения, связанные с особенностями работы регистров конечных точек. Разные биты в них отвечают не только за разные режимы работы, но и по-разному реагируют на попытки записать в них 0 или 1. Так что присаживайтесь поудобнее.

  • Старший бит, 0x8000, CTR_RX отвечает за приём и поднимается тогда, когда в область PMA для конечной точки пришли данные от хоста. Поднять его со стороны контроллера нельзя, только сбросить в 0.

  • Бит 0x4000, DTOG_RX обозначает чётность принятого пакета данных. При записи в него 1 меняет своё значение. В случае работы с COM-портом он нам не понадобится.

  • Биты 0x2000 и 0x1000 — STAT_RX — состояние приёмника конечной точки. Может принимать значения 0x3000 — готова к приёму, 0x2000 — данные обрабатываются и точка занята, 0x1000 — ошибка и 0x0000 — выключено. Эти биты переключаются так же, как и DTOG_RX. Именно поэтому вместо EP0R = 0x3020 приходится городить EP0R = ((EP0R ^ 0x3020) & 0x3030)

  • Бит 0x0800, SETUP — указывает на тип принятых данных, т.е. являются ли эти данные специфической командой.

  • Бит 0x0400 и 0x0200 — EP_TYPE — тип конечной точки: 0x0000 — данные (BULK), 0x0100 — контрольная (CONTROL), 0x0400 — асинхронная (ISO) и 0x0600 — прерывание для хоста (INTERRUPT).

  • Бит 0x0100, EP_KIND — дополнительный параметр. Для конечных точек типа BULK этот бит включает двойную буферизацию, режим, при котором области PMA для приёма и передачи данных меняются ролями после каждого принятого пакета. Это нужно для ускорения приёма-передачи. Для конечной точки типа CONTROL данный бит включает режим STATUS_OUT. Бывает нужен при завершении ответа, но в нашем случае работает и без него.

  • Биты с 0x0080 по 0x0010 (CTR_TX, DTOG_TX, STAT_TX) работают так же, как и старшие 4, только отвечают не за приём, а за передачу данных хосту

  • младшие 4 бита хранят номер конечной точки. Нам потребуется их заполнить когда устройство получит свой номер от хоста.

Как видно, в проекте у нас 4 конечных точки: контрольная нулевая, одна на передачу (первая), одна для прерываний (вторая) и одна на приём (третья). Таблицу разметки мы расположили в начале PMA. В нулевой ячейке таблицы записываем позицию начальной ячейки отправки для контрольной точки. Затем — сколько байт нужно отправить. Затем — начальная ячейка приёма данных. Последняя ячейка содержит информацию о количестве места и количестве принятых данных: старший бит (0x8000) указывает, что блоки имеют размер 32 байта (если 0 — то 2 байта на блок), затем биты с 0x4000 по 0x0400 — количество этих блоков (минус 1). Максимальный общий размер — 512 байт. Оставшиеся 10 бит будут хранить число принятых конечной точкой байт.

И далее по кругу для остальных конечных точек. При разметке важно, чтобы области PMA для разных нужд не пересекались. Иначе мы не сможем понять, что читаем.

Дескрипторы

Следующий важный момент, без которого мы не сможем настроить наше устройство — дескрипторы. Это заранее заготовленные и отформатированные ответы на запросы хоста «что ты такое» и «как ты работаешь».

Дескриптор устройства

const char DeviceDiscriptor[] = { 
0x12,   		// bLength / 
0x01,     		// bDescriptorType / 
0x00, 0x02,		// bcdUSB = 2.00 / 

// класс и протокол перечислены на сайте https://www.usb.org/defined-class-codes
0x02, 	// bDeviceClass: CDC / 
0x00,   // bDeviceSubClass / 
0x00,   // bDeviceProtocol / 
0x40,   // bMaxPacketSize0 / 

//STMicroelectronics  СDC
// данные с сайта https://www.the-sz.com/products/usbid/index.php?v=0x04830x83, 
0x04,   		// idVendor = 0x0483 / 
0x40, 0x57,		// idProduct = 0x7540 / 
0x00, 0x02,		// bcdDevice = 2.00 / 1,              

// Index of string descriptor describing manufacturer / 
2,              // Index of string descriptor describing product / 
3,              // Index of string descriptor describing the device's serial number / 
0x01    		// bNumConfigurations / 
};

Дескриптор конфигурации

 const char ConfigDescriptor[] = { 
//Configuration Descriptor 
0x09,   // bLength: Configuration Descriptor size 
0x02,	// bDescriptorType: Configuration 
67,		// wTotalLength:no of returned bytes 
0x00, 0x02,   // bNumInterfaces: 2 interface 
0x01,   // bConfigurationValue: Configuration value 
0x00,   // iConfiguration: Index of string descriptor describing the configuration 
0xC0,   // bmAttributes: self powered 
0x32,   // MaxPower 0 mA 
//Interface Descriptor 
0x09,   // bLength: Interface Descriptor size 
0x04,	// bDescriptorType: Interface 
// Interface descriptor type 
0x00,   // bInterfaceNumber: Number of Interface 
0x00,   // bAlternateSetting: Alternate setting 
0x01,   // bNumEndpoints: One endpoints used 
0x02,   // bInterfaceClass: Communication Interface Class 
0x02,   // bInterfaceSubClass: Abstract Control Model 
0x01,   // bInterfaceProtocol: Common AT commands 
0x00,   // iInterface: 
//Header Functional Descriptor 
0x05,   // bLength: Endpoint Descriptor size 
0x24,   // bDescriptorType: CS_INTERFACE 
0x00,   // bDescriptorSubtype: Header Func Desc 
0x10,   // bcdCDC: spec release number 
0x01,	//Call Management Functional Descriptor 
0x05,   // bFunctionLength 
0x24,   // bDescriptorType: CS_INTERFACE 
0x01,   // bDescriptorSubtype: Call Management Func Desc 
0x00,   // bmCapabilities: D0+D1 
0x01,   // bDataInterface: 1 
//ACM Functional Descriptor 
0x04,   // bFunctionLength 
0x24,   // bDescriptorType: CS_INTERFACE 
0x02,   // bDescriptorSubtype: Abstract Control Management desc 
0x02,   // bmCapabilities 
//Union Functional Descriptor 
0x05,   // bFunctionLength 
0x24,   // bDescriptorType: CS_INTERFACE 
0x06,   // bDescriptorSubtype: Union func desc 
0x00,   // bMasterInterface: Communication class interface 
0x01,   // bSlaveInterface0: Data Class Interface 
//Endpoint 2 Descriptor 
0x07,   // bLength: Endpoint Descriptor size 
0x05,   // bDescriptorType: Endpoint 
0x82,   // bEndpointAddress: (IN2) 
0x03,   // bmAttributes: Interrupt 
8,   	// wMaxPacketSize: 
0x00, 0xFF,   // bInterval: 
//Data class interface descriptor 
0x09,   // bLength: Endpoint Descriptor size 
0x04,  // bDescriptorType: 
0x01,   // bInterfaceNumber: Number of Interface 
0x00,   // bAlternateSetting: Alternate setting 
0x02,   // bNumEndpoints: Two endpoints used 
0x0A,   // bInterfaceClass: CDC 
0x00,   // bInterfaceSubClass: 
0x00,   // bInterfaceProtocol: 
0x00,   // iInterface: 
//Endpoint 3 Descriptor 
0x07,   // bLength: Endpoint Descriptor size 
0x05,   // bDescriptorType: Endpoint 
0x03,   // bEndpointAddress: (OUT3) 
0x02,   // bmAttributes: Bulk 
64,		// wMaxPacketSize: 0x00, 
0x00,   // bInterval: ignore for Bulk transfer 
//Endpoint 1 Descriptor 
0x07,   // bLength: Endpoint Descriptor size 
0x05,   // bDescriptorType: Endpoint 
0x81,   // bEndpointAddress: (IN1) 
0x02,   // bmAttributes: Bulk 
64,		// wMaxPacketSize: 
0x00, 0x00	// bInterval  
};

Дескрипторы обычно довольно подробно описываются во многих туториалах, да и в библиотеках и примерах их найти несложно. Так что эту часть объяснения оставлю коллегам. В частности, USB in a nutshell или USB made simple. Всё-таки моя статья скорее о том, как это добро правильнее передать чтобы оно было воспринято.

А не как тут

При подключении устройство никак не определялось до конца. Вроде COM, но какой-то скомканный. Оказалось, компилятор не смог один из дескрипторов расположить в памяти единым куском. А читаю то я его «от забора и до обеда»! Так что компьютер, вместо того чтобы получить на свой запрос «как ты работаешь», вместо заранее заготовленного ответа «я делаю вот это и вот это» получал ответ «через Ж…» и прекращал дальнейшее общение с устройством. Пришлось этот дескриптор разбить на несколько частей по 16 байт и слать их одну за другой. Заработало.

Потом оказалось, что этот дескриптор я переобъявлял в другом файле, и от костыля удалось избавиться. Теперь каждый раз перепроверяю свои дефайны.

Осталось определить несколько глобальных переменных и функций чтения-записи PMA

Собственно, переменные

typedef enum _CONTROL_STATE{
	WAIT_SETUP, SETTING_UP, IN_DATA, OUT_DATA, LAST_IN_DATA, LAST_OUT_DATA, WAIT_STATUS_IN, WAIT_STATUS_OUT, STALLED, PAUSE
} CONTROL_STATE; 

//массивы и переменные, через которые мы будем работать 
unsigned char USB_Buff1[64]; 
unsigned char USB_COM_TX_Buff1[64]; 
uint16_t USB_RX_Start_pointer=0; 
uint16_t USB_Recieved_bytes=0; 
uint16_t USB_Bytes_to_send_left = 0;

//Эта переменная нужна нам для общения с внешним миром 
uint8_t USB_Function_flags=0; 
//1 - запрос оправки данных на хост(IN) 
//2 - запрос получения данных от хоста (OUT) 
//4 - есть, что обработать 
//8 - Данные от хоста в EP3 обработаны

//переменные для хранения команд, полученных контрольной точкой  
uint16_t USB_Command; 
uint16_t USB_wValue; 
uint16_t USB_wLength;
//Стадия процесса обработки команды от хоста 
CONTROL_STATE USB_state_flag = WAIT_SETUP;
//параметры контрольной конечной точки и конфигурация устройства 
uint8_t USB_DADDR = 0; 
uint8_t USB_Feature; 
uint8_t USB_Configuration; 
uint8_t USB_Interface; 
uint8_t EPindex; 
uint16_t BKIstr=0;
uint16_t SaveTState;

Чтение и запись массивов данных в PMA

//P1 - указатель на первый элемент массива, откуда читаем
//P2 - позиция в массиве PMA, с которой записываем данные
//N - количество байт
//max - размер области PMA размеченный для данной операции
void TO_WRITE_PMA(uint16_t *P1, uint32_t P2, uint16_t N, uint16_t max){
	uint8_t i;
	N = (N > max)? max : N; 
	for(i=0; i<N; i+=2){ 
		PMA_SBUF(P2+i) = *P1;
		P1++; 
	}
}

void TO_READ_PMA(uint32_t P2, uint16_t *P1, uint16_t N, uint16_t max){
	uint8_t i;
	N = (N > max)? max : N; 
	for(i=0; i<N; i+=2){ 
		*P1 = PMA_SBUF(P2+i); 
		P1++; 
	}
}

//генерация текстовых дескрипторов.
//строка Str[] должна оканчиваться пустым символом
//EP - целевая конечная точка
void USB_Message (char Str[], uint8_t EP){
	uint16_t b1 = PMA_BUF(EP * 4);
	uint8_t i;
	//не более 31 символа, т.к. всего у нас 64 байта,
	//из которых 2 - это заголовок 
	//(младший байт - количество, старший - тип дескриптора),
	//а остальные - текст в юникоде 
	for (i = 0; (Str[i] && i < 31); i++) { 
		b1 += 2; 
		PMA_SBUF(b1) = Str[i]; 
	}
	i =i * 2 + 2 b1 = PMA_BUF(EP * 4); 
	PMA_SBUF(b1) = i | 0x0300; 
	//N bytes, 3 - string descriptor 
	PMA_BUF(EP * 4+1) = i;
}

Наконец, входим в подземелье

Во-первых, прерывание. Тут всё просто: определяем причину и выбираем обработчик. Причин у нас две — данные приняты и перезагрузить драйвер (см регистр CNTR выше)

Прерывание

void USB_LP_CAN1_RX0_IRQHandler(){
	BKIstr = ISTR;
	if(BKIstr & ISTR_CTR){
		CTR_LP_CTRX();
	}  
	if(BKIstr & ISTR_RESET){
		USB_RESET();
	}
}

Мы уже знакомы с USB_RESET(), так что не будем туда заглядывать. Настоящее приключение ждёт нас в CTR_LP_CTRX().

Код CTR_LP_CTRX()

#define ISTR_CTR		0x8000
#define ISTR_EP_ID	0x000F
#define EP_CTR_TX		0x0080
#define EP_CTR_RX		0x8000
#define USB_EP0_MAX_PACKET_SIZE	0x0040

void CTR_LP_CTRX() {
uint16_t TempEP;
uint8_t Related_Endpoint, Reserved;
uint16_t USB_wIndex;

while (((BKIstr = ISTR) & ISTR_CTR)) {
    // extract highest priority endpoint number 
    EPindex = (uint8_t)(BKIstr & ISTR_EP_ID);

  if (EPindex == 1 && (EP1R & EP_CTR_TX)){ //CDC Transmit
      EP1R &= 0x8F0F;
      USB_Function_flags |= 1;
	} else if (EPindex == 3 && (EP3R & EP_CTR_RX)){ //CDC Receive
			USB_Function_flags &= 0xEF;
			USB_RX_Start_pointer = 0;

			//you can save up current time
    	//in case you have a limited timeframe for processing this data
    	EP3R = (EP3R ^ 0x2000) & 0x3F8F;
    	USB_Recieved_bytes = (PMA_BUF(15)&0x3FF);
    	TO_READ_PMA(EP3RX_OFFSET, USB_Buff1, 64,64);
			USB_Function_flags |= 2;

        // Decode and service non control endpoints interrupt 
        // process related endpoint register 

    } else if(EPindex == 0){

        // Decode and service control endpoint interrupt 
        // calling related service routine 
        // (Setup0_Process, In0_Process, Out0_Process) 

        // save RX & TX status 
        // and set both to NAK 

  	    SaveTState = EP0R & 0x3030;

  	    EP0R = (EP0R^0x2020)&0xBFBF;
        // DIR bit = origin of the interrupt 
        if (BKIstr & ISTR_DIR) {
            // DIR = 1 */
            // DIR = 1 & CTR_RX       => SETUP or OUT int 
            // DIR = 1 & (CTR_TX | CTR_RX) => 2 int pending 
            	EP0R &=0x0F8F;// SETUP bit kept frozen while CTR_RX = 1 
            	USB_state_flag = STALLED;
            if (EP0R & EP_SETUP) {

              USB_Command = PMA_SBUF(EP0RX_OFFSET);
              USB_wValue  = PMA_SBUF(EP0RX_OFFSET+2);
              USB_wIndex  = PMA_SBUF(EP0RX_OFFSET+4);
              USB_wLength = PMA_SBUF(EP0RX_OFFSET+6);
              if (USB_wLength == 0)
              {
                // Setup with no data stage 

            	  USB_state_flag = WAIT_STATUS_IN;// After no data stage SETUP
            	  PMA_BUF(1) = 0;
            	  if (USB_Command == 0x0900){    // SET_CONFIGURATION
            			  USB_Configuration = USB_wValue;
            	  } else if (USB_Command == 0x0500){//SET ADDRESS
            		  USB_DADDR = USB_wValue | DADDR_EF;
            	  } else if ((USB_Command &0xFDFF) == 0x0100){//SET FEATURE for Device 0300, CLEAR FEATURE for Device 0100

            	  } else if (USB_Command == 0x0B01 && (USB_Configuration) && ((USB_wIndex & 0xFF) <= 1)){//SET INTERFACE
            			  USB_Interface = USB_wIndex;
            	  } else if (USB_Command == 0x0102){
            		  //EndPoint Clear Feature
            		  Related_Endpoint = USB_wIndex & 0x7F;
            	      if ((USB_wValue == 0)//ENDPOINT_STALL)
            	          && (USB_wIndex > 0x100) && USB_Configuration)
            	      {
            		        //Get Status of endpoint & stall the request if the related_ENdpoint
            		        is Disabled
            	    	  TempEP = ENDPOINT(Related_Endpoint);
            	    	  if (USB_wIndex & 0x80) {
            	    		  // IN endpoint 
            			      if ((TempEP&0x0030)==0x0010)
            			      {
            			          ENDPOINT(Related_Endpoint) = (TempEP ^ 0x0030)&0x8FFF;
            			      }
            		      } else {
            		    	  // OUT endpoint
            		    	  if ((TempEP&0x3000)==0x1000)
            		    	  {
            		    		  if (Related_Endpoint == 0)
            			          {
            			            // After clear the STALL, enable the default endpoint receiver 
            			        	  PMA_BUF(3) = 0x8400;
            			        	  EP0R = (EP0R ^ 0x3000)&0xBF8F;
            			          }
            			          else
            			          {
            			            ENDPOINT(Related_Endpoint) = (TempEP ^ 0x3000)&0xFF8F;
            			          }
            		    	  }
            		      }
            	      }
            	  } else if (USB_Command == 0x0302){
									// get Status of endpoint & stall the request if the related_ENdpoint is Disabled
            	      Related_Endpoint = USB_wIndex & 0x7F;
            	      if ((USB_wValue==0) && USB_Configuration){
            	    	  TempEP = ENDPOINT(Related_Endpoint);
            	    	  if (USB_wIndex & 0x80) {
            				    
            				    if(TempEP&0x0030){
            				    	ENDPOINT(Related_Endpoint) = (TempEP ^ 0x0010)&0x8FBF;
            				    }
            			  } else {
            				    if(TempEP&0x3000){
            				    	ENDPOINT(Related_Endpoint) = (TempEP ^ 0x1000)&0xBF8F;
            				    }
            			  }
            	      }

            	  } else if ((USB_Command & 0xDDFF) == 0x0021){
                  //SET_COMM_FEATURE 0221
                  //SET_CONTROL_LINE_STATE 2221
                  //SET Linecoding 2021
         
                  USB_Message("Device connectedr",1);
            		  USB_main_COM_react();

            	  } else {
            		  USB_state_flag = STALLED;
            	  }

            	  SaveTState |= 0x0030;
              } else {
                // Setup with data stage 
            	  USB_state_flag =  LAST_IN_DATA;
            	  PMA_BUF(1) = USB_wLength;
            		if (USB_Command == 0x0680){//GET DESCRIPTOR
            		      if (USB_wValue == 0x0100){
            		    	  TO_WRITE_PMA(DeviceDiscriptor,EP0TX_OFFSET,0x12,USB_EP0_MAX_PACKET_SIZE);
            		    	  PMA_BUF(1)= 0x12;
            		      } else if (USB_wValue == 0x0200){
            		    	  TO_WRITE_PMA(ConfigDescriptor,EP0TX_OFFSET,64,USB_EP0_MAX_PACKET_SIZE);

            		    	  if (USB_wLength>9){
                          
            		    		  PMA_BUF(1)= USB_EP0_MAX_PACKET_SIZE;
            		    		  USB_state_flag = IN_DATA;
            		    	  }
							  
            		      } else if (USB_wValue == 0x0300){
            		    	  PMA_SBUF(EP0TX_OFFSET) = 0x0304; // 3 - string descriptor, N=4
            		    	  PMA_SBUF(EP0TX_OFFSET + 2) = 0x0409;//Lang ID - US
            		    	  PMA_BUF(1)= 4;
            		      } else if (USB_wValue == 0x0301){
            		    	  USB_Message("VENDOR",0);
            		      } else if (USB_wValue == 0x0302){
            		    	  USB_Message("Device name",0);
            		      } else if (USB_wValue == 0x0303){
            		    	  uint32_t buf1 = *(__IO uint32_t*)(0x1FFFF7F0);//get unique chip ID
            		    	  for (Reserved = 2; Reserved<24; Reserved+=2){
            		    		  PMA_SBUF(EP0TX_OFFSET + Reserved)= (buf1 & 0x7) + '0';
            		    		  buf1 >>= 3;
            		    	  }
            		    	  PMA_SBUF(EP0TX_OFFSET)= 0x0318;
            		    	  PMA_BUF(1)=24;
            		      }// End of GET_DESCRIPTOR
            		} else if ((USB_Command&0xFFFE) == 0x0080){// GET STATUS for Device, GET STATUS for Interface
            			Reserved = 0;
            			if (USB_Command == 0x0080){
            			    if (USB_Feature & 0x20){// is Remote Wakeup enabled?
            			    	Reserved |= 2;
            			    }
            			    if (USB_Feature & 0x40){// is Bus-powered?
            			    	Reserved |= 1;
            			    }
            			}
            			PMA_SBUF(EP0TX_OFFSET) = Reserved; 
            		} else if (USB_Command == 0x0082){// GET STATUS for EndPoint
            			Related_Endpoint = USB_wIndex & 0x7F;
            			PMA_SBUF(EP0TX_OFFSET) =0;
            			TempEP = ENDPOINT(Related_Endpoint);
           				if (USB_wIndex & 0x80) {
            					// get Status of endpoint & stall the request if the related_ENdpoint is Disabled
            					if(TempEP&0x0030 == 0x0010){
            						PMA_SBUF(EP0TX_OFFSET) =1;// IN Endpoint stalled 
            					}
            				} else {
            					if(TempEP&0x3000 == 0x1000){
            						PMA_SBUF(EP0TX_OFFSET) =1;//OUT Endpoint stalled 
            					}
            				}

            		} else if (USB_Command == 0x0880){//GET CONFIGURATION
            			PMA_SBUF(EP0TX_OFFSET) = USB_Configuration;

            		} else if ((USB_Command & 0xFE7F) == 0x2021){
                  //Get (21A1)/set (20A1) Line Coding
            			PMA_BUF(EP0TX_OFFSET) = 9600;//baudrate
            			PMA_SBUF(EP0TX_OFFSET+4) = 0;//parity*256+format
            			PMA_SBUF(EP0TX_OFFSET+6) = 8;//datatype
            		} else {
            			USB_state_flag = STALLED;
            		}
            	    SaveTState = (0x3030);
               }
            }
        } else {
            // DIR = 0 
            // DIR = 0      => IN  int 
            // DIR = 0 implies that (EP_CTR_TX = 1) always  
        	EP0R &=0x8F0F;

        	if (USB_state_flag == IN_DATA){
            //last 3 bytes of Config Descriptor
            //no need to use procedure as there is only 2 assignments
		    		PMA_SBUF(EP0TX_OFFSET) = 0x0040;
		    		PMA_SBUF(EP0TX_OFFSET + 2) = 0;
		    		USB_state_flag =  LAST_IN_DATA;
		    		PMA_BUF(1)= 3;
		    		SaveTState = (0x3030);
        	} else if (USB_state_flag == LAST_IN_DATA){
        		USB_state_flag = WAIT_STATUS_OUT;
      			SaveTState = (0x3010);
        	} else {
        		if (USB_DADDR){//SetDeviceAddress
        			for (Reserved = 0; Reserved < EP_NUM; Reserved++) {
        				ENDPOINT(Reserved) = (ENDPOINT(Reserved) & 0x8F80) | Reserved ;
        			}
        			DADDR = USB_DADDR; // set device address and enable device function
        			USB_DADDR = 0;
        		}
        		USB_state_flag = STALLED;
        	}

        }
        PMA_BUF(3) = 0x8400;
       
        if (USB_state_flag == STALLED) SaveTState = 0x1010;

        EP0R = (EP0R^(SaveTState))&0xBFBF;
        return;
    }

  }
}

А теперь я расскажу, что тут происходит. И это именно то, ради чего вся эта статья писалась и ради чего вы её сейчас читаете.

Вначале мы попадаем в цикл, задача которого — обработать все отдельные элементы прерывания по ISTR_CTR (он же 0x8000 — приём данных).

Первое, что мы тут делаем, это определяем, какую именно конечную точку обрабатываем и обрабатываем приём или передачу. Номер конечной точки узнаём из прерывания. Это младшие 4 бита регистра ISTR. Приём или передача регулируются 5 битом (0x0010).

Если у нас актуальная конечная точка номер 1, и бит EP_CTR_TX (0x0080) поднят, хост хочет что-то от нас получить. Однако нам это не важно, так что мы просто опускаем бит CTR_TX (EP1R &= 0x8F0F), и, возможно, что-то делаем. Тут важно отметить, что если мы действительно хотим сформировать и отправить ответ, то нам нужно сначала перевести конечную точку в режим NAK, то есть осуществить операцию EP1R = (EP1R ^ 0x0020) & 0x8F3F, затем записать данные в PMA по адресу EP1TX_OFFSET (хранится в PMA_BUF(4)), затем записать в PMA_BUF(5) количество этих байт, и, наконец, перевести конечную точку в режим «готово» командой EP1R = (EP1R ^ 0x0030) & 0x8FBF

Однако мы вызываем этот процесс из других частей программы, то есть когда именно у нас есть, что отправить. Для этого служит следующая процедура (данные для отправки записаны ранее в рабочий массив USB_COM_TX_Buff1, а их количество хранится в переменной USB_Bytes_to_send_left)

Чтобы отправить данные хосту

Поместить данные (пакет не больше чем USB_EP1_MAX_PACKET_SIZE_TX) в рабочий буфер USB_COM_TX_Buff1,

записать в переменную USB_Bytes_to_send_left количество отправляемых байт в текущем пакете

Вызвать процедуру

#define USB_EP1_MAX_PACKET_SIZE_TX	0x40

//фактическая отправка данных хосту
//данные и их количество записаны в USB_COM_TX_Buff1
//и в USB_Bytes_to_send_left соответственно.
void USB_main_COM_react(){

	if (USB_Bytes_to_send_left){
		//send pack back to the Host
		USB_Function_flags &= 0xFE;
		TO_WRITE_PMA(USB_COM_TX_Buff1 ,EP1TX_OFFSET, 64, USB_EP1_MAX_PACKET_SIZE_TX);
		PMA_BUF(5) = USB_Bytes_to_send_left;
		EP1R = (EP1R^0x0030)&0x8FBF;
		USB_Bytes_to_send_left = 0;

		uint32_t i = 5000;
		while (((EP1R & 0x80) == 0) && (i)) {i--;}
  }
}

Теперь перейдём к обработчику конечной точки номер 3. Она служит для приёма данных. Итак, актуальная конечная точка номер 3 и её бит EP_CTR_RX (0x8000) поднят. Мы опускаем этот бит и одновременно переводим конечную точку в режим ожидания: EP3R = (EP3R ^ 0x2000) & 0x3F8F. Также можно помахать флагами для внешних частей общей программы, но главное, что нас сейчас интересует — это количество принятых байт. Эта информация находится в младших 10 битах ячейки (PMA_BUF(15)&0x3FF). Далее мы перекладываем данные из PMA в рабочий массив и их как-то обрабатываем.

Но это не всё. Когда мы закончим обработку этих данных, нам необходимо перевести конечную точку в состояние «готова к приёму». Это нужно сделать обязательно, иначе, по прошествии некоторого времени хост решит, что наше устройство зависло. Делается это очень просто: EP3R = (EP3R^0x3000)&0xBF8F. Также можно сбросить количество принятых байт в ноль, как это делалось в процедуре USB_RESET: PMA_BUF(15) = 0x8400.

Обработка полученных данных

Данные лежат в массиве USB_Buff1

их количество записано в переменной USB_Recieved_bytes

после обработки выполнить этот код:

PMA_BUF(15) = 0x8400;
EP3R = (EP3R^0x3000)&0xBF8F;

Наконец, самое сложное

Контрольная конечная точка, номер 0.

Помимо простых запросов IN и OUT (EP_CTR_RX и EP_CTR_TX соответственно), есть ещё запрос SETUP (бит 0x0800 в регистре конечной точки EP0R). Принципиально у нас два возможных сценария развития событий — когда команда предполагает ответ и когда не предполагает. Разберём их на примере команды запроса дескриптора конфигурации (как наиболее сложный случай), и на примере назначения адреса устройства.

Танец с бубном номер 1. Отправка дескриптора.

Шаг 1. Из прерывания мы вошли в процедуру и в цикл внутри этой процедуры. Выяснили, что у нас 0 конечная точка. На всякий случай сохраняем состояние приёма и передачи (SaveTState = EP0R & 0x3030), а затем переводим нулевую конечную точку в состояние NAK (обработка данных) и по каналу приёма, и по каналу передачи. Так у нас будет время сформировать ответ хосту.

На этом этапе направление передачи (ISTR_DIR) должно быть OUT (бит поднят), Бит EP_SETUP поднят. Бит EP_CTR_RX тоже поднят(его опускаем).

Копируем данные из области PMA, размеченной для приёма в рабочие переменные. Это будет команда USB_Command = PMA_SBUF(EP0RX_OFFSET);
параметр USB_wValue = PMA_SBUF(EP0RX_OFFSET+2);
индекс USB_wIndex = PMA_SBUF(EP0RX_OFFSET+4);
и длина требуемого ответа USB_wLength = PMA_SBUF(EP0RX_OFFSET+6);

Структура команды следующая:

  • верхние 8 бит (0x8000 … 0x0100) — собственно требуемая операция,

  • 7 бит (0x0080) — направление, если поднят, то хост ждёт от нас ответа

  • 5 бит (0x0020) — указывает на то, что запрос специфичен для данного класса устройств

  • младшие 4 бита — указывают на то, к какому слою USB запрос относится — к устройству, интерфейсу или конечной точке

Теперь мы определяем, нужно ли отправить ответ. Для этого смотрим на USB_wLength. В нашем примере (отправить дескриптор конфигурации) это не ноль.

Смотрим на команду. Её значение 0x0680 — отправить дескриптор. Смотрим на параметр. Он равен 0x0200 — дескриптор конфигурации.

Записываем начало нашего дескриптора в область PMA, которая размечена для отправки данных хосту от конечной точки 0.

Тут у нас может быть два варианта. Поскольку изначально хост не знает размера этого дескриптора, он запрашивает только первые 9 байт (в которых содержится в том числе и информация об общей длине). Это 6 и 7 байты запроса, мы их храним в переменной USB_wLength. В этом случае мы записываем в ячейку PMA (PMA_BUF(1)), в которой хранится количество байт для отправки, число 9. После чего переходим к шагу 3 (LAST_IN_DATA).

Если же у нас запрос полного дескриптора, то может быть 2 варианта: он меньше чем размер буфера отправки, или больше либо равен ему. Если меньше, то записываем то количество байт, которое есть и также переходим к шагу 3 (LAST_IN_DATA). Если же больше, то записываем в PMA_BUF(1) максимальное количество байт, которые можем отправить одним пакетом, и переходим к шагу 2 (IN_DATA).

Шаг 2. IN_DATA. Открываем нулевую конечную точку для приёма и передачи EP0R = (EP0R^0x3030)&0xBFBF и переходим на следующий виток цикла.

Теперь мы попадаем в цикл со следующими параметрами:
(ISTR_DIR) должно быть IN (бит опущен),
EP_SETUP опущен,
Бит EP_CTR_TX поднят(его опускаем)
Если что-то не так, то переводим нулевую конечную точку в состояние STALL (EP0R = (EP0R^0x1010)&0xBFBF) и выходим из цикла.

Если всё так как надо, переводим нулевую конечную точку в состояние NAK на приём и передачу, доотправляем оставшиеся байты дескриптора. Тут может оказаться, что их ноль, но это нормально. Записываем это число в PMA_BUF(1).

Опять же, в этом шаге мы можем крутиться до тех пор, пока оставшееся количество байт для дескриптора будет строго меньше чем максимальный размер пакета для отправки. Тогда мы можем перейти не снова в начало шага 2(IN_DATA), а к шагу 3 (LAST_IN_DATA).

Шаг 3. LAST_IN_DATA. Открываем нулевую конечную точку для приёма и передачи EP0R = (EP0R^0x3030)&0xBFBF и переходим на следующий виток цикла.

Мы снова попадаем в цикл со следующими параметрами:
ISTR_DIR должно быть IN (бит опущен),
EP_SETUP опущен,
Бит EP_CTR_TX поднят(его опускаем).
Если что-то не так, то переводим нулевую конечную точку в состояние STALL (EP0R = (EP0R^0x1010)&0xBFBF) и выходим из цикла.

Если всё так как надо, переводим нулевую конечную точку в состояние RX_VALID, TX_STALL (EP0R = (EP0R^0x3010)&0xBFBF). И снова попадаем в цикл, но параметры у нас вот такие:
ISTR_DIR должно быть OUT (бит поднят),
EP_SETUP опущен,
Бит EP_CTR_RX тоже поднят(его опускаем).

Переводим нулевую конечную точку в состояние STALL (EP0R = (EP0R^0x1010)&0xBFBF) и выходим из цикла.

Дескриптор отправлен.

Танец с бубном номер 2. Получение адреса устройства.

Шаг 1 начинается так же.

Из прерывания мы вошли в процедуру и в цикл внутри этой процедуры. Выяснили, что у нас 0 конечная точка. На всякий случай сохраняем состояние приёма и передачи (SaveTState = EP0R & 0x3030), а затем переводим нулевую конечную точку в состояние NAK (обработка данных) и по каналу приёма, и по каналу передачи. Так у нас будет время сформировать ответ хосту.

На этом этапе направление передачи (ISTR_DIR) должно быть OUT (бит поднят), Бит EP_SETUP поднят. Бит EP_CTR_RX тоже поднят(его опускаем).

Копируем данные из области PMA, размеченной для приёма в рабочие переменные. Это будет команда USB_Command = PMA_SBUF(EP0RX_OFFSET);
параметр USB_wValue = PMA_SBUF(EP0RX_OFFSET+2);
индекс USB_wIndex = PMA_SBUF(EP0RX_OFFSET+4);
и длина требуемого ответа USB_wLength = PMA_SBUF(EP0RX_OFFSET+6);

Смотрим на USB_wLength. На этот раз она равно нулю. Смотрим на команду. Её значение 0x0500 — назначить адрес. Значение адреса записано в USB_wValue, переписываем её во временную переменную USB_DADDR, заодно поднимая ей старший бит. Можно этого не делать, просто так немного удобнее. Переходим к шагу 2 (WAIT_STATUS_IN).

Шаг 2. WAIT_STATUS_IN. Открываем нулевую конечную точку для приёма и передачи EP0R = (EP0R^0x3030)&0xBFBF и переходим на следующий виток цикла.

Теперь мы попадаем в цикл со следующими параметрами:
(ISTR_DIR) должно быть IN (бит опущен),
EP_SETUP опущен,
Бит EP_CTR_TX поднят(его опускаем)

Тут мы только проверяем, нужно ли нам назначить адрес (для этого мы и записывали в USB_DADDR значение). Если да, то переписываем его в регистр адреса DADDR, и записываем в младшие 4 бита каждой из используемых конечных точек их номер — это будет сигнализировать драйверу, что с ними можно работать.
И наконец переводим нулевую конечную точку в состояние STALL (EP0R = (EP0R^0x1010)&0xBFBF) и выходим из цикла.

Адрес назначен.

И напоследок,

Ещё несколько команд, которые нужно уметь обрабатывать

Таблица

Команда (USB_Command)

Что от нас требуется

0x0900

Выбрать активную конфигурацию устройства (сконфигурировать). Номер конфигурации находится в USB_wValue и не должен превышать максимальное для нашего устройства

0x0500

Назначить адрес для данного устройства в текущей сессии. Адрес может быть от 1 до 127 (0 означает, что устройство пока без адреса). Именно по этому номеру хост будет обращаться к устройству

0x0100

Выключить возможность перевода устройства в спящий режим и обратно через порт (Clear Feature)

0x0300

Включить возможность перевода устройства в спящий режим и обратно (SET Feature)

0x0B01

Включить определённый интерфейс. Номер интерфейса содержится в младшем байте USB_wIndex, не должно быть больше чем количество интерфейсов у устройства и устройство к этому моменту должно быть сконфигурировано.

0x0102

Перевести конечную точку в состояние VALID (0x3000 или 0x0030), при условии, что она находится в состоянии STALL. Номер конечной точки находится в младших 7 битах USB_wIndex, направление — в 8м бите. Устройство должно быть сконфигурировано, конечная точка — активна.

0x0302

Перевести активную конечную точку в состояние STALL

0x0221

Настроить COM порт

В моём коде в этом месте ничего не происходит, но я заметил, что к этому моменту устройство и хост уже достаточно хорошо познакомились, и этот (а так же один из двух следующих) запрос приходит в момент подключения терминала. Таким образом сюда можно поставить, например, приветствие.

0x2021

Настроить бодрейт и прочие параметры передачи

0x2221

Настроить состояние линии управления портом

0x0680

Отправить хосту дескриптор. Какой именно определяется параметром USB_wValue:
0x0100 — дескриптор устройства
0x0200 — дескриптор конфигурации
0x0300 — поддерживаемые кодировки
0x0301 — поставщик, строка в юникоде
0x0302 — имя устройства. строка в юникоде
0x0303 — серийный номер устройства, человекочитаемая строка в юникоде

0x0080

Отправить хосту статус устройства. Если питается от хоста, то поднять бит 0x01, если возможность перевода в спящий режим и обратно включена, поднять бит 0x02. Иначе вернуть 0

0x0081

Статус интерфейса. Возвращаем 0.

0x0082

Статус конечной точки. Номер конечной точки находится в младших 7 битах USB_wIndex, направление — в 8м бите.
Возвращает 1, если состояние — STALL.

0x0880

Вернуть номер конфигурации. Это той, которую мы назначали командой 0x0900

0x2021

Обозначить бодрейт и прочие параметры передачи. И вернуть результат:
PMA_BUF(EP0TX_OFFSET) = 9600;//baudrate bytes
PMA_SBUF(EP0TX_OFFSET+4) = 0;//parity*256+format
PMA_SBUF(EP0TX_OFFSET+6) = 8;//datatype

0x2121

Передать хосту бодрейт и прочие параметры передачи. Ответ формируется так же, как и в предыдущем случае.

Надеюсь, что кому-то эта статья будет полезна. По крайней мере, именно такого разбора мне самому не хватало в процессе изучения библиотек и логики работы USB.

Certifications for Stm32 Virtual Com Port


5 stars for Stm32 Virtual Com Port «WOW! Finally my PC got up to speed!» Melany Kim 7/18/2016

Our main support OS are: Windows 7, Windows 7 64 bit, Windows 7 32 bit, Windows 10, Windows 10 64 bit, Windows 10 32 bit, and Windows 8.

Windows Compatibility for Stm32 Virtual Com Port driver


Stm32 Virtual Com Port Driver Details:

Stm32 Virtual Com Port File Name: stm32_virtual_com_port.zip

Stm32 Virtual Com Port Driver Version: 763XHEXA

Stm32 Virtual Com Port ZIP Size: 129.kb

Manufacturer: Stm32

Stm32 Virtual Com Port was fully scanned at: 2/2/2023


Scan Status: driver download scanOK

Stm32 Virtual Com Port now has a special edition for these Windows versions: Windows 7, Windows 7 64 bit, Windows 7 32 bit, Windows 10, Windows 10 64 bit,, Windows 10 32 bit, Windows 8,
Windows 7 Starter 64bit, Windows 7 Home Basic 32bit, Windows Vista Home Premium 32bit, Windows 10 Enterprise 32bit, Windows Vista Home Premium 64bit, Windows Vista Ultimate 64bit, Windows 10 Pro Education 32bit, Windows 10 IoT 64bit, Windows 7 Ultimate 64bit, Windows 10 Enterprise LTSB 64bit, Windows Vista Home Basic 64bit, Windows 7 Home Premium 32bit, Windows Vista Starter 64bit, Windows XP Starter Edition 32bit,

Stm32 Virtual Com Port Download Stats:

Driver Uploaded: 1/5/2019



Direct Downloads: 184
Most Recent Download: 11/9/2019


Managed Downloads: 484
Most Recent Download: 11/20/2019


Download Mirrors: 5
Current Delay: 21 Seconds
Driver License: Free Software

Direct Download Success Stats:

Reports Issued By 102/184 Users

Success Reported By 93/102 Users

Driver Download Score93/100

Driver Installation Score87/100

Overall preformance Score98/100

Stm32 Virtual Com Port 145 Thumbs Up145Thumbs
Up

Stm32 Virtual Com Port 4 Thumbs Down4Thumbs
Down

Stm32 Virtual Com Port driver direct download was reported as adequate by a large percentage of our reporters, so it should be good to download and install.

Installation Manager Success Stats:

Reports Issued By 459/484 Users

Success Reported By 443/459 Users

Driver Download Score91/100

Driver Installation Score90/100

Overall preformance Score93/100

Stm32 Virtual Com Port 475 Thumbs Up475Thumbs
Up

Stm32 Virtual Com Port 0 Thumbs Down0Thumbs
Down

Stm32 Virtual Com Port driver installation manager was reported as very satisfying by a large percentage of our reporters, so it is recommended to download and install.

DOWNLOAD OPTIONS:

1. Direct Download

Select your OS and press «Download».

This option requires basic OS understanding.
Select Your Operating System, download zipped files, and then proceed to
manually install them.
Recommended if Stm32 Virtual Com Port is the only driver on your PC you wish to update.

2. Driver Installation Manager

This option requires no OS understanding.
Automatically scans your PC for the specific required version of Stm32 Virtual Com Port + all other outdated drivers, and installs them all at once.

Please help us maintain a helpfull driver collection.
After downloading and installing Stm32 Virtual Com Port, or the driver installation manager, take a few minutes to send us a report:
* Only registered users can upload a report.


Don’t have a password?
Please register, and get one.

Stm32 Virtual Com Port may sometimes be at fault for other drivers ceasing to function

These are the driver scans of 2 of our recent wiki members*

*Scans were performed on computers suffering from Stm32 Virtual Com Port disfunctions.

Scan performed on 4/25/2019, Computer: Sony PCG-U101 — Windows 7 64 bit

Outdated or Corrupted drivers:5/19

Device/Driver Status Status Description Updated By Scanner
Motherboards
Intel(R) ICH10 Family 2 port Serial ATA Storage Controller 2 — 3A26 Outdated
Mice And Touchpads
Wacom Wacom Mouse Monitor Up To Date and Functioning
Usb Devices
Hewlett-Packard HP EWS Up To Date and Functioning
Sound Cards And Media Devices
Realtek Realtek AC97 Audio for VIA (R) Audio Controller Up To Date and Functioning
NVIDIA NVIDIA GeForce GTX 550 Ti Corrupted By Stm32 Virtual Com Port
Network Cards
Realtek Realtek RTL8187B Wireless 802.11b/g 54Mbps USB 2.0 Network Adapter Up To Date and Functioning
Keyboards
Microsoft Keyboard Device Filter Up To Date and Functioning
Hard Disk Controller
Microsoft ULi M5229 PCI-Bus-Master-IDE-Controller Outdated
Others
ULi Intel(R) 82801DB/DBM USB 2.0 Enhanced Host Controller — 24CD Up To Date and Functioning
Nokia Nokia C6-00 USB OBEX Up To Date and Functioning
Intel USB-IF USB 3.0 Hub Corrupted By Stm32 Virtual Com Port
Texas Instruments Texas Instruments PCI7420 Integrated FlashMedia Controller Up To Date and Functioning
AuthenTec Biometric Coprocessor Up To Date and Functioning
Cameras, Webcams And Scanners
USB2.0 PC CAMERA Periferica video USB Up To Date and Functioning
Video Cards
ATI ATI Technologies, Inc. 3D RAGE PRO AGP 2X Up To Date and Functioning
Input Devices
Microsoft HID Non-User Input Data Filter (KB 911895) Up To Date and Functioning
Port Devices
D-Link USB Single Port Up To Date and Functioning
Monitors
Sony Digital Flat Panel (1024×768) Up To Date and Functioning
Mobile Phones And Portable Devices
Acer NOKIA Corrupted By Stm32 Virtual Com Port

Scan performed on 4/25/2019, Computer: Sony VGN-TZ170C — Windows 7 32 bit

Outdated or Corrupted drivers:9/21

Device/Driver Status Status Description Updated By Scanner
Motherboards
Microsoft SiS964 PCI to LPC Bridge Up To Date and Functioning
Mice And Touchpads
Microsoft Microsoft USB Basic Optical Mouse (IntelliPoint) Outdated
A4Tech A4Tech USB Port Mouse Outdated
Usb Devices
Realtek Realtek USB 2.0 Card Reader Corrupted By Stm32 Virtual Com Port
Sound Cards And Media Devices
Intel Intel WiDi Audio Device Up To Date and Functioning
YUAN STK7700D Up To Date and Functioning
Network Cards
Microsoft SiS191-Ethernet-controller Up To Date and Functioning
Keyboards
Microsoft HID Keyboard Up To Date and Functioning
Hard Disk Controller
Advanced Micro Devices Controller standard PCI IDE Up To Date and Functioning
Others
Microsoft Texas Instruments PCI-7×20/6×20 CardBus Controller Up To Date and Functioning
Unibrain Texas Instruments 1394 OHCI Compliant Host Controller Up To Date and Functioning
Texas Instruments Masselagringskontroller Up To Date and Functioning
NEC Texas Instruments PCIxx21/x515 Cardbus Controller Corrupted By Stm32 Virtual Com Port
Broadcom Broadcom Bluetooth 4.0 Corrupted By Stm32 Virtual Com Port
Acronis Seagate DiscWizard Image Backup Archive Explorer Corrupted By Stm32 Virtual Com Port
Cameras, Webcams And Scanners
Canon Canon MP560 ser Outdated
Video Cards
Intel(R) HD Graphics Corrupted By Stm32 Virtual Com Port
Input Devices
Wacom Virtual Keyboard Interface Up To Date and Functioning
Port Devices
hspa Modem Application1 port (COM4) Outdated
Monitors
Sony Digital Flat Panel (1024×768) Up To Date and Functioning
Mobile Phones And Portable Devices
Acer NOKIA Up To Date and Functioning
Driver Model OS Original Upload Date Last Modification Driver File File Size Compatible Computer Models Availabilty To Instalation Manager
Stm32 Virtual Com Port 432.12.14 For Windows 7 32 bit 11/15/2014 10/20/2016 stm32_virtual_com_port-432.12.14.exe 45kb IBM 8187QG7, Sony VGN-SZ17SP_C, Sony VGN-NW265F, Lenovo ThinkStation S30, HP XY310ES, Fujitsu PRIMERGY TX140 S1, Sony VGN-TZ15AN, HP BK345AA-ACJ p6373i, Acer AS5320, HP KB010AA-AKB a6330.cs, , and more.
Stm32 Virtual Com Port 422.139 For Windows 7 64 bit 9/23/2014 6/15/2016 stm32_virtual_com_port-422.139.exe 118kb Intel C16, Sony VGNBZ579N03, Panasonic CF-Y2FW7AXS, Sony VGN-SZ32GP_B, ICP/iEi B216, HP PS294AA-ABZ t840.it, , and more.
Stm32 Virtual Com Port 431.166 For Windows 7 5/12/2016 8/8/2016 stm32_virtual_com_port-431.166.exe 119kb HP HP Compaq nx6125, EMachines EL1330, Fujitsu FMVXNN281, Sony VNG-FE25TP, RM Plc RM DESKTOP 201, Gigabyte M912, Sony VPCEH1Z1R, Packard Bell ISTART 2150, Sony VGN-FE770G, Toshiba SATELLITE L875-12M, Sony PCG-FX33S_BP, IBM System x3200 M3 -[7327Z29, Toshiba SATELLITE C855-12Z, , and more.
Stm32 Virtual Com Port 40379 For Windows 10 2/24/2016 10/1/2016 stm32_virtual_com_port-40379.exe 154kb Sony PCV-RZ211, Lenovo 7661ZRG, Sony VGN-NW305F, NEC PC-VL580CD1K, Lenovo 627436G, HP HP EliteBook 8530p, Lenovo ThinkCentre M77, IBM ThinkCentre A30, LG LB50-CC34ZL, NEC PC-LL750FS1SR, , and more.
Stm32 Virtual Com Port 41.171.10 For Windows 10 64 bit 11/12/2014 2/13/2017 stm32_virtual_com_port-41.171.10.exe 145kb Sony VPCEL13FX, LG F1-2242HS, IBM 1872W5A, Lenovo ThinkCentre A55, Gigabyte 945GDS1, HP Vd019aar-UUW P6110.sc, Lenovo ThinkPad Edge E320, Panasonic CF-53JAWZYDE, HP HP Pavilion DV9670EM, Lenovo 7665VEH, Sony PCV-RZ104, IBM 1847Y1Y, Toshiba Dynabook SS RX1/T9A, Sony VGN-NS225J, Panasonic CF-19THR65Q2, Toshiba SATELLITE L850-1J9, , and more.
Stm32 Virtual Com Port 41.171.11.1 For Windows 7 32 bit 9/17/2015 11/26/2016 stm32_virtual_com_port-41.171.11.1.exe 140kb IBM IBM System X3100 M4 -[2582K1G, HP HP 500 Notebook PC, IBM System x3200 M3 -[7328EBG, Notebook MIM 2270, Lenovo 6457WTU, Systemax Charity PC 2010, Toshiba Dynabook EX/66MWHS, HP RJ131AA-AB8 t3620.t, IBM 808735M, Sony VGNZ890G, Toshiba Dynabook Satellite T42 240E/5W, Gateway FX6840-03E, Acer Aspire 5670, , and more.
Stm32 Virtual Com Port 41.12.121.1 For Windows 7 64 bit 7/13/2015 9/16/2016 stm32_virtual_com_port-41.12.121.1.exe 43kb Lenovo 7360-D3U, IBM System x3200 M3 -[7328K3G, Gateway FX6800-05, Notebook MIM2290, Lenovo 20AQCTO1WW, HP BK373AA-AB1 HPE-176k, Gigabyte MSH61PI-EX, Olidata JumPC, Zenith Persidio, ASUS BM5275, IBM 2724JU1, Samsung 305E4A/305E5A/305E7A, CdcPointSpa MS-7277, Sony PCG-K215S, Lenovo ThinkCentre Edge 71z, Mitsubishi AL12FBHEZ42W, , and more.
Stm32 Virtual Com Port 42093 For Windows 7 9/15/2016 11/7/2016 stm32_virtual_com_port-42093.exe 164kb Toshiba Dynabook CX/45AB, Gateway DX4710-UB801A, HP HP d330 uT, Notebook MIM2290, HP P7-1090be, NEC PC-VL750CS, Toshiba SATELLITE C855-207, IBM 8185NUZ, Panasonic CF-50MB2FDKM, I.C.S Olivetti MK510, HP PS351AA-B1U d1260.se, IBM 48007X3, Sony VGNFW590FSB, NEC PC-MK29MLZDF, IBM 682532U, , and more.
Stm32 Virtual Com Port 43937 For Windows 10 3/11/2016 9/8/2016 stm32_virtual_com_port-43937.exe 139kb Sony VGN-TX72B_B, Alienware Alienware, HP DW225A-ABE t475.es, Sony VGN-SZ61VN_X, IBM ThinkCentre S50, Toshiba Dynabook EX/33JK, , and more.
Stm32 Virtual Com Port 42362.1 For Windows 10 64 bit 2/26/2014 7/3/2016 stm32_virtual_com_port-42362.1.exe 122kb IBM 8213WCM, MSI SO-7597, Lenovo 2876AR1, Lenovo 3000 S200 Desktop, HP Pavilion zx5000, Acer TM7520, HP EC658AA-ABH W5170.NL, Toshiba SATELLITE,A665, WIPRO WSG37425W7-0012, , and more.
Stm32 Virtual Com Port 41.1987 For Windows 8 3/1/2014 1/13/2017 stm32_virtual_com_port-41.1987.exe 54kb Fujitsu FMVS565ABC, Gateway MX7337H, Gateway T-1620, HP PJ425AA-ABN t670.no, NEC PC-MJ29MLZZ1BSG, HP FL399AA-ABZ m9495it, IBM 1847Y1Y, NEC VERSAP550 NN680209359, , and more.
Stm32 Virtual Com Port 401.12.18 For Windows 7 32 bit 9/25/2015 2/16/2017 stm32_virtual_com_port-401.12.18.exe 193kb HP HP ProBook 4510s, Toshiba SATELLITE L850-13U, Toshiba SATELLITE PRO C50-A-1HZ, SAMSUN 700Z, Lenovo 3000 J205 Desktop, Open Labs Neko/Miko, HP HPE-350es, HP KT439AA-AB4 a6545d, Acer Aspire 5940G, Panasonic CF-19TH187QW, Packard Bell SPIRIT 3000, Foxconn Inferno Katana GTI, NEC Express5800/56Xd [N8000-566C, NEC PC-VG32VVZMM, MAXDATA ECO4700IW, WIPRO WIV37455-1090, Advantech UNO-2178A, , and more.

STM32 Virtual COM Port Driver STSW-STM32102 V1.5.0

tags: Radar_System  VCP  STM32  V1.5.0  VCP Driver  STSW-STM32102

description

Compatible with the x86 and x64 platforms The STSW-STM32102 software package contains four installation files based on the various versions of the Microsoft® operating system.
OS versions prior to Windows® 7 are compatible with the Windows® 7 installations included in the package.
Starting from Windows® 10, the STSW-STM32102 driver is no more adequate and the usage of the native inbox driver is recommended.

All functions

Virtual COM port driver installation package for Windows® operating systems: 98SE, 2000, XP, Vista®, 7, and 8.x

STSW-STM32102 STM32 Virtual COM Port Driver 1.5.0

Website:

https://www.st.com/zh/development-tools/stsw-stm32102.html 

https://www.st.com/content/st_com/en/products/development-tools/software-development-tools/stm32-software-development-tools/stm32-utilities/stsw-stm32102.html

Volume new plus folder PATH list
 Volume serial number 000000E3 AED4: 4CF9
 D:  High Speed ​​USB Transport  STM32 USB VCP Driver Install  En.stsw-STM32102-STM32 Virtual COM Port Driver-1.5.0
    readme.txt
    VCP_V1.5.0_Setup_W7_x64_64bits.exe
    VCP_V1.5.0_Setup_W7_x86_32bits.exe
    VCP_V1.5.0_Setup_W8_x64_64bits.exe
    VCP_V1.5.0_Setup_W8_x86_32bits.exe
    version.txt
    
 No subfolder 

Intelligent Recommendation

STM32 UART serial port DMA driver design

Preface The stm32 serial port communication is a commonly used function in development, basically everyone will need it to transmit some information to other devices or PC. However, most people cannot…

STM32 analog serial port driver (with parity bit)

In a project a year ago, due to the shortage of serial port resources, the hardware used the mainMCU-STM32F030C8T6, Both of its hardware serial ports are used, and the communication between the other …

STM32 USB virtual serial port principle (on)

The USB virtual serial port is a communication interface implemented using the USB CDC (CDC is communication device class (virtual port com)) class. Use STM32’s built-in USB slave function to implemen…

More Recommendation

STM32 USB Virtual Serial Port Receiving Data

STM32 USB Virtual Serial Port Receiving Data 1.STM32 USB Virtual Serial Port Send Data Function 2.STM32 USB Virtual Serial Port Received Data Code (can receive any length of data, maximum reception le…

STM32 virtual serial port hal library development

STM32HAL library, processing virtual serial port data (full double work processing) Today I will tell you the development of virtual serial port data below to solve the problem of the hardware of the …

Понравилась статья? Поделить с друзьями:
  • Драйвер видеокарты не совместим с windows 10 как исправить
  • Драйвер видеокарты windows 10 для hp g62
  • Драйвер видеокарты radeon rx 580 series для windows 10
  • Драйвер видеокарты intel hd graphics 630 windows 10
  • Драйвер видеокарты dell inspiron 1501 windows 7 32 bit