65.9K
CodeProject 正在变化。 阅读更多。
Home

开始使用 STMicroelectronics 的 ST32F3DISCOVERY 微控制器板

starIconstarIconstarIconstarIconstarIcon

5.00/5 (6投票s)

2021年10月14日

CPOL

25分钟阅读

viewsIcon

13857

downloadIcon

156

STM32CubeIDE 与 ST32F3DISCOVERY 开发板的使用介绍和首次上手。

引言

STM32F3DISCOVERY 开发板是意法半导体 (STMicroelectronics) 推出的一款开发板,包含一个 STM32F303VCT6 微控制器(具有 256KB 闪存和 48KB RAM,以及两个 mini-B USB 连接器)、几个 ST MEMS 器件、一个用户按键和大约十个不同颜色的 LED。它是 ST 提供的几个 DISCOVERY 原型和实验开发板之一,旨在让用户能够使用意法半导体产品线中的微控制器。

我曾用 Arduino UNO、ESP32 和 ATMega 2560 微控制器进行过实验,觉得尝试一种完全不同的微控制器会很有趣。

我原希望能使用 Arduino IDE,但遇到了问题,所以改用制造商意法半导体的 STM32CubeIDE。现在我相信,尽管需要学习新软件,但使用 ST IDE 比使用 Arduino IDE 要容易得多。

我使用的是戴尔 i7-7700 上的 Windows 10,不过 ST IDE 也有 Linux 版本。我遇到了一些挫折并从中吸取了教训,但总的来说,尽管 ST IDE 复杂,但使用起来却出奇地容易。

我喜欢 DISCOVERY 产品的地方是(它有多种变体,具有不同的意法半导体产品,功能各异),与其他我用过的微控制器开发板不同,该产品内置了许多设备,只需开发板和一根 mini-B 转 Type A 的 USB 线缆即可进行实验。

缺点是意法半导体产品线主要面向商业微控制器应用,因此这些产品周围没有像 Arduino 和 ESP 产品那样活跃的创客社区。

STM32F3DISCOVERY 开发板除了 USB 之外,也缺乏任何外部通信功能。它没有 WiFi 或蓝牙功能。

STM32F3DISCOVERY 开发板与 Arduino Uno 开发板的一个主要区别是,**引脚是 3V 而不是 5V**,这降低了功耗。您必须小心使用的元件,以确保它们在 STM32 芯片的 3 伏范围内,否则有损坏芯片的风险。开发板上有 3V 和 5V 电源以及接地引脚,但请始终记住 GPIO 引脚是 3V 的电源或接收器。

开发板通过 USB 连接器供电,可以是 ST-LINK USB 连接器,也可以是 USER USB 连接器。这意味着您需要注意使用开发板上 3V 或 5V 源引脚的任何设备所消耗的电流。

通过 ST-LINK USB 连接器与开发板通信与 Arduino UNO 或 ESP32 有些不同。它更像一个大容量存储设备,而不是一个串行 COM 端口。然而,USER USB 应该会显示为一个串行 COM 端口。Linux 可能可以直接工作,但在 Windows 上使用 USER USB 时可能会遇到问题。详情请参阅 嵌入式 - stm32f3 'USER USB' 未检测到 - Stack Overflow

背景

我从 Mouser Electronics 购买了我的 STM32F3DISCOVERY 开发板,其价格比亚马逊低。我发现该开发板有两个 mini-B 型 USB 连接器(一个用于 ST-LINK USB 连接器,一个用于 USER USB 连接器),因此标准的手机 USB Type A 转 USB mini-A 线缆不起作用。幸运的是,我有一个带有 mini-B USB 连接器的读卡器设备,能够使用该线缆。请参阅 USB Mini A 和 Mini B 有什么区别? 以及 维基百科主题:USB 硬件

点击下方图片可查看完整尺寸图片以了解详情。开发板顶部边缘的两个 mini-B USB 连接器中,左侧的是用于调试的 ST-LINK,右侧的是用于标准 Type B USB 连接的。板子上有两个 50 针连接器,左边缘是 P1,右边缘是 P2。下边缘中央有一组八个不同颜色的 LED,呈圆形排列。P1 连接器中间左侧的蓝色按钮连接到一个 GPIO 引脚,可以用作输入。P2 连接器中间右侧的黑色按钮是 RESET 按钮,用于复位/重启开发板。

