细说HAL_GPIO_WritePin()函数写操作BSRR和BRR端口寄存器的原理

本文详细介绍了STM32HAL库中的HAL_GPIO_WritePin函数,比较了它与HAL_GPIO_TogglePin的区别,以及assert_param语句在参数校验中的作用。还讨论了ifdef条件编译在代码中的应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

目录

1.HAL_GPIO_WritePin()函数

(1) 细说HAL_GPIO_WritePin()函数

(2)HAL_GPIO_WritePin()和HAl_GPIO_TogglePin()的异同

2.assert_param语句的功能

(1)首先,HAL_GPIO_TogglePin()中的assert_param

(2)再看,assert_param()定义

3.ifdef条件编译


1.HAL_GPIO_WritePin()函数

        HAL_GPIO_WritePin()函数也是很常用的实现GPIO引脚输出状态变化的库函数。

        该函数HAL_GPIO_WritePin()的定义:

void HAL_GPIO_WritePin(GPIO_TypeDef *GPIOx,uint16_t GPIO_Pin,GPIO_PinState PinState)
{
    /*Check the parameter*/
    assert_param(IS_GPIO_PIN(GPIO_Pin));
    assert_param(IS_GPIO_PIN_ACTION(PinState));
    if (PinState != GPIO_PIN_RESET)
    {
        GPIOx ->BSRR = (uint32_t)GPIO_Pin; //BSRR寄存器的低16位置位
    }
    else
    {
        GPIOx ->BRR = (uint32_t)GPIO_Pin;  //BRR寄存器的低16位复位等效于BSRR寄存器的高16位复位
    }
}

        HAL_GPIO_WritePin()函数有3个参数,前面两个与GPIO_TogglePin()相同;多出的参数为PinState,顾名思义,是引脚状态。PinState的类型是GPIO_PinState,可以在固件库中查看对GPIO_PinState的声明:

typedef enum
{
    GPIO_PIN_RESET = 0U,
    GPIO_PIN_SET
} GPIO_PinState;

        这是一个枚举类型,用typedef关键词将枚举类型定义成GPIO_PinState,其有两个成员:GPIO_PIN_RESET,被赋值为0;另一个是GPIO_PIN_SET,被赋值为1(后续枚举成员的值在前一个枚举值基础上自动+1)。

        HAL_GPIO_WritePin()函数前面两个参数用于指定端口和引脚号,第3个参数用于设置该I/O引脚的状态(0或1)。

(1) 细说HAL_GPIO_WritePin()函数

        在HAL_GPIO_WritePin()函数的定义中,首先是两行assert_param()的声明语句。if语句的条件是判断传递过来的参数PinState是否不等于0(GPIO_PIN_RESET);如果参数PinState为1(即不等于0),则通过BSRR寄存器的低16位(BS)置位,使得该引脚输出1;如果等于0,则通过BRR寄存器复位该引脚状态,使其输出0。

        固件库中的函数控制I/O,是通过操作BSRR和BRR寄存实现的。实际上,也可以直接给ODR寄存器赋值来实现对I/O引脚状态的控制,但相比于操作BSRR和BRR寄存器,这样做效率会低一些。

(2)HAL_GPIO_WritePin()和HAl_GPIO_TogglePin()的异同

        HAL_GPIO_WritePin()和HAl_GPIO_TogglePin()都可以实现对I/O引脚状态的控制,但实现的功能有区别。HAL_GPIO_WritePin()可以让I/O输出一个给定的电平(高或低电平);而函数HAl_GPIO_TogglePin()只是让相应的I/O引脚输出状态翻转,至于是输出高电平还是低电平,取决于之前的状态。

2.assert_param语句的功能

        在函数HAL_GPIO_TogglePin()中,该语句为:

assert_param(IS_GPIO_PIN(GPIO_Pin));

        在HAL_GPIO_WritePin()中,该语句为:        

assert_param(IS_GPIO_PIN(GPIO_Pin));
assert_param(IS_GPIO_PIN_ACTION(PinState));

        实际上assert_param()是一种宏,用于函数的参数检查。括号中是一个表达式为真(有效),则程序会继续执行下去;若表达式为假,则程序就会跳转,去执行可以发出错误信息的代码段。

