环境搭建编译器:Keil5,AC5编辑器:VS Code,Embedded IDE,Cortex-Debug调试器:Openocd,Arm GNU Toolchain烧录器:ST-link V2必要的配置:// settings.json { "cortex-debug.armToolchainPath": "C:\\Program Files (x86)\\Arm GNU Toolchain arm-none-eabi\\12.3 Rel1\\bin", "cortex-debug.openocdPath": "E:\\OpenOCD-20211118-0.11.0\\bin\\openocd.exe", "cortex-debug.gdbPath": "C:\\Program Files (x86)\\Arm GNU Toolchain arm-none-eabi\\12.3 Rel1\\bin\\arm-none-eabi-gdb" , "showDevDebugOutput": "raw", "cortex-debug.gdbPath.windows": "C:\\Program Files (x86)\\Arm GNU Toolchain arm-none-eabi\\12.3 rel1\\bin\\arm-none-eabi-gdb.exe", "cortex-debug.stlinkPath": "C:\\ST\\STM32CubeIDE_1.8.0\\STM32CubeIDE\\plugins\\com.st.stm32cube.ide.mcu.externaltools.stlink-gdb-server.win32_2.0.100.202109301221\\tools\\bin\\ST-LINK_gdbserver.exe" } // launch.json { // 使用 IntelliSense 了解相关属性。 // 悬停以查看现有属性的描述。 // 欲了解更多信息,请访问: https://go.microsoft.com/fwlink/?linkid=830387 "version": "0.2.0", "configurations": [ { "name": "Cortex Debug(STLink)", "type": "cortex-debug", "request": "launch", "servertype": "openocd", "cwd": "${workspaceFolder}", "executable": "${workspaceFolder}/build/${workspaceFolderBasename}/${workspaceFolderBasename}.elf", "runToEntryPoint": "main", "configFiles": [ "E:\\OpenOCD-20211118-0.11.0\\share\\openocd\\scripts\\interface\\stlink-v2.cfg", "E:\\OpenOCD-20211118-0.11.0\\share\\openocd\\scripts\\target\\stm32f4x.cfg", ], // "gdbPath":"C:\\Program Files (x86)\\Arm GNU Toolchain arm-none-eabi\\12.3 Rel1\\bin\\arm-none-eabi-gdb.exe", "objdumpPath": "C:\\Program Files (x86)\\Arm GNU Toolchain arm-none-eabi\\12.3 rel1\\bin\\arm-none-eabi-objdump.exe", } ] }使用STM32CUBEMX生成代码框架,在VS Code中进行编辑,编译,烧录,调试。ST-Link烧录器管脚定义使用 printf 进行串口打印在要使用printf的文件包含头文件stdio.h。在usart.c文件中加入重定向,这里以串口1为例。/***************************************************************************** * 【功 能】 printf函数重定向支持代码 * 加入以下代码, 使用printf函数时, 不再需要选择use MicroLIB * 参 数: * 返回值: *****************************************************************************/ #pragma import(__use_no_semihosting) struct __FILE { int handle; }; // 标准库需要的支持函数 FILE __stdout; // FILE 在stdio.h文件 void _sys_exit(int x) { x = x; } // 定义_sys_exit()以避免使用半主机模式 int fputc(int ch, FILE *f) // 重定向fputc函数,使printf的输出,由fputc输出到UART, 这里使用串口1(USART1) { // if(xFlag.PrintfOK == 0) return 0; // 判断USART是否已配置,防止在配置前调用printf被卡死 while ((USART1->SR & 0X40) == 0) ; // 等待上一次串口数据发送完成 USART1->DR = (uint8_t)ch; // 写DR,串口1将发送数据 return ch; }进一步封装一个日志库,增加时间戳,文件名,行数,打印等级等功能。如果是在FreeRTOS下,还要考虑多个任务同时对串口1的使用互斥问题,这里有一个我封装好的日志库,sinlatansen/DBG: 一款线程安全的FreeRTOS(cmsis_os2)的日志调试库,基于STM32F4_HAL 。避开 HAL_DelayHAL库这个延时函数,有bug,建议自己实现延时函数,改用软件定时器实现。这里我使用的 STM32F407VET6 主频168MHz,其中我使用的 TIM6 所挂载的 AHB1 频率为 84MHz ,因此设置 PSC 为 83。代码实现:/* delay.c */ #include "delay.h" void Delay_ns(uint32_t ns) { uint32_t ticks = (ns / 11.9) + 1; // 计算需要的时钟周期数,+1确保延时不会少于请求的ns TIM6->CNT = 0; // 重置计数器 HAL_TIM_Base_Start(&htim6); // 启动定时器 while (TIM6->CNT < ticks) ; // 等待计数器达到目标值 HAL_TIM_Base_Stop(&htim6); // 停止定时器 } void Delay_us(uint32_t us) { uint32_t ticks = (84 * us) - 1; // 84时钟周期代表1微秒,-1确保延时不会少于请求的us TIM6->CNT = 0; // 重置计数器 HAL_TIM_Base_Start(&htim6); // 启动定时器 while (TIM6->CNT < ticks) ; // 等待计数器达到目标值 HAL_TIM_Base_Stop(&htim6); // 停止定时器 } // ms延时使用osDelay函数 /****************************************************************/ /* delay.h */ #ifndef DELAY_H_INCLUDED #define DELAY_H_INCLUDED #include "stm32f4xx_hal.h" #include "tim.h" void Delay_ns(uint32_t ns); void Delay_us(uint32_t us); #endif /* DELAY_H_INCLUDED */ STM32CUBEMX沙箱定义对于 CUBEMX 生成的代码,会有一套规范,通过注释提示了用户代码写在哪里,否则可能在下一次生成代码误删用户代码。一般我倾向于把用户代码写在自己添加的 .c 文件,在main.c中进行调用,毕竟那些注释看着还是比较乱。但是避免不了要在生成的代码中写内容时,还是遵循官方的风格与规范比较好。* USER CODE END Header */ /* Includes ------------------------------------------------------------------*/ #include "main.h" #include "gpio.h" /* Private includes 私有文件包含沙箱----------------------------------------------------------*/ /* USER CODE BEGIN Includes */ 这里存放定义的类型 /* USER CODE END Includes */ /* Private typedef 私有类型定义沙箱-----------------------------------------------------------*/ /* USER CODE BEGIN PTD */ 这里存放你的定义的结构体,枚举体,共用体等 /* USER CODE END PTD */ /* Private define 私有宏沙箱------------------------------------------------------------*/ /* USER CODE BEGIN PD */ 这里存放你定义的宏 /* USER CODE END PD */ /* Private macro 私有宏沙箱-------------------------------------------------------------*/ /* USER CODE BEGIN PM */ 这里存放你定义的宏 /* USER CODE END PM */ /* Private variables 私有变量沙箱---------------------------------------------------------*/ /* USER CODE BEGIN PV */ 这里存放你定义的变量 /* USER CODE END PV */ /* Private function prototypes 私有函数原型沙箱-----------------------------------------------*/ void SystemClock_Config(void); /* USER CODE BEGIN PFP */ 这里存放你的函数原型 /* USER CODE END PFP */ /* Private user code私有程序沙箱 ---------------------------------------------------------*/ /* USER CODE BEGIN 0 */ /* USER CODE END 0 */ /** * @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();//整个HAL库初始化 /* 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();//GPIO初始化 /* USER CODE BEGIN 2 */ /* USER CODE END 2 */ /* Infinite loop */ /* USER CODE BEGIN WHILE */ while (1) { /* USER CODE END WHILE */ /* USER CODE BEGIN 3 */ } /* USER CODE END 3 */ }CUBEMX 生成的工程在keil5中编译报错【转】Keil ARM开发 error L6236E错误解决 - 壹点灵异 - 博客园 (cnblogs.com)让CUBEMX 生成 UTF-8 文件,避免中文乱码添加系统环境变量:变量名:JAVA_TOOL_OPTIONS值 :-Dfile.encoding=UTF-8ASCIi 艺术字体生成Text to ASCII Art Generator (TAAG) --- 文本到 ASCII 艺术生成器 (TAAG) (patorjk.com)