另请参阅用户手册 UM1570,带 STM32F303VC MCU 的 Discovery 套件 - 用户手册,其中提供了有关开发板的更多详细信息。

我原希望能使用 Arduino IDE,因为我熟悉它,并且看到有文章表明我可以将其与 STM32F3DISCOVERY 开发板一起使用。然而,经过短暂尝试后,我放弃了这种方法,转而使用意法半导体的 STM32CubeIDE。请参阅 ST 网站上的文章 STM32CubeIDE:首个内置 STM32CubeMX 的免费 ST IDE。我只是在 Google 上搜索了 STM32CubeIDE,下载链接就在 URL STM32CubeIDE - 适用于 STM32 的集成开发环境 - STMicroelectronics,是列出的第一个项目。

意法半导体在 YouTube 上发布了许多视频,我建议您花些时间浏览列表并观看 STM32CubeIDE 的介绍。该 IDE 似乎建立在 Eclipse 平台之上,如果您不小心,您会发现自己为了完成某项任务而与 IDE 作斗争,而不是真正完成它。

意法半导体还在其网站上设有教育专区:STM32 MOOCs(大型开放式在线课程) - STMicroelectronics

STM32CubeIDE 快速概览

STM32CubeIDE 比 Arduino IDE 复杂得多,并且基于 Eclipse 平台构建。我有一个 32 英寸的 LCD 显示器,分辨率为 3840 x 2160,我发现这个尺寸和分辨率非常有帮助,因为 IDE 似乎占用了很多屏幕空间。

如果您有 Eclipse 经验,那么使用此 IDE 会更舒适。IDE 使用 Eclipse 工作区概念,因此多个独立的项目可以位于同一个工作区中。您也可以为您的工作拥有多个独立的独立工作区。您可以通过在 IDE 左侧的项目资源管理器面板中找到项目文件树中的 `.loc` 文件来打开一个项目。大多数对象,例如文件和文件夹,都可以通过双击打开或关闭。

IDE 还使用 Perspective(透视图)概念。当您打开 `.loc` 文件时,您处于一种 Perspective 或演示模式,当您打开源代码文件时,您可以切换到不同的 Perspective。有些 Perspective 在视觉上比其他 Perspective 更复杂,其中 Perspective 有多个窗格划分窗口。然而,各个窗格可以折叠或展开,也可以调整大小。

您可以向项目添加额外的源文件。有关该过程,请参阅 如何在 STM32CubeIde 中包含库文件 | VIDEO25 - YouTube

STM32CubeIDE 的基本工作流程

使用 STM32CubeIDE 的基本工作流程是

  • 创建新项目并设置目标选择
  • 指定微控制器各种引脚的设置
  • 生成代码,创建包含初始化代码的模板或骨架
  • 在注释标记的区域修改生成的代码
  • 构建并运行您的应用程序,将其下载到 STM32F3DISCOVERY 开发板

每次修改引脚设置时,您都需要重新执行生成代码操作,创建新版本的 `main.c` 文件。在 `main.c` 源文件中,有特殊的区域,用注释标记,您可以将自己的源代码放在这些区域中,生成器会保留它们。这些注释区域之外的源代码将在生成新版本的 `main.c` 时被删除。

生成器将其文件放入项目文件树中的 `Core/Inc` 和 `Core/Src` 文件夹,其中包含 `main.c` 以及项目所需的任何 ST 源代码文件。

你也可以把自己的源文件放在这些文件夹里,但为了整洁和有条理,最好是创建一个单独的文件夹来存放东西。

使用 STM32CubeIDE

启动新项目

使用 IDE 和 STM32F3DISCOVERY 开发板开始新项目时,您的首要任务是设置目标设备。这时您会发现 ST 提供了令人眼花缭乱的设备数量。STM32 产品线由复杂多样的设备组成,而 IDE 旨在适应所有这些设备。

选择目标设备后,IDE 会启动一个微控制器图像,其中显示了引脚分布和封装,并在一个框架中包含多个选项卡。我唯一使用的选项卡是 **Pinout & Configuration**(引脚配置),您可以在其中为项目配置所使用的引脚。

你可以在 **Pinout & Configuration**(引脚配置)选项卡中从 **Pinout view**(引脚视图)切换到 **System view**(系统视图)。**Pinout view** 更具图形化,而 **System view** 则是文本表格。你很可能会发现自己在这两种视图之间来回切换。你也可以组合这些视图。总而言之,IDE 在向用户呈现信息的方式上似乎很灵活,但这确实需要一些实验。

