STM32使用HAL库的初始化过程


HAL库驱动包

首先我们来介绍一下HAL库驱动包,HAL库是ST推出的STM32Cube软件生态下的一个分支,STM32Cube是ST公司提供的一套免费开发工具和STM32Cube固件包,包含两个关键部分

  • 允许用户通过图形化向导来生成C语言工程图形配置工具STM32CubeMX可以通过CubeMX实现方便地下载各种软件或开发固件包
  • 包括由STM32Cube硬件抽象层(HAL),还有一组一致的中间件组件(RTOS,USB,FAT文件系统,图形,TCP/IP和以太网),以及一系列完整的例程组成的STM32Cube固件包

新建HAL库版本MDK工程

新建HAL库版本MDK工程分为两个步骤:

  1. 新建工程文件夹
  2. 拷贝工程相关文件

新建工程文件夹

为了使工程的文件目录结构更加清晰,我们全在工程的根目录文件夹下建立以下几个文件夹,文件夹名称及其作用如下表

名称作用
Drivers存放于硬件相关的驱动层文件
Middlewares存放中间层组件文件和第三方中间层文件
Output存放工程编译输出文件
Projects存放MDK工程文件
User存放HAL库用户配置文件、main.c、stm32f1xx_it.c、以及我们自己编写的其他应用程序

拷贝工程相关文件

Drivers文件夹

该文件夹用于存放与硬件相关的驱动层文件,一般包括如下表所示的四个文件夹

文件夹名称作用
BSP存放开发板板级驱动代码
CMSIS存放CMSIS底层代码,如启动文件(.s文件)、stm32f1xx.h等
STM32F1xx_HAL_Driver存放ST提供的F1XX HAL库驱动代码
SYSTEM存放系统级驱动核心代码
CMSIS文件夹

CMSIS 文件夹,用于存放 CMSIS 底层代码(ARM 和 ST 提供,如:启动文件(.s 文件)、stm32f1xx.h 等各种头文件。该文件夹我们可以直接从 STM32CubeF1固件包里面拷贝,CMSIS 的文件夹路径在“STM32CubeF1 固件包→Drivers”。由于这个文件夹原来设计是用于匹配全部 F1 系列的芯片的,导致非常大, 部分文件对我们的例程来说不会使用到,而且浪费磁盘的存储空间,所以我们会对这个文件夹 进行精简:打开目录“CMSIS\Device\ST\STM32F1xx”,其中的Include文件夹里都是芯片的头文件我们只留下如图1这三个头文件,其他删除
图1
Source文件夹下的Templates 文件夹留下如图2的内容
图2
arm 文件夹存放的是启动文件,我们只需要 startup_stm32f103xe.s,其他全部删除。如图3所示
图3
最后就是 CMSIS 文件夹下的 Include 文件夹,里面都是内核的头文件,我们只需要如图4的内容
图4

SYSTEM 文件夹

SYSTEM 文件夹里面的代码由正点原子提供,是 STM32F1xx 系列的底层核心驱动函数, 可以用在 STM32F1xx系列的各个型号上面,方便大家快速构建自己的工程。

User文件夹

User文件夹用于存放HAL库用户配置文件stm32f1xx_it.cstm32f1xx_it.hmain.c、中断处理以及配置文件stm32f1xx_hal_conf.h

STM32F1时钟配置

时钟源

对于STM32F1输入时钟源主要包括HSIHSELSILSE
其中,从时钟频率来分可以分为高速时钟源和低速时钟源,HSIHSE是高速时钟,LSILSE是低速时钟
从来源可分为外部时钟源和内部时钟源
外部时钟源就是从外部接晶振的方式获取时钟源包括HSELSEHSILSI是内部时钟源,芯片上电即可产生
我们的配置过程本质就是将时钟源输出的时钟信号经过各种倍频和分频操作得到我们想要的频率

使用HAL库配置STM32F1时钟系统

编写自己的时钟配置函数在main函数里调用

在系统启动之后,程序会先执行SystemInit函数,进行系统一些初始化配置,主要做了如下两个方面工作

  • 外部存储器配置
  • 中断向量表地址配置
    HAL库的SystemInit函数并没有任何时钟相关设置,所以后续的初始化步骤我们必须编写自己的时钟配置函数,看看正点原子定义的 sys.c 文件中的时钟设置函数sys_stm32_clock_init的内容:
/**
 * @brief       系统时钟初始化函数
 * @param       plln: PLL倍频系数(PLL倍频), 取值范围: 2~16
                中断向量表位置在启动时已经在SystemInit()中初始化
 * @retval      无
 */

void sys_stm32_clock_init(uint32_t plln)
{
    HAL_StatusTypeDef ret = HAL_ERROR;
    RCC_OscInitTypeDef rcc_osc_init = {0};
    RCC_ClkInitTypeDef rcc_clk_init = {0};

    rcc_osc_init.OscillatorType = RCC_OSCILLATORTYPE_HSE;       /* 选择要配置HSE */
    rcc_osc_init.HSEState = RCC_HSE_ON;                         /* 打开HSE */
    rcc_osc_init.HSEPredivValue = RCC_HSE_PREDIV_DIV1;          /* HSE预分频系数 */
    rcc_osc_init.PLL.PLLState = RCC_PLL_ON;                     /* 打开PLL */
    rcc_osc_init.PLL.PLLSource = RCC_PLLSOURCE_HSE;             /* PLL时钟源选择HSE */
    rcc_osc_init.PLL.PLLMUL = plln;                             /* PLL倍频系数 */
    ret = HAL_RCC_OscConfig(&rcc_osc_init);                     /* 初始化 */

    if (ret != HAL_OK)
    {
        while (1);                                              /* 时钟初始化失败,之后的程序将可能无法正常执行,可以在这里加入自己的处理 */
    }
    /* 选中PLL作为系统时钟源并且配置HCLK,PCLK1和PCLK2*/

    rcc_clk_init.ClockType = (RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2);
    rcc_clk_init.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;        /* 设置系统时钟来自PLL */
    rcc_clk_init.AHBCLKDivider = RCC_SYSCLK_DIV1;               /* AHB分频系数为1 */
    rcc_clk_init.APB1CLKDivider = RCC_HCLK_DIV2;                /* APB1分频系数为2 */
    rcc_clk_init.APB2CLKDivider = RCC_HCLK_DIV1;                /* APB2分频系数为1 */
    ret = HAL_RCC_ClockConfig(&rcc_clk_init, FLASH_LATENCY_2);  /* 同时设置FLASH延时周期为2WS,也就是3个CPU周期。 */
    if (ret != HAL_OK)
    {
        while (1);                                              /* 时钟初始化失败,之后的程序将可能无法正常执行,可以在这里加入自己的处理 */
    }

sys.c文件中的时钟设置函数sys_stm32_clock_init,除了配置PLL相关参数确定SYSCLK值之外,还配置了AHBAHB1AHB2的分频系数,也就是确定了HCLKPCLK1PCLK2的时钟值。主要分为两个步骤

  • 配置时钟源相关参数:调用函数 HAL_RCC_OscConfig()
  • 配置系统时钟源SYSCLK以及AHB、APB1和APB2的分频系数:调用函数 HAL_RCC_ClockConfig()

STM32F1时钟使能和配置

在配置好时钟系统之后,如果我们要使用某些外设,例如GPIO,ADC等,我们还要使能这些外设时钟。STM32的外设时钟使能是在RCC相关寄存器中配置的。在STM32F1的HAL库中,外设时钟使能操作都是在RCC相关固件库文件头文件STM32F1xx_hal_rcc.h定义的。外设时钟使能在HAL库中都是通过宏定义标识符来实现的,首先,我们来看看 GPIOA 的外设时钟使能宏定义标识符:

#define __HAL_RCC_GPIOA_CLK_ENABLE() do { __IO uint32_t tmpreg; SET_BIT(RCC->APB2ENR, \ RCC_APB2ENR_IOPAEN); tmpreg = READ_BIT(RCC->APB2ENR, RCC_APB2ENR_IOPAEN); UNUSED(tmpreg); } \ while(0U)

这段代码主要是定义了一个宏定义标识符__HAL_RCC_GPIOA_CLK_ENABLE(),它的核 心操作是通过下面这行代码实现的:

SET_BIT(RCC->APB2ENR, RCC_APB2ENR_IOPAEN);

这行代码的作用是设置寄存器RCC->APB2ENR的相关位为1,至于是哪个位,是由宏定义标识符RCC_APB2ENR_IOPAEN的值决定的, 而它的值为:

#define RCC_APB2ENR_IOPAEN_Pos (0U)
#define RCC_APB2ENR_IOPAEN_Msk (0x1UL << RCC_APB2ENR_IOPAEN_Pos)
#define RCC_APB2ENR_IOPAEN RCC_APB2ENR_IOPAEN_Msk

上面三行代码很容易计算出来RCC_APB2ENR_IOPAEN= (0x00000001<<2),因此上面代码的作用是设置寄存器RCC->APB2ENR的位2为1,我们可以从STM32F1的参考手册中搜索APB2ENR寄存器定义,位2的作用是用来使用GPIOA时钟。APB2ENR寄存器的位2描述如下:

位 0 IOPAEN:IO 端 A 时钟使能(I/O port A clock enable)
由软件置‘1’或清‘0’
0:IO 端口 A 时钟关闭
1:IO 端口 A 时钟开启
那么我们只需要在我们的用户程序中调用宏定义标识符就可以实现GPIOA时钟使能。使用方法为:

__HAL_RCC_GPIOA_CLK_ENABLE(); /* 使能 GPIOA 时钟 */

对于其他外设,同样都是在STM32F1xx_hal_rcc.h头文件中定义,大家只需要找到相关宏定义标识符即可,这里我们列出几个常用使能外设时钟的宏定义标识符使用方法:

__HAL_RCC_DMA1_CLK_ENABLE(); /* 使能 DMA1 时钟 */
__HAL_RCC_USART2_CLK_ENABLE(); /* 使能串口 2 时钟 */
__HAL_RCC_TIM1_CLK_ENABLE(); /* 使能 TIM1 时钟 */

我们使用外设的时候需要使能外设时钟,如果我们不需要使用某个外设,同样我们可以禁止某个外设时钟。禁止外设时钟使用方法和使能外设时钟非常类似,同样是头文件中定义的宏 定义标识符。我们同样以 GPIOA 为例,宏定义标识符为:

#define __HAL_RCC_GPIOA_CLK_DISABLE() (RCC->APB2ENR) &= ~ (RCC_APB2ENR_GPIOAEN)

同样,宏定义标识符__HAL_RCC_GPIOA_CLK_DISABLE()的作用是设置RCC->APB2ENR寄存器的位2为0,也就是禁止GPIOA时钟。具体使用方法我们这里就不做过多讲解,我们这里同样列出几个常用的禁止外设时钟的宏定义标识符使用方法:

__HAL_RCC_DMA1_CLK_DISABLE(); /* 禁止 DMA1 时钟 */ 
__HAL_RCC_USART2_CLK_DISABLE(); /* 禁止串口 2 时钟 */ 
__HAL_RCC_TIM1_CLK_DISABLE(); /* 禁止 TIM1 时钟 */
  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
硬石YS-F1Pro 开发板开发手册(HAL 库) STM32 入门系列教程 STM32 Cube 是一个全面的软件平台,包括 ST 产品的每个系列, ( 如 STM32Cube_F1 是针对 STM32F1 系列)。平台包括了 STM32Cube 硬件抽象层(HAL) 和一套中间组件(RTOS,USB,FS,TCP 等等)。 STM32Cube 是由 ST 公司原创倡议,旨在减少开发负担、时间和费用,为开 发者提供轻松的开发体验。 STMCube 覆盖了 STM32 全系列。 其中 STM32CubeMX 是上位机配置软件,可以根据使用者的选择生成底层初始化代码。 硬件抽象层 (HAL), 便是 CubeMX 配套的库, HAL 库屏蔽了复杂的硬件寄存器操作, 统一了 外设的接口函数( 包含 USB/以太网等复杂外设),代码结构强壮, 已通过 CodeSonar 认证。 同时, HAL 还集成了广泛的例程,可以运行在不同意法半导体 的开发板上。 YS-F1Pro 开发板是硬石团队研发的针对 STM32F103 芯片的综合性实验测试 平台,开发板几乎囊括了芯片所有的外设,这对新手入门是非常好的学习平台, 开发板几乎所有芯片采购来着嘉立创平台,芯片质量有保证, 这与同行还是有很 大区别的。 YS-F1Pro 开发板是一款性价比极高的 STM32 开发板, 接口齐全,我们 特意添加了电机控制部分接口,包括舵机、步进电机、有刷电机、无刷电机控制 接口。 本文档的一个最重要目标是让完全小白都可以轻松愉快的入门学习STM32, 最终掌握 STM32 的基本编程方法,学会将 STM32CubeMX 应用到自己 DIY 应用 中去
### 回答1: 使用STM32F103 HAL库初始化SD卡的步骤如下: 1. 首先,确保已连接好SD卡的硬件接口和相关引脚,并通过HAL库设置好I/O引脚的模式和功能。 2. 在代码中引用HAL库的头文件,并初始化SD卡驱动。 ``` #include "sd_hal.h" SD_HandleTypeDef hsd; ``` 3. 设置SD卡的时序和通信参数。 ``` hsd.Instance = SDIO; hsd.Init.ClockEdge = SDIO_CLOCK_EDGE_RISING; hsd.Init.ClockBypass = SDIO_CLOCK_BYPASS_DISABLE; hsd.Init.ClockPowerSave = SDIO_CLOCK_POWER_SAVE_DISABLE; hsd.Init.BusWide = SDIO_BUS_WIDE_1B; // 设置为4位宽模式可以提高传输速率 hsd.Init.HardwareFlowControl = SDIO_HARDWARE_FLOW_CONTROL_DISABLE; hsd.Init.ClockDiv = 0; // 设置时钟分频系数,具体数值根据SD卡的时钟要求来定 ``` 4. 初始化SD卡驱动。 ``` if (HAL_SD_Init(&hsd) != HAL_OK) { // 初始化失败的处理代码 } ``` 5. 如果需要使用DMA进行数据传输,还需配置DMA参数。 ``` hdma_sdio.Instance = DMA2_Channel4; hdma_sdio.Init.Direction = DMA_MEMORY_TO_PERIPH; hdma_sdio.Init.PeriphInc = DMA_PINC_DISABLE; hdma_sdio.Init.MemInc = DMA_MINC_ENABLE; hdma_sdio.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD; hdma_sdio.Init.MemDataAlignment = DMA_MDATAALIGN_WORD; hdma_sdio.Init.Mode = DMA_NORMAL; hdma_sdio.Init.Priority = DMA_PRIORITY_HIGH; __HAL_LINKDMA(&hsd, hdma, hdma_sdio); ``` 6. 配置SD卡的块大小和块数。 ``` if (HAL_SD_ConfigWideBusOperation(&hsd, SDIO_BUS_WIDE_4B) != HAL_OK) { // 配置4位宽模式失败的处理代码 } if (HAL_SD_SetBlockSize(&hsd, BLOCK_SIZE) != HAL_OK) { // 设置块大小失败的处理代码 } if (HAL_SD_SetBlockCount(&hsd, BLOCK_COUNT) != HAL_OK) { // 设置块数失败的处理代码 } ``` 7. 最后,通过发送命令初始化SD卡。 ``` SD_CmdInitTypeDef sd_cmd; sd_cmd.Argument = 0; sd_cmd.CmdIndex = SD_CMD_GO_IDLE_STATE; sd_cmd.CmdResponse = SD_RESPONSE_NO; if (HAL_SD_SendCommand(&hsd, &sd_cmd, SD_TIMEOUT) != HAL_OK) { // 发送命令失败的处理代码 } ``` 以上就是使用STM32F103 HAL库初始化SD卡的基本步骤,具体的参数设置和错误处理需要根据实际情况进行调整。 ### 回答2: 要使用STM32F103 HAL库初始化SD卡,可以按照以下步骤进行操作: 1. 硬件连接:将SD卡插槽正确地连接到STM32F103系列微控制器的相应引脚和电源线上。确保为SD卡提供正确的电压。 2. 包含必要的库文件:在代码中包含与SD卡操作相关的库文件,以便能够使用HAL库提供的函数。 3. 初始化GPIO:使用HAL库函数初始化与SD卡连接的GPIO引脚。根据具体引脚连接配置初始化时钟和GPIO模式,使其能够与SD卡进行通信。 4. 配置SPI接口:使用HAL库函数配置SPI接口,设置通信速度、数据位和架构等参数。确保SPI接口能够与SD卡进行正确的通信。 5. 初始化SD卡:使用HAL库函数初始化SD卡。此函数将通过SPI接口发送命令和参数来正确地初始化SD卡。初始化过程中,需要等待SD卡的响应。 6. 配置和选择SPI片选线:使用SPI外设的HAL函数配置和选择SPI片选线,以便与SD卡进行通信。 7. 配置DMA(如果需要):根据需要配置DMA来实现更高效的数据传输。使用HAL库函数来初始化和配置DMA通道。 8. 编写SD卡读写函数:编写读写SD卡数据的函数,使用HAL库函数来实现SPI接口与SD卡的数据传输。 9. 测试SD卡功能:编写测试函数来验证SD卡的功能。可以通过读写测试数据来确认SD卡是否正常工作。 10. 错误处理:使用HAL库的错误处理机制来处理可能出现的错误。根据错误代码进行相应的错误处理。 以上是使用STM32F103 HAL库初始化SD卡的基本步骤。根据具体需求,还可以进一步扩展和优化代码,实现更复杂的功能。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

行列因式重组hhh

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值