• 正文
    • 修改设备树
    • 编写elf-aht20.c驱动
    • 完整的驱动elf-aht20.c源码
    • 编译
    • 编写测试应用源码aht20_app.c
    • 编译应用
    • 测试
  • 相关推荐
申请入驻 产业图谱

飞凌嵌入式ElfBoard ELF 1板卡-I2C设备驱动之I2C驱动之温湿度传感器

04/15 10:20
260
加入交流群
扫码加入
获取工程师必备礼包
参与热点资讯讨论

例程代码路径:ELF 1开发板资料包3-例程源码3-2 驱动例程源码7_I2C驱动-aht20

下面编写一个温湿度传感器的驱动,来了解I2C驱动的具体使用。

修改设备树

(一)查看原理图引脚复用表格,确定温湿度传感器连接引脚。

(二)I2C引脚复用,打开设备树文件arch/arm/boot/dts/imx6ull-elf1-emmc.dts我们看到原来的设备树文件已经添加了pinctrl_i2c1子节点,而且选择的引脚与UART4_TX_DATA、UART4_RX_DATA一致,所以此处无需修改:

(三)添加设备节点

在arch/arm/boot/dts/imx6ull-elf1-emmc.dts文件中的i2c1节点下添加温湿度传感器子节点aht20:

aht20@38 {

compatible = "elf,aht20";

reg = <0x38>;

status = "okay";

};

自带的mag3110和fxls8471没有用到,所以将其屏蔽掉添加后的效果如下:

(四)编译设备树:

. /opt/fsl-imx-x11/4.1.15-2.0.0/environment-setup-cortexa7hf-neon-poky-linux-gnueabi

elf@ubuntu:~/work/linux-imx-imx_4.1.15_2.0.0_ga$ make dtbs

编译生成的设备树文件为imx6ull-elf1-emmc.dtb,参考《01-0 ELF1、ELF1S开发板_快速启动手册_V1》4.4节单独更新设备树。

编写elf-aht20.c驱动

(一)在驱动中要操作很多芯片相关的寄存器,所以需要先新建一个i2c_aht20.h的头文件,用来定义相关寄存器值。

#ifndef I2C_AHT20_H

#define I2C_AHT20_H

#define AHT20_STATUS_CALI_SHIFT 3       // bit[3] CAL Enable

#define AHT20_STATUS_CALI_MASK  (0x1<<AHT20_STATUS_CALI_SHIFT)

#define AHT20_STATUS_CALI(status) ((status & AHT20_STATUS_CALI_MASK) >> AHT20_STATUS_CALI_SHIFT)

// bit[2:0] Reserved

#define AHT20_STATUS_BUSY_SHIFT 7       // bit[7] Busy indication

#define AHT20_STATUS_BUSY_MASK  (0x1<<AHT20_STATUS_BUSY_SHIFT)

#define AHT20_STATUS_BUSY(status) ((status & AHT20_STATUS_BUSY_MASK) >> AHT20_STATUS_BUSY_SHIFT)

#define AHT20_CMD_STATUS        0x71

#define AHT20_CMD_RESET         0xBA

#define AHT20_CMD_TRIGGER       0xAC

#define AHT20_CMD_TRIGGER_ARG0  0x33

#define AHT20_CMD_TRIGGER_ARG1  0x00

#define AHT20_CMD_CALIBRATION       0xBE

#define AHT20_CMD_CALIBRATION_ARG0  0x08

#define AHT20_CMD_CALIBRATION_ARG1  0x00

#define AHT20_STARTUP_TIME     20 //ms

#define AHT20_CALIBRATION_TIME 40 //ms

#define AHT20_MEASURE_TIME     75 //ms

#define AHT20_MAX_RETRY 5

#define AHT20_RESOLUTION                (1<<20)

#endif

(二)elf-aht20.c文件编写

(1)头文件引用

#include <linux/init.h>

#include <linux/module.h>

#include <linux/fs.h>