在 **Pinout view** 中,您可以缩放和移动微控制器芯片的引脚分布图,以便读取、选择或更改引脚。鼠标滚轮执行缩放功能,您可以点击并按住鼠标左键,然后拖动图像,再释放鼠标按钮。

在 **Pinout view** 或 **System view** 中,您需要指出将使用哪些引脚及其设置,然后 IDE 将生成初始化代码,并为您提供一个模板,供您插入自己的源代码。

引脚用户标签有助于组织代码。这些标签的格式似乎类似于“B1 [蓝色按键]”(不带引号),这表示引脚名称前缀是 B1,文本“[蓝色按键]”是对引脚用途的注释。引脚名称前缀用于生成的源代码中,以创建引脚端口号和引脚的助记符。以下定义被添加到头文件 `main.h` 中

#define B1_Pin GPIO_PIN_0
#define B1_GPIO_Port GPIOA

如果没有用户标签,引脚的助记符将是 GPIO_PIN_0,端口将是 GPIOA,这对于提醒引脚的用途并没有太大帮助。

在生成的函数 static void MX_GPIO_Init(void) 中,也会生成如下初始化源代码:

  /*Configure GPIO pin : B1_Pin */
  GPIO_InitStruct.Pin = B1_Pin;
  GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  HAL_GPIO_Init(B1_GPIO_Port, &GPIO_InitStruct);

使用 C++

有一件事让我很困惑,那就是如何将代码生成和目标从 `main.c` 更改为 `main.cpp`,以便我可以使用 C++。我创建了一个新项目,并选择 C++ 作为目标语言,但生成的文件仍然是 `main.c` 而不是 `main.cpp`。然后我找到了这篇文章 如何在 STM32CubeIDE 中使用 C++ - Shawn Hymel,其中指出还需要第二步,将 `main.c` 文件重命名为 `main.cpp`。

然而,我遇到的问题是 STM32CubeIDE 总是将源代码生成到 `main.c` 文件中,因此如果您采用这种方法,您创建的 `main.cpp` 将不会被更新。

我采取的做法是创建一个新的用户维护的 C++ 源文件 `mymain.cpp`,`main.c` 中的 `main()` 在初始化环境后会调用它,实际工作从这个函数开始。因此 `main.c` 中的 `main()` 看起来像这样

int main(void)
{
  /* USER CODE BEGIN 1 */

    // This is the entry point to my C++ source code files.
    // The STM32CubeIDE does not seem to be able to generate and update
    // C++ main.cpp files for some reason.
    // The workaround is to have another C++ source file that contains the
    // real entry point which this generated main.c source file will call after
    // completing initialization.
    extern int mymain(void);

  /* USER CODE END 1 */

  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_I2C1_Init();
  /* USER CODE BEGIN 2 */

  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
//  while (1)
  {
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }

  // The main entry point in the C++ source after initializing
  // the runtime environment.
  return mymain();
  /* USER CODE END 3 */
}

在 `mymain.cpp` 文件中,我确保包含头文件 `main.h`,它包含所有生成的常量以及一个声明,以确保 `mymain()` 入口点与 C 兼容,使用 `extern "C" int mymain(void)` 来防止名称修饰。

目标选择

目标选择对话框有几个选项卡。我建议您点击 **Board Selector**(开发板选择器)选项卡,然后在 **Commercial Part Number**(商业部件号)组合框中查找 `STM32F3DISCOVERY`。组合框会根据文本控件中输入的内容更新列表,因此如果您输入“`stm32f3d`”(不带引号),您应该很容易找到它。这似乎是最简单的程序,尽管我没有花很多时间尝试其他方法。

生成代码和修改

下面是一个简单的板载用户 LED 闪烁程序在 `main.c` 中生成的源代码示例。此代码示例提供了一些关于生成了哪些源代码的信息。这并非所有生成的源代码,其中大部分是各种类型的初始化代码,这些代码是根据您选择的引脚设置生成的。

请注意,注释显示了程序员可以插入非生成源代码的位置,例如 `/* USER CODE BEGIN 3 */` 和 `/* USER CODE END 3 */`。这些注释标记了源代码文件中的一个部分,生成器将保存并恢复该部分,因此这就是您放置任何源代码的地方。

