スキップしてメイン コンテンツに移動

STM32F103RCT6でクアッド USB Serial Converterは可能かどうか試してみる

STM32F103は利用できるUSBエンドポイントがep0を含めて8個までなので、CDCインターフェースを複数使用する場合、CMD epとData epの2epのセットが3個半になり、CDCインターフェースは3個が本来上限です。しかし、どこで読んだのかおぼえていないんですが、CMD epを使わない場合、Data epだけでもプログラム次第では(簡易的に)CDCとして機能できるとのことでした。
ちなみにcubemxのusb cdcのコードでは、何もしないとcommand endpointを使いませんので、usbd_cdc.cのUSBD_CDC_Init()でcommand endpointを初期化時にopenしなくても簡易vcp/cdcとしては機能します。(stm32f103rct6, stm32f407vet6, stm32g474reでcdcを複数簡易実装して確認済。なお、stm32でvcpを扱う記事では、command endpointの使用方法にまで言及していないものが多いです。)
そこでbluepill-serial-monster をベースにソースを編集し、vscodeでデバッグしたところ、ターゲットボード上では確かに8ep目(4つ目のcmd ep, ep_num 7)までは初期化しているようでしたが、9ep目(4つ目の data ep, ep_num 8)は初期化に失敗していることが確認できました。ハードウェアーとしては仕様通りです。
次に8ep目(ep_num 7)にdata epが来るようにしその他一部調整したところ、あっさりと、4 port 同時に115200で文字送受信・zmodemでファイル送受信できたので、本稿を挙げてみることにしてみました。

注意:rs232接続ではなく、tx/rxのみ使用する場合、bluepill-serial-monsterと同じく、9600等の低速だとテキストサイズが大きめ(約1.1k byte以上)の場合、残念ながら暴走します。また、同じく、9600等の低速だと、zmodemによるファイル転送はサイズの大小にかかわらず暴走してしまいます。なおkermitの場合は9600でもファイル転送できました。また、115200の場合はzmodemによるファイル転送は同時に4つ送受信できますが、テキストサイズが極端に大きい場合(例えば8k bytesのコピペでは)暴走します。逆に921600の場合、テキストサイズが100kを超える場合でも暴走しませんが、zmodeによるファイル転送は同時に2つまでしかできませんでした。CubeProgrammerは115200/even/8/1で接続・読出・書込できました。また、rs232での接続テストは行っていません。
使用にあたって当方は責任を一切負いませんので、あしからずご了承ください。

ターゲットボードはSTM32_mini black board (通称)を使用しました。2023/9月現在、国内ではこちらから入手できるようです。
まず、元となるのは例のbluepill-serial-monsterとSTM32CubeF1です。以下のようにしてビルドできるようにしておきます。
mkdir ~/stm32
cd stm32
git clone https://github.com/r2axz/bluepill-serial-monster
git clone https://github.com/STMicroelectronics/STM32CubeF1
export STM32CUBE_PATH=~/stm32/STM32CubeF1
つづいてパッチを当てます。長いのですが以下のようになります。
diff -urN bluepill-serial-monster/device_config.c rct6-quad-cdc/device_config.c
--- bluepill-serial-monster/device_config.c	2023-09-28 22:40:19.128560708 +0900
+++ rct6-quad-cdc/device_config.c	2023-09-27 19:40:53.947756241 +0900
@@ -9,15 +9,15 @@
 #include <limits.h>
 #include "device_config.h"
 
-#define DEVICE_CONFIG_FLASH_SIZE    0x10000UL
+#define DEVICE_CONFIG_FLASH_SIZE    0x40000UL
 #define DEVICE_CONFIG_NUM_PAGES     2
