
STM32的USB中断是1ms执行一次,ST官方的代码并不立即将收到的数据发往USB主机,而是定义了一个接收缓冲,接收缓冲的存在就是避免漏接字符,在回调函数中5ms发送一次数据到USB主机,这个时间间隔使得STM32有足够的时间向USB主机传输数据。RS485的情况需另行考虑。注意这是在STM32中实现超时自动添加换行字符,由于Windows并非实时系统,想要在Windows上位机中实现这个功能,那将是几乎是不可能完成的任务。 在ST官方例程Virtual_COM_Port中增加的函数如下: i+ p2 X v4 g$ x /******************************************************************************* * Function Name : TIMx_Base_Configration5 \% v0 `% @5 Q" k- b * Description : 定时器基础应用配置* M D* n1 i& p/ [ * Input : TIMx,Period,TIM_Prescaler * Output : None. * Return : None. *******************************************************************************/. u0 h5 ^8 `0 x void TIMx_Base_Configration(TIM_TypeDef* TIMx, uint16_t Period, uint16_t TIM_Prescaler). C9 ?0 ^& \5 `1 v- p {- a; y% x7 Y Z* v$ d _) v // TIM_Prescaler 72 分频则为1M, 计数加1为1us, 7200分频则为10K, 计数加1为100us TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;4 v" y5 R7 N3 { ; Z; l: T% N. Y4 \( S7 { TIM_TimeBaseStructure.TIM_Period = Period - 1; // 定时器计时总数,最大655355 e" a+ G1 W2 o TIM_TimeBaseStructure.TIM_Prescaler = TIM_Prescaler - 1; // 定时器预分频7 g" V1 S, Z6 ^- ]' B; c TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; // 定时器时钟输入分频 TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; // 向上计数模式 TIM_TimeBaseStructure.TIM_RepetitionCounter = 0; TIM_TimeBaseInit(TIMx, &TIM_TimeBaseStructure); // 初始化定时器" l/ q7 v# `0 K+ D2 L% e; ~. x , R9 i# ^5 Q5 t9 x `8 c. ? TIM_ClearFlag(TIMx, TIM_FLAG_Update); // 清除中断标志) H8 B0 _( ` G5 n7 m TIM_ITConfig(TIMx, TIM_IT_Update, ENABLE); // 使能定时器中断 + Q, O9 G: [6 @2 J1 u. A/ T TIM_SetCounter(TIMx, 0); // 设置定时器初始值 TIM_Cmd(TIMx, ENABLE); ; X1 [9 k' F2 X& T1 G6 |! g2 O. c v } 4 F0 `! v4 v: O , E6 y/ y( I( g6 b/ B: }$ j; m /* Enable TIM2 clocks */. w5 e0 i& A7 p3 x RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); // 使能定时器2 TIMx_Base_Configration(TIM2, 10, 7200); // 设定周期为1ms ( Q9 h7 E; h' k8 q" M4 x* z W! i+ | * j: Y* S5 }' o2 [ /*******************************************************************************; b/ U; t8 w% c" H: b4 W * Function Name : EVAL_COM1_IRQHandler * Description : This function handles EVAL_COM1 global interrupt request.% W/ P, F1 U8 N" }+ p3 a * Input : None4 P# z# e: A1 e! ] * Output : None * Return : None) t( n3 T8 y3 Y *******************************************************************************/. Z9 T! I9 C; M void EVAL_COM1_IRQHandler(void)1 w# ]; K V: m/ T u3 A { if (USART_GetITStatus(EVAL_COM1, USART_IT_RXNE) != RESET) { /* Send the received data to the PC Host*/ USART_To_USB_Send_Data();, ?) g, e4 c6 W; S K /*& P& M; V" X1 N2 m 最后两个字节为0x0d,0x0a, 这是一个符合微软标准的换行字符 如果最后两个字节不为0x0d,0x0a, 超时1毫秒则自动添加0x0d,0x0a' O' K% K% f* K) t* m# S */9 r, f. {: P- v m: x: U last_char[0] = last_char[1]; last_char[1] = USART_Rx_Buffer[USART_Rx_ptr_in-1];; Q3 n. d A5 H( ?1 N if((last_char[0] == 0x0d)&&(last_char[1] == 0x0a)){ Flag_CR_CN = 0;- W% g; k) A4 O$ `; w- z TIM_Cmd(TIM2, DISABLE); }else{ TIM_SetCounter(TIM2, 0);- x( k' y5 d0 K TIM_Cmd(TIM2, ENABLE);* t' l' w- E9 t) s" }2 p9 p Flag_CR_CN = 1;* K# z) B8 S3 r }, k/ ^& e. V+ }3 i; S } Y* n& g1 [5 i0 S% { * ~+ U; o3 A5 s' {8 G t/ ^2 P# g5 g, C, { /* If overrun condition occurs, clear the ORE flag and recover communication */ if (USART_GetFlagStatus(EVAL_COM1, USART_FLAG_ORE) != RESET) { (void)USART_ReceiveData(EVAL_COM1); } }5 @1 J) Y3 }, f6 R /*******************************************************************************; B; b D0 l# P5 T& F * Function Name : TIM2_IRQHandler * Description : This function handles TIM2 global interrupt request.2 _% V5 [- u' Z1 {3 K * Input : None * Output : None * Return : None: r3 _1 m/ H, L6 K' d0 Z. g0 a *******************************************************************************/ - t" P, u3 d, {5 H void TIM2_IRQHandler(void)5 ^- |4 T9 r) w1 r {) Y: S, M1 Y `1 S* B) G F1 k TIM_Cmd(TIM2, DISABLE); TIM_ClearITPendingBit(TIM2, TIM_IT_Update); if(Flag_CR_CN){ Flag_CR_CN = 0; USART_Rx_Buffer[USART_Rx_ptr_in] = 0x0d; USART_Rx_ptr_in++; if(USART_Rx_ptr_in == USART_RX_DATA_SIZE)" b. h% y+ d* o# v {! p( e5 ^( R( o( p USART_Rx_ptr_in = 0;/ N2 o) L7 `, ?0 E } USART_Rx_Buffer[USART_Rx_ptr_in] = 0x0a; USART_Rx_ptr_in++;" d. M+ D8 g. R6 N if(USART_Rx_ptr_in == USART_RX_DATA_SIZE)0 j% ?2 q; W1 ]! j1 v { USART_Rx_ptr_in = 0;- D' s8 u0 M6 y# p# S; l }( c/ c1 @4 t9 e }) _4 W& v* v( i" `: S# L1 p }0 S$ e7 L9 r. p* N: m7 R; O ! u9 c6 S: U9 C6 i$ [5 I 基于ST官方原版例程Virtual_COM_Port修改,添加智能换行功能 下载地址: ![]() |
最全USB HID开发资料,悉心整理一个月,亲自测试
实战经验 | 选择USBX模块生成USB CDC ACM无PD的项目
STM32 USB HID键盘例程
刘氓兔的杂谈【001】-片上USB 高速PHY
【经验分享】在进行 USB CDC 类开发时,无法发送 64整数倍的数据
【源码】STLINK-V3MINI 高速USB仿真器,成功改刷【高速CMSIS-DAP】
在线直播|无需编写任何代码即可在STM32上实现USB-C Power Delivery
STM32 USB CDC 虚拟多串口
圈圈发布USB图书第二版有感,以及分享一些我学习USB过程...
USB Audio设计与实现
可以,上拉D+为全速设备,上拉D-为低速设备,要作为高速设备使用时,才需要使用三极管控制上拉
回复:【MCU实战经验】+ 利用ST官方STM32_USB-FS-Device_Lib_V4.0.0例程实现USB-TO-TTL232的数据包添加智能换行的功能
将64字节的定义改为63字节,可以解决64字节不响应的bug,以上所有例程都有这个bug
可以解决64字节不响应的bug,以上所有例程都有这个bug