例程代碼路徑:ELF 1開發(fā)板資料包3-例程源碼3-2 驅(qū)動例程源碼7_I2C驅(qū)動-aht20
下面編寫一個溫濕度傳感器的驅(qū)動,來了解I2C驅(qū)動的具體使用。
修改設備樹
(二)I2C引腳復用,打開設備樹文件arch/arm/boot/dts/imx6ull-elf1-emmc.dts我們看到原來的設備樹文件已經(jīng)添加了pinctrl_i2c1子節(jié)點,而且選擇的引腳與UART4_TX_DATA、UART4_RX_DATA一致,所以此處無需修改:
(三)添加設備節(jié)點
在arch/arm/boot/dts/imx6ull-elf1-emmc.dts文件中的i2c1節(jié)點下添加溫濕度傳感器子節(jié)點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開發(fā)板_快速啟動手冊_V1》4.4節(jié)單獨更新設備樹。
編寫elf-aht20.c驅(qū)動
(一)在驅(qū)動中要操作很多芯片相關的寄存器,所以需要先新建一個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)創(chuàng)建相關宏定義和變量
#define DEV_NAME "aht20" ?/*設備名稱*/
#define DEV_CNT (1) /* Private typedef -----------------------------------------------------------*/ /* aht20設備結(jié)構體 */ typedef struct { dev_t devid; ???????????????????/* 設備號 */ struct cdev cdev; ??????????????/* cdev */ struct class *class; ???/* 類 */ struct device *device; ?/* 設備 */ struct device_node *nd; /*設備節(jié)點 */ int major; ?????????????????????????????/*主設備號 */ void *private_data; ????????????/* 私有數(shù)據(jù) */ unsigned short ir, als, ps; ????/* 光傳感數(shù)據(jù) */ }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)驅(qū)動模塊的入口和出口
module_init(aht20_driver_init);
module_exit(aht20_driver_exit); |
(4)ath20_driver_init和aht20_driver_exit實現(xiàn)
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); } |
在入口函數(shù)中調(diào)用了i2c_add_driver函數(shù),來注冊I2C總線驅(qū)動程序。在出口函數(shù)中調(diào)用了i2c_del_driver函數(shù),來注銷I2C驅(qū)動程序。
i2c_add_driver函數(shù)原型如下:
int i2c_add_driver(struct i2c_driver *driver); |
該函數(shù)接受一個指向struct i2c_driver結(jié)構的指針作為參數(shù),該結(jié)構包含了驅(qū)動程序的相關信息,例如驅(qū)動程序的名稱、ID表、探測函數(shù)等。函數(shù)返回一個整數(shù)值,表示注冊是否成功。如果成功,返回0;如果失敗,返回一個負數(shù)錯誤代碼。
以下是struct i2c_driver結(jié)構體的常見成員:
driver:這是一個指向struct device_driver結(jié)構的指針,用于描述I2C驅(qū)動程序所屬的設備驅(qū)動程序。
probe:這是一個函數(shù)指針,指向設備探測函數(shù)。當一個設備與I2C總線匹配時,該函數(shù)會被調(diào)用。設備探測函數(shù)負責初始化設備并進行必要的配置。
remove:這是一個函數(shù)指針,指向設備移除函數(shù)。當一個設備從I2C總線上移除時,該函數(shù)會被調(diào)用。設備移除函數(shù)負責釋放設備所占用的資源。
id_table:這是一個指向struct i2c_device_id數(shù)組的指針,用于描述I2C設備的標識信息。驅(qū)動程序可以使用這些標識信息來識別與之匹配的設備。
address_list:這是一個指向unsigned short數(shù)組的指針,用于描述驅(qū)動程序支持的I2C設備地址列表。驅(qū)動程序會使用這些地址來匹配和識別設備。
driver.name:這是一個字符串,表示驅(qū)動程序的名稱。它在設備和驅(qū)動程序之間建立關聯(lián)。
通過調(diào)用i2c_add_driver函數(shù)并傳入正確配置的struct i2c_driver結(jié)構體,可以將I2C總線驅(qū)動程序注冊到Linux內(nèi)核,使其能夠接收和處理I2C設備的相關操作。
(5)i2c_driver類型結(jié)構體定義
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實現(xiàn),用來與設備樹中的compatible匹配
static const struct of_device_id aht20_match_table[] = {
{.compatible = "elf,aht20", }, { }, }; |
(7)remove函數(shù)實現(xiàn),執(zhí)行aht20設備的清理操作
static int aht20_remove(struct i2c_client *client)
{ // 銷毀設備節(jié)點  device_destroy(aht20dev.class, aht20dev.devid);  // 銷毀設備類  class_destroy(aht20dev.class);  // 刪除字符設備  cdev_del(&aht20dev.cdev); // 注銷字符設備驅(qū)動程序  unregister_chrdev_region(aht20dev.devid, DEV_CNT);  return 0; } |
(8)probe函數(shù)實現(xiàn),此處簡略描述regmap注冊的過程。
static int aht20_probe(struct i2c_client *client, const struct i2c_device_id *id)
{  int ret = -1; // 注冊字符設備驅(qū)動程序  ret = alloc_chrdev_region(&aht20dev.devid, 0, DEV_CNT, DEV_NAME);  if (ret < 0)  {   printk("fail to alloc aht_devn");   goto alloc_err;  } //初始化字符設備結(jié)構體  cdev_init(&aht20dev.cdev, &aht20_chr_dev_fops); //將字符設備添加到內(nèi)核中  ret = cdev_add(&aht20dev.cdev, aht20dev.devid, DEV_CNT);  if (ret < 0)  {   printk("fail to add cdevn");   goto add_err;  } // 創(chuàng)建設備類  aht20dev.class = class_create(THIS_MODULE, DEV_NAME); // 創(chuàng)建設備節(jié)點并關聯(lián)到設備類  aht20dev.device = device_create(aht20dev.class, NULL, aht20dev.devid, NULL, DEV_NAME);  aht20dev.private_data = client;  aht20_init();  return 0; add_err:  unregister_chrdev_region(aht20dev.devid, DEV_CNT);  printk("n add_err error! n"); alloc_err:  return -1; } |
probe函數(shù)中實現(xiàn)的就是前面講到的字符設備的注冊流程,注冊完成后調(diào)用aht20_init()函數(shù),對芯片進行初始化。
(9)定義file_operations類型結(jié)構體:
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)
{  filp->private_data = &aht20dev; return 0; } static ssize_t aht20_read(struct file *filp, char __user *buf, size_t cnt, loff_t *off) {  float temp,humi; // uint8_t data[7] = {0};  uint32_t data[2] = {0};  long err = 0;  filp->private_data = &aht20dev;  aht20_get_measure(&data[0]);  err = copy_to_user(buf, data, sizeof(data)); return 0; } static int aht20_release(struct inode *inode, struct file *filp) { return 0; } |
(10)操作函數(shù)的實現(xiàn):
aht20_read函數(shù)中調(diào)用aht20_get_measuer()函數(shù)讀取溫濕度傳感器中的數(shù)據(jù),然后通過copy_to_user()函數(shù)將數(shù)據(jù)拷貝到用戶空間。
(11)aht20_get_measuer()函數(shù)定義:
static int aht20_get_measure(uint32_t* RAW)
{  int retval = 0, i = 0;  uint8_t data[7] = {0};  retval = aht20_write_regs(&aht20dev,AHT20_CMD_TRIGGER,trigger_arg,2);  msleep(AHT20_MEASURE_TIME);  aht20_read_data(&aht20dev,data,7);    for (i = 0; AHT20_STATUS_BUSY(data[0]) && i < AHT20_MAX_RETRY; i++) {   printk("AHT20 device busy, retry %d/%d!rn", i, AHT20_MAX_RETRY);   msleep(AHT20_MEASURE_TIME);   aht20_read_data(&aht20dev,data,7); }  if (i >= AHT20_MAX_RETRY) { printk("AHT20 device always busy!rn"); }  uint32_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];  RAW[0] = humiRaw;  RAW[1] = tempRaw; // printk("aht20 humiRAW = %05X, ?tempRAW = %05Xrn", humiRaw, tempRaw);  return 0; } |
aht20_get_measuer()函數(shù)中又調(diào)用了aht20_write_regs()函數(shù)寫寄存器,aht20_read_data()函數(shù)來讀取數(shù)據(jù)。
(12)aht20_write_regs()函數(shù)和aht20_read_data()函數(shù)的定義:
static s32 aht20_write_regs(aht20_dev_t *dev, u8 reg, u8 *buf, u8 len)
{  u8 byte[256] = {0};  struct i2c_msg msg;  struct i2c_client *client = (struct i2c_client*)dev->private_data;  byte[0] = reg; /*!< 寄存器首地址 */  memcpy(&byte[1], buf, len); /*!< 拷貝數(shù)據(jù) */    msg.addr = client->addr; /*!< aht20地址 */  msg.flags = 0; /*!< 標記為寫數(shù)據(jù) */  msg.buf = byte;  msg.len = len + 1;  /*!< 要寫入數(shù)據(jù)的長度 */     return i2c_transfer(client->adapter, &msg, 1); } static int aht20_read_data(aht20_dev_t *dev, void *val, int len) {  int ret = 0;  struct i2c_msg msg[2];  struct i2c_client *client = (struct i2c_client*)dev->private_data;  /* msg[1]為要讀取數(shù)據(jù) */  msg[0].addr = client->addr; /*!< aht20地址 */  msg[0].flags = I2C_M_RD; /*!< 標記為讀取數(shù)據(jù) */  msg[0].buf = val; /*!< 讀取數(shù)據(jù)的緩沖區(qū) */  msg[0].len = len;  /*!< 讀取數(shù)據(jù)的長度 */   return i2c_transfer(client->adapter, msg, 1); } |
函數(shù)中都調(diào)用了i2c_transfer()函數(shù),這是一個用來進行I2C數(shù)據(jù)傳輸的函數(shù),原型如下:
int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num); |
該函數(shù)接受三個參數(shù):
adap:一個指向struct i2c_adapter結(jié)構的指針,表示要使用的I2C適配器。
msgs:一個指向struct i2c_msg結(jié)構數(shù)組的指針,每個結(jié)構表示一個I2C傳輸消息(包括讀取和寫入操作)。
num:傳輸消息的數(shù)量,即msgs數(shù)組中的元素個數(shù)。
函數(shù)返回一個整數(shù)值,表示傳輸是否成功。如果成功,返回傳輸?shù)南?shù)量;如果失敗,返回一個負數(shù)錯誤代碼。
struct i2c_msg結(jié)構體用于描述一個I2C傳輸消息,定義如下:
struct i2c_msg {
__u16 addr; ??????// 設備地址 __u16 flags; ?????// 消息標志位 __u16 len; ???????// 數(shù)據(jù)長度 __u8 *buf; ???????// 數(shù)據(jù)緩沖區(qū) }; |
該結(jié)構包含以下成員:
addr:表示I2C設備的地址。
flags:用于指定消息的標志位,例如讀取或?qū)懭氩僮鳌?/p>
len:指定數(shù)據(jù)緩沖區(qū)的長度。
buf:指向數(shù)據(jù)緩沖區(qū)的指針,用于存儲要傳輸?shù)臄?shù)據(jù)。
通過調(diào)用i2c_transfer函數(shù),可以將一系列的I2C傳輸消息發(fā)送到指定的I2C設備上,以實現(xiàn)數(shù)據(jù)的讀取和寫入操作。
完整的驅(qū)動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設備結(jié)構體 */ typedef struct {  dev_t devid;   /*!< 設備號 */  struct cdev cdev;  /*!< cdev */  struct class *class; /*!< 類 */  struct device *device; /*!< 設備 */  struct device_node *nd; /*!< 設備節(jié)點 */  int major;    /*!< 主設備號 */  void *private_data;  /*!< 私有數(shù)據(jù) */  unsigned short ir, als, ps; /*!< 光傳感數(shù)據(jù) */ }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) {  u8 byte[256] = {0};  struct i2c_msg msg;  struct i2c_client *client = (struct i2c_client*)dev->private_data;  byte[0] = reg; /*!< 寄存器首地址 */  memcpy(&byte[1], buf, len); /*!< 拷貝數(shù)據(jù) */    msg.addr = client->addr; /*!< aht20地址 */  msg.flags = 0; /*!< 標記為寫數(shù)據(jù) */  msg.buf = byte;  msg.len = len + 1;  /*!< 要寫入數(shù)據(jù)的長度 */     return i2c_transfer(client->adapter, &msg, 1); } static int aht20_read_regs(aht20_dev_t *dev, u8 reg, void *val, int len) {  int ret = 0;  struct i2c_msg msg[2];  struct i2c_client *client = (struct i2c_client*)dev->private_data;  /* msg[0]為發(fā)送要讀取的首地址 */  msg[0].addr = client->addr; /*!< aht20地址 */  msg[0].flags = 0; /*!< 標記為發(fā)送數(shù)據(jù) */  msg[0].buf = ? /*!< 讀取的首地址 */  msg[0].len = 1;  /*!< reg長度 */  /* msg[1]為要讀取數(shù)據(jù) */  msg[1].addr = client->addr; /*!< aht20地址 */  msg[1].flags = I2C_M_RD; /*!< 標記為讀取數(shù)據(jù) */  msg[1].buf = val; /*!< 讀取數(shù)據(jù)的緩沖區(qū) */  msg[1].len = len;  /*!< 讀取數(shù)據(jù)的長度 */   ret = i2c_transfer(client->adapter, msg, 2);  if (ret == 2) ret = 0;  else ret = -EREMOTEIO;  return ret; } static void aht20_write_reg(aht20_dev_t *dev, u8 reg, u8 data) {  u8 buf = 0;  buf = data;  aht20_write_regs(dev, reg, &buf, 1); } static unsigned char aht20_read_reg(aht20_dev_t *dev, u8 reg) {  u8 data = 0;  aht20_read_regs(dev, reg, &data, 1);  return data; #if 0  struct i2c_client *client = (struct i2c_client *)dev->private_data;  return i2c_smbus_read_byte_data(client, reg); #endif } static int aht20_read_data(aht20_dev_t *dev, void *val, int len) {  int ret = 0;  struct i2c_msg msg[2];  struct i2c_client *client = (struct i2c_client*)dev->private_data;  /* msg[1]為要讀取數(shù)據(jù) */  msg[0].addr = client->addr; /*!< aht20地址 */  msg[0].flags = I2C_M_RD; /*!< 標記為讀取數(shù)據(jù) */  msg[0].buf = val; /*!< 讀取數(shù)據(jù)的緩沖區(qū) */  msg[0].len = len;  /*!< 讀取數(shù)據(jù)的長度 */   return i2c_transfer(client->adapter, msg, 1); } static int aht20_get_measure(uint32_t* RAW) {  int retval = 0, i = 0;  uint8_t data[7] = {0};  retval = aht20_write_regs(&aht20dev,AHT20_CMD_TRIGGER,trigger_arg,2);  msleep(AHT20_MEASURE_TIME);  aht20_read_data(&aht20dev,data,7);    for (i = 0; AHT20_STATUS_BUSY(data[0]) && i < AHT20_MAX_RETRY; i++) {   printk("AHT20 device busy, retry %d/%d!rn", i, AHT20_MAX_RETRY);   msleep(AHT20_MEASURE_TIME);   aht20_read_data(&aht20dev,data,7); }  if (i >= AHT20_MAX_RETRY) { printk("AHT20 device always busy!rn"); }  uint32_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];  RAW[0] = humiRaw;  RAW[1] = tempRaw; // printk("aht20 humiRAW = %05X, ?tempRAW = %05Xrn", humiRaw, tempRaw);  return 0; } /* send reset cmd */ static int aht20_write_reset(aht20_dev_t *dev) {  u8 byte[256] = {0};  struct i2c_msg msg;  struct i2c_client *client = (struct i2c_client*)dev->private_data;  byte[0] = AHT20_CMD_RESET; /*!< 寄存器首地址 */  msg.addr = client->addr; /*!< aht20地址 */  msg.flags = 0; /*!< 標記為寫數(shù)據(jù) */  msg.buf = byte; /*!< 要寫入的數(shù)據(jù)緩沖區(qū) */  msg.len = 1;  /*!< 要寫入數(shù)據(jù)的長度 */     return i2c_transfer(client->adapter, &msg, 1); } static int aht20_init(void) {  unsigned char status;  int retval;  float temp, humi;  uint8_t RAW[7];  status = aht20_read_reg(&aht20dev, AHT20_CMD_STATUS);  if (AHT20_STATUS_BUSY(status) || !AHT20_STATUS_CALI(status)) {   retval = aht20_write_reset(&aht20dev);   msleep(AHT20_STARTUP_TIME);   retval = aht20_write_regs(&aht20dev,AHT20_CMD_CALIBRATION,clibrate_arg,2);   msleep(AHT20_CALIBRATION_TIME);   return retval; }  aht20_get_measure(&RAW[0]); } static int aht20_open(struct inode *inode, struct file *filp) {  filp->private_data = &aht20dev; return 0; } static ssize_t aht20_read(struct file *filp, char __user *buf, size_t cnt, loff_t *off) {  float temp,humi; // uint8_t data[7] = {0};  uint32_t data[2] = {0};  long err = 0;  filp->private_data = &aht20dev;  aht20_get_measure(&data[0]);  err = 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) {  int ret = -1;  ret = alloc_chrdev_region(&aht20dev.devid, 0, DEV_CNT, DEV_NAME);  if (ret < 0)  {   printk("fail to alloc aht_devn");   goto alloc_err;  } // aht20_chr_dev.owner = THIS_MODULE;  cdev_init(&aht20dev.cdev, &aht20_chr_dev_fops);  ret = cdev_add(&aht20dev.cdev, aht20dev.devid, DEV_CNT);  if (ret < 0)  {   printk("fail to add cdevn");   goto add_err;  }  aht20dev.class = class_create(THIS_MODULE, DEV_NAME);  aht20dev.device = device_create(aht20dev.class, NULL, aht20dev.devid, NULL, DEV_NAME);  aht20dev.private_data = client;  aht20_init();  return 0; add_err:  unregister_chrdev_region(aht20dev.devid, DEV_CNT);  printk("n add_err error! n"); alloc_err:  return -1; } static int aht20_remove(struct i2c_client *client) {  device_destroy(aht20dev.class, aht20dev.devid);   class_destroy(aht20dev.class);   cdev_del(&aht20dev.cdev);  unregister_chrdev_region(aht20dev.devid, DEV_CNT);  return 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設備結(jié)構體*/ 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驅(qū)動中的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驅(qū)動-aht20/aht20$ make |
將編譯生成的elf-aht20.ko模塊拷貝到開發(fā)板。
編寫測試應用源碼aht20_app.c
測試源碼中循環(huán)讀取驅(qū)動傳到用戶空間的數(shù)據(jù):
#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[]) {  int fd;  unsigned int databuf[2];  int c1,t1;  float hum,temp;  int ret = 0;  fd = open(AHT20_DEV, O_RDWR);  if(fd < 0) {   printf("can't open file %srn", AHT20_DEV);   return -1;  }  while (1) {   ret = read(fd, databuf, sizeof(databuf));   if(ret == 0) {    /* ?????? */   ?c1 = databuf[0]*1000/1024/1024; ?//   ?t1 = databuf[1] *200*10/1024/1024-500;   ?hum = (float)c1/10.0;   ?temp = (float)t1/10.0;   printf("hum = %0.2f temp = %0.2f rn",hum,temp);   usleep(500000);   }  }  close(fd);   return 0; } |
編譯應用
. /opt/fsl-imx-x11/4.1.15-2.0.0/environment-setup-cortexa7hf-neon-poky-linux-gnueabi
elf@ubuntu:~/work/test/07_I2C驅(qū)動-aht20/aht20_app$?$CC aht20_app.c -o aht20_app |
將編譯好的測試應用拷貝到開發(fā)板中。
測試
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 |