如果您没有将源代码插入到正确的位置,下次运行代码生成器时,任何不在正确位置的源代码都将被删除,并且不会出现在新生成的源代码中。

/**
  * @brief  The application entry point.
  * @retval int
  */
int main(void)
{
  /* USER CODE BEGIN 1 */

  /* USER CODE END 1 */

  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_I2C1_Init();
  MX_SPI1_Init();
  MX_USB_PCD_Init();
  /* USER CODE BEGIN 2 */
    static unsigned short pin_state = 0;
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
      pin_state = !pin_state;
      // write pin state
      // NOTE: You can in turn use HAL_GPIO_TogglePin
      HAL_GPIO_WritePin(LD3_GPIO_Port, LD3_Pin, pin_state);
      // synchronous delay for 500 ms
      HAL_Delay(500);
  }
  /* USER CODE END 3 */
}

在上面的示例中,我有两个插入源代码的区域:(1)在 `/* USER CODE BEGIN 2 */` 处,我定义了变量 `pin_state`,用于记住当前的引脚状态,以便我可以切换它;(2)在 `/* USER CODE BEGIN 3 */` 处的源代码,我设置了 GPIO 引脚状态,以便点亮和熄灭 LED。

/* USER CODE BEGIN 2 */
static unsigned short pin_state = 0;
/* USER CODE END 2 */

以及 `while` 循环中的闪烁代码

 /* USER CODE BEGIN 3 */
pin_state = !pin_state;
// write pin state
// NOTE: You can in turn use HAL_GPIO_TogglePin
HAL_GPIO_WritePin(LD3_GPIO_Port, LD3_Pin, pin_state);
// synchronous delay for 500 ms
HAL_Delay(500); 

STM32CubeIDE 调试器连接失败

请参阅意法半导体的这份资料 UM2576,其中描述了 STM32CubeIDE ST-LINK GDB 服务器的工作原理。STM32CubeIDE ST-LINK GDB 服务器 - 用户手册 提供了调试设置架构的概述。另请参阅 UM1734,其中描述了 STM32Cube USB 设备库,UM1734_STM32Cube.book

我在使用 STM32CubeIDE 时遇到的一个问题是,在成功下载并运行我的测试应用程序几次之后,突然遇到了以下错误。

Waiting for debugger connection...

Debugger connected

Debugger connection lost.

Shutting down...

问题及其解决方案在此处描述:stm32 - STM32CubeIDE 只能烧录一次,没有 SWD 调试 - Stack Overflow。但是,所发布的答案适用于 STM32F3DISCOVERY 之外的 ST 设备。

这个问题似乎与您是否使用了某些引脚有关,这些引脚以某种方式干扰或冲突了 STM32F3DISCOVERY 开发板上用于调试器连接的 ST-LINK 设备。STM32 芯片上的大多数引脚都是多功能的,对于新手来说,很容易遇到引脚冲突。

有两个引脚,`PC4` 和 `PC5`,似乎与 USB 连接有关,它们似乎有助于解决一些调试器连接问题。根据 带 STM32F303VC MCU 的 Discovery 套件 - 用户手册 第 6.2.3 节 VCP 配置(第 15/36 页),有这样一段简短的文字:

引用

STM32F3DISCOVERY 上的 ST-LINK/V2-B 通过 U2 引脚 12 (ST-LINK_TX) 和 U2 引脚 13 (ST-LINK_RX) 支持虚拟串口 (VCP),这些引脚通过 SB13 和 SB15 焊桥连接到 STM32F303 MCU 目标 STM32 USART1 (PC4, PC5)。

查看 STM32CubeIDE 中的芯片引脚图,这两个位于芯片下边缘的引脚被禁用了。我将引脚 PC4 的设置更改为 USART1_TX,将引脚 PC5 的设置更改为 USART1_RX,之后下载似乎又开始正常工作了。

然而,当从头开始一个新项目时,无论引脚是否禁用,调试器连接似乎都工作正常。

似乎在您的项目中使用引脚 PA0 以允许使用蓝色用户按钮也可能与 ST32F3DISCOVERY 开发板的 ST-LINK UART 功能发生冲突。我注意到,当我将引脚 PA0 更改为 GPIO_Input 时,在 System Core 列表中看到了一个带 SYS 的警告。然而,调试器和下载可能仍然有效。

