Desarrollo del controlador I2C del marco HDF del sistema estándar OpenHarmony

contenido principal

  • Conceptos básicos de I2C
  • Medios de depuración I2C
  • Controlador de dispositivo I2C bajo marco HDF

Conceptos básicos de I2C ## Conceptos básicos de I2C - Conceptos y características

  • El bus de circuito integrado I2C (IIC, I2C) consta de una línea de datos en serie SDA y una línea de reloj en serie SCL Para un dispositivo de interfaz I2C, se requieren al menos alimentación y conexión a tierra;
  • El bus I2C es bidireccional, semidúplex
  • Admite que varios hosts y varios esclavos se conecten a un bus I2C al mismo tiempo. Cuando varios hosts solicitan el bus al mismo tiempo, se puede evitar que los datos del bus se destruyan a través del mecanismo de arbitraje y detección de conflictos.
  • Cada dispositivo esclavo tiene una dirección única, el dispositivo esclavo puede ser direccionado (también conocido como seleccionado), solo el dispositivo esclavo seleccionado puede participar en la comunicación, y solo un dispositivo maestro y un dispositivo esclavo participan en cada comunicación
  • El dispositivo maestro inicia una comunicación y el dispositivo esclavo responde: tanto los dispositivos maestros como los esclavos pueden enviar y recibir datos, y el reloj SCL es enviado por el dispositivo maestro. En ingeniería, MCU o SOC se usa a menudo como dispositivo maestro, y el estado de los dispositivos maestro y esclavo se puede intercambiar.

inserte la descripción de la imagen aquí

I2C es un bus serial de baja velocidad, y las velocidades de transmisión comunes son las siguientes:

  • Modo estándar: hasta 100kbit/s
  • Modo rápido (modo rápido): tarifa 400kbit/s
  • Modo rápido + (modo rápido plus): velocidad de 1 Mbit/s
  • Modo de alta velocidad (modo de alta velocidad): tasa de 3,4 Mbit/s

Dispositivos esclavos I2C comunes compatibles con el modo estándar y el modo rápido en ingeniería

inserte la descripción de la imagen aquí

  • Todos los dispositivos esclavos en un bus I2C tienen una dirección de dispositivo única, que no se puede repetir con otras direcciones de dispositivos en el bus;
  • La dirección del dispositivo tiene dos formatos, 7 bits y 10 bits, y el formato común de 7 bits
  • El dispositivo maestro I2C puede realizar operaciones de escritura y lectura en el dispositivo esclavo, y la operación de escritura y la operación de lectura se distinguen por la dirección de escritura y la dirección de lectura.

7 bits de dirección del dispositivo: 101000 (0x50) 8 bits de dirección de escritura: desplaza un bit a la izquierda de la dirección del dispositivo, agrega 0 al último bit: 1010000 (0xA0) 8 bits de dirección de lectura: desplaza un bit a la izquierda de la dirección del dispositivo, agregue 1 al último bit: 1010001 (0xA1) El mismo dispositivo I2C puede tener varias direcciones de dispositivo, que generalmente se pueden configurar a través de los pines del dispositivo esclavo.Tome el chip ROM AT24C256 de la interfaz I2C como un ejemplo:

inserte la descripción de la imagen aquí

  • Si los dos pines A1 y A0 están conectados a tierra, la dirección del dispositivo de 7 bits es: 1010000 (0x50), la dirección de escritura de 8 bits: 1010000 (0xA0) y la dirección de lectura de 8 bits: 1010001 (0xA1)
  • Dirección en chip, desplazamiento en chip, dirección de palabra: direccionamiento interno del dispositivo esclavo, como dirección de registro interno o dirección de lectura y escritura de ROM, etc.
  • La cantidad de dispositivos montados en el mismo bus I2C está limitada por la capacitancia máxima en el bus que no excede los 400pF

