STM32CubeMX

使用意法半导体最新版的STM32CubeMX配置工具创建STM32 微控制器(MCU)项目,将会更直观,更高效。STM32CubeMX v.5.0的最新设计的多面板GUI界面在不改变屏幕视图的情况下,能够让用户查看更多参数,完成更多任务,从而让优化MCU配置参数变得更加轻松自如,得心应手。

STM32CubeMX帮助用户从800多款STM32产品中选择最适合的产品,配置基本硬件功能,自动生成MCU初始化代码,开启嵌入式项目开发之旅。

用户可以利用功能强大的器件配置实用工具配置微控制器参数,包括可解决冲突的引脚选择器和时钟树设置的辅助,以及能够在早期准确评估能耗需求和节能机会的功耗计算器,还有用于配置外围设备和中间件堆栈的工具,例如,TCP / IP或USB协议栈,并支持参数约束动态验证。

配置完成后,STM32CubeMX会自动生成初始化代码,支持许多常用开发环境,包括适用于IAR-EWARMKeil MDK-ARMAC6-SystemWorkbench STM32系列或独立的GCC(GNU编译器集合)工具链项目。

STM32CubeMX是既可在主要PC操作系统上运行,也可通过Eclipse插件运行的独立软件。用户可以从 www.st.com/stm32cubemx 页面免费下载,还可以一起下载STM32Cube其它软件,包括专用硬件抽象层(HAL)中间件和代码示例。

围观 371

在16年ST就推出了STM32F769I-DISCO开发板,功能搭配可算得上眼前为之一亮,无论是POE、SPDIF输入输出、MEMS麦克风、音频编解码器、还是TFT电容触摸LCD显示屏,各种器件与STM32F769NI这块芯片恰到好处搭配。这块开发板的厚重底蕴难以形容,搭配恰如其分,相得益彰,完美地与STM32F769NI这可主控芯片融合。

本章介绍STM32CubeMX的相关配置,实现使用STM32CubeMX建立基于STM32F769I-DISCO的工程。

1)选择Graphic Choice:

你会不会使用STM32CubeMX建立STM32F769I-DISCO的工程?

2)选择Graphic Choice对应参数
显示分辨率:800x480;
显示接口:DSI Command;
帧缓冲格式:24bpp-RGB888;
GFX RAM:External SDRAM32-Bit
GFX FLASH: External QuadSPI DDR FCPU/3

3)选择产品系列、产品线、封装
系列:SIM32F7
产品线:STM32F7x9
封装:TFBGA216

4)在MCU List中选择STM32F769NI
选择芯片后,点击上方的"Start Project"开始工程,在等待系统初始化后进入后边的步骤

5)在Pinout\SYS中配置Debug方式:
Debug:配置Serial Wire,分别接PA13、PA14

6)在Pinout\RTC中配置时钟源:激活RTC时钟源

7)在Pinout\RCC中配置HSE、LSE:

你会不会使用STM32CubeMX建立STM32F769I-DISCO的工程?

参考原理图,HSE:配置外部晶振;LSE:配置外部晶振。

8)在Clock Configuration配置系统时钟
时钟树上分别选择HSE、LSE,在HCLK处输入216MHz(最大频率)

你会不会使用STM32CubeMX建立STM32F769I-DISCO的工程?

9)配置LCD-TFT控制器、DSI主机及对应时钟树
LTDC显示模式配置最高的RTB888–DSI模式。根据原理图所示DSIHOST模式选择带TEPin的适配器模式:

你会不会使用STM32CubeMX建立STM32F769I-DISCO的工程?

并设置DSI_RESET所占用的PJ15

你会不会使用STM32CubeMX建立STM32F769I-DISCO的工程?

DSI的时钟树频率:TFT的最大频率值为62.5MHz。

你会不会使用STM32CubeMX建立STM32F769I-DISCO的工程?

LCD=TFT时钟树频率:计算方式 DSI通道速率*通道数/位每像素,
500Mbs*2/24=41.6666,约等于41.7MHz。

10)配置GUI所需的扩展内存(FMC方式,STemWin需要)

你会不会使用STM32CubeMX建立STM32F769I-DISCO的工程?

根据SDRAM选型和原图引脚配置,配置FMC的相关参数为如下:
SDCKE0+SDNE0(SDCKE1+SDNE1被其他功能占用,这里只能选KE0、NE0);
4M = 12bit 地址线;
BA0、BA1 均连接到SDRAM上,共4 *BANKS
32bits 数据位宽寻址;
启用32位访问方式。

你会不会使用STM32CubeMX建立STM32F769I-DISCO的工程?

11)配置GRAPHICS
第三方插件STemWin要用到CRC,先使能CRC;
使能STemWin,并选择显示接口方式LTDC-DSIHOST

12)工程小结及项目预览
完成以上配置后,一个基本图形环境搭建完成,TFBGA216引脚占用图如下,还是有很大的功能扩展空间;

你会不会使用STM32CubeMX建立STM32F769I-DISCO的工程?

功能参数配置,只选用了最基础的LTDC、DSI-Host、FMC、GPIO、NVIC功能,配置界面如下图:

你会不会使用STM32CubeMX建立STM32F769I-DISCO的工程?

经过简单的配置后,几乎不修改代码即可生成STemWin开发环境,以下为Hello World验证图,要到下图的效果还需要在STM32CubeMX上配置一些TFT屏幕控制器、DIS、内存、STemWin等设备相关参数。

你会不会使用STM32CubeMX建立STM32F769I-DISCO的工程?

原文作者是STM32中文社区用户:wolfgang2015

转自:STM32单片机

围观 551

前言