我还发现,使用 I2C 可能会影响 STM32CubeIDE 调试器的使用。在单步调试一个使用 I2C 的简单应用程序时,调试器在 I2C 系统初始化时失去了连接。当我移除 I2C 的使用时,调试器工作正常,我能够单步调试程序,查看变量,设置断点等。

所以到目前为止,我还不确定实际的解决方案是什么,并怀疑可能有几种不同的情况会导致冲突,从而导致调试器连接丢失。每种情况都有其自己的解决方案或变通方法。而解决方案可能不仅涉及引脚分配和功能,还涉及时钟设置。

USB 驱动更新后 ST-LINK 故障

在 Windows 10 更新(其中包括两次 USB 驱动更新)后,我目前在使用 STM32CubeIDE 下载和运行程序到我的 STM32F3DISCOVERY 开发板时遇到了问题。发生的情况是,当我使用 mini-B USB 连接器将开发板连接到 ST-LINK 调试连接器时,Windows 10 现在加载的 USB 驱动程序(一个 COM4 的串口驱动程序)与更新前加载的驱动程序(一个大容量存储设备驱动程序)不同。

当我使用“运行”菜单项下载并运行程序时,会弹出一个对话框,显示文本“未检测到 ST-LINK!请连接 ST-LINK 并重新启动调试会话。”

请参阅 stm32 - STM32F3DISCOVERY 开发板使用 STM32CubeIDE 下载失败“未检测到 ST-LINK!”,但 CubeProgrammer 工作正常 - Electrical Engineering Stack Exchange,以及 微控制器 - stm32 - 是否有出厂重置? - Electrical Engineering Stack Exchange

一个能将程序下载到开发板的变通方法是使用 STMicroelectronicsSTM32CubeProgrammer 应用程序。然而,虽然这允许我继续将新版本的程序下载到开发板,但我目前无法使用调试器单步调试代码、查看变量以及远程调试器在开发和测试微控制器程序时允许的其他任务。

随着我对这个问题了解得更多,我会更新我在 Electrical Engineering Stack Exchange 上创建的帖子。我可能需要卸载并重新安装 STM32CubeIDE,或者安装此处提供的 ST-LINK 驱动程序,STSW-LINK009 - 适用于 Windows7、Windows8、Windows10 的 ST-LINK、ST-LINK/V2、ST-LINK/V2-1、STLINK-V3 USB 驱动程序签名 - STMicroelectronics

Using the Code

引言

本文附带的项目源代码执行几个简单的任务

  • 按下 STM32F3DISCOVERY 板上的蓝色用户按钮,打开和关闭一个外部 LED
  • 使用 ADC 监测一个 10K 欧姆电位器或可变电阻的设置,并使用板载 LED 显示数值

第一个任务的材料是一个限流电阻和一个 LED。电阻的原因在此处描述:欧姆定律 - 为什么 LED 需要电阻 - Electrical Engineering Stack Exchange

第二个任务的材料是一个 10K 欧姆电位器和一个电容。电容的原因在此处描述:电压 - 10K 电位器与 Arduino Uno 和 5v 配合使用正常,但相同的电位器与 ESP32 和 3.3v 浮动值配合使用出现问题 - Electrical Engineering Stack Exchange

您还需要一些杜邦连接器和一个小型面包板来固定各种组件。有关杜邦连接器的文章,请参阅:压接杜邦连接器:10 个步骤(带图片) - Instructables

硬件

该硬件用于演示使用 STM32F3DISCOVERY 开发板的嵌入式微控制器的三个常见任务

  • 从按钮读取数字信号,高或低
  • 写入数字信号,高或低,以点亮或熄灭 LED
  • 使用 ADC 读取模拟信号

八个板载 LED 呈圆形排列,像钟面一样。

板载 LED 编号和引脚并非按数字顺序排列。将开发板的 USB 连接器朝上,观察圆形排列,您可以在开发板上看到一个指南针图,N 在顶部,E 在右侧,S 在底部,W 在左侧。沿着 LED 圆周顺时针方向,从顶部或 N 开始

  • LED 3 在顶部,通常标记为 LD_3,红色,连接到引脚 PE9
  • LED 5 在右上角,通常标记为 LD_5,橙色,连接到引脚 PE10
  • LED 7 在右侧,通常标记为 LD_7,绿色,连接到引脚 PE11
  • LED 9 在右下角,通常标记为 LD_9,蓝色,连接到引脚 PE12
  • LED 10 在底部,通常标记为 LD_10,红色,连接到引脚 PE13
  • LED 8 在左下角,通常标记为 LD_8,橙色,连接到引脚 PE14
  • LED 6 在左侧,通常标记为 LD_6,绿色,连接到引脚 PE15
  • LED 4 在左上角,通常标记为 LD_4,蓝色,连接到引脚 PE8

