OpenHarmony標準システムHDFフレームワークのI2Cドライバ開発

メインコンテンツ

  • I2C の基礎
  • I2Cデバッグ手段
  • HDF フレームワークの I2C デバイス ドライバー

I2C の基本 ## I2C の基本 - 概念と機能

  • I2C (IIC、I2C) 集積回路バスは、シリアル データ ライン SDA とシリアル クロック ライン SCL で構成されます. I2C インターフェイス デバイスの場合、少なくとも電源とグランドが必要です。
  • I2C バスは双方向、半二重です
  • I2C バスに同時に接続する複数のホストと複数のスレーブをサポート 複数のホストが同時にバスを要求した場合、競合検出および調停メカニズムによりバス データが破壊されるのを防ぐことができます。
  • 各スレーブ デバイスには固有のアドレスがあり、スレーブ デバイスはアドレス指定 (選択とも呼ばれます) でき、選択されたスレーブ デバイスのみが通信に参加でき、1 つのマスター デバイスと 1 つのスレーブ デバイスのみが各通信に参加します。
  • マスター デバイスが通信を開始し、スレーブ デバイスが応答します: マスター デバイスとスレーブ デバイスの両方がデータを送受信でき、SCL クロックはマスター デバイスによって送信されます. エンジニアリングでは、MCU または SOC がマスター デバイスとしてよく使用されます。マスター デバイスとスレーブ デバイスの状態を交換できます。

ここに画像の説明を挿入

I2C はシリアル低速バスで、一般的な伝送速度は次のとおりです。

  • 標準モード: 最大 100kbit/s
  • 高速モード (高速モード): レート 400kbit/s
  • 高速モード + (高速モード プラス): レート 1Mbit/s
  • ハイスピードモード(高速モード):レート3.4Mbit/s

エンジニアリングで標準モードと高速モードに対応する一般的な I2C スレーブ デバイス

ここに画像の説明を挿入

  • I2C バス上のすべてのスレーブ デバイスには固有のデバイス アドレスがあり、バス上の他のデバイス アドレスと重複することはありません。
  • デバイス アドレスには、7 ビットと 10 ビットの 2 つの形式があり、共通の 7 ビット形式があります。
  • I2C マスター デバイスは、スレーブ デバイスで書き込みおよび読み取り操作を実行できます。書き込み操作と読み取り操作は、書き込みアドレスと読み取りアドレスによって区別されます。

デバイス アドレスの 7 ビット: 101000 (0x50) 書き込みアドレスの 8 ビット: デバイス アドレスの左に 1 ビット シフトし、最後のビットに 0 を追加: 1010000 (0xA0) 読み取りアドレスの 8 ビット: 左に 1 ビット シフトデバイス アドレスの最後のビットに 1 を追加: 1010001 ( 0xA1) 同じ I2C デバイスが複数のデバイス アドレスを持つ場合があり、通常はスレーブ デバイスのピンを介して構成できます. I2C インターフェイスの ROM チップ AT24C256 を例:

ここに画像の説明を挿入

  • A1 と A0 の 2 つのピンが接地されている場合、7 ビットのデバイス アドレスは 1010000 (0x50)、8 ビットの書き込みアドレスは 1010000 (0xA0)、8 ビットの読み取りアドレスは 1010001 (0xA1) になります。
  • オンチップ アドレス、オンチップ オフセット、ワード アドレス: 内部レジスタ アドレスや ROM の読み書きアドレスなど、スレーブ デバイスの内部アドレッシング。
  • 同じ I2C バスに搭載されるデバイスの数は、400pF を超えないバス上の最大静電容量によって制限されます

