热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

STM32从固件库到HAL库

让坚持成为一种热爱,极致成为一种精神。历时10个月,目前我又重新回到了程序员的身份,2023想玩不一样的嵌入式。🚀目录前言



让坚持成为一种热爱,极致成为一种精神。历时10个月,目前我又重新回到了程序员的身份,2023想玩不一样的嵌入式。🚀




目录


  • 前言
  • 一、安装STM32CubeMX
  • 二、配置自己的HAL库MDK工程
  • 三、HAL库与固件库的区别
    • 1.句柄
    • 2.回调函数
    • 3.时钟配置
    • 4.HAL_Init()
    • 5.其他

  • 结语






前言

我学STM32是基于固件库的,之后的一年都是用固件库开发STM32。其实固件库还是挺好用的,很稳定,即使ST已经放弃很多年了。
为什么改用HAL库?
1)CubeMX+HAL库是大势所趋。
2)CubeMX可以直接生成驱动代码,而且CubeMX是很好的工具,即使不用HAL库,里面的一些工具对开发也很有帮助。
3)HAL库效率不如固件库,一些关键代码可以改为寄存器操作,比如串口、DMA、ADC。
4)现在很多优秀的开源项目都是基于HAL库开发,看不懂代码怎么CV[doge]
一些个人做法
直接用CubeMX生成的MDK工程开发我认为是不可取的。开发过程中随时会修改驱动参数,而CubeMX每次修改都会覆盖原工程,只能在固定区域编程,极其不优雅。因此我的做法是用自己配置的工程开发,然后把CubeMX生成的驱动代码复制到自己工程里。本文基于这种做法展开。

开发环境
操作系统:Windows 11
JAVA 1.8.0_351
STM32CubeMX 6.5
STM32Cube_FW_F1_V 1.8.4
STM32Cube_FW_F4_V 1.26.1
keil MDK 5.36


一、安装STM32CubeMX

CubeMX需要JAVA环境,首先需要安装JAVA。适用于 Windows 的 64位 Java
然后安装CubeMX,直接在ST官网搜即可。ST官网
安装完配置仓库地址,然后下载对应的芯片包即可。
第一次打开设置时会报错,等几分钟再打开
在这里插入图片描述
在这里插入图片描述
下载芯片包
在这里插入图片描述
在这里插入图片描述
开启工程后第一步应该先设置RCC时钟源和SYS调试接口,根据我的做法这部分可以直接跳过,因为只需要驱动代码,所以直接配置IO口生成即可。



二、配置自己的HAL库MDK工程

这部分跟配置固件库工程步骤相同,只是把对应文件改为HAL库,这里我直接在正点原子的例程代码基础上进行修改,HAL库版本的原子例程依旧带有system文件夹。然后建立BSP文件,把CubeMX生成的驱动代码复制过来即可。下面讲下HAL库跟固件库的一些区别。



三、HAL库与固件库的区别

这里简单说下除命名外的一些区别。


1.句柄

所谓句柄,在HAL库里就是一个外设的结构体。
在固件库中我们是这样初始化外设的:先定义一个函数,在函数里定义一个结构体,开启时钟,然后操作这个结构体成员,最后利用这个结构体初始化外设。
在HAL库中,需要定义一个全局变量结构体,也就是句柄,为什么是全局变量?因为不只是初始化外设要用到,后面对外设的一系列操作都需要用到,比如说ADC的校正、读取,串口的接发。


2.回调函数

在固件库初始化外设中,一般都是先打开时钟,配置GPIO,配置复用功能。而在HAL库中时钟、GPIO和中断的配置都放到了回调函数中(带有MspInit结尾),因为HAL库在Init外设后会自动调用回调函数,回调函数是weak(弱定义)函数,默认为空,也就是ST让用户重新定义的函数。既然回调函数中放入了时钟和GPIO配置,那么初始化函数中就只剩下外设的复用配置。
还有一个问题,例如串口只有一个回调函数HAL_UART_MspInit,在配置USART1时会调用函数HAL_UART_MspInit,配置USART2时也会调用函数HAL_UART_MspInit,所以HAL_UART_MspInit中必须判断目前在配置哪个串口,这样才不会出错。
另外,回调函数默认是一个空函数,不一定要按照ST的思路来编程,我们也可以把回调函数中的代码放回到初始化函数中,也就是像用固件库编程那样。下面是串口的回调函数代码。

