簡體   English   中英

使用STM32F303的MPU6050響應時間

[英]MPU6050 response time with STM32F303

我正在嘗試使用STM32F303微控制器構建自平衡機器人,並且為了進行位置測量,我正在使用MPU6050。 使用I2C傳輸協議發送和接收數據時。 我以前有使用STM32F303的經驗,也熟悉I2C協議,並且通過查閱MPU6050STM32F303的數據表和一些在線資源,我已經使用ST電子提供的標准外圍設備庫構建了代碼。 問題是我正在獲取讀數,但是響應時間太慢。 平均而言,它要消耗最大2-3ms但在這里它會消耗比400ms的更多,我完全無法弄清楚。 由於時間是該項目的關鍵,因此我需要有關此問題的指南。 我使用Atollic作為我的IDE,代碼文件如下Main.c

    #include "stm32f30x.h"
    #include "stm32f30x_gpio.h"
    #include "stm32f30x_i2c.h"
    #include "stm32f30x_rcc.h"
    #include "stm32f30x_spi.h"
    #include "stm32f30x_misc.h"
    #include "math.h"

    #include "mpu6050.h"

    #define I2C_SDA_PIN     GPIO_Pin_9  // PORTB
    #define I2C_SCL_PIN     GPIO_Pin_8  // PORTB
    #define degconvert 57.2957786 // There are like 57 degrees in a radian.

    MPU6050_errorstatus err;
    static int i = 0;
    float gyro_xdata;
    float gyro_ydata;
    float gyro_zdata;
    float accel_xdata;
    float accel_ydata;
    float accel_zdata;
    float roll;
    float pitch;

    void initializeGPIO();
    void initializeI2C();

    int main(void)
    {
        initializeGPIO();
        initializeI2C();

        err = MPU6050_Initialization();

        // Getting Raw Values
        err = MPU6050_Get_Gyro_Data(&gyro_xdata, &gyro_ydata, &gyro_zdata);
        err = MPU6050_Get_Accel_Data(&accel_xdata, &accel_ydata, &accel_zdata);

        // Calculate Pitch and Roll
        roll = atan2f(accel_ydata, accel_zdata) * degconvert;
        pitch = atan2f(-accel_xdata, accel_zdata) * degconvert;

        while(1)
        {
            // Collect Raw Data from Sensor
            err = MPU6050_Get_Gyro_Data(&gyro_xdata, &gyro_ydata, &gyro_zdata);
            err = MPU6050_Get_Accel_Data(&accel_xdata, &accel_ydata, &accel_zdata);

            // The next two lines calculate the orientation of the Accelerometer relative
            // to the earth and convert the output of atan2 from Radians to Degrees
            // We will use this data to correct any Cumulative errors in the Orientation
            // that the gyroscope develops.
            roll = atan2f(accel_ydata, accel_zdata) * degconvert;
            pitch = atan2f(-accel_xdata, accel_zdata) * degconvert;
        }
    }

