Desarrollo del controlador I2C del marco HDF del sistema estándar OpenHarmony
- contenido principal
- Conceptos básicos de I2C ## Conceptos básicos de I2C - Conceptos y características
- Medios de depuración I2C ## Medios de depuración I2C - hardware
- Controlador de dispositivo I2C bajo el marco HDF ## Controlador de dispositivo I2C bajo el marco HDF - descripción del caso
-
- Controlador de dispositivo I2C bajo marco HDF - programa de modo de usuario
- Controlador de dispositivo I2C en el marco HDF: entrada del controlador
- Controlador de dispositivo I2C en el marco HDF: inicialización del dispositivo
- Controlador de dispositivo I2C bajo el marco HDF - Despacho de unidades
- Controlador de dispositivo I2C bajo el marco HDF: controlador de lectura y escritura
- Resumir
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.
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
- 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:
- 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
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
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
-
Abra la opción del kernel: CONFIG_I2C_CHARDEV (hacer menuconfig)
-
Use el comando i2c_detect en el kit de herramientas i2c_tools para detectar todos los dispositivos montados en un bus
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
- 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
- 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)