Conceptos básicos de I2C: protocolo, cuatro combinaciones de señales

  • Las señales de inicio y parada I2C son emitidas por el maestro
  • S: La señal de reloj SCL permanece alta y la señal de datos SDA cambia de alta a baja
  • P: La señal de reloj SCL permanece alta y la señal de datos SDA cambia de baja a alta
  • Señal de escritura: el dispositivo maestro o esclavo escribe datos en la línea de datos SDA cuando la señal de reloj SCL es baja, es decir, la línea de datos solo puede pasar de alta a baja cuando SCL es baja
  • Leer datos: la línea de datos debe ser estable cuando SCL es alto, y el dispositivo esclavo o maestro también leerá datos de SDA en este momento

inserte la descripción de la imagen aquí

Medios de depuración I2C ## Medios de depuración I2C - hardware

  • El protocolo I2C estipula que en estado inactivo, el bus está en un nivel alto: el voltaje de funcionamiento del dispositivo esclavo VDD, los voltajes SDA y SCL no son inferiores a 0,7 VDD (el nivel bajo no es superior a 0,3 VDD), y el Los VDD comunes son 1.8V, 3.3V, 5V tres especificaciones
  • El nivel alto se realiza mediante una resistencia pull-up externa, y es necesario asegurarse de que la resistencia pull-up sea válida

inserte la descripción de la imagen aquí

Medios de depuración I2C - Software

  • El procesador admite múltiples buses I2C, confirme el número de bus montado en el dispositivo I2C: Hi3516DV300 admite 8 buses I2C, numerados del 0 al 7
    inserte la descripción de la imagen aquí

  • Abra la opción del kernel: CONFIG_I2C_CHARDEV (hacer menuconfig)
    inserte la descripción de la imagen aquí

  • Use el comando i2c_detect en el kit de herramientas i2c_tools para detectar todos los dispositivos montados en un bus
    inserte la descripción de la imagen aquí

Controlador de dispositivo I2C bajo el marco HDF ## Controlador de dispositivo I2C bajo el marco HDF - descripción del caso

  • Esclavo I2C: AT24C256, EEPROM, 256Kb
  • Ambos pines A1 y A2 están conectados a tierra, entonces la dirección del dispositivo de 7 bits es: 1010000 (0x50), la dirección de escritura de 8 bits: 1010000 (0xA0) y la dirección de lectura de 8 bits: 1010001 (0xA1)
  • Operación de escritura: el programa de modo de usuario envía la dirección de palabra y los datos al controlador, y el controlador escribe los datos en la dirección de palabra del dispositivo
  • Operación de lectura: el programa de usuario envía la dirección de palabra al controlador, y el controlador lee los datos de la dirección de palabra del dispositivo especificado y devuelve los datos al programa de modo de usuario

Operación específica (operación de escritura):

  • Operación de escritura: espacio de 32 KByte, según el direccionamiento de bytes, requiere una dirección de palabra de 15 bits (7 bits alto + 8 bits bajo), la dirección de palabra ocupa dos bytes

inserte la descripción de la imagen aquí

  • Señal de inicio, dirección de dispositivo (bit0 = 0), dirección de palabra (byte alto), dirección de palabra (byte bajo), datos

Operación específica (operación de lectura):

  • Operación de lectura: espacio de 32 KByte, según el direccionamiento de bytes, requiere una dirección de palabra de 15 bits (7 bits alto + 8 bits bajo), la dirección de palabra ocupa dos bytes

inserte la descripción de la imagen aquí

  • Señal de inicio, dirección de dispositivo (bit0 = 0), dirección de palabra (byte alto), dirección de palabra (byte bajo)
  • Señal de inicio, dirección del dispositivo (bit0 = 1), recibir datos
  • Las operaciones de lectura incluyen operaciones de escritura

Controlador de dispositivo I2C bajo marco HDF - programa de modo de usuario

  • La aplicación vincula al controlador a través del nombre del servicio y establece una conexión con el controlador
#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;
}

Archivo hcs correspondiente:

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";
        }
    }
}
  • Todas las operaciones del programa de modo de usuario en el controlador o dispositivo se basan en el servicio
  • El programa de modo de usuario escribe datos en el dispositivo en bytes
#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);
}
  • Obtenga dos datos de búfer y responda
  • Escriba la dirección de la palabra (15 bits) y los datos (8 bits) en el búfer de datos
  • Llame a Despacho para enviar la palabra dirección y datos al conductor
  • Leer el valor de retorno del controlador

Operación de lectura del programa de estado del usuario:

  • El programa de modo de usuario lee los datos del dispositivo en bytes
#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);
}
  • Obtenga dos datos de búfer y responda
  • Escriba la dirección de la palabra (15 bits) en el búfer de datos
  • Llame a Despacho para enviar la palabra dirección al conductor
  • Leer el valor de retorno del controlador

Controlador de dispositivo I2C en el marco HDF: entrada del controlador

  • Entrada del conductor:
struct HdfDriverEntry g_SensorDriverEntry = {
    .moduleVersion = 1,
    .moduleName = "at24_drv",
    .Bind = HdfSensorDriverBind,
    .Init = HdfSensorDriverInit,
    .Release = HdfSensorDriverRelease,
}

HDF_INIT(g_SensorDriverEntry);
  • device_info.hcs define el nodo del dispositivo
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";
        }
    }
}

Un atributo privado de dispositivo se define en el nodo de dispositivo 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;
}
  • Analice el atributo at24_driver_attr definido en el archivo de configuración hcs para obtener el valor del atributo privado del dispositivo
  • Inicializar esclavo i2c

Propiedades privadas del dispositivo (i2c_test_config.hcs)

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

Archivo de configuración global (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";
        }
    }
}

Controlador de dispositivo I2C en el marco HDF: inicialización del dispositivo

  • inicialización del dispositivo
static int32_t at24_init(void)
{
    tpDevice.i2cHandle = i2cOpen(tpDevice.busId);
    return HDF_SUCCESS;
}
Clasificación funcional nombre de la interfaz describir
Interfaz de gestión del controlador I2C I2cAbrir Encienda el controlador I2C
I2cCerrar Apague el controlador I2C
interfaz de transferencia de mensajes i2c Transferencia I2c transporte personalizado

Controlador de dispositivo I2C bajo el marco HDF - Despacho de unidades

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");
    }
}

Escribir datos:

  • Leer la dirección de la palabra de dos bytes.
  • Leer los datos que se escribirán en la dirección de la palabra
  • Realizar una operación de escritura, el parámetro 1 significa escribir un byte de datos
  • devolver valor al programa de usuario

Leer datos:

  • Leer la dirección de la palabra de dos bytes.
  • Realice una operación de lectura, el parámetro 1 significa leer un byte de datos
  • devolver valor al programa de usuario

Controlador de dispositivo I2C bajo el marco HDF: controlador de lectura y escritura

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;
}
  • Número de autobús: busId = 5
  • Dirección del dispositivo: dirección = 0x50
  • Ancho de dirección: 2 bytes

Operación de lectura:

  • 1 es la bandera de lectura
  • El bit menos significativo de la dirección del dispositivo es 1

operación de escritura:

  • 0 es el indicador de escritura
  • El bit menos significativo de la dirección del dispositivo es 0

Entre ellos, la descripción del parámetro:

  • regAddr y regBuf almacenan la dirección de palabra de dos bytes
  • dataLen indica la longitud en bytes de los datos de lectura y escritura
  • La dirección de palabra para las operaciones de lectura y escritura se escribe como datos en el dispositivo esclavo
  • regData almacena datos de lectura y escritura
  • las banderas distinguen entre operaciones de lectura y escritura
  • El valor de retorno de I2cTransfer indica el número de paquetes i2cMsg enviados con éxito

Resumir

  • Conceptos básicos de I2C: conceptos y características, 4 direcciones (dirección de dispositivo, dirección de lectura, dirección de escritura, dirección de palabra), formas de onda (inicio, fin, transmisión de datos, recepción de datos)
  • Medios de depuración de I2C: voltaje, resistencia pull-up, /dev/i2c-x, i2c-tools
  • Controlador HDF framework I2C: el chip AT24C256 lee y escribe según el modo de direccionamiento de bytes (según el direccionamiento de bytes de la página 64, lectura y escritura continuas)

Supongo que te gusta

Origin blog.csdn.net/qq_37596943/article/details/128580735
Recomendado
Clasificación