MPU6050.c

    #include "mpu6050.h"

    uint32_t MPU6050_Timeout = MPU6050_FLAG_TIMEOUT;
    MPU6050_dataStruct dataStruct;

    /* @brief Sets up MPU6050 internal clock and sensors sensitivity rate
    *  This function must be called before using the sensor!
    *
    * @retval @MPU6050_errorstatus
    */
    MPU6050_errorstatus MPU6050_Initialization(void){

        MPU6050_errorstatus errorstatus;

        /* Set Clock source for the chip
         * possible values @pwr_mngt_1
         */
        errorstatus = MPU6050_Set_Clock(MPU6050_PLL_X_GYRO);
        if(errorstatus != 0) return errorstatus;

        /* Set Gyroscope's full scope range
         * possible values @gyro_scale_range
         */
        errorstatus = MPU6050_Gyro_Set_Range(MPU6050_GYRO_250);
        if(errorstatus != 0) return errorstatus;

        /* Set Accelerometer's full scope range
         * possible values @accel_scale_range
         */
        errorstatus = MPU6050_Accel_Set_Range(MPU6050_ACCEL_2g);
        if(errorstatus != 0) return errorstatus;

        return MPU6050_NO_ERROR;
    }


    /* @brief Test if chip is visible on I2C line
     * Reads the WHO_AM_I register
     *
     * @retval @MPU6050_errorstatus
     */
    MPU6050_errorstatus MPU6050_Test(void){

        MPU6050_errorstatus errorstatus;
        uint8_t tmp;

        errorstatus = MPU6050_Read((MPU6050_ADDRESS & 0x7f) << 1, WHO_AM_I, &tmp, 1);
        if(tmp != (uint8_t)0x68){
            return errorstatus;
        }
        return MPU6050_NO_ERROR;
    }

    /* @brief Get Gyroscope's full scale range
     * Reads the GYRO_CONFIG register and returns the value of gyro's range
     *
     * @retval tmp - value of gyro's range
     */
    uint8_t MPU6050_Gyro_Get_Range(void){

        MPU6050_errorstatus errorstatus;
        uint8_t tmp;

        errorstatus = MPU6050_Read((MPU6050_ADDRESS & 0x7f) << 1, GYRO_CONFIG, &tmp, 1);
        if(errorstatus != 0){
            return 1;
        }
        else return tmp;

    }

    /* @brief Set Gyroscope's full scale range
     * @param range - check @MPU6050_Gyro_Range
     * @retval @MPU6050_errorstatus
     */
    MPU6050_errorstatus MPU6050_Gyro_Set_Range(MPU6050_Gyro_Range range){

        MPU6050_errorstatus errorstatus;
        dataStruct.gyroMul = range;

        errorstatus = MPU6050_Write((MPU6050_ADDRESS & 0x7f) << 1, GYRO_CONFIG, &range);
        if(errorstatus != 0){
            return errorstatus;
        }
        else return MPU6050_NO_ERROR;

    }

    /* @brief Get Accelerometer full scale range
     * Reads the Accel_CONFIG register and returns the value of accelerometer's range
     *
     * @retval tmp - value of accelerometer's range
     */
    uint8_t MPU6050_Accel_Get_Range(void){

        MPU6050_errorstatus errorstatus;
        uint8_t tmp;

        errorstatus = MPU6050_Read((MPU6050_ADDRESS & 0x7f) << 1, ACCEL_CONFIG, &tmp, 1);
        if(errorstatus != 0){
            return 1;
        }
        else return tmp;

    }

    /* @brief Set Accelerometer full scale range
     * @param range - check @MPU6050_Accel_Range
     * @retval @MPU6050_errorstatus
     */
    MPU6050_errorstatus MPU6050_Accel_Set_Range(MPU6050_Accel_Range range){

        MPU6050_errorstatus errorstatus;
        dataStruct.accelMul = range;

        errorstatus = MPU6050_Write((MPU6050_ADDRESS & 0x7f) << 1, ACCEL_CONFIG, &range);
        if(errorstatus != 0){
            return errorstatus;
        }
        else return MPU6050_NO_ERROR;

    }

    /* @brief Set MPU6050 clock source
     * @param clock - check @MPU6050_Clock_Select
     * @retval @MPU6050_errorstatus
     */
    MPU6050_errorstatus  MPU6050_Set_Clock(MPU6050_Clock_Select clock){

        MPU6050_errorstatus errorstatus;

        errorstatus = MPU6050_Write((MPU6050_ADDRESS & 0x7f) << 1, PWR_MGMT_1, &clock);
        if(errorstatus != 0){
            return errorstatus;
        }
        else return MPU6050_NO_ERROR;

    }

    /* @brief Read MPU6050 temperature
     * @retval temp_celsius - temperature in degrees celsius
     */
    int16_t MPU6050_Get_Temperature(void){

        MPU6050_errorstatus errorstatus;
        uint8_t temp_low;
        uint8_t temp_high;
        int16_t temp;
        int16_t temp_celsius;

        errorstatus = MPU6050_Read((MPU6050_ADDRESS & 0x7f) << 1, TEMP_OUT_L, &temp_low, 1);
        if(errorstatus != 0){
            return 1;
        }

        errorstatus = MPU6050_Read((MPU6050_ADDRESS & 0x7f) << 1, TEMP_OUT_H, &temp_high, 1);
        if(errorstatus != 0){
            return 1;
        }

        temp = (uint16_t)(temp_high << 8 | temp_low);

        temp_celsius = temp/340 + 36;
        return temp_celsius;

    }

    /* @brief Get Gyroscope X,Y,Z raw data
     *
     * @param X - sensor roll on X axis
     * @param Y - sensor pitch on Y axis
     * @param Z - sensor jaw on Z axis
     *
     * @retval @MPU6050_errorstatus
     */
    MPU6050_errorstatus MPU6050_Get_Gyro_Data_Raw(int16_t* X, int16_t* Y, int16_t* Z){

        MPU6050_errorstatus errorstatus;

        uint8_t xlow, xhigh, ylow, yhigh, zlow, zhigh;

        errorstatus = MPU6050_Read((MPU6050_ADDRESS & 0x7f) << 1, GYRO_XOUT_L, &xlow, 1);
        if(errorstatus != 0){
            return errorstatus;
        }

        errorstatus = MPU6050_Read((MPU6050_ADDRESS & 0x7f) << 1, GYRO_XOUT_H, &xhigh, 1);
        if(errorstatus != 0){
            return errorstatus;
        }

        errorstatus = MPU6050_Read((MPU6050_ADDRESS & 0x7f) << 1, GYRO_YOUT_L, &ylow, 1);
        if(errorstatus != 0){
            return errorstatus;
        }

        errorstatus = MPU6050_Read((MPU6050_ADDRESS & 0x7f) << 1, GYRO_YOUT_H, &yhigh, 1);
        if(errorstatus != 0){
            return errorstatus;
        }

        errorstatus = MPU6050_Read((MPU6050_ADDRESS & 0x7f) << 1, GYRO_ZOUT_L, &zlow, 1);
        if(errorstatus != 0){
            return errorstatus;
        }

        errorstatus = MPU6050_Read((MPU6050_ADDRESS & 0x7f) << 1, GYRO_ZOUT_H, &zhigh, 1);
        if(errorstatus != 0){
            return errorstatus;
        }

        *X = (int16_t)(xhigh << 8 | xlow);
        *Y = (int16_t)(yhigh << 8 | ylow);
        *Z = (int16_t)(zhigh << 8 | zlow);

        return MPU6050_NO_ERROR;
    }

    /* @brief Get Accelerometer X,Y,Z raw data
     *
     * @param X - sensor accel on X axis
     * @param Y - sensor accel on Y axis
     * @param Z - sensor accel on Z axis
     *
     * @retval @MPU6050_errorstatus
     */
    MPU6050_errorstatus MPU6050_Get_Accel_Data_Raw(int16_t* X, int16_t* Y, int16_t* Z){

        MPU6050_errorstatus errorstatus;

        uint8_t xlow, xhigh, ylow, yhigh, zlow, zhigh;

        errorstatus = MPU6050_Read((MPU6050_ADDRESS & 0x7f) << 1, ACCEL_XOUT_L, &xlow, 1);
        if(errorstatus != 0){
            return errorstatus;
        }

        errorstatus = MPU6050_Read((MPU6050_ADDRESS & 0x7f) << 1, ACCEL_XOUT_H, &xhigh, 1);
        if(errorstatus != 0){
            return errorstatus;
        }

        errorstatus = MPU6050_Read((MPU6050_ADDRESS & 0x7f) << 1, ACCEL_YOUT_L, &ylow, 1);
        if(errorstatus != 0){
            return errorstatus;
        }

        errorstatus = MPU6050_Read((MPU6050_ADDRESS & 0x7f) << 1, ACCEL_YOUT_H, &yhigh, 1);
        if(errorstatus != 0){
            return errorstatus;
        }

        errorstatus = MPU6050_Read((MPU6050_ADDRESS & 0x7f) << 1, ACCEL_ZOUT_L, &zlow, 1);
        if(errorstatus != 0){
            return errorstatus;
        }

        errorstatus = MPU6050_Read((MPU6050_ADDRESS & 0x7f) << 1, ACCEL_ZOUT_H, &zhigh, 1);
        if(errorstatus != 0){
            return errorstatus;
        }

        *X = (int16_t)(xhigh << 8 | xlow);
        *Y = (int16_t)(yhigh << 8 | ylow);
        *Z = (int16_t)(zhigh << 8 | zlow);

        return MPU6050_NO_ERROR;
    }

    /* @brief Get Gyroscope X,Y,Z calculated data
     *
     * @param X - sensor roll on X axis
     * @param Y - sensor pitch on Y axis
     * @param Z - sensor jaw on Z axis
     *
     * @retval @MPU6050_errorstatus
     */
    MPU6050_errorstatus MPU6050_Get_Gyro_Data(float* X, float* Y, float* Z){

        MPU6050_errorstatus errorstatus;

        float mult;
        int16_t gyro_x, gyro_y, gyro_z;

        errorstatus = MPU6050_Get_Gyro_Data_Raw(&gyro_x, &gyro_y, &gyro_z);

        if(dataStruct.gyroMul == MPU6050_GYRO_250){
            mult = (float)(1/MPU6050_GYRO_RANGE_250);
        }
        else if(dataStruct.gyroMul == MPU6050_GYRO_500){
            mult = (float)(1/MPU6050_GYRO_RANGE_500);
        }
        else if(dataStruct.gyroMul == MPU6050_GYRO_1000){
            mult = (float)(1/MPU6050_GYRO_RANGE_1000);
        }
        else mult = (float)(1/MPU6050_GYRO_RANGE_2000);

        *X = (float)(gyro_x*mult);
        *Y = (float)(gyro_y*mult);
        *Z = (float)(gyro_z*mult);

        return MPU6050_NO_ERROR;
    }

    /* @brief Get Accelerometer X,Y,Z calculated data
     *
     * @param X - sensor accel on X axis
     * @param Y - sensor accel on Y axis
     * @param Z - sensor accel on Z axis
     *
     * @retval @MPU6050_errorstatus
     */
    MPU6050_errorstatus MPU6050_Get_Accel_Data(float* X, float* Y, float* Z){

        MPU6050_errorstatus errorstatus;

        float mult;
        int16_t accel_x, accel_y, accel_z;

        errorstatus = MPU6050_Get_Accel_Data_Raw(&accel_x, &accel_y, &accel_z);

        if(dataStruct.accelMul == MPU6050_ACCEL_2g){
            mult = (float)(1/MPU6050_ACCEL_RANGE_2g);
        }
        else if(dataStruct.accelMul == MPU6050_ACCEL_2g){
            mult = (float)(1/MPU6050_ACCEL_RANGE_4g);
        }
        else if(dataStruct.accelMul == MPU6050_ACCEL_2g){
            mult = (float)(1/MPU6050_ACCEL_RANGE_8g);
        }
        else mult = (float)(1/MPU6050_ACCEL_RANGE_16g);

        *X = (float)(accel_x*mult);
        *Y = (float)(accel_y*mult);
        *Z = (float)(accel_z*mult);

        return MPU6050_NO_ERROR;
    }

    /* @brief Reads bytes from MPU6050
     *
     * @param SlaveAddr - Slave I2C address
     * @param RegAddr - register address
     * @param pBuffer - buffer to write to
     * @ param NumByteToRead - number of bytes to read
     *
     * @retval @MPU6050_errorstatus
     */
    MPU6050_errorstatus MPU6050_Read(uint8_t SlaveAddr, uint8_t RegAddr, uint8_t* pBuffer, uint16_t NumByteToRead)
    {

        /* Test if SDA line busy */
        MPU6050_Timeout = MPU6050_LONG_TIMEOUT;
        while(I2C_GetFlagStatus(I2C1, I2C_FLAG_BUSY) != RESET)
        {
            if((MPU6050_Timeout--) == 0) return MPU6050_I2C_ERROR;
        }

        I2C_TransferHandling(I2C1, SlaveAddr, 1, I2C_SoftEnd_Mode, I2C_Generate_Start_Write);

        MPU6050_Timeout = MPU6050_LONG_TIMEOUT;
        while(I2C_GetFlagStatus(I2C1, I2C_FLAG_TXIS) == RESET)
        {
            if((MPU6050_Timeout--) == 0) return MPU6050_I2C_ERROR;
        }

        if(NumByteToRead>1)
        RegAddr |= 0x80;

        I2C_SendData(I2C1, (uint8_t)RegAddr);

        MPU6050_Timeout = MPU6050_LONG_TIMEOUT;
        while(I2C_GetFlagStatus(I2C1, I2C_FLAG_TC) == RESET)
        {
            if((MPU6050_Timeout--) == 0) return MPU6050_I2C_TX_ERROR;
        }

        I2C_TransferHandling(I2C1, SlaveAddr, NumByteToRead, I2C_AutoEnd_Mode, I2C_Generate_Start_Read);

        while (NumByteToRead)
        {
            MPU6050_Timeout = MPU6050_LONG_TIMEOUT;
            while(I2C_GetFlagStatus(I2C1, I2C_FLAG_RXNE) == RESET)
            {
                if((MPU6050_Timeout--) == 0) return MPU6050_I2C_RX_ERROR;
            }

            *pBuffer = I2C_ReceiveData(I2C1);
            pBuffer++;

            NumByteToRead--;
        }

        MPU6050_Timeout = MPU6050_LONG_TIMEOUT;
        while(I2C_GetFlagStatus(I2C1, I2C_FLAG_STOPF) == RESET)
        {
          if((MPU6050_Timeout--) == 0) return MPU6050_I2C_ERROR;
        }

        I2C_ClearFlag(I2C1, I2C_FLAG_STOPF);

        return MPU6050_NO_ERROR;
    }

    /* @brief Writes bytes to MPU6050
     *
     * @param SlaveAddr - Slave I2C address
     * @param RegAddr - register address
     * @param pBuffer - buffer to write from
     *
     * @retval @MPU6050_errorstatus
     */
    MPU6050_errorstatus MPU6050_Write(uint8_t SlaveAddr, uint8_t RegAddr, uint8_t* pBuffer)
    {

        /* Test if SDA line busy */
        MPU6050_Timeout = MPU6050_LONG_TIMEOUT;
        while(I2C_GetFlagStatus(I2C1, I2C_FLAG_BUSY) != RESET)
        {
            if((MPU6050_Timeout--) == 0) return MPU6050_I2C_ERROR;
        }

        I2C_TransferHandling(I2C1, SlaveAddr, 1, I2C_Reload_Mode, I2C_Generate_Start_Write);

        MPU6050_Timeout = MPU6050_LONG_TIMEOUT;
        while(I2C_GetFlagStatus(I2C1, I2C_FLAG_TXIS) == RESET)
        {
            if((MPU6050_Timeout--) == 0) return MPU6050_I2C_ERROR;
        }

        I2C_SendData(I2C1, (uint8_t) RegAddr);

        MPU6050_Timeout = MPU6050_LONG_TIMEOUT;
        while(I2C_GetFlagStatus(I2C1, I2C_FLAG_TCR) == RESET)
        {
            if((MPU6050_Timeout--) == 0) return MPU6050_I2C_ERROR;
        }

        I2C_TransferHandling(I2C1, SlaveAddr, 1, I2C_AutoEnd_Mode, I2C_No_StartStop);

        MPU6050_Timeout = MPU6050_LONG_TIMEOUT;
        while(I2C_GetFlagStatus(I2C1, I2C_FLAG_TXIS) == RESET)
        {
            if((MPU6050_Timeout--) == 0) return MPU6050_I2C_ERROR;
        }

        I2C_SendData(I2C1, *pBuffer);

        MPU6050_Timeout = MPU6050_LONG_TIMEOUT;
        while(I2C_GetFlagStatus(I2C1, I2C_FLAG_STOPF) == RESET)
        {
          if((MPU6050_Timeout--) == 0) return MPU6050_I2C_ERROR;
        }

        I2C_ClearFlag(I2C1, I2C_FLAG_STOPF);

        return MPU6050_NO_ERROR;
    }