#include <linux/cdev.h>

#include <linux/uaccess.h>

#include <linux/i2c.h>

#include <linux/types.h>

#include <linux/kernel.h>

#include <linux/delay.h>

#include <linux/ide.h>

#include <linux/errno.h>

#include <linux/gpio.h>

#include <asm/mach/map.h>

#include <linux/of.h>

#include <linux/of_address.h>

#include <linux/of_gpio.h>

#include <asm/io.h>

#include <linux/device.h>

#include <linux/platform_device.h>

#include “i2c_aht20.h”

(2)创建相关宏定义和变量

#define DEV_NAME "aht20"  /*设备名称*/

#define DEV_CNT (1)

/* Private typedef -----------------------------------------------------------*/

/* aht20设备结构体 */

typedef struct {

dev_t devid;                    /* 设备号 */

struct cdev cdev;               /* cdev */

struct class *class;    /* 类 */

struct device *device;  /* 设备 */

struct device_node *nd; /*设备节点 */

int major;                              /*主设备号 */

void *private_data;             /* 私有数据 */

unsigned short ir, als, ps;     /* 光传感数据 */

}aht20_dev_t;

/* Private variables ---------------------------------------------------------*/

static aht20_dev_t aht20dev;

uint8_t clibrate_arg[] = {AHT20_CMD_CALIBRATION_ARG0, AHT20_CMD_CALIBRATION_ARG1};

uint8_t trigger_arg[] = {AHT20_CMD_TRIGGER_ARG0,AHT20_CMD_TRIGGER_ARG1};

(3)驱动模块的入口和出口

module_init(aht20_driver_init);

module_exit(aht20_driver_exit);

(4)ath20_driver_init和aht20_driver_exit实现

static int __init aht20_driver_init(void)

{

pr_info("aht20 driver initn");

return i2c_add_driver(&aht20_driver);

}

static void __exit aht20_driver_exit(void)

{

pr_info("aht20 driver exitn");

i2c_del_driver(&aht20_driver);

}

在入口函数中调用了i2c_add_driver函数,来注册I2C总线驱动程序。在出口函数中调用了i2c_del_driver函数,来注销I2C驱动程序。

i2c_add_driver函数原型如下:

int i2c_add_driver(struct i2c_driver *driver);

该函数接受一个指向struct i2c_driver结构的指针作为参数,该结构包含了驱动程序的相关信息,例如驱动程序的名称、ID表、探测函数等。函数返回一个整数值,表示注册是否成功。如果成功,返回0;如果失败,返回一个负数错误代码。

以下是struct i2c_driver结构体的常见成员:

driver:这是一个指向struct device_driver结构的指针,用于描述I2C驱动程序所属的设备驱动程序。

probe:这是一个函数指针,指向设备探测函数。当一个设备与I2C总线匹配时,该函数会被调用。设备探测函数负责初始化设备并进行必要的配置。

remove:这是一个函数指针,指向设备移除函数。当一个设备从I2C总线上移除时,该函数会被调用。设备移除函数负责释放设备所占用的资源。

id_table:这是一个指向struct i2c_device_id数组的指针,用于描述I2C设备的标识信息。驱动程序可以使用这些标识信息来识别与之匹配的设备。

address_list:这是一个指向unsigned short数组的指针,用于描述驱动程序支持的I2C设备地址列表。驱动程序会使用这些地址来匹配和识别设备。

driver.name:这是一个字符串,表示驱动程序的名称。它在设备和驱动程序之间建立关联。

通过调用i2c_add_driver函数并传入正确配置的struct i2c_driver结构体,可以将I2C总线驱动程序注册到Linux内核,使其能够接收和处理I2C设备的相关操作。

(5)i2c_driver类型结构体定义

struct i2c_driver aht20_driver = {

.probe = aht20_probe,

.remove = aht20_remove,

.id_table = aht20_device_id,

.driver = {

.name = "elf,aht20",

.owner = THIS_MODULE,

.of_match_table = aht20_match_table,

},

};

