|
本帖最后由 longge 于 2019-3-29 13:47 编辑
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));
}
希望大家测试下,是否真的有这个问题。还有,以上都是个人意见,如有不妥之处,希望大家见谅。
|
评分
-
查看全部评分
|