我找到了我問題的答案。 當我嘗試對其進行調試時,發現I2C協議的時鍾頻率非常低,大約為1 Khz ,因此應在4 Khz的范圍內才能正常運行和提高性能。 我試圖查閱數據表,但發現問題出在I2C模塊的時序寄存器。 在I2C時序寄存器中,后4位專用於時鍾頻率預分頻器,在我的情況下,它被設置為滿,因此頻率被最大程度地分頻。 因此在I2C模塊的初始化段中

    void  initializeI2C(){

    RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1, ENABLE);

    I2C_InitTypeDef I2C_InitStructure;
    I2C_InitStructure.I2C_Ack = I2C_Ack_Enable;
    I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;
    I2C_InitStructure.I2C_OwnAddress1 = 0;
    I2C_InitStructure.I2C_AnalogFilter = I2C_AnalogFilter_Enable;
    I2C_InitStructure.I2C_DigitalFilter = 0x00;
    I2C_InitStructure.I2C_Mode = I2C_Mode_I2C;
    I2C_InitStructure.I2C_Timing = 0xF062121F;
    I2C_Init(I2C1, &I2C_InitStructure);

    I2C_Cmd(I2C1, ENABLE);

}

我將I2C_Timing寄存器的值更改為0x0062121F,並且恢復了I2C的時鍾頻率,因此我能夠獲得將近4 ms的最大采樣時間

I2C_InitStructure.I2C_Timing = 0x0062121F;

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM