非标准STM32F407学习笔记(一) 前情提要 学习硬件设备固然是很有意思的一件事,不过使用某绿色(或者蓝色)礦ision配上带劲的音乐在Linux设备上并不是什么容易的事情,因此在本篇中,我将简要地介绍如何配置一套用常用组件组合起来的开发、编译、调试环境。
调试设备:Pico Probe 介绍 树莓派Pico,是一款使用了RP2040的微控制器,可以通过刷入Pico Probe(debugprobe)固件使其转换为一个支持标准CMSIS-DAP协议(即SWCLK和SWDIO)的调试设备。在这种情况下,我们就可以省去ST-Link或类似的调试设备,而仅仅是通过一个简单的开发板来完成连接工作。 可以通过官方GitHub连接 获取Pico Probe固件,即(debugprobe_on_pico.uf2)。
安装 获取到这个固件之后,按住树莓派Pico上的BOOTSEL按钮并插入USB线到电脑上,会识别到出于FS Mode的树莓派Pico,此时只需要将上述uf2格式的固件复制到这个模拟U盘中,就可以自动安装相应的固件。Pico会自动重启,并且工作在预期的工作模式:
1 2 3 4 $ lsusb ... Bus 003 Device 015: ID 2e8a:000c Raspberry Pi Debugprobe on Pico (CMSIS-DAP) ...
udev配置 在现代Linux系统上,一般是使用udev来管理USB设备的权限,理论上在安装OpenOCD后会自动具有通用CMSIS-DAP的配置,不过我在这里附一个参考的:
1 2 # CMSIS-DAP compatible adapters ATTRS{product}=="*CMSIS-DAP*", MODE="660", GROUP="plugdev", TAG+="uaccess"
1 sudo udevadm control --reload-rules && sudo udevadm trigger
接线指南 根据树莓派Debug on Pico指南(上述GitHub仓库中提到),位于Getting Started with Pico PDF 。
其附录 A.2. Debug with a second Pico or Pico 2 给出了接线指南:
GPIO
功能
GND
地线
VBUS
5V USB电源
GP2
SWCLK
GP3
SWDIO
GP4
TXD
GP5
RXD
验证 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 $ openocd -f interface/cmsis-dap.cfg Open On-Chip Debugger 0.12.0+dev-01445-gb80f554fd-dirty (2026-04-11-11:03) Licensed under GNU GPL v2 For bug reports, read http://openocd.org/doc/doxygen/bugs.html Info : Listening on port 6666 for tcl connections Info : Listening on port 4444 for telnet connections Warn : An adapter speed is not selected in the init scripts. OpenOCD will try to run the adapter at very low speed (100 kHz). Warn : To remove this warnings and achieve reasonable communication speed with the target, set "adapter speed" or "jtag_rclk" in the init scripts. Info : Using CMSIS-DAPv2 interface with VID:PID=0x2e8a:0x000c, serial=530328472925599C Info : CMSIS-DAP: SWD supported Info : CMSIS-DAP: Atomic commands supported Info : CMSIS-DAP: Test domain timer supported Info : CMSIS-DAP: FW Version = 2.0.0 Error: CMSIS-DAP: JTAG not supported
编译环境: arm-none-eabi-gcc + STM32CubeMX + Ninja(CMake) arm-none-eabi-gcc 理论上可以用arm官方提供的gcc ,不过鉴于我们都用Linux了,为何不用包管理器呢:
1 sudo pacman -S arm-none-eabi-binutils arm-none-eabi-gcc
1 2 3 4 5 6 7 8 9 $ arm-none-eabi-gcc -v Using built-in specs. COLLECT_GCC=arm-none-eabi-gcc COLLECT_LTO_WRAPPER=/usr/lib/gcc/arm-none-eabi/16.2.0/lto-wrapper Target: arm-none-eabi Configured with: /build/arm-none-eabi-gcc/src/gcc-16.2.0/configure --target=arm-none-eabi --prefix=/usr --with-sysroot=/usr/arm-none-eabi --with-native-system-header-dir=/include --libexecdir=/usr/lib --enable-languages=c,c++ --enable-plugins --disable-decimal-float --disable-libffi --disable-libgomp --disable-libmudflap --disable-libquadmath --disable-libssp --disable-libstdcxx-pch --disable-nls --disable-shared --disable-threads --disable-tls --with-gnu-as --with-gnu-ld --with-system-zlib --with-newlib --with-headers=/usr/arm-none-eabi/include --with-python-dir=share/gcc-arm-none-eabi --with-gmp --with-mpfr --with-mpc --with-isl --with-libelf --enable-gnu-indirect-function --with-host-libstdcxx='-static-libgcc -Wl,-Bstatic,-lstdc++,-Bdynamic -lm' --with-pkgversion='Arch Repository' --with-bugurl=https://gitlab.archlinux.org/archlinux/packaging/packages/arm-none-eabi-gcc/-/issues --with-multilib-list=rmprofile Thread model: single Supported LTO compression algorithms: zlib zstd gcc version 16.2.0 (Arch Repository)
STM32CubeMX 可以从STM32官网 获取STM32CubeMX,接着配置即可。 可以选取cmake作为编译器。
1 2 3 4 5 mkdir build && cd buildcmake -G Ninja \ -DCMAKE_TOOLCHAIN_FILE=../cmake/gcc-arm-none-eabi.cmake \ -DCMAKE_BUILD_TYPE=Debug .. ninja
烧录环境: OpenOCD 1 openocd -f interface/cmsis-dap.cfg -f target/stm32f4x.cfg -c "program LED.elf verify reset exit"
调试环境:GDB 1 2 3 4 5 6 7 8 $ openocd -f interface/cmsis-dap.cfg -f target/stm32f4x.cfg Info : [stm32f4x.cpu] starting gdb server on 3333 Info : Listening on port 3333 for gdb connections Info : accepting 'gdb' connection on tcp/3333 $ gdb (gdb) target remote localhost:3333 (gdb) file LED.elf (gdb) c
附录: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 /** ****************************************************************************** * @file main.c * @brief STM32F407 PC13 LED 100ms 周期性闪烁 (完整版) * @note 默认低电平点亮,高电平熄灭。若您的硬件相反,请交换 RESET/SET。 ****************************************************************************** */ #include "stm32f4xx_hal.h" // 函数声明 void SystemClock_Config(void); static void MX_GPIO_Init(void); void Error_Handler(void); /** * @brief 主函数 * @retval int */ int main(void) { // 1. 初始化HAL库 HAL_Init(); // 2. 配置系统时钟 (使用HSI内部振荡器 -> PLL倍频至168MHz) SystemClock_Config(); // 3. 初始化GPIO (PC13) MX_GPIO_Init(); // 4. 主循环:LED以100ms间隔亮灭 while (1) { // PC13输出低电平 -> LED点亮 (注:多数开发板PC13为低电平有效) HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, GPIO_PIN_RESET); HAL_Delay(100); // 延时100ms // PC13输出高电平 -> LED熄灭 HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, GPIO_PIN_SET); HAL_Delay(100); // 延时100ms } } /** * @brief 系统时钟配置 (使用HSI -> PLL) * @note 最终时钟: HSI 16MHz / 16 * 336 / 2 = 168MHz * HCLK = 168MHz, APB1 = 42MHz, APB2 = 84MHz * @retval 无 */ void SystemClock_Config(void) { RCC_OscInitTypeDef RCC_OscInitStruct = {0}; RCC_ClkInitTypeDef RCC_ClkInitStruct = {0}; // 使能电源接口并配置电压调节器为模式1 (168MHz需要) __HAL_RCC_PWR_CLK_ENABLE(); __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1); // 配置振荡器:使用HSI (16MHz) 作为PLL输入 RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI; RCC_OscInitStruct.HSIState = RCC_HSI_ON; RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT; RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON; RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSI; RCC_OscInitStruct.PLL.PLLM = 16; // 分频: 16MHz / 16 = 1MHz RCC_OscInitStruct.PLL.PLLN = 336; // 倍频: 1MHz * 336 = 336MHz RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2; // 主PLL分频: 336MHz / 2 = 168MHz RCC_OscInitStruct.PLL.PLLQ = 7; // 用于USB/SDIO: 336/7 = 48MHz if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK) { Error_Handler(); } // 配置时钟源、AHB/APB分频 RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2; RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK; RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1; // HCLK = 168MHz RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV4; // APB1 = 42MHz (最大允许) RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV2; // APB2 = 84MHz (最大允许) // 配置Flash等待周期 (168MHz需要5个等待周期) if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_5) != HAL_OK) { Error_Handler(); } } /** * @brief GPIO初始化函数 (PC13) * @retval 无 */ static void MX_GPIO_Init(void) { // 使能GPIOC时钟 __HAL_RCC_GPIOC_CLK_ENABLE(); GPIO_InitTypeDef GPIO_InitStruct = {0}; // 配置PC13为推挽输出,无上下拉,低速度 (PC15~PC13驱动能力受限) GPIO_InitStruct.Pin = GPIO_PIN_13; GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; GPIO_InitStruct.Pull = GPIO_NOPULL; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW; HAL_GPIO_Init(GPIOC, &GPIO_InitStruct); // 默认熄灭LED (输出高电平) HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, GPIO_PIN_SET); } /** * @brief 错误处理函数 (当时钟或外设初始化失败时进入死循环) * @retval 无 */ void Error_Handler(void) { // 用户可以在此添加调试代码(如点亮错误指示灯) while (1) { // 死循环 } }