1. 开发环境
使用stm32f103c8t6,hal库,使用cube自带的usb库。工程使用vscode+gcc编译,工程文件在文末链接下载,提供makefile和keil两个版本。
2. 功能介绍
使用stm32 usb功能完成usb转串口功能,使用引脚配置如下:
使用usart1作为调试信息输出,usart2作为串口输出,led为系统指示。
3. cubemx 配置
设置系统时钟为72mhz,调试串口uasart1波特率为921600(选择高波特率,少占用中断时间),usart2波特率默认为115200,开启中断。
选择usb device功能,速度为默认全速usb设备12mhz,并使能usb_device库,选择virtual port com (虚拟串口,vpc),使用默认配置。
设置系统时钟为72mhz,然后生成工程。
4.软件部分
使用cube生成的代码编译下载后,将usb插入电脑,在电脑设备管理器中将显示新的串口设备(使用stm32的usb vpc时需要对应的驱动程序,驱动在程序也在文末的链接中)
在串口调试助手中,可打开或关闭串口,不过此时还没有任何功能。
4.1 发送函数
接下来进行功能配置,虚拟串口的主要配置代码在 src->usbd_cdc_if.c中,其中几个重要函数为:
static int8_t cdc_control_fs(uint8_t cmd, uint8_t* pbuf, uint16_t length);static int8_t cdc_receive_fs(uint8_t* buf, uint32_t *len);uint8_t cdc_control_fs(uint8_t* buf, uint16_t len);
uint8_t cdc_transmit_fs(uint8_t* buf, uint16_t len);函数用于向usb vpc发送数据,buf为待发送的数据缓冲区地址,len为待发送数据长度。
/*示例一,在main函数中的while循环中输入下列代码 在连接打开串口助手后可接收到数据*/while(1){ cdc_control_fs((uint8_t *)hello,6); hal_delay(1000);}
在编译下载后,将usb插入电脑,使用川酷哦调试助手将会每秒接收到一次hello。
4.2 usb上电重新枚举
在使用上面代码的时候存在一个问题:每次下载完程序后都需要重新拔插一次usb才可以识别串口,这是由于芯片在下载完程序后没有重新枚举所导致的。需要在设备上电后对usb进行重新枚举即可,使用方法为将usb dp(pa12)引脚拉低一段时间后即可。
/*usb 重新枚举函数*/void usb_reset(void){ gpio_inittypedef gpio_initstruct = {0}; __hal_rcc_gpioa_clk_enable(); gpio_initstruct.pin = gpio_pin_12; gpio_initstruct.mode = gpio_mode_output_pp; gpio_initstruct.pull = gpio_nopull; gpio_initstruct.speed = gpio_speed_freq_low; hal_gpio_init(gpioa, &gpio_initstruct); hal_gpio_writepin(gpioa,gpio_pin_12,gpio_pin_reset); hal_delay(100); hal_gpio_writepin(gpioa,gpio_pin_12,gpio_pin_set);}
该函数使用时应放在usb初始化之前,或者使用其他io控制三极管拉低电平。
/* configure the system clock */ systemclock_config(); /* user code begin sysinit */ usb_reset(); /* user code end sysinit */ /* initialize all configured peripherals */ mx_gpio_init(); mx_usart1_uart_init(); mx_usb_device_init(); mx_usart2_uart_init();
重新下载上电后,可发现串口已重新枚举识别,只需重新开启串口调试助手即可。
4.3 usb接收函数
static int8_t cdc_receive_fs(uint8_t* buf, uint32_t *len)为usb接收回调函数,在usb vpc接收到数据时,会进入该函数,在该函数中进行usb数据接收处理即可。
usb转串口设备,需要在stm32的usb端接收到数据后转发到stm32 串口端
static int8_t cdc_receive_fs(uint8_t* buf, uint32_t *len){ /* user code begin 6 */ extern uart_handletypedef huart2,huart1; /*将usb接收到的数据转发到usart2*/ hal_uart_transmit_it(&huart2,buf,*len); usbd_cdc_setrxbuffer(&husbdevicefs, &buf[0]); usbd_cdc_receivepacket(&husbdevicefs); return (usbd_ok); /* user code end 6 */}
在stm32虚拟的串口中发送数据,可在stm32的usart2的tx引脚(pa2)收接收到数据。
4.4 设置usb虚拟串口波特率
在前面的发送和接收中,均不能进行波特率设置,usb发送到串口的数据波特率为默认值115200。usb的波特率配置在static int8_t cdc_control_fs(uint8_t cmd, uint8_t* pbuf, uint16_t length)函数中,cmd为usb cdc的控制命令,pbuf为数据接收指针,length为数据长度。
当cmd为0x20时为设置虚拟串口波特率;
其接收到的数据一共七位,数据格式定义(小端模式)如下:
由于stm32芯片并不支持这么多串口参数,在无对应参数时使用默认配置。在接收到修改波特率命令后修改usart配置。
case cdc_set_line_coding:{ extern uart_handletypedef huart2; huart2.init.baudrate=*((uint32_t*)pbuf); switch (pbuf[4]) { case 2: huart2.init.stopbits=uart_stopbits_2; break; default: huart2.init.stopbits=uart_stopbits_1; break; } switch (pbuf[5]) { case 1: huart2.init.parity=uart_parity_odd; break; case 2: huart2.init.parity=uart_parity_even; break; default: huart2.init.parity=uart_parity_none; break; } huart2.init.wordlength=uart_wordlength_8b; hal_uart_init(&huart2);}break;
配置完成后,在串口调试助手中修改波特率,可该改变对应串口数据输出波特率,实测1.5m波特率可正常运行。
4.5 串口接收数据
在前面部分已经完成了usb转串口的发送部分,还有usb转串口的接收部分未完成。
该部分实现思路为在串口中断中接收数据,然后将数据发送至usb。
不过由于usb协议并不是实时发送,经过测试两次连续调用cdc_transmit_fs小于100us将导致数据丢包,.并且由于usb缓冲区大小原因,一次性发送或接收大量数据将会严重丢包。
故使用循环队列对发送接收数据进行缓冲,在发送和接收数据时先进入缓冲区,然后使用定每隔500us定时将缓冲区数据分包发送。
字节版GPTs“扣子”上线
荣耀10和OPPOR15买哪个好
出现“Duplicate Pin Name found on
金融科技公司黄金管家正在采用区块链技术杜绝黄金造假
无人机通信专网是如何设计的?
如何使用stm32 USB功能完成USB转串口功能
LED显示屏室内、户外、亚户外的区别
光纤收发器如何接入网
新时代对于传感器的要求是什么
接线端子使用中常见问题分析
应用于故障诊断系统测量中的下三种数字滤波方法研究
AI和特朗普这两件事紧密相连且备受关注
新款Wireless Gecko SoC帮助开发人员解决多协议IoT设计挑战
SPEA获ISO45001体系认证
基于开环CPS谐振器实现三阶CPS低通滤波器的设计
AI技术如何融合应用于工业物联网
长期现钞回收E5515C E5515B E5515A 896
16×16多波束相控阵天线的设计
SA615高性能低功耗混频器FM IF系统
广和通5G Sub6 GHz模组FG160系列亮相MWC 2022