STM32 提供了丰富的音频应用外设,并得益于灵活高效的内部架构,可以支持广泛的音频应用。本文中,在简单介绍音频采集的背景知识后,从应用需求出发,确定麦克风的选用。然后,描述了 STM32 内部 DFSDM (Digital Filter for SigmaDelta Modulator)在 PDM 麦克风采集中应用。最后逐步介绍如何利用 STM32CubeMX 进行 DFSDM 设计开发,实现 PDM麦克风声音采集。

一 背景知识

声音通过声学传感器获取模拟信号,经过模数转换器,转换成二进制码 0 和 1,这些 0 和 1 便构成了音频数字信号。

PDM 麦克风能够实现上述的模拟信号获取,并输出 PDM 信号。PDM(Pulse Density Modulation)脉冲密度调制,利用脉冲密度表示模拟信号强度。

从 PDM 位流中获取数据,还需要经过如下图环节才能获得模拟信号幅度对应的数字量。

利用 DFSDM 开发 PDM 麦克风应用介绍

二 应用需求及 DFSDM 支持分析

在音频应用开发前,需要根据应用需求,对麦克风个数、支持编码类型、采样率及分辨率等进行确定。下面围绕 DFSDM在这些需求方面的支持情况进行分析。

2.1 麦克风数量

同时运行的最大麦克风数量,对于 DFSDM,由 DFSDM 中滤波器数量决定。简单理解,就是一个滤波器对应一个麦克风。注意这种简单等同并不适用于非同时采样的应用场景。

麦克风的数量不直接对应通道数量,如下图。

利用 DFSDM 开发 PDM 麦克风应用介绍

可映射任一 DFSDM 通道单元至滤波器单元。对于通道 CH(y-1),数据线可来源于 DATIN(y-1)引脚,也可来自于DATIN(y),在通道单元中可以选择获取数据的时刻(上升沿或者下降沿)。这样带来的益处是,可利用内部两个通道单元对同一个数据线上数据进行分离并处理获得采样数据。而这个应用,直接满足了双通道数据在同一条数据线上的数据采集场景。

上述描述的应用场景中,数据处理流向如下图所示:

利用 DFSDM 开发 PDM 麦克风应用介绍

注 1:图中 CLK 线硬件设计上不一定需要连接,DFSDM 可在内部关联,实现利用输出时钟作为时钟输入,具体可通过参考手册了解。

2.2 编码类型

DFSDM 支持 PDM、曼彻斯特编码,支持具有类似编码的麦克风器件,具体可通过参考手册了解。

2.3 采样率

DFSDM 通过时钟源、滤波模式、快速模式选择、过采样配置,实现不同采样率的支持。能够支持常见的8k,16k,22k,44k,48k 的采样率,也能够支持特殊应用场合所需的更高采样率,例如 192k, 384k 等。
更多关于 DFSDM 采样率介绍,以及具体计算公式可通过参考手册了解。

2.4 分辨率

DFSDM 具有 24 位数据寄存器,可通过配置实现不同分辨率的支持,有效数据最高支持到 24 位。同时,新的 HAL 库支持全硬件获取 16 位采样数据,不增加 CPU 负载。

DFSDM 分辨率由过采样率,滤波器类型和右移位器决定,更多内容可通过参考手册了解。

利用 DFSDM 开发 PDM 麦克风应用介绍

在不同处理环节数据分辨率情况如下图所示。

利用 DFSDM 开发 PDM 麦克风应用介绍

积分器最大数据输出范围如下表。例如当 FOSR 为 64,IOSR 为 1,采用 Sinc3滤波,在不考虑后续处理环节时,输出范围为±262144,分辨率能够达到 19 位。并可通过右移位器灵活获得需要的有效数据位数。

利用 DFSDM 开发 PDM 麦克风应用介绍

三 前期准备

出于将重心放在 DFSDM 应用介绍,简化其他环节考虑。本文中实现例在 ST 提供的 NUCLEO-L476RG 和 X-NUCLEOCCA02M1板展开。

考虑到利用 DFSDM 实现 PDM 麦克风采集,首先根据 UM1900 对麦克风板 X-NUCLEO-CCA02M1 进行处理,使其支持基于 DFSDM 采集的两路 PDM 麦克风。需要准备软硬件资源如下表。

利用 DFSDM 开发 PDM 麦克风应用介绍

四 实现过程

4.1 应用实现

利用 X-NUCLEO-CCA02M1 板上已有的两路 PDM 麦克风,可实现最多两路麦克风数据采集。

在本例中,先将应用需求定为两路麦克风采集,采样率为 8KHz,分辨率为 16-bit。后续介绍如何利用 STM32CubeMX生成遵循应用需求的工程,以及在获得工程后,如何启动采样,实现麦克风采集的应用。

4.2 开发流程

利用 DFSDM 开发 PDM 麦克风应用介绍

4.3 STM32CubeMX 配置实现

STM32CubeMX 操作流程如下图所示。

利用 DFSDM 开发 PDM 麦克风应用介绍

a. DFSDM 通道选择

根据 X-NUCLEO- CCA02M1 板原理图,可知在将其处理成支持 DFSDM 采集的两路麦克风时,麦克风总线引脚与STM32L476RG 连接情况如下。

利用 DFSDM 开发 PDM 麦克风应用介绍

在 STM32CubeMX 中,选择 Channel1 中“ PDM/SPI Input from ch2 and internal clock ”和 Channel2 中“ PDM/SPI Inputfrom ch2 and internal clock ”,并选择“CKOUT”,如下图所示,PC2、PB14 自动对应与 DFSDM 的 Clock out 和 Data In功能脚。

