目录:
第1部分:参照“正点原子USB虚拟串口工程移植步骤”移植ST的USB HID工程(失败了);
第2部分:在1的基础上,替换USB HID初始化代码为ST 例程中的代码,编译后根据报错调试(失败了);
第3部分:直接移植ST的USB HID工程,根据报错调试代码(成功了)。
小孙想要总结这一年来学到的关于stm32的USB相关知识,但又不知道怎么总结,于是决定
从头
开始调试固件库代码,直到实现USB功能为止!
首先准备参照正点原子《第88讲 USB虚拟串口实验-M3》,把HID相关库包含进工程中,工
程选用
正点原子的USART试验。
因为目前手里的开发板是“微雪电子”的stm32f103cbt6开发板,
其
硬件串口为:USART1(PA9和PA10)和
SART2(PA2和PA3)。准备先把正点原子《实验4-串口
实验》实例代码移植
到这款开发板上,好方便以后调试使用。
因为正点原子stm32f1开发板使用的晶振和手里的开发板一样都是8M的,所以在Keil里直接修
改MCU
型号和Flash大小后准备编译下载。没有报错。
查看代码中串口初始化函数,发现代码中如下语句:
是使用串口1,于是把开发板上的USART1插上USB转
串口模块。准备下载后调试。
插上模块后开始下载,结果发现不能下载,报错信息如下图所示:
猜测可能是串口模块干扰到SWD下载,于是把串口模块拔掉。结果发现开发板没电了。
也就是说,刚刚是靠串口模块供电的。即刚刚也可能是MCU供电不足导致无法下载。于是
先拨动开关,用外接电源给开发板供电,
然后再重新烧录,结果一次就成功了。
下面先把USB转串口模块插回开发板的USART1,测试串口功能是否正常。
打开串口调试工具,选择串口后,点击“打开串口”,串口调试助手就开始接受数据了。
也就是说,正点原子的stm32f1串口代码只要在Keil中修改MCU和flash大小后,可以在微雪
开发板上直接使用。
接下来就要准备开始USB相关的工程了。
《第1部分:
参照“正点原子USB虚拟串口工程移植步骤”移植ST的USB HID工程(失败了)
》
这里准备大致分为四布调试:
第一步:了解正点原子的USB虚拟串口实现步骤(不需要记得,大致看一下,需要时再
回头仔细看);
第二步:下载正点原子的USB虚拟串口代码到微雪开发板,看是否能正常工作。如果能
正常工作,则可以轻松地执行第三步;否则需要先查找原因,直到可以正常工作时,再执
行第三步;
第三步:不管第二部分是否正常工作,都需要亲自按照第一步的步骤重新移植一遍,为
移植HID做准备;
第四步:参照第三步的步骤,移植ST官方库中的HID代码到微雪开发板。
---------------
第一步: (不想看可以先跳过去,直接看第二步)
----------
正点原子视频教程中USB虚拟串口实现步骤如下:
正点原子f1开发板中USB硬件用的是PA11和PA12。微雪电子开发板中用的也是PA11
和PA12。查看f103收据手册发现,这款MCU就只有这一个USB接口,所以不用担心搞错
接口了。正点原子和微雪开发板上都是在USB_D+上接1.5k上拉电阻,说明都定义此USB
是高速设备。
下面是《第88讲 USB虚拟串口试验-M3》PPT中移植ST官方的Virtual_COM_Port例程的过程:
《stm32f1开发指南-库函数版本》中USB虚拟串口的实现步骤如下:
---------------
第一步结束
------------------------------------------------------------------------
---------------
第二步:
---------------------------------------------------------------------------
好了,下面是第二部分,直接下载正点原子开发板的程序,看是否能正常用,如果能
正常工作,则可以放心的执行第三步;否则,需要先查找到原因后再执行第三步。
为了避免液晶和串口功能的干扰,我们先屏蔽掉代码中的液晶和
串口部分的功能。
下载后实现效果如下:
很幸运,微雪开发板可以直接实现虚拟串口功能,可以直接进入第三步了。
(注:代码见附件:第二步:实验48 USB虚拟串口实验 _微雪开发板)
--------------- 第二步结束---------------------------------------------------------------------------
---------------
第三步:
-----------------------------------------------------------------------------
亲自按照第一步的步骤重新移植一遍虚拟串口的代码
移植后编译,结果
报错如下:
在第二步和第三步中的程序中分别查找“ EXTI_ClearITPendingBit”和“EXTI_Init”两个变量。
第二步程序中搜索结果:
第三步程序中搜索结果:
从上面结果看,第三步中工程缺少库文件:stm32f10x_exti.c。于是在Keil中把这个文件包含进去,
重新编译,结果如下:
把代码下载到微雪电子开发板中,希望能直接成功。
结果如下:
看来电脑识别到USB虚拟串口了。
(注:代码见附件:第三步:实验4 串口实验 _修改为VCP.rar)
--------------- 第三步结束---------------------------------------------------------------------
--------------- 第四步---------------------------------------------------------------------------
移植Custom_HID代码到正点原子的串口代码中,希望一切顺利。
参照PPT《第88讲 USB虚拟串口实验-M3》,开始进行下列移植:
1.打开ST官方的Custom_HID例程,如下图所示
2.移植USB通信需要的从机驱动代码;
3.打开USUART通信实验,拷贝USB从机驱动代码;
4.打开
USART试验工程,添加USB相关代码,如下图所示:
突然发现PPT中有下列一句话:
④, 修改usb_endp.c。
原来第三步中刚开始调试报错,是因为没看到这里的"添加stm32f10x_exti.c"。
还是太粗心了。
5.编译代码,根据报错提示,修改相关内容。
①, platform_config.h, include部分, 使用如下代码替代:
#include "sys.h"
另外, 去掉#define USE_STM32303C_EVAL等宏定义, 无需使用
②,修改hw_config.c。
2.1,去掉stm32_it.h,并添加一些其他头文件,如下:
#include "usb_lib.h" #include "usb_prop.h" #include "usb_desc.h" #include "usb_istr.h" #include "hw_config.h" #include "usb_pwr.h" #include "usart.h" #include "string.h" #include "stdarg.h" #include "stdio.h"
因为HID例程中此处没有stm32_it.h,所以直接替换成上面的代码。
2.2,去掉HSEStartUpStatus和EXTI_InitStructure等结构体和变量的定义,
, 采用如下代码替代 :
因为替换的代码是串口相关的,所以此时不知道是否应该替换了!!!
那就先编译一下,根据报错提示修改看看是否能直接运行。
编译结果如下:
发现有很多错误,是跟评估板硬件相关的内容,应该是哪个宏定义没正确使用。
这样一个一个修改太麻烦了,还是先根据PPT《第88讲 USB虚拟串口实验-M3》,
接着把" 能修改的部分 "修改了再根据情况调试。注:2.2这节暂时不修改了。
2.3,删掉Set_System函数
并添加USBWakeUp_IRQHandler和USB_LP_
CAN1_RX0_IRQHandler函数(官方例程是在stm32_it.c里面, 我们将其移到
这里) , 代码如下
//USB唤醒中断服务函数 void USBWakeUp_IRQHandler(void) { EXTI_ClearITPendingBit(EXTI_Line18);//清除USB唤醒中断挂起位 } //USB中断处理函数 void USB_LP_CAN1_RX0_IRQHandler(void) { USB_Istr(); }
2.4,修改Set_USBClock函数为:
2.5,修改Enter_LowPowerMode函数为:
2.6,修改Leave_LowPowerMode函数为:
2.7,修改USB_Interrupts_Config函数为:
2.8,修改USB_Cable_Config函数为:
2.9,删除USART_Config_Default函数,新增USB_Port_Set函数,代码如下:
因为当前例程中没有虚拟串口,所以不用删除,直接添加USB_Port_Set函数即可。
2.10,修改USART_Config函数为:
(和串口相关,不用管):
2.11,修改USB_To_USART_Send_Data函数为
:
(和串口相关,不用管):
2.12,
删除Handle_USBAsynchXfer和USART_To_USB_Send_Data这两个
函数, 然后, 新增USB_USART_SendData函数, 用于虚拟串口发送一个字节
到USB(这里实际上只写到了发送FIFO, 最终还是由EP1_IN_Callback函数实
现输出给USB):
(和串口相关,不用管):
2.13, 删除IntToUnicode函数前面的static关键字,
新增usb_printf函数, 用于
实现USB虚拟串口的printf, 代码如下: (和串口相关,不用管):
2.14, 修改hw_config.h, 删除MASS_MEMORY_START等宏定义, 然后, 新
增USB_USART_TXFIFO_SIZE等宏定义和结构体, 代码如下:
(和串口相关,不用管):
2.15, 修改hw_config.h, 新增IntToUnicode、
USB_Port_Set
USB_USART_SendData、
和usb_printf等函数的声明, 代码如下: (和串口相关,不用管):
4.
1, 修改EP1_IN_Callback函数为: (和串口相关,不用管):
4.2, 修改SOF_Callback函数为:
(工程中没有这个函数的定义,直接从虚拟串口的工程中复制过来):
⑤, 修改usb_prop.c。 本例程没有用到USART_Config_Default函数, 所以:
注释掉Virtual_Com_Port_init函数里面对该函数的调用
:(工程中没有这个函数,不用管):
⑥, 修改usb_pwr.c。 修改Suspend函数为:
6, 修改main.c。
因为虚拟串口例程中的main.c里面执行的是串口收发,所以这里只用其初始化部分,while(1)
里面什么也不做。结果编译后报错如下:
还是很多宏定义没有实现。接下来需要找到他们,定义他们或者删除他们。
第一个报错:
RCC_APB2Periph_GPIO_DISCONNECT未定义。定位到函数如下:
void GPIO_Configuration(void)
但是程序中并未调用它,所以可以直接屏蔽掉。
重新编译后,第一个报错:
KEY_BUTTON_EXTI_LINE未定义。定位到函数如下:
void EXTI_Configuration(void)
重新编译后,第一个报错:
ADC1_DR_Address未定义。定位到函数如下:
void ADC_Configuration(void)
但是程序中并未调用它,所以可以直接屏蔽掉。
重新编译后,第一个报错:
LED1未定义。定位到函数如下:
void EP1_OUT_Callback(void)
自定义,于是把里面原来开发板上的功能(主要是开关LED)屏蔽掉。只接收数据,
不做处理。
重新编译后,第一个报错:LED1未定义。定位到函数如下:
第一个报错:还是
LED1未定义。定位到函数如下:
void CustomHID_Status_In(void)
该函数的功能是:状态输入。虽然不知道具体是做什么的,但是根据网上看过
的HID程序资料,这个函数不能删除。于是只屏蔽掉里面关于LED等信息的语句。
重新编译后,只有3个错误了:
第一个报错: bDeviceState 未定义。定位到函数如下:
void SOF_Callback(void)
关于这个函数,网上找到下列解释:
函数SOF_Callback定时查询用户是否有要发送的数据,如果有则进行发送,
在
发送完成后会触发发送中断EP1_IN_Callback函数,如果发送完毕就不调用
SetEPTxValid(ENDP1)函数,发送完成后就不会再触发EP1_IN_Callback函数。
所以就不删除了。
bDeviceState在代码中肯定是定义过了的,应该是被屏蔽了。
搜索变量
bDeviceState,看它的定义在什么地方,为什么
被屏蔽了。结果如下:
看来确实定义过,还在头文件中声明过可以外部调用。那应该是这个头文件
没有被usb_endp.c包含导致的。打开usb_endp.c查看,果然没有这个头文件。
加上后,编译结果如下:
很多关于usb_prop.h的错误,这个是才加进去的头文件,却导致个能多错误,
应该是加的位置不对。打开看到usb_endp.c开头只有几个头文件:
#include "hw_config.h" #include "usb_lib.h" #include "usb_istr.h" #include "usb_prop.h"
干脆直接把USB虚拟串口相同文件中包含的头文件都包含
进来编译看看。结果如下:
老规矩,全局搜索变量“
VCOMPORT_IN_FRAME_INTERVAL”,结果如下:
竟然发现代码中真的没有定义过。因为这个代码是从USB虚拟串口拷贝过来的,在
USB虚拟串口代码中全局搜索,结果如下:
直接在usb_endp.c开头加上这个宏定义,重新编译结果如下:
直接点击错误信息,不能跳转,
全局搜索PrevXferComplete,结果如下:
发现竟然真的没有定义。这部分是ST官方库中的代码,直接打开ST的HID工程,
全局搜索,发现是在main.c开始位置定义的。于是我们也在main.c处定义这个变量,
编译后结果如下:
ADC_SoftwareStartConvCmd好像是和串口有关,但没有直接证据,全局搜索发现
也就是说,这个函数只有声明,没有定义。
同理,在ST官方库中HID工程中搜索,结果如下:
原来,这个函数在adc库文件中。而我的工程还没有添加这个库文件。
添加后编译,结果如下:
没有报错,很好,但是下载后结果如何还不知道,因为刚刚在ST的HID工程中看到main函数
初始化时的步骤和这里从USB虚拟串口移植过来的不一样。不过还是先试一下看看。
开始下载......
微雪开发板上电后结果如下:
太神奇了,虽然初始化代码和ST工程不一样,但竟然一下子就识别成HID了!!!
趁热打铁,接下来就是实现HID发送和接收功能,否则小孙的三分钟热性一旦消失,就要再
耗费半天才能继续下去了。
刚才细看USB虚拟串口代码,发现其发送和接收功能其实和网上的HID类似,都是调用
UserToPMABufferCopy()和
PMAToUserBufferCopy()函数。而这两个函数分别有另一个函
数调用,即
USB_SIL_Write()和
USB_SIL_Read()。在ST的HID工程中也有这两个函数,这
里先实现他们看看是否有效果。
添加上面的代码,编译下载后结果如下:
一直没有数据发送上来。看来需要让开发板在调试模式运行,看是否是卡在哪里了。
结果调试模式下发现,代码一直在循环执行,没有卡顿问题。就是说,发送代码执行了,
但是数据没有发送出去。
打开USB的配置描述符发现,默认最大发送长度为2字节,而小孙的发送函数一次发送
3字节,需要修改一下。
修改之后,编译下载,结果还是和上面一样,不能发送数据。
把单片机多功能调试助手打开,打开开发板的端口检测。结果仍然没有数据发送。
把串口初始化代码屏蔽掉时,PC端反而不能识别HID了。
分析uart_init()函数,没发现什么特别功能的语句。屏蔽掉它,继续调试。
但是目前进度卡住了,不知道怎么向下调试了。
这种情况,一年前刚刚学习stm32 USB时就遇到过,后来是别人替小孙
调好的。
当时小孙调了接近1个月,都没有正常收发数据,后来觉得自己短时间内调
不出来
了跟领导说让其重
新招人,自己准备辞职的。后来是领导花两三天帮他调试出来的。
虽然当时已经有两年半工作经验了,但那几天都在想自己是否真的适合走嵌入式这条路???
因为没有刨根问底的精神,小孙这几年的电子生涯一直是闭着眼睛瞎混的,没有
好好的钻研让自己拿得出手的技术,所以每天都生活在自卑和失落中。
综上所述,在屏蔽掉uart_init()函数后,程序并不能被PC识别为HID设备。
《第2部分:在1的基础上,替换USB HID初始化代码为ST 例程中的代码,编译后根据报错调试(失败了)》
俗话说,人不能让不撞南墙不回头,这里是装了南墙必须回头,否则没有哪个
老板愿意给你薪水。所以,小孙准备从ST的HID工程本身提供的初始化HID方式
尝试实现HID功能。毕竟,网上大多数HID工程初始化部分都和ST的HID部分类似,
而跟他上面自己移植的不一样。
****************** 根据ST的HID修改工程代码 ***********************
预想中的修改步骤如下:
第一步:先用HID工程中的初始化代码替换上面工程中的初始化代码;
第二步:根据报错,修改上面步骤中被修改过的相关函数,使HID初始化成功;
第三步:添加HID发送和接收函数,并实现功能;
---------------
第一步:
-----------------------------------------------------------------------------
移植ST的HID工程初始化代码后编译,结果如下:
发现报错中提到的函数是上面在hw_config.c中屏蔽掉的,于是释放开。重新编译后
报错如下:
发现其中大都是跟具体硬件相关的。
全局搜索第一处的未定义变量,发现在platform_config.h中有两处根据开发板
类型分别定义的
变量。
在STM3210E_EVAL开发板原理图上发现,上面的GPIO_DISCONNECT是
指接在USBDP上的上拉电阻控制线。
因为微雪电子开发板上USBPD上的上拉电阻通过三极管直接拉高了,所以这里
不用管,直接屏蔽掉。还有下面的配置USB_DISCONNECT_PIN的
GPIO_Configuration函数也一起屏蔽掉,再编译,结果如下:
全局搜索“RCC_AHBPeriph_GPIOA”,结果如下:
并没有找到其定义,在ST的HID官方库中搜索结果如下:
即,其定义在stm32f30x_rcc.h中,文件对应MCU型号不对。在stm32f10x_rcc.h中
搜索GPIOA的时钟外设对应的变量,有如下发现:
用RCC_APB2Periph_GPIOA替代上面的RCC_AHBPeriph_GPIOA,重新编译结果如下:
下载后,PC端显示如下:
调到这一步,小孙已经失去耐心了,不愿意在USB HID的工程上花时间调试了。因为网上的HID
都是从stm32手柄工程修改过来的,准备第二天换成JoyStickMouse工程,从上面开始调试。
------------------------------------------------------ 两天后 ------------------------------------------------------------
《第3部分:直接移植ST的USB HID工程,根据报错调试代码(成功了)》
小孙经过重新思考,准备重新从ST的HID库直接修改,而不是先参考正点原子USB虚拟串口,把里面
的相关函数修改了。如果还是不成功,再参考网上从“ JoyStickMouse”修改。
把ST的HID代码添加到正点原子的串口实验代码中,修改main.c中初始化语句为ST的HID代码,
屏蔽掉原串口代码并编译。
结果如下:
缺少对开发板的宏定义,这里直接用“#include "stm32f10x.h"”替换。编译结果如下:
“usb_type.h”在移植过来的USB库中,直接用"
#include "../STM32_USB-FS-Device_Driver/inc/usb_type.h"
"包含进来。
重新编译后结果为:
仿照上面操作,添加把“usb_lib.h”包含进来,重新编译后结果为:
还有下列文件需要包含这些头文件,一并处理了。
stm32_it.c,hw_config.c,usb_desc.c,usb_pwr.c,usb_endp.c,usb_prop.c
还有STM32_USB-FS-Device_Device\sr\usb_core.c,usb_init.c,usb_int.c,
usb_regs.c,usb_mem.c,usb_sil.c需要用“../inc/usb_lib.h”包含“usb_lib.h”文件。
编译后报错:
说明上面添加的头文件正确。接下来需要屏蔽掉与具体硬件相关的代码。定位到
“KEY_BUTTON_EXTI_LINE”,如下:
这个是开发板的按键中断处理函数,直接屏蔽掉。
重新编译,报错如下:
第一个报错:
RCC_APB2Periph_GPIO_DISCONNECT在上面看过了,是
USBDP的上拉电阻,微雪开发板不需要上拉电阻,是直接拉高的,屏蔽掉。
还有,stm32f103的usb管脚不需要配置,当设置好usb时钟后,直接就用作usb功能了。
Set_System函数中把他们
屏蔽掉,还有按键,led,adc功能都屏蔽掉。
这样,Set_System()函数的内容都屏蔽掉了。
重新编译,报错如下:
第一个报错“STM_EVAL_LEDOn”在函数CustomHID_Status_In()"中,其功能是
控制LED的,直接屏蔽掉内容,保留外壳。
重新编译,报错如下:
根据上面信息,这里直接屏蔽GPIO相关的函数内容,保留外壳,重新编译报错信息如下:
还是按键灯功能,直接屏蔽函数内容,保留外壳。
重新编译,报错如下:
屏蔽掉部分函数体,保留外壳及部分功能,因为这个函数中需要把PC发来的数据接收下来,
以后才能继续接收数据。
编译结果如下:
移植来的stm32_it.c里面有很多中断处理函数和stm32f10x_it.c重复,直接屏蔽掉重复部分。
重新编译,报错信息如下:
主要是hw_config.c、stm32_it.c,和usb_prop.cADC和DMA功能,直接屏蔽掉。
至于EXTI_ClearITPendingBit,如下所示,屏蔽掉宏定义的限制。
至于保留哪一个函数名,直接分别全局搜索他们,发现stm32f10x对应的是下面的函数名。
屏蔽后重新编译,报错如下:
全局搜索如下:
看来工程中缺少包含stm32f10x_exti.c,包含后重新编译,报错信息如下:
全局搜索如下:
未找到定义,在ST的HID工程中查找结果如下:
在main.c中找到了。添加到自己的工程中后,编译结果如下:
终于把所有的错误调好了。下载看看是否识别到HID。
发现还是不能识别HID,看来usb初始化代码有问题。下面从头开始查看初始化代码。
第一个“Set_System()”,根据上面结论,不用管;
第二个“USB_Interrupts_Config()”如下:
根据需要屏蔽上面的不相干代码,结果如下所示:
重新编译下载,结果如下
添加发送函数,如下:
下载结果如下:
屏蔽掉发送函数后,重新编译下载,还是失败。
修改VID后,重新下载,还是失败。
重新按照上面的步骤生成新的工程,依然是“未知USB设备”,请求设备描述符失败。
此后觉得自己永远没法独立调试出来了,然后用BeyondCompare软件比较上面
的最后一版软件和别人帮我调试好的代码,还有微雪开发板的USB手柄代码,不断
地屏蔽、复制和粘贴,以排除。最后发现是
Suspend()
函数被修改成了下面的样子,
才会让代码从
不能
用变为能用。
此时,下载结果如下:
后来发现,在正点原子的USB虚拟串口实验中,Suspend()函数也是被修改成上面的样子的!!!
**********************************************************************************************************************
下面开始进行HID数据的发送!
在usb_sil.c中发现一个USB发送函数“
USB_SIL_Write”,将其放到while(1)中,如下所示:
编译后下载,结果如下:
没有数据输出。
在网上找到发送函数的用法发现:
于是增加语句:SetEPRxValid(
ENDP1),编译下载后,还是没有数据发送。
在上面的USB虚拟串口代码中查找发送代码,发现发送函数USB_SIL_Write()并没有被调用,
而是使用下列语句发送的:
而USB_SIL_Write()函数如下:
上面函数的前两行和USB虚拟串口调用的发送代码相同,但是USB虚拟串口还是用了
SetEPTxValid()函数,这和上面从网上找到的SetEPRxValid()函数不同。尝试替换,看是
否能发送数据
结果如下:
BusHound报错如下:
从网上搜到:
于是分别修改下列几处代码:
(1) usb_desc.c中:
(2)usb_prop.c中:
(3)stm32f10x_it.c中:
没找到相关信息,暂时不改。
(4)在
usb_desc.c中:
(5)usb_desc.h中:
重新编译后,下载,结果如下:
USB数据发送完成!
下面开始进行HID数据的接收!
在usb_endp.c中有接收数据,用于控制LED。
在keil的调试模式下,用BusHound发送数据
在Keil中的Watch1窗口中观察变量:Receive_Buffer,结果发现数据并未变化。
本以为HID接收功能失败了,结果在点击调试暂停按钮时意外发现数据收到了:
看来,数据确实收到了,只是暂时未显示出来。
那么,
先判断收到的数据,再把数据返回,以此来判断数据收发是否正常。
看起效果如下:
所以,上面的数据收发功能正常。(代码见附件:第5步:实验4 串口实验 从头开始修改为HID_20170801_5.rar)