源代码使用开发板上的以下引脚,所有这些引脚都位于开发板左边缘的 P1 50 针连接器上

  • 电压源,3v 引脚
  • 接地,gnd 引脚
  • 外部 LED,PC1 引脚作为 GPIO_Output
  • 通过 ADC 输入电位器,PA1 引脚作为 GPIO_Analog,带 ADC1_IN2

此外,还有几个引脚内部连接到板载设备

  • 蓝色按钮,PA0 作为 GPIO_Input
  • LED 4,PE8 作为 GPIO_Output
  • LED 3,PE9 作为 GPIO_Output
  • LED 5,PE10 作为 GPIO_Output
  • LED 7,PE11 作为 GPIO_Output
  • LED 9,PE12 作为 GPIO_Output
  • LED 10,PE13 作为 GPIO_Output
  • LED 8,PE14 作为 GPIO_Output
  • LED 6,PE15 作为 GPIO_Output

最后,还需要配置模数转换器设备 ADC1 的设置。电位器提供一个模拟信号,其电压随旋钮转动而变化。我们需要使用 ADC 将模拟信号转换为数字值。

由于电位器输出连接到引脚 PA1,并且该引脚已配置为带有 ADC1_IN2GPIO_Analog,因此我们需要确保 ADC1 通道 IN2 的模式和配置设置允许我们读取模拟信号并将其转换为数字值。

查看 STM32CubeIDEADC1 的模式和配置部分的模式对话框,我们看到有九个不同的通道,IN1IN9。这些通道通常设置为 Disable(禁用)。由于我们的 PA1 引脚使用 ADC1_IN2,我们希望将 ADC 设备 ADC1 的通道 IN2 的模式设置为提供表示检测到的模拟电压信号的数字值的模式。

在模式和配置对话框中,将 ADC1 通道 IN2 的模式设置为 IN2 单端

有关模数转换的更多阅读,请参阅应用笔记 AN3116,STM32™ ADC 模式及其应用

实验接线

接线很简单。您将需要以下连接

  • 从引脚 PC1 到 LED 的电路,通过电阻接地
  • 3v 引脚到电位器和引脚 PA1 的电路,通过电容接地

警告GPIO 引脚为 3V 而非 5V。为防止损坏 ST32F3DISCOVERY 开发板,请仔细检查连接到开发板任何引脚的电源。

将电位器的一个外部端子连接到电容器的一个引线。将电容器的另一个引线连接到 GND 引脚。将另一个外部端子连接到 3v 引脚。然后将中心端子连接到开发板上的引脚 PA1。这将使引脚 PA1 根据电位器旋钮的设置看到 0v 到 3v 的电压水平。

将 LED 的一个端子连接到电阻的一个引线。将电阻的另一个引线连接到 GND 引脚。将 LED 的另一个端子连接到引脚 PC1。当引脚 PC1HIGH 时,LED 将亮起;当引脚为 LOW 时,LED 将熄灭。

LED 的亮度取决于电流大小。电流由电阻限制,因此不同的电阻值会提供不同的亮度级别。由于我们使用 PC1 引脚的 3v 高电平为 LED 供电,LED 将不如使用 5v 供电时亮,因为电流会更小。请参阅 LED 亮度会随电压变化吗? - Electrical Engineering Stack Exchange。此外,不同颜色的 LED 具有不同的亮度级别,因为它们具有略微不同的电气特性。

注意:LED 是一种二极管,这意味着电流只能沿一个方向流过二极管。如果 LED 在电路中放置方向不正确,则 LED 将不会发光,因为驱动它的电流无法流动。

提示:测试 LED 电路最简单的方法是将连接到引脚 PC1 的电线连接到 3v 源引脚。如果 LED 亮起,则电路中 LED 的放置是正确的,您可以将电线改回引脚 PC1。如果 LED 不亮,则很可能是极性不正确,因此将其反转并再次测试电路。您还需要在 3v 源和 LED 之间、LED 和电阻一端之间以及电阻另一端和地之间保持良好的连接。

源代码