-#define DEVICE_CONFIG_PAGE_SIZE     0x400UL
+#define DEVICE_CONFIG_PAGE_SIZE     0x800UL
 #define DEVICE_CONFIG_FLASH_END     (FLASH_BASE + DEVICE_CONFIG_FLASH_SIZE)
 #define DEVICE_CONFIG_BASE_ADDR     ((void*)(DEVICE_CONFIG_FLASH_END - DEVICE_CONFIG_NUM_PAGES * DEVICE_CONFIG_PAGE_SIZE))
 #define DEVICE_CONFIG_MAGIC         0xDECFDECFUL
 
 static const device_config_t default_device_config = {
-    .status_led_pin = { .port = GPIOC, .pin = 13, .dir = gpio_dir_output, .speed = gpio_speed_low, .func = gpio_func_general, .output = gpio_output_od, .polarity = gpio_polarity_low },
+    .status_led_pin = { .port = GPIOD, .pin = 2, .dir = gpio_dir_output, .speed = gpio_speed_low, .func = gpio_func_general, .output = gpio_output_pp, .polarity = gpio_polarity_low },
     .config_pin = { .port = GPIOB, .pin = 5, .dir = gpio_dir_input, .pull = gpio_pull_up, .polarity = gpio_polarity_low },
     .cdc_config = {
         .port_config = {
@@ -62,10 +62,25 @@
                     /* dsr */ { .port = GPIOB, .pin =  6, .dir = gpio_dir_input,  .pull = gpio_pull_up, .polarity = gpio_polarity_low },
                     /* dtr */ { .port = GPIOA, .pin =  6, .dir = gpio_dir_output, .speed = gpio_speed_medium, .func = gpio_func_general, .output = gpio_output_pp, .polarity = gpio_polarity_low  },
                     /* dcd */ { .port = GPIOB, .pin =  9, .dir = gpio_dir_input,  .pull = gpio_pull_up, .polarity = gpio_polarity_low },
-                    /*  ri */ { .port = GPIOA, .pin =  8, .dir = gpio_dir_input,  .pull = gpio_pull_up, .polarity = gpio_polarity_low },
+                    /*  ri */ { .port = GPIOB, .pin =  2, .dir = gpio_dir_input,  .pull = gpio_pull_up, .polarity = gpio_polarity_low },
                     /* txa */ { .port = GPIOA, .pin =  7, .dir = gpio_dir_output, .speed = gpio_speed_medium, .func = gpio_func_general, .output = gpio_output_pp, .polarity = gpio_polarity_high  },
                 }
             },
+            /*  Port 3 */
+            {
+                .pins = 
+                {
+                    /*  rx */ { .port = GPIOC, .pin = 11, .dir = gpio_dir_input,  .pull = gpio_pull_up, .polarity = gpio_polarity_high },
+                    /*  tx */ { .port = GPIOC, .pin = 10, .dir = gpio_dir_output, .speed = gpio_speed_medium, .func = gpio_func_alternate, .output = gpio_output_pp, .polarity = gpio_polarity_high  },
+                    /* rts */ { .port = GPIOC, .pin =  9, .dir = gpio_dir_output, .speed = gpio_speed_medium, .func = gpio_func_general, .output = gpio_output_pp, .polarity = gpio_polarity_low },
+                    /* cts */ { .port = GPIOC, .pin =  8, .dir = gpio_dir_input,  .pull = gpio_pull_down, .polarity = gpio_polarity_low },
+                    /* dsr */ { .port = GPIOB, .pin =  4, .dir = gpio_dir_input,  .pull = gpio_pull_up, .polarity = gpio_polarity_low },
+                    /* dtr */ { .port = GPIOC, .pin =  5, .dir = gpio_dir_output, .speed = gpio_speed_medium, .func = gpio_func_general, .output = gpio_output_pp, .polarity = gpio_polarity_low  },
+                    /* dcd */ { .port = GPIOC, .pin =  6, .dir = gpio_dir_input,  .pull = gpio_pull_up, .polarity = gpio_polarity_low },
+                    /*  ri */ { .port = GPIOC, .pin =  7, .dir = gpio_dir_input,  .pull = gpio_pull_up, .polarity = gpio_polarity_low },
+                    /* txa */ { .port = GPIOC, .pin =  4, .dir = gpio_dir_output, .speed = gpio_speed_medium, .func = gpio_func_general, .output = gpio_output_pp, .polarity = gpio_polarity_high  },
+                }
+            },
         }
     }
 };
diff -urN bluepill-serial-monster/Makefile rct6-quad-cdc/Makefile
--- bluepill-serial-monster/Makefile	2023-09-28 22:40:19.128560708 +0900
+++ rct6-quad-cdc/Makefile	2023-09-28 10:42:42.538547065 +0900
@@ -1,5 +1,5 @@
 # General Target Settings
-TARGET	= bluepill-serial-monster
+TARGET	= rct6-quad-cdc
 SRCS	= main.c system_clock.c system_interrupts.c status_led.c usb_core.c usb_descriptors.c\
 	usb_io.c usb_uid.c usb_panic.c usb_cdc.c cdc_shell.c gpio.c device_config.c
 
@@ -14,15 +14,15 @@
 
 # STM32Cube Path
 STM32CUBE	= ${STM32CUBE_PATH}
-STM32_STARTUP	= $(STM32CUBE)/Drivers/CMSIS/Device/ST/STM32F1xx/Source/Templates/gcc/startup_stm32f103xb.s
+STM32_STARTUP	= $(STM32CUBE)/Drivers/CMSIS/Device/ST/STM32F1xx/Source/Templates/gcc/startup_stm32f103xe.s
 STM32_SYSINIT	= $(STM32CUBE)/Drivers/CMSIS/Device/ST/STM32F1xx/Source/Templates/system_stm32f1xx.c
-STM32_LDSCRIPT	= $(STM32CUBE)/Drivers/CMSIS/Device/ST/STM32F1xx/Source/Templates/gcc/linker/STM32F103XB_FLASH.ld
+STM32_LDSCRIPT	= $(STM32CUBE)/Drivers/CMSIS/Device/ST/STM32F1xx/Source/Templates/gcc/linker/STM32F103XE_FLASH.ld
 
 STM32_INCLUDES	+= -I$(STM32CUBE)/Drivers/CMSIS/Core/Include
 STM32_INCLUDES	+= -I$(STM32CUBE)/Drivers/CMSIS/Core_A/Include
 STM32_INCLUDES	+= -I$(STM32CUBE)/Drivers/CMSIS/Device/ST/STM32F1xx/Include
 
-DEFINES		= -DSTM32F103xB -DHSE_VALUE=8000000U
+DEFINES		= -DSTM32F103xE -DHSE_VALUE=8000000U
 CPUFLAGS	= -mthumb -mcpu=cortex-m3
 WARNINGS	= -Wall
 OPTIMIZATION	= -O3
