tinyusb是一款MIT协议开源的跨平台USB host/device协议栈,专为嵌入式系统设计,各种常见的usb class都有实现,常见的或不常见的usb ip都有支持,比如像dwc2、musb、chipidea、stm32 fsdev等。更为友好的是它自带了大量的测试样例,也自带了各种常见MCU开发板的移植。当下支持usb device的MCU很常见,所以tinyusb在这些MCU上应用很广泛,由此一个常见的话题是如何评价目标MCU tinyusb的性能,在获得性能数据后如何提升性能。
一个比较好的测试场景是usb cdc loopback设备,所谓loopback就是把收到的数据再发回去,然后上位机通过c或者python写的程序与下位机的cdc交互,测量读写一大块数据的时间从而获得性能数据。此方法优点是:usb cdc设备可通用操作系统usb cdc驱动,所以我们不用再实现上位机操作系统下的驱动;另usb cdc是bulk传输,在usb设备无其他端点时,可以简单认为测得的cdc性能数据即MCU的usb性能数据。
测试用例usb cdc loopback的实现
如上所述,tinyusb自带大量的测试样例,我们通常不会从头开始写,而是基于测试样例改。tinyusb在examples/device/cdc_dual_ports/有一个双cdc端口的实现例子,原来的实现是把usb cdc端口收到的数据写到uart中去,为实现测试性能的usb cdc loopback, 笔者将其中的cdc_task函数修改如下:
staticvoidcdc_task(void) {for(uint8_titf =0; itf < CFG_TUD_CDC; itf++) {// connected() check for DTR bit// Most but not all terminal client set this when making connection// if ( tud_cdc_n_connected(itf) ){if(tud_cdc_n_available(itf)) {uint8_tbuf[64];uint32_tcount = tud_cdc_n_read(itf, buf,sizeof(buf));// echo backtud_cdc_n_write(itf, buf, count);}}}}
目标MCU和开发板
笔者选中基于STM32F103C8T6的小蓝板,板子非常流行且价格便宜,tinyusb也有现成的移植,hw/bsp/stm32f1/boards/stm32f103_bluepill
编译小蓝板固件和烧录
cdexamples/device/cdc_dual_portsmakeBOARD=stm32f103_bluepill
编译出的固件在_build/stm32f103_bluepill/cdc_dual_ports.bin,将其烧录到小蓝板即可,比如笔者使用openocd烧录
openocd-f interface/stlink.cfg -f target/stm32f1x.cfg -c"program _build/stm32f103_bluepill/cdc_dual_ports.bin verify reset exit 0x8000000"
openocd烧录固件的截图:
烧录完毕后板子自动重启,用usb线缆连接PC机与板子usb口,PC linux dmesg信息如下:
可以看到基于tinyusb的cdc acm设备已经被PC识别,接下来需要来个性能测试的上位机,为简单起见,直接python脚本吧。
上位机测试python程序
笔者使用的python程序代码如下:
#!/usr/bin/python3importsysimportserialfromtimeimportperf_counterdefformat_bytes(size):B = float(size)KB = float(1024)MB = float(KB **2)# 1,048,576ifB < KB:return'{0} {1}'.format(B,'B')elifKB <= B < MB:return'{0:.2f} KB'.format(B/KB)else:return'{0:.2f} MB'.format(B/MB)defmain():iflen(sys.argv) !=2:print("No port name specified")exit()ser = serial.Serial(sys.argv[1], baudrate=115200)data = bytes()foriinrange(4096):data = data + bytes(range(256))print("Testing port", sys.argv[1])t_start = perf_counter()ser.write(data)t_stop = perf_counter()print("Throughput:", format_bytes(4096*256/ (t_stop - t_start)) +"/s")if__name__ =="__main__":main()
测得的性能数据:
性能数据分析
这个数据可以算比较差的。STM32F103的usb特征是full speed usb device,理论速率12Mbps,考虑到协议负载等,理论上的传输性能大概在1MB/s上下。
性能优化措施
据笔者对于tinyusb源码分析发现tinyusb默认设置是比较保守的,追求资源占用最少。例如:默认编译
优化选项是”-Os“而非常见的"-O2"或"-O3", 且各buf size也是设置最小的。笔者在STM32F103平台采取
性能优化措施如下:
去掉cdc dual port中的一个
笔者只用一个cdc port,故而删除dual port中一个,以节省资源
编译”-Os“改成”-O2“
diff --git a/examples/build_system/make/toolchain/gcc_common.mk b/examples/build_system/make/toolchain/gcc_common.mkindex42fd01183..9364177a0100644--- a/examples/build_system/make/toolchain/gcc_common.mk+++ b/examples/build_system/make/toolchain/gcc_common.mk@@ -39,7+39,7@@ CFLAGS_CLANG += \# -Wconversion# Size Optimization as default-CFLAGS_OPTIMIZED ?= -Os+CFLAGS_OPTIMIZED ?= -O2# Debugging/Optimizationifeq ($(DEBUG),1)
增大CFG_TUD_CDC_EP_BUFSIZE与CFG_TUD_CDC_RX/TX_BUFSIZE
--- a/examples/device/cdc_dual_ports/src/tusb_config.h+++ b/examples/device/cdc_dual_ports/src/tusb_config.h@@-91,7+91,7@@#endif//------------- CLASS -------------//-#define CFG_TUD_CDC 2+#define CFG_TUD_CDC 1#define CFG_TUD_MSC 0#define CFG_TUD_HID 0#define CFG_TUD_MIDI 0@@-100,11+100,11@@#define CFG_TUD_CDC_NOTIFY 1// Enable use of notification endpoint// CDC FIFO size of TX and RX-#define CFG_TUD_CDC_RX_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64)-#define CFG_TUD_CDC_TX_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64)+#define CFG_TUD_CDC_RX_BUFSIZE 4096+#define CFG_TUD_CDC_TX_BUFSIZE 4096// CDC Endpoint transfer buffer size, more is faster-#define CFG_TUD_CDC_EP_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64)+#define CFG_TUD_CDC_EP_BUFSIZE 4096
再次编译烧录后性能测试如下
可以看到经笔者优化后,usb cdc基本达到期望的性能。
。
---------------------
作者:xhackerustc
链接:https://bbs.21ic.com/icview-3502794-1-1.html
来源:21ic.com
此文章已获得原创/原创奖标签,著作权归21ic所有,任何人未经允许禁止转载。