利用 DFSDM 开发 PDM 麦克风应用介绍

b. 配置通道

切换至“Configuration” 标签页,点击“Control\DFSDM”打开 DFSDM 配置界面。由于选择了通道 1 和通道 2,这里可以对这两个通道进行配置。配置情况如下图。

利用 DFSDM 开发 PDM 麦克风应用介绍

Type:配置数据读取时刻。SPI with rising edge 在时钟的上升沿读取数据;SPI with falling edge 在时钟下降沿读取数据。

Spi clock:总线时钟源。Internal SPI clock 利用内部时钟,对应为 CKOUT 时钟。
Offset:数据偏移量补偿。

Right Bit Shift : 右移位。右移位的确定,涉及到获取有效数据的位数,如“分辨率”小节中图所示。需要结合滤波器和积分器配置及分辨率需求进行确定。本文中,经过滤波器和积分器处理后输出数据分辨率为 29-bit,所以将右移位设置为 5,从而在 24 位数据寄存器中获得有效的 24-bit 数据。

Analog watchdog parameters : DFSDM 中模拟看门狗参数设置。应用例中没涉及到此功能,参数保持默认。

c. 配置滤波器

切换至“Filter0”/ “Filter1” 标签页,配置情况如下图。

利用 DFSDM 开发 PDM 麦克风应用介绍

Regular channel selection: 数据来源。选择与外部麦克风连接的通道。

Continuous mode: 转换模式。选择连续转换模式。

Trigger to start regular conversion: 启动转换的触发源。

Fast mode: 快速模式。在连续转换数据源来自于同一个通道时可启用,能够提高转换速度。本文应用例满足条件,使能Fast mode。

DMA mode : DMA 模式。如果无法使能,需参考后面“配置 DMA”小节,先完成对 DMA 参数的配置。

Inject channel selection: 注入通道选择。应用例中没涉及到此功能,参数保持默认。

Sinc Order: 滤波类型。

Fosr: 滤波器过采样率。

Iosr: 积分器过采样率。

其中,Sinc order, Fosr 和 Iosr 共同决定了积分器处理后的内部数据分辨率。根据“分辨率”小节,在如上配置时,积分器最大输出分辨率为 29 位。

d. 配置时钟

切换至“Output Clock” 标签页,配置情况如下图。

利用 DFSDM 开发 PDM 麦克风应用介绍

Selection: CKOUT 时钟源选择。

Divider: 时钟预分频因子。CKOUT = Audio clock / divider。

其中,Audio clock 来源于 SAI1 时钟,如下图配置,SAI1 时钟配置为 17.411765MHz。CKOUT 输出时钟频率为1.0242MHz。

在 Fast mode 情况下,采样率 Fs 如下:

利用 DFSDM 开发 PDM 麦克风应用介绍

利用 DFSDM 开发 PDM 麦克风应用介绍

e. 配置 DMA

切换至“DMA Settings” 标签页,配置情况如下图。

利用 DFSDM 开发 PDM 麦克风应用介绍

DMA 配置中,选择 Circular 模式,可实现循环向数据 buffer 中填充采样数据。
Data Width 设置为 Half Word,以便实现只获取数据寄存器的高 16 位数据,实现 16-bit 分辨率数据采集。

f. 生成工程

在 STM32CubeMX\Help 打开帮助文档 UM1718,参考文档,生成 IAR 工程。至此获得与硬件对应,支持两路麦克风,采样率为 8KHz,分辨率为 16-bit 的初始化软件工程。

g. 启动采样

完成上述步骤后,即可调用 HAL 库中提供的函数,启动麦克风数据采集。建议利用 DMA 方式实现麦克风采集,占用 CPU 开销最小。下述为启动采样的实现例程。在例程中通过调用 HAL_DFSDM_FilterRegularMsbStart_DMA 启动采样,这个函数实现了利用 DMA 接收 DFSDM 的数据寄存器的高 16 位数据,并传输到分配的 Buffer 空间中。

注:HAL_DFSDM_FilterRegularMsbStart_DMA()的实现需要在 DMA 配置中将 Data Width 设置为 Half Word。

由于 DMA 采用 Circular 模式,采样数据向 buffer 空间的搬运连续进行,而半传输完成和传输完成中断回调函数被执行时,意味新的数据被填充至 Buffer 空间,用户可以在其中增加处理指令,完成利用 DFSDM 的麦克风数据采集应用的开发。

#define SAMPLE_FREQ 8000
#define BYTE_PER_SAMPLE 2
#define MICROPHEN_NUMBER 2
#define FRAME_NUMBER 2
//16bit sample resolution
#define BUF_LENGTH (SAMPLE_FREQ/1000*MICROPHEN_NUMBER*FRAME_NUMBER)
…
/* Buffer 分配 */
int16_t Buf_Mic0[BUF_LENGTH];
int16_t Buf_Mic1[BUF_LENGTH];
…
main()
{
…
/*启动采样,在初始化完成后调用*/
HAL_DFSDM_FilterRegularMsbStart_DMA(&hdfsdm1_filter0,Buf_Mic0,BUF_LENGTH);
HAL_DFSDM_FilterRegularMsbStart_DMA(&hdfsdm1_filter1,Buf_Mic1,BUF_LENGTH);
…
While (1){}
…
}
…
/* I2S DMA 回调函数 */
void HAL_DFSDM_FilterRegConvHalfCpltCallback(DFSDM_Filter_HandleTypeDef *hdfsdm_filter)
{
 //半传输完成,可在此添加对采样数据的处理
}
void HAL_DFSDM_FilterRegConvCpltCallback(DFSDM_Filter_HandleTypeDef *hdfsdm_filter)
{
 //传输完成,可在此添加对采样数据的处理
}

