某项目需要单片机把ADC数据上传到电脑。方法有两种:1、USB;2、以太网。百度必应了一番,发现有人用过NXP的LPC的一款带USB2.0 HighSpeed phy的片子,但是采用BGA封装,开发难度上了一个层次。这两年内使用的STM32F1、F4比较多,对其开发比较熟悉,资料也丰富。因此决定使用STM32F4跑一下CDC_Device例程,调一下这部分的数据传输。
Win7、Atollic TrueSTUDIO for STM32
STM32CubeMX v4.27.0,装上STM32CubeF4 Firmware Package V1.21.0
我采用的是STM32F4discovery。也可以是STM32F4的开发板。
需要用到的IO口资源:连接4个指示灯的GPIO(PD12-PD15)。连接了按键的PA0(但实际未用到)。连接了外部8MHz晶振的PH0和PH1。连接到USB插座的PA11和PA12。连接到STLINK仿真器的PA13、PA14。
Peripherals中需要配置RCC,STM32F4discovery未焊接外部低速晶振。因此LSE默认disable,而有8MHz晶振,因此配置为Crystal/Ceramic。
由于使用的是STLINK(STM32discovery板载stlinkv2),配置debug模式为Serial Wire。
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)。
STM32F407使用外部的8MHz晶振,Input frequency输入8后,选菜单栏中的clock Configuration -> Resolve Clock Issues即可自动为芯片内部的各个模块配置好时钟频率。这里需要注意,STM32F4内置的usb controller时钟需要48HMz才能正常工作。
在这个页面没有红色字体后,这个页面的配置也就完成了。
这个页面的各个部分采用默认值就可以了。不需要改动。(这也是我第一次跑这个demo,尽量根据前人的步骤来就不怕出错了)
生成代码前需要选择project-> Settings配置工程目录以及IDE。记得把mimimum heap size和mimmum stack size提高。有网友反映这个size低了是会出error的。我把两个size都改大了,分别设为0x600和0x1000。其他默认,点击OK。
然后点Project -> Generate Code。工程初始化已经完成。
在main.c的合适区域增加以下代码:
顾名思义,第一个参数是接受数据,双缓冲数组结构。第二个参数是发送数据。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; //// 发送数据的标志
我想知道上传到电脑的数据是否准确连续。故自行按顺序初始化了发送数组中的每个数值。
for(i=0;i
HAL_GPIO_TogglePin(LD4_GPIO_Port, LD4_Pin);
// 每次CDC_Itf_Receive()接收到新数据都会更换缓存,所以发现缓存切换过就是有新的数据。
// 这里必须保证数据处理的速度足够快,否则缓存切换了两次才处理的话,就没法识别有新数据到来了。
if (uLastRxBufIndex != uRxBufIndex)
{
// --> 指令译码开始。
for (i=0; i 指令译码开始。
for (i=0; i
uint8_t UserRxBufferFS[2][APP_RX_DATA_SIZE];
uint32_t BuffLength;
uint32_t nRxLength;
uint8_t uRxBufIndex = 0; //// 当前使用的缓冲区索引号
uint8_t uLastRxBufIndex = 0; //// 上次使用的接收缓冲区索引号
USBD_CDC_SetRxBuffer(&hUsbDeviceFS, UserRxBufferFS);
改为:
USBD_CDC_SetRxBuffer(&hUsbDeviceFS, UserRxBufferFS[0]);
把
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);
..\Src\usbd_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];
硬件照:
红色USB线用于下载,仿真调试,stlink。下面的黑色USB线用于和电脑传输用户数据,CDC。
下载好程序后,我的电脑可以自行下载了VCP驱动。可以看到虚拟出串口26。
使用网友提供的上位机软件。测得发送速度为828.48KB/s。接收速度为837.29KB/s。
打开XCOM串口调试助手。
往STM32发送55,STM32接收到0x55后,会把发送数据内的数据上传到电脑。然后对STM32发送0xaa即可停止STM32的发送。
可以看到,数据是0x00-0xff,试验中没看到明显的丢包、以及误码。但仍需进一步验证。
本文记录了基于STM32F4的USB2.0FS数据传输的试验过程。采用了最新的HAL库,好在网友提供了不少资料。昨天搜集资料,今天上机,现在是中午12:00。已经实现了demo功能。在此特别感谢http://bbs.21ic.com/icview-811704-1-1.html。
在STM32CubeMX只需要修改:
这里:
和这里:
而程序修改的步骤和上面的一样的。只是某些参数名字FS变为HS。
下载程序到板子里, 发送速度为8605.04KB/s,接收速度为26947.37KB/s。可能PCB上线路太长了。发送速度不稳定。这次的试验应该到此为止了。
参考资料有:
1. http://bbs.21ic.com/icview-811704-1-1.html
2. STM32Cube\Repository\STM32Cube_FW_F4_V1.21.0\Projects\STM324xG_EVAL\Applications\USB_Device\CDC_Standalone。这个默认安装在C:\Users\用户名下。