
STM32的USB中断是1ms执行一次,ST官方的代码并不立即将收到的数据发往USB主机,而是定义了一个接收缓冲,接收缓冲的存在就是避免漏接字符,在回调函数中5ms发送一次数据到USB主机,这个时间间隔使得STM32有足够的时间向USB主机传输数据。RS485的情况需另行考虑。注意这是在STM32中实现超时自动添加换行字符,由于Windows并非实时系统,想要在Windows上位机中实现这个功能,那将是几乎是不可能完成的任务。 * y: b! h: h0 ~1 r1 o, x 在ST官方例程Virtual_COM_Port中增加的函数如下 /******************************************************************************* * Function Name : TIMx_Base_Configration: k# D) D$ @0 X+ u9 H4 ]) l) A! p( d * Description : 定时器基础应用配置 * Input : TIMx,Period,TIM_Prescaler. E1 T9 U9 t w5 `- C * Output : None.5 X) u+ C# E- v# I8 B' [ * Return : None./ P; ^7 P! S$ S *******************************************************************************/ void TIMx_Base_Configration(TIM_TypeDef* TIMx, uint16_t Period, uint16_t TIM_Prescaler) { // TIM_Prescaler 72 分频则为1M, 计数加1为1us, 7200分频则为10K, 计数加1为100us- V! V5 L4 n8 L$ G TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; / Q0 Z2 [: J @6 P) S# K; A TIM_TimeBaseStructure.TIM_Period = Period - 1; // 定时器计时总数,最大655353 l1 N( ^1 l5 u; T0 j$ A7 J TIM_TimeBaseStructure.TIM_Prescaler = TIM_Prescaler - 1; // 定时器预分频 TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; // 定时器时钟输入分频: m) t& K/ j H8 w. { TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; // 向上计数模式 TIM_TimeBaseStructure.TIM_RepetitionCounter = 0;2 [' B4 |0 t2 e; P5 P \2 T TIM_TimeBaseInit(TIMx, &TIM_TimeBaseStructure); // 初始化定时器) W2 g6 K2 Z3 W! e9 [+ O( H" C9 C, t. C TIM_ClearFlag(TIMx, TIM_FLAG_Update); // 清除中断标志 TIM_ITConfig(TIMx, TIM_IT_Update, ENABLE); // 使能定时器中断4 t+ K, w9 z. U1 O) u. x$ ] $ r# K9 p2 r6 ~7 P9 t TIM_SetCounter(TIMx, 0); // 设置定时器初始值1 n Y1 z! z6 o TIM_Cmd(TIMx, ENABLE); 5 v/ Y! Q$ Z8 ~: o( M } & R5 H; g( K: ?' S % i0 R5 {1 t v8 P/ t8 P /* Enable TIM2 clocks */ RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); // 使能定时器2 TIMx_Base_Configration(TIM2, 10, 7200); // 设定周期为1ms7 a% e# u3 D+ P" x /******************************************************************************* * Function Name : EVAL_COM1_IRQHandler, G, ^# m, p# B5 h+ k1 R, I8 A * Description : This function handles EVAL_COM1 global interrupt request. * Input : None& Y0 N; m. S( {, t) h * Output : None' p+ z- I" f+ P- r, O * Return : None: |5 g# `$ i8 c0 J *******************************************************************************/' m U9 ]/ |7 a6 ?* i# o void EVAL_COM1_IRQHandler(void) { if (USART_GetITStatus(EVAL_COM1, USART_IT_RXNE) != RESET)- L- U6 w, n2 e4 U {; u- _% ^# u5 P; G /* Send the received data to the PC Host*/ USART_To_USB_Send_Data();/ K/ B( C0 O! P$ `; Q1 E2 B# V$ T /* 最后两个字节为0x0d,0x0a, 这是一个符合微软标准的换行字符5 M: l/ F9 ^% y& c! N# _ 如果最后两个字节不为0x0d,0x0a, 超时1毫秒则自动添加0x0d,0x0a */ last_char[0] = last_char[1];& ? Z' G! v) d! `. @ last_char[1] = USART_Rx_Buffer[USART_Rx_ptr_in-1]; if((last_char[0] == 0x0d)&&(last_char[1] == 0x0a)){ Flag_CR_CN = 0;% }8 [& `: u3 Z1 Y4 t TIM_Cmd(TIM2, DISABLE); ]' R. ^9 f3 N, a. c }else{ TIM_SetCounter(TIM2, 0); A$ [" @& b9 Y- \# C& x L TIM_Cmd(TIM2, ENABLE);' w) q6 K- {, C' P+ m0 q Flag_CR_CN = 1; }- a6 _1 R9 ~" G2 l3 ` } /* If overrun condition occurs, clear the ORE flag and recover communication */# x6 M2 }& ?, d7 } z( e if (USART_GetFlagStatus(EVAL_COM1, USART_FLAG_ORE) != RESET) { (void)USART_ReceiveData(EVAL_COM1);& p4 T2 w1 U, r8 H) Z& m" H }# k: o! I& b* j4 u/ k } /******************************************************************************* * Function Name : TIM2_IRQHandler 9 c g8 Z, Y0 \ P4 n% e/ {& a * Description : This function handles TIM2 global interrupt request. * Input : None! U) ?2 {2 Z2 P5 v: i * Output : None7 \ w$ L; y; m, W/ g( M0 x$ X6 \ * Return : None# ?: O9 Y" [7 C0 T' c6 a4 Z *******************************************************************************/% r! V' G. G) l. j void TIM2_IRQHandler(void) { TIM_Cmd(TIM2, DISABLE); TIM_ClearITPendingBit(TIM2, TIM_IT_Update);* ~$ T& e R3 g# K8 F9 x i4 Z if(Flag_CR_CN){2 q9 f* c+ e: G$ B& U( V! h: w Flag_CR_CN = 0;1 n. J+ l1 X& F USART_Rx_Buffer[USART_Rx_ptr_in] = 0x0d;1 }3 e5 `+ k; J) _ USART_Rx_ptr_in++;' M* Q* y+ M) S4 [/ j. D if(USART_Rx_ptr_in == USART_RX_DATA_SIZE) {' q$ S' | }' T# f, P, w. ~ USART_Rx_ptr_in = 0;! ~8 M: L4 K; i$ o }) }( v; M O1 Y s USART_Rx_Buffer[USART_Rx_ptr_in] = 0x0a; USART_Rx_ptr_in++; if(USART_Rx_ptr_in == USART_RX_DATA_SIZE)5 H" y) \2 }& O, C { USART_Rx_ptr_in = 0; }" Q( Z# C7 I1 s+ B9 i' s } }$ F7 c# d, o7 ?! W3 ]( W8 O: \ 1 D; c1 \) v, t1 Z6 ?2 h& f 基于ST官方原版例程Virtual_COM_Port修改,添加智能换行功能5 J$ k8 J# H" e% ]: r7 ` 下载地址: ![]() |
最全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