(6)aht20_match_table实现,用来与设备树中的compatible匹配

static const struct of_device_id aht20_match_table[] = {

{.compatible = "elf,aht20", },

{ },

};

(7)remove函数实现,执行aht20设备的清理操作

static int aht20_remove(struct i2c_client *client)

{

// 销毁设备节点

&nbspdevice_destroy(aht20dev.class, aht20dev.devid);&nbsp

// 销毁设备类

&nbspclass_destroy(aht20dev.class);&nbsp

// 删除字符设备

&nbspcdev_del(&aht20dev.cdev);

// 注销字符设备驱动程序

&nbspunregister_chrdev_region(aht20dev.devid, DEV_CNT);

&nbspreturn 0;

}

(8)probe函数实现,此处简略描述regmap注册的过程。

static int aht20_probe(struct i2c_client *client, const struct i2c_device_id *id)

{

&nbspint ret = -1;

// 注册字符设备驱动程序

&nbspret = alloc_chrdev_region(&aht20dev.devid, 0, DEV_CNT, DEV_NAME);

&nbspif (ret < 0)

&nbsp{

&nbsp&nbspprintk("fail to alloc aht_devn");

&nbsp&nbspgoto alloc_err;

&nbsp}

//初始化字符设备结构体

&nbspcdev_init(&aht20dev.cdev, &aht20_chr_dev_fops);

//将字符设备添加到内核中

&nbspret = cdev_add(&aht20dev.cdev, aht20dev.devid, DEV_CNT);

&nbspif (ret < 0)

&nbsp{

&nbsp&nbspprintk("fail to add cdevn");

&nbsp&nbspgoto add_err;

&nbsp}

// 创建设备类

&nbspaht20dev.class = class_create(THIS_MODULE, DEV_NAME);

// 创建设备节点并关联到设备类

&nbspaht20dev.device = device_create(aht20dev.class, NULL, aht20dev.devid, NULL, DEV_NAME);

&nbspaht20dev.private_data = client;

&nbspaht20_init();

&nbspreturn 0;

add_err:

&nbspunregister_chrdev_region(aht20dev.devid, DEV_CNT);

&nbspprintk("n add_err error! n");

alloc_err:

&nbspreturn -1;

}

probe函数中实现的就是前面讲到的字符设备的注册流程,注册完成后调用aht20_init()函数,对芯片进行初始化。

(9)定义file_operations类型结构体:

static struct file_operations aht20_chr_dev_fops =

{

.owner = THIS_MODULE,

.open = aht20_open,

.read = aht20_read,

.release = aht20_release,

};

static int aht20_open(struct inode *inode, struct file *filp)

{

&nbspfilp->private_data = &aht20dev;

return 0;

}

static ssize_t aht20_read(struct file *filp, char __user *buf, size_t cnt, loff_t *off)

{

&nbspfloat temp,humi;

//&nbspuint8_t data[7] = {0};

&nbspuint32_t data[2] = {0};

&nbsplong err = 0;

&nbspfilp->private_data = &aht20dev;

&nbspaht20_get_measure(&data[0]);

&nbsperr = copy_to_user(buf, data, sizeof(data));

return 0;

}

static int aht20_release(struct inode *inode, struct file *filp)

{

return 0;

}

(10)操作函数的实现:

aht20_read函数中调用aht20_get_measuer()函数读取温湿度传感器中的数据,然后通过copy_to_user()函数将数据拷贝到用户空间。

(11)aht20_get_measuer()函数定义:

static int aht20_get_measure(uint32_t* RAW)