该项目的源代码主要由 `main.c` 文件组成,该文件大部分是根据选择的各种微控制器资源由 STM32CubeIDE 生成的代码。然而,为了在该项目中使用 C++,我在用户代码部分添加了几行代码,以调用以 `mymain()` 函数开始的 C++ 代码,该函数位于 `mymain.cpp` 源文件中。

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */

    // this is the entry point to my C++ source code files.
    // the STM32CubeIDE does not seem to be able to generate and update
    // C++ main.cpp files for some reason.
    // the work around is to have another C++ source file that contains the
    // real entry point which this generated main.c source file will call after
    // completing initialization.
    extern int mymain(void);

//  while (1)  comment out the while() because the main loop is in mymain() in mymain.cpp
  {
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }

  // the main entry point in the C++ source after initializing
  // the runtime environment.
  return mymain();
  /* USER CODE END 3 */

这是源文件 `mymain.cpp` 中的源代码,我将其放在 `Core/Src` 文件夹中,与 `main.c` 源文件并列。关于这个源文件有几点需要注意。

头文件 `main.h` 被包含到这个源文件中,因为生成器将其所有生成的代码都放在那里。该头文件包含定义的常量以及 STM32 开发环境中的头文件,因此它是必需的。

另外需要注意的两点是:(1) `extern "C" int mymain(void);` 语句,它防止了入口点函数 `int mymain(void)` 的 C++ 名称修饰;以及 (2) `extern` 语句列表,用于声明由生成器创建并在 `main.c` 源文件中定义的全局对象。对于更复杂的示例,我可能会选择将这些 `extern` 声明放在 `main.h` 文件中,并且有点沮丧生成器不会自动为我完成此操作。在 `main.h` 中,有这样一个方便的部分

/* Exported functions prototypes ---------------------------------------------*/
void Error_Handler(void);

/* USER CODE BEGIN EFP */

/* USER CODE END EFP */

我已尽力为代码的重要区域提供注释。这是一种通过每次循环轮询从 ADC 获取电位器设置的简单方法。在实际应用中,还有其他使用 ADC 的方法,但在这种情况下,我们没有任何实际的性能或功耗限制。

作为测试代码的一部分,该代码根据从 ADC 读取的值点亮不同数量的板载 LED,我使用了一个简单的计数器来模拟不同的 ADC 结果,这样我就不需要实际的电路和硬件。

#include "main.h"

extern "C" int mymain(void);           // make this main entry point callable from C program

// external objects defined in main.c by the code generator.
// any such objects created in main.c used here need to be
// declared here.

extern "C" ADC_HandleTypeDef hadc1;

