建立 F103C8T6 HAL库 Makefile FreeRTOS 工程
F103C8T6 HAL库 Makefile FreeRTOS 工程模板
环境
-
该工程的开发平台为 ARM-GCC 工具链和 Make
> arm-none-eabi-gcc -v gcc version 9.2.1 20191025 (release) [ARM/arm-9-branch revision 277599] (GNU Tools for Arm Embedded Processors 9-2019-q4-major)
> make -v GNU Make 3.81
-
下载调试工具为 OpenOCD + CMSIS-DAP
Open On-Chip Debugger 0.10.0 (2020-01-14) [https://github.com/sysprogs/openocd]
-
固件信息
- 官方 HAL 库版本:
STM32Cube_FW_F1_V1.8.0
- FreeRTOS API :
CMSIS v2
- FreeRTOS version :
10.0.1
- CMSIS-RTOS version :
2.00
- 官方 HAL 库版本:
-
适配芯片为
STM32F103C8
-
开发环境为
Windows10_64bit
+VSCode
资源
-
BSP_LED -> PB12:板载LED, 指示灯用
-
USART1:串口,主要调试工具
- TxD -> PA9
- RxD -> PA10
-
TIM4:HAL 库时基源
-
SysTick:FreeRTOS 时基源
现象
-
单片机上电后先通过串口发送一堆测试数据。
-
BSP_LED 闪烁。
-
串口每隔一段时间向上位机发送一段测试信息。
- 现象 2 和 3 “同时”进行、互不影响。
目录结构
├─.vscode/ // vscode 配置文件
├─build/ // 编译生成的中间文件
├─DOC/ // 说明文档
├─Drivers/ // ST HAL 库的库文件
├─Inc/ // 用户层的 .h 文件
├─Middlewares/ // 中间件,里面主要包含的是 FreeRTOS 的源码
├─Peripheral/ // 外设。用户层,usart 初始化的 c 和 h 我就写在了这里边
├─Src/ // 用户层的 .c 文件
├─Makefile // 最最重要的 Markfile 文件,工程编译全靠它
├─startup_stm32f103xb.s // 单片机的启动文件,程序其实是从这里开始运行的
├─STM32F103C8Tx_FLASH.ld // 单片机的内存管理相关文件
└─STM32F103xx.svd // 单片机的 svd 文件,大概相当于寄存器的目录吧,调试时使用
工程中的其他注意事项
-
中断优先级分为4,16级抢占优先级(0~15,越低越优先)无响应优先级。
(HAL_Init() --> HAL_NVIC_SetPriorityGrouping(NVIC_PRIORITYGROUP_2);)
-
GCC 中 printf() 底层的实现方式与 Keil 中的不一样,移植工程时须留意。
-
在 Src 目录下有一个 stm32f1xx_hal_timebase_tim.c 文件,让 TIM 取代 SysTick 就是在这里实现的。(FreeRTOS 的任务调度仍是使用的 SysTick)
-
因为用到了 TIM4 做 HAL 库时基源 ,所以在 main.c 文件中使用了 HAL_TIM_PeriodElapsedCallback() 函数,以后再使用定时器中断时注意这里 !!!
过程
以完美的 VSCode + GCC工具链 + OpenOCD 的 HAL 库工程为基础再做一个 FreeRTOS 的模板就很简单了。
-
stm32CubeMX . 用它新建一个工程,配置好 RCC, SYS 和 GPIO , 需要注意的时由于 OS 需要使用 SysTick , 因此将 HAL 库的
时基源调为 TIM1时基源调为 TIM4 , 因为 TIM1 是高级定时器,高级定时器不多,省着点用。而 TIM4 是 F103C8 的最后一个定时器,后来我又看了几款芯片,发先惯例是每款芯片的最后一个定时器都是那些定时器里最不高级的,因此就用这个吧,免得以后用到什么高级功能麻烦。 -
在 Middleware 中启用 FreeRTOS (CMSIS_V2) 然后进行一些简单的配置(其实也没什么可配置的)。因为以后我们要自己创建任务所以可以先在这里把总任务堆栈调大一点,其他的配置跟原子推荐的都差不多,用不着动。
-
可以发现当我们启用 OS 后系统中断发生了一些变化,大概像下图那样。因为 OS 的任务调度跟中断关联很大,所以图上那些灰色不给改的中断配置千万要留意,在 CubeMX 中不能改,在代码中也千万别去改。
-
配置一下工程的输出参数,然后生成工程吧。
-
生成工程后先删掉一些不需要的东西,否则工程文件夹太大了。一般来说删掉 \Drivers\CMSIS 目录下除 Device 和 Include 之外的所有文件就可以了。
-
将前面制作的 HAL 库工程里的 .vscode 文件夹和 STM32F103xx.svd 文件复制到新工程里。最好测试一下串口,所以把 Peripheral 文件夹和 config.h, syscalls.c 也复制过来,然后去 Makefile 和 stm32f1xx_hal_conf.h 中做相应的修改。如果 vscode 报错就看看 makefile 引用了什么新的头文件,然后包含进 c_cpp_properties.json .
-
现在编译一次应该能通过的,然后再看着改改试试能不能正常用就行了,应该没什么大问题。
补充
-
关于新建任务。新建任务其实并不复杂,甚至比原子的方式还要简单。仿照默认任务的创建代定义一个新任务的结构体,然后声明一下任务函数,在 main() 中创建一下任务,最后在找个空白的地方写下这个任务函数的一些实现就 OK 了。
注意!接下来要说的事非常重要!!
-
在任务函数中无法打印浮点数
这个问题的确出现了而且很令人抓狂,我在第二个任务中放了一个
printf("%f\r\n",3.14)
但是程序一旦运行到这里就会死机,陷入到HardFault_Handler()
中。使用操作系统时遇到莫名其妙的问题最先想到的可能就是堆栈溢出,但是直到我把运行 printf() 函数的任务堆栈调大到 10kB 后仍没解决问题时我就觉得没那么简单。从网上了找到了修复使用 newlib 库时遇到这个问题的方案,但是我应该没有使用这个东西。查了一晚上的资料(真的是一晚上,我六点才睡觉)也没什么有效的解决方案,但是我隐约明白了这个东西应该跟内存分配有关系。今天下午又修了修也实在是没什么效果,但是果然正当我打算放弃的时候转机就出现了😂——还记得当初重定向 printf() 函数时用到了一个文件叫 syscalls.c (这个在上文也有提到),重定向 printf() 其实只要用到里面的 _write() 函数就行了,但是我当时的打算是其他函数现在虽然不知道有什么用但难保以后用不着,所以并没有删掉里面的其他函数。今天下午在我放弃之前的最后一刻我突然想到了我的代码中最不可靠的因素——就是那个 syscalls.c 文件,整个工程的核心代码是 STM32CubeMX 根据我的配置自动生成的应该没什么问题,我手动在 main.c 里边写了些东西但那都是我能解释的应该也没什么问题,所以这样说来工程中最可能出问题的就是那个我擅自从 STM32 的 HAL 库例程中复制过来的 syscalls.c 文件了!
果然当我注释掉 syscalls.c 除与 _write() 函数相关的其余所有代码后问题就解决了。事实上后来查明主要原因是因为 syscalls.c 中的 _sbrk() 函数,但是当我查明后还是果断地把除 _write() 之外的所有函数都注释掉了,永绝后患。
这件事告诉我们对于未知的事物一定要时刻保持最大限度的怀疑。你以为 ST 官方的 HAL 库的例程中的文件都是好的、不会坑你?不存在的。
-
另注:printf() 这种函数还是很耗费内存的,在任务中运行时虽然不用给任务堆栈分配 10kB 那么夸张,但最低给分配 512*4B 还是有必要的。
附录
-
CubeMX 中关于 FreeRTOS 配置的中文翻译