历史上的今天
返回首页

历史上的今天

今天是: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指令 配置部分还没写完,就先不放工程上来了。

推荐阅读

史海拾趣

DIOO公司的发展小趣事

面对数字化时代的挑战和机遇,DIOO公司决定加快数字化转型步伐。通过引入云计算、大数据、人工智能等先进技术,DIOO公司实现了产品设计、生产、销售等各个环节的数字化管理。同时,DIOO公司还积极探索新的商业模式和市场机会,与互联网企业、电信运营商等合作伙伴共同打造智能生态圈。未来,DIOO公司将继续秉承创新、卓越、服务的理念,致力于成为全球领先的电子产品制造商和服务提供商。

Advanced Milliwave Laboratories Inc公司的发展小趣事

随着技术的不断成熟和产品的不断优化,AML开始积极拓展市场。公司参与了多个国际电子展会,与全球各地的客户建立了联系。通过展示其独特的技术优势和高质量的产品,AML逐渐在微波技术领域建立了良好的品牌形象。同时,AML还加强了与合作伙伴的合作关系,共同开拓市场,实现互利共赢。

EOREX公司的发展小趣事

随着环保意识的日益提高,EOREX公司积极响应国家的绿色发展战略,将环保理念融入到产品研发和生产过程中。他们采用环保材料和绿色生产工艺,减少产品对环境的影响。同时,EOREX还加大了对环保技术的研发投入,推出了一系列具有环保功能的电子产品。这些举措不仅提升了公司的品牌形象和社会责任感,还为公司的可持续发展奠定了坚实的基础。

请注意,以上故事均为虚构内容,旨在展示一个电子公司可能的发展路径和策略。在实际应用中,每个公司的发展故事都有其独特性和复杂性。

DB Lectro Inc公司的发展小趣事

为了满足亚洲市场的需求,DB Lectro Inc决定在中国上海设立子公司及工厂。这一战略举措不仅使公司能够更贴近亚洲客户,还大大降低了生产成本。在上海工厂的建设过程中,公司充分考虑到环保和可持续性发展的要求,采用了先进的生产工艺和设备。随着工厂的投产和产能的不断提升,DB Lectro Inc的产品在亚洲市场的占有率也稳步上升。

复旦微电子(FM)公司的发展小趣事

DB Lectro Inc公司自创立之初,就致力于气体检测技术的研发与创新。在公司总部的加利福尼亚州研发中心,一群充满激情的工程师们夜以继日地工作,成功研发出了首款高精度、高稳定性的气体检测仪器。这款产品凭借其卓越的性能和精准的检测结果,迅速在污水处理、化工等领域打开了市场。随着技术的不断进步和产品的不断升级,DB Lectro Inc逐渐在气体检测领域树立了领先地位。

Daniels Manufacturing公司的发展小趣事

在当今社会,绿色环保和可持续发展已成为全球共识。作为一家具有社会责任感的企业,DMC积极践行绿色环保理念,致力于实现可持续发展。在生产过程中,DMC采用环保材料和清洁能源,减少废弃物和污染物的排放。同时,DMC还注重产品的环保性能设计,推出了一系列符合环保标准的产品。此外,DMC还积极参与社会公益活动,支持环保事业和社区建设。

问答坊 | AI 解惑

光电脉搏传感器的研制和噪声分析

本帖最后由 jameswangsynnex 于 2015-3-3 19:59 编辑 1 引 言   人体心室周期性的收缩和舒张导致主动脉的收缩和舒张, 使血流压力以波的形式从主动脉根部开始沿着整个动脉系统传播, 这种波称为脉搏波。脉搏波所呈现出的形态、强度、速率和节律等 ...…

查看全部问答>

S3C2410 串口通讯问题

最近本人编写了一个简单串口程序,只是发送,PC的软件接收。但是几天下来,都不成功,调试时PC软件根本接收不到,汗 !!! 现在把代码贴出来,请大家帮忙指点。 #include \"2410addr.h\" void Delay(int NUM) {     int i;   ...…

查看全部问答>

怎么让液晶表省电←省电精灵

怎么让液晶表省电←省电精灵c卡预付费电表控制器※电表倒转QQ:272208552 电话:13115998303 如何让插卡电表倒走,【网站打不开请点百度快照】如何使电表慢走 电表倒转的原因 ic卡电表偷电方法 电表节电器控制器电表控制器,电表干扰器:微电脑控表器 ...…

查看全部问答>

想做一个GSM或者CDMA短信转发给无线接收机的东东,可没什么思路,哪们大侠帮个忙?

想做一个GSM或者CDMA短信转发给无线接收机的东东,主要实现:1、短信模块负责接收短信,2、和这个模块相连的一个无线转发,3、一个接收机接收,并显示短信。 有没有什么好建议,好的话不白用哟!…

查看全部问答>

大功率LED封装使用细颗粒荧光粉当扩散粉的案例

replyreload += \',\' + 753394;Timson,如果您要查看本帖隐藏内容请回复…

查看全部问答>

求一块LM3S9B96 或9B95的芯片

RT:求一块LM3S9B96 或9B95的芯片…

查看全部问答>

求实现的较理想的算法,能给出C或者C++实现更好

误码率计算,求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可以看到路由器器所带的配置文件为: ...…

查看全部问答>

微电子器件和工艺硕博的筒子们,大多都是转行的节奏吗?

各位大神,读完器件和工艺硕士或博士之后,除去极少数继续科研,其他的都是转行的节奏吗? 真有这么惨。。。 有谁来说两句 大概什么情况,肿么哥原因啵,给大家一起来看看哦。…

查看全部问答>

2013年电子竞赛k题探讨

本帖最后由 paulhyde 于 2014-9-15 03:38 编辑 K题当中选用什么传感器比较好用呢    …

查看全部问答>