I2C の基礎 - プロトコル、4 つの信号の組み合わせ

  • I2C の開始信号と停止信号はマスターによって発行されます
  • S: クロック信号 SCL は High のままで、データ信号 SDA は High から Low に遷移します。
  • P: クロック信号 SCL は High のままで、データ信号 SDA は Low から High に遷移します。
  • 書き込み信号: マスターまたはスレーブ デバイスは、クロック信号 SCL が Low のときにデータ ライン SDA にデータを書き込みます。つまり、データ ラインは、SCL が Low のときにのみ High から Low に遷移できます。
  • データの読み取り: SCL が High の場合、データ ラインは安定している必要があり、スレーブ デバイスまたはマスター デバイスもこの時点で SDA からデータを読み取ります。

ここに画像の説明を挿入

I2C デバッグ手段 ## I2C デバッグ手段 - ハードウェア

  • I2C プロトコルでは、アイドル状態でバスが高レベルにあることが規定されています。スレーブ デバイスの動作電圧 VDD、SDA および SCL 電圧は 0.7VDD を下回っていません (低レベルは 0.3VDD を超えていません)。一般的なVDDは1.8V、3.3V、5Vの3仕様
  • ハイレベルは外付けプルアップ抵抗で実現しており、プルアップ抵抗が有効であることを確認する必要があります。

ここに画像の説明を挿入

I2C デバッグ手段 - ソフトウェア

  • プロセッサは複数の I2C バスをサポートしています。I2C デバイスに搭載されているバス番号を確認してください。 Hi3516DV300 は、0 ~ 7 の番号が付いた 8 つの I2C バスをサポートしています。
    ここに画像の説明を挿入

  • カーネル オプションを開きます: CONFIG_I2C_CHARDEV (make menuconfig)
    ここに画像の説明を挿入

  • バスにマウントされているすべてのデバイスを検出するには、i2c_tools ツールキットの i2c_detect コマンドを使用します。
    ここに画像の説明を挿入

HDF フレームワーク下の I2C デバイス ドライバー ## HDF フレームワーク下の I2C デバイス ドライバー - ケースの説明

  • I2C スレーブ: AT24C256、EEPROM、256Kb
  • ピン A1 と A2 の両方が接地されている場合、7 ビットのデバイス アドレスは 1010000 (0x50)、8 ビットの書き込みアドレスは 1010000 (0xA0)、8 ビットの読み取りアドレスは 1010001 (0xA1) になります。
  • 書き込み操作: ユーザー モード プログラムがワード アドレスとデータをドライバーに送信し、ドライバーがデータをデバイスのワード アドレスに書き込みます。
  • 読み出し操作:ユーザープログラムがワードアドレスをドライバーに送信し、ドライバーが指定されたデバイスのワードアドレスからデータを読み出してユーザーモードプログラムに返す

特定の操作 (書き込み操作):

  • 書き込み操作: 32KByte 空間、バイト アドレス指定によると、15 ビットのワード アドレス (上位 7 ビット + 下位 8 ビット) が必要で、ワード アドレスは 2 バイトを占有します。

ここに画像の説明を挿入

  • スタート信号、デバイスアドレス(bit0=0)、ワードアドレス(上位バイト)、ワードアドレス(下位バイト)、データ

特定の操作 (読み取り操作):

  • 読み取り操作: 32KByte 空間、バイト アドレス指定によると、15 ビットのワード アドレス (上位 7 ビット + 下位 8 ビット) が必要で、ワード アドレスは 2 バイトを占有します。

ここに画像の説明を挿入

  • スタート信号、デバイスアドレス(bit0=0)、ワードアドレス(上位バイト)、ワードアドレス(下位バイト)
  • スタート信号、デバイスアドレス(bit0=1)、受信データ
  • 読み取り操作には書き込み操作が含まれます

HDF フレームワーク下の I2C デバイス ドライバー - ユーザー モード プログラム

  • アプリケーションは、サービス名を使用してドライバーをバインドし、ドライバーとの接続を確立します。
#define SAMPLE_SERVICE_NAME "at24_service"