diff -urN bluepill-serial-monster/usb_cdc.c rct6-quad-cdc/usb_cdc.c
--- bluepill-serial-monster/usb_cdc.c	2023-09-28 22:40:19.128560708 +0900
+++ rct6-quad-cdc/usb_cdc.c	2023-09-28 10:20:29.598544632 +0900
@@ -55,7 +55,7 @@
 /* Helper Functions */
 
 static USART_TypeDef* const usb_cdc_port_usarts[] = {
-    USART1, USART2, USART3
+    USART1, USART2, USART3, UART4
 };
 
 static USART_TypeDef* usb_cdc_get_port_usart(int port) {
@@ -76,6 +76,7 @@
         { DMA1_Channel5,  DMA1_Channel4 },
         { DMA1_Channel6,  DMA1_Channel7 },
         { DMA1_Channel3,  DMA1_Channel2 },
+        { DMA2_Channel3,  DMA2_Channel5 },
     };
     if (port < (sizeof(port_dma_channels) / sizeof(*port_dma_channels)) && 
         port_dir < usb_cdc_port_direction_last) {
@@ -88,6 +89,7 @@
     usb_endpoint_address_cdc_0_data,
     usb_endpoint_address_cdc_1_data,
     usb_endpoint_address_cdc_2_data,
+    usb_endpoint_address_cdc_3_data,
 };
 
 static uint8_t usb_cdc_get_port_data_ep(int port) {
@@ -110,6 +112,7 @@
     usb_endpoint_address_cdc_0_interrupt,
     usb_endpoint_address_cdc_1_interrupt,
     usb_endpoint_address_cdc_2_interrupt,
+    usb_endpoint_address_cdc_3_interrupt,
 };
 
 static uint8_t usb_cdc_get_port_notification_ep(int port) {
@@ -120,7 +123,7 @@
 }
 
 static uint8_t const usb_cdc_port_interfaces[] = {
-    usb_interface_cdc_0, usb_interface_cdc_1, usb_interface_cdc_2
+    usb_interface_cdc_0, usb_interface_cdc_1, usb_interface_cdc_2, usb_interface_cdc_3
 };
 
 static int usb_cdc_get_port_interface(int port) {
@@ -499,6 +502,14 @@
     usb_cdc_port_tx_complete(2);
 }
 
+void DMA2_Channel4_5_IRQHandler() {
+    (void)DMA2_Channel4_5_IRQHandler;
+    uint32_t status = DMA2->ISR & ( DMA_ISR_TCIF5 );
+    DMA2->IFCR = status;
+    usb_cdc_port_tx_complete(3);
+}
+
+
 /* USART Interrupt Handlers */
 
 __attribute__((always_inline)) inline static void usb_cdc_usart_irq_handler(int port, USART_TypeDef * usart,
@@ -533,6 +544,11 @@
     usb_cdc_usart_irq_handler(2, usb_cdc_port_usarts[2], usb_cdc_states[2].txa_bitband_clear);
 }
 