int mymain (void){
      GPIO_PinState pinState = GPIO_PIN_SET;

      HAL_GPIO_WritePin(LD_EXT_GPIO_Port, LD_EXT_Pin, pinState);

      uint16_t iCount = 0;  // this is used to generate values to simulate ADC readings.

      // the main infinite loop where we do our work. ordinarily
      // this would be in main() in the file main.c however we have
      // moved it to here n order to allow C++ for this project.
      while (1)
      {
          // For example, for a 8-bit ADC, the digital output value will
          // be between 0-255, for a 10-bit ADC, the digital output value
          // will be between 0-1023 and for a 12-bit ADC, the digital
          // output value will be between 0-4095.
          uint32_t Adc_value = 0;
          uint32_t Adc_range = 4095;  // using 12 bit ADC, ADC_RESOLUTION_12B

            HAL_ADC_Start(&hadc1);    // start A/D conversion
            if(HAL_ADC_PollForConversion(&hadc1, 5) == HAL_OK) //check if conversion 
                                                               //is completed
            {
                Adc_value  = HAL_ADC_GetValue(&hadc1); // read digital value and 
                                                       // save it inside uint32_t variable
            }
            HAL_ADC_Stop(&hadc1);     // stop conversion
            HAL_Delay(200);

            // determine the LEDs to light up. this order is in the clockwise order of the
            // LEDs on the STM32F3DISCOVERY board rather than the numeric order.
            // LED 3 is the red LED on the top with the board oriented 
            // so that the USB connectors are on the top edge.
            uint32_t pinNo [] = {LD3_Pin, LD5_Pin, LD7_Pin, LD9_Pin, LD10_Pin, 
                                 LD8_Pin, LD6_Pin, LD4_Pin};
            GPIO_TypeDef *pinPort [] = {LD3_GPIO_Port, LD5_GPIO_Port, LD7_GPIO_Port, 
            LD9_GPIO_Port, LD10_GPIO_Port, LD8_GPIO_Port, LD6_GPIO_Port, LD4_GPIO_Port};

            // Uncomment the following if you want to test the logic
            // for lighting the LED array based on the ADC reading without
            // needing an analogue source connected to the ADC pin.
            // Adc_value = iCount * 200;

            // scale the ADC value to the number of LEDs in the array of
            // LEDs and then go through the list of LEDs to turn on and
            // turn off a subset of the LEDs in order to represent the
            // position of the potentiometer.
            Adc_value /= Adc_range / (sizeof(pinNo)/sizeof(pinNo[0])) + 1;

            for (uint32_t id = 0; id < sizeof(pinNo)/sizeof(pinNo[0]); id++) {
                if (id <= Adc_value)
                    HAL_GPIO_WritePin(pinPort[id], pinNo[id], GPIO_PIN_SET);
                else
                    HAL_GPIO_WritePin(pinPort[id], pinNo[id], GPIO_PIN_RESET);
            }

            // give the LEDs the opportunity to turn on before we
            // continue.
            HAL_Delay(250);

            // check if the blue USER contact pushbutton is depressed
            if (HAL_GPIO_ReadPin(B1_GPIO_Port, B1_Pin)) {
              // blue button pressed. change the pin state and the light status of the LED
              // then delay a moment to debounce the button.
              pinState = (pinState == GPIO_PIN_SET) ? GPIO_PIN_RESET : GPIO_PIN_SET;
//              HAL_GPIO_WritePin(LD4_GPIO_Port, LD4_Pin, pinState);

              // toggle the external LED by either turning on the pin to
              // source 3v to light the LED or turn it off to unlight the LED.
              HAL_GPIO_WritePin(LD_EXT_GPIO_Port, LD_EXT_Pin, pinState);
              HAL_Delay(250);   // give the LED a moment to change state.

              // ADC value simulator
              iCount++;
              if (iCount * 200 > 4090) iCount = 0;
            }
      }

      return 0;
}

关注点

走到这一步花了一些时间。由于必须学习如何使用 STM32CubeIDE 软件以及了解 STM32F3DISCOVERY 开发板,学习曲线相当陡峭。

STM32F3DISCOVERY 开发板似乎不是一个受欢迎的选择,因为找到其他人关于如何操作的资料有点困难。看起来这款开发板已经上市十年左右了,但是 F0 和 F4 Discovery 套件似乎在互联网上有更多的资料可用。

我最初是在一个关于使用 Rust 编程语言进行嵌入式项目的教程中偶然发现了这块板子。该教程就是基于使用这块板子,而我对学习 Rust 很感兴趣。该教程在 硬件 - 嵌入式 Rust 书 (rust-embedded.org)

我期待着使用这款开发板进一步探索嵌入式主题。我认为微控制器的复杂性将促使我学习更多关于电子的知识,因为我将研究 I2C、SPI 和 ADC 等主题。

该开发板还允许使用 FreeRTOS,而且 STM32CubeIDE 似乎也支持它,因此还有一个广阔的探索领域有待追求。

STM32CubeIDE 要点

STM32CubeIDE 呈现某些开发板设置选项的方式并不总是与关于开发板和微控制器的文章使用相同的语言。

另一方面,我还没有花太多时间在意法半导体的网站和他们的学习资料上,所以也许是时候这样做了。

STM32CubeIDE 极大地帮助降低了学习曲线,使其不那么陡峭。大部分工具链的“魔法”都被隐藏起来了,所以我无需处理那些,可以专注于学习 STM32F3DISCOVERY 开发板本身。

我的印象是,使用开源工具链(例如 Rust 的工具链)在 Linux 环境中更容易,而我使用的是 Windows 10。

调试器连接问题有点恼人,我不确定对于任何导致引脚使用冲突的项目该怎么办。我怀疑有解决冲突的方法,这取决于冲突的原因,但这需要比我更深入地了解开发板和微控制器。

当我能够使用调试器时,我发现它工作得很好。我能够设置断点、单步执行代码、检查变量以及执行我通常使用调试器执行的其他操作。

历史

  • 2021年10月14日:初始版本
从意法半导体的 ST32F3DISCOVERY 微控制器开发板开始 - CodeProject - 代码之家
© . All rights reserved.