struct HdfIoService *serv = HdfIoServiceBind(SAMPLE_SERVICE_NAME);
if(serv == NULL){
    printf("fail to get service %s \n", SAMPLE_SERVICE_NAME);
    return HDF_FAILURE;
}

対応する hcs ファイル:

i2c_host :: host{
    hostName = "my_i2c_test";
    priority = 100;
    device_i2c :: device {
        device0 :: deviceNode {
            policy = 2;
            priority = 100;
            preload = 0;
            permission = 0664;
            moduleName = "at24_drv";
            serviceName = "at24_service";
            deviceMatchAttr = "at24_driver_attr";
        }
    }
}
  • ドライバーまたはデバイス上のユーザー モード プログラムのすべての操作は、サービスに基づいています。
  • ユーザーモードプログラムは、データをデバイスにバイト単位で書き込みます
#define I2C_RD_CMD    456
#define I2C_WR_CMD    789

static int write _data (struct HdfIoService *serv, uint16_t addr, uint8_t value)
{
    //用户态写操作
    struct HdfSBuf *data = HdfSBufObtainDefault Size();
    if(data == NULL){
        HDF_LOGE("fail to obtain sbuf data");
        ret = HDF_DEV_ERR_NO_MEMORY;
        goto out;
    }
    struct HdfSBuf *reply = HdfSBufObtainDefaultSize();
    HdfSbufWriteUint16(data, addr);
    HdfSbufWriteUint8(data, value);
    serv->dispatcher->Dispatch(&serv->object, I2C_WR_CMD, data, reply);
    HdfSbufReadString(reply);
    printf("Get reply is : %s\n", str);
}
  • 2 つのバッファ データを取得して応答する
  • ワードアドレス(15bit)とデータ(8bit)をデータバッファに書き込む
  • Dispatch を呼び出して、ワード アドレスとデータをドライバーに送信します。
  • ドライバの戻り値を読む

ユーザー状態プログラム読み取り操作:

  • ユーザーモードプログラムは、デバイスからデータをバイト単位で読み取ります
#define I2C_RD_CMD    456
#define I2C_WR_CMD    789

static int write _data (struct HdfIoService *serv, uint16_t addr, uint8_t value)
{
    //用户态读操作
    struct HdfSBuf *data = HdfSBufObtainDefault Size();
    if(data == NULL){
        HDF_LOGE("fail to obtain sbuf data");
        ret = HDF_DEV_ERR_NO_MEMORY;
        goto out;
    }
    struct HdfSBuf *reply = HdfSBufObtainDefaultSize();
    HdfSbufWriteUint16(data, addr);
    serv->dispatcher->Dispatch(&serv->object,I2C_RD_CMD, data, reply);
    HdfSbufReadUint8(reply, pval);
    HdfSbufReadString(reply);
    printf("Get reply is : data 0x%hhx, str :%s\n", *pval,  str);
}
  • 2 つのバッファ データを取得して応答する
  • ワードアドレス(15bit)をデータバッファに書き込む
  • Dispatch を呼び出してワード アドレスをドライバーに送信する
  • ドライバの戻り値を読む

HDF フレームワーク下の I2C デバイス ドライバー - ドライバー エントリ

  • ドライバーエントリー:
struct HdfDriverEntry g_SensorDriverEntry = {
    .moduleVersion = 1,
    .moduleName = "at24_drv",
    .Bind = HdfSensorDriverBind,
    .Init = HdfSensorDriverInit,
    .Release = HdfSensorDriverRelease,
}

HDF_INIT(g_SensorDriverEntry);
  • device_info.hcs は、デバイス ノードを定義します。
i2c_host :: host{
    hostName = "my_i2c_test";
    priority = 100;
    device_i2c :: device {
        device0 :: deviceNode {
            policy = 2;
            priority = 100;
            preload = 0;
            permission = 0664;
            moduleName = "at24_drv";
            serviceName = "at24_service";
            deviceMatchAttr = "at24_driver_attr";
        }
    }
}

