历史上的今天
返回首页

历史上的今天

今天是:2025年07月29日(星期二)

正在发生

2019年07月29日 | STM32F4discovery_CDC_Device数据传输的一步步实现

2019-07-29 来源:eefocus

1. 需求

某项目需要单片机把ADC数据上传到电脑。方法有两种:1、USB;2、以太网。百度必应了一番,发现有人用过NXP的LPC的一款带USB2.0 HighSpeed phy的片子,但是采用BGA封装,开发难度上了一个层次。这两年内使用的STM32F1、F4比较多,对其开发比较熟悉,资料也丰富。因此决定使用STM32F4跑一下CDC_Device例程,调一下这部分的数据传输。


2. 环境

2.1 软件

Win7、Atollic TrueSTUDIO for STM32


STM32CubeMX v4.27.0,装上STM32CubeF4 Firmware Package V1.21.0


2.2 硬件

我采用的是STM32F4discovery。也可以是STM32F4的开发板。


3. 试验

3.1 使用STM32Cube生成项目

需要用到的IO口资源:连接4个指示灯的GPIO(PD12-PD15)。连接了按键的PA0(但实际未用到)。连接了外部8MHz晶振的PH0和PH1。连接到USB插座的PA11和PA12。连接到STLINK仿真器的PA13、PA14。


3.1.1 晶振

Peripherals中需要配置RCC,STM32F4discovery未焊接外部低速晶振。因此LSE默认disable,而有8MHz晶振,因此配置为Crystal/Ceramic。



3.1.2 仿真/下载

由于使用的是STLINK(STM32discovery板载stlinkv2),配置debug模式为Serial Wire。



3.1.3 USB设备

STM32F407VG芯片支持USB2.0 Fullspeed。但是usb2.0 high speed需要外接控制器如usb3300。这里的方式是STM32F407VG的引脚pa11和pa12经过电阻直接连接USB插座。需要配置为usb_device_fullspeed。


配置USB_OTG_FS如下:PC为usb host,而STM32作为usb device。



选好了device_only后,Configuration栏的USB_DEVICE即有效。把USB_DEVICE中,配置Class For FS IP为Communication Device Class(CDC)。



3.1.4 时钟树配置


STM32F407使用外部的8MHz晶振,Input frequency输入8后,选菜单栏中的clock Configuration -> Resolve Clock Issues即可自动为芯片内部的各个模块配置好时钟频率。这里需要注意,STM32F4内置的usb controller时钟需要48HMz才能正常工作。



在这个页面没有红色字体后,这个页面的配置也就完成了。


3.1.5 内部模块配置


这个页面的各个部分采用默认值就可以了。不需要改动。(这也是我第一次跑这个demo,尽量根据前人的步骤来就不怕出错了)


3.1.6 生成代码


生成代码前需要选择project-> Settings配置工程目录以及IDE。记得把mimimum heap size和mimmum stack size提高。有网友反映这个size低了是会出error的。我把两个size都改大了,分别设为0x600和0x1000。其他默认,点击OK。


然后点Project -> Generate Code。工程初始化已经完成。



4. 用户代码修改

在main.c的合适区域增加以下代码:


4.1  引用头文件以及参数声明

顾名思义,第一个参数是接受数据,双缓冲数组结构。第二个参数是发送数据。cdc每接收一个包,会刷新接收数据。需要把其当前数据包的长度记录。


#include "usbd_cdc_if.h"

 

extern uint8_t UserRxBufferFS[2][2048];

extern uint8_t UserTxBufferFS[2048];

 

extern uint32_t nRxLength; //// 接收到的数据长度

extern uint8_t uRxBufIndex; //// 当前使用的缓冲区索引号

extern uint8_t  uLastRxBufIndex;//// 上次使用的接收缓冲区索引号

int bSendMark = 0; //// 发送数据的标志

4.2 main函数内的参数初始化区域

