(直播)分析老外的关于stm32的usb虚拟串口程序,体会配置方法和实现流程

老外的这个stm32中利用usb做虚拟串口实验,程序在本站****里面,
他是用一个名叫Coocox的软件+Gcc软件编写的
程序写的很漂亮,就分析下他实现的流程和硬件外设方面的配置问题
我是刚接触这个stmcubemx生成CDC代码
所以不知道应该怎么去配置,甚至不知道需要哪些外设?
需要定时器?uart?usb口DP和DM?还有DMA?GPIO?
反正我看官方提供的那个usb_device是头大..........
希望借此能稍微懂点配置问题
本人呢追求的是读书不求甚解,所以官方那个usb的文档也看了...只是没看仔细
这些文档本站资源下载里面也有的.
呵呵,我只希望能调试出来能用就行了,不想知道为什么这样做.
只想知道我这样做是不是可以就行了,
我要求低,能读通,能配置,能使用
下面开始分析
已邀请:

admin

赞同来自: 酸辣野人

老外那个全部的定义如下:

#define USBD_CFG_MAX_NUM 1
#define USBD_ITF_MAX_NUM 1
#define USB_MAX_STR_DESC_SIZ 50
#define CDC_IN_EP 0x81 / EP1 for data IN /
#define CDC_OUT_EP 0x01 / EP1 for data OUT /
#define CDC_CMD_EP 0x82 / EP2 for CDC commands /
#define CDC_DATA_MAX_PACKET_SIZE 64 / Endpoint IN & OUT Packet size /
#define CDC_CMD_PACKET_SZE 8 / Control Endpoint Packet size /

#define CDC_IN_FRAME_INTERVAL 5 / Number of frames between IN transfers /
#define APP_RX_DATA_SIZE 2048 /* Total size of IN buffer:

#define RX_FIFO_FS_SIZE 128
#define TX0_FIFO_FS_SIZE 64
#define TX1_FIFO_FS_SIZE 128
#define TX2_FIFO_FS_SIZE 0
#define TX3_FIFO_FS_SIZE 0
#define TXH_NP_FS_FIFOSIZ 96
#define TXH_P_FS_FIFOSIZ 96



#define USBD_VID 0x0483

#define USBD_PID 0x5740

/** @defgroup USB_String_Descriptors
* @{
*/
#define USBD_LANGID_STRING 0x409
#define USBD_MANUFACTURER_STRING "STMicroelectronics"

#define USBD_PRODUCT_HS_STRING "STM32 Virtual ComPort in HS mode"
#define USBD_SERIALNUMBER_HS_STRING "00000000050B"

#define USBD_PRODUCT_FS_STRING "STM32 Virtual ComPort in FS Mode"
#define USBD_SERIALNUMBER_FS_STRING "00000000050C"

#define USBD_CONFIGURATION_HS_STRING "VCP Config"
#define USBD_INTERFACE_HS_STRING "VCP Interface"

#define USBD_CONFIGURATION_FS_STRING "VCP Config"
#define USBD_INTERFACE_FS_STRING "VCP Interface"

#define USB_DEVICE_DESCRIPTOR_TYPE 0x01
#define USB_CONFIGURATION_DESCRIPTOR_TYPE 0x02
#define USB_STRING_DESCRIPTOR_TYPE 0x03
#define USB_INTERFACE_DESCRIPTOR_TYPE 0x04
#define USB_ENDPOINT_DESCRIPTOR_TYPE 0x05
#define USB_SIZ_DEVICE_DESC 18
#define USB_SIZ_STRING_LANGID 4

admin

赞同来自:

首先看这个文件结构,驱动中用了Exti/GPIO/RCC/Usart.c文件
说明用到了额usb,时钟,io口,串口uart配置...................
其他的貌似没有,就是说定时器是不用的???官方那个为嘛有?(先放着不管它)

无标题.png

admin

赞同来自:

打开main.c第一个看到

