【linux开发-驱动】-I2C驱动相关

一、I2C基础知识

1、I2C 使用两条线在主控制器和从机之间进行数据通信。一条是 SCL(串行时钟线),另外一条是 SDA(串行数据线)。

2、I2C 是支持多从机的,也就是一个 I2C 控制器下可以挂多个 I2C 从设备,这些不同的 I2C从设备有不同的器件地址。

3、I2C时序相关

①起始位:通信起始标志,SCL 为高电平的时候,SDA 出现下降沿就表示为起始位。

②停止位:停止 I2C 通信的标志位,SCL 位高电平的时候,SDA出现上升沿就表示为停止位。

③数据传输:数据传输时,要保证SCL在高电平期间,SDA 上的数据变化只能在 SCL 低电平期间发生。

④应答信号:当 I2C 主机发送完 8 位数据以后会将 SDA 设置为输入状态,等待 I2C 从机应答,也就是等待 I2C 从机告诉主机它接收到数据了。应答信号是由从机发出的,主机需要提供应答信号所
需的时钟,主机发送完 8 位数据以后紧跟着的一个时钟信号就是给应答信号使用的。从机通过将SDA 拉低来表示发出应答信号,表示通信成功,否则表示通信失败。

主机通过 I2C 总线与从机之间进行通信包括两个操作:写和读。

二、I2C设备驱动编写

在ST提供的源码中,内核源码 arch/arm/boot/dts/stm32mp151.dtsi 设备树文件中找到
STM32MP1 的 I2C 控制器节点。已经提供了I2C适配器驱动程序。

1、若不使用设备树

在未使用设备树的时候需要在 BSP 里面使用 i2c_board_info 结构体来描述一个具体的 I2C 设备

struct i2c_board_info {
    char type[I2C_NAME_SIZE];//设置I2C设备名字
    unsigned short flags;
    unsigned short addr; //设置I2C设备地址
    const char *dev_name;
    void    *platform_data;
    struct device_node *of_node;
    struct fwnode_handle *fwnode;
    const struct property_entry *properties;
    const struct resource *resources;
    unsigned int num_resources;
    int irq;
};

2、使用设备树

使用设备树的时候 I2C 设备信息通过创建相应的节点就行了

例如,使用I2C5挂载一个传感器

&i2c5 {
 pinctrl-names = "default", "sleep";
 pinctrl-0 = <&i2c5_pins_a>;
 pinctrl-1 = <&i2c5_pins_sleep_a>;
 status = "okay";

 ap3216c@1e {
 compatible = " alientek,xxx";
 reg = <0x1e>;
 };
 };

修改设备树,打开 stm32mp15-pinctrl.dtsi

找到如下内容:

i2c5_pins_a: i2c5-0 {
          pins {
              pinmux = <STM32_PINMUX('A', 11, AF4)>, /* I2C5_SCL */
                   <STM32_PINMUX('A', 12, AF4)>; /* I2C5_SDA */
              bias-disable;
              drive-open-drain;
              slew-rate = <0>;
          };
      };
  
      i2c5_pins_sleep_a: i2c5-1 {
          pins {
              pinmux = <STM32_PINMUX('A', 11, ANALOG)>, /* I2C5_SCL */
                   <STM32_PINMUX('A', 12, ANALOG)>; /* I2C5_SDA */
  
          };
      };

定义了 I2C5 接口的两个 pinmux 配置分别为:i2c5_pins_a 和i2c5_pins_sleep_a。第一个默认的状态下使用,第二个是在 sleep 状态下使用。

打开 stm32mp157d-atk.dts 文件,向 i2c5 节点中添加“ap3216c@1e”子节点,

&i2c5 {
     pinctrl-names = "default", "sleep";
     pinctrl-0 = <&i2c5_pins_a>;
     pinctrl-1 = <&i2c5_pins_sleep_a>;
     status = "okay";

     ap3216c@1e { //le是ap3216c的器件地址
         compatible = "alientek,ap3216c";
         reg = <0x1e>;//reg 属性也是设置 ap3216c 器件地址的,因此 reg 设置为 0x1e。
     };
 };

修改完成后,使用make dtbs编译一下,编译完成后,会在/sys/bus/i2c/devices 目录下看到一个名为“0-001e”的子目录,进入0-001e 目录,可以看到“name”文件,name 文件保存着此设备名字。

最后,编写驱动程序。

//①定义结构体,包括设备,设备号,设备节点等
//②从设备读取多个寄存器数据
//③向多个寄存器写入数据
//④读取指定寄存器值,读取一个寄存器
//⑤指定寄存器写入指定的值,写一个寄存器
//⑥读取数据
void ap3216c_readdata(struct ap3216c_dev *dev)
 {
     unsigned char i =0;
     unsigned char buf[6];

     /* 循环读取所有传感器数据 */
     for(i = 0; i < 6; i++) {
         buf[i] = ap3216c_read_reg(dev, AP3216C_IRDATALOW + i);
     }

     if(buf[0] & 0X80) /* IR_OF 位为 1,则数据无效 */
     dev->ir = 0;
     else
    /* 读取 IR 传感器的数据 */
     dev->ir = ((unsigned short)buf[1] << 2) | (buf[0] & 0X03);

     dev->als = ((unsigned short)buf[3] << 8) | buf[2];

     if(buf[4] & 0x40) /* IR_OF 位为 1,则数据无效 */
         dev->ps = 0;
        else
    /* 读取 PS 传感器的数据 */
     dev->ps = ((unsigned short)(buf[5] & 0X3F) << 4) | (buf[4] &
    0X0F);
    }
//从设备读取数据
static ssize_t ap3216c_read(struct file *filp, char __user *buf,
size_t cnt, loff_t *off)
 {
     short data[3];
     long err = 0;

     struct cdev *cdev = filp->f_path.dentry->d_inode->i_cdev;
     struct ap3216c_dev *dev = container_of(cdev, struct ap3216c_dev,cdev);

     ap3216c_readdata(dev);

     data[0] = dev->ir;
     data[1] = dev->als;
     data[2] = dev->ps;
     err = copy_to_user(buf, data, sizeof(data));
     return 0;
 }
//关闭/释放设备
//写i2c 驱动的 probe 函数,当驱动与设备匹配以后此函数就会执行
//i2c 驱动的 remove 函数,移除 i2c 驱动的时候此函数会执行
//驱动入口和出口函数
/* module_i2c_driver(ap3216c_driver) */

 module_init(ap3216c_init);
 module_exit(ap3216c_exit);
 MODULE_LICENSE("GPL");
 MODULE_AUTHOR("ALIENTEK");
 MODULE_INFO(intree, "Y");

 编写测试程序,

编译出来拷贝到对应的目录中,重启开发板,加载驱动模块,看读取的数据变化。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值