【linux】i2c使用分析&原始碼實戰

李柱明發表於2020-11-30


前言

  • 目前不涉及驅動原始碼
  • 原文

1. 裝置檢查命令

1.1 檢視I2C驅動

  • 命令:ls /sys/bus/i2c/devices 用於檢視系統上存在的 I2C 匯流排

1.2 i2c-tools

  • i2c-tools,安裝 i2c-tools 方便除錯 i2c裝置

1.2.1 I2C-detect安裝

  • 使用命令:sudo apt install i2c-tools -y 安裝 i2c-tools
  • 安裝後可以使用命令:i2cdetect、i2cdump、i2cseti2cget

1.2.2 i2cdetect 命令

  • i2cdetect
    • 用於掃描 I2C 匯流排上的裝置
  • 語法
    • i2cdetect [-y] [-a] [-q|-r] i2cbus [first last]
    • 引數
      • y:關閉互動模式,使用該引數時,不會提示警告資訊。
      • a:掃描匯流排上的所有裝置
      • q:使用SMBus的“quick write”命令進行檢測,不建議使用該引數
      • r:使用SMBus的“receive byte”命令進行檢測,不建議使用該引數
      • i2cbus:指定i2c匯流排的編號
      • first、last:掃描的地址範圍
    • 返回值
      • '-':表示該地址被檢測,但是沒有晶片應答
      • 'UU':表示該地址當前由核心驅動程式使用
      • '**':** 表示以16進製表示的裝置地址編號,如“68”
  • 例子:
    • i2cdetect -a 0
      • i2cdetect:i2cdetect命令
      • -a:匯流排上所有裝置
      • 0:標號為 0 的 I2C,即是 I2C 1。
    • 上圖中掃描出存在裝置地址為 0x1e0x68 的裝置。
    • i2cdetect -F i2cbus:查詢 i2c 匯流排的功能,引數 i2cbus 表示 i2c 匯流排(看上
    • i2cdetect -V:列印軟體的版本號
    • i2cdetect -l:檢測當前系統有幾組 i2c 匯流排

1.2.3 i2cget 命令

  • i2cget
    • 用於讀取 I2C 裝置的某個暫存器的值
  • 語法
    • i2cget [-f] [-y] i2cbus chip-address [data-address [mode]]
    • 引數
      • f:強制訪問
      • y:關閉互動模式,使用該引數時,不會提示警告資訊
      • i2cbus:指定 I2C 匯流排的編號
      • chip-address:I2C 裝置地址
      • data-address:I2C 暫存器地址
      • mode:指定讀取的大小, 可以是b, w, s或i,分別對應了位元組,字,SMBus塊, I2C塊

1.2.4 i2cset 命令

  • i2cset
    • 寫入指定 I2C 裝置的某個暫存器的值
  • 語法
    • i2cset [-f] [-y] [-m mask] [-r] i2cbus chip-address data-address [value] … [mode]
    • 引數
      • f:強制訪問
      • y:關閉互動模式,使用該引數時,不會提示警告資訊
      • m
      • r:寫入後立即回讀暫存器值,並將結果與寫入的值進行比較
      • i2cbus:指定 I2C 匯流排的編號
      • chip-address:I2C 裝置地址
      • data-address:I2C 暫存器地址
      • value:要寫入的值
      • mode:指定讀取的大小, 可以是b, w, s或i,分別對應了位元組,字,SMBus塊, I2C塊

1.2.5 i2cdump 命令

  • i2cdump
    • 讀取指定裝置的全部暫存器的值
  • 語法
    • i2cdump [-f] [-r first-last] [-y] i2cbus address [mode [bank [bankreg]]]
    • 引數
      • r:指定暫存器範圍,只能掃描從 firstlast 區域
      • f:強制訪問裝置
      • y:關閉人機互動模式
      • i2cbus:指定 I2C 匯流排的編號
      • address:指定裝置地址
      • mode:指定讀取的大小, 可以是b, w, s或i,分別對應了位元組,字,SMBus塊, I2C塊
  • 例子
    • i2cdump -V:列印軟體的版本號

2. 原始碼實戰

  • 採用MPU6050裝置進行實驗
  • 步驟:
    • 先編寫基礎的 I2C 基礎函式
    • 編寫 MPU6050 初始化函式和關閉裝置檔案函式
    • 編寫獲取 MPU6050 資料函式
    • 編寫業務函式

2.1 編寫 bsp_mpu6050.h 檔案

  • 編寫好 MPU6050 需要的巨集
  • extern 外部函式
/** @file         bsp_mpu6050.h
 *  @brief        簡要說明
 *  @details      詳細說明
 *  @author       lzm
 *  @date         2020-11-28 19:22:20
 *  @version      v1.0
 *  @copyright    Copyright By lizhuming, All Rights Reserved
 *
 **********************************************************
 *  @LOG 修改日誌:
 **********************************************************
*/
#ifndef _BSP_MPU6050_H_ 
#define _BSP_MPU6050_H_
/* 巨集 */
#define SMPLRT_DIV                                  0x19
#define CONFIG                                      0x1A
#define GYRO_CONFIG                                 0x1B
#define ACCEL_CONFIG                                0x1C
#define ACCEL_XOUT_H                                0x3B
#define ACCEL_XOUT_L                                0x3C
#define ACCEL_YOUT_H                                0x3D
#define ACCEL_YOUT_L                                0x3f
#define ACCEL_ZOUT_H                                0x3F
#define ACCEL_ZOUT_L                                0x40
#define TEMP_OUT_H                                  0x41
#define TEMP_OUT_L                                  0x42
#define GYRO_XOUT_H                                 0x43
#define GYRO_XOUT_L                                 0x44
#define GYRO_YOUT_H                                 0x45
#define GYRO_YOUT_L                                 0x46
#define GYRO_ZOUT_H                                 0x47
#define GYRO_ZOUT_L                                 0x48
#define PWR_MGMT_1                                  0x6B
#define WHO_AM_I                                    0x75
#define SlaveAddress                                0xD0
#define Address                                     0x68                  //MPU6050地址
#define I2C_RETRIES                                 0x0701
#define I2C_TIMEOUT                                 0x0702
#define I2C_SLAVE                                   0x0703       //IIC從器件的地址設定
#define I2C_BUS_MODE                                0x0780
/* 型別轉換 */
typedef unsigned char uint8_t;
/* 函式 */
uint8_t mpu6050_init(char * i2cDev);
void    mpu6050_close(void);
short   getData(unsigned char regAddr);
#endif /* #define _BSP_MPU6050_H_ */

2.2 編寫 bsp_mpu6050.c 檔案

  • bsp_mpu6050.c
  • 編寫 I2C 讀寫函式
  • 編寫 MPU6050 裝置初始化函式及關閉檔案函式
  • 編寫獲取 MPU6050 裝置暫存器資料函式

/** @file         bsp_mpu6050.c
 *  @brief        簡要說明
 *  @details      詳細說明
 *  @author       lzm
 *  @date         2020-11-28 19:20:20
 *  @version      v1.0
 *  @copyright    Copyright By lizhuming, All Rights Reserved
 *
 **********************************************************
 *  @LOG 修改日誌:
 **********************************************************
*/
/* 標頭檔案 */
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/ioctl.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<sys/select.h>
#include<sys/time.h>
#include "bsp_mpu6050.h"
/* 裝置控制程式碼 */
int i2cFd; // i2c裝置控制程式碼
/**
  * @brief  i2c 寫
  * @param  fd:i2c裝置控制程式碼
  * @param  regAddr:暫存器地址
  * @param  val:需要寫入的值
  * @retval 0:寫入成功
  * @retval -1:寫入失敗
  * @author lzm
  */
static uint8_t i2c_write(int fd, uint8_t regAddr, uint8_t val)
{
    int cnt; // 寫入失敗後,重複寫入的次數
    uint8_t data[2]; // data[0]為暫存器地址,data[1]為需要寫入的值
    data[0] = regAddr;
    data[1] = val;
    for(cnt=5; cnt>0; cnt--)
    {
        if(write(fd, data, 2) == 2)
            return 0; // 寫入成功
    }
    return -1; // 寫入失敗
}
/**
  * @brief  i2c 讀
  * @param  fd:i2c裝置控制程式碼
  * @param  regAddr:暫存器地址
  * @param  val:讀取到資料儲存的地方
  * @retval 0:讀取成功
  * @retval -1:讀取失敗
  * @author lzm
  */
static uint8_t i2c_read(int fd, uint8_t regAddr, uint8_t * val)
{
    int cnt; // 讀取失敗後,重新讀取的次數
    for(cnt=5; cnt>0; cnt--)
    {
        if(write(fd, &regAddr, 1) == 1)
        {
            if(read(fd, val, 1) == 1)
                return 0;
        }
    }
    return -1;
}
/**
  * @brief  mpu6050初始化
  * @param  i2cDev
  * @retval 1:初始化成功
  * @retval 0:初始化失敗
  * @author lzm
  */
uint8_t mpu6050_init(char * i2cDev)
{
    i2cFd = open(i2cDev, O_RDWR); // 開啟i2c裝置檔案
    if(i2cFd < 0)
    {
        printf("Can't open %s!\n", i2cDev);
        exit(1);
    }
    printf("Open %s success!", i2cDev);
    if(ioctl(i2cFd, I2C_SLAVE, Address) < 0)
    {
        printf("fail to set i2c device slave address!");
        close(i2cFd); // 關閉i2c裝置檔案
        return -1;
    }
    printf("set slave address to 0x%x success!", Address);
    i2c_write(i2cFd, PWR_MGMT_1, 0X00);
    i2c_write(i2cFd, SMPLRT_DIV, 0X07);
    i2c_write(i2cFd, CONFIG, 0X06);
    i2c_write(i2cFd, ACCEL_CONFIG, 0X01);
    return 1;
}
/**
  * @brief  關閉裝置檔案
  * @param  
  * @retval 
  * @author lzm
  */
void mpu6050_close(void)
{
    close(i2cFd);
}
/**
  * @brief  獲取資料
  * @param  regAddr:暫存器地址
  * @retval 獲取到的資料
  * @author lzm
  */
short getData(unsigned char regAddr)
{
    char chH; // 高位元組
    char chL; // 低位元組
    i2c_read(i2cFd, regAddr, &chH);
    usleep(1000);
    i2c_read(i2cFd, regAddr, &chL);
    return ((chH << 8) + chL);
}

2.3 編寫 main.c 檔案

  • 編寫業務函式

/** @file         main.c
 *  @brief        簡要說明
 *  @details      詳細說明
 *  @author       lzm
 *  @date         2020-11-28 19:18:20
 *  @version      v1.0
 *  @copyright    Copyright By lizhuming, All Rights Reserved
 *
 **********************************************************
 *  @LOG 修改日誌:
 **********************************************************
*/
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/ioctl.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<sys/select.h>
#include<sys/time.h>
#include "mpu6050/bsp_mpu6050.h"
int main(int argc, char * argv[])
{
    /* 程式開始標語 */
    printf("This is a mpu6050 test!\n");
    /* 初始化 MCPU6050 */
    mpu6050_init("/dev/i2c-0");
    sleep(1);
    /* mpu6050 應用測試 */
    while(1)
    {
        printf("get mpu6050 data!\n");
        usleep(1000 * 100);
        printf("ACCE_X:%6d\n ", getData(ACCEL_XOUT_H));
        usleep(1000 * 100);
        printf("ACCE_Y:%6d\n ", getData(ACCEL_YOUT_H));
        usleep(1000 * 100);
        printf("ACCE_Z:%6d\n ", getData(ACCEL_ZOUT_H));
        usleep(1000 * 100);
        printf("GYRO_X:%6d\n ", getData(GYRO_XOUT_H));
        usleep(1000 * 100);
        printf("GYRO_Y:%6d\n ", getData(GYRO_YOUT_H));
        usleep(1000 * 100);
        printf("GYRO_Z:%6d\n\n ", getData(GYRO_ZOUT_H));
        sleep(1);
    }
    /* 退出 mpu6050 */
    mpu6050_close();
}

相關連結

相關文章