STC8H開發(七): I2C驅動MPU6050三軸加速度+三軸角速度檢測模塊


目錄

MPU-6050

MPU-6050是InvenSense生產的六軸運動跟蹤芯片, 芯片尺寸4×4×0.9mm, QFN封裝. 整合了三軸陀螺儀, 三軸加速度計, 片內溫度傳感器和數字運動處理器(DMP), 可以使用I2C接口外接三軸電子羅盤的輸入,提供完整的九軸運動融合輸出.

MPU-6050包含6個16位ADC, 3個用於陀螺儀輸出, 3個用於加速度計輸出. 用戶可以設置的陀螺儀滿量程范圍為±250,±500,±1000,±2000°/秒(dps), 可設置的加速度計滿量程范圍為±2g, ±4g, ±8g和±16g. 通信使用 400kHz的 I2C接口或 1MHz的 SPI接口(SPI僅MPU-6000可用).

關於I2C通信

在 FwLib_STC8 中, I2C 通信部分的頭文件代碼如下

typedef enum
{
    I2C_MasterCmd_Wait          = 0x00, // Wait, idle
    I2C_MasterCmd_Start         = 0x01, // START
    I2C_MasterCmd_Send          = 0x02, /* Send data. This command will generate 8 clocks on SCL, and 
                                           send I2CTXD to SDA bit by bit from MSB */
    I2C_MasterCmd_RxAck         = 0x03, /* Recive Ack. This command will generate 1 clock on SCL, and
                                           save the received bit to MSACKI(I2CMSST.1) */
    I2C_MasterCmd_Recv          = 0x04, // Recive data
    I2C_MasterCmd_TxAck         = 0x05, /* Send Ack. This command will generate 1 clock on SCL, and 
                                           write the value of MSACKO(I2CMSST.0) to SDA */
    I2C_MasterCmd_Stop          = 0x06, // STOP. This command will send STOP signal, and reset MSBUSY flag
    I2C_MasterCmd_StartSendRxAck = 0x09, // START + Send data + RxAck
    I2C_MasterCmd_SendRxAck     = 0x0A, // Send data + RxAck
    I2C_MasterCmd_RecvTxAck0    = 0x0B, // Receive data + TxAck(0)
    I2C_MasterCmd_RecvNAck      = 0x0C, // Receive data + NAck
} I2C_MasterCmd_t;

#define I2C_SendMasterCmd(__CMD__) {                                 \
                (I2CMSCR) =  (I2CMSCR) & ~(0x0F) | ((__CMD__) & 0x0F);  \
                while (!(I2CMSST & 0x40));                              \
                I2CMSST &= ~0x40;                                       \
            }

#define I2C_MasterStart()               I2C_SendMasterCmd(I2C_MasterCmd_Start)
#define I2C_MasterSendData(__DATA__)    do{I2CTXD = (__DATA__); I2C_SendMasterCmd(I2C_MasterCmd_Send);}while(0)
#define I2C_MasterRxAck()               I2C_SendMasterCmd(I2C_MasterCmd_RxAck)
#define I2C_MasterAck()                 do{I2CMSST &= ~(0x01); I2C_SendMasterCmd(I2C_MasterCmd_TxAck);}while(0)
#define I2C_MasterNAck()                do{I2CMSST |= 0x01; I2C_SendMasterCmd(I2C_MasterCmd_TxAck);}while(0)
#define I2C_MasterStop()                I2C_SendMasterCmd(I2C_MasterCmd_Stop)

對這部分的說明如下:
STC8H 對 I2C 通信涉及的功能做了硬件封裝, 通過在 I2CMSCR 中寫入命令完成I2C操作. 命令執行的結束, 通過 I2CMSST 的 B6 判斷, 命令完成時這一位會置1, 需要軟件清零, 這就是這兩行代碼的作用

while (!(I2CMSST & 0x40));                              \
I2CMSST &= ~0x40;  

所有的命令執行完都要使用這兩行進行處理.

主設備往從設備的寫操作實現如下, 可以看到在每一個數據發送后, 都要用RxAck產生一個時鍾, 讀取從設備在SDA上產生的電平, 可以用於判斷是ACK還是NAK