除此之外,还可以调用 HAL_DFSDM_FilterRegularStart_DMA 启动采样,而这种实现获取的整个 32 位的数据寄存器内容,获取数据需要利用软件指令将低 8 位,非采样数据移除。同时这种实现,需要在 DMA 配置中,将 Data Width 配置为 Word。

五 小结

本文虽然尽可能详细的介绍在 PDM 麦克风采集中,DFSDM 作用及实现步骤。但并没有涉及到所有 DFSDM 支持功能的介绍,而是侧重于 DFSDM 在 PDM 麦克风采集中涉及的功能介绍,更多 DFSDM 功能介绍可以通过参考手册了解。另外不同系列 STM32 的 DFSDM 支持情况会略有差异,需以对应型号的参考手册为准。

来源:ST

围观 697

前言

在这篇文章中,我将介绍如何从零开始建立一个以太网工程。

ST 推出的 Nucleo-144 板子上集成了以太网接口,所以在本文中,将以 STM32F746-Nucelo 板为例,通过CubeMXv4.18 来新建一个 TCPEchoserver 的程序。

用 CubeMX 建立基于 STM32F746-Nucleo 的工程

用 CubeMX 进行初始化配置

这回我们直接选择 STM32F746-Nucleo 板上对应的芯片 STM32F746ZGT6U,而不是选择 STM32F746-Nucleo 板。

1. 新建一个 Project,在向导中选择 STM32F746ZGT6U。

从零开始使用 CubeMX 创建以太网工程

这个时候我们看到的还是一个空的工程。如下图:

从零开始使用 CubeMX 创建以太网工程

2.外设使能,引脚配置

2.1 以太网外设引脚配置

Nucleo-144 板上用的 PHY LAN8742A,RMII 接口。在 Cubemx 中使能 ETH 外设,选择 RMII 接口。Cubemx 会自动配置对应的以太网接口。如下图:

从零开始使用 CubeMX 创建以太网工程

STM32 的很多引脚都有复用功能,同一个功能也可以 remap 到不同的引脚。所以这里要记得将 CubeMX 自动配置的引脚和实际电路中使用的引脚进行对比,保证是一致的。

从 UM1974 中可以找到 Nucleo-144 板上以太网引脚分配表。对比这张表格和 CubeMX 的默认配置,会发现 PB11,PB12 引脚在 STM32F746-Nucleo 板中没有用做以太网的接口,而是用作其他用途了。

STM32F746-Nucleo 上的引脚分配:

从零开始使用 CubeMX 创建以太网工程

CubeMX 的默认分配:
从零开始使用 CubeMX 创建以太网工程

在 Cubemx 中修改引脚配置:
修改方法见下图,用同样的方法配置 PG11 和 PG13。配置 PG11 和 PG13 后,对应 PB11 和 PB12 会自动清除之前的配置,以免冲突。
从零开始使用 CubeMX 创建以太网工程

到现在位置,已经将所有的 GPIO 口都配置好了。
在 Configuration 页面中,还可以看到所有配置的 GPIO。并可以做进一步的配置,这里就先用默认的设置。
从零开始使用 CubeMX 创建以太网工程

2.2 使能 LwIP 协议栈

在这个工程内,我们会用到 LwIP 协议栈,所以还需要在这一页的 Middlewares 部分将 LWIP 勾选上。之后就可以在Configuration 页面对 LWIP 协议栈进行配置了。

从零开始使用 CubeMX 创建以太网工程

3.时钟配置

接下来进行时钟配置。CubeMX 默认系统时钟 16MHz,但以太网外设需要至少 25MHz 的系统时钟,所以这里会看到 Clock Configuration 页面显示"X"

打开 Clock Configuration 页面会自动跳出一个提示框,可以选择让 CubeMX 来帮你自动调整时钟配置,也可以自己手动进行调整。这里,我选择让 CubeMX 自动配置,CubeMX 会自动将时钟配成 216MHz。

从零开始使用 CubeMX 创建以太网工程

4. 配置以太网参数

从零开始使用 CubeMX 创建以太网工程

在 Parameter Settings 页面,可以配置 MAC 地址,PHY 的地址,是否进行自动协商等。
这里,我们设置了 MAC 地址为本地地址 02 :00 :0 :00 :00 :00。LAN8742a 的 PHY 地址由上电时 PHYAD0 的状态决定。根据STM32F746-Nucleo 板的原理图,设置 PHY 地址为 0。

从零开始使用 CubeMX 创建以太网工程

接收数据的模式有轮询和中断两种方式,中断方式需要和操作系统一起使用,这里我们没有使用任何操作系统,所以在 RX Mode 这一项只能选择 Polling Mode。

最后一项是”TX IP Header Checksum Computation”,STM32 的 MAC 控制器可以在发送数据时自动添加 IP 数据报的 checksum,如果需要这项功能,就将这一项设置为“By hardware”

在 Advanced Parameters 页,可以根据所用的 PHY 修改寄存器的地址和一些 MASK 的设置。因为 STM32F746 的两款开发板上用的都是 PHY LAN8742A,所以 CubeMX 中默认的配置是以 LAN8742A 为例进行设置的。所以这里,我们不需要做任何修改就可以直接用。但如果是其他的 PHY,可以在 PHY 这一项选择“user PHY”,然后根据所用 PHY 的数据手册,配置下面的参数,对于部分无法通过 CubeMX 进行配置的参数,需要手动的修改代码。将有冲突的地方删除,或者添加某个功能。

Advanced Parameters 页分为三个部分:

