STM32学习HAL库的一些知识点积累

最近,我用STM32F107RCT6设计了一款LED灯板控制板,现在板子已经完成嘉立创PCB制板+贴片了,现在需要编写对应测试代码程序用于测试。现在需要用到STM32CubeMX这个工具生成基于keil MDK的模板工程文件,现在遇到了一些知识点(以前学习过),现在记录一下希望对自己有所帮助。

(1)外部时钟晶振HSE的选择

首先如果使用的是外部无源晶振,那么RCC的High Speed Clock(HSE)需要选择:Crystal/Ceramic Resonator这个选项。

现在大家需要注意的是,如果HSE选项选择了Bypass Clock Source,会发现STM32cubeMX软件也分配了2个引脚,这个与我以前记忆中的不一致了,毕竟有源时钟就需要一个OSC_IN就可以了。后来我查询了好多资料,发现F1、F2、F4都存在这样的问题了,后来查询了一下资料,并问了以前的同事,发现并不是stm32cubeMX的bug,而是stm32为了避险,防止OSC_OUT引脚用作其他功能(毕竟已经被占用了)。而且在生成的HAL库中Bypass是兼容Cystal/Ceramic Resonator的,如下图所示:

cpp 复制代码
#define RCC_HSE_BYPASS    (uint32_t)(RCC_CR_HSEBYP | RCC_CR_HSEON)

(2)HAL库中位操作如何找到对应参考手册中的寄存器以及寄存器操作

首先,HAL库环境中使用宏定义的方式对寄存器的地址进行了封装,如下图所示:

cpp 复制代码
#define RCC                 ((RCC_TypeDef *)RCC_BASE)
#define CRC                 ((CRC_TypeDef *)CRC_BASE)
#define FLASH               ((FLASH_TypeDef *)FLASH_R_BASE)
#define OB                  ((OB_TypeDef *)OB_BASE)
#define ETH                 ((ETH_TypeDef *) ETH_BASE)
#define DBGMCU              ((DBGMCU_TypeDef *)DBGMCU_BASE)

#define USB_OTG_FS          ((USB_OTG_GlobalTypeDef *)USB_OTG_FS_PERIPH_BASE)

RCC为STM32的RCC模块对应寄存器组的首地址,我们都知道ARM为32位单片机,寄存器为32位的占4个字节,如果继续查询可以知道RCC对应的地址为寄存器组的首元素的地址。

通过追踪RCC宏定义的值,得到RCC的值为/**0x4002_1000 */可以查询参考手册中RCC字样得到内存映射(Memory map)相关章节:

两者起始地址都是0x4002 1000。

另外,RCC这个地址值0x4000_1000被强制转换为"(RCC_TypeDef *)",这个RCC_TypeDef为自定义结构体类型:

cpp 复制代码
typedef struct
{
  __IO uint32_t CR;
  __IO uint32_t CFGR;
  __IO uint32_t CIR;
  __IO uint32_t APB2RSTR;
  __IO uint32_t APB1RSTR;
  __IO uint32_t AHBENR;
  __IO uint32_t APB2ENR;
  __IO uint32_t APB1ENR;
  __IO uint32_t BDCR;
  __IO uint32_t CSR;

  __IO uint32_t AHBRSTR;
  __IO uint32_t CFGR2;

} RCC_TypeDef;

这个结构体类型内部成员是各种uint32_t类型的变量,而且有一定顺序,通过查询参考手册,发现成员变量为各个寄存器(相当于内存空间),如下所示。

cpp 复制代码
#define __HAL_RCC_GET_FLAG(__FLAG__) (((((__FLAG__) >> 5U) == CR_REG_INDEX)?   RCC->CR   : \
                                      ((((__FLAG__) >> 5U) == BDCR_REG_INDEX)? RCC->BDCR : \
                                                                              RCC->CSR)) & (1U << ((__FLAG__) & RCC_FLAG_MASK)))

再回到HAL代码中,通过RCC->xxx寄存器来读写访问这些寄存器,如果RCC->xxx寄存器作为赋值运算符的左值,那么会进行写操作,如果RCC->xxx寄存器作为右值则进行读操作。可以将c语言中"->"使用"_"替代,再参考手册中查询得到对应寄存的值。例如:搜索RCC_CR可以搜索找到CR寄存器。

其中,Address offset:0x00表示距离首元素首地址偏移量为0x00。Reset Value为复位上电后的默认值。

表格中的31~0表示CR寄存器的bit位,其中有一些是作为一组共同作用的,例如bit7~bit3一共4为一共有2^4=16种可能,每一种可能都对应某些操作,我们只需要使用c语言的相关位操作来操作那个这些寄存器位即可实现硬件功能控制。表格中间层表示bit位或者bit位组表示的功能,表格最下层r:只读,rw:可读可写,w:只写。

那么,现在遇到一个问题:c语言代码软件是如何表示这个bit位的呢。

答案:还是宏定义,我们可以通过搜索RCC_xxx寄存器_yyy功能来找到对应寄存器的bit位或者比特位组。

例如:搜索RCC_CR_HSION,找到如下:

cpp 复制代码
#define RCC_CR_HSEON_Msk                     (0x1U << RCC_CR_HSEON_Pos)        /*!< 0x00010000 */
#define RCC_CR_HSEON                         RCC_CR_HSEON_Msk

经过计算:RCC_CR_HSEON:0x1U<<16,这个与硬件参考手册RCC_CR寄存器的bit16相对应。

使用了含参宏SET_BIT()、CLEAR_BIT()对这些bit位进行读1或者写0操作。

cpp 复制代码
                      else if ((__STATE__) == RCC_HSE_BYPASS)               \
                      {                                                     \
                        SET_BIT(RCC->CR, RCC_CR_HSEBYP);                    \
                        SET_BIT(RCC->CR, RCC_CR_HSEON);                     \
                      }                                                     \
                      else                                                  \
                      {                                                     \
                        CLEAR_BIT(RCC->CR, RCC_CR_HSEON);                   \
                        CLEAR_BIT(RCC->CR, RCC_CR_HSEBYP);      
cpp 复制代码
#define SET_BIT(REG, BIT)     ((REG) |= (BIT))

#define CLEAR_BIT(REG, BIT)   ((REG) &= ~(BIT))

#define READ_BIT(REG, BIT)    ((REG) & (BIT))

#define CLEAR_REG(REG)        ((REG) = (0x0))

#define WRITE_REG(REG, VAL)   ((REG) = (VAL))

#define READ_REG(REG)         ((REG))

#define MODIFY_REG(REG, CLEARMASK, SETMASK)  WRITE_REG((REG), (((READ_REG(REG)) & (~(CLEARMASK))) | (SETMASK)))

#define POSITION_VAL(VAL)     (__CLZ(__RBIT(VAL))) 

因为,这个bit位对应功能是可读可写的,所以位与运算(&)实现了对应bit位中数值的读取工作

cpp 复制代码
else if((RCC->CR &RCC_CR_HSEON) == RCC_CR_HSEON)
  {
    RCC_OscInitStruct->HSEState = RCC_HSE_ON;
  }

最后,再次理解"硬件软件一盘棋"这句话的含义:硬件就是对应数据手册中相关寄存器以及寄存器功能,软件指的是用c语言编写实现的代码。硬件有的功能都可以通过软件编写实现出来。另外,c语言为什么是最适合操作硬件的语言,这句话也得到充分论证。

相关推荐
知识分享小能手4 分钟前
深度学习学习教程,从入门到精通,概率与信息论 — 知识点详解(3)
人工智能·深度学习·学习·数据挖掘·概率论
Wendy不吃榴莲10 分钟前
# AI短剧教程 - 《后西游记》开播后,AI影视为什么更考验“连续讲故事”?
人工智能·笔记·学习·ai·视频
Agudamu116115 分钟前
B站学习视频怎么变笔记:用 Ai好记 + Obsidian 搭建个人知识库的完整教程
人工智能·笔记·学习·音视频
kkkkkkkkkk_Z1 小时前
学嵌入式和Linux应用编程|学习日记:计算机网络基础
linux·学习·计算机网络
麦田里的粮仓1 小时前
图形设计工具箱(免费开源)
学习
每天题库1 小时前
交安C证报考条件有哪些?公路水运安全员C证报名时间及考试科目
学习·安全·考试·题库·考证
lifallen2 小时前
长任务怎样选择遗忘:clearing、compaction 与 memory
人工智能·学习·ai·ai编程
高亦真2 小时前
今天是学习嵌入式的第35天
linux·学习·算法
️学习的小王2 小时前
FastAPI核心知识点与高频易错点总结
学习·fastapi
HugoStudio_SWAN2 小时前
洛谷 P1319 / P1320 压缩技术——同一枚硬币的正反面
c++·学习·程序人生·算法