andysworkshop / stm32plus

The C++ library for the STM32 F0, F100, F103, F107 and F4 microcontrollers
http://www.andybrown.me.uk
Other
745 stars 224 forks source link

I2C initialization broken when coming from GPIO #137

Closed mikepurvis closed 8 years ago

mikepurvis commented 9 years ago

This is a work in progress, but we're dealing with a peripheral which requires a bit-banged initialization sequence (don't ask), so the I2C pins are being brought up first as GPIOs, and then afterward as hardware I2C. This shouldn't be a problem, but the I2C peripheral seems to get into a nasty loop where it reports bus-error status and all writes fail, even after a peripheral reset (#136) clears the BERR flag.

However, the really interesting thing about this is that linking in a separate translation unit that uses the ST HAL 1.8.0 library allows me to use their I2C initialization functions. The following invocation, called between the stm32plus I2C initialization, and actual use of the peripheral, corrects the issue:

        // Peripheral reset
    I2C2->CR1 |= I2C_CR1_SWRST;
    I2C2->CR1 &= (uint16_t)~((uint16_t)I2C_CR1_SWRST);

    I2CHandleStruct.Instance = I2C2;
    I2CHandleStruct.Init.ClockSpeed = 31056;  // ~100KHz
    I2CHandleStruct.Init.DutyCycle = I2C_DUTYCYCLE_2;
    I2CHandleStruct.Init.OwnAddress1 = 0x00;
    I2CHandleStruct.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT;
    I2CHandleStruct.Init.DualAddressMode = I2C_DUALADDRESS_DISABLE;
    I2CHandleStruct.Init.OwnAddress2 = 0x00;
    I2CHandleStruct.Init.GeneralCallMode = I2C_GENERALCALL_DISABLE;
    I2CHandleStruct.Init.NoStretchMode = I2C_NOSTRETCH_DISABLE;
    HAL_I2C_Init( &I2CHandleStruct );   

Note that the peripheral reset is required; without it the second initialization fails. And it also isn't enough to put the reset ahead of the stm32plus I2C initialization. There's something in the HAL_I2C_Init function that is more thorough than the stdperiph routine which backs stm32plus. It'd be great to figure out what it is.

For reference, I've gisted the HAL source: https://gist.github.com/mikepurvis/d7c52178d41ba6565700#file-stm32f4xx_hal_i2c-c-L289

andysworkshop commented 9 years ago

Comparing the two init functions reveals that the HAL version initializes more register values than the standard peripheral version. Notably:

Also perhaps notable is that I2C_CR1 and I2C_OAR1 in the standard peripheral library are configured while the peripheral is enabled. In the HAL they leave it disabled while configuring those two registers.

ENGC can be configured in stdperiph by calling I2C_GeneralCallCmd(), NOSTRETCH by calling I2C_StretchClockCmd() and OAR2 can be set by calling I2C_OwnAddress2Config. It would be interesting to see if calling these three has the same effect as calling the HAL initialiser.