uint8_t I2C_Write(uint8_t devAddr, uint8_t memAddr, uint8_t *dat, uint16_t size)
{
    SFRX_ON();
    I2C_MasterStart();
    I2C_MasterSendData(devAddr & 0xFE);
    I2C_MasterRxAck();
    I2C_MasterSendData(memAddr);
    I2C_MasterRxAck();
    while(size--)
    {
        I2C_MasterSendData(*dat++);
        I2C_MasterRxAck();
    }
    I2C_MasterStop();
    SFRX_OFF();
    return HAL_OK;
}

主設備對從設備的讀操作實現如下, 可以看到在寫地址部分和寫操作是一樣的, 在讀操作部分, 每次讀取完之后, 主設備會回寫ACK或NAK響應, 告訴從設備是否要繼續給主設備發送數據.

uint8_t I2C_Read(uint8_t devAddr, uint8_t memAddr, uint8_t *buf, uint16_t size)
{
    SFRX_ON();
    I2C_MasterStart();
    I2C_MasterSendData(devAddr & 0xFE);
    I2C_MasterRxAck();
    I2C_MasterSendData(memAddr);
    I2C_MasterRxAck();
    I2C_MasterStart();
    I2C_MasterSendData(devAddr | 0x01);
    I2C_MasterRxAck();
    while(size--)
    {
        I2C_SendMasterCmd(I2C_MasterCmd_Recv);
        *buf++ = I2CRXD;
        if (size == 0)
        {
            I2C_MasterNAck();
        }
        else
        {
            I2C_MasterAck();
        }
    }
    I2C_MasterStop();
    SFRX_OFF();
    return HAL_OK;
}

I2C的響應問題

對於每一個從設備(slaver), 當它被尋址后, 都要求在接收到每一個字節后產生一個響應. 因此主設備必須產生一個額外的時鍾脈沖用於讀取從設備的響應.
在這個脈沖期間, 主設備釋放對SDA的控制, 從設備必須將SDA拉低並在時鍾的高電平期間保持住, 這代表返回了一個ACK; 如果不拉低SDA, 就代表返回了NACK.
在從設備發送完最后一個字節后, 主設備(讀取方)必須產生一個不響應位, 用以通知從設備不要再發送信息, 這樣從機就知道該將SDA釋放了, 而后主設備發出停止信號.

模塊與STC8H的接線

市面上的模塊, 一般是8個pin腳, 如果只是獲取6軸采樣和溫度, 與STC8H只需要連接4根線

    P32   -> SCL
    P33   -> SDA
    GND   -> GND
    3.3V  -> VCC

未連接的其它4個pin分別是

  • XDA和XCL: I2C主設備接口, 用於外接I2C從設備,
  • AD0: I2C從設備地址LSB
  • INT: 中斷輸出

模塊與STC8H的通信

I2C頻率最高為400KHz, STC8H的設置如下, 其中I2C功能復用選擇的是P32/P33, 如果換到其它復用腳需要相應調整

void I2C_Init(void)
{
    // 主節點模式
    I2C_SetWorkMode(I2C_WorkMode_Master);
    /**
     * I2C 時鍾 = SYSCLK / 2 / (__prescaler__ * 2 + 4)
     * MPU6050 works with i2c clock up to 400KHz
     * 
     * 44.2368 / 2 / (26 * 2 + 4) = 0.39 MHz
    */
    I2C_SetClockPrescaler(0x1A);
    // 復用口選擇
    I2C_SetPort(I2C_AlterPort_P32_P33);
    // 啟動 I2C
    I2C_SetEnabled(HAL_State_ON);
}

與MPU6050的通信可以直接使用SDK中的I2C讀寫方法, 需要注意的幾點

  1. 一次性讀出的雙字節結果, 如果直接轉uint16_t, 其高字節和低字節位置是相反的, 需要調換后再轉換
  2. 可以一次性讀出6軸+溫度數據
uint16_t swap(uint16_t num)
{
    return (num >> 8) | (num << 8);
}

void MPU6050_Write(uint8_t addr, uint8_t dat)
{
    I2C_Write(MPU6050_ADDR, addr, &dat, 1);
}

uint8_t MPU6050_Read(uint8_t addr)
{
    uint8_t ret;
    I2C_Read(MPU6050_ADDR, addr, &ret, 1);
    return ret;
}

