前言
本文依照稚晖君分享的配置CLion用于STM32开发【优雅の嵌入式开发】,尝试配置STM32CubeMX + CLion开发环境,并在此基础上移植FreeRTOS工程。记录一些Bug和经验,与诸君共勉。
- STM32型号:STM32F103C8T6、STM32F407ZGT6
- FreeRTOS版本:V202212.01
- Keil 5-MDK版本:5.40.0.3
- CLion版本:2024.1
- FreeRTOS移植工程类型:标准库(仅STM32F1)、HAL库(F1、F4)
- 烧录工具:ST-Link V2(U盘版本)、ST-Link/V2(正点原子授权版)
一、portable 文件选择
如果您和我一样,之前(习惯)使用Keil进行STM32开发FreeRTOS,突然转到CLion开发环境,这时就需要更改选择的 portable 文件的路径。由于要使用MinGW编译环境编译C/C++代码,并且CLion使用CMake组织工程编译方式,所以 portable 文件需要选择 FreeRTOS
→
\rightarrow
→ Source
→
\rightarrow
→ portable
→
\rightarrow
→ ARM_CM3 中的port.h
和portmacro.h
文件。内存管理文件的路径不需要更改,同样选择MemMang中的heap_4.c
文件加入工程即可。
二、自定义文件添加
CLion开发环境使用CMakeLists.txt
构建和组织工程,这时如果需要添加include path
和BSP包,就需要部分修改CubeMX自动生成的CMakeLists.txt
。(每次在CubeMX中点击GENERATE CODE后都会覆盖之前修改的CMakeLists.txt
,所以修改CubeMX配置后需要手动修改CMakeLists.txt
,之后重置缓存并重新加载项目)
如果添加了自定义的头文件和源文件,请修改CMakeLists.txt
中的include_directories
和file
配置:
如果需要在CLion中创建C源文件或者头文件,注意不要在创建时将其添加到CMake目标,如果添加会破坏上图中黄色框标出部分,可能会导致编译报错。注意取消勾选“添加到目标”,如下图所示:
三、ST-Link v2 烧录问题
使用OpenOCD烧录STM32程序需要一个配置文件.cfg
。在这个配置文件中,定义了烧录方式,烧录速度等信息。笔者开发STM32F103C8T6使用的ST-Link v2是江科大套件中的U盘型:
OpenOCD配置如下图所示:
如果需要修改配置设置,可以这样快速打开配置:
stlink.cfg
# 这里的stlink.cfg是OpenOCD官方编写的,适用于STLINK的V2、V2-1、V2-2版本
source [find interface/stlink.cfg]
transport select hla_swd
# 下载速度 10000kHz,即10MHz
adapter speed 10000
# 内存大小
# STM32F103C8T6为 RAM20KB, FLASH 64KB
set WORKAREASIZE 0x5000
set FLASH_SIZE 0x10000
# chip name
set CHIPNAME STM32F103C8T6
# CPUTAPID 应该是 0x1ba01477,如果烧录失败使用 0x2ba01477(芯片版权问题)
set CPUTAPID 0x2ba01477
source [find target/stm32f1x.cfg]
# 删掉这一行,否则无法复位开发板
# reset_config srst_only
奇怪的是,博主使用上面的配置文件无法使芯片进入Debug状态,或者说进入Debug状态后程序无法暂停。如果点击暂停后会报错:
报错信息:
Open On-Chip Debugger 0.12.0 (2023-10-02) [https://github.com/sysprogs/openocd]
Licensed under GNU GPL v2
libusb1 09e75e98b4d9ea7909e8837b7a3f00dda4589dc3
For bug reports, read
http://openocd.org/doc/doxygen/bugs.html
Info : The selected transport took over low-level target control. The results mi
ght differ compared to plain JTAG/SWD
Info : clock speed 1000 kHz
Info : STLINK V2J45S7 (API v2) VID:PID 0483:3748
Info : Target voltage: 3.234574
Info : [stm32f1x.cpu] Cortex-M3 r2p0 processor detected
Info : [stm32f1x.cpu] target has 6 breakpoints, 4 watchpoints
Info : starting gdb server for stm32f1x.cpu on 3333
Info : Listening on port 3333 for gdb connections
[stm32f1x.cpu] halted due to debug-request, current mode: Thread
xPSR: 0x01000000 pc: 0x080004c8 msp: 0x20005000
(((READY)))
Info : tcl server disabled
Info : Listening on port 4444 for telnet connections
Info : accepting 'gdb' connection on tcp/3333
Info : device id = 0x10010414
Info : flash size = 256 KiB
Warn : Prefer GDB command "target extended-remote :3333" instead of "target remo
te :3333"
调试器已连接至 tcp:localhost:3333
The target is not running when halt was requested, stopping GDB.
Info : The target is not running when halt was requested, stopping GDB.
Error: [stm32f1x.cpu] not halted (gdb fileio)
提示芯片无法进入暂停状态。初步排查错误后估计是OpenOCD或者CLion的设置问题,因为在Keil中对相同的芯片和开发板进行Debug调试没有出现问题。对具体无法调试的原因进行排查,先后排除了cfg
文件的设置、GDB的选择存在问题,依然无法调试(希望不是盗版芯片问题)。在网上没有找到更多信息,好在不是什么致命问题,如有知道原因的朋友欢迎在评论区指出,不胜感激!
四、STM32F407工程中程序无法启动调度器
笔者在创建STM32F407ZGT6的CLion工程的过程中遇到一个相当奇怪的问题。通过网上的各种资料、官方手册,甚至CubeMX中的配置页面都可以得到一个结论:STM32中决定中断优先级的寄存器位数为4位。Cortex-M内核最多提供8位,但是不同的单片机厂商使用不同的位数。例如STM32使用高4位,所以最多有16级可分组的优先级(优先级值0到15);MSP432系列中使用3位,所以最多有8级可分组的优先级(优先级值0到7,该信息出自文章CORTEX-M4F基本知识)。
在配置过程中,笔者使用的FreeRTOSconfig.h
文件如下所示。该文件修改自官方提供的Demo工程CORTEX_M4F_STM32F407ZG-SK。
/*
* FreeRTOS V202212.01
* Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
* https://www.FreeRTOS.org
* https://github.com/FreeRTOS
*
*/
#ifndef FREERTOS_CONFIG_H
#define FREERTOS_CONFIG_H
/*-----------------------------------------------------------
* Application specific definitions.
*
* These definitions should be adjusted for your particular hardware and
* application requirements.
*
* THESE PARAMETERS ARE DESCRIBED WITHIN THE 'CONFIGURATION' SECTION OF THE
* FreeRTOS API DOCUMENTATION AVAILABLE ON THE FreeRTOS.org WEB SITE.
*
* See http://www.freertos.org/a00110.html
*----------------------------------------------------------*/
/* Ensure stdint is only used by the compiler, and not the assembler. */
// #ifdef __ICCARM__
// #include <stdint.h>
// extern uint32_t SystemCoreClock;
// #endif
#if defined(__ICCARM__) || defined(__CC_ARM) || defined(__GNUC__)
#include <stdint.h>
extern uint32_t SystemCoreClock;
#endif
#define configUSE_PREEMPTION 1
#define configUSE_IDLE_HOOK 0
#define configUSE_TICK_HOOK 0
#define configCPU_CLOCK_HZ ( SystemCoreClock )
#define configTICK_RATE_HZ ( ( TickType_t ) 1000 )
#define configMAX_PRIORITIES ( 5 )
#define configMINIMAL_STACK_SIZE ( ( unsigned short ) 130 )
#define configTOTAL_HEAP_SIZE ( ( size_t ) ( 75 * 1024 ) )
#define configMAX_TASK_NAME_LEN ( 10 )
#define configUSE_TRACE_FACILITY 1
#define configUSE_16_BIT_TICKS 0
#define configIDLE_SHOULD_YIELD 1
#define configUSE_MUTEXES 1
#define configQUEUE_REGISTRY_SIZE 8
#define configCHECK_FOR_STACK_OVERFLOW 0
#define configUSE_RECURSIVE_MUTEXES 1
#define configUSE_MALLOC_FAILED_HOOK 0
#define configUSE_APPLICATION_TASK_TAG 0
#define configUSE_COUNTING_SEMAPHORES 1
#define configGENERATE_RUN_TIME_STATS 0
/* Software timer definitions. */
#define configUSE_TIMERS 1
#define configTIMER_TASK_PRIORITY ( 2 )
#define configTIMER_QUEUE_LENGTH 10
#define configTIMER_TASK_STACK_DEPTH ( configMINIMAL_STACK_SIZE * 2 )
/* Set the following definitions to 1 to include the API function, or zero
to exclude the API function. */
#define INCLUDE_vTaskPrioritySet 1
#define INCLUDE_uxTaskPriorityGet 1
#define INCLUDE_vTaskDelete 1
#define INCLUDE_vTaskCleanUpResources 1
#define INCLUDE_vTaskSuspend 1
#define INCLUDE_vTaskDelayUntil 1
#define INCLUDE_vTaskDelay 1
/* Cortex-M specific definitions. */
#ifdef __NVIC_PRIO_BITS
/* __BVIC_PRIO_BITS will be specified when CMSIS is being used. */
#define configPRIO_BITS __NVIC_PRIO_BITS
#else
#define configPRIO_BITS 4 /* 15 priority levels */
#endif
/* The lowest interrupt priority that can be used in a call to a "set priority"
function. */
#define configLIBRARY_LOWEST_INTERRUPT_PRIORITY 0xf
/* The highest interrupt priority that can be used by any interrupt service
routine that makes calls to interrupt safe FreeRTOS API functions. DO NOT CALL
INTERRUPT SAFE FREERTOS API FUNCTIONS FROM ANY INTERRUPT THAT HAS A HIGHER
PRIORITY THAN THIS! (higher priorities are lower numeric values. */
#define configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY 5
/* Interrupt priorities used by the kernel port layer itself. These are generic
to all Cortex-M ports, and do not rely on any particular library functions. */
#define configKERNEL_INTERRUPT_PRIORITY ( configLIBRARY_LOWEST_INTERRUPT_PRIORITY << (8 - configPRIO_BITS) )
/* !!!! configMAX_SYSCALL_INTERRUPT_PRIORITY must not be set to zero !!!!
See http://www.FreeRTOS.org/RTOS-Cortex-M3-M4.html. */
#define configMAX_SYSCALL_INTERRUPT_PRIORITY ( configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY << (8 - configPRIO_BITS) )
/* Normal assert() semantics without relying on the provision of an assert.h
header file. */
#define configASSERT( x ) if( ( x ) == 0 ) { taskDISABLE_INTERRUPTS(); for( ;; ); }
/* Definitions that map the FreeRTOS port interrupt handlers to their CMSIS
standard names. */
#define vPortSVCHandler SVC_Handler
#define xPortPendSVHandler PendSV_Handler
#define xPortSysTickHandler SysTick_Handler
#endif /* FREERTOS_CONFIG_H */
乍一看没什么问题,甚至网络上其他关于STM32F407的FreeRTOS移植教程都使用这个文件作为基准修改。主要修改的点在于SystemCoreClock
的声明、钩子函数的使能等。但是当笔者使用修改后的该文件作为FreeRTOS配置文件加入工程后,添加两个任务,让LED0和LED1分别以1s和0.5s闪烁,编译通过,烧录却没有现象。为什么呢?通过Debug调试,发现函数卡死在port.c
的下面的程序语句中:
#ifdef configPRIO_BITS
{
/* Check the FreeRTOS configuration that defines the number of
* priority bits matches the number of priority bits actually queried
* from the hardware. */
configASSERT( ( portMAX_PRIGROUP_BITS - ulMaxPRIGROUPValue ) == configPRIO_BITS );
}
#endif
笔者移植时采用的port.c
文件是FreeRTOS
→
\rightarrow
→ Source
→
\rightarrow
→ portable
→
\rightarrow
→ GCC
→
\rightarrow
→ ARM_CM4F 中的文件,按理说在CLion这种基于GCC编译器的开发环境中不应该有问题。仔细阅读代码和Debug信息,发现是该程序段上方的检查中断优先级寄存器的可用位出了问题:
将0xFF
赋值给0xe000e400
地址的寄存器,此时寄存器内的值居然不是255,而是224?见鬼了。冷静下来思考一下,224说明此时寄存器的值为0b11100000
,查阅资料后发现该地址的寄存器就是内核的其中一个中断优先级寄存器NVIC_IPR0
,所以博主猜测该芯片使用3位表示中断优先级。但是笔者搜遍资料,STM32全系列都采用高4位表示中断优先级。莫非是又遇到了假芯片,这次碰到的甚至是假内核?
抱着试一试的态度,将FreeRTOSconfig.h
中关于中断优先级表示的宏定义改为3:
/* Cortex-M specific definitions. */
#ifdef __NVIC_PRIO_BITS
/* __BVIC_PRIO_BITS will be specified when CMSIS is being used. */
#define configPRIO_BITS __NVIC_PRIO_BITS
#else
#define configPRIO_BITS 3 /* 8 priority levels */
#endif
编译同样不会报错,通过Debug和烧录,任务函数正常执行,说明程序可以正常启动调度器了。
在FreeRTOS官网的文章可以找到这样的描述:
简单分析一下,FreeRTOS源码中断言函数的作用就是检查当前工程配置的中断优先级位数和实际硬件中是否匹配,既然将configPRIO_BITS
改为3程序可以正常运行,说明笔者使用的芯片中确实使用3位作为中断优先级寄存器。Cortex-M系列内核最多可以支持寄存器的八位表示中断优先级,但是市面上常见的单片机使用3位或者4位。既然STM32都采用4位(并且通过CubeMX配置可以发现STM32F407ZGT6确实也使用4位作为中断优先级),只能说明该芯片有可能不是真正的STM32,而是“Chinese Fake Chips”。
回首以往,在之前的学习过程中好像确实遇到过中断函数嵌套无法正常运行的情况。难道问题就出现在这里?答案就不得而知了。笔者手中也没有多余的同款芯片和开发板来进行进一步验证,所以求证之路只能到此为止。好在这一点问题并不影响学习FreeRTOS,只是这一次Debug的结论可能是单片机芯片可能是假的,格外令人气愤,遂记录在此,望后来者可少花时间,继续向前。
持续更新完善中……
原创笔记,码字不易,欢迎点赞,收藏~ 如有谬误敬请在评论区不吝告知,感激不尽!博主将持续更新有关嵌入式开发、FPGA方面的学习笔记。