
本帖最后由 绿茵场上的舞者 于 2019-3-29 13:46 编辑 ST的官方工具STM32CubeMX是目前开发STM32的常用工具,之前在STM32F4和STM32L0使用过,感觉还可以。我用的都是LL库,不是HAL库系列。因为LL库比HAL效率更高,接近之前的标准库,当然是封装的层少,个人感觉。 今天手里有块STM32F103C8的开发板,用STM32CubeMX开发程序中,发现本来设定的LED灯都是常闭的,可是程序跑起来后,发现默认都是常亮的。自己一度怀疑是程序写的有问题或是J-link有问题。 后来,调试发现是ST官方的STM32Cube_FW_F1_V1.7.0存在bug。调试中发现通过设置GPIO port bit set/reset register (GPIOx_BSRR) (x = A..I/J/K)来设置GPIO port output data register (GPIOx_ODR) (x = A..I/J/K)寄存器,引脚对应的寄存器位正常被设置, 但是在随后的LL_GPIO_Init(GPIOA, &GPIO_InitStruct)函数中设置对应引脚上下拉时,对应的GPIOx_ODR (x = A..I/J/K)寄存器也会被设置。还有,我都是通过STM32CubeMX里边下载官方库,应该是最新的SMT32F1系列的HAL库,STM32F1版本:STM32Cube_FW_F1_V1.7.0,STM32F4版本:STM32Cube_FW_F4_V1.24.0,STM32L0版本:STM32Cube_FW_L0_V1.11.2。 以下是STM32CubeMX自动生成的GPIO初始化代码:官方生成的初始化代码都是先设置对应的引脚电平,然后再初始化设置引脚的模式,速率等。如果先初始化引脚的模式等,再设置引脚的电平不会出现这个问题。 /** Configure pins as * Analog * Input * Output * EVENT_OUT * EXTI */ void MX_GPIO_Init(void) { LL_GPIO_InitTypeDef GPIO_InitStruct = {0}; /* GPIO Ports Clock Enable */ LL_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_GPIOD); LL_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_GPIOA); LL_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_GPIOB); /**/ LL_GPIO_ResetOutputPin(GPIOA, LED0_Pin | POWER_MC20_Pin); LL_GPIO_SetOutputPin(GPIOA, LED0_Pin); LL_GPIO_SetOutputPin(GPIOA, LL_GPIO_PIN_0); //设置对应的引脚默认为高电平,但是经过以下的LL_GPIO_Init(GPIOA, &GPIO_InitStruct)后,GPIOx_ODR会被设置为低电平。我设置的是GPIOA0引脚,换为GPIOB0和GPIOB1引脚还是存在这个问题。 /**/ LL_GPIO_SetOutputPin(GPIOB, LED1_Pin | LED2_Pin); /**/ GPIO_InitStruct.Pin = LED0_Pin | POWER_MC20_Pin; GPIO_InitStruct.Mode = LL_GPIO_MODE_OUTPUT; GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_LOW; GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL; LL_GPIO_Init(GPIOA, &GPIO_InitStruct); // LL_GPIO_SetOutputPin(GPIOA, LED0_Pin); /**/ GPIO_InitStruct.Pin = LED1_Pin | LED2_Pin; GPIO_InitStruct.Mode = LL_GPIO_MODE_OUTPUT; GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_LOW; GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL; LL_GPIO_Init(GPIOB, &GPIO_InitStruct); // LL_GPIO_SetOutputPin(GPIOB, LED1_Pin | LED2_Pin); } 注释部分是我手动添加的,默认软件是先设置对应的引脚电平,然后在初始化的。可是,由于函数LL_GPIO_Init(GPIOA, &GPIO_InitStruct)中设置上下拉部分的作用会导致之前设置的高电平被清零掉。要想真正的起作用,必须在函数LL_GPIO_Init(GPIOA, &GPIO_InitStruct)后重新设置对应引脚的电平。 以下是官方HAL库中stm32f1xx_ll_gpio.c文件中函数LL_GPIO_Init()代码,加注释的地方应该存在bug。 /** * @brief Initialize GPIO registers according to the specified parameters in GPIO_InitStruct. * @param GPIOx GPIO Port * @param GPIO_InitStruct: pointer to a @ref LL_GPIO_InitTypeDef structure * that contains the configuration information for the specified GPIO peripheral. * @retval An ErrorStatus enumeration value: * - SUCCESS: GPIO registers are initialized according to GPIO_InitStruct content * - ERROR: Not applicable */ ErrorStatus LL_GPIO_Init(GPIO_TypeDef *GPIOx, LL_GPIO_InitTypeDef *GPIO_InitStruct) { uint32_t pinmask; uint32_t pinpos; uint32_t currentpin; /* Check the parameters */ assert_param(IS_GPIO_ALL_INSTANCE(GPIOx)); assert_param(IS_LL_GPIO_PIN(GPIO_InitStruct->Pin)); /* ------------------------- Configure the port pins ---------------- */ /* Initialize pinpos on first pin set */ pinmask = ((GPIO_InitStruct->Pin) << GPIO_PIN_MASK_POS) >> GPIO_PIN_NB; pinpos = POSITION_VAL(pinmask); /* Configure the port pins */ while ((pinmask >> pinpos) != 0U) { /* skip if bit is not set */ if ((pinmask & (1U << pinpos)) != 0U) { /* Get current io position */ if (pinpos < GPIO_PIN_MASK_POS) { currentpin = (0x00000101U << pinpos); } else { currentpin = ((0x00010001U << (pinpos - GPIO_PIN_MASK_POS)) | 0x04000000U); } /* Check Pin Mode and Pin Pull parameters */ assert_param(IS_LL_GPIO_MODE(GPIO_InitStruct->Mode)); assert_param(IS_LL_GPIO_PULL(GPIO_InitStruct->Pull)); /* Pin Mode configuration */ LL_GPIO_SetPinMode(GPIOx, currentpin, GPIO_InitStruct->Mode); /* Pull-up Pull-down resistor configuration*/ LL_GPIO_SetPinPull(GPIOx, currentpin, GPIO_InitStruct->Pull); //设置上下拉部分代码,就是这部分导致之前设置的GPIOx_ODR寄存器对应位会被清零掉。 if ((GPIO_InitStruct->Mode == LL_GPIO_MODE_OUTPUT) || (GPIO_InitStruct->Mode == LL_GPIO_MODE_ALTERNATE)) { /* Check speed and Output mode parameters */ assert_param(IS_LL_GPIO_SPEED(GPIO_InitStruct->Speed)); assert_param(IS_LL_GPIO_OUTPUT_TYPE(GPIO_InitStruct->OutputType)); /* Speed mode configuration */ LL_GPIO_SetPinSpeed(GPIOx, currentpin, GPIO_InitStruct->Speed); /* Output mode configuration*/ LL_GPIO_SetPinOutputType(GPIOx, currentpin, GPIO_InitStruct->OutputType); } } pinpos++; } return (SUCCESS); } 后来,测试STM32L0和STM32F4的LL库这部分,没有存在问题。看芯片的技术文档,发现:STM32L0和F4系列有专门的上下拉设置寄存器GPIO port pull-up/pull-down register (GPIOx_PUPDR),而STM32F1系列应该是没有这个配置的寄存器(也不是很确定,我看的RM0008 技术手册,对应 STM32F101xx, STM32F102xx, STM32F103xx, STM32F105xx and STM32F107xx advanced ARM®-based 32-bit MCUs,里面是没有提到有上下拉寄存器),所以同样的函数LL_GPIO_Init()代码处理是不一样的,在F4和L0系列中,LL_GPIO_Init()函数没有问题,在F1中存在这个问题。 以下是F1系列,F4系列,L0系列中设置上下拉函数LL_GPIO_SetPinPull()的代码: F1系列:个人感觉既然F1系列中没有上下拉设置寄存器,这个函数显得有些鸡肋,虽然显得HAL库中接口统一了。 __STATIC_INLINE void LL_GPIO_SetPinPull(GPIO_TypeDef *GPIOx, uint32_t Pin, uint32_t Pull) { MODIFY_REG(GPIOx->ODR, (Pin >> GPIO_PIN_MASK_POS), Pull << (POSITION_VAL(Pin >> GPIO_PIN_MASK_POS))); } F4系列: __STATIC_INLINE void LL_GPIO_SetPinPull(GPIO_TypeDef *GPIOx, uint32_t Pin, uint32_t Pull) { MODIFY_REG(GPIOx->PUPDR, (GPIO_PUPDR_PUPDR0 << (POSITION_VAL(Pin) * 2U)), (Pull << (POSITION_VAL(Pin) * 2U))); } L0系列: __STATIC_INLINE void LL_GPIO_SetPinPull(GPIO_TypeDef *GPIOx, uint32_t Pin, uint32_t Pull) { MODIFY_REG(GPIOx->PUPDR, ((Pin * Pin) * GPIO_PUPDR_PUPD0), ((Pin * Pin) * Pull)); } 希望大家测试下,是否真的有这个问题。还有,以上都是个人意见,如有不妥之处,希望大家见谅。 |
这个没问题。上拉下模式,配合输出寄存器来设置的,它们是相关的。
上拉模式,必然对应的输出寄存器为1;反之下拉模式,对应的输出寄存器值就是0。
评分
查看全部评分
我设置的输出引脚模式,另外,我的意思是使用STM32CubeMX官方软件自动生成的代码存在问题。
另外就是,希望大家在F1xx系列的芯片上测试下,是不是有这个问题。大家一起谈论下,看看。
明白楼主说的了,经验证的确如此。LL库的确坑多多,谨慎使用,推荐使用HAL库。
用HAL库就不存在此类问题了
的HAL库的IO初始化子函数中,做了IO输入判断,只有在输入模式下,才会有上下拉一说。
再次,在初始化IO结构体赋值前已经加了上下拉选项。