デバイスのプライベート属性は、hcs デバイス ノードで定義されます。 deviceMatchAttr = "at24_driver_attr";

static int32_t GetAT24ConfigData(const struct DeviceResourceNode *node)
{    struct DeviceResourceIface *parser = NULL;
    const struct DeviceResourceNode *at24 = NULL;
    parser = DeviceResourceGetIfaceInstance(HDF_CONFIG_SOURCE);
    at24 = parser->GetChildNode(node, "at24Attr");
    parser->GetUint16(at24, "busId", &(tpDevice.busId), 0);
    parser->GetUint16(at24, "addr", &(tpDevice.addr), 0);
    parser->GetUint16(at24, "regLen", &(tpDevice.regLen), 0);
    return HDF_SUCCESS;
}

int32_t HdfSensorDriverInit(struct HdfDeviceObject *deviceObject)
{
    if(GetAT24ConfigData(deviceObject->property) != HDF_SUCCESS){
        HDF_LOGE("%s: get at24 config fail!", __func__);
        return HDF_FAILURE;
    }

    if(at24_init() != HDF_SUCCESS){
        HDF_LOGE("i2c at24 driver init failed!");
        return -1;
    }

    HDF_LOGD("i2c at24 driver init success.");
    return 0;
}
  • hcs 構成ファイルで定義されている属性 at24_driver_attr を解析して、デバイスのプライベート属性の値を取得します
  • i2c スレーブの初期化

デバイスのプライベート プロパティ (i2c_test_config.hcs)

root {
    match_attr = "at24_driver_attr";
    at24Attr {    //节点名字 at24Attr
        busId = 5;    //总线编号    5
        addr = 0x50;    //设备地址    0x50
        regLen = 2;        //地址宽度    2字节
    }
}

グローバル構成ファイル (device_info.hcs)

i2c_host :: host{
    hostName = "my_i2c_test";
    priority = 100;
    device_i2c :: device {
        device0 :: deviceNode {
            policy = 2;
            priority = 100;
            preload = 0;
            permission = 0664;
            moduleName = "at24_drv";
            serviceName = "at24_service";
            deviceMatchAttr = "at24_driver_attr";
        }
    }
}

HDF フレームワークでの I2C デバイス ドライバー - デバイスの初期化

  • デバイスの初期化
static int32_t at24_init(void)
{
    tpDevice.i2cHandle = i2cOpen(tpDevice.busId);
    return HDF_SUCCESS;
}
機能分類 インターフェイス名 説明
I2C コントローラ管理インターフェイス I2c開く I2C コントローラーをオンにする
I2c閉じる I2C コントローラーをオフにする
i2c メッセージ転送インターフェース I2c転送 カスタム輸送

HDF フレームワーク下の I2C デバイス ドライバー - ドライブ ディスパッチ

int32_t HdfSensorDriverDispatch(struct HdfDeviceIoClient *client, int id, struct HdfSBuf *data, struct HdfSBuf *reply)
{
    uint16_t addr = 0;
    uint8_t value = 0;

    if(id == I2C_WR_CMD){
        HdfSbufReadUint16(data, &addr);
        HdfSbufReadUint8(data, &value);
        TpI2cWriteReg(&tpDevice, addr, &value, 1);
        HdfSbufWriteString(reply, "write success");
    }
    else if(id == I2C_RD_CMD){
       HdfSbufReadUint16(data, &addr);
        TpI2cWriteReg(&tpDevice, addr, &value, 1);
        HdfSbufWriteUint8(reply, value);
        HdfSbufWriteString(reply, "read success");
    }
}

書き込みデータ:

  • 2バイトのワードアドレスを読む
  • ワードアドレスに書き込む読み出しデータ
  • 書き込み操作を実行します。パラメーター 1 は、1 バイトのデータを書き込むことを意味します。
  • ユーザープログラムへの戻り値