{

&nbspint retval = 0, i = 0;

&nbspuint8_t data[7] = {0};

&nbspretval = aht20_write_regs(&aht20dev,AHT20_CMD_TRIGGER,trigger_arg,2);

&nbspmsleep(AHT20_MEASURE_TIME);

&nbspaht20_read_data(&aht20dev,data,7);

&nbsp

&nbspfor (i = 0; AHT20_STATUS_BUSY(data[0]) && i < AHT20_MAX_RETRY; i++) {

&nbsp&nbspprintk("AHT20 device busy, retry %d/%d!rn", i, AHT20_MAX_RETRY);

&nbsp&nbspmsleep(AHT20_MEASURE_TIME);

&nbsp&nbspaht20_read_data(&aht20dev,data,7);

}

&nbspif (i >= AHT20_MAX_RETRY) {

printk("AHT20 device always busy!rn");

}

&nbspuint32_t humiRaw = data[1];

humiRaw = (humiRaw << 8) | data[2];

humiRaw = (humiRaw << 4) | ((data[3] & 0xF0) >> 4);

uint32_t tempRaw = data[3] & 0x0F;

tempRaw = (tempRaw << 8) | data[4];

tempRaw = (tempRaw << 8) | data[5];

&nbspRAW[0] = humiRaw;

&nbspRAW[1] = tempRaw;

//&nbspprintk("aht20 humiRAW = %05X,  tempRAW = %05Xrn", humiRaw, tempRaw);

&nbspreturn 0;

}

aht20_get_measuer()函数中又调用了aht20_write_regs()函数写寄存器,aht20_read_data()函数来读取数据。

(12)aht20_write_regs()函数和aht20_read_data()函数的定义:

static s32 aht20_write_regs(aht20_dev_t *dev, u8 reg, u8 *buf, u8 len)

{

&nbspu8 byte[256] = {0};

&nbspstruct i2c_msg msg;

&nbspstruct i2c_client *client = (struct i2c_client*)dev->private_data;

&nbspbyte[0] = reg;&nbsp/*!< 寄存器首地址 */

&nbspmemcpy(&byte[1], buf, len);&nbsp/*!< 拷贝数据 */

&nbsp

&nbspmsg.addr = client->addr;&nbsp/*!< aht20地址 */

&nbspmsg.flags = 0;&nbsp/*!< 标记为写数据 */

&nbspmsg.buf = byte;

&nbspmsg.len = len + 1;&nbsp&nbsp/*!< 要写入数据的长度 */&nbsp

&nbsp

&nbspreturn i2c_transfer(client->adapter, &msg, 1);

}

static int aht20_read_data(aht20_dev_t *dev, void *val, int len)

{

&nbspint ret = 0;

&nbspstruct i2c_msg msg[2];

&nbspstruct i2c_client *client = (struct i2c_client*)dev->private_data;

&nbsp/* msg[1]为要读取数据 */

&nbspmsg[0].addr = client->addr;&nbsp/*!< aht20地址 */

&nbspmsg[0].flags = I2C_M_RD;&nbsp/*!< 标记为读取数据 */

&nbspmsg[0].buf = val;&nbsp/*!< 读取数据的缓冲区 */

&nbspmsg[0].len = len;&nbsp&nbsp/*!< 读取数据的长度 */&nbsp

&nbspreturn i2c_transfer(client->adapter, msg, 1);

}

函数中都调用了i2c_transfer()函数,这是一个用来进行I2C数据传输的函数,原型如下:

int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num);

该函数接受三个参数:

adap:一个指向struct i2c_adapter结构的指针,表示要使用的I2C适配器

msgs:一个指向struct i2c_msg结构数组的指针,每个结构表示一个I2C传输消息(包括读取和写入操作)。

num:传输消息的数量,即msgs数组中的元素个数。

函数返回一个整数值,表示传输是否成功。如果成功,返回传输的消息数量;如果失败,返回一个负数错误代码。

struct i2c_msg结构体用于描述一个I2C传输消息,定义如下:

struct i2c_msg {

__u16 addr;       // 设备地址

__u16 flags;      // 消息标志位

__u16 len;        // 数据长度

__u8 *buf;        // 数据缓冲区

};

该结构包含以下成员:

addr:表示I2C设备的地址。

flags:用于指定消息的标志位,例如读取或写入操作。