+void UART4_IRQHandler() {
+    (void)UART4_IRQHandler;
+    usb_cdc_usart_irq_handler(3, usb_cdc_port_usarts[3], usb_cdc_states[3].txa_bitband_clear);
+}
+
 /* Port Configuration & Control Lines Functions */
 
 void usb_cdc_reconfigure_port_pin(int port, cdc_pin_t pin) {
@@ -579,6 +595,8 @@
     NVIC_EnableIRQ(DMA1_Channel4_IRQn);
     NVIC_SetPriority(DMA1_Channel7_IRQn, SYSTEM_INTERRUTPS_PRIORITY_HIGH);
     NVIC_EnableIRQ(DMA1_Channel7_IRQn);
+    NVIC_SetPriority(DMA2_Channel4_5_IRQn, SYSTEM_INTERRUTPS_PRIORITY_HIGH);
+    NVIC_EnableIRQ(DMA2_Channel4_5_IRQn);    
     /* 
      * Disable JTAG interface (SWD is still enabled),
      * this frees PA15, PB3, PB4 (needed for DSR/RI inputs).
@@ -589,14 +607,16 @@
     gpio_pin_init(&device_config->config_pin);
     /* USART & DMA Reset and Setup */
     RCC->APB2ENR |= RCC_APB2ENR_USART1EN;
-    RCC->APB1ENR |= RCC_APB1ENR_USART2EN | RCC_APB1ENR_USART3EN;
-    RCC->AHBENR |= RCC_AHBENR_DMA1EN;
+    RCC->APB1ENR |= RCC_APB1ENR_USART2EN | RCC_APB1ENR_USART3EN | RCC_APB1ENR_UART4EN;
+    RCC->AHBENR |= RCC_AHBENR_DMA1EN | RCC_AHBENR_DMA2EN;
     RCC->APB2RSTR |= RCC_APB2RSTR_USART1RST;
     RCC->APB1RSTR |= RCC_APB1RSTR_USART2RST;
     RCC->APB1RSTR |= RCC_APB1RSTR_USART3RST;
+    RCC->APB1RSTR |= RCC_APB1RSTR_UART4RST;
     RCC->APB2RSTR &= ~(RCC_APB2RSTR_USART1RST);
     RCC->APB1RSTR &= ~(RCC_APB1RSTR_USART2RST);
     RCC->APB1RSTR &= ~(RCC_APB1RSTR_USART3RST);
+    RCC->APB1RSTR &= ~(RCC_APB1RSTR_UART4RST);
     memset(&usb_cdc_states, 0, sizeof(usb_cdc_states));
     for (int port=0; port<USB_CDC_NUM_PORTS; port++) {
         (void)usb_cdc_states[port]._rx_data;
@@ -624,6 +644,8 @@
     NVIC_EnableIRQ(USART2_IRQn);
     NVIC_SetPriority(USART3_IRQn, SYSTEM_INTERRUTPS_PRIORITY_CRITICAL);
     NVIC_EnableIRQ(USART3_IRQn);
+    NVIC_SetPriority(UART4_IRQn, SYSTEM_INTERRUTPS_PRIORITY_CRITICAL);
+    NVIC_EnableIRQ(UART4_IRQn);
 }
 
 void usb_cdc_enable() {
diff -urN bluepill-serial-monster/usb_cdc.h rct6-quad-cdc/usb_cdc.h
--- bluepill-serial-monster/usb_cdc.h	2023-09-28 22:40:19.128560708 +0900
+++ rct6-quad-cdc/usb_cdc.h	2023-09-27 19:40:53.947756241 +0900
@@ -198,7 +198,7 @@
 
 /* CDC Device Definitions */
 
-#define USB_CDC_NUM_PORTS                       3
+#define USB_CDC_NUM_PORTS                       4 
 #define USB_CDC_BUF_SIZE                        0x400
 #define USB_CDC_CRTL_LINES_POLLING_INTERVAL     20 /* ms */
 #define USB_CDC_CONFIG_PORT                     0
diff -urN bluepill-serial-monster/usb_core.c rct6-quad-cdc/usb_core.c
--- bluepill-serial-monster/usb_core.c	2023-09-28 22:40:19.128560708 +0900
+++ rct6-quad-cdc/usb_core.c	2023-09-28 10:58:22.188561086 +0900
@@ -150,19 +150,21 @@
                                             void **payload, size_t *payload_size, usb_tx_complete_cb_t *tx_callback_ptr) {
 
     uint8_t ep_num = setup->wIndex & ~(usb_endpoint_direction_in);
-    usb_endpoint_direction_t ep_direction = setup->wIndex & usb_endpoint_direction_in;
-    if ((setup->bRequest == usb_device_request_set_feature) ||
-        (setup->bRequest == usb_device_request_clear_feature)) {
-        uint8_t ep_stall = (setup->bRequest == usb_device_request_set_feature);
-        usb_endpoint_set_stall(ep_num, ep_direction, ep_stall);
-        return usb_status_ack;
-    } else if (setup->bRequest == usb_device_request_get_status) {
-        ((uint8_t*)(*payload))[0] = usb_endpoint_is_stalled(ep_num , ep_direction);
-        ((uint8_t*)(*payload))[1] = 0;
-        return usb_status_ack;
-    } else {
-        return usb_status_fail;
-    }
+    if (ep_num != 8){
+        usb_endpoint_direction_t ep_direction = setup->wIndex & usb_endpoint_direction_in;
+        if ((setup->bRequest == usb_device_request_set_feature) ||
+            (setup->bRequest == usb_device_request_clear_feature)) {
+            uint8_t ep_stall = (setup->bRequest == usb_device_request_set_feature);
+            usb_endpoint_set_stall(ep_num, ep_direction, ep_stall);
+            return usb_status_ack;
+        } else if (setup->bRequest == usb_device_request_get_status) {
+            ((uint8_t*)(*payload))[0] = usb_endpoint_is_stalled(ep_num , ep_direction);
+            ((uint8_t*)(*payload))[1] = 0;
+            return usb_status_ack;
+        } else {
+            return usb_status_fail;
+        }
+    } 
 }
 
 /* Control Endpoint Request Processing */
diff -urN bluepill-serial-monster/usb_descriptors.c rct6-quad-cdc/usb_descriptors.c
--- bluepill-serial-monster/usb_descriptors.c	2023-09-28 22:40:19.128560708 +0900
+++ rct6-quad-cdc/usb_descriptors.c	2023-09-28 10:20:29.598544632 +0900
@@ -15,8 +15,9 @@
 #define USB_CDC_DATA_ENDPOINT_SIZE_LARGE    64
 
 #define USB_CDC_DATA_0_ENDPOINT_SIZE         USB_CDC_DATA_ENDPOINT_SIZE_SMALL
-#define USB_CDC_DATA_1_ENDPOINT_SIZE         USB_CDC_DATA_ENDPOINT_SIZE_LARGE
-#define USB_CDC_DATA_2_ENDPOINT_SIZE         USB_CDC_DATA_ENDPOINT_SIZE_LARGE
+#define USB_CDC_DATA_1_ENDPOINT_SIZE         USB_CDC_DATA_ENDPOINT_SIZE_SMALL
+#define USB_CDC_DATA_2_ENDPOINT_SIZE         USB_CDC_DATA_ENDPOINT_SIZE_SMALL
+#define USB_CDC_DATA_3_ENDPOINT_SIZE         USB_CDC_DATA_ENDPOINT_SIZE_SMALL
 
 #define USB_CDC_INTERRUPT_ENDPOINT_POLLING_INTERVAL 20
 
@@ -70,15 +71,32 @@
         .tx_size    = USB_CDC_DATA_2_ENDPOINT_SIZE,
         .event_handler = usb_cdc_data_endpoint_event_handler,
     },
