历史上的今天
今天是:2025年05月13日(星期二)
2020年05月13日 | 使用stm32cubemx的usb-host-cdc库驱动EC20模块
2020-05-13 来源:eefocus
开发环境:
开发板:正点原子F407探索者
代码生成工具:STM32CubeMX v5.4.0
IDE: eclipse + ac6工具链
实现功能:
单片机可以通过usb接口和EC20的AT指令虚拟串口通讯。
为了方便测试,配置串口2,将 模块->单片机 方向的数据通过串口2发送到电脑,将电脑通过串口工具发送到单片机的数据,转发给模块。
开始
1、使用STM32CubeMX配置工程,生成基础代码
1.1.1 配置晶振

1.1.2 配置时钟

1.2.1 配置串口2

1.2.2 配置DMA

1.2.3 开串口中断

1.3.1 配置USB Host_Only,不使用VBUS SOF

1.3.2 在Middleware中选择USB_HOST

将Class for FS IP 配置为 Communication Host Class (Virtual Port Com)
因为移远EC20模块连接后,配置描述符下一共有5个 Interface , 并且 Interface 中至多有3个Endpoint(下面简称Ep)。所以 注意: USBH_MAX_NUM_ENDPOINTS 需配置 >3 , USBH_MAX_NUM_INTERFACES 需配置 >5 。
1.4 配置USB供电引脚

查询探索者F4开发板原理图可知USB-HOST供电引脚为 PA15 ,在右侧双击芯片引脚配置为输出模式。
1.5 在左侧GPIO中进一步将 PA15 配置为高电平输出。并对 串口2 和 usb 引脚进行配置。

1.6 sys->debug方式,选择 Trace Asynchronous Sw 。

1.7 点击Project Manager 配置项目名称,生成代码。