void HAL_UART_MspInit(UART_HandleTypeDef *huart)
{
GPIO_InitTypeDef gpio_init_struct;
if(huart->Instance == USART_UX) /* 如果是串口1,进行串口1 MSP初始化 */
{
USART_UX_CLK_ENABLE(); /* USART1 时钟使能 */
USART_TX_GPIO_CLK_ENABLE(); /* 发送引脚时钟使能 */
USART_RX_GPIO_CLK_ENABLE(); /* 接收引脚时钟使能 */
gpio_init_struct.Pin = USART_TX_GPIO_PIN; /* TX引脚 */
gpio_init_struct.Mode = GPIO_MODE_AF_PP; /* 复用推挽输出 */
gpio_init_struct.Pull = GPIO_PULLUP; /* 上拉 */
gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH; /* 高速 */
gpio_init_struct.Alternate = USART_TX_GPIO_AF; /* 复用为USART1 */
HAL_GPIO_Init(USART_TX_GPIO_PORT, &gpio_init_struct); /* 初始化发送引脚 */
gpio_init_struct.Pin = USART_RX_GPIO_PIN; /* RX引脚 */
gpio_init_struct.Alternate = USART_RX_GPIO_AF; /* 复用为USART1 */
HAL_GPIO_Init(USART_RX_GPIO_PORT, &gpio_init_struct); /* 初始化接收引脚 */
#if USART_EN_RX
HAL_NVIC_EnableIRQ(USART_UX_IRQn); /* 使能USART1中断通道 */
HAL_NVIC_SetPriority(USART_UX_IRQn, 3, 3); /* 抢占优先级3,子优先级3 */
#endif
}

3.时钟配置

在固件库中,根据外设对应的AHB、APB1、APB2时钟用对应的RCC函数打开时钟。如

RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA |RCC_APB2Periph_ADC1, ENABLE );

在HAL库中把每个外设和GPIO的时钟都单独分开为一个宏。如

ADC_ADCX_CHY_CLK_ENABLE(); /* 使能ADCx时钟 */
ADC_ADCX_CHY_GPIO_CLK_ENABLE(); /* 开启GPIO时钟 */

4.HAL_Init()

main函数中需要加入HAL_Init()函数进行系统初始化。
HAL_Init()的功能是配置Flash,设置中断分组、系统时钟。默认的中断分组是4(如果后面不进行修改)

HAL_StatusTypeDef HAL_Init(void)
{
/* Configure Flash prefetch, Instruction cache, Data cache */
#if (INSTRUCTION_CACHE_ENABLE != 0U)
__HAL_FLASH_INSTRUCTION_CACHE_ENABLE();
#endif /* INSTRUCTION_CACHE_ENABLE */
#if (DATA_CACHE_ENABLE != 0U)
__HAL_FLASH_DATA_CACHE_ENABLE();
#endif /* DATA_CACHE_ENABLE */
#if (PREFETCH_ENABLE != 0U)
__HAL_FLASH_PREFETCH_BUFFER_ENABLE();
#endif /* PREFETCH_ENABLE */
/* Set Interrupt Group Priority */
HAL_NVIC_SetPriorityGrouping(NVIC_PRIORITYGROUP_4);
/* Use systick as time base source and configure 1ms tick (default clock after Reset is HSI) */
HAL_InitTick(TICK_INT_PRIORITY);
/* Init the low level hardware */
HAL_MspInit();
/* Return function status */
return HAL_OK;
}

不难发现,里面也有一个回调函数,这里也是一个空函数。


5.其他

以上四个是重要区别,其他基本都是命名区别,可以结合例程代码进行配置,在熟悉固件库的基础上花几个小时就可以快速入门HAL库。

另外我在配置时钟,发现我之前一直忽视了32的ADC时钟,以F1为例,官方给出的ADC最高采样率是1M,时钟频率最高为14MHz。
采样率 = 1 / ( 用户配置时钟周期 + 12.5(固定转换时钟周期)×(1/14MHz))
用户配置周期最小1.5,在这种情况下可以达到1M采样率。
但是当系统为72M时ADC并不能达到14MHz,如下图所示,ADC时钟最高只能达到12MHz
此时最高率为1 / ( 1.5 + 12.5(固定转换时钟周期)×(1/12MHz))≈ 857KHz
也就是说如果要ADC跑到1M(F1芯片),只能更换系统时钟频率(降频)。
在这里插入图片描述



结语

以上是本篇文章的所有内容。