void SysTick_Handler(void);
void NMI_Handler(void);
void HardFault_Handler(void);
void MemManage_Handler(void);
void BusFault_Handler(void);
void UsageFault_Handler(void);
void SVC_Handler(void);
void DebugMon_Handler(void);
void PendSV_Handler(void);
void OTG_FS_IRQHandler(void);
void OTG_FS_WKUP_IRQHandler(void);
整个工程中准备用到的各种中断....
额,好多没见过的........
第一个是滴答时钟的......
第二个是空的,没用,不认识
第三个硬故障中断....调用了ColorfulRingOfDeath();
而这个函数是这个意思
* Call this to indicate a failure. Blinks the STM32F4 discovery LEDs
* in sequence. At 168Mhz, the blinking will be very fast - about 5 Hz.
* Keep that in mind when debugging, knowing the clock speed might help
* with debugging.
表明调用出错了....,以5HZ的频率闪烁led,有利于debug调试用.....汗
好了,直到OTG_FS_IRQHandler();上面都是要不然错误闪烁led,要不就是空的,没啥用.
倒数第二个调用了USBD_OTG_ISR_Handler (&USB_OTG_dev);这个是usb总中断函数
而这个被调用的是在文件usb_dcd_init.c里面,这个文件是驱动文件,
这就意味着这个文件不是我们可以随便修改的,呵呵,那咱就不管它了,只要知道它是中断就行
至于处理了些啥,就不管它了.
最后一个中断貌似看着意思是usb唤醒中断.....这个函数位于main.c中,,必须去看看
void OTG_FS_WKUP_IRQHandler(void)
{
if(USB_OTG_dev.cfg.low_power)
{
]i[url=0xE000ED10]/url &= 0xFFFFFFF9 ;
SystemInit();
USB_OTG_UngateClock(&USB_OTG_dev);
}
EXTI_ClearITPendingBit(EXTI_Line18);
}
意思大概是,如果开了低功耗模式,则执行操作然后系统唤醒并开启usb时钟,然后清除外部中断标志位..
貌似这个应该是vbus线的作用了........

admin

赞同来自:

继续看main函数中,先设置系统时钟,
再Initialize USB, IO, SysTick, and all those other things you do in the morning.
初始化usb,io,滴答,和其他......
这个是重点,我要的配置应该就在这里了....
init()里面,先定义了STM32F4 discovery LEDs
discovery板里面的led分别在PD12,13,14,15,他设置了输出上拉,中速,
然后设置了滴答时钟i1ms...
继续设置usb调用了
USBD_Init(&USB_OTG_dev,
USB_OTG_FS_CORE_ID,
&USR_desc,
&USBD_CDC_cb,
&USR_cb);
第一定义了不是host,不是otg,是dev即device方式...
USB_OTG_FS_CORE_ID是=1的.意思是使用FS模式.......
第三个参数定义了各种usb应用函数.......USBD_DESC_Private_Variables.(先不管)
第四个厉害了,定义了usb用到的外设.....打开看看
/ CDC interface class callbacks structure /
USBD_Class_cb_TypeDef USBD_CDC_cb =
{
usbd_cdc_Init,
usbd_cdc_DeInit,
usbd_cdc_Setup,
NULL, / EP0_TxSent, /
usbd_cdc_EP0_RxReady,
usbd_cdc_DataIn,
usbd_cdc_DataOut,
usbd_cdc_SOF,
NULL,
NULL,
USBD_cdc_GetCfgDesc,
#ifdef USE_USB_OTG_HS
USBD_cdc_GetOtherCfgDesc, / use same cobfig as per FS /
#endif / USE_USB_OTG_HS /
};

admin

赞同来自:

先看usbd_cdc_Init我想说的是这个函数位于usbd_cdc_core.c中,意味着可以不看
但是里面引用了一些usb的参数在usb_conf.h中
/** @defgroup USB_VCP_Class_Layer_Parameter
* @{
*/
#define CDC_IN_EP 0x81 / EP1 for data IN /
#define CDC_OUT_EP 0x01 / EP1 for data OUT /
#define CDC_CMD_EP 0x82 / EP2 for CDC commands /
这个是要我们可以动的.......(无需改)
然后定义了发送和接收函数......
VCP_Init, VCP_DeInit, VCP_Ctrl, VCP_DataTx,VCP_DataRx .....
流程就是
Open EP IN->Open EP OUT->Open Command IN EP->初始化发送和接收函数
然后/ Prepare Out endpoint to receive next packet /
DCD_EP_PrepareRx(pdev,
CDC_OUT_EP,
(uint8_t*)(USB_Rx_Buffer),
CDC_DATA_OUT_PACKET_SIZE);
准备输出结束点去接收下一个数据包...(不去管它.).

admin

赞同来自:

继续USBD_Init()里面的第一个函数USB_OTG_BSP_Init(pdev);这个是硬件初始化,看看里面
#ifdef USE_USB_OTG_FS

RCC_AHB1PeriphClockCmd( RCC_AHB1Periph_GPIOA , ENABLE);

/ Configure SOF VBUS ID DM DP Pins /
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8 |
GPIO_Pin_9 |
GPIO_Pin_11 |
GPIO_Pin_12;

GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL ;
GPIO_Init(GPIOA, &GPIO_InitStructure);

GPIO_PinAFConfig(GPIOA,GPIO_PinSource8,GPIO_AF_OTG1_FS) ;
GPIO_PinAFConfig(GPIOA,GPIO_PinSource9,GPIO_AF_OTG1_FS) ;
GPIO_PinAFConfig(GPIOA,GPIO_PinSource11,GPIO_AF_OTG1_FS) ;
GPIO_PinAFConfig(GPIOA,GPIO_PinSource12,GPIO_AF_OTG1_FS) ;

/ this for ID line debug /


GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
GPIO_InitStructure.GPIO_OType = GPIO_OType_OD;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP ;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_PinAFConfig(GPIOA,GPIO_PinSource10,GPIO_AF_OTG1_FS) ;

RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE);
RCC_AHB2PeriphClockCmd(RCC_AHB2Periph_OTG_FS, ENABLE) ;
我们只用FS模式,就看这些就行了
修改SOF , VBUS , ID ,DM ,DP关键设置
设置PA8 , 9 , 11 ,12端口模式为AFPP,无上拉,High speed模式
而PA10开漏输出,上拉,高速模式(千万别设置成AF模式啊),这个只是用来测试id用于debug的

2.png

admin

赞同来自:

继续看
#ifdef USB_OTG_FS_LOW_PWR_MGMT_SUPPORT
EXTI_ClearITPendingBit(EXTI_Line18);

EXTI_InitStructure.EXTI_Line = EXTI_Line18;
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising;
EXTI_InitStructure.EXTI_LineCmd = ENABLE;
EXTI_Init(&EXTI_InitStructure);

EXTI_ClearITPendingBit(EXTI_Line18);

NVIC_InitStructure.NVIC_IRQChannel = OTG_FS_WKUP_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);

EXTI_ClearITPendingBit(EXTI_Line18);
#endif
如果低功耗开启了...
External interrupt line 18 Connected to the USB OTG FS Wakeup from suspend event
外部中断18链接的是usb_otg的唤醒端口......
开启外部中断模式,上升沿出发,并使能,开启OTG唤醒模式..中断优先级0,0,使能中断
清除中断标志位.....
最后又来一个 EXTI_ClearITPendingBit(EXTI_Line0);
这个是干啥的啊??貌似没用这个外部中断啊....
额,刚才看到里面有个STM_EVAL_PBInit(BUTTON_USER, BUTTON_MODE_EXTI);
不过是被注释掉了,估计这个老外也是从EVAL板改过来的,这个外部0中断也许就是它了
可以无视它

admin

赞同来自:

回到USBD_Init()里面继续
/Register class and user callbacks /
pdev->dev.class_cb = class_cb;
pdev->dev.usr_cb = usr_cb;
pdev->dev.usr_device = pDevice;
寄存器类和用户回调
将void USBD_Init(USB_OTG_CORE_HANDLE *pdev,
USB_OTG_CORE_ID_TypeDef coreID,
USBD_DEVICE *pDevice,
USBD_Class_cb_TypeDef *class_cb,
USBD_Usr_cb_TypeDef *usr_cb)
里面的变量赋到程序中,其中PDevice里面包含了各种用户回调函数..
有这些
typedef struct _Device_TypeDef
{
uint8_t GetDeviceDescriptor]i[url= uint8_t speed , uint16_t *length]/url;
uint8_t GetLangIDStrDescriptor]i[url= uint8_t speed , uint16_t *length]/url;
uint8_t GetManufacturerStrDescriptor]i[url= uint8_t speed , uint16_t *length]/url;
uint8_t GetProductStrDescriptor]i[url= uint8_t speed , uint16_t *length]/url;
uint8_t GetSerialStrDescriptor]i[url= uint8_t speed , uint16_t *length]/url;
uint8_t GetConfigurationStrDescriptor]i[url= uint8_t speed , uint16_t *length]/url;
uint8_t GetInterfaceStrDescriptor]i[url= uint8_t speed , uint16_t *length]/url;
} USBD_DEVICE, *pUSBD_DEVICE;

