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 バイト アドレッシング、連続読み取りおよび書き込みに従って)。