如果使用MDK5 或 其他IDE可以在 Toolchain / IDE 中切换。
1.8 配置完成,点击右上角 GENERATE CODE 。
2、修改cubemx生成的代码
2.1 修改CDC_Class结构体,将 USB_CDC_CLASS 修改为 0xFF
USBH_ClassTypeDef CDC_Class =
{
"CDC",
0xFF,
USBH_CDC_InterfaceInit,
USBH_CDC_InterfaceDeInit,
USBH_CDC_ClassRequest,
USBH_CDC_Process,
USBH_CDC_SOFProcess,
NULL,
};
2.2 修改usbh_cdc.c 中的 USBH_CDC_InterfaceInit 函数
static USBH_StatusTypeDef USBH_CDC_InterfaceInit(USBH_HandleTypeDef *phost)
{
USBH_StatusTypeDef status = USBH_FAIL;
uint8_t interface;
CDC_HandleTypeDef *CDC_Handle;
// 默认系统配置标准CDC接口
// interface = USBH_FindInterface(phost,
// USB_CDC_CLASS,
// ABSTRACT_CONTROL_MODEL,
// COMMON_AT_COMMAND);
/**
* 注:
* cubemx生成的例程中,标准的CDC类设备,1个配置描述符中需要2接口
* 其中一个为Communication Interface Class, 该接口需要一个方向为in的Ep
* 另外一个为Data Interface Class, 该接口需要一个方向为in的Ep和一个方向为out的Ep
* 所以USBH_CDC_InterfaceInit函数,调用了两次USBH_FindInterface函数
* 查找两个匹配的Interface, 分别进行配置
*
* USB-TTL串口工具,debug状态下查询设备描述符结构体中,只有一个接口
* 但是该接口拥有3个Ep, 2个方向为in, 1个方向为out.
* 由此猜测,串口工具并没有将Interface分开
* 经测试, Communication Interface使用的Ep为2, Data Interface使用Ep0,1
*
* Ec20模块,可以读到5个Interface,但是只有Interface 1 2 3 有3个Ep,0 和 4 只有2个Ep
* 经测试,接口AT指令的串口Interface为 2.
* Interface 2中,Communication Interface使用的Ep为0
* Data Interface使用的Ep为1 和 2
*/
// USB-TTL串口工具接口配置
// interface = USBH_FindInterface(phost,
// USER_USB_CDC_CLASS,
// DIRECT_LINE_CONTROL_MODEL, 02);
// 移远4G模块接口配置
interface = USBH_FindInterfaceIndex(phost, 2, 0);
if (interface == 0xFFU) /* No Valid Interface */
{
USBH_DbgLog("Cannot Find the interface for Communication Interface Class.",
phost->pActiveClass->Name);
}
else
{
USBH_SelectInterface(phost, interface);
phost->pActiveClass->pData = (CDC_HandleTypeDef*) USBH_malloc(
sizeof(CDC_HandleTypeDef));
CDC_Handle = (CDC_HandleTypeDef*) phost->pActiveClass->pData;
/*Collect the notification endpoint address and length*/
if (phost->device.CfgDesc.Itf_Desc[interface].Ep_Desc[0].bEndpointAddress
& 0x80U)
{
CDC_Handle->CommItf.NotifEp =
phost->device.CfgDesc.Itf_Desc[interface].Ep_Desc[0].bEndpointAddress;
CDC_Handle->CommItf.NotifEpSize =
phost->device.CfgDesc.Itf_Desc[interface].Ep_Desc[0].wMaxPacketSize;
}
/*Allocate the length for host channel number in*/
CDC_Handle->CommItf.NotifPipe = USBH_AllocPipe(phost,
CDC_Handle->CommItf.NotifEp);
/* Open pipe for Notification endpoint */
USBH_OpenPipe(phost, CDC_Handle->CommItf.NotifPipe,
CDC_Handle->CommItf.NotifEp, phost->device.address, phost->device.speed,
USB_EP_TYPE_INTR, CDC_Handle->CommItf.NotifEpSize);
USBH_LL_SetToggle(phost, CDC_Handle->CommItf.NotifPipe, 0U);
// 默认系统配置标准CDC接口
// interface = USBH_FindInterface(phost,
// DATA_INTERFACE_CLASS_CODE,
// RESERVED,
// NO_CLASS_SPECIFIC_PROTOCOL_CODE);
if (interface == 0xFFU) /* No Valid Interface */
{
USBH_DbgLog("Cannot Find the interface for Data Interface Class.",
phost->pActiveClass->Name);
}
else
{
/*Collect the class specific endpoint address and length*/
if (phost->device.CfgDesc.Itf_Desc[interface].Ep_Desc[2].bEndpointAddress
& 0x80U)
{
CDC_Handle->DataItf.InEp =
phost->device.CfgDesc.Itf_Desc[interface].Ep_Desc[2].bEndpointAddress;
CDC_Handle->DataItf.InEpSize =
phost->device.CfgDesc.Itf_Desc[interface].Ep_Desc[2].wMaxPacketSize;
}
else
{
CDC_Handle->DataItf.OutEp =
phost->device.CfgDesc.Itf_Desc[interface].Ep_Desc[2].bEndpointAddress;
CDC_Handle->DataItf.OutEpSize =
phost->device.CfgDesc.Itf_Desc[interface].Ep_Desc[2].wMaxPacketSize;
}
if (phost->device.CfgDesc.Itf_Desc[interface].Ep_Desc[1].bEndpointAddress
& 0x80U)
{
CDC_Handle->DataItf.InEp =
phost->device.CfgDesc.Itf_Desc[interface].Ep_Desc[1].bEndpointAddress;
CDC_Handle->DataItf.InEpSize =
phost->device.CfgDesc.Itf_Desc[interface].Ep_Desc[1].wMaxPacketSize;
}
else
{
CDC_Handle->DataItf.OutEp =
phost->device.CfgDesc.Itf_Desc[interface].Ep_Desc[1].bEndpointAddress;
CDC_Handle->DataItf.OutEpSize =
phost->device.CfgDesc.Itf_Desc[interface].Ep_Desc[1].wMaxPacketSize;
}
/*Allocate the length for host channel number out*/
CDC_Handle->DataItf.OutPipe = USBH_AllocPipe(phost,
CDC_Handle->DataItf.OutEp);
/*Allocate the length for host channel number in*/
CDC_Handle->DataItf.InPipe = USBH_AllocPipe(phost,
CDC_Handle->DataItf.InEp);
/* Open channel for OUT endpoint */
USBH_OpenPipe(phost, CDC_Handle->DataItf.OutPipe,
CDC_Handle->DataItf.OutEp, phost->device.address, phost->device.speed,
USB_EP_TYPE_BULK, CDC_Handle->DataItf.OutEpSize);
/* Open channel for IN endpoint */
USBH_OpenPipe(phost, CDC_Handle->DataItf.InPipe, CDC_Handle->DataItf.InEp,
phost->device.address, phost->device.speed,
USB_EP_TYPE_BULK, CDC_Handle->DataItf.InEpSize);
CDC_Handle->state = CDC_IDLE_STATE;
USBH_LL_SetToggle(phost, CDC_Handle->DataItf.OutPipe, 0U);
USBH_LL_SetToggle(phost, CDC_Handle->DataItf.InPipe, 0U);
status = USBH_OK;
}
}
return status;
}
修改原因请参见代码中的注释
2.3 修改 USBH_CDC_ClassRequest 函数
static USBH_StatusTypeDef USBH_CDC_ClassRequest(USBH_HandleTypeDef *phost)
{
USBH_StatusTypeDef status = USBH_FAIL;
CDC_HandleTypeDef *CDC_Handle =
(CDC_HandleTypeDef*) phost->pActiveClass->pData;
// /*Issue the get line coding request*/
// status = GetLineCoding(phost, &CDC_Handle->LineCoding);
CDC_Handle->data_rx_state = CDC_IDLE;
CDC_Handle->data_tx_state = CDC_IDLE;
CDC_Handle->LineCoding.b.bCharFormat = 0;
CDC_Handle->LineCoding.b.bDataBits = 8;
CDC_Handle->LineCoding.b.bParityType = 0;
CDC_Handle->LineCoding.b.dwDTERate = 115200;
status = USBH_OK;
if (status == USBH_OK)
{
phost->pUser(phost, HOST_USER_CLASS_ACTIVE);
}
return status;
}
因为读取不到LineCoding相关参数,所以在这里进行相关参数的手动设置。
具体读不到的原因,我也不是很清楚。因为串口连接电脑之后,也是通过串口工具手动选择的波特率。所以猜测这里也是同样的道理,需要自己手动设置。希望各位指正。
2.4 在 usb_host.c 中添加接收代码。
在 MX_USB_HOST_Process 函数中添加接收代码。
// RecData.buf 为数据缓存区首地址
// UART_BUF_LEN 为数据缓存区长度
// 可以根据自己的需要自定义
void MX_USB_HOST_Process(void)
{
CDC_HandleTypeDef *CDC_Handle = hUsbHostFS.pActiveClass->pData;
/* USB Host Background task */
USBH_Process(&hUsbHostFS);
if (hUsbHostFS.gState == HOST_CLASS)
{
if (CDC_Handle->data_rx_state == CDC_IDLE)
{
USBH_CDC_Receive(&hUsbHostFS, RecData.buf, UART_BUF_LEN);
}
}
}
2.5 在 main.c 或者其他需要的位置,添加 USBH_CDC_ReceiveCallback 函数。(原函数为弱函数,用户添加后会将原函数覆盖)
// 这里我设置了一个read_flag表示数据未读,别处代码有用到
// uart_send是我封装的一个串口发送函数,将数据通过串口2转发电脑
void USBH_CDC_ReceiveCallback(USBH_HandleTypeDef *phost)
{
RecData.len_rec = USBH_CDC_GetLastReceivedDataSize(phost);
RecData.read_flag = 0;
uart_send(&uart2, Recbuff.buf, len);
}
2.6 在 main() while(1) 中等待接收串口2发过来的数据,长度不为0就调用usb发送函数,将数据发送出去。
while (1)
{
/* USER CODE END WHILE */
MX_USB_HOST_Process();
/* USER CODE BEGIN 3 */
switch (Appli_state)
{
case APPLICATION_READY:
len_rec = uart_receive(&uart2, main_buf, 1);
if (len_rec != 0)
{
USBH_CDC_Transmit(&hUsbHostFS, main_buf, len_rec);
}
break;
case APPLICATION_DISCONNECT:
ec20.init_flag = 0;
break;
default:
break;
}
3、 编译下载运行,成功发送at并接收到at指令回复。

总结
整个USB-HOST配置过程,难点在于对 usbh_cdc.c 文件的修改。网上做stm32
usb-host-cdc的资料很少。我是先配置连接usb串口工具成功以后,对usb通讯的配置描述符、接口描述符、端点描述符有了一定的了解。之后才进行的 ec20 模块配置。希望可以对跟我一样的新手有所帮助。
因为 EC20 模块 at指令 配置部分还没写完,就先不放工程上来了。
史海拾趣
|
本帖最后由 jameswangsynnex 于 2015-3-3 19:59 编辑 1 引 言 人体心室周期性的收缩和舒张导致主动脉的收缩和舒张, 使血流压力以波的形式从主动脉根部开始沿着整个动脉系统传播, 这种波称为脉搏波。脉搏波所呈现出的形态、强度、速率和节律等 ...… 查看全部问答> |
|
最近本人编写了一个简单串口程序,只是发送,PC的软件接收。但是几天下来,都不成功,调试时PC软件根本接收不到,汗 !!! 现在把代码贴出来,请大家帮忙指点。 #include \"2410addr.h\" void Delay(int NUM) { int i;   ...… 查看全部问答> |
|
怎么让液晶表省电←省电精灵c卡预付费电表控制器※电表倒转QQ:272208552 电话:13115998303 如何让插卡电表倒走,【网站打不开请点百度快照】如何使电表慢走 电表倒转的原因 ic卡电表偷电方法 电表节电器控制器电表控制器,电表干扰器:微电脑控表器 ...… 查看全部问答> |
|
想做一个GSM或者CDMA短信转发给无线接收机的东东,可没什么思路,哪们大侠帮个忙? 想做一个GSM或者CDMA短信转发给无线接收机的东东,主要实现:1、短信模块负责接收短信,2、和这个模块相连的一个无线转发,3、一个接收机接收,并显示短信。 有没有什么好建议,好的话不白用哟!… 查看全部问答> |
|
误码率计算,求C/C++实现的较理想的算法。已知XXX.txt文件中每一行有A和B两个变量,二者的格式如下:“$JJZ,m,nnnnnn,E24ACA95F4……”m:即为A的值。nnnnnn:即为B的值。其中A和B的增长规律为A=A%5+1; //即从1至5循环变化If(A==5) B=B%604800+3; // ...… 查看全部问答> |
|
使用的协议栈版本信息: ZigBee2006\\ZStack-1.4.3-1.2.1Zigbee网络设备启动流程—路由器(自启动模式)—以SampleApp的路由器为例. 1、路由器预编译信息通过project->options->c/c++compiler->extraOptions可以看到路由器器所带的配置文件为: ...… 查看全部问答> |
|
各位大神,读完器件和工艺硕士或博士之后,除去极少数继续科研,其他的都是转行的节奏吗? 真有这么惨。。。 有谁来说两句 大概什么情况,肿么哥原因啵,给大家一起来看看哦。… 查看全部问答> |




