硬件环境:
1、荔枝派nano(f1c100s)
2、使用f1c100s的i2c0,PE11和PE12引脚
软件环境:
1、Linux 4.15
2、BME280使用介绍
文章目录
- 一、I2C子系统
- 1、应用层访问i2c设备
- 2、驱动层访问i2c设备
- 2.1、i2c总线设备驱动模型
- 2.2、i2c_client 和 i2c_driver
- 二、程序编写
- 1、驱动程序
- 2、i2c_client实现
- 3、测试应用程序
- 三、总结
一、I2C子系统
对i2c设备的访问,有两种方法:
1、在应用层直接访问i2c设备;
2、编写i2c设备的驱动程序,也就是在驱动层访问;
1、应用层访问i2c设备
- 在应用层可以通过i2c-dev.c驱动程序访问芯片内部i2c控制器的驱动adapter_driver,进而实现访问i2c控制器下的i2c设备。也就是可以在应用程序中直接使用内核提供的i2c-dev.c提供的API函数对i2c设备进行读写操作,不用再去编写该设备的驱动程序。i2ctools就是基于i2c-dev.c实现的在应用层访问i2c设备的工具;
2、驱动层访问i2c设备
- 所谓的驱动层访问i2c设备,就是需要我们真正编写某个i2c设备的驱动程序;
2.1、i2c总线设备驱动模型
- 可以看到这很像Linux下的platform总线设备驱动模型,但platform总线设备驱动模型是虚拟的,而i2c总线设备驱动模型是真实存在的;
- 当有新的i2c_client时,i2c总线会匹配其对应的i2c_driver;当有新的i2c_driver注册时,就会匹配还未匹配驱动的i2c_client;一旦匹配成功,i2c_driver的probe函数就被调用;
2.2、i2c_client 和 i2c_driver
-
i2c_client:
i2c_client结构体会存放设备地址(addr)、名字(name)、挂载在哪个i2c控制器下(adapter),等相关硬件信息; -
i2c_driver:
i2c_driver结构体实现相关的probe、remove等函数;
二、程序编写
程序以访问BME280传感器为例,访问其它i2c设备也是类似的;重在框架;
本驱动程序读取BME280修正参数及温湿度值和大气压值,在应用程序计算最终的温湿度值和大气压值,BME280相关介绍可以参考BME280使用介绍;
1、驱动程序
驱动程序编写大致流程:
1、先定义i2c_driver结构体;
2、实现probe和remove函数;
3、实现file_operations结构体的open和read等函数、比如在open函数里初始化i2c设备,read函数里读寄存器;
驱动程序定义i2c_driver结构体:
static const struct of_device_id of_match_ids_bme280[] = {{ .compatible = "bosch,bme280", .data = NULL },{ /* END OF LIST */ },
};static const struct i2c_device_id bme280_ids[] = {{ "bme280", (kernel_ulong_t)NULL },{ /* END OF LIST */ }
};static struct i2c_driver bme280_driver = {.driver = {.name = "bme280",.of_match_table = of_match_ids_bme280,},.probe_new = bme280_probe, //匹配成功后的probe函数.remove = bme280_remove,.id_table = bme280_ids,
};
实现probe和remove函数:
static struct file_operations bme280_ops = {.owner = THIS_MODULE,.open = bme280_open,.read = bme280_read,
};static int bme280_probe(struct i2c_client *client)
{printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);bme280_client = client; //保存client,里面有设备地址,后续要用major = register_chrdev(0, "bme280", &bme280_ops);bme280_class = class_create(THIS_MODULE, "bme280_class");device_create(bme280_class, NULL, MKDEV(major, 0), NULL, "bme280"); /* /dev/bme280 */return 0;
}static int bme280_remove(struct i2c_client *client)
{ printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);device_destroy(bme280_class, MKDEV(major, 0));class_destroy(bme280_class);unregister_chrdev(major, "bme280");return 0;
}
实现open和read函数:
static ssize_t bme280_read (struct file *file, char __user *buf, size_t size, loff_t *offset)
{int err;if(size != sizeof(struct bme280_parameter))return 0;bme280_refresh();bme280_read_temp();bme280_read_press();bme280_read_humi();err = copy_to_user(buf, &bme280_para, size);return size;
}static int bme280_open (struct inode *node, struct file *file)
{/* 在open函数中初始化BME280 *//* init bme280 */i2c_smbus_write_byte_data(bme280_client, BME280_REGISTER_CTRL_MEAS, 0x55);i2c_smbus_write_byte_data(bme280_client, BME280_REGISTER_CONFIG, 0x10);/* 读取修正参数 *//* read bme280 parameter */bme280_read_parameter();printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);return 0;
}
完整的驱动程序:
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/of_device.h>
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/mutex.h>
#include <linux/mod_devicetable.h>
#include <linux/bitops.h>
#include <linux/jiffies.h>
#include <linux/property.h>
#include <linux/acpi.h>
#include <linux/i2c.h>
#include <linux/nvmem-provider.h>
#include <linux/regmap.h>
#include <linux/pm_runtime.h>
#include <linux/gpio/consumer.h>
#include <linux/uaccess.h>
#include <linux/fs.h>/* BME280 REGISTER */
#define BME280_REGISTER_ID (0xD0)
#define BME280_REGISTER_RESET (0xE0)
#define BME280_REGISTER_STATUS (0xF3)
#define BME280_REGISTER_CTRL_MEAS (0xF4)
#define BME280_REGISTER_CONFIG (0xF5)
#define BME280_REGISTER_PRESS_MSB (0xF7)
#define BME280_REGISTER_PRESS_LSB (0xF8)
#define BME280_REGISTER_PRESS_XLSB (0xF9)
#define BME280_REGISTER_TEMP_MSB (0xFA)
#define BME280_REGISTER_TEMP_LSB (0xFB)
#define BME280_REGISTER_TEMP_XLSB (0xFC)
#define BME280_REGISTER_HUMI_MSB (0xFD)
#define BME280_REGISTER_HUMI_LSB (0xFE)struct bme280_parameter{unsigned short int T1;short int T2;short int T3;unsigned short int P1;short int P2;short int P3;short int P4;short int P5;short int P6;short int P7;short int P8;short int P9;unsigned char H1;short int H2;unsigned char H3;short int H4;short int H5;unsigned char H6;int adc_T;int adc_P;int adc_H;int t_fine;
};static int major;
static struct class *bme280_class;
static struct i2c_client *bme280_client;
static struct bme280_parameter bme280_para;static void bme280_read_parameter (void)
{unsigned char tmp;//dig_T1bme280_para.T1 = i2c_smbus_read_byte_data(bme280_client, 0x89);bme280_para.T1 <<= 8;bme280_para.T1 |= i2c_smbus_read_byte_data(bme280_client, 0x88);//dig_T2bme280_para.T2 = i2c_smbus_read_byte_data(bme280_client, 0x8B);bme280_para.T2 <<= 8;bme280_para.T2 |= i2c_smbus_read_byte_data(bme280_client, 0x8A);//dig_T3bme280_para.T3 = i2c_smbus_read_byte_data(bme280_client, 0x8D);bme280_para.T3 <<= 8;bme280_para.T3 |= i2c_smbus_read_byte_data(bme280_client, 0x8C);//dig_P1bme280_para.P1 = i2c_smbus_read_byte_data(bme280_client, 0x8F);bme280_para.P1 <<= 8;bme280_para.P1 |= i2c_smbus_read_byte_data(bme280_client, 0x8E);//dig_P2bme280_para.P2 = i2c_smbus_read_byte_data(bme280_client, 0x91);bme280_para.P2 <<= 8;bme280_para.P2 |= i2c_smbus_read_byte_data(bme280_client, 0x90);//dig_P3bme280_para.P3 = i2c_smbus_read_byte_data(bme280_client, 0x93);bme280_para.P3 <<= 8;bme280_para.P3 |= i2c_smbus_read_byte_data(bme280_client, 0x92);//dig_P4bme280_para.P4 = i2c_smbus_read_byte_data(bme280_client, 0x95);bme280_para.P4 <<= 8;bme280_para.P4 |= i2c_smbus_read_byte_data(bme280_client, 0x94);//dig_P5bme280_para.P5 = i2c_smbus_read_byte_data(bme280_client, 0x97);bme280_para.P5 <<= 8;bme280_para.P5 |= i2c_smbus_read_byte_data(bme280_client, 0x96);//dig_P6bme280_para.P6 = i2c_smbus_read_byte_data(bme280_client, 0x99);bme280_para.P6 <<= 8;bme280_para.P6 |= i2c_smbus_read_byte_data(bme280_client, 0x98);//dig_P7bme280_para.P7 = i2c_smbus_read_byte_data(bme280_client, 0x9B);bme280_para.P7 <<= 8;bme280_para.P7 |= i2c_smbus_read_byte_data(bme280_client, 0x9A);//dig_P8bme280_para.P8 = i2c_smbus_read_byte_data(bme280_client, 0x9D);bme280_para.P8 <<= 8;bme280_para.P8 |= i2c_smbus_read_byte_data(bme280_client, 0x9C);//dig_P9bme280_para.P9 = i2c_smbus_read_byte_data(bme280_client, 0x9F);bme280_para.P9 <<= 8;bme280_para.P9 |= i2c_smbus_read_byte_data(bme280_client, 0x9E);//dig_H1bme280_para.H1 = i2c_smbus_read_byte_data(bme280_client, 0xA1);//dig_H2bme280_para.H2 = i2c_smbus_read_byte_data(bme280_client, 0xE2);bme280_para.H2 <<= 8;bme280_para.H2 |= i2c_smbus_read_byte_data(bme280_client, 0xE1);//dig_H3bme280_para.H3 = i2c_smbus_read_byte_data(bme280_client, 0xE3);//dig_H4bme280_para.H4 = i2c_smbus_read_byte_data(bme280_client, 0xE4);bme280_para.H4 <<= 4;tmp = i2c_smbus_read_byte_data(bme280_client, 0xE5);tmp &= 0x0f;bme280_para.H4 |= tmp;//dig_Hbme280_para.H5 = i2c_smbus_read_byte_data(bme280_client, 0xE6);bme280_para.H5 <<= 4;tmp = i2c_smbus_read_byte_data(bme280_client, 0xE5);tmp &= 0xf0;tmp >>= 4;bme280_para.H5 |= tmp;//dig_H6bme280_para.H6 = i2c_smbus_read_byte_data(bme280_client, 0xE7);
}static void bme280_refresh (void)
{int err;// refresh before read dataerr = i2c_smbus_write_byte_data(bme280_client, BME280_REGISTER_CTRL_MEAS, 0x55);msleep(45);
}static void bme280_read_temp (void)
{int ret;// read temp dataret = i2c_smbus_read_byte_data(bme280_client, BME280_REGISTER_TEMP_MSB);bme280_para.adc_T = ret;bme280_para.adc_T <<= 8;ret = i2c_smbus_read_byte_data(bme280_client, BME280_REGISTER_TEMP_LSB);bme280_para.adc_T |= ret;bme280_para.adc_T <<= 8;ret = i2c_smbus_read_byte_data(bme280_client, BME280_REGISTER_TEMP_XLSB);bme280_para.adc_T |= ret;bme280_para.adc_T >>= 4;
}static void bme280_read_press (void)
{int ret;// read press dataret = i2c_smbus_read_byte_data(bme280_client, BME280_REGISTER_PRESS_MSB);bme280_para.adc_P = ret;bme280_para.adc_P <<= 8;ret = i2c_smbus_read_byte_data(bme280_client, BME280_REGISTER_PRESS_LSB);bme280_para.adc_P |= ret;bme280_para.adc_P <<= 8;ret = i2c_smbus_read_byte_data(bme280_client, BME280_REGISTER_PRESS_XLSB);bme280_para.adc_P |= ret;bme280_para.adc_P >>= 4;
}static void bme280_read_humi (void)
{int ret;// read humi dataret = i2c_smbus_read_byte_data(bme280_client, BME280_REGISTER_HUMI_MSB);bme280_para.adc_H = ret;bme280_para.adc_H <<= 8;ret = i2c_smbus_read_byte_data(bme280_client, BME280_REGISTER_HUMI_LSB);bme280_para.adc_H |= ret;
}static ssize_t bme280_read (struct file *file, char __user *buf, size_t size, loff_t *offset)
{int err;if(size != sizeof(struct bme280_parameter))return 0;bme280_refresh();bme280_read_temp();bme280_read_press();bme280_read_humi();err = copy_to_user(buf, &bme280_para, size);return size;
}static int bme280_open (struct inode *node, struct file *file)
{/* init bme280 */i2c_smbus_write_byte_data(bme280_client, BME280_REGISTER_CTRL_MEAS, 0x55);i2c_smbus_write_byte_data(bme280_client, BME280_REGISTER_CONFIG, 0x10);/* read bme280 parameter */bme280_read_parameter();printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);return 0;
}static struct file_operations bme280_ops = {.owner = THIS_MODULE,.open = bme280_open,.read = bme280_read,
};static const struct of_device_id of_match_ids_bme280[] = {{ .compatible = "bosch,bme280", .data = NULL },{ /* END OF LIST */ },
};static const struct i2c_device_id bme280_ids[] = {{ "bme280", (kernel_ulong_t)NULL },{ /* END OF LIST */ }
};static int bme280_probe(struct i2c_client *client)
{printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);bme280_client = client;major = register_chrdev(0, "bme280", &bme280_ops);bme280_class = class_create(THIS_MODULE, "bme280_class");device_create(bme280_class, NULL, MKDEV(major, 0), NULL, "bme280"); /* /dev/bme280 */return 0;
}static int bme280_remove(struct i2c_client *client)
{ printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);device_destroy(bme280_class, MKDEV(major, 0));class_destroy(bme280_class);unregister_chrdev(major, "bme280");return 0;
}static struct i2c_driver bme280_driver = {.driver = {.name = "bme280",.of_match_table = of_match_ids_bme280,},.probe_new = bme280_probe,.remove = bme280_remove,.id_table = bme280_ids,
};static int __init bme280_driver_init(void)
{printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);return i2c_add_driver(&bme280_driver);
}static void __exit bme280_driver_exit(void)
{i2c_del_driver(&bme280_driver);
}module_init(bme280_driver_init);
module_exit(bme280_driver_exit);MODULE_AUTHOR("Cohen0415");
MODULE_LICENSE("GPL");
2、i2c_client实现
我们知道i2c_driver是靠编写程序实现,但没说i2c_client怎么实现;i2c_client也可以通过程序实现,但在这我们通过设备树来实现,i2c控制器驱动程序会自动把设备树中的i2c节点转成i2c_client(个人理解);
本次使用f1c100s的i2c0,引脚PE11(CK)、PE12(DA),只列出部分设备树的定义,如下:
suniv.dtsi:
suniv-f1c100s-licheepi-nano.dts:
在i2c节点下添加你的i2c设备
3、测试应用程序
应用程序比较简单,直接列出完整程序:
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>struct bme280_parameter{unsigned short int T1;short int T2;short int T3;unsigned short int P1;short int P2;short int P3;short int P4;short int P5;short int P6;short int P7;short int P8;short int P9;unsigned char H1;short int H2;unsigned char H3;short int H4;short int H5;unsigned char H6;int adc_T;int adc_P;int adc_H;int t_fine;
};struct bme280_parameter bme280;float compute_temp() //通过修正参数计算最终温度值
{int var1, var2, T;var1 = ((((bme280.adc_T>>3) - (bme280.T1<<1))) * bme280.T2) >> 11;var2 = (((((bme280.adc_T>>4)-bme280.T1) * (bme280.adc_T>>4)-bme280.T1) >> 12) * bme280.T3) >> 14;bme280.t_fine = var1 + var2;T = (bme280.t_fine * 5 + 128) >> 8;return (float)T/100;
}float compute_press() //通过修正参数计算最终压力值,修正算法来自BME280手册
{int64_t var1, var2, p;var1 = ((int64_t)bme280.t_fine) - 128000;var2 = var1 * var1 * (int64_t)bme280.P6;var2 = var2 + ((var1 * (int64_t)bme280.P5) << 17);var2 = var2 + (((int64_t)bme280.P4) << 35);var1 = ((var1 * var1 * (int64_t)bme280.P3) >> 8) +((var1 * (int64_t)bme280.P2) << 12);var1 =(((((int64_t)1) << 47) + var1)) * ((int64_t)bme280.P1) >> 33;if (var1 == 0){return 0;}else{p = 1048576 - bme280.adc_P;p = (((p << 31) - var2) * 3125) / var1;var1 = (((int64_t)bme280.P9) * (p >> 13) * (p >> 13)) >> 25;var2 = (((int64_t)bme280.P8) * p) >> 19;p = ((p + var1 + var2) >> 8) + (((int64_t)bme280.P7) << 4);return (float)p/256;}
}float compute_humi() //通过修正参数计算最终湿度值
{double var_H;var_H = (((double)bme280.t_fine) - 76800.00);var_H = (bme280.adc_H - (((double)bme280.H4) * 64.0 + ((double)bme280.H5) / 16384.0 * var_H)) * (((double)bme280.H2) / 65536.0 * (1.0 + ((double)bme280.H6) / 67108864.0 * var_H * (1.0 + ((double)bme280.H3) / 67108864.0 * var_H)));var_H = var_H * (1.0 - ((double)bme280.H1) * var_H / 524288.0);if(var_H > 100.0){var_H = 100.0;}else if(var_H < 0.0){var_H = 0.0;}return var_H;
}int main(int argc, char **argv)
{int fd;int len;float temp = 0, press = 0, humi = 0;fd = open("/dev/bme280", O_RDWR);if (fd == -1){printf("can not open file /dev/bme280\n");return -1;}while(1){len = read(fd, &bme280, sizeof(struct bme280_parameter)); temp = compute_temp();press = compute_press();humi = compute_humi();printf("temp=%.2f press=%.2f humi=%.2f\n", temp, press, humi);sleep(1);}close(fd);return 0;
}
三、总结
1、以上出现的专业术语或名词解释或个人理解有不妥,恳请指出!