読み取りデータ:

  • 2バイトのワードアドレスを読む
  • 読み取り操作を実行します。パラメーター 1 は、1 バイトのデータを読み取ることを意味します。
  • ユーザープログラムへの戻り値

HDF フレームワーク下の I2C デバイス ドライバー - ドライバーの読み取りと書き込み

struct TpI2cDevice{
    uint16_t busId;
    uint16_t addr;
    uint16_t regLen;
    DevHandle i2cHandle;
}

struct I2cMsg{
    uin16_t addr;    //i2c 设备地址
    uintt8_t *buf;    //缓存区
    uint16_t len;        //数据传输长度
    uint16_t flags;        //传输模式 flags,区分读写。
}

static struct TpI2cDevice tpDevice;

static inline int TpI2cReadReg(struct TpI2cDevice *tpDevice, uint16_t regAddr, uint8_t *regData, uint32_t dataLen)
{
    return TpI2cReadWrite()tpDevice, regAddr, regData, dataLen, 1);
}

static inline int TpI2cWriteReg(struct TpI2cDevice *tpDevice, uint16_t regAddr, uint8_t *regData, uint32_t dataLen)
{
    return TpI2cReadWrite()tpDevice, regAddr, regData, dataLen, 0);
}

static int TpI2cReadWrite(struct TpI2cDevice *tpDevice, uint16_t regAddr, uint8_t *regData, uint32_t dataLen, uint8_t flaag)
{
    int index = 0;
    unsigned char regBuf[2] = {0};
    struct I2cMsg msgs[2] = {0};

    if(tpDevice->regLen == 1){
        regBuf[index++] = regAddr & 0xFF;
    }
    else {
        regBuf[index++] = (regAddr >> 8 ) & 0xFF;
        regBuf[index++] = regAddr & 0xFF;
    }

    msgs[0].addr = tpDevice->addr;
    msgs[0].flags = 0;
    msgs[0].len = tpDevice->regLen;
    msgs[0].buf = regBuf;

    msgs[1].addr = tpDevice->addr;
    msgs[1].flags = (flag == 1) ? I2C_FLAG_READ : 0;
    msgs[1].len = dataLen;
    msgs[1].buf = regData;

    if(I2cTransfer(tpDevice->i2cHandle, msgs, 2) != 2)
        return HDF_FAILURE;

    return HDF_SUCCESS;
}
  • バス番号: busId = 5
  • デバイスアドレス: addr = 0x50
  • アドレス幅:2バイト

読み取り操作:

  • 1 は読み取りフラグです。
  • デバイスアドレスの最下位ビットは 1 です

書き込み操作:

  • 0 は書き込みフラグです。
  • デバイスアドレスの最下位ビットが0

その中で、パラメータの説明:

  • regAddr と regBuf は 2 バイトのワード アドレスを格納します。
  • dataLen は、読み取りおよび書き込みデータのバイト長を示します
  • 読み取りおよび書き込み操作のワードアドレスは、スレーブデバイスにデータとして書き込まれます
  • regData は読み取りデータと書き込みデータを格納します
  • フラグは読み取り操作と書き込み操作を区別します
  • I2cTransfer の戻り値は、正常に送信された i2cMsg パケットの数を示します

要約する

  • I2C の基本: 概念と特徴、4 つのアドレス (デバイス アドレス、読み取りアドレス、書き込みアドレス、ワード アドレス)、波形 (開始、終了、データ送信、データ受信)
  • I2C デバッグ手段: 電圧、プルアップ抵抗、/dev/i2c-x、i2c-tools
  • HDF フレームワーク I2C ドライバー: AT24C256 チップは、バイト アドレッシング モードに従って読み取りおよび書き込みを行います (ページ 64 バイト アドレッシング、連続読み取りおよび書き込みに従って)。

おすすめ

転載: blog.csdn.net/qq_37596943/article/details/128580735