建立 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 配置的中文翻译

建立 F103C8T6 HAL库 Makefile FreeRTOS 工程的更多相关文章
- STM32,下载HAL库写的代码后J-Link识别不到芯片,必须要按住复位才能下载?
问题描述:最近在学STM32的HAL库,据说可以统一STM32江湖,前途无量.最近一段时间参照STM32CubeMX和原子的资料自己学着建了两个HAL库的工程模板,F4的还好说,F1的出现了一个玄学问 ...
- 新建基于STM32F103ZET6的工程-HAL库版本
1.STM32F103ZET6简介 STM32F103ZET6的FLASH容量为512K,64K的SRAM.按照STM32芯片的容量产品划分,STM32F103ZET6属于大容量的芯片. 2.下载HA ...
- 【GMT43智能液晶模块】基于HAL库的SDRAM和LCD驱动例程(MDK工程&CubeMX工程)
说明: 1.该工程基于HAL库实现动态存储器SDRAM驱动以及液晶控制器LCD驱动. 2.工程通过STM32CubeMX(Version 4.22.0)配置生成,可直接打开进行配置. 3.KEIL M ...
- STM32F072从零配置工程-基于HAL库的串口UART中断配置
先上一个采用串口直接传输的Demo: 此处的思路是完全采用HAL库来实现的,核心是运用HAL_UART_Transmit_IT和HAL_UART_Receive_IT两个函数来实现的,可以作为一个De ...
- STM32 HAL库利用DMA实现串口不定长度接收方法
参考:https://blog.csdn.net/u014470361/article/details/79206352 我这里使用的芯片是 F1 系列的,主要是利用 DMA 数据传输方式实现的,在配 ...
- 【STM32H7教程】第32章 STM32H7的TIM定时器基础知识和HAL库API
完整教程下载地址:http://www.armbbs.cn/forum.php?mod=viewthread&tid=86980 第32章 STM32H7的TIM定时器基础知识和H ...
- 【STM32H7教程】第29章 STM32H7的USART串口基础知识和HAL库API
完整教程下载地址:http://www.armbbs.cn/forum.php?mod=viewthread&tid=86980 第29章 STM32H7的USART串口基础知识和 ...
- 如何使用keil5将stm32的hal库编译成lib文件——F1版本
hal库中keil5中编译的速度是比较慢的,相同情况下,每次都要编译的时候,比标准库是要慢很多的,因此就hal库编译成lib文件是一种加快编译速度的方法,当然也有其自身的缺点.一.步骤1.使用cube ...
- 【STM32H7教程】第59章 STM32H7的DAC基础知识和HAL库API
完整教程下载地址:http://www.armbbs.cn/forum.php?mod=viewthread&tid=86980 第59章 STM32H7的DAC基础知识和HAL库 ...
随机推荐
- Spark的两种核心Shuffle详解
在 MapReduce 框架中, Shuffle 阶段是连接 Map 与 Reduce 之间的桥梁, Map 阶段通过 Shuffle 过程将数据输出到 Reduce 阶段中.由于 Shuffle 涉 ...
- Linux 基础学习篇 序篇
读序篇可以知道的: 1.有些指令知道前和知道后,自己的操作是完全不同的,可能知道前,会用reset把系统重新启动一遍,而知道后会使用ps和kill来关闭进程. 2.如果对Linus的学习知识" ...
- docker搭建kafka集群(高级版)
1. 环境docker, docker-compose 2.zookeeper集群 /data/zookeeper/zoo1/config/zoo.cfg # The number of millis ...
- AlexNet论文总结
论文链接:https://papers.nips.cc/paper/2012/file/c399862d3b9d6b76c8436e924a68c45b-Paper.pdf Q1:解决了什么? 目前主 ...
- NOIP 模拟 $17\; \rm 时间机器$
题解 \(by\;zj\varphi\) 一道贪心的题目 我们先将节点和电阻按左边界排序,相同的按右边界排序 对于每一个节点,我们发现,选取左边界小于等于它的电阻中右边界大于它且最接近的它的一定是最优 ...
- snoop的基本用法
关于Snoop的用法 snoop是开发wpf应用程序的利器.用它可以观察WPF的可视树,监听事件,更改元素属性等. 下面我介绍下snoop一些用法. 1.获取指定应用程序的UI 打 ...
- 15.SpringMVC之异步请求
SpringMVC中异步请求相关组件 SpringMVC在此基础上对异步请求进行了封装.提供了AsyncWebRequest类型的request,并提供了处理异步请求的管理器WebAsyncManag ...
- react启动报babel-eslint依赖版本不一致
遇到的问题: gitlab上拉的react的项目,下载依赖之后,yarn start启动报错 因为按他说的步骤:1.改动比较大, 2.完全没什么作用(重点) 解决方案:直接忽略,如果项目没有.env文 ...
- Java随手记录
Spring @Configuration注解及配置方法 转自:https://www.jb51.net/article/184822.htm Spring @Configuration注解 Spri ...
- IDEA 创建Maven Web工程
一.Maven环境搭建 二.Maven常用命令 mvn clean 清除生成的target文件 mvn install 生成target文件 mvn clean install 相当于先删除targe ...