我想知道上传到电脑的数据是否准确连续。故自行按顺序初始化了发送数组中的每个数值。


 

  for(i=0;i  {

  UserTxBufferFS[i]=i;

  }

4.3 main函数内的死循环

HAL_GPIO_TogglePin(LD4_GPIO_Port, LD4_Pin);

 

// 每次CDC_Itf_Receive()接收到新数据都会更换缓存,所以发现缓存切换过就是有新的数据。

// 这里必须保证数据处理的速度足够快,否则缓存切换了两次才处理的话,就没法识别有新数据到来了。

if (uLastRxBufIndex != uRxBufIndex)

{

// --> 指令译码开始。

for (i=0; i {

if (UserRxBufferFS[uLastRxBufIndex][i] == 0x55) // 0x55, 开始发送数据指令

bSendMark = 1;

if (UserRxBufferFS[uLastRxBufIndex][i] == 0xAA) // 0xAA, 停止发送数据指令

bSendMark = 0;

}

// <-- 指令译码结束。

uLastRxBufIndex = (uLastRxBufIndex + 1) & 1;

}

 

if (bSendMark)

{

int32_t k = 0;

while (CDC_Transmit_FS(UserTxBufferFS, APP_TX_DATA_SIZE) != USBD_OK)

k++;

}

 

// 处理接收到的数据:解码数据流中的指令。可以改成你自己的数据处理过程。

// 每次CDC_Itf_Receive()接收到新数据都会更换缓存,所以发现缓存切换过就是有新的数据。

// 这里必须保证数据处理的速度足够快,否则缓存切换了两次才处理的话,就没法识别有新数据到来了。

if (uLastRxBufIndex != uRxBufIndex)

{

// --> 指令译码开始。

for (i=0; i {

if (UserRxBufferFS[uLastRxBufIndex][i] == 0x55) // 0x55, 开始发送数据指令

bSendMark = 1;

if (UserRxBufferFS[uLastRxBufIndex][i] == 0xAA) // 0xAA, 停止发送数据指令

bSendMark = 0;

}

// <-- 指令译码结束。

uLastRxBufIndex = (uLastRxBufIndex + 1) & 1;

}

if (bSendMark)

{

int32_t k = 0;

while (CDC_Transmit_FS(UserTxBufferFS, APP_TX_DATA_SIZE) != USBD_OK)

k++;

}

4.4 usbd_cdc_if.c

4.4.1:1维数组重新定义为2维。

uint8_t UserRxBufferFS[2][APP_RX_DATA_SIZE];

4.4.2:定义全局变量

 

uint32_t BuffLength;

uint32_t nRxLength;

uint8_t uRxBufIndex = 0; //// 当前使用的缓冲区索引号

uint8_t  uLastRxBufIndex = 0; //// 上次使用的接收缓冲区索引号

4.4.3:static int8_t CDC_Init_FS(void)这个函数内:

  USBD_CDC_SetRxBuffer(&hUsbDeviceFS, UserRxBufferFS);

改为:


  USBD_CDC_SetRxBuffer(&hUsbDeviceFS, UserRxBufferFS[0]);

4.4.4:CDC_Receive_FS函数


  USBD_CDC_SetRxBuffer(&hUsbDeviceFS, &Buf[0]);

 

  USBD_CDC_ReceivePacket(&hUsbDeviceFS);

  return (USBD_OK);

改为:


  //USBD_CDC_SetRxBuffer(&hUsbDeviceFS, &Buf[0]);

 

nRxLength = *Len;

//// 接收到的数据在main()函数中处理

//// 这里只是简单地切换缓存

uRxBufIndex++;

uRxBufIndex &= 0x01;

USBD_CDC_SetRxBuffer(&hUsbDeviceFS, &UserRxBufferFS[uRxBufIndex][0]);

 

  USBD_CDC_ReceivePacket(&hUsbDeviceFS);

  return (USBD_OK);

5 编译错误

5.1 错误1

..Srcusbd_cdc_if.c:201:39: warning: passing argument 2 of 'USBD_CDC_SetRxBuffer' makes pointer from integer without a cast [-Wint-conversion]

   USBD_CDC_SetRxBuffer(&hUsbDeviceFS, UserRxBufferFS[0]);

STM32CubeMX自动生成的UserRxBufferFS是1维数组,我们的是2维数据。


 


因此需要把


uint8_t UserRxBufferFS[APP_RX_DATA_SIZE];

改为:


uint8_t UserRxBufferFS[2][APP_RX_DATA_SIZE];

6 试验结果

硬件照:



红色USB线用于下载,仿真调试,stlink。下面的黑色USB线用于和电脑传输用户数据,CDC。


下载好程序后,我的电脑可以自行下载了VCP驱动。可以看到虚拟出串口26。



使用网友提供的上位机软件。测得发送速度为828.48KB/s。接收速度为837.29KB/s。



打开XCOM串口调试助手。



往STM32发送55,STM32接收到0x55后,会把发送数据内的数据上传到电脑。然后对STM32发送0xaa即可停止STM32的发送。


可以看到,数据是0x00-0xff,试验中没看到明显的丢包、以及误码。但仍需进一步验证。


7. 小结

本文记录了基于STM32F4的USB2.0FS数据传输的试验过程。采用了最新的HAL库,好在网友提供了不少资料。昨天搜集资料,今天上机,现在是中午12:00。已经实现了demo功能。在此特别感谢http://bbs.21ic.com/icview-811704-1-1.html。


8. 升级到high speed


在STM32CubeMX只需要修改:


这里:



和这里:



而程序修改的步骤和上面的一样的。只是某些参数名字FS变为HS。


下载程序到板子里, 发送速度为8605.04KB/s,接收速度为26947.37KB/s。可能PCB上线路太长了。发送速度不稳定。这次的试验应该到此为止了。


 


参考资料有:


1. http://bbs.21ic.com/icview-811704-1-1.html


2. STM32CubeRepositorySTM32Cube_FW_F4_V1.21.0ProjectsSTM324xG_EVALApplicationsUSB_DeviceCDC_Standalone。这个默认安装在C:Users用户名下。


推荐阅读

史海拾趣

EUPEC [eupec GmbH]公司的发展小趣事

1999年,EUPEC从西门子独立出来,形成了Infineon(英飞凌)公司。作为Infineon的全资子公司,EUPEC继续专注于电力半导体器件的研发和生产。在这个阶段,EUPEC不断推出创新产品,如IGBT模块、晶闸管、整流二极管等,凭借其卓越的品质和技术,赢得了客户的广泛认可。

成都芯进(CrossChip)公司的发展小趣事

随着技术的不断积累和市场的逐步拓展,成都芯进电子逐渐在磁传感器芯片领域崭露头角。他们不仅实现了霍尔效应和磁阻效应磁传感器芯片的量产,还成功研发了电流传感器芯片、电机驱动芯片和电源管理芯片等系列产品。这些产品的推出,不仅丰富了公司的产品线,也进一步提升了公司的市场竞争力。

Alutronic Kuhlkorper Gmbh & Co Kg公司的发展小趣事

作为一家立志于立足中国、服务全球的电子企业,成都芯进电子始终保持着开放的国际视野。他们积极参与国际技术交流与合作,不断提升自身的技术水平和市场竞争力。同时,公司也在全球范围内拓展业务,希望将更多优质的电子产品推向国际市场,为全球消费者带来更好的使用体验。

请注意,以上故事是基于参考文章内容的合理扩展和虚构,具体细节可能与实际情况存在差异。

amcc [applied micro circuits corp]公司的发展小趣事

2023年6月,成都芯进电子宣布完成超1亿元A轮融资。这一轮融资的成功,不仅为公司的发展提供了充足的资金保障,也吸引了更多知名产业机构和投资基金的关注。公司借此机会扩大了研发团队和生产规模,进一步提升了产品的研发和生产能力。

Gang Song Electronics Co Ltd公司的发展小趣事

在加入Telit后,GainSpan的技术和产品迅速在全球范围内得到推广和应用。Telit凭借其强大的市场网络和品牌影响力,将GainSpan的低功耗WiFi模组带到了更多的国家和地区。特别是在北美、欧洲和亚洲等物联网市场发展迅速的地区,GainSpan的技术和产品更是成为了众多企业和项目的首选。通过全球化布局,Telit进一步巩固了其在物联网市场的领先地位。

全智景(Allvision)公司的发展小趣事

在电子行业快速发展的背景下,全智景公司始终保持着强烈的创新意识和敏锐的市场洞察力。公司不断推出具有创新性的产品和服务,如智能安防监控系统、无人机视觉导航系统等,满足了市场不断变化的需求。同时,全智景公司还积极投入研发资源,探索新兴技术的应用前景,为公司的未来发展储备了更多的技术储备。

问答坊 | AI 解惑

是什么原因造成有mv值dcs还显示断线?

热偶冷端和现场站安全栅处测量出一样的mv值,dcs却显示断线。拆开补偿线接到其他工作正常安全栅dcs也显示断线,用其他补偿线接到这个安全栅处dcs显示正常。(其中补偿线中间还有接头)。是什么原因造成有mv值dcs还显示断线?请教诸位专家!谢谢!…

查看全部问答>

wince6.0 连接sql server数据库

不使用.net 应用程序开发工具evc wince6.0系统可定制…

查看全部问答>

自学ARM需要买一些什么硬件

自学可行不? 是只需要仿真器呢,还是仿真器,开发板都买 我不懂.想自学.或者还需要添加些什么…

查看全部问答>

Symbol Mc1000播放声音

在这个数据采集器里,可不可以播放声音文件。 可以的话,怎样调用,请附上代码,谢谢…

查看全部问答>

MSP430G2开发板学习(五):低功耗模式下的IO学习

MSP430最大的特点是超低功耗,本次学习的是超低功耗模式下的IO操作。 /****************************************************************************** * 超低功耗频率 1.5KHZ    * P1.0有1/100的周期激活 * 超低功耗的范例 ...…

查看全部问答>

sw笨笨的STM32学前班教程之二:怎么开发

sw笨笨的STM32学前班教程之二:怎么开发目前手头的入门阶段使用的开发器概述该产品为简易STM32调试器和DEMO板一体化的调试学习设备,价格在一百多块。2、硬件配置仿真部分:USB口,reset,指示灯,JTAGDEMO部分:4按键(IO),4LED(IO),一 ...…

查看全部问答>

ADC怎么采样不到0.2V以下的电压啊。

如题! 进行背光控制的时候,发现背光的电压是在0 ~ 0.2V之间变化的,可发现ADC在0.2V以下就一直反应为0了。 请问有什么地方可设置吗。 谢谢!…

查看全部问答>

2013年电赛全国奖评审--复测名单 --G题

本帖最后由 paulhyde 于 2014-9-15 03:06 编辑 2013年电赛全国奖评审--复测名单 --G题    …

查看全部问答>

电路不起振

实际搭这样的电路 ,结果调不起振,为什么? …

查看全部问答>

SimpliciTI通信终端节点不能进入PM2模式但是可以进去PM3模式

最近在用TI的SimpliciTI协议做一个小项目,原本想让终端节点工作一段时间休眠进入PM2模式,然后用休眠定时器唤醒!我在没有移植通信协议的裸机上让单片机进入PM2模式并用休眠定时器唤醒是可以的,但是一加到移植了SimpliciTI协议的单片机上,单片机 ...…

查看全部问答>