len:指定数据缓冲区的长度。

buf:指向数据缓冲区的指针,用于存储要传输的数据。

通过调用i2c_transfer函数,可以将一系列的I2C传输消息发送到指定的I2C设备上,以实现数据的读取和写入操作。

完整的驱动elf-aht20.c源码

#include <linux/init.h>

#include <linux/module.h>

#include <linux/fs.h>

#include <linux/cdev.h>

#include <linux/uaccess.h>

#include <linux/i2c.h>

#include <linux/types.h>

#include <linux/kernel.h>

#include <linux/delay.h>

#include <linux/ide.h>

#include <linux/errno.h>

#include <linux/gpio.h>

#include <asm/mach/map.h>

#include <linux/of.h>

#include <linux/of_address.h>

#include <linux/of_gpio.h>

#include <asm/io.h>

#include <linux/device.h>

#include <linux/platform_device.h>

#include "i2c_aht20.h"

#define DEV_NAME "aht20"

#define DEV_CNT (1)

/* Private typedef -----------------------------------------------------------*/

/* aht20设备结构体 */

typedef struct {

&nbspdev_t devid;&nbsp&nbsp&nbsp/*!< 设备号 */

&nbspstruct cdev cdev;&nbsp&nbsp/*!< cdev */

&nbspstruct class *class;&nbsp/*!< 类 */

&nbspstruct device *device;&nbsp/*!< 设备 */

&nbspstruct device_node *nd; /*!< 设备节点 */

&nbspint major;&nbsp&nbsp&nbsp&nbsp/*!< 主设备号 */

&nbspvoid *private_data;&nbsp&nbsp/*!< 私有数据 */

&nbspunsigned short ir, als, ps;&nbsp/*!< 光传感数据 */

}aht20_dev_t;

/* Private variables ---------------------------------------------------------*/

static aht20_dev_t aht20dev;

uint8_t clibrate_arg[] = {AHT20_CMD_CALIBRATION_ARG0, AHT20_CMD_CALIBRATION_ARG1};

uint8_t trigger_arg[] = {AHT20_CMD_TRIGGER_ARG0,AHT20_CMD_TRIGGER_ARG1};

static s32 aht20_write_regs(aht20_dev_t *dev, u8 reg, u8 *buf, u8 len)

{

&nbspu8 byte[256] = {0};

&nbspstruct i2c_msg msg;

&nbspstruct i2c_client *client = (struct i2c_client*)dev->private_data;

&nbspbyte[0] = reg;&nbsp/*!< 寄存器首地址 */

&nbspmemcpy(&byte[1], buf, len);&nbsp/*!< 拷贝数据 */

&nbsp

&nbspmsg.addr = client->addr;&nbsp/*!< aht20地址 */

&nbspmsg.flags = 0;&nbsp/*!< 标记为写数据 */

&nbspmsg.buf = byte;

&nbspmsg.len = len + 1;&nbsp&nbsp/*!< 要写入数据的长度 */&nbsp

&nbsp

&nbspreturn i2c_transfer(client->adapter, &msg, 1);

}

static int aht20_read_regs(aht20_dev_t *dev, u8 reg, void *val, int len)

{

&nbspint ret = 0;

&nbspstruct i2c_msg msg[2];

&nbspstruct i2c_client *client = (struct i2c_client*)dev->private_data;

&nbsp/* msg[0]为发送要读取的首地址 */

&nbspmsg[0].addr = client->addr;&nbsp/*!< aht20地址 */

&nbspmsg[0].flags = 0;&nbsp/*!< 标记为发送数据 */

&nbspmsg[0].buf = ®&nbsp/*!< 读取的首地址 */

&nbspmsg[0].len = 1;&nbsp&nbsp/*!< reg长度 */

&nbsp/* msg[1]为要读取数据 */

&nbspmsg[1].addr = client->addr;&nbsp/*!< aht20地址 */

&nbspmsg[1].flags = I2C_M_RD;&nbsp/*!< 标记为读取数据 */

&nbspmsg[1].buf = val;&nbsp/*!< 读取数据的缓冲区 */

&nbspmsg[1].len = len;&nbsp&nbsp/*!< 读取数据的长度 */&nbsp

&nbspret = i2c_transfer(client->adapter, msg, 2);

&nbspif (ret == 2)&nbspret = 0;

&nbspelse ret = -EREMOTEIO;

&nbspreturn ret;

}

