Linux驱动之I2C,设备树读取AP3216C模块环境光数据

Linux驱动之I2C,设备树读取AP3216C环境光模块

开发板:STM32MP157A
板上系统:Linux 5.10.6

一、查看原理图

img

img

img

//上述可以得知:AP3216C的从机地址为0x1E,使用i2c通信,时钟线和数据线分别接到PF15,PF14引脚。

查看I2C1的内存映射:

img

查看PF14,PF15的i2c复用:

img

二、写设备树节点

1、画原理图

img

2、查看Linux设备树官方文档

路径:
linux-5.10.61/Documentation/devicetree/bindings/i2c

img

仿照案例寻找我们需要的信息:
利用我们找到的I2C1对应的映射地址去寻找:

img

根据我们在手册中找到的PF14,PF15复用为AF5来寻找我们的I2C复用配置:’

img

3、写自己的设备树节点

(一)、将i2c1节点引用到我们的设备树中

(二)、配置引脚信息

(三)、修改配置

img

三、写驱动

img

我这里只是用到了AP3216c模块的光照强度测量功能,为了方便我快速验证成果,我这里并没有用到此模块的中断功能。

头文件:AP3216c.h

#ifndef __AP3216C_H__
#define __AP3216C_H__

#include <linux/module.h>
#include <linux/init.h>
#include <linux/i2c.h>
#include <linux/cdev.h>
#include <linux/fs.h>
#include <linux/device.h>
#include <linux/errno.h>
#include <linux/err.h>



#define AP3216_I2C_ADDRESS 0x1e // 从机地址: 0x1e

#endif

驱动程序:ap3216_dev.c

#include "../include/AP3216c.h"
#include <linux/uaccess.h>

#define NAME "ap3216c_dev"

// 全局变量定义
struct cdev *ap3216_cdev = NULL;    // 字符设备结构体指针
struct class *cls = NULL;           // 设备类指针
struct device *dev = NULL;          // 设备指针
struct i2c_client *gclient = NULL;  // I2C客户端指针

// 函数:ap3216c_write_cmd
// 作用:向AP3216C传输写命令
// 参数:
//   reg - 要写入的寄存器地址
//   cmd - 要写入的数据
// 返回值:成功返回0,失败返回错误码
int ap3216c_write_cmd(u8 reg, u8 cmd)
{
    int ret = 0;
    u8 buf[2] = {reg, cmd};  // 数据缓冲区

    struct i2c_msg msg = {
        .addr = AP3216_I2C_ADDRESS,  // AP3216C的I2C地址
        .flags = 0,                  // 标志为0表示写操作
        .len = 2,                    // 缓冲区长度
        .buf = buf,                  // 缓冲区指针
    };

    ret = i2c_transfer(gclient->adapter, &msg, 1);  // 传输I2C消息
    if (ret != 1)
    {
        printk("i2c_transfer_write error\n");
        return -EAGAIN;  // 返回错误码
    }
    return 0;
}

// 函数:ap3216c_read_data
// 作用:从AP3216C读取数据
// 参数:
//   reg - 要读取的寄存器地址
//   data - 读取到的数据缓冲区
//   size - 要读取的数据长度
// 返回值:成功返回0,失败返回错误码
int ap3216c_read_data(u8 reg, unsigned char *data, size_t size)
{
    int ret = 0;

    struct i2c_msg msg[2] = {
        [0] = {
            .addr = AP3216_I2C_ADDRESS,  // AP3216C的I2C地址
            .flags = 0,                  // 标志为0表示写操作
            .len = 1,                    // 寄存器地址长度
            .buf = &reg,                 // 寄存器地址指针
        },
        [1] = {
            .addr = AP3216_I2C_ADDRESS,  // AP3216C的I2C地址
            .flags = 1,                  // 标志为1表示读操作
            .len = size,                 // 要读取的数据长度
            .buf = data,                 // 数据缓冲区指针
        }
    };

    // 先发送寄存器地址,再读取数据
    ret = i2c_transfer(gclient->adapter, msg, 2);
    if (ret != 2)
    {
        printk("i2c_transfer_read error\n");
        return -EAGAIN;  // 返回错误码
    }

    return 0;
}

// 函数:ap3126_ALS_init
// 作用:初始化AP3216C的ALS(Ambient Light Sensor)功能
// 返回值:成功返回0,失败返回错误码
int ap3126_ALS_init(void)
{
    int ret = 0;
    short a = 0;
    char buf_data[2] = {0};

    // 启用ALS功能
    ret = ap3216c_write_cmd(0x00, 0x01);
    if (ret)
        return -1;

    // 读取ALS数据低位
    ret = ap3216c_read_data(0x0c, &buf_data[0], sizeof(char));
    if (ret)
        return -1;

    // 读取ALS数据高位
    ret = ap3216c_read_data(0x0D, &buf_data[1], sizeof(char));
    if (ret)
        return -1;

    // 组合高低位数据
    a = (buf_data[1] << 8) + buf_data[0];
    printk("光照强度为:%d\n", a);

    return 0;
}

// 函数:ap3216_open
// 作用:设备打开函数
// 返回值:成功返回0
int ap3216_open(struct inode *node, struct file *file)
{
    printk("%s,%s,%d\n", __FILE__, __func__, __LINE__);
    return 0;
}

// 函数:ap3216_release
// 作用:设备关闭函数
// 返回值:成功返回0
int ap3216_release(struct inode *node, struct file *file)
{
    printk("%s,%s,%d\n", __FILE__, __func__, __LINE__);
    return 0;
}

// 函数:ap3216_ioctl
// 作用:设备IO控制函数
// 参数:
//   cmd - 控制命令
//   arg - 命令参数
// 返回值:成功返回0
long ap3216_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
    printk("%s,%s,%d\n", __FILE__, __func__, __LINE__);
    return 0;
}