// 讀取16位數據
uint16_t MPU6050_ReadInt(uint8_t addr)
{
    uint16_t ret;
    I2C_Read(MPU6050_ADDR, addr, (uint8_t *)&ret, 2);
    return swap(ret); // swap high/low bits for correct order
}

// 一次性讀取7個16位數據
void MPU6050_ReadAll(uint16_t *buf)
{
    uint8_t i;
    I2C_Read(MPU6050_ADDR, MPU6050_REG_ACCEL_XOUT_H, (uint8_t *)buf, 14);
    for (i = 0; i < 7; i++)
    {
        *(buf + i) = swap(*(buf + i));
    }
}

主要的寄存器設置

幾個可能會用到的寄存器說明

電源管理寄存器 0x6B 和 0x6C

這兩個寄存器控制了MPU6050的工作模式: 睡眠模式(Sleep), 節電模式(Cycle)和正常工作模式

  • 睡眠模式: 0x6B 的第六位置1開啟, 睡眠模式下可以通信, 但是所有的檢測轉換都是停止的, 結果讀取的值都是0
  • 節電模式: 是一種睡眠和正常交替的模式, MPU6050每隔一段時間做一次檢測轉換, 其余時間都處在睡眠狀態
    • 進入節電模式, 需要將sleep位置0, cycle位置1, 禁止溫度采樣位置1, STBY_XG, STBY_YG, STBY_ZG置1
    • 節點模式下的采樣頻率可以設置為 1.25Hz, 5Hz, 20Hz, 40Hz
  • 正常模式: 正常模式按設置進行正常的檢測和轉換

在 0x6C 中, 還可以指定六個檢測軸中的哪些軸暫停檢測

采樣速率寄存器 0x19

這個寄存器用於設置陀螺儀輸出速率分頻系數, 采樣速率通過陀螺儀輸出速率除以此寄存器的值產生:

采樣速率 = 陀螺儀輸出速率 / (1 + SMPLRT_DIV)

其中, 禁用DLPF(低通過濾)時陀螺儀輸出速率為8KHz(DLPF_CFG = 0 or 7), 當啟用DLPF時為1KHz.

配置寄存器 0x1A

這個寄存器用於設置外部幀同步(FSYNC)引腳采樣和數字陀螺儀和加速度計的低通濾波器(DLPF). 連接到FSYNC引腳的外部信號可以通過配置EXT_SYNC_SET來采樣, FSYNC引腳的信號變化被鎖存, 以便捕捉信號, FSYNC信號將以寄存器0x19中定義的采樣速率進行采樣。采樣后鎖存器將復位到當前的FSYNC信號狀態.

  • 位3,4,5: 設置FSYNC位的位置
  • 位0,1,2: 取值0 - 6, 設置DLPF, 數字越大延遲越大.

角速度檢測(陀螺儀)配置寄存器 0x1B

這個寄存器用於設置角速度三軸的自檢和滿刻度范圍

加速度檢測配置寄存器 0x1C

這個寄存器用於設置加速度三軸的自檢和滿刻度范圍

模塊的中斷類型及設置

中斷功能通過中斷配置寄存器進行配置。 可配置的項目包括INT引腳配置,中斷鎖存和清除方法以及中斷觸發器。 可觸發中斷的項目有:

  1. 時鍾發生器鎖定到新的參考振盪器(用於切換時鍾源)
  2. 可以讀取新數據(來自FIFO和數據寄存器)
  3. 加速度計事件中斷
  4. MPU-6050 沒有收到輔助傳感器的確認I2C總線

中斷狀態可以從中斷狀態寄存器讀取。

檢測數據

MPU6050在檢測過程中, 根據采樣速率不斷輸出六軸加溫度的檢測值, 如果設置的滿刻度范圍較大, 則測量輸出的數字較小, 靈敏度較低, 要增加靈敏度可以調小滿刻度范圍. 相比較 ADXL345, MPU6050更適合運動中的物體檢測, 能檢測到更准確的運動中物體姿態變化, 同樣的, 如果沒有接入電子羅盤(磁強計), 只能得到俯仰角和橫滾角數據, 不能得到航向角數據.

演示代碼

演示代碼以100ms的時間間隔, 不斷讀取7個檢測數據並通過UART1串口輸出, 接線正確的話, 可以通過串口軟件觀察到模塊運動帶來的檢測值變化


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM