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语言为什么是最适合操作硬件的语言,这句话也得到充分论证。

相关推荐
薛不痒2 小时前
计算机视觉opencv之图片旋转&模版匹配&银行卡号的识别
人工智能·opencv·学习·计算机视觉
江苏世纪龙科技2 小时前
当虚拟实训照进课堂:新能源汽车教学而生的动力总成拆装与检测软件
学习
Century_Dragon2 小时前
赛教融合新体验:新能源汽车动力总成虚拟仿真实训系统
学习
hkNaruto2 小时前
【AI】AI学习笔记:什么是ReAct模式 ReAct模式详解:让AI学会思考与行动
人工智能·笔记·学习
Kratzdisteln2 小时前
【1902】自适应学习系统 - 完整技术方案
java·python·学习
詩不诉卿2 小时前
Zephyr学习之点亮LED
学习
web小白成长日记2 小时前
React Router DOM 全面学习笔记:从原理到实战
笔记·学习·react.js
saoys2 小时前
Opencv 学习笔记:直方图均衡化(灰度 / 彩色图像二值化优化)
笔记·opencv·学习
嗯嗯=3 小时前
STM32单片机学习篇2
stm32·单片机·学习