【STM32 CubeMX】HAL库的本质读写寄存器

举报
人才程序员 发表于 2024/02/28 21:10:39 2024/02/28
【摘要】 @TOC 前言在嵌入式系统开发中,HAL(Hardware Abstraction Layer)库是一个重要的概念,它提供了一个抽象层,使开发者可以更容易地编写可移植的代码,而不必担心底层硬件的细节。STM32CubeMX是一款由STMicroelectronics提供的工具,用于生成STM32微控制器的初始化代码,其中包括了HAL库的使用。HAL库的本质与HAL库源码分析HAL库的本质是一...

@TOC


前言

在嵌入式系统开发中,HAL(Hardware Abstraction Layer)库是一个重要的概念,它提供了一个抽象层,使开发者可以更容易地编写可移植的代码,而不必担心底层硬件的细节。STM32CubeMX是一款由STMicroelectronics提供的工具,用于生成STM32微控制器的初始化代码,其中包括了HAL库的使用。

HAL库的本质与HAL库源码分析
HAL库的本质是一个由供应商提供的软件库,旨在提供一系列抽象接口,用于访问底层硬件资源,如GPIO、USART、I2C等。这些接口隐藏了底层硬件的细节,使得开发者能够以统一的方式进行开发,而不必担心不同型号或者不同系列的微控制器的差异。

HAL库的源码分析可以揭示其内部的工作原理和实现细节。通过分析源码,我们可以了解到每个函数的具体功能、调用关系以及与底层硬件交互的细节。HAL库通常包括了对不同硬件模块的驱动程序,这些驱动程序是与特定型号的微控制器兼容的。


一、HAL库的本质

1.1 HAL库的本质是操作寄存器

其实点灯就是操作下面的output data register
在这里插入图片描述

比如我们之前点灯写的HAL_GPIO_WritePin()他的源码如下:

void HAL_GPIO_WritePin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin, GPIO_PinState PinState)
{
  /* Check the parameters */
  assert_param(IS_GPIO_PIN(GPIO_Pin));
  assert_param(IS_GPIO_PIN_ACTION(PinState));

  if (PinState != GPIO_PIN_RESET)
  {
    GPIOx->BSRR = GPIO_Pin;
  }
  else
  {
    GPIOx->BSRR = (uint32_t)GPIO_Pin << 16u;
  }
}

他去设置某个寄存器等于某个值,这叫是HAL库的本质

1.2 自己实现HAL_GPIO_WritePin

寄存器

CPU可以发出地址,然后访问比如Flash、RAM、GPIOC
我们可以通过访问某一个地址区间去访问Flash
访问其他地址访问GPIOC等等等等

我们可以访问ram,写入val,读出仍是val
我们可以访问flash,使用读出指令,不能直接写

在GPIOC里面有很多的寄存器,但我们不能像操作ram和flash一样,这些寄存器的功能各有不同
比如说以我这个F103ZE为例子:
在这里插入图片描述
比如Port configuration配置寄存器,一个低位,一个高位
比如输入寄存器:Port input data,通过读他,可以得到引脚的状态/数据
比如输出寄存器:Port output data,我们可以通过写这个寄存器,让这个引脚输出高低电平
还有一些其他的

通过寄存器的操作点灯

我们可以在芯片手册中找到GPIOC的基地址,在看GPIOC的输出寄存器偏移地址可以得出,要访问输出寄存器就要访问0x400110C这个地址的寄存器

在这里插入图片描述
比如说,举个例子:我们可以通过一个C语言的指针指向这个要操作的寄存器
然后把里面的值给改变了是不是就操作了寄存器的值
他的每一个寄存器的大小都是2bytes

首先我们使用指针指向寄存器的位子

unsigned int *p;
p	= (unsigned int*)0x40010C0C;

接下来我们需要操作第十三个寄存器,即可点亮我们的灯
比如我们可以这样设置他为1:

unsigned int val = *p;
val = val | (1<<5);
*p = val;

我们可以这样设置他为0:

val = *p;
val = val & ~(1<<5);
*p = val;

这样我们就通过寄存器输出高低电平了

要注意的是,比如你要操作GPIOC里面的13,你就要移13,像下面这样,其他的也是一样
要操作哪个引脚就偏移他的引脚名称,我这里的灯是PB5,所以就把他的地址里面的值偏移5即可
在这里插入图片描述

代码概况

/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  /* USER CODE BEGIN 2 */
	
	unsigned int *p;
	p	= (unsigned int*)0x40010C0C;

  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
		unsigned int val = *p;
		val = val | (1<<5);
		*p = val;
		
		HAL_Delay(500);
		
		val = *p;
		val = val & ~(1<<5);
		*p = val;
		
		HAL_Delay(500);
		
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */

Port bit set/reset register寄存器

像我们上面,我们需要把寄存器的内容拿出来,然后通过控制某一位的0/1来操作高低电平,这样稍微有点麻烦,那么我们可以使用下面这个寄存器Port bit set/reset register,他只需要写入1到某一位就可以输出高电平/低电平
在这里插入图片描述
他是一个32位的寄存器

比如BRy
在这里插入图片描述
他写入1就把指定的GPIO reset
0就是没有任何作用

比如BSy
在这里插入图片描述
他写入1就是指定GPIO set
0就是没有任何作用

那么我们就可以把代码变成这样:

unsigned int *p;
	p	= (unsigned int*)(0x40010C00 + 0x10);

  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
		unsigned int val = *p;
		*p = (1 << 21);
		
		HAL_Delay(500);
		
		*p = (1 << 5);
		
		HAL_Delay(500);
		
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */

通过写21位,把他变成reset状态
通过写5位,把他变成set状态,这样就实现了闪烁灯


总结

HAL库作为嵌入式系统开发中的重要工具,提供了一种方便、快捷的方式来访问STM32微控制器的硬件资源。通过使用HAL库,开发者可以更加专注于应用程序的开发,而不必花费大量时间去编写底层的驱动程序。通过深入分析HAL库的源码,我们可以更好地理解其内部实现细节,从而更好地利用这一工具来开发高效、可靠的嵌入式应用程序。

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。