• External PHY Configuration 。复位延时,读/写超时的参数设置
• Common :External PHY Configuration。PHY 的基础寄存器配置,这部分寄存器对于大部分 PHY 都是相同或类似的。
• Extended :External PHY Configuration。 PHY 的扩展寄存器配置,这部分对于每个 PHY 都是不一样的。如果是使用非 CubeMX 默认的 PHY,这部分内容需要特别注意。

从零开始使用 CubeMX 创建以太网工程

4. 修改 LWIP 的参数

配置好以太网的参数后,点击 OK,回到 CubeMX 的配置界面。选择 LWIP 继续进行参数配置。

从零开始使用 CubeMX 创建以太网工程

首先是 GeneralSettings 页面,在这里我们可以看到 LWIP 的版本号。配置 IP 地址信息,可以选择通过 DHCP 的方式动态分配 IP,也可以分配一个静态的 IP 地址。这里,我们选择配置静态的 IP 地址 192.168.0.10,子网掩码 255.255.255.0,网关192.168.0.1。ICMP 协议打开,因为我们用的是 TCP 协议,所以把 UDP 协议关掉。

不用担心不知道每项参数是做什么用的,选择每一项参数后都会在窗口的底部显示该项参数的解释

从零开始使用 CubeMX 创建以太网工程

在 Key Options 这一页里,有更多的参数可以配置。关于接收/发送内存的配置也是在这里。选择右上方的“Show Advanced Parameters”后,还有更多的参数配置项。这里,我们也可以不做修改,使用默认值。CubeMX 中每个参数项的名称和代码中的名称相同,这样也方便了在代码中进行查找。

从零开始使用 CubeMX 创建以太网工程

到此为止,我们在 CubeMX 中需要做的配置就全部完成了。选择 Project——>Generate Code,生成初始的工程。

从零开始使用 CubeMX 创建以太网工程

添加用户代码

用 IAR 打开前面已经生成好的工程。我们还需要两步就可以完成一个简单的 TCP EchoServer 程序了。

1.新建 tcp_echoserver.c 文件,在 tcp_echoserver.c 里要做下面这几件事情:

1)新建一个 tcp_echoserver_pcb(调用 tcp_new 函数);
2)将新建的 tcp_echoserver_pcb 与要监听的端口绑定(调用 tcp_bind 函数)
3)转成监听状态(调用 tcp_listen 函数)
4)注册回调函数 tcp_echoserver_accept,当有新连接建立后会调用该函数(调用 tcp_accept 函数)
5)注册回调函数 tcp_echoserver_recv,当该连接接收到数据后会调用该函数(调用 tcp_recv 函数)
6)完成 tcp_echoserver_recv 函数,在该函数内,将收到的数据再发出去。

需要注意,本文的目的是示例如何用 CubeMX 建立一个简单的 TCP EchoServer 程序,所以考虑的都是最基本简单的情况。

比如,在回发数据部分,我们假设 Client 发来的数据都在一个 Pbuf 的大小以内。
完成 tcp_echoserver.c 后,将其加入到工程项目中。

#include "stats.h"
#include "tcp.h"
void tcp_echoserver_init(void);
static err_t tcp_echoserver_accept(void *arg, struct tcp_pcb *newpcb,err_t err);
static err_t tcp_echoserver_recv(void *arg, struct tcp_pcb *tpcb, struct pbuf *p,err_t
err);
static struct tcp_pcb *tcp_echoserver_pcb;
void tcp_echoserver_init(void)
{
 err_t err;
 tcp_echoserver_pcb = tcp_new();

 if(tcp_echoserver_pcb !=NULL)
 {
 err = tcp_bind(tcp_echoserver_pcb,IP_ADDR_ANY,7);
 if(err == ERR_OK)
 {
 tcp_echoserver_pcb = tcp_listen(tcp_echoserver_pcb);
 tcp_accept(tcp_echoserver_pcb,tcp_echoserver_accept);
 }
 else
 {
 memp_free(MEMP_TCP_PCB, tcp_echoserver_pcb);
 }

 }

}
static err_t tcp_echoserver_accept(void *arg, struct tcp_pcb *newpcb,err_t err)
{
 /* initialize lwip tcp_recv callback function for newpcb */
 tcp_recv(newpcb, tcp_echoserver_recv);

 return ERR_OK;
}
static err_t tcp_echoserver_recv(void *arg, struct tcp_pcb *tpcb, struct pbuf *p,err_t
err)
{

 tcp_write(tpcb,p->payload,p->len,1);
 pbuf_free(p);
 return ERR_OK;
}

2. 在 main 函数中添加 tcp_echoserver_init(),在 while(1)中添加 MX_LWIP_Process()查询接收数据。记得要将代码加在/*USER CODE BEGIN*/和/*USER CODE END*/之间,这样才不会在下次用 CubeMX 生成代码时被覆盖掉。

extern void tcp_echoserver_init(void);
int main(void)
{
 MX_LWIP_Init();

 /* USER CODE BEGIN 2 */
 tcp_echoserver_init();
 /* USER CODE END 2 */
 /* Infinite loop */
 /* USER CODE BEGIN WHILE */
 while (1)
 {
 /* USER CODE END WHILE */
 /* USER CODE BEGIN 3 */
 MX_LWIP_Process();
 }
 /* USER CODE END 3 */
}

一个简单的 TCP Echoserver 程序就完成了。

测试结果

我们来看一看 ping 测试和 TCP 测试工具的结果
1. 通过电脑(192.168.0.11)ping STM32F746-Nucleo 板(192.168.0.10)

从零开始使用 CubeMX 创建以太网工程

