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です。以下のようにしてビルドできるようにしておきます。
パッチファイルを~/stm32/triple-to-quad.diff として保存した場合、例えば以下のようにします。
結論として言えば、Command Endpoint経由での処理が必要な場合は、今回のやり方はよくないが、簡易的にVCP/CDCを使うにはなんとかなるといったところでしょうか。
今回は以上です。それでは, Happy coding!!
ちなみに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!!
コメント
コメントを投稿