// 函数:ap3216_write
// 作用:设备写函数
// 返回值:成功返回0
ssize_t ap3216_write(struct file *file, const char *ubuf, size_t size, loff_t *offs)
{
    printk("%s,%s,%d\n", __FILE__, __func__, __LINE__);
    return 0;
}

// 函数:ap3216_read
// 作用:设备读函数
// 返回值:成功返回读取的数据长度,失败返回错误码
ssize_t ap3216_read(struct file *file, char *ubuf, size_t size, loff_t *offs)
{
    int ret = 0;
    short a = 0;
    char buf_data[2] = {0};
    printk("%s,%s,%d\n", __FILE__, __func__, __LINE__);

    // 读取ALS数据低位
    ret = ap3216c_read_data(0x0c, &buf_data[0], sizeof(char));
    if (ret)
        return -1;

    // 读取ALS数据高位
    ret = ap3216c_read_data(0x0D, &buf_data[1], sizeof(char));
    if (ret)
        return -1;

    // 组合高低位数据
    a = (buf_data[1] << 8) + buf_data[0];
    printk("光照强度为:%d\n", a);

    // 将数据复制到用户空间
    ret = copy_to_user(ubuf, buf_data, size);
    if (ret != 0)
    {
        printk("copy_to_user error \n");
        return -1;
    }

    return 0;
}

// 文件操作结构体
struct file_operations fops = {
    .open = ap3216_open,
    .release = ap3216_release,
    .unlocked_ioctl = ap3216_ioctl,
    .write = ap3216_write,
    .read = ap3216_read,
};

// 函数:ap3216_probe
// 作用:设备探测函数
// 返回值:成功返回0,失败返回错误码
int ap3216_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
    int ret = 0;
    printk("%s,%s,%d\n", __FILE__, __func__, __LINE__);
    gclient = client;

    // 分配字符设备结构体
    ap3216_cdev = cdev_alloc();

    // 初始化字符设备
    cdev_init(ap3216_cdev, &fops);

    // 注册设备号
    ret = alloc_chrdev_region(&(ap3216_cdev->dev), 0, 1, NAME);
    if (ret < 0)
    {
        printk("alloc_chrdev_region error\n");
        goto NO_1;
    }

    // 添加字符设备
    ret = cdev_add(ap3216_cdev, ap3216_cdev->dev, 1);
    if (ret < 0)
    {
        printk("cdev_add error\n");
        goto NO_2;
    }

    // 创建设备类
    cls = class_create(THIS_MODULE, NAME);
    if (IS_ERR(cls))
    {
        ret = PTR_ERR(cls);
        goto NO_3;
    }

    // 创建设备节点
    dev = device_create(cls, NULL, ap3216_cdev->dev, NULL, NAME);
    if (IS_ERR(dev))
    {
        ret = PTR_ERR(dev);
        goto NO_4;
    }

    // 初始化ALS功能
    ret = ap3126_ALS_init();
    if (ret)
    {
        printk("ap3126_ALS_init error\n");
        goto NO_5;
    }

    return 0;
NO_5:
    device_destroy(cls, dev->devt);

NO_4:
    class_destroy(cls);

NO_3:
    cdev_del(ap3216_cdev);
NO_2:
    unregister_chrdev_region(ap3216_cdev->dev, 1);
NO_1:
    return ret;
}

// 函数:ap3216_remove
// 作用:设备移除函数
// 返回值:成功返回0
int ap3216_remove(struct i2c_client *client)
{
    printk("%s,%s,%d\n", __FILE__, __func__, __LINE__);
    device_destroy(cls, dev->devt);
    class_destroy(cls);
    cdev_del(ap3216_cdev);
    unregister_chrdev_region(ap3216_cdev->dev, 1);
    return 0;
}

// 设备树匹配表
struct of_device_id ap3216_table[] = {
    [0] = {.compatible = "wyc,ap3216c"},
    {}
};

// I2C驱动结构体
struct i2c_driver iic_dev = {
    .probe = ap3216_probe,
    .remove = ap3216_remove,
    .driver = {
        .name = "ap3216c",
        .of_match_table = ap3216_table,
    }
};

// 模块初始化函数
int __init ap3216_init(void)
{
    int ret;
    printk("%s,%s,%d\n", __FILE__, __func__, __LINE__);
    ret = i2c_register_driver(THIS_MODULE, &iic_dev);
    if (ret < 0)
    {
        printk("i2c_register_driver error\n");
        return ret;
    }

    return 0;
}

// 模块退出函数
void __exit ap3216_exit(void)
{
    printk("%s,%s,%d\n", __FILE__, __func__, __LINE__);
    i2c_del_driver(&iic_dev);
}

// 注册模块初始化和退出函数
module_init(ap3216_init);
module_exit(ap3216_exit);

// 模块许可声明
MODULE_LICENSE("GPL");


应用程序:ap3216c_main.c

#include <stdio.h>
#include <sys/stat.h>
#include <fcntl.h>

#include <unistd.h>
int main(int argc, const char *argv[])
{
    int fd = 0;
    int ret = 0;
    char ubuf[2] = {0};
    int light_intensity = 0;
    fd = open("/dev/ap3216c_dev", O_RDWR);
    if (fd < 0)
    {
        perror("open error");
        return -1;
    }

    while (1)
    {
        sleep(3);
        ret = read(fd, ubuf, sizeof(ubuf));
        if (ret < 0)
        {
            perror("read open ");
            return -1;
        }
        light_intensity = (ubuf[1] << 8) + ubuf[0];
        printf("main光照强度为:%d\n", light_intensity);
    }

    return 0;
}

愿大家可以有所收获!!!

  • 13
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值