2.通过 TCP 测试工具模拟客户端,向 STM32F746-Nucleo 板发一串数据。
从零开始使用 CubeMX 创建以太网工程

测试结果说明我们刚刚建立的 TCP EchoServer 程序已经能正常工作了。

来源: eefocus

围观 497

前言

客户在做 USB 通讯的时候,基本的需求就是发送某些数据到 USB host 端,同时接收一些数据从 USB Host 端,那么如何快速的建立一个工程并验证数据是否正确呢?下边我们就结合 STM32F072 的评估板(其他的 STM32xx 系列的实现方式都是类似的)来快速实现一个简单的数据收发实验。

问题分析

USB Host 软件

PC 端软件使用 ST 免费提供的 Usb Hid Demonstrator。这个软件可以在 ST 官网上免费下载到。连接
地址:
STSW-STM32084,此软件调用的是 windows 标准的 HID 类驱动,所以无需安装任何驱动程序及可运行。

通过 STM32CubeMX 生成 HID 双向通讯工程

下载安装完这个软件之后,我们就可以开始开发 STM32 的 USB 从机程序了。
首先,打开 STM32CubeMX,新建工程,选择 STM32F072B-DISCOVERY 开发板。
通过 STM32CubeMX 生成 HID 双向通讯工程

其次,在 Pinout 选项中,开打 USB 的 device 功能。
通过 STM32CubeMX 生成 HID 双向通讯工程

并在 Middleware 中选择开启 class for IP 中的 custom Human Interface Device(HID)。
通过 STM32CubeMX 生成 HID 双向通讯工程

点击“保存”后直接生成工程。我们这里以生成 IAR 工程为例,项目名叫做 HID
通过 STM32CubeMX 生成 HID 双向通讯工程

