1.编写设备树,添加蜂鸣器等设备
驱动代码:
#include <linux/init.h>
#include <linux/module.h>
#include <linux/i2c.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/device.h>
#include "si7006.h"
#include <linux/of_gpio.h>
#include <linux/gpio.h>
int major;
struct class *cls;
struct device *dev;
//tclient指向的结构体,存储设备驱动和总线驱动匹配成功的信息
struct i2c_client *tclient;
//指向gpio节点
struct device_node *node;
//定义结构体指针指向gpio编号
struct gpio_desc *gpiono;
int rcv;
//获取温湿度的函数
int i2c_read_hum_tem(unsigned char reg)
{int ret;char r_buf[]={reg}; //地址放到数组中,读取消息unsigned short val; //读的数据//设备驱动将要收发的消息放到结构体中struct i2c_msg r_msg[]={[0]={.addr=tclient->addr,.flags=0,.len=1, //发送的地址长度是8bit一字节.buf=r_buf,},[1]={.addr=tclient->addr,.flags=1,.len=2,.buf=(char*)&val,}};//消息传输ret=i2c_transfer(tclient->adapter,r_msg,ARRAY_SIZE(r_msg));if(ret!=ARRAY_SIZE(r_msg)){printk("获取温湿度数据失败\n");return -EAGAIN;}return val;
}
int si7006_open(struct inode *inode, struct file *file)
{printk("open\n");return 0;
}
long si7006_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{int tem,hum;int ret;switch(cmd){case GET_HUM:hum=i2c_read_hum_tem(0xe5);ret=copy_to_user((void*)arg,(void*)&hum,sizeof(int));if(ret){printk("copy to user failed\n");return -EINVAL;}break;case GET_TEM:tem=i2c_read_hum_tem(0xe3);ret=copy_to_user((void*)arg,(void*)&tem,sizeof(int));if(ret){printk("copy to user failed\n");return -EINVAL;}break;case FENGM:gpiod_set_value(gpiono,1);break;}return 0;
}
int si7006_close(struct inode *inode, struct file *file)
{printk("close\n");return 0;
}
struct file_operations fops={.open=si7006_open,.unlocked_ioctl=si7006_ioctl,.release=si7006_close,
};
//注册字符设备驱动
int chrdev(void){major=register_chrdev(0,"si7006",&fops);if(major<0){printk("字符设备驱动注册失败\n");return major;}printk("字符设备驱动注册成功\n");//自动创建设备节点cls=class_create(THIS_MODULE,"si7006");if(IS_ERR(cls)){printk("向上提交目录失败\n");return PTR_ERR(cls);}printk("向上提交目录成功\n");dev=device_create(cls,NULL,MKDEV(major,0),NULL,"si7006");if(IS_ERR(dev)){printk("向上提交设备信息失败\n");return PTR_ERR(dev);}printk("向上提交设备信息成功\n");return 0;}//定义一个probe函数int si7006_probe(struct i2c_client *client,const struct i2c_device_id *id){tclient=client;//将总线驱动指针编程全局的//注册字符设备驱动rcv=chrdev();//获取节点信息node=of_find_node_by_name(NULL,"extend_dev");if(node==NULL){printk("通过名字解析设备树节点失败\n");return -EFAULT;}printk("设备树解析成功\n");//获取并申请gpio编号,键值fengmgpiono=gpiod_get_from_of_node(node,"fengm",0,GPIOD_OUT_LOW,NULL);if(IS_ERR(gpiono)){printk("获取gpiono 失败\n");return PTR_ERR(gpiono);}printk("获取gpiono 成功\n");return 0;}//设备分离后执行removeint si7006_remove(struct i2c_client *client){gpiod_set_value(gpiono,0);//销毁设备节点device_destroy(cls,MKDEV(major,0));//销毁目录class_destroy(cls);//驱动注销unregister_chrdev(major,"si7006");printk("%s:%d",__func__,__LINE__);return 0;}struct of_device_id oftable[]={{.compatible="hqyj,si7006",},{},};//热插拔宏,可以在插入硬件时,自动安装驱动MODULE_DEVICE_TABLE(of,oftable);//对象初始化struct i2c_driver si7006={.probe=si7006_probe,.remove=si7006_remove, .driver={.name="tem_hum_driver", //按名字匹配,只能匹配一个设备.of_match_table=oftable,//设备树匹配 }, };module_i2c_driver(si7006);
MODULE_LICENSE("GPL");
应用层代码:
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <stdlib.h>
#include <arpa/inet.h>
#include "si7006.h"
int main(int argc, char const *argv[])
{int hum,tem;float hum1,tem1;int ret;int ubuf[20];int fd=open("/dev/si7006",O_RDWR);if(fd<0){printf("打开设备文件失败\n");exit(-1);}while(1){//通过ioctl获取温湿度,放到tem,hum所在的地址ioctl(fd,GET_TEM,&tem);ioctl(fd,GET_HUM,&hum);//大端转小端hum=ntohs(hum);tem=ntohs(tem);//温湿度转换hum1=125.0*hum/65536-6;tem1=175.72*tem/65536-46.85;printf("tem:%f hum=%f\n",tem1,hum1);//当湿度达到70触发警报---PB6蜂鸣器if(hum1>=70){ioctl(fd,FENGM);printf("警报\n");}sleep(1);}return 0;
}
头文件:
#ifndef __SI7006_H__
#define __SI7006_H__
//命令码封装,方向,大小,类型m,功能0
#define GET_TEM _IOR('m',0,int)
#define GET_HUM _IOR('m',1,int)
#define FENGM _IOW('u',3,int)
#endif
报错:
[root@fsmp1a /]#rmmod driver.ko
rmmod: can't unload module 'driver': Device or resource busy
解决办法:
vi /proc/devices
删除驱动信息,重启开发板
实验现象: