i2c 使用
简介
aio-3399j 开发板上有 9 个片上 i2c 控制器,各个 i2c 的使用情况如下表:
本文主要描述如何在该开发板上配置 i2c。
配置 i2c 可分为两大步骤:
定义和注册 i2c 设备
定义和注册 i2c 驱动
下面以配置 gsl3680 为例。
定义和注册 i2c 设备
在注册i2c设备时,需要结构体 i2c_client 来描述 i2c 设备。然而在标准linux中,用户只需要提供相应的 i2c 设备信息,linux就会根据所提供的信息构造 i2c_client 结构体。
用户所提供的 i2c 设备信息以节点的形式写到 dts 文件中,如下所示:
kernel/arch/arm64/boot/dts/rockchip/rk3399-firefly-mini-edp.dts &i2c4 { status = okay; gsl3680: gsl3680@41 { compatible = gslx680; reg = ; screen_max_x = ; screen_max_y = ; touch-gpio = ; reset-gpio = ; }; };
定义和注册 i2c 驱动
定义 i2c 驱动
在定义 i2c 驱动之前,用户首先要定义变量 of_device_id 和 i2c_device_id 。
of_device_id 用于在驱动中调用dts文件中定义的设备信息,其定义如下所示:
static struct of_device_id gsl_ts_ids[] = { {.compatible = gslx680}, {} };
定义变量 i2c_device_id:
static const struct i2c_device_id gsl_ts_id[] = { {gslx680_i2c_name, 0}, {} }; module_device_table(i2c, gsl_ts_id);
i2c_driver 如下所示:
static struct i2c_driver gsl_ts_driver = { .driver = { .name = gslx680_i2c_name, .owner = this_module, .of_match_table = of_match_ptr(gsl_ts_ids), }, #ifndef config_has_earlysuspend //.suspend = gsl_ts_suspend, //.resume = gsl_ts_resume, #endif .probe = gsl_ts_probe, .remove = gsl_ts_remove, .id_table = gsl_ts_id, };
注:变量id_table指示该驱动所支持的设备。
注册 i2c 驱动
使用i2c_add_driver函数注册 i2c 驱动。
i2c_add_driver(&gsl_ts_driver);
在调用 i2c_add_driver 注册 i2c 驱动时,会遍历 i2c 设备,如果该驱动支持所遍历到的设备,则会调用该驱动的 probe 函数。
通过 i2c 收发数据
在注册好 i2c 驱动后,即可进行 i2c 通讯。
向从机发送信息:
int i2c_master_send(const struct i2c_client *client, const char *buf, int count) { int ret; struct i2c_adapter *adap = client->adapter; struct i2c_msg msg; msg.addr = client->addr; msg.flags = client->flags & i2c_m_ten; msg.len = count; msg.buf = (char *)buf; ret = i2c_transfer(adap, &msg, 1); /* + if everything went ok (i.e. 1 msg transmitted), return #bytes + transmitted, else error code. */ return (ret == 1) ? count : ret; }
向从机读取信息:
int i2c_master_recv(const struct i2c_client *client, char *buf, int count) { struct i2c_adapter *adap = client->adapter; struct i2c_msg msg; int ret; msg.addr = client->addr; msg.flags = client->flags & i2c_m_ten; msg.flags |= i2c_m_rd; msg.len = count; msg.buf = buf; ret = i2c_transfer(adap, &msg, 1); /* + if everything went ok (i.e. 1 msg received), return #bytes received, + else error code. */ return (ret == 1) ? count : ret; } export_symbol(i2c_master_recv);
faqs
q1: 通信失败,出现这种log:”timeout, ipd: 0x00, state: 1”该如何调试?
a1: 请检查硬件上拉是否给电。
q2: 调用i2c_transfer返回值为-6?
a2: 返回值为-6表示为nack错误,即对方设备无应答响应,这种情况一般为外设的问题,常见的有以下几种情况:
i2c地址错误,解决方法是测量i2c波形,确认是否i2c 设备地址错误;
i2c slave 设备不处于正常工作状态,比如未给电,错误的上电时序等;
时序不符合 i2c slave设备所要求也会产生nack信号。
q3: 当外设对于读时序要求中间是stop信号不是repeat start信号的时候,该如何处理?
a3: 这时需要调用两次i2c_transfer, i2c read 拆分成两次,修改如下:
static int i2c_read_bytes(struct i2c_client *client, u8 cmd, u8 *data, u8 data_len) { struct i2c_msg msgs[2]; int ret; u8 *buffer; buffer = kzalloc(data_len, gfp_kernel); if (!buffer) return -enomem;; msgs[0].addr = client->addr; msgs[0].flags = client->flags; msgs[0].len = 1; msgs[0].buf = &cmd; ret = i2c_transfer(client->adapter, msgs, 1); if (ret adapter->dev, i2c read failed\n); kfree(buffer); return ret; } msgs[1].addr = client->addr; msgs[1].flags = client->flags | i2c_m_rd; msgs[1].len = data_len; msgs[1].buf = buffer; ret = i2c_transfer(client->adapter, &msgs[1], 1); if (ret adapter->dev, i2c read failed\n); else memcpy(data, buffer, data_len); kfree(buffer); return ret; }
简述HDL中循环语句的可综合性
区块链技术真正的目的是什么
要保持活力 多与自己不喜欢的人合作
如何透过科技提升并丰富大众的生活!
IBM陈旭东:碳中和不止节能减排,科技最优解在于全局可持续
fireflyAIO-3399J主板I2C使用介绍
经典胆机线路图集TDA1543 DAC
绿米发布全新Aqara智能墙壁开关D1系列 售价139元起
华为杨涛详谈5G新发展:未来十年是5G+物联网的时代
新制造遇见新电商会带来怎样的市场机遇
Manz 亚智科技G10.5面板湿制程设备交付出货 创下了国产化的里程碑
被各大原厂所看好的MRAM存储技术的发展
pwm占空比和电压的关系 pid输出和pwm占空比怎么联系
老房子如何加装燃气热水器?卡萨帝场景化服务为用户解难题
华为P10闪存门最新消息:华为P10闪存门事件为何至今仍未平息?深度分析华为P10闪存门原因很有可能是这个
故障分析:盘点10kv高压开关经常跳闸有哪些类型和主要原因
LLaMA2上下文长度暴涨至100万tokens,只需调整1个超参数
Trax全场景数字化,驱动未来实体零售
半导体工艺竞争激烈 台积电12nm已成功拿下 4 家订单
浅析变频器外接开关控制正、反转电路