+     /*  CDC 3 Data Endpoint */
+    {
+        .type       = usb_endpoint_type_bulk,
+        .rx_size    = USB_CDC_DATA_3_ENDPOINT_SIZE,
+        .tx_size    = USB_CDC_DATA_3_ENDPOINT_SIZE,
+        .event_handler = usb_cdc_data_endpoint_event_handler,
+    },
+#if 1
+    /*  CDC 3 Interrupt Endpoint */
+    {
+        .type       = usb_endpoint_type_interrupt,
+        .rx_size    = 0,
+        .tx_size    = USB_CDC_INTERRUPT_ENDPOINT_SIZE,
+        .event_handler = 0,
+    },
+#endif
 };
 
 const usb_string_descriptor_t usb_string_lang                   = USB_ARRAY_DESC(usb_language_code_en_US);
-const usb_string_descriptor_t usb_string_manufacturer           = USB_STRING_DESC("R2AXZ Kirill Kotyagin");
-const usb_string_descriptor_t usb_string_product                = USB_STRING_DESC("Bluepill Serial Monster");
+const usb_string_descriptor_t usb_string_manufacturer           = USB_STRING_DESC("XXXX YYYY ZZZZ");
+const usb_string_descriptor_t usb_string_product                = USB_STRING_DESC("STM32F1 Quad CDC");
 const usb_string_descriptor_t usb_string_serial                 = USB_STRING_DESC("NO SERIAL"); /* Placeholder, replaced by STM32 UID */
 const usb_string_descriptor_t usb_string_uart_1_interface_name  = USB_STRING_DESC("UART1");
 const usb_string_descriptor_t usb_string_uart_2_interface_name  = USB_STRING_DESC("UART2");
 const usb_string_descriptor_t usb_string_uart_3_interface_name  = USB_STRING_DESC("UART3");
+const usb_string_descriptor_t usb_string_uart_4_interface_name  = USB_STRING_DESC("UART4");
 
 const usb_string_descriptor_t *usb_string_descriptors[usb_string_index_last] = {
     &usb_string_lang,
@@ -88,6 +106,7 @@
     &usb_string_uart_1_interface_name,
     &usb_string_uart_2_interface_name,
     &usb_string_uart_3_interface_name,
+    &usb_string_uart_4_interface_name,
 };
 
 const usb_device_descriptor_t usb_device_descriptor = {
@@ -112,7 +131,7 @@
         .bLength                = sizeof(usb_configuration_descriptor.config),
         .bDescriptorType        = usb_descriptor_type_configuration,
         .wTotalLength           = sizeof(usb_configuration_descriptor),
-        .bNumInterfaces         = 6,
+        .bNumInterfaces         = 8,
         .bConfigurationValue    = 1,
         .iConfiguration         = usb_string_index_none,
         .bmAttributes           = USB_CFG_ATTR_RESERVED,
@@ -364,5 +383,86 @@
         .wMaxPacketSize         = USB_CDC_DATA_2_ENDPOINT_SIZE,
         .bInterval              = 0,
     },
