RK3568平台入门到精通系列讲解之UBOOT开发篇(I2C操作)

这篇具有很好参考价值的文章主要介绍了RK3568平台入门到精通系列讲解之UBOOT开发篇(I2C操作)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

rk3568 uboot,4. UBOOT开发篇,嵌入式硬件,linux,android,arm开发,驱动开发,Powered by 金山文档

一、简介

uboot中i2c读写有2种方式,一种使用uboot驱动模型,通过宏CONFIG_DM_I2C定义,另一种是传统方式,通过宏CONFIG_SYS_I2C定义。

二、uboot中使用I2C命令进行读写

在uboot命令行中,通过定义宏CONFIG_CMD_I2C,可以打开i2c cmd 子系统。输入i2c查看 usage。


i2c bus - 查看当前总线
i2c dev [dev] - 设置总线号
i2c md chip address[.0, .1, .2] [# ofobjects] - i2c设备读
i2c mw chip address[.0, .1, .2] value[count] - i2c设备写
 
=> i2c bus
Bus 2: i2c@48060000  (active 2)
  58: generic_58, offset len 1, flags 0
  6b: generic_6b, offset len 1, flags 0
  6a: generic_6a, offset len 1, flags 0
=> i2c dev 2
Setting bus to 2
=> i2c md 6b d0.1 1
00d0: 14   .
=> i2c mw 6b d0.1 0x15 1
=>

举例:读取i2c地址为0x20的外设芯片,从第0个寄存器开始读,共读16个寄存器。

u-boot> i2c md 0x20  0  16
md   ---- i2c读
0x20 ---- i2c外设的地址,每个i2c外设都有一个独立的地址,一般是外设芯片出厂时就已经定好。
0  ---- 从外设芯片的第0号寄存器开始读
16  ---- 总共读16个寄存器
 
u-boot> i2c mw 0x20 0x05 0x0A
mw   ---- i2c写
0x20 ---- i2c外设的地址,每个i2c外设都有一个独立的地址,一般是外设芯片出厂时就已经定好。
0x05 ---- 寄存器地址
0x0A ---- 发送数据

三、传统方式SYS_I2C

3.1 设置总线号

int i2c_set_bus_num(unsigned int bus);

3.2 读/写

int i2c_read(uint8_t chip, unsigned intaddr, int alen, uint8_t *buffer, int len);
int i2c_write(uint8_t chip, unsigned intaddr, int alen, uint8_t *buffer, int len);
uint8_t i2c_reg_read(uint8_t addr, uint8_treg);
void i2c_reg_write(uint8_t addr, uint8_treg, uint8_t val);
函数实现见: drivers/i2c/i2c_core.c
接口说明:
int i2c_read(u_int8_t chip,   //芯片的i2c地址(7位地址),不包含读写位
         u_int32_t addr,  //芯片内的读写地址,比如寄存器地址
         int alen,        //alen为1说明寄存器地址是8bit的,为2则是16bit的
         u_int8_t *buf,   //保存读到的数据
         int len)         //数据长度
 
int i2c_write(u_int8_t chip,   //芯片的i2c地址(7位地址),不包含读写位
          u_int32_t  addr, //芯片内的读写地址,比如寄存器地址
          int alen,        //alen为1说明寄存器地址是8bit的,为2则是16bit的
          u_int8_t *buffer, //保存要写的数据
          int len);        //数据长度
返回值: 0 on success, not 0 on failure

3.3 示例

传统方式示例-8位寄存器

#define DP_I2C_ADDR    0x5C //0xB8
//返回值:读取到的寄存器值 addr:设备i2c slave地址 offset:要读取设备的哪一个寄存器
unsigned char ReadI2C_Byte(unsigned charaddr, unsigned char offset)
{
   unsigned char value;
   i2c_read(addr, offset, sizeof(offset), &value, sizeof(value));
    
   return value;
}
 
//addr:设备i2c slave地址 offset:要写入设备的哪一个寄存器,d:要写入的值
unsigned char WriteI2C_Byte(unsigned charaddr, unsigned char offset, unsigned char d)
{
    
   unsigned char value = d;
   i2c_write(addr, offset, sizeof(offset), &value, sizeof(value));
    
   return value;
}
 
static int it6251_device_i2c(void)
{   
   int tmp;
    
   i2c_set_bus_num(2); // /dev/i2c-2
   i2c_init(100000, 0);
   i2c_set_bus_speed(100000);
   tmp = i2c_probe(DP_I2C_ADDR);
   //printf("james debug it6251 probe=%d\n", tmp);
}
 
void IT6251_PowerOn(void)
{
unsigned char ucValue;
ucValue = ReadI2C_Byte(DP_I2C_ADDR, 0x0D);
....
WriteI2C_Byte(DP_I2C_ADDR, 0x05, 0x00);
}
 
void init_it6251(void)
{
   it6251_device_i2c();
 
   //printf("reg 0x0=0x%x\n",ReadI2C_Byte(DP_I2C_ADDR, 0));
   IT6251_PowerOn();
}

四、DM驱动模型DM_I2C

4.1 根据uclass id和总线编号,获取总线udevice

int uclass_get_device_by_seq(enum uclass_idid, int seq, struct udevice **devp);

4.2 获取设备udevice

int i2c_get_chip(struct udevice *bus, uintchip_addr, uint offset_len, struct udevice **devp);

4.3 设置设备寄存器地址长度

int i2c_set_chip_offset_len(struct udevice*dev, uint offset_len);

4.4 读/写

int dm_i2c_read(struct udevice *dev, uintoffset, uint8_t *buffer, int len);
int dm_i2c_write(struct udevice *dev, uintoffset, const uint8_t *buffer,int len);

函数实现见: drivers/i2c/i2c-uclass.c

DM_I2C对SYS_I2C的兼容

DM_I2C在drivers/i2c/i2c-uclass-compat.c中,通过定义宏CONFIG_DM_I2C_COMPAT,实现了SYS_I2C的兼容,从SYS_I2C切换到DM_I2C时,不用修改原来SYS_I2C的i2c读写流程代码。

4.5 示例

DM方式示例-RK3568 Android11验证通过,I2C设备以HYM8563为例:

(示例包含i2c操作与gpio操作)

1. dts文件(dts文件写在kernel中)

&i2c3 {
       clock-frequency = <400000>;
       status = "okay";
 
       hym8563: hym8563@51 {
                compatible ="haoyu,hym8563";
                reg = <0x51>;
                status = "okay";
                enable-gpios = <&gpio0RK_PB6 GPIO_ACTIVE_HIGH>;  //此IO仅仅演示GPIO操作
                reset-gpios = <&gpio0RK_PB5 GPIO_ACTIVE_LOW>; //此IO仅仅演示GPIO操作
       };
};

2. hym8563.h

#ifndef _HYM8563_H_
#define _HYM8563_H_
#include <dm/device.h>
#include <asm/gpio.h>
struct hym8563 {
         structudevice *dev;
         structgpio_desc enable_gpio;
         structgpio_desc reset_gpio;
};
//8位寄存器,8位值
int hym8563_i2c_write_u8(struct hym8563*hym8563, u8 reg, u8 val);
int hym8563_i2c_read_u8(struct hym8563*hym8563, u8 reg, u8 *val);
//16位寄存器,32位值
int hym8563_i2c_write_u32(struct hym8563*hym8563, u16 reg, u32 val);
int hym8563_i2c_read_u32(struct hym8563*hym8563, u16 reg, u32 *val);
#endif

3. hym8563.c

#include <common.h>
#include <i2c.h>
#include <errno.h>
#include <dm.h>
#include <dm/uclass.h>
#include <dm/uclass-id.h>
#include "hym8563.h"
/********************************************
&i2c3 {
         clock-frequency= <400000>;
         status= "okay";
 
         hym8563:hym8563@51 {
                   compatible= "haoyu,hym8563";
                   reg= <0x51>;
                   status= "okay";
                   enable-gpios= <&gpio0 RK_PB6 GPIO_ACTIVE_HIGH>;
                   reset-gpios= <&gpio0 RK_PB5 GPIO_ACTIVE_LOW>;
         };
};
********************************************/
//8位寄存器,8位值
int hym8563_i2c_write_u8(struct hym8563*hym8563, u8 reg, u8 val)
{
         structdm_i2c_chip *chip = dev_get_parent_platdata(hym8563->dev);
         structi2c_msg msg;
         u8buf[] = {
                   (reg>> 0) & 0xff,
                   (val>> 0) & 0xff
         };
         u8ret;
 
         msg.addr= chip->chip_addr;
         msg.flags= 0;
         msg.len= sizeof(buf);
         msg.buf= buf;
 
         ret= dm_i2c_xfer(hym8563->dev, &msg, 1);
         if(ret) {
                   dev_err(hym8563->dev,"Could not execute transfer: %d\n", ret);
                   returnret;
         }
 
         return0;
}
 
int hym8563_i2c_read_u8(struct hym8563*hym8563, u8 reg, u8 *val)
{
         structdm_i2c_chip *chip = dev_get_parent_platdata(hym8563->dev);
         u8data;
         structi2c_msg msg[] = {
                   {
                            .addr= chip->chip_addr,
                            .flags= 0,
                            .buf= (u8 *)&reg,
                            .len= 1,
                   },{
                            .addr= chip->chip_addr,
                            .flags= I2C_M_RD,
                            .buf= (u8 *)&data,
                            .len= 1,
                   }
         };
         intret;
 
         ret= dm_i2c_xfer(hym8563->dev, msg, 2);
         if(ret) {
                   dev_err(hym8563->dev,"Could not execute transfer: %d\n", ret);
                   returnret;
         }
 
         *val= data;
 
         return0;
}
 
//16位寄存器,32位值
int hym8563_i2c_write_u32(struct hym8563*hym8563, u16 reg, u32 val)
{
         structdm_i2c_chip *chip = dev_get_parent_platdata(hym8563->dev);
         structi2c_msg msg;
         u8buf[] = {
                   (reg>> 0) & 0xff, (reg >> 8) & 0xff,
                   (val>> 0) & 0xff, (val >> 8) & 0xff,
                   (val>> 16) & 0xff, (val >> 24) & 0xff
         };
         intret;
 
         msg.addr= chip->chip_addr;
         msg.flags= 0;
         msg.len= sizeof(buf);
         msg.buf= buf;
 
         ret= dm_i2c_xfer(hym8563->dev, &msg, 1);
         if(ret) {
                   dev_err(hym8563->dev,"Could not execute transfer: %d\n", ret);
                   returnret;
         }
 
         return0;
}
 
int hym8563_i2c_read_u32(struct hym8563*hym8563, u16 reg, u32 *val)
{
         structdm_i2c_chip *chip = dev_get_parent_platdata(hym8563->dev);
         u32data;
         structi2c_msg msg[] = {
                   {
                            .addr= chip->chip_addr,
                            .flags= 0,
                            .buf= (u8 *)&reg,
                            .len= 2,
                   },{
                            .addr= chip->chip_addr,
                            .flags= I2C_M_RD,
                            .buf= (u8 *)&data,
                            .len= 4,
                   }
         };
         intret;
 
         ret= dm_i2c_xfer(hym8563->dev, msg, 2);
         if(ret) {
                   dev_err(hym8563->dev,"Could not execute transfer: %d\n", ret);
                   returnret;
         }
 
         *val= data;
 
         return0;
}
 
static int hym8563_probe(struct udevice*dev)
{
         structhym8563 *hym8563 = dev_get_priv(dev);
         hym8563->dev= dev;
         intret, i;
         u8val;
         
         //从dts中解析gpio
         ret= gpio_request_by_name(dev, "enable-gpios", 0,
                                        &hym8563->enable_gpio, GPIOD_IS_OUT);
         if(ret && ret != -ENOENT) {
                   dev_err(dev,"Cannot get enable GPIO: %d\n", ret);
                   returnret;
         }
 
         //从dts中解析gpio
         ret= gpio_request_by_name(dev, "reset-gpios", 0,
                                        &hym8563->reset_gpio, GPIOD_IS_OUT);
         if(ret) {
                   dev_err(dev,"Cannot get reset GPIO: %d\n", ret);
                   returnret;
         }
 
         //设置io口电平
         if(dm_gpio_is_valid(&hym8563->enable_gpio))
         {
                   dm_gpio_set_value(&hym8563->enable_gpio,1);
         }
         dm_gpio_set_value(&hym8563->reset_gpio,1);
 
         //i2c读写
         for(i= 0; i < 5; i++)
         {
                   hym8563_i2c_read_u8(hym8563,0x02, &val); //0x02寄存器为rtc芯片的秒计数
                   printf("reg:0x00=0x%x\n",val);
                   mdelay(1000);
         }
         return0;
}
 
static const struct udevice_idhym8563_of_match[] = {
         {.compatible = "haoyu,hym8563" },
         {}
};
 
U_BOOT_DRIVER(hym8563) = {
         .name= "hym8563",
         .id= UCLASS_I2C_GENERIC,
         .of_match= hym8563_of_match,
         .probe= hym8563_probe,
         .bind= dm_scan_fdt_dev,
         .priv_auto_alloc_size= sizeof(struct hym8563),
};

4. main.c

#include <common.h>
#include <command.h>
#include <part.h>
#include <dm.h>
#include <asm/gpio.h> 
int do_demo(cmd_tbl_t *cmdtp, int flag, intargc, char * const argv[])
{
    
       struct udevice *udev;
       int ret;
       //uboot DM中,用户必须主动调用框架接口发起 probe,才会进入到hym8563_probe函数
       ret = uclass_get_device_by_name(UCLASS_I2C_GENERIC,"hym8563@51",&udev);
 
       return ret;
}
 
U_BOOT_CMD(
   demo,     2,      0,     do_demo,
   "make demo running.",
   NULL
);

注意:U-Boot 通过 DM 管理所有的设备和驱动,它和 kernel 的 device-driver 模型非常类似。kernel 初始化时使用 initcall 机制把所有已经 bind 过的 device-driver 进行 probe,但是 U-Boot 没有这样的机制。如果要让 U-Boot 中某个 driver 执行 probe,用户必须主动调用框架接口发起 probe。文章来源地址https://www.toymoban.com/news/detail-785146.html

// 常用:
int uclass_get_device(enum uclass_id id,int index, struct udevice **devp);
int uclass_get_device_by_name(enumuclass_id id, const char *name,struct udevice **devp);
// 不常用:
int uclass_get_device_by_seq(enum uclass_idid, int seq, struct udevice **devp);
int uclass_get_device_by_of_offset(enumuclass_id id, int node, struct udevice **devp);
int uclass_get_device_by_ofnode(enumuclass_id id, ofnode node, struct udevice **devp);
int uclass_get_device_by_phandle_id(enumuclass_id id, int phandle_id, struct udevice **devp);
int uclass_get_device_by_phandle(enumuclass_id id, struct udevice *parent, struct udevice **devp);
int uclass_get_device_by_driver(enum uclass_idid, const struct driver *drv, struct udevice **devp);
int uclass_get_device_tail(struct udevice*dev, int ret, struct udevice **devp);
......

到了这里,关于RK3568平台入门到精通系列讲解之UBOOT开发篇(I2C操作)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • RK3568平台开发系列讲解(驱动基础篇)自动创建设备节点

    🚀返回专栏总目录 沉淀、分享、成长,让自己和他人都能有所收获!😄 📢自动创建设备节点分为两个步骤: 步骤一:使用 class_create 函数创建一个类。 步骤二:使用 device_create 函数在我们创建的类下面创建一个设备。 Linux 驱动实验中,当我们通过 insmod 命令加载模块后,

    2023年04月12日
    浏览(50)
  • RK3568平台开发系列讲解(音视频篇)RTMP 推流

    🚀返回专栏总目录 沉淀、分享、成长,让自己和他人都能有所收获!😄 📢目前常见的视频监控和视频直播都是使用了 RTMP 、 RTSP 、 HLS 、 MPEG-DASH 、 WebRTC 流媒体传输协议等。 RTSP (Real-Time Streaming Protocol):实时流传输协议,用于控制媒体服务器上的实时流传输,支持音频

    2024年02月05日
    浏览(55)
  • RK3568平台开发系列讲解(Linux系统篇)Linux 内核打印

    🚀返回总目录 在终端使用 dmseg 命令可以获取内核打印信息,该命令的具体使用方法如下所示: 首先在串口终端使用 “ dmseg ”命令,可以看见相应的内核打印信息已经加载了出来,如下图所示: 然后使用以下组合命令查找 nfs 相关的打印信息

    2024年02月02日
    浏览(40)
  • RK3568平台开发系列讲解(调试篇)Linux 性能调试工具汇总

    🚀返回专栏总目录 沉淀、分享、成长,让自己和他人都能有所收获!😄 📢本篇将汇总介绍 Linux 调试优化的工具。 我们来看 Linux 的性能工具。首先还是要推荐下面这张图,也就是 Brendan Gregg 整理的性能工具谱图。我在专栏中多次提到过,你肯定也已经参考过。 这张图从

    2023年04月15日
    浏览(62)
  • RK3568平台开发系列讲解(网络篇)网络包的接收过程

    🚀返回专栏总目录 沉淀、分享、成长,让自己和他人都能有所收获!😄 📢本篇我们一起来梳理下网络包的接收过程。 硬件网卡接收到网络包之后,通过 DMA 技术,将网络包放入 Ring Buffer ; 硬件网卡通过中断通知 CPU 新的网络包的到来; 网卡驱动程序会注册中断处理函数

    2024年02月06日
    浏览(39)
  • RK3568平台开发系列讲解(Linux系统篇)文件系统的读写

    🚀返回专栏总目录 沉淀、分享、成长,让自己和他人都能有所收获!😄 📢本篇我们一起学习 read 和 write 调用过程。 rea

    2023年04月19日
    浏览(51)
  • RK3568平台开发系列讲解(Linux系统篇)Linux 目录结构介绍

    🚀返回专栏总目录 沉淀、分享、成长,让自己和他人都能有所收获!😄 📢本篇我们从目录管理入手,会更直观的理解 linux 的目录结构。 Linux 整个文件系统是以“ / ”目录开始,根目录是最顶层,前面讲根目录和家目录概念的时候已经提到了。它下边包括众多的目录,这些

    2023年04月13日
    浏览(46)
  • RK3568平台开发系列讲解(音视频篇)H264 的编码结构

    🚀返回专栏总目录 沉淀、分享、成长,让自己和他人都能有所收获!😄 📢视频编码的码流结构其实就是指视频经过编码之后得到的二进制数据是怎么组织的,换句话说,就是编码后的码流我们怎么将一帧帧编码后的图像数据分离出来,以及在二进制码流数据中,哪一块数

    2024年02月09日
    浏览(45)
  • RK3568平台开发系列讲解(Linux系统篇)Linux 内部的全景图

    沉淀、分享、成长,让自己和他人都能有所收获!😄 📢多核系统底层驱动提供了 cpu 的 Plugin / Unplug 接口,可以实现动态调整 cpu 使用运行。 在单核系统中,只有一个 CPU ,只要系统处于开机状态就不能关闭这一唯一的 CPU 。随着 SMP(Symmetrical Multi-Processing) 的出现,多核系

    2023年04月16日
    浏览(46)
  • RK3568平台开发系列讲解(视频篇)摄像头采集视频的相关配置

    🚀返回专栏总目录 沉淀、分享、成长,让自己和他人都能有所收获!😄 📢 Android 平台的摄像头的采集核心部分都是在 Native 层构建的,所以这就会涉及 JNI 层的一些转换操作。 要想使用 Android 平台提供的摄像头,必须在配置文件里添加权限要求。

    2023年04月08日
    浏览(46)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包