static void aht20_write_reg(aht20_dev_t *dev, u8 reg, u8 data)

{

&nbspu8 buf = 0;

&nbspbuf = data;

&nbspaht20_write_regs(dev, reg, &buf, 1);

}

static unsigned char aht20_read_reg(aht20_dev_t *dev, u8 reg)

{

&nbspu8 data = 0;

&nbspaht20_read_regs(dev, reg, &data, 1);

&nbspreturn data;

#if 0

&nbspstruct i2c_client *client = (struct i2c_client *)dev->private_data;

&nbspreturn i2c_smbus_read_byte_data(client, reg);

#endif

}

static int aht20_read_data(aht20_dev_t *dev, void *val, int len)

{

&nbspint ret = 0;

&nbspstruct i2c_msg msg[2];

&nbspstruct i2c_client *client = (struct i2c_client*)dev->private_data;

&nbsp/* msg[1]为要读取数据 */

&nbspmsg[0].addr = client->addr;&nbsp/*!< aht20地址 */

&nbspmsg[0].flags = I2C_M_RD;&nbsp/*!< 标记为读取数据 */

&nbspmsg[0].buf = val;&nbsp/*!< 读取数据的缓冲区 */

&nbspmsg[0].len = len;&nbsp&nbsp/*!< 读取数据的长度 */&nbsp

&nbspreturn i2c_transfer(client->adapter, msg, 1);

}

static int aht20_get_measure(uint32_t* RAW)

{

&nbspint retval = 0, i = 0;

&nbspuint8_t data[7] = {0};

&nbspretval = aht20_write_regs(&aht20dev,AHT20_CMD_TRIGGER,trigger_arg,2);

&nbspmsleep(AHT20_MEASURE_TIME);

&nbspaht20_read_data(&aht20dev,data,7);

&nbsp

&nbspfor (i = 0; AHT20_STATUS_BUSY(data[0]) && i < AHT20_MAX_RETRY; i++) {

&nbsp&nbspprintk("AHT20 device busy, retry %d/%d!rn", i, AHT20_MAX_RETRY);

&nbsp&nbspmsleep(AHT20_MEASURE_TIME);

&nbsp&nbspaht20_read_data(&aht20dev,data,7);

}

&nbspif (i >= AHT20_MAX_RETRY) {

printk("AHT20 device always busy!rn");

}

&nbspuint32_t humiRaw = data[1];

humiRaw = (humiRaw << 8) | data[2];

humiRaw = (humiRaw << 4) | ((data[3] & 0xF0) >> 4);

uint32_t tempRaw = data[3] & 0x0F;

tempRaw = (tempRaw << 8) | data[4];

tempRaw = (tempRaw << 8) | data[5];

&nbspRAW[0] = humiRaw;

&nbspRAW[1] = tempRaw;

//&nbspprintk("aht20 humiRAW = %05X,  tempRAW = %05Xrn", humiRaw, tempRaw);

&nbspreturn 0;

}

/* send reset cmd */

static int aht20_write_reset(aht20_dev_t *dev)

{

&nbspu8 byte[256] = {0};

&nbspstruct i2c_msg msg;

&nbspstruct i2c_client *client = (struct i2c_client*)dev->private_data;

&nbspbyte[0] = AHT20_CMD_RESET;&nbsp/*!< 寄存器首地址 */

&nbspmsg.addr = client->addr;&nbsp/*!< aht20地址 */

&nbspmsg.flags = 0;&nbsp/*!< 标记为写数据 */

&nbspmsg.buf = byte;&nbsp/*!< 要写入的数据缓冲区 */

&nbspmsg.len = 1;&nbsp&nbsp/*!< 要写入数据的长度 */&nbsp

&nbsp

&nbspreturn i2c_transfer(client->adapter, &msg, 1);

}