(1)首先,HAL_GPIO_TogglePin()中的assert_param

assert_param(IS_GPIO_PIN(GPIO_Pin));

        其中,GPIO_Pin是HAL_GPIO_TogglePin的参数,用于指定I/O的引脚号,例如PB3,则GPIO_Pin就是无符号数0x0008。

        IS_GPIO_PIN()是判断传递过来的GPIO_Pin是否为有效的GPIO引脚。GPIO_Pin是无符号数,GPIO端口通常为16个,GPIO_Pin从右边第1位开始,分别为0x0001、0x0004、0x0008、…、0x8000。也就是说,传递过来的GPIO_Pin应该是不为0并且只有16位的数。查看固件库中关于IS_GPIO_PIN()的声明,stm32g4xx_hal_gpio.h文件中,可知它是由define宏定义的:

# define IS_GPIO_PIN(__PIN__)((((uint32_t)(__PIN__) & GPIO_PIN_MASK) != 0x00U) && (((uint32_t)(__PIN_) & ~GPIO_PIN_MASK) = 0x00U))

        其中,__PIN__也是库函数中的宏定义,用来指明引脚号;GPIO_PIN_MASK也是通过宏定义的:

# define GPIO_PIN_MASK (0×0000FTTFU) /*PIN mask for assert test */

        在IS_GPIO_PIN(__PIN__)的定义中,当右侧两个表达式同时成立时IS_GPIO_PIN(__PIN__)才为真,这两个表达式通过&&连接。第一个表达式中,(__PIN__)&.GPIO_PIN_MASK)是用于判断传递过来的引脚号是否为GPIO_PIN_MASK为0x0000 FFFF,只要(__PIN__)不为0x0000,与0x0000 FFFF按位“与”的结果就不为0,此时第一个表达式即为真;否则为假。第二个表达式是将传递过来的引脚号和~GPIO_PIN_MASK进行逻辑“与”,只要传递过来的引脚号不多于16位,该表达式(==)就为真;否则表达式为假。两个表达式同时为真时,IS_GPIO_PIN(__PIN__)真;此时assert_param()中的表达式为真,程序会继续执行。否则就会调用assert_failed函数,输出错误信息。

        assert_param()的定义在固件库的stm32g4xx_hal_conf.h文件中,其声明如下:

#ifdef USE_FULL_ASSERT
/**
  * @brief  The assert_param macro is used for function's parameters check.
  * @param  expr: If expr is false, it calls assert_failed function
  *         which reports the name of the source file and the source
  *         line number of the call that failed.
  *         If expr is true, it returns no value.
  * @retval None
  */
#define assert_param(expr) ((expr) ? (void)0U : assert_failed((uint8_t *)__FILE__, __LINE__))
/* Exported functions ------------------------------------------------------- */
void assert_failed(uint8_t *file, uint32_t line);
#else
#define assert_param(expr) ((void)0U)
#endif /* USE_FULL_ASSERT */

(2)再看,assert_param()定义

        如果USE_FULL_ASSERT已定义过,则会编译下面两条语句:

# define assert_param(expr)((expr)?(void)0U:assert_failed((uint8_t *)__FILE__ , __LINE__))
void assert_failed(uint8_t * file,uint32_t line);

        第一句define宏定义中的(void)0U,是空语句的表达式。将assert_param(expr)定义为:

        如果表达式expr真,就是空语句(void)0U;如果表达式expr假,则为assert_failed()函数。define宏定义后,就是对assert_failed()的声明。

        如果USE_FULL_ASSERT未定义过,则会编译#else中的内容,就是用define宏定义直接将assert_param(expr)定义为空语句(void)0U。

        其中,在(void)0U中,“0”后跟个“U”表示此“0”为无符号数。"__FILE__,__LINE__"也是宏定义,用来表示文件和行数。函数assert_failed()在这个定义中只是进行了声明,并没有具体定义。

3.ifdef条件编译

        声明中用到了ifdef条件编译。它的定义格式如下:

# ifdef 标识符
    程序段1  //标识符被定义过(通常是用#define命令定义的),则编译程序段1
# else      //条件编译中可以没有#else部分
    程序段2  //否则编译程序段2
# endif

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

wenchm

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

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

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

打赏作者

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

抵扣说明:

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

余额充值