typedef struct _USBD_USR_PROP
{
void (*Init)(void);
void (*DeviceReset)(uint8_t speed);
void (*DeviceConfigured)(void);
void (*DeviceSuspended)(void);
void (*DeviceResumed)(void);

void (*DeviceConnected)(void);
void (*DeviceDisconnected)(void);

}
USBD_Usr_cb_TypeDef;
紧接着就 / set USB OTG core params /
DCD_Init(pdev , coreID);设置了usb的编程函数(就是初始化)....
这个函数里面有个引用
void USB_OTG_BSP_mDelay (const uint32_t msec)
{
USB_OTG_BSP_uDelay(msec * 1000);
}
位于usb_bsp.c中,用来提供一个ms级延时...
用于这个 / Force Device Mode/
USB_OTG_SetCurrentMode(pdev, DEVICE_MODE);里面
延时了50个ms............
继续....
调用了
/**
* @brief USB_OTG_CoreInitDev : Initializes the USB_OTG controller registers
* for device mode
* @param pdev : Selected device
* @retval USB_OTG_STS : status
*/
USB_OTG_STS USB_OTG_CoreInitDev (USB_OTG_CORE_HANDLE *pdev)
初始化usb_otg驱动模式的控制寄存器(无需了解它,知道就行了)
继续看
/ Enable USB Global interrupt /
USB_OTG_EnableGlobalInt(pdev);
使能了usb的全局中断.....这个提示我们要开启usb的全局中断.....

admin

赞同来自:

继续返回看USBD_Init()函数中的
/ Upon Init call usr callback /
pdev->dev.usr_cb->Init();
初始化用户调用模块.....
就是这几个
void (*Init)(void);
void (*DeviceReset)(uint8_t speed);
void (*DeviceConfigured)(void);
void (*DeviceSuspended)(void);
void (*DeviceResumed)(void);

void (*DeviceConnected)(void);
void (*DeviceDisconnected)(void);
最后
/ Enable Interrupts /
USB_OTG_BSP_EnableInterrupt(pdev);
里面的东东是下面的
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);
NVIC_InitStructure.NVIC_IRQChannel = OTG_FS_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
看得懂吧,呵呵

admin

赞同来自:

再看while里面的代码
/ Blink the orange LED at 1Hz /
if (500 == ticker)
{
GPIOD->BSRRH = GPIO_Pin_13;
}
else if (1000 == ticker)
{
ticker = 0;
GPIOD->BSRRL = GPIO_Pin_13;
}
闪灯....1s闪一次
/* If there's data on the virtual serial port:
* - Echo it back
* - Turn the green LED on for 10ms
*/
uint8_t theByte;
if (VCP_get_char(&theByte))
{
VCP_put_char(theByte);


GPIOD->BSRRL = GPIO_Pin_12;
downTicker = 10;
}
if (0 == downTicker)
{
GPIOD->BSRRH = GPIO_Pin_12;
}
如果虚拟串口上有数据,返回数据,则绿灯10ms闪烁......(能看出来吗)
额,所有的程序完了......
貌似没看到usart串口啊....
这个好像就是usb得到数据立马由usb返回源数据........

admin

赞同来自: ZJZTO

用cube生成了usb虚拟串口程序,但是设备管理器那里显示一个叹号,驱动安装不成功....
我说说我的配置,估计是配置有问题
Pinout界面中修改:
开启USB_OTG_FS模式下的Device_Only,其他不选
middleWare里面的USB_DEVICE中的Class For FS IP选择Communication Device Class (Virtual Port Com)
时钟配置界面:8M外部晶振,设置系统168M,Apb1分频4,APB2分频2
Configuration界面中:
GPIO设置PD12,13,14,15输出上拉,中速
usb的GPIO的DM和DP均为AF,无上啦,高速
////////////////////////////////////////////
猜想可能是DP要加上拉的?等会试下
///////////////////////////////////////////
中断GRoup4
NVIC中断开启usb on the Go FS Global interrupt 中断1,0
USB_FS configuration 设置 speed为Full , ENDpoint 0 max packet size为64bytes
低功耗不使能, vbus不是能.....
usb_device configuration中的Parameter setting设置

1.png


2.png

常青树

赞同来自:

有点看不懂

咫尺,天涯

赞同来自:

请问下测得下位机通过虚拟串口向上位机发送的速度是多少呢

Wise Up - 90后

赞同来自:

看不懂


ujewm - 90后理工

赞同来自:

哇~这么一大堆分析,但是看的好头疼呀!有个小建议,楼主下次可以整个录屏直播,手把手操作实现一遍,顺便讲解,这样的话会好很多

赞同来自:

求教楼主

我连上上位机 枚举成功之后  在设备名称后面会用 括号显示 serial number, 如何才能不显示这个,怎么删除,我只想要自己定义的设备名称。

???

要回复问题请先登录注册