推荐阅读
  • 本文介绍了一种轻巧方便的工具——集算器,通过使用集算器可以将文本日志变成结构化数据,然后可以使用SQL式查询。集算器利用集算语言的优点,将日志内容结构化为数据表结构,SPL支持直接对结构化的文件进行SQL查询,不再需要安装配置第三方数据库软件。本文还详细介绍了具体的实施过程。 ... [详细]
  • Nginx使用(server参数配置)
    本文介绍了Nginx的使用,重点讲解了server参数配置,包括端口号、主机名、根目录等内容。同时,还介绍了Nginx的反向代理功能。 ... [详细]
  • 本文介绍了在Win10上安装WinPythonHadoop的详细步骤,包括安装Python环境、安装JDK8、安装pyspark、安装Hadoop和Spark、设置环境变量、下载winutils.exe等。同时提醒注意Hadoop版本与pyspark版本的一致性,并建议重启电脑以确保安装成功。 ... [详细]
  • 本文介绍了如何使用C#制作Java+Mysql+Tomcat环境安装程序,实现一键式安装。通过将JDK、Mysql、Tomcat三者制作成一个安装包,解决了客户在安装软件时的复杂配置和繁琐问题,便于管理软件版本和系统集成。具体步骤包括配置JDK环境变量和安装Mysql服务,其中使用了MySQL Server 5.5社区版和my.ini文件。安装方法为通过命令行将目录转到mysql的bin目录下,执行mysqld --install MySQL5命令。 ... [详细]
  • Android工程师面试准备及设计模式使用场景
    本文介绍了Android工程师面试准备的经验,包括面试流程和重点准备内容。同时,还介绍了建造者模式的使用场景,以及在Android开发中的具体应用。 ... [详细]
  • 本文介绍了Composer依赖管理的重要性及使用方法。对于现代语言而言,包管理器是标配,而Composer作为PHP的包管理器,解决了PEAR的问题,并且使用简单,方便提交自己的包。文章还提到了使用Composer能够避免各种include的问题,避免命名空间冲突,并且能够方便地安装升级扩展包。 ... [详细]
  • 本文讨论了在VMWARE5.1的虚拟服务器Windows Server 2008R2上安装oracle 10g客户端时出现的问题,并提供了解决方法。错误日志显示了异常访问违例,通过分析日志中的问题帧,找到了解决问题的线索。文章详细介绍了解决方法,帮助读者顺利安装oracle 10g客户端。 ... [详细]
  • 如何实现JDK版本的切换功能,解决开发环境冲突问题
    本文介绍了在开发过程中遇到JDK版本冲突的情况,以及如何通过修改环境变量实现JDK版本的切换功能,解决开发环境冲突的问题。通过合理的切换环境,可以更好地进行项目开发。同时,提醒读者注意不仅限于1.7和1.8版本的转换,还要适应不同项目和个人开发习惯的需求。 ... [详细]
  • Go GUIlxn/walk 学习3.菜单栏和工具栏的具体实现
    本文介绍了使用Go语言的GUI库lxn/walk实现菜单栏和工具栏的具体方法,包括消息窗口的产生、文件放置动作响应和提示框的应用。部分代码来自上一篇博客和lxn/walk官方示例。文章提供了学习GUI开发的实际案例和代码示例。 ... [详细]
  • Go Cobra命令行工具入门教程
    本文介绍了Go语言实现的命令行工具Cobra的基本概念、安装方法和入门实践。Cobra被广泛应用于各种项目中,如Kubernetes、Hugo和Github CLI等。通过使用Cobra,我们可以快速创建命令行工具,适用于写测试脚本和各种服务的Admin CLI。文章还通过一个简单的demo演示了Cobra的使用方法。 ... [详细]
  • 本文讨论了clone的fork与pthread_create创建线程的不同之处。进程是一个指令执行流及其执行环境,其执行环境是一个系统资源的集合。在调用系统调用fork创建一个进程时,子进程只是完全复制父进程的资源,这样得到的子进程独立于父进程,具有良好的并发性。但是二者之间的通讯需要通过专门的通讯机制,另外通过fork创建子进程系统开销很大。因此,在某些情况下,使用clone或pthread_create创建线程可能更加高效。 ... [详细]
  • 本文介绍了win7系统休眠功能无法启动和关闭的解决方法,包括在控制面板中启用休眠功能、设置系统休眠的时间、通过命令行定时休眠、手动进入休眠状态等方法。 ... [详细]
  • 解决github访问慢的问题的方法集锦
    本文总结了国内用户在访问github网站时可能遇到的加载慢的问题,并提供了解决方法,其中包括修改hosts文件来加速访问。 ... [详细]
  • 修复安装win10失败并提示“磁盘布局不受UEFI固件支持”的方法
    本文介绍了修复安装win10失败并提示“磁盘布局不受UEFI固件支持”的方法。首先解释了UEFI的概念和作用,然后提供了两种解决方法。第一种方法是在bios界面中将Boot Mode设置为Legacy Support,Boot Priority设置为Legacy First,并关闭UEFI。第二种方法是使用U盘启动盘进入PE系统,运行磁盘分区工具DiskGenius,将硬盘的分区表设置为gpt格式,并留出288MB的内存。最后,通过运行界面输入命令cmd来完成设置。 ... [详细]
  • 本文介绍了使用哈夫曼树实现文件压缩和解压的方法。首先对数据结构课程设计中的代码进行了分析,包括使用时间调用、常量定义和统计文件中各个字符时相关的结构体。然后讨论了哈夫曼树的实现原理和算法。最后介绍了文件压缩和解压的具体步骤,包括字符统计、构建哈夫曼树、生成编码表、编码和解码过程。通过实例演示了文件压缩和解压的效果。本文的内容对于理解哈夫曼树的实现原理和应用具有一定的参考价值。 ... [详细]
author-avatar
刘小飘168585
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有