stm32库函数各类宏定义总结

stm32库函数各类宏定义总结

在各类芯片官方的库函数中,都会遇到大量的宏定义,无论是结构体,还是各类数据量、函数,都用宏来定义,封装了一层实际意义,不用对着datasheet计算各种寄存器的初始值,极大地简化了开发时间。今天就来总结一下在stm32官方库函数中宏定义的作用。

一、宏的表现形式

宏定义的写法:

#define A B

可以理解为,在c程序预编译过程中,程序中所有出现A的地方,都会用B来代替,然后程序再送到编译器进行编译。

二、宏定义的种类

1.普通定义

#define SYSCLK_FREQ_72MHz  72000000

该语句的作用是将数字72000000定义为SYSCLK_FREQ_72MHz,该标识符能直观表示系统时钟为72MHZ。

2.宏函数

#define BITBAND(addr, bitnum) ((addr & 0xF0000000)+0x2000000+((addr &0xFFFFF)<<5)+(bitnum<<2)) 

这看起来复杂,其实就是定义了一个函数BITBAND(addr, bitnum),右边可以理解为关于这两个参数的运算。

宏函数还可以嵌套,比如

#define PAout(n)   BIT_ADDR(GPIOA_ODR_Addr,n)

3.条件宏

条件宏的运用也很多,一般在头文件中开头都会出现如下所示语句:

#ifndef __SYS_H
#define __SYS_H	
...
#endif

这套语句的主要作用是防止头文件被重复引用,比如,再A.h中#include “B.h”,然后C.c中#include “A.h”,#include “B.h”,如果B.h中没有这套语句,那么B.h就会在C.c中被重复引用,本来一遍就完事了的,硬生生多搞了一遍,导致编译效率低下。如果头文件中还有变量的定义,那么就会导致变量重复定义。

条件宏还有以下表现形式:

#if __sizeof_ptr == 8
#define SIZE_MAX UINT64_MAX
#else
#define SIZE_MAX UINT32_MAX
#endif

理解起来就和普通的if语句一样

#if !defined(__cplusplus) 
...
#endif

#ifndef __cplusplus
...
#endif

上面两种表达方式是一样的,如果在c文件当中,它接下来的语句就会被考虑编译。如果在c++文件中,接下来的语句就会被忽略。

三、常见的宏表达

上面介绍了宏在库函数中的集中表现形式,几乎涵盖了大部分定义,但是实际上情况层出不穷,下面从stm32的库函数中摘取各种表现形式进行具体分析。

1.B为表达式的

#define GPIOA_ODR_Addr    (GPIOA_BASE+12) 
#define GPIOA_BASE        (APB2PERIPH_BASE + 0x0800)
#define APB2PERIPH_BASE   (PERIPH_BASE + 0x10000)
#define PERIPH_BASE       ((uint32_t)0x40000000)

这个还是非常有意思的,我们知道,在单片机里面,任何的配置都是对寄存器的配置。在stm32里面,寄存器都为32位的。表达一个寄存器的地址也是32位。例如:

*((unsigned long  *)(addr)) = 0x01;

就表示将地址addr转化为指针,而最前面的*表示取值。这样,往它赋值0x01,就表示在地址为addr的寄存器中写0x01啦。

2.宏函数的嵌套

#define BITBAND(addr, bitnum) ((addr & 0xF0000000)+0x2000000+((addr &0xFFFFF)<<5)+(bitnum<<2)) 
#define MEM_ADDR(addr)  *((volatile unsigned long  *)(addr)) 
#define BIT_ADDR(addr, bitnum)   MEM_ADDR(BITBAND(addr, bitnum)) 

#define PAout(n)   BIT_ADDR(GPIOA_ODR_Addr,n) 

从最后一行往上看,不断替换,其实上面一大段表示:

*((volatile unsigned long  *)((((GPIOA_ODR_Addr & 0xF0000000)+0x2000000+((GPIOA_ODR_Addr &0xFFFFF)<<5)+(n<<2)))))

把后面那个啰嗦的表达式简化为A,为:

*((volatile unsigned long  *) A

这下就一目了然了。所以最终的PAout(n)就表示GPIOA的第n个引脚,赋值0或1就表示输出低或高电平。

那么问题又来了,地址A不是表示一个指向32位的地址么,为什么能单独改变一个口呢?其实这是stm32的一个特性,可以理解为将ODR(输出数据寄存器)的每一位都映射成一个寄存器,往这个寄存器写值,相当于改变相应位的值。

3.地址转化

#define GPIO_Pin_0        ((uint16_t)0x0001)  /*!< Pin 0 selected */

这是每个GPIO的标志位。比如BSRR为GPIO的设置清除寄存器。每个GPIO为16个口,那么寄存器中的0~15位就表示这16个口,哪个位为1,相应的口输出就为1.

4.为宏函数

#define IS_GPIO_ALL_PERIPH(PERIPH) (((PERIPH) == GPIOA) ||\
                                ((PERIPH) == GPIOB) || \
                                ((PERIPH) == GPIOC) || \
                                ((PERIPH) == GPIOD) || \
                                ((PERIPH) == GPIOE) || \
                                ((PERIPH) == GPIOF) || \
                                ((PERIPH) == GPIOG))

这个表达式在stm32的官方库中也十分常见,用于判断你的输入是否在规定的值当中。上面表示的就是判断PERIPH是否在GPIOA~GPIOG之间。

  • 19
    点赞
  • 76
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值