首先设置I2C1引脚的复用功能,找到 pinctrl_i2c1 节点:
vi stm32mp15-pinctrl.dtsi
stm32mp151.dtsi
i2c1: i2c@40012000 {
compatible = "st,stm32mp15-i2c"; //这个是和i2c控制器驱动完成匹配
reg = <0x40012000 0x400>; //控制器的地址和长度
interrupt-names = "event", "error";
interrupts-extended = <&exti 21 IRQ_TYPE_LEVEL_HIGH>,
<&intc GIC_SPI 32 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&rcc I2C1_K>;
resets = <&rcc I2C1_R>;
#address-cells = <1>; //修饰子节点地址的个数
#size-cells = <0>; //修饰子节点长度的个数
dmas = <&dmamux1 33 0x400 0x80000001>,//dam内存直取,可以把数据直接从一个地址搬移到另一个地址
<&dmamux1 34 0x400 0x80000001>;
dma-names = "rx", "tx";
power-domains = <&pd_core>;
st,syscfg-fmp = <&syscfg 0x4 0x1>;
wakeup-source;
i2c-analog-filter;
status = "disabled"; //控制器没有使能
};
/home/linux/linux-5.10.61/Documentation/devicetree/bindings/i2c/
&i2c1{//第0个成员工作状态,第1个休眠状态
pinctrl-names = "default", "sleep";//这个就是列表
pinctrl-0 = <&i2c1_pins_b>;//pinctrl-0 代表列表中第0个成员//i2c1_pins_b代表管脚复用
pinctrl-1 = <&i2c1_sleep_pins_b>;//pinctrl-1 代表列表中第1个成员
i2c-scl-rising-time-ns = <100>;//上升沿时间毫秒
i2c-scl-falling-time-ns = <7>;//下降沿时间
status = "okay"; //使能
/delete-property/dmas; //删除dma属性
/delete-property/dma-names;
si7006@40{ //添加的设备树节点
compatible = "st,si7006";
reg = <0x40>;//从机地址
};
};
make dtbs
重启开发板
安装驱动
#include
#include
#include
int si7006_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
printk("%s:%d\n", __func__, __LINE__);
return 0;
}
int si7006_remove(struct i2c_client *client)
{
printk("%s:%d\n", __func__, __LINE__);
return 0;
}
//通过设备树的形式匹配进入probe函数
const struct of_device_id oftable[] = {
{
.compatible = "st,si7006",
},
{},
};
//支持热插拔
MODULE_DEVICE_TABLE_TABLE(of, oftable);
struct i2c_driver si7006 = {
.probe = si7006_probe,
.remove = si7006_remove,
.driver = {
.name = "hello",
.of_match_table = oftable,
}
};
module_i2c_driver(si7006);
MODULE_LICENSE("GPL");
ifeq ($(arch),arm)
KERNELDIR :=/home/linux/linux-5.10.61
CROSS_COMPILE ?=arm-linux-gnueabihf-
else
KERNELDIR :=/lib/modules/$(shell uname -r)/build
CROSS_COMPILE ?=
endif
modname ?=
PWD :=$(shell pwd)
CC :=$(CROSS_COMPILE)gcc
all:
make -C $(KERNELDIR) M=$(PWD) modules
# $(CC) test.c -o test
clean:
make -C $(KERNELDIR) M=$(PWD) clean
# rm test
install:
cp *.ko ~/nfs/rootfs/
# cp test ~/nfs/rootfs/
help:
echo "make arch = arm or x86 modname= dirvers file name"
obj-m:=$(modname).o
linux@ubuntu:~/linu/driver/csdn/si7006$ make arch=arm modname=si7006
#include
#include
#include
#include
#include
#include
#define CNAME "si7006"
dev_t devno;
int major = 0;
int minor = 0;
int count = 1;
struct cdev *cdev;
struct class *cls;
struct device *dev;
int si7006_open(struct inode *inode, struct file *file)
{
printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
return 0;
}
long si7006_ioctl(struct file *file, unsigned int cmd, unsigned long which)
{
printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
return 0;
}
int si7006_close(struct inode *inode, struct file *file)
{
printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
return 0;
}
struct file_operations fops = {
.open = si7006_open,
.unlocked_ioctl = si7006_ioctl,
.release = si7006_close,
};
int si7006_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
int ret;
// 1.分配对象
cdev = cdev_alloc();
if (cdev == NULL)
{
printk("alloc memory failed\n");
ret = -ENOMEM;
goto ERR1;
}
// 2.初始化对象
cdev_init(cdev, &fops);
// 3.申请设备号
if (major > 0)
{ //静态申请
ret = register_chrdev_region(MKDEV(major, minor), count, CNAME);
if (ret != 0)
{
printk("static:alloc device number failed!\n");
goto ERR2;
}
}
else if (major == 0)
{ //动态申请
ret = alloc_chrdev_region(&devno, 0, 1, CNAME);
if (ret != 0)
{
printk("dynamic:alloc device number failed!\n");
goto ERR2;
}
major = MAJOR(devno);
minor = MINOR(devno);
}
// 4.注册对象
ret = cdev_add(cdev, MKDEV(major, minor), count);
if (ret)
{
printk("add cdev failed\n");
goto ERR3;
}
// 5.向上层提交目录的信息
cls = class_create(THIS_MODULE, "hello");
if (IS_ERR(cls))
{
printk("create class failed\n");
ret = PTR_ERR(cls);
goto ERR4;
}
// 6.向上层提交设备的信息
dev = device_create(cls, NULL, MKDEV(major, minor), NULL, CNAME);
if (IS_ERR(dev))
{
printk("create device failed\n");
ret = PTR_ERR(dev);
goto ERR5;
}
return 0;
ERR5:
class_destroy(cls);
ERR4:
cdev_del(cdev);
ERR3:
unregister_chrdev_region(MKDEV(major, minor), count);
ERR2:
kfree(cdev);
ERR1:
return ret;
}
int si7006_remove(struct i2c_client *client)
{
device_destroy(cls,MKDEV(major,minor));
class_destroy(cls);
cdev_del(cdev);
unregister_chrdev_region(MKDEV(major, minor), count);
kfree(cdev);
printk("%s:%d\n", __func__, __LINE__);
return 0;
}
}
//通过设备树的形式匹配进入probe函数
const struct of_device_id oftable[] = {
{
.compatible = "st,si7006",
},
{},
};
//支持热插拔
MODULE_DEVICE_TABLE(of, oftable);
struct i2c_driver si7006 = {
.probe = si7006_probe,
.remove = si7006_remove,
.driver = {
.name = "hello",
.of_match_table = oftable,
}};
module_i2c_driver(si7006);
MODULE_LICENSE("GPL");
3.封装i2c操作代码
(1) 全局变量中添加 i2c_client 成员:
struct i2c_client *gclient;
(2)在 si7006_probe中获取i2c_client结构体
int si7006_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
gclient = client;
}
(3)si7006读取温湿度的顺序如下
int i2c_read_tmp_hum(unsigned char reg)
{
int ret;
unsigned char r_buf[] = { reg };
unsigned short data;
// 1.封装消息
struct i2c_msg r_msg[] = {
[0] = {
.addr = gclient->addr,
.flags = 0,
.len = 1,
.buf = r_buf,
},
[1] = {
.addr = gclient->addr,
.flags = 1,
.len = 2,
.buf = (__u8 *)&data,//这里返回的数据是两位的
//这里先返回的是高8位u也就是地址的地位存储着数据的高8位,
//所以要把他调转回来arm是小端存储地址低位存储数据地位
},
};
// 2发送消息
ret = i2c_transfer(gclient->adapter, r_msg, ARRAY_SIZE(r_msg));
if (ret != ARRAY_SIZE(r_msg)) {
printk("i2c read serial or firmware error\n");
return -EAGAIN;
}
data = data >> 8 | data << 8;
return data;
}
ioctl头文件引入
#include
#include "si7006.h"
添加头文件,封装命令码
si7006.h
#ifndef __SI7006_H__
#define __SI7006_H__
#define GET_SI7006_TMP _IOR('r',0,int)
#define GET_SI7006_HUM _IOR('r',1,int)
#define GET_CMD_SIZE(cmd) ((cmd>>16)&0x3fff)
#define TMP_ADDR 0xe3 //温度寄存器地址
#define HUM_ADDR 0xe5 //湿度寄存器地址
#endif
open
int si7006_open(struct inode *inode, struct file *file)
{
printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
return 0;
}
ioctl
long si7006_ioctl(struct file *file, unsigned int cmd, unsigned long args)
{
int ret,data;
switch (cmd) {
case GET_SI7006_TMP:
data = i2c_read_tmp_hum(TMP_ADDR);
ret = copy_to_user((void *)args,&data,GET_CMD_SIZE(GET_SI7006_TMP));
if(ret){
printk("copy data to user error\n");
return -EIO;
}
break;
case GET_SI7006_HUM:
data = i2c_read_tmp_hum(HUM_ADDR);
ret = copy_to_user((void *)args,&data,GET_CMD_SIZE(GET_SI7006_HUM));
if(ret){
printk("copy data to user error\n");
return -EIO;
}
break;
}
printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
return 0;
}
close
int si7006_close(struct inode *inode, struct file *file)
{
printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
return 0;
}
#include
#include
#include
#include
#include
#include "si7006.h"
#define PRINT_ERR(msg) \
do \
{ \
perror(msg); \
return -1; \
} while (0)
int main(int argc, const char* argv[])
{
int fd;
int tmp, hum;
float rtmp, rhum;
if ((fd = open("/dev/si7006", O_RDWR)) == -1)
PRINT_ERR("open error");
while (1) {
ioctl(fd, GET_SI7006_TMP, &tmp);
ioctl(fd, GET_SI7006_HUM, &hum);
rtmp = 175.72 * tmp / 65536 - 46.85;
rhum = 125 * hum / 65536 - 6;
printf("tmp = %.2f,hum=%.2f\n", rtmp, rhum);
sleep(1);
}
close(fd);
return 0;
}
成功读取读到温湿度