-
+   .comm_iad_3 = {
+        .bLength                = sizeof(usb_configuration_descriptor.comm_iad_3),
+        .bDescriptorType        = usb_descriptor_type_interface_assoc,
+        .bFirstInterface        = usb_interface_cdc_3,
+        .bInterfaceCount        = 2,
+        .bFunctionClass         = usb_class_cdc,
+        .bFunctionSubClass      = usb_subclass_cdc_acm,
+        .bFunctionProtocol      = USB_PROTOCOL_CDC_DEFAULT,
+        .iFunction              = usb_string_index_none,
+    },
+    .comm_3 = {
+        .bLength                = sizeof(usb_configuration_descriptor.comm_3),
+        .bDescriptorType        = usb_descriptor_type_interface,
+        .bInterfaceNumber       = usb_interface_cdc_3,
+        .bAlternateSetting      = 0,
+        .bNumEndpoints          = 1,
+        .bInterfaceClass        = usb_class_cdc,
+        .bInterfaceSubClass     = usb_subclass_cdc_acm,
+        .bInterfaceProtocol     = USB_PROTOCOL_CDC_DEFAULT,
+        .iInterface             = usb_string_index_uart_4_interface_name,
+    },
+    .cdc_hdr_3 = {
+        .bFunctionLength        = sizeof(usb_configuration_descriptor.cdc_hdr_3),
+        .bDescriptorType        = usb_descriptor_type_cs_interface,
+        .bDescriptorSubType     = usb_descriptor_subtype_cdc_header,
+        .bcdCDC                 = USB_BCD_VERSION(1, 1, 0),
+    },
+    .cdc_mgmt_3 = {
+        .bFunctionLength        = sizeof(usb_configuration_descriptor.cdc_mgmt_3),
+        .bDescriptorType        = usb_descriptor_type_cs_interface,
+        .bDescriptorSubType     = usb_descriptor_subtype_cdc_call_management,
+        .bmCapabilities         = 0,
+        .bDataInterface         = usb_interface_cdc_3 + 1,
+    },
+    .cdc_acm_3 = {
+        .bFunctionLength        = sizeof(usb_configuration_descriptor.cdc_acm_3),
+        .bDescriptorType        = usb_descriptor_type_cs_interface,
+        .bDescriptorSubType     = usb_descriptor_subtype_cdc_acm,
+        .bmCapabilities         = USB_CDC_ACM_CAPABILITIES,
+    },
+    .cdc_union_3 = {
+        .bFunctionLength        = sizeof(usb_configuration_descriptor.cdc_union_3),
+        .bDescriptorType        = usb_descriptor_type_cs_interface,
+        .bDescriptorSubType     = usb_descriptor_subtype_cdc_union,
+        .bMasterInterface0      = usb_interface_cdc_3,
+        .bSlaveInterface0       = usb_interface_cdc_3 + 1,
+    },
+    .comm_ep_3 = {
+        .bLength                = sizeof(usb_configuration_descriptor.comm_ep_3),
+        .bDescriptorType        = usb_descriptor_type_endpoint,
+        .bEndpointAddress       = usb_endpoint_direction_in | usb_endpoint_address_cdc_3_interrupt,
+        .bmAttributes           = usb_endpoint_type_interrupt,
+        .wMaxPacketSize         = USB_CDC_INTERRUPT_ENDPOINT_SIZE,
+        .bInterval              = USB_CDC_INTERRUPT_ENDPOINT_POLLING_INTERVAL,
+    },
+    .data_3 = {
+        .bLength                = sizeof(usb_configuration_descriptor.data_3),
+        .bDescriptorType        = usb_descriptor_type_interface,
+        .bInterfaceNumber       = usb_interface_cdc_3 + 1,
+        .bAlternateSetting      = 0,
+        .bNumEndpoints          = 2,
+        .bInterfaceClass        = usb_class_cdc_data,
+        .bInterfaceSubClass     = usb_subclass_cdc_none,
+        .bInterfaceProtocol     = usb_protocol_cdc_none,
+        .iInterface             = usb_string_index_none,
+    },
+    .data_eprx_3 = {
+        .bLength                = sizeof(usb_configuration_descriptor.data_eprx_3),
+        .bDescriptorType        = usb_descriptor_type_endpoint,
+        .bEndpointAddress       = usb_endpoint_direction_out | usb_endpoint_address_cdc_3_data,
+        .bmAttributes           = usb_endpoint_type_bulk,
+        .wMaxPacketSize         = USB_CDC_DATA_3_ENDPOINT_SIZE,
+        .bInterval              = 0,
+    },
+    .data_eptx_3 = {
+        .bLength                = sizeof(usb_configuration_descriptor.data_eptx_3),
+        .bDescriptorType        = usb_descriptor_type_endpoint,
+        .bEndpointAddress       = usb_endpoint_direction_in | usb_endpoint_address_cdc_3_data,
+        .bmAttributes           = usb_endpoint_type_bulk,
+        .wMaxPacketSize         = USB_CDC_DATA_3_ENDPOINT_SIZE,
+        .bInterval              = 0,
+    },
 };
diff -urN bluepill-serial-monster/usb_descriptors.h rct6-quad-cdc/usb_descriptors.h
--- bluepill-serial-monster/usb_descriptors.h	2023-09-28 22:40:19.128560708 +0900
+++ rct6-quad-cdc/usb_descriptors.h	2023-09-28 09:00:33.378581006 +0900
@@ -28,6 +28,7 @@
     usb_string_index_uart_1_interface_name,
     usb_string_index_uart_2_interface_name,
     usb_string_index_uart_3_interface_name,
+    usb_string_index_uart_4_interface_name,
     usb_string_index_last,
 } __attribute__ ((packed)) usb_string_index_t;
 
@@ -43,6 +44,8 @@
     usb_endpoint_address_cdc_1_data         = 0x04,
     usb_endpoint_address_cdc_2_interrupt    = 0x05,
     usb_endpoint_address_cdc_2_data         = 0x06,
+    usb_endpoint_address_cdc_3_data         = 0x07,
+    usb_endpoint_address_cdc_3_interrupt    = 0x08,
     usb_endpoint_address_last
 };
 
@@ -54,6 +57,7 @@
     usb_interface_cdc_0 = 0x00,
     usb_interface_cdc_1 = 0x02,
     usb_interface_cdc_2 = 0x04,
+    usb_interface_cdc_3 = 0x06,
 };
 
 /* Device Descriptor */
@@ -94,6 +98,16 @@
     usb_interface_descriptor_t          data_2;
     usb_endpoint_descriptor_t           data_eprx_2;
     usb_endpoint_descriptor_t           data_eptx_2;
+    usb_iad_descriptor_t                comm_iad_3;
+    usb_interface_descriptor_t          comm_3;
+    usb_cdc_header_desc_t               cdc_hdr_3;
+    usb_cdc_call_mgmt_desc_t            cdc_mgmt_3;
+    usb_cdc_acm_desc_t                  cdc_acm_3;
+    usb_cdc_union_desc_t                cdc_union_3;
+    usb_endpoint_descriptor_t           comm_ep_3;
+    usb_interface_descriptor_t          data_3;
+    usb_endpoint_descriptor_t           data_eprx_3;
+    usb_endpoint_descriptor_t           data_eptx_3;
 } __attribute__((packed)) usb_device_configuration_descriptor_t;
 
 extern const usb_device_configuration_descriptor_t usb_configuration_descriptor;