static int aht20_init(void)

{

&nbspunsigned char status;

&nbspint retval;

&nbspfloat temp, humi;

&nbspuint8_t RAW[7];

&nbspstatus = aht20_read_reg(&aht20dev, AHT20_CMD_STATUS);

&nbspif (AHT20_STATUS_BUSY(status) || !AHT20_STATUS_CALI(status)) {

&nbsp&nbspretval = aht20_write_reset(&aht20dev);

&nbsp&nbspmsleep(AHT20_STARTUP_TIME);

&nbsp&nbspretval = aht20_write_regs(&aht20dev,AHT20_CMD_CALIBRATION,clibrate_arg,2);

&nbsp&nbspmsleep(AHT20_CALIBRATION_TIME);

&nbsp&nbspreturn retval;

}

&nbspaht20_get_measure(&RAW[0]);

}

static int aht20_open(struct inode *inode, struct file *filp)

{

&nbspfilp->private_data = &aht20dev;

return 0;

}

static ssize_t aht20_read(struct file *filp, char __user *buf, size_t cnt, loff_t *off)

{

&nbspfloat temp,humi;

//&nbspuint8_t data[7] = {0};

&nbspuint32_t data[2] = {0};

&nbsplong err = 0;

&nbspfilp->private_data = &aht20dev;

&nbspaht20_get_measure(&data[0]);

&nbsperr = copy_to_user(buf, data, sizeof(data));

return 0;

}

static int aht20_release(struct inode *inode, struct file *filp)

{

return 0;

}

static struct file_operations aht20_chr_dev_fops =

{

.owner = THIS_MODULE,

.open = aht20_open,

.read = aht20_read,

.release = aht20_release,

};

static int aht20_probe(struct i2c_client *client, const struct i2c_device_id *id)

{

&nbspint ret = -1;

&nbspret = alloc_chrdev_region(&aht20dev.devid, 0, DEV_CNT, DEV_NAME);

&nbspif (ret < 0)

&nbsp{

&nbsp&nbspprintk("fail to alloc aht_devn");

&nbsp&nbspgoto alloc_err;

&nbsp}

//&nbspaht20_chr_dev.owner = THIS_MODULE;

&nbspcdev_init(&aht20dev.cdev, &aht20_chr_dev_fops);

&nbspret = cdev_add(&aht20dev.cdev, aht20dev.devid, DEV_CNT);

&nbspif (ret < 0)

&nbsp{

&nbsp&nbspprintk("fail to add cdevn");

&nbsp&nbspgoto add_err;

&nbsp}

&nbspaht20dev.class = class_create(THIS_MODULE, DEV_NAME);

&nbspaht20dev.device = device_create(aht20dev.class, NULL, aht20dev.devid, NULL, DEV_NAME);

&nbspaht20dev.private_data = client;

&nbspaht20_init();

&nbspreturn 0;

add_err:

&nbspunregister_chrdev_region(aht20dev.devid, DEV_CNT);

&nbspprintk("n add_err error! n");

alloc_err:

&nbspreturn -1;

}

static int aht20_remove(struct i2c_client *client)

{

&nbspdevice_destroy(aht20dev.class, aht20dev.devid);&nbsp

&nbspclass_destroy(aht20dev.class);&nbsp

&nbspcdev_del(&aht20dev.cdev);

&nbspunregister_chrdev_region(aht20dev.devid, DEV_CNT);

&nbspreturn 0;

}

static const struct i2c_device_id aht20_device_id[] = {

{"elf,aht20", 0},

{ }

};

/*定义设备树匹配表*/

static const struct of_device_id aht20_match_table[] = {

{.compatible = "elf,aht20", },

{ },

};

