#背景故事:在嵌入式开发以及硬件设计的学习道路上,能够使人快速成长的只有俩种情况:压力山大的项目工作和兴趣学习。实际上,生活节奏很快,工作很现实。小公司没有培训时间,大公司很关注学历和你现有的能力。一旦选择工作,就很难再花足够的时间去探究学习(报班除外)
#学习过程:有一个导师指点远比你自己摸索快得多,所以争取每一份工作尽量都能掌握新的技能。很多前辈都跟我说在这个行业,你的项目经验和资历很重要,但同样的情况是,只要是程序员到了35岁这个年纪,就会面临着精力无法跟上的情况,无法再胜任一线的岗位。所以,趁年轻,学会将自己的经验条保存下来。
#学习推荐:自主学习来说,市面上常见的正点原子、野火、凡亿教育、张飞电子的资料很多。下单了他们家的产品就会免费送资料。以下资料部分来自 正点原子,且博客内容较长,包括了一些调试过程中会出现的问题。
#以下博客目录内容分布为:
目录
1--如何建立一个标准的HAL库工程文件
1、初步认识HAL库文件
个人理解为:将寄存器的操作封装为函数,对开发者(上层用户)来说屏蔽了底层寄存器操作。
比如你需要配置某个引脚时候,通常你需要调整相应寄存器的操作(置1或者0),包括端口时钟使能、端口引脚输入输出模式等,在芯片手册上你可以看见相应需要调整的寄存器名称。
所以HAL库实际上就是一堆打包好的C文件和头文件(C文件写具体函数的函数体,H文件写宏定义和函数声明),以及最重要的核心底层文件,参考下图
以上部分,最重要的就是文件就是stm32f103xe.h 和stm32f103xe.s。
当你得到一份完整的HAL固件包时,会包含以下这些文件。
通常会把.h的头文件放在 Include 文件夹里 .c文件放在Source文件夹里,这样方便区分开。
CMSIS文件夹的内容主要有:
这些.h文件在你工程的头文件路径里都需要囊括进来,少一个可能都编译不出来。当然你也可以单独把所有H文件和C文件领出来存在一起。
需要注意的是,因为F1系列有很多具体类型,但是HAL库中类似于stm32f1xx.h这种头文件是通用型的,适用于这个系列内所有的芯片。当你编译的时候,会根据芯片选择的类型在宏定义内区分开头文件。如下图:
在没有宏定义到底用的是哪个芯片的时候,这里有一堆elif语句用来判断,所以第一次编译犯的错就是在#error 那里 ,最后解决办法就是自己补充一个宏定义:
Middlewares 就是中间件,使用基础外设的话暂时用不到。
Projects 就是例程 ,工程文件存储的地方,根据你的芯片类型来选择。
2、如何获取一份完整的HAL库文件包?
获取途径:ST官网 https://www.st.com.cn/content/st_com/zh.html
点击”工具和软件“,依次选择 STM32Cube Ecosystem,下拉找到 "STM32Cube MCU和MPU软件包"。
我购买的开发板是正点原子的STM32F103战舰V3,所以下载F1的固件包。当然,你也可以直接下载正点原子哥的资料,里面包含了F1的固件包。微信gz号搜索 正点原子。
(STM32CubeMX 本身是一个图形化开发软件,本身就是基于HAL库进行开发,开发效率相对来说更高一点)
3、如何建立一个相对完整的HAL工程文件?
(1)建立分区的文件夹
文件夹 储存何种文件 同第一步 初步认识HAL库文件 中所说的一样。
User 用户自己写的驱动和main.c
Project 储存工程文件
Drivers 底层驱动文件
Output 编译产生的输出文件(可以在工程设置-Output-Set Folder Object调整输出文件的输出位置)
建立完分区文件夹以后,可以先把你需要的驱动文件放在合适的位置,或者整个复制粘贴过来也是没问题的。
例如我的Drivers文件夹就是这样:
(2)新建工程文件
新建工程Project的时候选择好自己的芯片型号,然后调整魔术棒设置如下:
Browse Information 用于后期调试代码 否则的话不方便右键找definition
include path里需要添加你所有头文件所在的位置,主要包括Drivers文件夹里的CMSIS和STM32F1xx_HAL_Drivers 即前面说的核心文件和HAL库的头文件。
这里注意,如果你添加的头文件的path 并没有指向具体存放xxx.h文件的文件夹,那么当你include
xxx.h的时候,肯定会找不到。于是乎,出现了相对路径的写法。
Define 的内容是宏定义,其效果类似于你直接.h文件里写# define
(3)建立工程里的项目文件夹
在Project Items的视图里,右键单击 Manage Project Items
在这里你可以增加或者删除工程视图里的文件夹和文件(点击新建以后,点击三个点)
方框 图标是新建
箭头则是上下移动 ×是删除,最后添加的结果像下面这样:tap.c就是main.c
最后你可以将文件夹名字命名成你认为容易区分的类型(注意它并不需要和你第一步中建立分区文件夹的名字一模一样,也不会在你实际的文件夹里建立新的文件夹,单纯只在这个工程里有。)
(4)调试小技巧:
前面说到,在C/C++这一栏,需要添加Define处的宏定义,当然你也可以像我之前一样编译错误的时候就在文件里面自己写出宏定义,否则编译错误显示一些数据类型无法找到definition,就很难确认到底是以下三种情况哪一种:1、本身缺少.h文件 2、缺少宏定义 3、Include Path 里面少囊括了头文件。
比如像下面这种:
一个一个去调试已经没有意义了,可能是某种原因导致其中一个头文件没有参加编译(我确定文件都是全的,因为它没有显示是找不到.h文件)。这时候就需要单击错误信息,看一看是什么地方没定义。
以上述报错当中HAL_Init为例:
CTRL + F打开 查找框
选择Find in Files
查找结果显示:HAL_Init 主要分布在stm32f1xx_hal.c 和stm32f1xx.h文件中,上图倒数第二行给我们展示了HAL_Init 函数是一个返回值类型是 HAL_StatusTypeDef 结构体 类型的函数 而且是 在.h文件中。
这时候我们就考虑这个.h文件是不是出什么问题了,继续在Find in Files查找这个这个头文件
其中前面10行,指向的都是hal的驱动文件,它们属于建立在stm32f1xx_hal.h基础上的外设驱动文件,而.h文件本身就用来初始化HAL库,所以include 属于正常的现象。
而倒数第二行指向的stm32f1xx.c跟stm32f1xx.h是一对互相诠释的好兄弟
值得怀疑的是最后一行 stm32f1xx.h,这是一个引入HAL库文件的开始点
我们点开查看
果然,定位到的地方是一个if define语句 如果我们没有定义USE_HAL_DRIVER,那这个头文件显然就没有参加include
继续 查找一下 USE_HAL_DRIVER
全都是#if define 压根就没有直接define USE_HAL_DRIVER 可以确认问题就是缺了define
这时候就可以考虑自己写一个define USE_HAL_DRIVER 或者直接在设置里添加宏定义,再次编译,没有问题了。
4、总结
HAL库的好处:初级开发者不需要太仔细阅读芯片手册;封闭性较好;开发速度较快
坏处:你需要去阅读HAL库文件的函数,英文备注很烧脑,甚至你会觉得他的算法比较抽象;代码量太大,占内存,优化不是很好。
推荐还是需要仔细去看stm32f1xx.h 了解其中结构体数据类型有哪些 然后从寄存器的角度去看待HAL库函数做了些什么。