这样我们的工程就基本成功了,但是还缺少最最关键的一步,就是 USB 主机和从机的通讯“协议”,
这个协议在那里实现呢?因为我们 Host 端软件已经是 Usb Hid Demonstrator,那么这边的协议就已经
固定了(其实在实际的开发中大多是主机端和从机相互沟通后,软件自行修改的),从机只需要对应
这套协议即可。
将如下代码复制,替换掉 usbd_custom_hid_if.c 文件中的同名数组。
__ALIGN_BEGIN static uint8_t CUSTOM_HID_ReportDesc_FS [USBD_CUSTOM_HID_REPORT_DESC_SIZE]
__ALIGN_END =
{
 0x06, 0xFF, 0x00, /* USAGE_PAGE (Vendor Page: 0xFF00) */
 0x09, 0x01, /* USAGE (Demo Kit) */
 0xa1, 0x01, /* COLLECTION (Application) */
 /* 6 */

 /* LED1 */
 0x85, LED1_REPORT_ID, /* REPORT_ID (1) */
 0x09, 0x01, /* USAGE (LED 1) */
 0x15, 0x00, /* LOGICAL_MINIMUM (0) */
 0x25, 0x01, /* LOGICAL_MAXIMUM (1) */
 0x75, 0x08, /* REPORT_SIZE (8) */
 0x95, LED1_REPORT_COUNT, /* REPORT_COUNT (1) */
 0xB1, 0x82, /* FEATURE (Data,Var,Abs,Vol) */

 0x85, LED1_REPORT_ID, /* REPORT_ID (1) */
 0x09, 0x01, /* USAGE (LED 1) */
 0x91, 0x82, /* OUTPUT (Data,Var,Abs,Vol) */
 /* 26 */

 /* LED2 */
 0x85, LED2_REPORT_ID, /* REPORT_ID 2 */
 0x09, 0x02, /* USAGE (LED 2) */
 0x15, 0x00, /* LOGICAL_MINIMUM (0) */
 0x25, 0x01, /* LOGICAL_MAXIMUM (1) */
 0x75, 0x08, /* REPORT_SIZE (8) */
 0x95, LED2_REPORT_COUNT, /* REPORT_COUNT (1) */
 0xB1, 0x82, /* FEATURE (Data,Var,Abs,Vol) */

 0x85, LED2_REPORT_ID, /* REPORT_ID (2) */
 0x09, 0x02, /* USAGE (LED 2) */
 0x91, 0x82, /* OUTPUT (Data,Var,Abs,Vol) */
 /* 46 */

 /* LED3 */
 0x85, LED3_REPORT_ID, /* REPORT_ID (3) */
 0x09, 0x03, /* USAGE (LED 3) */
 0x15, 0x00, /* LOGICAL_MINIMUM (0) */
 0x25, 0x01, /* LOGICAL_MAXIMUM (1) */
 0x75, 0x08, /* REPORT_SIZE (8) */
 0x95, LED3_REPORT_COUNT, /* REPORT_COUNT (1) */
 0xB1, 0x82, /* FEATURE (Data,Var,Abs,Vol) */

 0x85, LED3_REPORT_ID, /* REPORT_ID (3) */
 0x09, 0x03, /* USAGE (LED 3) */
 0x91, 0x82, /* OUTPUT (Data,Var,Abs,Vol) */

 /* 66 */

 /* LED4 */
 0x85, LED4_REPORT_ID, /* REPORT_ID 4) */
 0x09, 0x04, /* USAGE (LED 4) */
0x15, 0x00, /* LOGICAL_MINIMUM (0) */
 0x25, 0x01, /* LOGICAL_MAXIMUM (1) */
 0x75, 0x08, /* REPORT_SIZE (8) */
 0x95, LED4_REPORT_COUNT, /* REPORT_COUNT (1) */
 0xB1, 0x82, /* FEATURE (Data,Var,Abs,Vol) */

 0x85, LED4_REPORT_ID, /* REPORT_ID (4) */
 0x09, 0x04, /* USAGE (LED 4) */
 0x91, 0x82, /* OUTPUT (Data,Var,Abs,Vol) */
 /* 86 */

 /* key Push Button */
 0x85, KEY_REPORT_ID, /* REPORT_ID (5) */
 0x09, 0x05, /* USAGE (Push Button) */
 0x15, 0x00, /* LOGICAL_MINIMUM (0) */
 0x25, 0x01, /* LOGICAL_MAXIMUM (1) */
 0x75, 0x01, /* REPORT_SIZE (1) */
 0x81, 0x82, /* INPUT (Data,Var,Abs,Vol) */

 0x09, 0x05, /* USAGE (Push Button) */
 0x75, 0x01, /* REPORT_SIZE (1) */
 0xb1, 0x82, /* FEATURE (Data,Var,Abs,Vol) */

 0x75, 0x07, /* REPORT_SIZE (7) */
 0x81, 0x83, /* INPUT (Cnst,Var,Abs,Vol) */
 0x85, KEY_REPORT_ID, /* REPORT_ID (2) */

 0x75, 0x07, /* REPORT_SIZE (7) */
 0xb1, 0x83, /* FEATURE (Cnst,Var,Abs,Vol) */
 /* 114 */

 /* Tamper Push Button */
 0x85, TAMPER_REPORT_ID,/* REPORT_ID (6) */
 0x09, 0x06, /* USAGE (Tamper Push Button) */
 0x15, 0x00, /* LOGICAL_MINIMUM (0) */
 0x25, 0x01, /* LOGICAL_MAXIMUM (1) */
 0x75, 0x01, /* REPORT_SIZE (1) */
 0x81, 0x82, /* INPUT (Data,Var,Abs,Vol) */

 0x09, 0x06, /* USAGE (Tamper Push Button) */
 0x75, 0x01, /* REPORT_SIZE (1) */
 0xb1, 0x82, /* FEATURE (Data,Var,Abs,Vol) */

 0x75, 0x07, /* REPORT_SIZE (7) */ 
 0x81, 0x83, /* INPUT (Cnst,Var,Abs,Vol) */
 0x85, TAMPER_REPORT_ID,/* REPORT_ID (6) */

 0x75, 0x07, /* REPORT_SIZE (7) */
 0xb1, 0x83, /* FEATURE (Cnst,Var,Abs,Vol) */
 /* 142 */

 /* ADC IN */
 0x85, ADC_REPORT_ID, /* REPORT_ID */
 0x09, 0x07, /* USAGE (ADC IN) */
0x15, 0x00, /* LOGICAL_MINIMUM (0) */
 0x26, 0xff, 0x00, /* LOGICAL_MAXIMUM (255) */
 0x75, 0x08, /* REPORT_SIZE (8) */
 0x81, 0x82, /* INPUT (Data,Var,Abs,Vol) */
 0x85, ADC_REPORT_ID, /* REPORT_ID (7) */
 0x09, 0x07, /* USAGE (ADC in) */
 0xb1, 0x82, /* FEATURE (Data,Var,Abs,Vol) */
 /* 161 */

 0xc0 /* END_COLLECTION */

注意:这里一定要覆盖“同名”数组,千万不要覆盖错了。
之后将如下代码复制到 usbd_custom_hid_if_if.h 中。

#define LED1_REPORT_ID 0x01
#define LED1_REPORT_COUNT 0x01
#define LED2_REPORT_ID 0x02
#define LED2_REPORT_COUNT 0x01
#define LED3_REPORT_ID 0x03
#define LED3_REPORT_COUNT 0x01
#define LED4_REPORT_ID 0x04
#define LED4_REPORT_COUNT 0x01
#define KEY_REPORT_ID 0x05
#define TAMPER_REPORT_ID 0x06
#define ADC_REPORT_ID 0x07

最后在 usbd_conf.h 文件中将 USBD_CUSTOM_HID_REPORT_DESC_SIZE 的定义值修改为 163
(默认值是 2)

#define USBD_CUSTOM_HID_REPORT_DESC_SIZE 163 //2

为什么这样修改呢? 简单说一下其中关键值的含义。这个 HID 的报文描述符其实定义了 8 个部分(条
目)的功能定义,分为 LED1,LED2,LED3,LED4,按键输入,篡改按键输入和 ADC 输入。每一个
部分基本的格式都是固定的。以 LED1 为例(其他条目可自行对照文档解析):
0x85, LED1_REPORT_ID, 含义是这个功能的 ID 号是 LED1_REPORT_ID(宏定义为 0x01)
这个 ID 号是每次报文发送的时候最先被发送出去的(USB 都是 LSB)字节,之后跟着的才是我们实际
有效的数据/指令,到底是数据还是指令,就看你的应用程序如何去解析这个数据了。

0x09, 0x01,          这个功能序号为 1,后边的序号依次递加,没什么好说的

0x15, 0x00,         这个是规定逻辑最小值为 0
0x25, 0x01,          这个是规定逻辑最大值为 1

上边的这两条语句规定了跟在报文 ID 后边的数据范围,最大值是 1,最小值是 0.(因为我们的 LED 也
就只有灭和亮两种状态)

0x75, 0x08,          这个是报文的大小为 8,只要别写错就行了

0x95, LED1_REPORT_COUNT, 这个是说下边有 LED1_REPORT_COUNT (宏定义为 1)个项目会被添加,
即这个功能的数量是 1 个
0xB1, 0x82,           这个是规定能够发送给从机设备的数据信息
0x91, 0x82,            这个规定了这个功能的数据方向是输出(USB 的方向都是针对主机来说的)

总结一下,通过这个报文描述符,我们就告诉了主机,在 HID 中有一个功能 ID 为 1 的功能,其方向是
从主机到从机,每次发送 1 个有效数据(前边的 ID 是都要含有的),这个数据可以是 0 或者是 1.
关于 HID 报文描述符的详细信息,您可以在下边的网址下载一篇叫做《Device Class Definition for
HID》的文档来参考。
http://www.usb.org/developers/hidpage

这样基本的程序框架就已经成功了。此时我们可以先编译一下,看看是否有任何遗漏的或者笔误。如
果编译是正确的,那么我们就可以先下载到硬件开发板上,连接到 PC 端,看看是否可以枚举出设备。
如果您前边的修改都是正确的,那么在 PC 的设备管理器中会看到如下图所示的内容。

通过 STM32CubeMX 生成 HID 双向通讯工程

注意:开发板上有两个一模一样的 Mini USB 接口,一个是 USB USER,另 一个是 USB ST-link,下
载代码的时候用 USB ST-Link,连接电脑运行程序的时候要用 USB USER。
此时我们的 USB 枚举就完成了,这个是 USB 通讯的关键步骤,之后的应用通讯内容都是通过
这个枚举工程来进行“规划”的。

数据发送

就类似串口通讯,我们首先做一个数据的发送工作。
在 Main.c 文件中,我们在 while(1)的主循环中增加我们的发送函数,主要就是调用发送报文的 API:
USBD_CUSTOM_HID_SendReport()

 /* USER CODE BEGIN 2 */
 uint8_t i=0;
 sendbuffer[0]=0x07; //这个是 report ID,每次发送报文都需要以这个为开始,这样主机才能正确
//解析后边的数据含义
 sendbuffer[1]=0x01; //这个是实际发送的数据,可以自由定义,只要不超过报文描述符的限制
 /* USER CODE END 2 */
 /* Infinite loop */
 /* USER CODE BEGIN WHILE */
 while (1)
 {

 HAL_Delay(100); //延迟 100ms
 sendbuffer[1]++; //每次发送都将变量自加 1
 USBD_CUSTOM_HID_SendReport(&hUsbDeviceFS,sendbuffer,2);//发送报文
 /* USER CODE END WHILE */
 /* USER CODE BEGIN 3 */
 }
 /* USER CODE END 3 */

编译后下载到 MCU 内,连接上位机软件即可看到如下所示的进度条在不断的增长。

通过 STM32CubeMX 生成 HID 双向通讯工程

这个就是我们上传到的数据在上位机的图形显示,你也可以看 Input/output transfer 里的数据变化。
通过 STM32CubeMX 生成 HID 双向通讯工程

这样看起来是不是更像是串口调试助手了?嘿嘿 本来机制就差不多的。

数据接收

MCU 的 USB 数据是如何接收的呢?是不调用一个类似于串口接收的 API 呢?
不是的!USB 的数据接收都是在中断中完成的,在新建的工程中,我们在函数
CUSTOM_HID_OutEvent_FS 内增加如下代码。

static int8_t CUSTOM_HID_OutEvent_FS (uint8_t event_idx, uint8_t state)
{
 /* USER CODE BEGIN 6 */ switch(event_idx)
 {
 case 1: /* LED3 */
 (state == 1) ? HAL_GPIO_WritePin(LD3_GPIO_Port,LD3_Pin,GPIO_PIN_SET) :
 HAL_GPIO_WritePin(LD3_GPIO_Port,LD3_Pin,GPIO_PIN_RESET);
 break;
 case 2: /* LED4 */
 (state == 1) ? HAL_GPIO_WritePin(LD4_GPIO_Port,LD4_Pin,GPIO_PIN_SET) :
 HAL_GPIO_WritePin(LD4_GPIO_Port,LD4_Pin,GPIO_PIN_RESET);
 break;
 case 3: /* LED5 */
 (state == 1) ? HAL_GPIO_WritePin(LD5_GPIO_Port,LD5_Pin,GPIO_PIN_SET) :
 HAL_GPIO_WritePin(LD5_GPIO_Port,LD5_Pin,GPIO_PIN_RESET);
 break;
 case 4: /* LED6 */
 (state == 1) ? HAL_GPIO_WritePin(LD6_GPIO_Port,LD6_Pin,GPIO_PIN_SET) :
 HAL_GPIO_WritePin(LD6_GPIO_Port,LD6_Pin,GPIO_PIN_RESET);
 break;
 default:
 break;
 }

 return (0);
 /* USER CODE END 6 */
}

编译之后下载到 MCU 内,通过 USB USER 连接到 PC 端,打开 Usb Hid Demonstrator,我们可以通
过勾选右下角的图形界面来实现控制开发板上的 LED 电量或者关闭。

通过 STM32CubeMX 生成 HID 双向通讯工程

当然,这个是通过图像化的界面来进行控制,你也可以通过 Input/output transfer 中的写入对话框来完
成这个操作。注意,写入的第一个字节是 ID,表示你想控制的是哪个 LED;第二个字节是 0 或者是 1,表示你想让这个 LDE 的状态变成灭还是亮。
通过 STM32CubeMX 生成 HID 双向通讯工程

总结:

本范例程序是为了快速实现 USB 从机设备与主句设备双向通讯目的,其初始化代码是用STM32CubeMX 来生成的,大大降低了工程师开发 USB 设备的难度(尤其是是入门阶段的难度)。从这个工程的基础上,工程师可以比较方便的建立好框架工程并,对其中的代码进行研究,进而移植或增加自己的应用代码。

围观 486
订阅 RSS - STM32CubeMX