diff -urN bluepill-serial-monster/.vscode/c_cpp_properties.json rct6-quad-cdc/.vscode/c_cpp_properties.json
--- bluepill-serial-monster/.vscode/c_cpp_properties.json	1970-01-01 09:00:00.000000000 +0900
+++ rct6-quad-cdc/.vscode/c_cpp_properties.json	2023-09-25 08:23:27.461681254 +0900
@@ -0,0 +1,20 @@
+{
+    "configurations": [
+        {
+            "name": "Linux",
+            "includePath": [
+                "${workspaceFolder}/**",
+                "~/stm32/STM32CubeF1/Drivers/STM32F1xx_HAL_Driver/Inc/**",
+                "~/stm32/STM32CubeF1/Drivers/CMSIS/Device/ST/STM32F1xx/Include/**",
+            ],
+            "defines": [
+                "STM32F103xE",
+            ],
+            "compilerPath": "/usr/bin/gcc",
+            "cStandard": "c17",
+            "cppStandard": "gnu++17",
+            "intelliSenseMode": "linux-gcc-x64"
+        }
+    ],
+    "version": 4
+}
\ No newline at end of file
diff -urN bluepill-serial-monster/.vscode/launch.json rct6-quad-cdc/.vscode/launch.json
--- bluepill-serial-monster/.vscode/launch.json	1970-01-01 09:00:00.000000000 +0900
+++ rct6-quad-cdc/.vscode/launch.json	2023-09-25 08:37:13.811685588 +0900
@@ -0,0 +1,15 @@
+{
+    "version": "0.2.0",
+    "configurations": [
+        {
+            "name": "Cortex Debug",
+            "cwd": "${workspaceRoot}",
+            "executable": "${workspaceRoot}/rct6-quad-cdc.elf",
+            "request": "launch",
+            "type": "cortex-debug",
+            "servertype": "external",
+            "gdbTarget": "192.168.0.250:3333",
+            "gdbPath": "gdb-multiarch",
+        }
+    ]
+}
\ No newline at end of file
パッチは以上です。あとはパッチをあててビルドすればOKです。
パッチファイルを~/stm32/triple-to-quad.diff として保存した場合、例えば以下のようにします。
cd ~/stm32/bluepill-serial-monster
patch -p1 < ../triple-to-quad.diff
make
make flash
パケットサイズを小さくしpmaアドレスセットをコンパクトにするなどしていますが、ポート追加でポイントとなるのは以下の2か所でした。
usb_descriptors.c

+     /*  CDC 3 Data Endpoint */
+    {
+        .type       = usb_endpoint_type_bulk,
+        .rx_size    = USB_CDC_DATA_3_ENDPOINT_SIZE,
+        .tx_size    = USB_CDC_DATA_3_ENDPOINT_SIZE,
+        .event_handler = usb_cdc_data_endpoint_event_handler,
+    },
+#if 1
+    /*  CDC 3 Interrupt Endpoint */
+    {
+        .type       = usb_endpoint_type_interrupt,
+        .rx_size    = 0,
+        .tx_size    = USB_CDC_INTERRUPT_ENDPOINT_SIZE,
+        .event_handler = 0,
+    },
+#endif

usb_descriptors.h

+    usb_endpoint_address_cdc_3_data         = 0x07,
+    usb_endpoint_address_cdc_3_interrupt    = 0x08,

それと、無効なcommand endpoint(ep_num 8、本来ならep_num 7)については、処理をスキップさせないと延々とUSBにメッセージを垂れ流しStatus LEDを点滅させてしまうので、強制的に処理をスキップさせました。
usb_core.c

+    if (ep_num != 8){
+        usb_endpoint_direction_t ep_direction = setup->wIndex & usb_endpoint_direction_in;
+        if ((setup->bRequest == usb_device_request_set_feature) ||
+            (setup->bRequest == usb_device_request_clear_feature)) {
+            uint8_t ep_stall = (setup->bRequest == usb_device_request_set_feature);
+            usb_endpoint_set_stall(ep_num, ep_direction, ep_stall);
+            return usb_status_ack;
+        } else if (setup->bRequest == usb_device_request_get_status) {
+            ((uint8_t*)(*payload))[0] = usb_endpoint_is_stalled(ep_num , ep_direction);
+            ((uint8_t*)(*payload))[1] = 0;
+            return usb_status_ack;
+        } else {
+            return usb_status_fail;
+        }
+    } 
 
command endpointでエラー処理通知などをするので、USB Serial Converterとしては、スキップすべきではない処理だと思います。なお、速度の変更やパリティーの変更等の通信設定はcommand endpoint経由ではないので、その点は問題はありません。
結論として言えば、Command Endpoint経由での処理が必要な場合は、今回のやり方はよくないが、簡易的にVCP/CDCを使うにはなんとかなるといったところでしょうか。
今回は以上です。それでは, Happy coding!!

コメント

このブログの人気の投稿

wsdd を使ってSamba サーバをネットワークに表示