/*定义i2c设备结构体*/

struct i2c_driver aht20_driver = {

.probe = aht20_probe,

.remove = aht20_remove,

.id_table = aht20_device_id,

.driver = {

.name = "elf,aht20",

.owner = THIS_MODULE,

.of_match_table = aht20_match_table,

},

};

static int __init aht20_driver_init(void)

{

pr_info("aht20 driver initn");

return i2c_add_driver(&aht20_driver);

}

static void __exit aht20_driver_exit(void)

{

pr_info("aht20 driver exitn");

i2c_del_driver(&aht20_driver);

}

module_init(aht20_driver_init);

module_exit(aht20_driver_exit);

MODULE_LICENSE("GPL");

MODULE_AUTHOR("bkxr@outlook.com");

MODULE_DESCRIPTION("aht20 sensor driver");

编译

复制7.7.3驱动中的Makefile文件,将其中的platform_led.o修改为elf-aht20.o,效果如下:

. /opt/fsl-imx-x11/4.1.15-2.0.0/environment-setup-cortexa7hf-neon-poky-linux-gnueabi

elf@ubuntu:~/work/test/07_I2C驱动-aht20/aht20$ make

将编译生成的elf-aht20.ko模块拷贝到开发板。

编写测试应用源码aht20_app.c

测试源码中循环读取驱动传到用户空间的数据:

#include "stdio.h"

#include "unistd.h"

#include "sys/types.h"

#include "sys/stat.h"

#include "sys/ioctl.h"

#include "fcntl.h"

#include "stdlib.h"

#include "string.h"

#include <poll.h>

#include <sys/select.h>

#include <sys/time.h>

#include <signal.h>

#include <fcntl.h>

#define AHT20_DEV "/dev/aht20"

int main(int argc, char *argv[])

{

&nbspint fd;

&nbspunsigned int databuf[2];

&nbspint c1,t1;

&nbspfloat hum,temp;

&nbspint ret = 0;

&nbspfd = open(AHT20_DEV, O_RDWR);

&nbspif(fd < 0) {

&nbsp&nbspprintf("can't open file %srn", AHT20_DEV);

&nbsp&nbspreturn -1;

&nbsp}

&nbspwhile (1) {

&nbsp&nbspret = read(fd, databuf, sizeof(databuf));

&nbsp&nbspif(ret == 0) { &nbsp&nbsp&nbsp/* ?????? */

&nbsp&nbsp c1 = databuf[0]*1000/1024/1024;  //

&nbsp&nbsp t1 = databuf[1] *200*10/1024/1024-500;

&nbsp&nbsp hum = (float)c1/10.0;

&nbsp&nbsp temp = (float)t1/10.0;

&nbsp&nbspprintf("hum = %0.2f temp = %0.2f rn",hum,temp);

&nbsp&nbspusleep(500000);

&nbsp&nbsp}

&nbsp}

&nbspclose(fd);&nbsp

&nbspreturn 0;

}

编译应用

. /opt/fsl-imx-x11/4.1.15-2.0.0/environment-setup-cortexa7hf-neon-poky-linux-gnueabi

elf@ubuntu:~/work/test/07_I2C驱动-aht20/aht20_app$ $CC aht20_app.c -o aht20_app

将编译好的测试应用拷贝到开发板中。

测试

root@ELF1:~# insmod elf-aht20.ko

aht20 driver init

root@ELF1:~# ./aht20_app

hum = 45.60 temp = 30.60

hum = 45.60 temp = 30.60

hum = 45.70 temp = 30.60

hum = 45.70 temp = 30.60

hum = 45.60 temp = 30.60

root@ELF1:~# rmmod elf-aht20.ko

aht20 driver exit

点赞
收藏
评论
分享
加入交流群
举报

相关推荐

登录即可解锁
  • 海量技术文章
  • 设计资源下载
  • 产业链客户资源
  • 写文章/发需求
立即登录