
STM32的USB中断是1ms执行一次,ST官方的代码并不立即将收到的数据发往USB主机,而是定义了一个接收缓冲,接收缓冲的存在就是避免漏接字符,在回调函数中5ms发送一次数据到USB主机,这个时间间隔使得STM32有足够的时间向USB主机传输数据。RS485的情况需另行考虑。注意这是在STM32中实现超时自动添加换行字符,由于Windows并非实时系统,想要在Windows上位机中实现这个功能,那将是几乎是不可能完成的任务。 S {7 Y* d% L7 `2 l 5 k- B8 U! e- [# M, B! O X* S 在ST官方例程Virtual_COM_Port中增加的函数如下9 w1 G* Q+ R m; |6 R: j /******************************************************************************* * Function Name : TIMx_Base_Configration * Description : 定时器基础应用配置 * Input : TIMx,Period,TIM_Prescaler+ b, f* p; ^1 |8 Y. A * Output : None. * Return : None. *******************************************************************************/6 M0 \, S# b3 c& P1 _ void TIMx_Base_Configration(TIM_TypeDef* TIMx, uint16_t Period, uint16_t TIM_Prescaler)( f7 l; M+ x9 M3 U. F- B, g1 V {$ e3 j: l* L9 c( s- | // TIM_Prescaler 72 分频则为1M, 计数加1为1us, 7200分频则为10K, 计数加1为100us) [8 u4 p' k) @ TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; , Q& V' b4 r; W# v9 L6 ~6 n3 P1 z TIM_TimeBaseStructure.TIM_Period = Period - 1; // 定时器计时总数,最大65535 TIM_TimeBaseStructure.TIM_Prescaler = TIM_Prescaler - 1; // 定时器预分频5 u; {: c1 Q4 m! m3 M$ C TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; // 定时器时钟输入分频 TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; // 向上计数模式& J6 ?# [& j7 S: W3 j TIM_TimeBaseStructure.TIM_RepetitionCounter = 0;: C& {# U- F" c& L0 H( r \ TIM_TimeBaseInit(TIMx, &TIM_TimeBaseStructure); // 初始化定时器 TIM_ClearFlag(TIMx, TIM_FLAG_Update); // 清除中断标志; u' f& ~& @3 p TIM_ITConfig(TIMx, TIM_IT_Update, ENABLE); // 使能定时器中断 3 ]# n9 G+ x' l g9 a1 _* S TIM_SetCounter(TIMx, 0); // 设置定时器初始值 TIM_Cmd(TIMx, ENABLE); / z. p9 |5 `2 B) B9 l } & l4 X& s! _3 h4 w j /* Enable TIM2 clocks */ RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); // 使能定时器2; H, b6 f- X$ G: T TIMx_Base_Configration(TIM2, 10, 7200); // 设定周期为1ms ' p! h5 l1 P0 m4 p2 F2 o6 d /******************************************************************************* * Function Name : EVAL_COM1_IRQHandler; B9 K# C9 L( U) r) B, M5 D * Description : This function handles EVAL_COM1 global interrupt request.0 o, F. V' G& s% G& c& a" ~ * Input : None$ h3 y8 B, S3 U* ? * Output : None * Return : None *******************************************************************************/ void EVAL_COM1_IRQHandler(void) { if (USART_GetITStatus(EVAL_COM1, USART_IT_RXNE) != RESET)3 l* L/ X2 U) q/ K! k& V) d { /* Send the received data to the PC Host*/ USART_To_USB_Send_Data();! ^' o9 @! f7 C7 x1 d& O, b; A , G1 C6 ^8 D& B /*' V+ G+ @6 ~0 b) _5 A 最后两个字节为0x0d,0x0a, 这是一个符合微软标准的换行字符 如果最后两个字节不为0x0d,0x0a, 超时1毫秒则自动添加0x0d,0x0a */ last_char[0] = last_char[1]; last_char[1] = USART_Rx_Buffer[USART_Rx_ptr_in-1]; if((last_char[0] == 0x0d)&&(last_char[1] == 0x0a)){ Flag_CR_CN = 0;: ]1 m# M$ \5 l# _! m. t2 } TIM_Cmd(TIM2, DISABLE); }else{, s2 J; R. l0 k9 k+ v' {5 q TIM_SetCounter(TIM2, 0); TIM_Cmd(TIM2, ENABLE); Flag_CR_CN = 1; }, W; b1 ~: U$ A: A2 a5 \ } 3 o' X# i! r% D /* If overrun condition occurs, clear the ORE flag and recover communication */ if (USART_GetFlagStatus(EVAL_COM1, USART_FLAG_ORE) != RESET)8 Y6 e. C& V# V, Y) ~& _! X4 W* Q' ` { (void)USART_ReceiveData(EVAL_COM1);0 I4 ?6 `( K8 |3 Z# v5 g }1 c) s0 J: z4 S6 N: |1 w$ l6 V- v/ ` } /*******************************************************************************0 `3 q+ }3 T x) W2 i * Function Name : TIM2_IRQHandler * Description : This function handles TIM2 global interrupt request.. v0 x( D( f& [0 Y2 v# l! o! R! g/ H# { * Input : None * Output : None * Return : None0 D2 o) c& j4 f *******************************************************************************/# W' |; k7 {- `% n; j % P0 l, g7 n/ c1 U3 v# W void TIM2_IRQHandler(void)3 I d' q9 b- P4 q {. V K0 A1 U4 R6 D TIM_Cmd(TIM2, DISABLE);. v: R# t, {; Y) ? TIM_ClearITPendingBit(TIM2, TIM_IT_Update); if(Flag_CR_CN){+ |$ V0 N. T3 G6 j& V+ a: \$ i Flag_CR_CN = 0; 8 I* ^- i5 ~) {1 _ USART_Rx_Buffer[USART_Rx_ptr_in] = 0x0d;0 x& F* @% W! [, d- M9 x) c& b USART_Rx_ptr_in++;4 Z* s4 D) r* J4 [6 `8 N( ~ if(USART_Rx_ptr_in == USART_RX_DATA_SIZE)9 R7 s N6 i B( o* u {0 V9 n6 K2 m: r+ |( M6 w+ H USART_Rx_ptr_in = 0; }' q+ G- r3 i( W1 C+ ` USART_Rx_Buffer[USART_Rx_ptr_in] = 0x0a; USART_Rx_ptr_in++;9 l- R/ I* k; Y! y( O if(USART_Rx_ptr_in == USART_RX_DATA_SIZE), q' [2 \, Q! v0 A {& V# a' |' t8 I% ]$ r: [8 \; W( U USART_Rx_ptr_in = 0;: x& X6 d) N1 ]4 _: b } }& K3 E7 ]9 x5 f0 @- N( r/ I } ' F0 `& V# k; ?8 c# J 基于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