Windows 10のアップデートで、セキュリティー対応のため、smbv1がデフォルトではインストールされなくなり、Samba serverがエクスプローラーのネットワークに表示されなくなってしまいました。そこで、いくつか方法を調べたのですが、linuxでwsdの実装がないか探したところ、 https://github.com/christgau/wsdd が、見つかりましたので、さっそくインストールしてみました。まだパッケージにはないようですが、インストール自身は簡単です。wsdd自体は以下のように取得し、linkを張っておきます。 cd /usr/local/bin/ sudo wget https://raw.githubusercontent.com/christgau/wsdd/master/src/wsdd.py sudo chmod 755 wsdd.py sudo ln -sf wsdd.py wsdd こちらのsambaサーバはDebianなので、/etc/systemd/system/wsdd.serviceは以下のようにしました。 [Unit] Description=Web Services Dynamic Discovery host daemon Requires=network-online.target After=network.target network-online.target multi-user.target [Service] Type=simple ExecStart=/usr/local/bin/wsdd -d MYDOMAIN [Install] WantedBy=multi-user.target wsdd -d MYDOMAINのところを、環境にあわせて書き換えてください。 次に、systemdに登録・起動テストを行います。 systemctl enable wsdd systemctl start wsdd 起動に成功すると、エクスプローラーのネットワークに表示されます。  なおこのwsddはpython3が必要です。一度試してみてください。SMBv1/CIFSを停止していても、大丈夫です。 cで書かれたほかのwsddの実装もあるようなので、いずれパッケージになるかもしれませ

Hyper-V Server2019にワークグループ環境下でWindows10(1809)から接続

Hyper-V server 2019に、ワークグループ環境にてWindows10(1809)から接続してみました。Windows10にHyper-V管理ツールがインストールされていることと、Hyper-V Serverをインストール済であることが前提です。以下、Hyper-V serverは名前がHyperVSV、アドレスは192.168.1.110としています。 まず、Hyper-V server上で、powershellを起動し、以下のコマンドを入力します。 Enable-WSManCredSSP -Role Server -Force 続いて、クライアントのWindows10のpowershell で以下のコマンドを入力します。 winrm quickconfig -Force Enable-WSManCredSSP -Role Client -DelegateComputer * -Force さらに、クライアントマシンで、gpedit(グループポリシーエディタ)を起動し、以下の要領でポリシーを設定します。 a. [コンピューターの構成]->[管理テンプレート]->[システム]->[資格情報の委任]->[NTLMのみのサーバー認証で新しい資格情報の委任を許可する] を有効にし、サーバを一覧に追加[表示...]ボタンをクリックして、「WSMAN/*」を追加 b. [コンピューターの構成]->[管理テンプレート]->[システム]->[資格情報の委任]->[NTLM のみのサーバー認証で保存された資格情報の委任を許可する] を有効にし、サーバを一覧に追加[表示...]ボタンをクリックして、「*」を追加 また、名前解決できるように、(notepadを管理者権限で実行し)C:\Windows\System32\Drivers\etc\hostsにサーバ名とIPアドレスの対を追加。 192.168.1.110 HyperVSV 最後に、Hyper-Vマネージャーを起動し、Windows10からHyper-V サーバに接続します。手順は以下の通りです。 「サーバーに接続」->コンピュータの選択->別のコンピューターに[HyperVSV]と入力し、[別のユーザーとして接続する

フレッツ光クロス:MAP-E ROUTER by Debian Box (iptables)

フレッツ光クロスがようやく開通したので、Debianにてrouterを構成し接続してみました。なお、プロバイダーを選ぶにあたっては、IPoE方式がそれぞれ異なるため検討したところ、IPoEでは、MAP-Eでもv6plusとocnバーチャルコネクトがあり、前者がポート数240なのに対し、後者は約4倍のポート数が使えるようなネットの情報をみて、OCNバーチャルコネクトを選択しました。(プロバイダーとしてはぷららです。なおDS-LiteはCE側でのNATではないので今回は見送りました。)そこで、OCN バーチャルコネクトをDebian(iptables)で実現するとどうなるかと思い、ネットの情報を頼りにしつつ、設定した次第です。 実際に試した結果、とりあえず通信できていますが、MAP-Eは本来マッピングルールをマップサーバから取得するはずなので、今回のやり方が正解とはいえませんし、仕様変更されると通信できなくなる可能性があります。あくまでも参考程度ですが、本稿をUPしてみました。 2023/03/16追記: こちら にゲームコンソールNAT越え(Nintendo Switch ナットタイプ A判定)対応版を投稿しました。 2023/03/28追記:※1の記述および3行無効化によりNAT越え(Nintendo Switch ナットタイプ B判定)できるようになりました。 構成は以下の通りです。 ルーターがDebianで回線がOCNバーチャルコネクトであること以外はなにも特別なところはない構成です。 さて、いきなり設定ですが、まず、割り当てられたプレフィックスを確認します。 確認は、 dhclient -6 -d -P enp2s0 とします。出力の中に 前略 RCV: | | X-- IAPREFIX 2400:4050:5c71:af00::/56 後略 このようにプレフィックスが表示されるので、その確認したプレフィックスを書き留めておきます。これを こちらで 入力します。すると、 CE: 2400:4050:5c71:af00:99:f171:c600:2f00 IPv4 アドレス: 153.241.113.198 ポート番号:(1776-1791 2800-2815 3824-3839) 4848-4863 5872-5887 6896-